@vbyte/btc-dev
Version:
Batteries-included toolset for plebian bitcoin development
156 lines (141 loc) • 4.88 kB
text/typescript
import { Buff } from '@vbyte/buff'
import { Assert } from '@vbyte/micro-lib'
import { assert_tx_data } from './validate.js'
import { COINBASE } from '@/const.js'
import {
TxInput,
TxOutput,
TxData
} from '@/types/index.js'
export function encode_tx (
txdata : TxData,
use_segwit = true
) : Buff {
// Assert the txdata is a valid tx data object.
assert_tx_data(txdata)
// Unpack the transaction data.
const { version, vin, vout, locktime } = txdata
// Create a buffer for the transaction.
const buffer : Buff[] = [ encode_tx_version(version) ]
// If the transaction is a segwit transaction,
if (use_segwit) {
// Add the segwit marker to the buffer.
buffer.push(Buff.hex('0001'))
}
// Add the inputs to the buffer.
buffer.push(encode_tx_inputs(vin))
// Add the outputs to the buffer.
buffer.push(encode_tx_outputs(vout))
// If the transaction is a segwit transaction,
if (use_segwit) {
// For each input in the transaction,
for (const input of vin) {
// Add the witness data to the buffer.
buffer.push(encode_vin_witness(input.witness))
}
}
// Add the locktime to the buffer.
buffer.push(encode_tx_locktime(locktime))
// Return the buffer as a single payload.
return Buff.join(buffer)
}
export function encode_tx_version (num : number) : Buff {
// Encode the transaction version as a 4-byte little-endian number.
return Buff.num(num, 4).reverse()
}
export function encode_txin_txid (txid : string) : Buff {
// Encode the transaction ID as a 32-byte little-endian number.
return Buff.hex(txid, 32).reverse()
}
export function encode_txin_vout (vout : number) : Buff {
// Encode the output index as a 4-byte little-endian number.
return Buff.num(vout, 4).reverse()
}
export function encode_txin_sequence (sequence : number) : Buff {
// Encode the sequence number as a 4-byte little-endian number.
return Buff.num(sequence, 4).reverse()
}
export function encode_tx_inputs (vin : TxInput[]) : Buff {
// Create a buffer for the inputs, starting with the array length.
const raw : Buff[] = [ Buff.varint(vin.length, 'le') ]
// For each input in the array,
for (const input of vin) {
// Encode the input, and add it to the buffer.
raw.push(encode_vin(input))
}
// Return the buffer as a single payload.
return Buff.join(raw)
}
export function encode_vin (txin : TxInput) : Buff {
// If the input is a coinbase,
if (txin.coinbase !== null) {
// Encode and return the coinbase as a single payload.
return Buff.join([
encode_txin_txid(COINBASE.TXID),
encode_txin_vout(COINBASE.VOUT),
encode_script_data(txin.coinbase),
encode_txin_sequence(txin.sequence)
])
} else {
// Encode and return the input as a single payload.
return Buff.join([
encode_txin_txid(txin.txid),
encode_txin_vout(txin.vout),
encode_script_data(txin.script_sig),
encode_txin_sequence(txin.sequence)
])
}
}
export function encode_vout_value (value : bigint) : Buff {
// Encode the value as an 8-byte little-endian number.
return Buff.big(value, 8).reverse()
}
export function encode_tx_outputs (vout : TxOutput[]) : Buff {
// Create a buffer for the outputs, starting with the array length.
const buffer : Buff[] = [ Buff.varint(vout.length, 'le') ]
// For each output in the array,
for (const output of vout) {
// Encode the output, and add it to the buffer.
buffer.push(encode_tx_vout(output))
}
// Return the buffer as a single payload.
return Buff.join(buffer)
}
export function encode_tx_vout (txout : TxOutput) : Buff {
// Get the value and script pubkey from the output.
const { value, script_pk } = txout
// Return the data encoded as a single payload.
return Buff.join([
encode_vout_value(value),
encode_script_data(script_pk)
])
}
export function encode_vin_witness (data : string[]) : Buff {
// Create a buffer for the witness data, starting with the array length.
const buffer : Buff[] = [ Buff.varint(data.length) ]
// For each parameter in the witness array,
for (const param of data) {
// Encode the parameter, and add it to the buffer.
buffer.push(encode_script_data(param))
}
// Return the buffer as a single payload.
return Buff.join(buffer)
}
export function encode_tx_locktime (locktime : number) : Buff {
// Encode the locktime as a 4-byte little-endian number.
return Buff.num(locktime, 4).reverse()
}
export function encode_script_data (
script : string | null
) : Buff {
// If the script is not null,
if (script !== null) {
// Assert that the script is a hex string.
Assert.is_hex(script)
// Encode the script, and add it to the buffer.
return Buff.hex(script).prefix_varint('le')
} else {
// Return a single byte of zero.
return Buff.hex('00')
}
}