import * as varuint from 'varuint-bitcoin' ; import * as typeforce from 'typeforce' ; import { reverseBuffer } from '../bufferutils' ; import * as bcrypto from '../crypto' ; import * as types from '../types' ; import { bitcoin as BITCOIN_NETWORK , Network } from '../networks' ; export function varSliceSize ( someScript ) { const { length } = someScript ; return varuint . encodingLength ( length ) + length ; } export function vectorSize ( someVector ) { return ( varuint . encodingLength ( someVector . length ) + someVector . reduce ( ( sum , witness ) => sum + varSliceSize ( witness ) , 0 ) ) ; } export function isCoinbaseHash ( buffer ) { typeforce ( types . Hash256bit , buffer ) ; for ( let i = 0 ; i < 32 ; ++ i ) { if ( buffer [ i ] !== 0 ) return false ; } return true ; } export const EMPTY_SCRIPT = Buffer . allocUnsafe ( 0 ) ; export interface TxOutput { script : Buffer ; value : string ; decredVersion ? : number ; } export interface TxInput { hash : Buffer ; index : number ; script : Buffer ; sequence : number ; witness : Buffer [ ] ; decredTree ? : number ; decredWitness ? : { value : string ; height : number ; blockIndex : number ; script : Buffer ; } ; } export type TransactionOptions = { nostrict ? : boolean ; network ? : Network ; } ; export class TransactionBase < S = undefined > { version = 1 ; locktime = 0 ; ins : TxInput [ ] = [ ] ; outs : TxOutput [ ] = [ ] ; specific : S | undefined ; network : Network ; type : number | undefined ; timestamp : number | undefined ; expiry : number | undefined ; constructor ( options ) { this . network = options . network || BITCOIN_NETWORK ; this . specific = options . txSpecific ; } isCoinbase ( ) { return this . ins . length === 1 && isCoinbaseHash ( this . ins [ 0 ] . hash ) ; } hasWitnesses ( ) { return this . ins . some ( x => x . witness . length !== 0 ) ; } weight ( ) { const base = this . byteLength ( false ) ; const total = this . byteLength ( true ) ; return base * 3 + total ; } virtualSize ( ) { return Math . ceil ( this . weight ( ) / 4 ) ; } byteLength ( _ALLOW_WITNESS = true ) { const hasWitnesses = _ALLOW_WITNESS && this . hasWitnesses ( ) ; return ( ( hasWitnesses ? 10 : 8 ) + ( this . timestamp ? 4 : 0 ) + varuint . encodingLength ( this . ins . length ) + varuint . encodingLength ( this . outs . length ) + this . ins . reduce ( ( sum , input ) => sum + 40 + varSliceSize ( input . script ) , 0 ) + this . outs . reduce ( ( sum , output ) => sum + 8 + varSliceSize ( output . script ) , 0 ) + ( hasWitnesses ? this . ins . reduce ( ( sum , input ) => sum + vectorSize ( input . witness ) , 0 ) : 0 ) ) ; } getHash ( forWitness = false ) { if ( forWitness && this . isCoinbase ( ) ) return Buffer . alloc ( 32 , 0 ) ; return bcrypto . hash256 ( this . toBuffer ( undefined , undefined , forWitness ) ) ; } getId ( ) { return reverseBuffer ( this . getHash ( false ) ) . toString ( 'hex' ) ; } getExtraData ( ) { } getSpecificData ( ) { return this . specific ; } toBuffer ( _buffer ? , _initialOffset ? , _ALLOW_WITNESS = true ) { return EMPTY_SCRIPT ; } toHex ( ) { return this . toBuffer ( ) . toString ( 'hex' ) ; } }