UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

189 lines 6.17 kB
import { hash256 } from '../primitives/Hash.js'; import { toHex, toArray } from '../primitives/utils.js'; import Transaction from './Transaction.js'; import { BEEF_V2, TX_DATA_FORMAT } from './Beef.js'; /** * A single bitcoin transaction associated with a `Beef` validity proof set. * * Simple case is transaction data included directly, either as raw bytes or fully parsed data, or both. * * Supports 'known' transactions which are represented by just their txid. * It is assumed that intended consumer of this beef already has validity proof for such a transaction, * which they can merge if necessary to create a valid beef. */ export default class BeefTx { _bumpIndex; _tx; _rawTx; _txid; inputTxids = []; /** * true if `hasProof` or all inputs chain to `hasProof`. * * Typically set by sorting transactions by proven dependency chains. */ isValid = undefined; get bumpIndex() { return this._bumpIndex; } set bumpIndex(v) { this._bumpIndex = v; this.updateInputTxids(); } get hasProof() { return this._bumpIndex !== undefined; } get isTxidOnly() { return this._txid !== undefined && this._txid !== null && (this._rawTx == null) && (this._tx == null); } get txid() { if (this._txid !== undefined && this._txid !== null && this._txid !== '') return this._txid; if (this._tx != null) { this._txid = this._tx.id('hex'); return this._txid; } if (this._rawTx != null) { this._txid = toHex(hash256(this._rawTx)); return this._txid; } throw new Error('Internal'); } get tx() { if (this._tx != null) return this._tx; if (this._rawTx != null) { this._tx = Transaction.fromBinary(this._rawTx); return this._tx; } return undefined; } get rawTx() { if (this._rawTx != null) return this._rawTx; if (this._tx != null) { this._rawTx = this._tx.toBinary(); return this._rawTx; } return undefined; } /** * @param tx If string, must be a valid txid. If `number[]` must be a valid serialized transaction. * @param bumpIndex If transaction already has a proof in the beef to which it will be added. */ constructor(tx, bumpIndex) { if (typeof tx === 'string') { this._txid = tx; } else if (Array.isArray(tx)) { this._rawTx = tx; } else { this._tx = tx; } this.bumpIndex = bumpIndex; this.updateInputTxids(); } static fromTx(tx, bumpIndex) { return new BeefTx(tx, bumpIndex); } static fromRawTx(rawTx, bumpIndex) { return new BeefTx(rawTx, bumpIndex); } static fromTxid(txid, bumpIndex) { return new BeefTx(txid, bumpIndex); } updateInputTxids() { if (this.hasProof || (this.tx == null)) { // If we have a proof, or don't have a parsed transaction this.inputTxids = []; } else { const inputTxids = {}; // ✅ Explicit object type for (const input of this.tx.inputs) { if (input.sourceTXID !== undefined && input.sourceTXID !== null && input.sourceTXID !== '') { // ✅ Ensure sourceTXID is defined inputTxids[input.sourceTXID] = true; } } this.inputTxids = Object.keys(inputTxids); } } toWriter(writer, version) { const writeByte = (bb) => { writer.writeUInt8(bb); }; const writeTxid = () => { if (this._txid == null) { throw new Error('Transaction ID (_txid) is undefined'); } writer.writeReverse(toArray(this._txid, 'hex')); }; const writeTx = () => { if (this._rawTx != null) { writer.write(this._rawTx); } else if (this._tx != null) { writer.write(this._tx.toBinary()); } else { throw new Error('a valid serialized Transaction is expected'); } }; const writeBumpIndex = () => { if (this.bumpIndex === undefined) { writeByte(TX_DATA_FORMAT.RAWTX); // 0 } else { writeByte(TX_DATA_FORMAT.RAWTX_AND_BUMP_INDEX); // 1 writer.writeVarIntNum(this.bumpIndex); // the index of the associated bump } }; if (version === BEEF_V2) { if (this.isTxidOnly) { writeByte(TX_DATA_FORMAT.TXID_ONLY); writeTxid(); } else if (this.bumpIndex !== undefined) { writeByte(TX_DATA_FORMAT.RAWTX_AND_BUMP_INDEX); writer.writeVarIntNum(this.bumpIndex); writeTx(); } else { writeByte(TX_DATA_FORMAT.RAWTX); writeTx(); } } else { writeTx(); writeBumpIndex(); } } static fromReader(br, version) { let data; let bumpIndex; let beefTx; if (version === BEEF_V2) { // V2 const format = br.readUInt8(); if (format === TX_DATA_FORMAT.TXID_ONLY) { beefTx = BeefTx.fromTxid(toHex(br.readReverse(32))); } else { if (format === TX_DATA_FORMAT.RAWTX_AND_BUMP_INDEX) { bumpIndex = br.readVarIntNum(); } data = Transaction.fromReader(br); beefTx = BeefTx.fromTx(data, bumpIndex); } } else { // V1 data = Transaction.fromReader(br); bumpIndex = br.readUInt8() !== 0 ? br.readVarIntNum() : undefined; beefTx = BeefTx.fromTx(data, bumpIndex); } return beefTx; } } //# sourceMappingURL=BeefTx.js.map