// Package tx is the transaction the wallet builds and the id it is known by. // // Only the parts a signer needs are here: an unsigned transaction, its non-witness // serialisation (which is what a txid is), and the same transaction with witnesses once it has // been signed. The wire form and the txid are pinned against a transaction the rig node built, // because a serialiser that is one byte wrong is a different transaction with a different id. package tx import ( "crypto/sha256" "encoding/binary" "encoding/hex" "errors" ) // Errors the builder refuses. var ( // ErrNoInputs is a transaction that spends nothing. ErrNoInputs = errors.New("tx: a transaction needs an input") // ErrNoOutputs is a transaction that pays nobody. ErrNoOutputs = errors.New("tx: a transaction needs an output") // ErrTooMany is a count that does not fit a compact size. ErrTooMany = errors.New("tx: too many entries") // ErrScriptTooLong is an output script that cannot be length-prefixed in the usual form. ErrScriptTooLong = errors.New("tx: the output script is too long") ) // Version is the transaction version the wallet writes. BIP68 relative locktimes need 2. const Version uint32 = 2 // SequenceFinal is a sequence number that makes a locktime usable and a relative locktime // meaningless, which is what a cooperative spend wants. const SequenceFinal uint32 = 0xffffffff // Outpoint names one output of one transaction. type Outpoint struct { // Hash is the transaction id, in its wire order (the reverse of the displayed hex). Hash [32]byte // Index is the output's position in that transaction. Index uint32 } // Input spends one outpoint. type Input struct { // Outpoint is what it spends. Outpoint // ScriptSig is the unlocking script. It is empty for a segwit spend. ScriptSig []byte // Sequence is the input's sequence number. Sequence uint32 // Witness is the stack, in push order, deepest first. It is empty until signed. Witness [][]byte } // Output pays an amount to a script. type Output struct { // Value is the amount in satoshis. Value uint64 // Script is the scriptPubKey. Script []byte } // Tx is one transaction. type Tx struct { // Version is the transaction version. Version uint32 // Inputs spend. Inputs []Input // Outputs pay. Outputs []Output // LockTime is the transaction's absolute locktime. LockTime uint32 } // New starts a transaction with the wallet's version. func New() *Tx { return &Tx{Version: Version} } // AddInput spends an outpoint. func (t *Tx) AddInput(outpoint Outpoint, sequence uint32) { t.Inputs = append(t.Inputs, Input{Outpoint: outpoint, Sequence: sequence}) } // AddOutput pays a script. func (t *Tx) AddOutput(value uint64, script []byte) { t.Outputs = append(t.Outputs, Output{Value: value, Script: script}) } // Serialize is the non-witness form: version, inputs, outputs, locktime. // // This is the form a txid is hashed from, and the form a legacy sighash commits to. A segwit // transaction is serialised with witnesses for the wire but is still identified by this. func (t *Tx) Serialize() ([]byte, error) { if len(t.Inputs) == 0 { return nil, ErrNoInputs } if len(t.Outputs) == 0 { return nil, ErrNoOutputs } out := make([]byte, 0, 128) out = binary.LittleEndian.AppendUint32(out, t.Version) out = appendCompactSize(out, len(t.Inputs)) for _, input := range t.Inputs { if len(input.ScriptSig) > 0xffff { return nil, ErrScriptTooLong } out = append(out, input.Hash[:]...) out = binary.LittleEndian.AppendUint32(out, input.Index) out = appendCompactSize(out, len(input.ScriptSig)) out = append(out, input.ScriptSig...) out = binary.LittleEndian.AppendUint32(out, input.Sequence) } out = appendCompactSize(out, len(t.Outputs)) for _, output := range t.Outputs { if len(output.Script) > 0xffff { return nil, ErrScriptTooLong } out = binary.LittleEndian.AppendUint64(out, output.Value) out = appendCompactSize(out, len(output.Script)) out = append(out, output.Script...) } return binary.LittleEndian.AppendUint32(out, t.LockTime), nil } // SerializeWitness is the wire form once there are witnesses. // // The marker and flag are only written when at least one input has a witness: a transaction // with none is not a segwit transaction, and writing the marker anyway is a different // serialisation that a node would reject. func (t *Tx) SerializeWitness() ([]byte, error) { base, err := t.Serialize() if err != nil { return nil, err } any := false for _, input := range t.Inputs { if len(input.Witness) > 0 { any = true break } } if !any { return base, nil } out := make([]byte, 0, len(base)+128) out = binary.LittleEndian.AppendUint32(out, t.Version) out = append(out, 0x00, 0x01) out = appendCompactSize(out, len(t.Inputs)) for _, input := range t.Inputs { out = append(out, input.Hash[:]...) out = binary.LittleEndian.AppendUint32(out, input.Index) out = appendCompactSize(out, len(input.ScriptSig)) out = append(out, input.ScriptSig...) out = binary.LittleEndian.AppendUint32(out, input.Sequence) } out = appendCompactSize(out, len(t.Outputs)) for _, output := range t.Outputs { out = binary.LittleEndian.AppendUint64(out, output.Value) out = appendCompactSize(out, len(output.Script)) out = append(out, output.Script...) } for _, input := range t.Inputs { out = appendCompactSize(out, len(input.Witness)) for _, item := range input.Witness { out = appendCompactSize(out, len(item)) out = append(out, item...) } } return binary.LittleEndian.AppendUint32(out, t.LockTime), nil } // TxID is the double SHA-256 of the non-witness serialisation, in wire order. func (t *Tx) TxID() ([32]byte, error) { raw, err := t.Serialize() if err != nil { return [32]byte{}, err } first := sha256.Sum256(raw) return sha256.Sum256(first[:]), nil } // TxIDHex is the id as a person sees it: reversed, because the hash is stored in wire order. func (t *Tx) TxIDHex() (string, error) { id, err := t.TxID() if err != nil { return "", err } reversed := make([]byte, len(id)) for index := range id { reversed[len(id)-1-index] = id[index] } return hex.EncodeToString(reversed), nil } // Hex is the wire form, witnesses and all. func (t *Tx) Hex() (string, error) { raw, err := t.SerializeWitness() if err != nil { return "", err } return hex.EncodeToString(raw), nil } // appendCompactSize writes Bitcoin's compact size integer. func appendCompactSize(out []byte, value int) []byte { switch { case value < 0xfd: return append(out, byte(value)) case value <= 0xffff: return append(out, 0xfd, byte(value), byte(value>>8)) case value <= 0xffffffff: return append(out, 0xfe, byte(value), byte(value>>8), byte(value>>16), byte(value>>24)) } return append(out, 0xff, byte(value), byte(uint64(value)>>8), byte(uint64(value)>>16), byte(uint64(value)>>24), byte(uint64(value)>>32), byte(uint64(value)>>40), byte(uint64(value)>>48), byte(uint64(value)>>56)) }