import { Sha256 } from '../crypto/sha256' ; import { bigIntToBitcoinVarInt , binToHex , binToNumberUint32LE , flattenBinArray , numberToBinUint32LE , readBitcoinVarInt , } from '../format/format' ; import { Input , Output , Transaction } from './transaction-types' ; param bin param offset export const readTransactionInput = ( bin , offset ) => { const sha256HashBytes = 32 ; const uint32Bytes = 4 ; const offsetAfterTxHash = offset + sha256HashBytes ; const outpointTransactionHash = bin . slice ( offset , offsetAfterTxHash ) . reverse ( ) ; const offsetAfterOutpointIndex = offsetAfterTxHash + uint32Bytes ; const outpointIndex = binToNumberUint32LE ( bin . subarray ( offsetAfterTxHash , offsetAfterOutpointIndex ) ) ; const { nextOffset : offsetAfterBytecodeLength , value : bytecodeLength , } = readBitcoinVarInt ( bin , offsetAfterOutpointIndex ) ; const offsetAfterBytecode = offsetAfterBytecodeLength + Number ( bytecodeLength ) ; const unlockingBytecode = bin . slice ( offsetAfterBytecodeLength , offsetAfterBytecode ) ; const nextOffset = offsetAfterBytecode + uint32Bytes ; const sequenceNumber = binToNumberUint32LE ( bin . subarray ( offsetAfterBytecode , nextOffset ) ) ; return { input : { outpointIndex , outpointTransactionHash , sequenceNumber , unlockingBytecode , } , nextOffset , } ; } ; param output export const encodeInput = ( input ) => flattenBinArray ( [ input . outpointTransactionHash . slice ( ) . reverse ( ) , numberToBinUint32LE ( input . outpointIndex ) , bigIntToBitcoinVarInt ( BigInt ( input . unlockingBytecode . length ) ) , input . unlockingBytecode , numberToBinUint32LE ( input . sequenceNumber ) , ] ) ; param inputs export const encodeInputs = ( inputs ) => flattenBinArray ( [ bigIntToBitcoinVarInt ( BigInt ( inputs . length ) ) , ... inputs . map ( encodeInput ) , ] ) ; param bin param offset export const readTransactionOutput = ( bin , offset ) => { const uint64Bytes = 8 ; const offsetAfterSatoshis = offset + uint64Bytes ; const satoshis = bin . slice ( offset , offsetAfterSatoshis ) ; const { nextOffset : offsetAfterScriptLength , value } = readBitcoinVarInt ( bin , offsetAfterSatoshis ) ; const bytecodeLength = Number ( value ) ; const nextOffset = offsetAfterScriptLength + bytecodeLength ; const lockingBytecode = bytecodeLength === 0 ? new Uint8Array ( ) : bin . slice ( offsetAfterScriptLength , nextOffset ) ; return { nextOffset , output : { lockingBytecode , satoshis , } , } ; } ; param output export const encodeOutput = ( output ) => flattenBinArray ( [ output . satoshis , bigIntToBitcoinVarInt ( BigInt ( output . lockingBytecode . length ) ) , output . lockingBytecode , ] ) ; param outputs export const encodeOutputsForTransaction = ( outputs ) => flattenBinArray ( [ bigIntToBitcoinVarInt ( BigInt ( outputs . length ) ) , ... outputs . map ( encodeOutput ) , ] ) ; param bin export const decodeTransactionUnsafe = ( bin ) : Transaction => { const uint32Bytes = 4 ; const version = binToNumberUint32LE ( bin . subarray ( 0 , uint32Bytes ) ) ; const offsetAfterVersion = uint32Bytes ; const { nextOffset : offsetAfterInputCount , value : inputCount , } = readBitcoinVarInt ( bin , offsetAfterVersion ) ; let cursor = offsetAfterInputCount ; const inputs = [ ] ; for ( let i = 0 ; i < Number ( inputCount ) ; i ++ ) { const { input , nextOffset } = readTransactionInput ( bin , cursor ) ; cursor = nextOffset ; inputs . push ( input ) ; } const { nextOffset : offsetAfterOutputCount , value : outputCount , } = readBitcoinVarInt ( bin , cursor ) ; cursor = offsetAfterOutputCount ; const outputs = [ ] ; for ( let i = 0 ; i < Number ( outputCount ) ; i ++ ) { const { output , nextOffset } = readTransactionOutput ( bin , cursor ) ; cursor = nextOffset ; outputs . push ( output ) ; } const locktime = binToNumberUint32LE ( bin . subarray ( cursor , cursor + uint32Bytes ) ) ; return { inputs , locktime , outputs , version , } ; } ; export enum TransactionDecodingError { invalidFormat = 'Transaction decoding error: message does not follow the version 1 or version 2 transaction format.' } param bin export const decodeTransaction = ( bin ) => { try { return decodeTransactionUnsafe ( bin ) ; } catch { return TransactionDecodingError . invalidFormat ; } } ; /**
 * Encode a `Transaction` using the standard P2P network format. This
 * serialization is also used when computing the transaction's hash (A.K.A.
 * "transaction ID" or "TXID").
 */ export const encodeTransaction = ( tx ) => flattenBinArray ( [ numberToBinUint32LE ( tx . version ) , encodeInputs ( tx . inputs ) , encodeOutputsForTransaction ( tx . outputs ) , numberToBinUint32LE ( tx . locktime ) , ] ) ; returns param transaction param sha256 export const getTransactionHashBE = ( sha256 , transaction ) => sha256 . hash ( sha256 . hash ( transaction ) ) ; remarks returns param transaction param sha256 export const getTransactionHashLE = ( sha256 , transaction ) => getTransactionHashBE ( sha256 , transaction ) . reverse ( ) ; param transaction param sha256 export const getTransactionHash = ( sha256 , transaction ) => binToHex ( getTransactionHashBE ( sha256 , transaction ) ) ; param inputs param sha256 export const encodeOutpoints = ( inputs ) => flattenBinArray ( inputs . map ( ( i ) => flattenBinArray ( [ i . outpointTransactionHash . slice ( ) . reverse ( ) , numberToBinUint32LE ( i . outpointIndex ) , ] ) ) ) ; param outputs export const encodeOutputsForSigning = ( outputs ) => flattenBinArray ( outputs . map ( encodeOutput ) ) ; param inputs export const encodeSequenceNumbersForSigning = ( inputs ) => flattenBinArray ( inputs . map ( ( i ) => numberToBinUint32LE ( i . sequenceNumber ) ) ) ;