From ed91b3f57b92bc1e4e02a04234dd1d8d17955dec Mon Sep 17 00:00:00 2001 From: Jim Hamill Date: Mon, 14 Jul 2025 00:27:00 +0000 Subject: [PATCH] fix(BlockchainInMemory): BIP30 protections and fetchChainTip --- .../src/blockchain/blockchain-inmemory.ts | 41 +++++++++++++------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/packages/stack/src/blockchain/blockchain-inmemory.ts b/packages/stack/src/blockchain/blockchain-inmemory.ts index 95509e5..bb345fd 100644 --- a/packages/stack/src/blockchain/blockchain-inmemory.ts +++ b/packages/stack/src/blockchain/blockchain-inmemory.ts @@ -23,8 +23,9 @@ import { createVirtualMachineBCH, } from '@bitauth/libauth'; -// Local Blockchain Adapter. -// This simulates the BCH Blockchain. +/** + * @remarks This provider DOES NOT enforce the 100 confirmations Coinbase Transaction rule. + **/ export class BlockchainInMemory extends BaseBlockchain { static async from(this: T) { const blockchain = new this(); @@ -104,10 +105,14 @@ export class BlockchainInMemory extends BaseBlockchain { return blockHeader; } - // TODO: Implement a method for fetching the chain tip. - // This is a placeholder for now. async fetchChainTip(): Promise { - throw new Error('Not implemented yet'); + const height = this.blockHeaders.length; + const blockHeader = this.blockHeaders[height - 1]; + + return { + height, + blockHeader, + }; } async broadcastTransaction( @@ -347,14 +352,6 @@ export class BlockchainInMemory extends BaseBlockchain { } async mineBlock(outputs: Array): Promise { - // Create a block header from the current timestamp. - const newBlockHeader = BlockHeader.generateRandom( - Math.floor(Date.now() / 1000), - ); - - // Add it to our list of block headers. - this.blockHeaders.push(newBlockHeader); - // Create a transaction that pays to our subscribed address. const transaction = Transaction.fromRaw({ version: 2, @@ -370,6 +367,24 @@ export class BlockchainInMemory extends BaseBlockchain { outputs, }); + // Calculate the hash of this transaction. + const coinbaseTxHash = transaction.toHash(); + + // Ensure that this transaction hash does not already exist (as per BIP30). + if (this.transactions.has(coinbaseTxHash.toHex())) { + throw new Error( + `Transaction ID "${coinbaseTxHash.toHex()}" already exists (see BIP30 for details)`, + ); + } + + // Create a block header from the current timestamp. + const newBlockHeader = BlockHeader.generateRandom( + Math.floor(Date.now() / 1000), + ); + + // Add it to our list of block headers. + this.blockHeaders.push(newBlockHeader); + // Broadcast that transaction. await this.broadcastTransaction(transaction, { skipValidation: true }); }