diff --git a/packages/primitives/src/transaction-graph.ts b/packages/primitives/src/transaction-graph.ts deleted file mode 100644 index 6a20b66..0000000 --- a/packages/primitives/src/transaction-graph.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { Transaction } from './transaction.js'; - -import { binToHex, Output } from '@bitauth/libauth'; - -export interface UTXO extends Output { - outpointTransactionHash: string; - outpointIndex: number; -} - -export class TransactionGraph { - private transactions: Map; - - constructor() { - this.transactions = new Map(); - } - - addTransaction(tx: Transaction) { - this.transactions.set(tx.toHashHex(), tx); - } - - removeTransaction(txHash: string) { - this.transactions.delete(txHash); - } - - removeDependentTransactions(txHash: string): string[] { - const removedTxids: string[] = []; - const toRemove: Set = new Set([txHash]); - - const findDependents = (id: string) => { - for (const [otherTxid, tx] of this.transactions) { - if ( - tx - .getInputs() - .some((input) => binToHex(input.outpointTransactionHash) === id) - ) { - toRemove.add(otherTxid); - findDependents(otherTxid); - } - } - }; - - findDependents(txHash); - - for (const id of toRemove) { - if (this.transactions.has(id)) { - this.transactions.delete(id); - removedTxids.push(id); - } - } - - return removedTxids; - } - - getTransaction(txHash: string): Transaction | undefined { - return this.transactions.get(txHash); - } - - getAllTransactions(): Transaction[] { - return Array.from(this.transactions.values()); - } - - deriveUtxoState(): UTXO[] { - const utxoMap = new Map(); - - // First, add all outputs as potential UTXOs - for (const tx of this.transactions.values()) { - tx.getOutputs().forEach((output, outpointIndex) => { - const txHashHex = tx.toHashHex(); - - const utxo: UTXO = { - outpointTransactionHash: txHashHex, - outpointIndex, - valueSatoshis: output.valueSatoshis, - lockingBytecode: output.lockingBytecode, - }; - utxoMap.set(`${txHashHex}:${outpointIndex}`, utxo); - }); - } - - // Then, remove all spent outputs - for (const tx of this.transactions.values()) { - tx.getInputs().forEach((input) => { - const outpointTransactionHashHex = binToHex( - input.outpointTransactionHash - ); - - utxoMap.delete(`${outpointTransactionHashHex}:${input.outpointIndex}`); - }); - } - - return Array.from(utxoMap.values()); - } - - isTransactionValid(tx: Transaction): boolean { - const currentUtxos = this.deriveUtxoState(); - const utxoMap = new Map( - currentUtxos.map((utxo) => [ - `${utxo.outpointTransactionHash}:${utxo.outpointIndex}`, - utxo, - ]) - ); - - return tx.getInputs().every((input) => { - const outpointTransactionHashHex = binToHex( - input.outpointTransactionHash - ); - return utxoMap.has( - `${outpointTransactionHashHex}:${input.outpointIndex}` - ); - }); - } - - getDependentTransactions(txHash: string): Transaction[] { - return this.getAllTransactions().filter((tx) => { - tx.getInputs().some((input) => { - const outpointTransactionHashHex = binToHex( - input.outpointTransactionHash - ); - return outpointTransactionHashHex === txHash; - }); - }); - } - - getBroadcastOrder(): Transaction[] { - const visited = new Set(); - const order: Transaction[] = []; - - const visit = (txid: string) => { - if (visited.has(txid)) return; - - const tx = this.transactions.get(txid); - if (!tx) return; - - visited.add(txid); - - // First, visit all transactions that this transaction depends on - for (const input of tx.getInputs()) { - const outpointTransactionHashHex = binToHex( - input.outpointTransactionHash - ); - visit(outpointTransactionHashHex); - } - - order.push(tx); - }; - - // Start the topological sort from each transaction - for (const tx of this.transactions.values()) { - visit(tx.toHashHex()); - } - - return order; - } - - getNextTransactionToBroadcast(): Transaction | undefined { - // This is pretty inefficient in the sense we recompile the graph each time. - // But I'm not sure if that would really have much real-world performance impact. - return this.getBroadcastOrder()[0] ?? undefined; - } -}