diff --git a/txscript/engine.go b/txscript/engine.go index 2b3e4cc0..0e38be3d 100644 --- a/txscript/engine.go +++ b/txscript/engine.go @@ -704,6 +704,68 @@ func NewEngine(scriptPubKey []byte, tx *wire.MsgTx, txIdx int, flags ScriptFlags vm.astack.verifyMinimalData = true } + // Check to see if we should execute in witness verification mode + // according to the set flags. We check both the pkScript, and sigScript + // here since in the case of nested p2sh, the scriptSig will be a valid + // witness program. For nested p2sh, all the bytes after the first data + // push should *exactly* match the witness program template. + if vm.hasFlag(ScriptVerifyWitness) { + // If witness evaluation is enabled, then P2SH MUST also be + // active. + if !vm.hasFlag(ScriptBip16) { + errStr := "P2SH must be enabled to do witness verification" + return nil, scriptError(ErrInvalidFlags, errStr) + } + + var witProgram []byte + + switch { + case isWitnessProgram(vm.scripts[1]): + // The scriptSig must be *empty* for all native witness + // programs, otherwise we introduce malleability. + if len(scriptSig) != 0 { + errStr := "native witness program cannot " + + "also have a signature script" + return nil, scriptError(ErrWitnessMalleated, errStr) + } + + witProgram = scriptPubKey + case len(tx.TxIn[txIdx].Witness) != 0 && vm.bip16: + // The sigScript MUST be *exactly* a single canonical + // data push of the witness program, otherwise we + // reintroduce malleability. + dataPush := vm.scripts[0][0] + if len(vm.scripts[0]) == 1 && canonicalPush(dataPush) && + IsWitnessProgram(dataPush.data) { + + witProgram = dataPush.data + } else { + errStr := "signature script for witness " + + "nested p2sh is not canonical" + return nil, scriptError(ErrWitnessMalleatedP2SH, errStr) + } + } + + if witProgram != nil { + var err error + vm.witnessVersion, vm.witnessProgram, err = ExtractWitnessProgramInfo(witProgram) + if err != nil { + return nil, err + } + vm.witness = true + } else { + // If we didn't find a witness program in either the + // pkScript or as a datapush within the sigScript, then + // there MUST NOT be any witness data associated with + // the input being validated. + if !vm.witness && len(tx.TxIn[txIdx].Witness) != 0 { + errStr := "non-witness inputs cannot have a witness" + return nil, scriptError(ErrWitnessUnexpected, errStr) + } + } + + } + vm.tx = *tx vm.txIdx = txIdx diff --git a/txscript/example_test.go b/txscript/example_test.go index ad6942b6..7bf2b3f0 100644 --- a/txscript/example_test.go +++ b/txscript/example_test.go @@ -103,7 +103,7 @@ func ExampleSignTxOutput() { // contains a single output that pays to address in the amount of 1 BTC. originTx := wire.NewMsgTx(wire.TxVersion) prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0)) - txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) + txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}, nil) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { @@ -121,7 +121,7 @@ func ExampleSignTxOutput() { // signature script at this point since it hasn't been created or signed // yet, hence nil is provided for it. prevOut = wire.NewOutPoint(&originTxHash, 0) - txIn = wire.NewTxIn(prevOut, nil) + txIn = wire.NewTxIn(prevOut, nil, nil) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, @@ -166,7 +166,7 @@ func ExampleSignTxOutput() { txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, - flags, nil) + flags, nil, nil, -1) if err != nil { fmt.Println(err) return diff --git a/txscript/hashcache.go b/txscript/hashcache.go new file mode 100644 index 00000000..f9c2caf7 --- /dev/null +++ b/txscript/hashcache.go @@ -0,0 +1,89 @@ +// Copyright (c) 2016 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "sync" + + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" +) + +// TxSigHashes houses the partial set of sighashes introduced within BIP0143. +// This partial set of sighashes may be re-used within each input across a +// transaction when validating all inputs. As a result, validation complexity +// for SigHashAll can be reduced by a polynomial factor. +type TxSigHashes struct { + HashPrevOuts chainhash.Hash + HashSequence chainhash.Hash + HashOutputs chainhash.Hash +} + +// NewTxSigHashes computes, and returns the cached sighashes of the given +// transaction. +func NewTxSigHashes(tx *wire.MsgTx) *TxSigHashes { + return &TxSigHashes{ + HashPrevOuts: calcHashPrevOuts(tx), + HashSequence: calcHashSequence(tx), + HashOutputs: calcHashOutputs(tx), + } +} + +// HashCache houses a set of partial sighashes keyed by txid. The set of partial +// sighashes are those introduced within BIP0143 by the new more efficient +// sighash digest calculation algorithm. Using this threadsafe shared cache, +// multiple goroutines can safely re-use the pre-computed partial sighashes +// speeding up validation time amongst all inputs found within a block. +type HashCache struct { + sigHashes map[chainhash.Hash]*TxSigHashes + + sync.RWMutex +} + +// NewHashCache returns a new instance of the HashCache given a maximum number +// of entries which may exist within it at anytime. +func NewHashCache(maxSize uint) *HashCache { + return &HashCache{ + sigHashes: make(map[chainhash.Hash]*TxSigHashes, maxSize), + } +} + +// AddSigHashes computes, then adds the partial sighashes for the passed +// transaction. +func (h *HashCache) AddSigHashes(tx *wire.MsgTx) { + h.Lock() + h.sigHashes[tx.TxHash()] = NewTxSigHashes(tx) + h.Unlock() +} + +// ContainsHashes returns true if the partial sighashes for the passed +// transaction currently exist within the HashCache, and false otherwise. +func (h *HashCache) ContainsHashes(txid *chainhash.Hash) bool { + h.RLock() + _, found := h.sigHashes[*txid] + h.RUnlock() + + return found +} + +// GetSigHashes possibly returns the previously cached partial sighashes for +// the passed transaction. This function also returns an additional boolean +// value indicating if the sighashes for the passed transaction were found to +// be present within the HashCache. +func (h *HashCache) GetSigHashes(txid *chainhash.Hash) (*TxSigHashes, bool) { + h.RLock() + item, found := h.sigHashes[*txid] + h.RUnlock() + + return item, found +} + +// PurgeSigHashes removes all partial sighashes from the HashCache belonging to +// the passed transaction. +func (h *HashCache) PurgeSigHashes(txid *chainhash.Hash) { + h.Lock() + delete(h.sigHashes, *txid) + h.Unlock() +} diff --git a/txscript/hashcache_test.go b/txscript/hashcache_test.go new file mode 100644 index 00000000..406bbe50 --- /dev/null +++ b/txscript/hashcache_test.go @@ -0,0 +1,182 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package txscript + +import ( + "math/rand" + "testing" + "time" + + "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" +) + +// genTestTx creates a random transaction for uses within test cases. +func genTestTx() (*wire.MsgTx, error) { + tx := wire.NewMsgTx(2) + tx.Version = rand.Int31() + + numTxins := rand.Intn(11) + for i := 0; i < numTxins; i++ { + randTxIn := wire.TxIn{ + PreviousOutPoint: wire.OutPoint{ + Index: uint32(rand.Int31()), + }, + Sequence: uint32(rand.Int31()), + } + _, err := rand.Read(randTxIn.PreviousOutPoint.Hash[:]) + if err != nil { + return nil, err + } + + tx.TxIn = append(tx.TxIn, &randTxIn) + } + + numTxouts := rand.Intn(11) + for i := 0; i < numTxouts; i++ { + randTxOut := wire.TxOut{ + Value: rand.Int63(), + PkScript: make([]byte, rand.Intn(30)), + } + if _, err := rand.Read(randTxOut.PkScript); err != nil { + return nil, err + } + tx.TxOut = append(tx.TxOut, &randTxOut) + } + + return tx, nil +} + +// TestHashCacheAddContainsHashes tests that after items have been added to the +// hash cache, the ContainsHashes method returns true for all the items +// inserted. Conversely, ContainsHashes should return false for any items +// _not_ in the hash cache. +func TestHashCacheAddContainsHashes(t *testing.T) { + t.Parallel() + + rand.Seed(time.Now().Unix()) + + cache := NewHashCache(10) + + var err error + + // First, well generate 10 random transactions for use within our + // tests. + const numTxns = 10 + txns := make([]*wire.MsgTx, numTxns) + for i := 0; i < numTxns; i++ { + txns[i], err = genTestTx() + if err != nil { + t.Fatalf("unable to generate test tx: %v", err) + } + } + + // With the transactions generated, we'll add each of them to the hash + // cache. + for _, tx := range txns { + cache.AddSigHashes(tx) + } + + // Next, we'll ensure that each of the transactions inserted into the + // cache are properly located by the ContainsHashes method. + for _, tx := range txns { + txid := tx.TxHash() + if ok := cache.ContainsHashes(&txid); !ok { + t.Fatalf("txid %v not found in cache but should be: ", + txid) + } + } + + randTx, err := genTestTx() + if err != nil { + t.Fatalf("unable to generate tx: %v", err) + } + + // Finally, we'll assert that a transaction that wasn't added to the + // cache won't be reported as being present by the ContainsHashes + // method. + randTxid := randTx.TxHash() + if ok := cache.ContainsHashes(&randTxid); ok { + t.Fatalf("txid %v wasn't inserted into cache but was found", + randTxid) + } +} + +// TestHashCacheAddGet tests that the sighahes for a particular transaction +// care properly retrieved by the GetSigHashes function. +func TestHashCacheAddGet(t *testing.T) { + t.Parallel() + + rand.Seed(time.Now().Unix()) + + cache := NewHashCache(10) + + // To start, we'll generate a random transaction and compute the set of + // sighashes for the transaction. + randTx, err := genTestTx() + if err != nil { + t.Fatalf("unable to generate tx: %v", err) + } + sigHashes := NewTxSigHashes(randTx) + + // Next, add the transaction to the hash cache. + cache.AddSigHashes(randTx) + + // The transaction inserted into the cache above should be found. + txid := randTx.TxHash() + cacheHashes, ok := cache.GetSigHashes(&txid) + if !ok { + t.Fatalf("tx %v wasn't found in cache", txid) + } + + // Finally, the sighashes retrieved should exactly match the sighash + // originally inserted into the cache. + if *sigHashes != *cacheHashes { + t.Fatalf("sighashes don't match: expected %v, got %v", + spew.Sdump(sigHashes), spew.Sdump(cacheHashes)) + } +} + +// TestHashCachePurge tests that items are able to be properly removed from the +// hash cache. +func TestHashCachePurge(t *testing.T) { + t.Parallel() + + rand.Seed(time.Now().Unix()) + + cache := NewHashCache(10) + + var err error + + // First we'll start by inserting numTxns transactions into the hash cache. + const numTxns = 10 + txns := make([]*wire.MsgTx, numTxns) + for i := 0; i < numTxns; i++ { + txns[i], err = genTestTx() + if err != nil { + t.Fatalf("unable to generate test tx: %v", err) + } + } + for _, tx := range txns { + cache.AddSigHashes(tx) + } + + // Once all the transactions have been inserted, we'll purge them from + // the hash cache. + for _, tx := range txns { + txid := tx.TxHash() + cache.PurgeSigHashes(&txid) + } + + // At this point, non of the transaction inserted into the hash cache + // should be found within the ache. + for _, tx := range txns { + txid := tx.TxHash() + if ok := cache.ContainsHashes(&txid); ok { + t.Fatalf("tx %v found in cache but should have "+ + "been purged: ", txid) + } + } +} diff --git a/txscript/opcode.go b/txscript/opcode.go index 5d8f503b..2a289d20 100644 --- a/txscript/opcode.go +++ b/txscript/opcode.go @@ -2050,7 +2050,23 @@ func opcodeCheckSig(op *parsedOpcode, vm *Engine) error { subScript = removeOpcodeByData(subScript, fullSigBytes) // Generate the signature hash based on the signature hash type. - hash := calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx) + var hash []byte + if vm.witness { + var sigHashes *TxSigHashes + if vm.hashCache != nil { + sigHashes = vm.hashCache + } else { + sigHashes = NewTxSigHashes(&vm.tx) + } + + hash, err = calcWitnessSignatureHash(subScript, sigHashes, hashType, + &vm.tx, vm.txIdx, vm.inputAmount) + if err != nil { + return err + } + } else { + hash = calcSignatureHash(subScript, hashType, &vm.tx, vm.txIdx) + } pubKey, err := btcec.ParsePubKey(pkBytes, btcec.S256()) if err != nil { @@ -2301,7 +2317,23 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error { } // Generate the signature hash based on the signature hash type. - hash := calcSignatureHash(script, hashType, &vm.tx, vm.txIdx) + var hash []byte + if vm.witness { + var sigHashes *TxSigHashes + if vm.hashCache != nil { + sigHashes = vm.hashCache + } else { + sigHashes = NewTxSigHashes(&vm.tx) + } + + hash, err = calcWitnessSignatureHash(script, sigHashes, hashType, + &vm.tx, vm.txIdx, vm.inputAmount) + if err != nil { + return err + } + } else { + hash = calcSignatureHash(script, hashType, &vm.tx, vm.txIdx) + } var valid bool if vm.sigCache != nil { diff --git a/txscript/script.go b/txscript/script.go index 45ad2372..30dc9337 100644 --- a/txscript/script.go +++ b/txscript/script.go @@ -278,6 +278,190 @@ func removeOpcodeByData(pkscript []parsedOpcode, data []byte) []parsedOpcode { } +// calcHashPrevOuts calculates a single hash of all the previous outputs +// (txid:index) referenced within the passed transaction. This calculated hash +// can be re-used when validating all inputs spending segwit outputs, with a +// signature hash type of SigHashAll. This allows validation to re-use previous +// hashing computation, reducing the complexity of validating SigHashAll inputs +// from O(N^2) to O(N). +func calcHashPrevOuts(tx *wire.MsgTx) chainhash.Hash { + var b bytes.Buffer + for _, in := range tx.TxIn { + // First write out the 32-byte transaction ID one of whose + // outputs are being referenced by this input. + b.Write(in.PreviousOutPoint.Hash[:]) + + // Next, we'll encode the index of the referenced output as a + // little endian integer. + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], in.PreviousOutPoint.Index) + b.Write(buf[:]) + } + + return chainhash.DoubleHashH(b.Bytes()) +} + +// calcHashSequence computes an aggregated hash of each of the sequence numbers +// within the inputs of the passed transaction. This single hash can be re-used +// when validating all inputs spending segwit outputs, which include signatures +// using the SigHashAll sighash type. This allows validation to re-use previous +// hashing computation, reducing the complexity of validating SigHashAll inputs +// from O(N^2) to O(N). +func calcHashSequence(tx *wire.MsgTx) chainhash.Hash { + var b bytes.Buffer + for _, in := range tx.TxIn { + var buf [4]byte + binary.LittleEndian.PutUint32(buf[:], in.Sequence) + b.Write(buf[:]) + } + + return chainhash.DoubleHashH(b.Bytes()) +} + +// calcHashOutputs computes a hash digest of all outputs created by the +// transaction encoded using the wire format. This single hash can be re-used +// when validating all inputs spending witness programs, which include +// signatures using the SigHashAll sighash type. This allows computation to be +// cached, reducing the total hashing complexity from O(N^2) to O(N). +func calcHashOutputs(tx *wire.MsgTx) chainhash.Hash { + var b bytes.Buffer + for _, out := range tx.TxOut { + wire.WriteTxOut(&b, 0, 0, out) + } + + return chainhash.DoubleHashH(b.Bytes()) +} + +// calcWitnessSignatureHash computes the sighash digest of a transaction's +// segwit input using the new, optimized digest calculation algorithm defined +// in BIP0143: https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki. +// This function makes use of pre-calculated sighash fragments stored within +// the passed HashCache to eliminate duplicate hashing computations when +// calculating the final digest, reducing the complexity from O(N^2) to O(N). +// Additionally, signatures now cover the input value of the referenced unspent +// output. This allows offline, or hardware wallets to compute the exact amount +// being spent, in addition to the final transaction fee. In the case the +// wallet if fed an invalid input amount, the real sighash will differ causing +// the produced signature to be invalid. +func calcWitnessSignatureHash(subScript []parsedOpcode, sigHashes *TxSigHashes, + hashType SigHashType, tx *wire.MsgTx, idx int, amt int64) ([]byte, error) { + + // As a sanity check, ensure the passed input index for the transaction + // is valid. + if idx > len(tx.TxIn)-1 { + return nil, fmt.Errorf("idx %d but %d txins", idx, len(tx.TxIn)) + } + + // We'll utilize this buffer throughout to incrementally calculate + // the signature hash for this transaction. + var sigHash bytes.Buffer + + // First write out, then encode the transaction's version number. + var bVersion [4]byte + binary.LittleEndian.PutUint32(bVersion[:], uint32(tx.Version)) + sigHash.Write(bVersion[:]) + + // Next write out the possibly pre-calculated hashes for the sequence + // numbers of all inputs, and the hashes of the previous outs for all + // outputs. + var zeroHash chainhash.Hash + + // If anyone can pay isn't active, then we can use the cached + // hashPrevOuts, otherwise we just write zeroes for the prev outs. + if hashType&SigHashAnyOneCanPay == 0 { + sigHash.Write(sigHashes.HashPrevOuts[:]) + } else { + sigHash.Write(zeroHash[:]) + } + + // If the sighash isn't anyone can pay, single, or none, the use the + // cached hash sequences, otherwise write all zeroes for the + // hashSequence. + if hashType&SigHashAnyOneCanPay == 0 && + hashType&sigHashMask != SigHashSingle && + hashType&sigHashMask != SigHashNone { + sigHash.Write(sigHashes.HashSequence[:]) + } else { + sigHash.Write(zeroHash[:]) + } + + txIn := tx.TxIn[idx] + + // Next, write the outpoint being spent. + sigHash.Write(txIn.PreviousOutPoint.Hash[:]) + var bIndex [4]byte + binary.LittleEndian.PutUint32(bIndex[:], txIn.PreviousOutPoint.Index) + sigHash.Write(bIndex[:]) + + if isWitnessPubKeyHash(subScript) { + // The script code for a p2wkh is a length prefix varint for + // the next 25 bytes, followed by a re-creation of the original + // p2pkh pk script. + sigHash.Write([]byte{0x19}) + sigHash.Write([]byte{OP_DUP}) + sigHash.Write([]byte{OP_HASH160}) + sigHash.Write([]byte{OP_DATA_20}) + sigHash.Write(subScript[1].data) + sigHash.Write([]byte{OP_EQUALVERIFY}) + sigHash.Write([]byte{OP_CHECKSIG}) + } else { + // For p2wsh outputs, and future outputs, the script code is + // the original script, with all code separators removed, + // serialized with a var int length prefix. + rawScript, _ := unparseScript(subScript) + wire.WriteVarBytes(&sigHash, 0, rawScript) + } + + // Next, add the input amount, and sequence number of the input being + // signed. + var bAmount [8]byte + binary.LittleEndian.PutUint64(bAmount[:], uint64(amt)) + sigHash.Write(bAmount[:]) + var bSequence [4]byte + binary.LittleEndian.PutUint32(bSequence[:], txIn.Sequence) + sigHash.Write(bSequence[:]) + + // If the current signature mode isn't single, or none, then we can + // re-use the pre-generated hashoutputs sighash fragment. Otherwise, + // we'll serialize and add only the target output index to the signature + // pre-image. + if hashType&SigHashSingle != SigHashSingle && + hashType&SigHashNone != SigHashNone { + sigHash.Write(sigHashes.HashOutputs[:]) + } else if hashType&sigHashMask == SigHashSingle && idx < len(tx.TxOut) { + var b bytes.Buffer + wire.WriteTxOut(&b, 0, 0, tx.TxOut[idx]) + sigHash.Write(chainhash.DoubleHashB(b.Bytes())) + } else { + sigHash.Write(zeroHash[:]) + } + + // Finally, write out the transaction's locktime, and the sig hash + // type. + var bLockTime [4]byte + binary.LittleEndian.PutUint32(bLockTime[:], tx.LockTime) + sigHash.Write(bLockTime[:]) + var bHashType [4]byte + binary.LittleEndian.PutUint32(bHashType[:], uint32(hashType)) + sigHash.Write(bHashType[:]) + + return chainhash.DoubleHashB(sigHash.Bytes()), nil +} + +// CalcWitnessSigHash computes the sighash digest for the specified input of +// the target transaction observing the desired sig hash type. +func CalcWitnessSigHash(script []byte, sigHashes *TxSigHashes, hType SigHashType, + tx *wire.MsgTx, idx int, amt int64) ([]byte, error) { + + parsedScript, err := parseScript(script) + if err != nil { + return nil, fmt.Errorf("cannot parse output script: %v", err) + } + + return calcWitnessSignatureHash(parsedScript, sigHashes, hType, tx, idx, + amt) +} + // calcSignatureHash will, given a script and hash type for the current script // engine instance, calculate the signature hash to be used for signing and // verification. @@ -367,8 +551,8 @@ func calcSignatureHash(script []parsedOpcode, hashType SigHashType, tx *wire.Msg // The final hash is the double sha256 of both the serialized modified // transaction and the hash type (encoded as a 4-byte little-endian // value) appended. - wbuf := bytes.NewBuffer(make([]byte, 0, txCopy.SerializeSize()+4)) - txCopy.Serialize(wbuf) + wbuf := bytes.NewBuffer(make([]byte, 0, txCopy.SerializeSizeStripped()+4)) + txCopy.SerializeNoWitness(wbuf) binary.Write(wbuf, binary.LittleEndian, hashType) return chainhash.DoubleHashB(wbuf.Bytes()) } diff --git a/txscript/sign.go b/txscript/sign.go index 03d95c25..09ba8d1f 100644 --- a/txscript/sign.go +++ b/txscript/sign.go @@ -14,6 +14,61 @@ import ( "github.com/btcsuite/btcutil" ) +// RawTxInWitnessSignature returns the serialized ECDA signature for the input +// idx of the given transaction, with the hashType appended to it. This +// function is identical to RawTxInSignature, however the signature generated +// signs a new sighash digest defined in BIP0143. +func RawTxInWitnessSignature(tx *wire.MsgTx, sigHashes *TxSigHashes, idx int, + amt int64, subScript []byte, hashType SigHashType, + key *btcec.PrivateKey) ([]byte, error) { + + parsedScript, err := parseScript(subScript) + if err != nil { + return nil, fmt.Errorf("cannot parse output script: %v", err) + } + + hash, err := calcWitnessSignatureHash(parsedScript, sigHashes, hashType, tx, + idx, amt) + if err != nil { + return nil, err + } + + signature, err := key.Sign(hash) + if err != nil { + return nil, fmt.Errorf("cannot sign tx input: %s", err) + } + + return append(signature.Serialize(), byte(hashType)), nil +} + +// WitnessSignature creates an input witness stack for tx to spend BTC sent +// from a previous output to the owner of privKey using the p2wkh script +// template. The passed transaction must contain all the inputs and outputs as +// dictated by the passed hashType. The signature generated observes the new +// transaction digest algorithm defined within BIP0143. +func WitnessSignature(tx *wire.MsgTx, sigHashes *TxSigHashes, idx int, amt int64, + subscript []byte, hashType SigHashType, privKey *btcec.PrivateKey, + compress bool) (wire.TxWitness, error) { + + sig, err := RawTxInWitnessSignature(tx, sigHashes, idx, amt, subscript, + hashType, privKey) + if err != nil { + return nil, err + } + + pk := (*btcec.PublicKey)(&privKey.PublicKey) + var pkData []byte + if compress { + pkData = pk.SerializeCompressed() + } else { + pkData = pk.SerializeUncompressed() + } + + // A witness script is actually a stack, so we return an array of byte + // slices here, rather than a single byte slice. + return wire.TxWitness{sig, pkData}, nil +} + // RawTxInSignature returns the serialized ECDSA signature for the input idx of // the given transaction, with hashType appended to it. func RawTxInSignature(tx *wire.MsgTx, idx int, subScript []byte, diff --git a/wire/bench_test.go b/wire/bench_test.go index c329a347..f6637d42 100644 --- a/wire/bench_test.go +++ b/wire/bench_test.go @@ -236,7 +236,7 @@ func BenchmarkReadTxOut(b *testing.B) { func BenchmarkWriteTxOut(b *testing.B) { txOut := blockOne.Transactions[0].TxOut[0] for i := 0; i < b.N; i++ { - writeTxOut(ioutil.Discard, 0, 0, txOut) + WriteTxOut(ioutil.Discard, 0, 0, txOut) } } diff --git a/wire/msgtx.go b/wire/msgtx.go index a22118e6..a7784433 100644 --- a/wire/msgtx.go +++ b/wire/msgtx.go @@ -707,7 +707,7 @@ func (msg *MsgTx) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error } for _, to := range msg.TxOut { - err = writeTxOut(w, pver, msg.Version, to) + err = WriteTxOut(w, pver, msg.Version, to) if err != nil { return err } @@ -981,9 +981,12 @@ func readTxOut(r io.Reader, pver uint32, version int32, to *TxOut) error { return err } -// writeTxOut encodes to into the bitcoin protocol encoding for a transaction +// WriteTxOut encodes to into the bitcoin protocol encoding for a transaction // output (TxOut) to w. -func writeTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error { +// +// NOTE: This function is exported in order to allow txscript to compute the +// new sighashes for witness transactions (BIP0143). +func WriteTxOut(w io.Writer, pver uint32, version int32, to *TxOut) error { err := binarySerializer.PutUint64(w, littleEndian, uint64(to.Value)) if err != nil { return err