From 6e2ba386dd36cd3f17be86d898337f4db82ce6ef Mon Sep 17 00:00:00 2001 From: "Owain G. Ainsworth" Date: Thu, 17 Apr 2014 23:15:30 +0100 Subject: [PATCH] Add an entypoint to sign and verify a transaction input. This handles merging in of previous scripts, also. Add tests for the important paths. ok @davecgh --- internal_test.go | 12 - script.go | 398 ++++++++++++- script_test.go | 1421 ++++++++++++++++++++++++++++++++++++++++++++- test_coverage.txt | 216 +++---- 4 files changed, 1885 insertions(+), 162 deletions(-) diff --git a/internal_test.go b/internal_test.go index ba299f0d..7b86d1be 100644 --- a/internal_test.go +++ b/internal_test.go @@ -5,14 +5,12 @@ package btcscript import ( - "crypto/ecdsa" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/conformal/btcutil" "github.com/conformal/btcwire" - "io" "io/ioutil" "strconv" "strings" @@ -47,16 +45,6 @@ func (s *Script) TstSetPC(script, off int) { s.scriptoff = off } -// TstSignatureScriptCustomReader allows the test modules to test the internal -// function signatureScriptCustomReader. -func TstSignatureScriptCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int, - subscript []byte, hashType byte, privkey *ecdsa.PrivateKey, - compress bool) ([]byte, error) { - - return signatureScriptCustomReader(reader, tx, idx, subscript, - hashType, privkey, compress) -} - // Internal tests for opcodde parsing with bad data templates. func TestParseOpcode(t *testing.T) { fakemap := make(map[byte]*opcode) diff --git a/script.go b/script.go index e9e471d6..21e91089 100644 --- a/script.go +++ b/script.go @@ -1089,27 +1089,10 @@ func MultiSigScript(pubkeys []*btcutil.AddressPubKey, nrequired int) ([]byte, er // compress. This format must match the same format used to generate // the payment address, or the script validation will fail. func SignatureScript(tx *btcwire.MsgTx, idx int, subscript []byte, hashType byte, privkey *ecdsa.PrivateKey, compress bool) ([]byte, error) { - return signatureScriptCustomReader(rand.Reader, tx, idx, subscript, - hashType, privkey, compress) -} - -// This function exists so we can test ecdsa.Sign's error for an invalid -// reader. -func signatureScriptCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int, - subscript []byte, hashType byte, privkey *ecdsa.PrivateKey, - compress bool) ([]byte, error) { - - parsedScript, err := parseScript(subscript) + sig, err := signTxOutput(tx, idx, subscript, hashType, privkey) if err != nil { - return nil, fmt.Errorf("cannot parse output script: %v", err) + return nil, err } - hash := calcScriptHash(parsedScript, hashType, tx, idx) - r, s, err := ecdsa.Sign(reader, privkey, hash) - if err != nil { - return nil, fmt.Errorf("cannot sign tx input: %s", err) - } - ecSig := btcec.Signature{R: r, S: s} - sig := append(ecSig.Serialize(), hashType) pk := (*btcec.PublicKey)(&privkey.PublicKey) var pkData []byte @@ -1122,6 +1105,383 @@ func signatureScriptCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int, return NewScriptBuilder().AddData(sig).AddData(pkData).Script(), nil } +func signTxOutput(tx *btcwire.MsgTx, idx int, subScript []byte, hashType byte, + key *ecdsa.PrivateKey) ([]byte, error) { + + return signTxOutputCustomReader(rand.Reader, tx, idx, subScript, + hashType, key) +} + +func signTxOutputCustomReader(reader io.Reader, tx *btcwire.MsgTx, idx int, + subScript []byte, hashType byte, key *ecdsa.PrivateKey) ([]byte, error) { + parsedScript, err := parseScript(subScript) + if err != nil { + return nil, fmt.Errorf("cannot parse output script: %v", err) + } + hash := calcScriptHash(parsedScript, hashType, tx, idx) + r, s, err := ecdsa.Sign(reader, key, hash) + if err != nil { + return nil, fmt.Errorf("cannot sign tx input: %s", err) + } + + return append((&btcec.Signature{R: r, S: s}).Serialize(), hashType), nil +} + +func p2pkSignatureScript(tx *btcwire.MsgTx, idx int, subScript []byte, hashType byte, privkey *ecdsa.PrivateKey) ([]byte, error) { + sig, err := signTxOutput(tx, idx, subScript, hashType, privkey) + if err != nil { + return nil, err + } + + return NewScriptBuilder().AddData(sig).Script(), nil +} + +// signMultiSig signs as many of the outputs in the provided multisig script as +// possible. It returns the generated script and a boolean if the script fulfils +// the contract (i.e. nrequired signatures are provided). Since it is arguably +// legal to not be able to sign any of the outputs, no error is returned. +func signMultiSig(tx *btcwire.MsgTx, idx int, subScript []byte, hashType byte, + addresses []btcutil.Address, nRequired int, kdb KeyDB) ([]byte, bool) { + // We start with a single OP_FALSE to work around the (now standard) + // but in the reference implementation that causes a spurious pop at + // the end of OP_CHECKMULTISIG. + builder := NewScriptBuilder().AddOp(OP_FALSE) + signed := 0 + for _, addr := range addresses { + key, _, err := kdb.GetKey(addr) + if err != nil { + continue + } + sig, err := signTxOutput(tx, idx, subScript, hashType, key) + if err != nil { + continue + } + + builder.AddData(sig) + signed++ + if signed == nRequired { + break + } + + } + + return builder.Script(), signed == nRequired +} + +func sign(net btcwire.BitcoinNet, tx *btcwire.MsgTx, idx int, subScript []byte, + hashType byte, kdb KeyDB, sdb ScriptDB) ([]byte, ScriptClass, + []btcutil.Address, int, error) { + + class, addresses, nrequired, err := ExtractPkScriptAddrs(subScript, net) + if err != nil { + return nil, NonStandardTy, nil, 0, err + } + + switch class { + case PubKeyTy: + // look up key for address + key, _, err := kdb.GetKey(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + script, err := p2pkSignatureScript(tx, idx, subScript, hashType, + key) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case PubKeyHashTy: + // look up key for address + key, compressed, err := kdb.GetKey(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + script, err := SignatureScript(tx, idx, subScript, hashType, + key, compressed) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case ScriptHashTy: + script, err := sdb.GetScript(addresses[0]) + if err != nil { + return nil, class, nil, 0, err + } + + return script, class, addresses, nrequired, nil + case MultiSigTy: + script, _ := signMultiSig(tx, idx, subScript, hashType, + addresses, nrequired, kdb) + return script, class, addresses, nrequired, nil + case NullDataTy: + return nil, class, nil, 0, + errors.New("can't sign NULLDATA transactions") + default: + return nil, class, nil, 0, + errors.New("can't sign unknown transactions") + } +} + +// mergeScripts merges sigScript and prevScript assuming they are both +// partial solutions for pkScript spending output idx of tx. class, addresses +// and nrequired are the result of extracting the addresses from pkscript. +// The return value is the best effort merging of the two scripts. Calling this +// function with addresses, class and nrequired that do not match pkScript is +// an error and results in undefined behaviour. +func mergeScripts(net btcwire.BitcoinNet, tx *btcwire.MsgTx, idx int, + pkScript []byte, class ScriptClass, addresses []btcutil.Address, + nRequired int, sigScript, prevScript []byte) []byte { + + // TODO(oga) the scripthash and multisig paths here are overly + // inefficient in that they will recompute already known data. + // some internal refactoring could probably make this avoid needless + // extra calculations. + switch class { + case ScriptHashTy: + // Remove the last push in the script and then recurse. + // this could be a lot less inefficient. + sigPops, err := parseScript(sigScript) + if err != nil || len(sigPops) == 0 { + return prevScript + } + prevPops, err := parseScript(prevScript) + if err != nil || len(prevPops) == 0 { + return sigScript + } + + // assume that script in sigPops is the correct one, we just + // made it. + script := sigPops[len(sigPops)-1].data + + // We already know this information somewhere up the stack. + class, addresses, nrequired, err := + ExtractPkScriptAddrs(script, net) + + // regenerate scripts. + sigScript, _ := unparseScript(sigPops) + prevScript, _ := unparseScript(prevPops) + + // Merge + mergedScript := mergeScripts(net, tx, idx, script, class, + addresses, nrequired, sigScript, prevScript) + + // Reappend the script and return the result. + builder := NewScriptBuilder() + builder.script = mergedScript + builder.AddData(script) + return builder.Script() + case MultiSigTy: + return mergeMultiSig(tx, idx, addresses, nRequired, pkScript, + sigScript, prevScript) + + // It doesn't actualy make sense to merge anything other than multiig + // and scripthash (because it could contain multisig). Everything else + // has either zero signature, can't be spent, or has a single signature + // which is either present or not. The other two cases are handled + // above. In the conflict case here we just assume the longest is + // correct (this matches behaviour of the reference implementation). + default: + if len(sigScript) > len(prevScript) { + return sigScript + } + return prevScript + } +} + +// mergeMultiSig combines the two signature scripts sigScript and prevScript +// that both provide signatures for pkScript in output idx of tx. addresses +// and nRequired should be the results from extracting the addresses from +// pkScript. Since this function is internal only we assume that the arguments +// have come from other functions internally and thus are all consistent with +// each other, behaviour is undefined if this contract is broken. +func mergeMultiSig(tx *btcwire.MsgTx, idx int, addresses []btcutil.Address, + nRequired int, pkScript, sigScript, prevScript []byte) []byte { + + // This is an internal only function and we already parsed this script + // as ok for multisig (this is how we got here), so if this fails then + // all assumptions are broken and who knows which way is up? + pkPops, _ := parseScript(pkScript) + + sigPops, err := parseScript(sigScript) + if err != nil || len(sigPops) == 0 { + return prevScript + } + + prevPops, err := parseScript(prevScript) + if err != nil || len(prevPops) == 0 { + return sigScript + } + + // Convenience function to avoid duplication. + extractSigs := func(pops []parsedOpcode, sigs [][]byte) [][]byte { + for _, pop := range pops { + if len(pop.data) != 0 { + sigs = append(sigs, pop.data) + } + } + return sigs + } + + possibleSigs := make([][]byte, 0, len(sigPops)+len(prevPops)) + possibleSigs = extractSigs(sigPops, possibleSigs) + possibleSigs = extractSigs(prevPops, possibleSigs) + + // Now we need to match the signatures to pubkeys, the only real way to + // do that is to try to verify them all and match it to the pubkey + // that verifies it. we then can go through the addresses in order + // to build our script. Anything that doesn't parse or doesn't verify we + // throw away. + addrToSig := make(map[string][]byte) +sigLoop: + for _, sig := range possibleSigs { + + // can't have a valid signature that doesn't at least have a + // hashtype, in practise it is even longer than this. but + // that'll be checked next. + if len(sig) < 1 { + continue + } + tSig := sig[:len(sig)-1] + hashType := sig[len(sig)-1] + + pSig, err := btcec.ParseDERSignature(tSig, btcec.S256()) + if err != nil { + continue + } + + // We have to do this each round since hash types may vary + // between signatures and so the hash will vary. We can, + // however, assume no sigs etc are in the script since that + // would make the transaction nonstandard and thus not + // MultiSigTy, so we just need to hash the full thing. + hash := calcScriptHash(pkPops, hashType, tx, idx) + + for _, addr := range addresses { + // All multisig addresses should be pubkey addreses + // it is an error to call this internal function with + // bad input. + pkaddr := addr.(*btcutil.AddressPubKey) + + pubKey := pkaddr.PubKey() + + // If it matches we put it in the map. We only + // can take one signature per public key so if we + // already have one, we can throw this away. + if ecdsa.Verify(pubKey.ToECDSA(), hash, + pSig.R, pSig.S) { + + aStr := addr.EncodeAddress() + if _, ok := addrToSig[aStr]; !ok { + addrToSig[aStr] = sig + } + continue sigLoop + } + } + } + + // Extra opcode to handle the extra arg consumed (due to previous bugs + // in the reference implementation). + builder := NewScriptBuilder().AddOp(OP_FALSE) + doneSigs := 0 + // This assumes that addresses are in the same order as in the script. + for _, addr := range addresses { + sig, ok := addrToSig[addr.EncodeAddress()] + if !ok { + continue + } + builder.AddData(sig) + doneSigs++ + if doneSigs == nRequired { + break + } + } + + // padding for missing ones. + for i := doneSigs; i < nRequired; i++ { + builder.AddOp(OP_0) + } + + return builder.Script() +} + +// KeyDB is an interface type provided to SignTxOutput, it encapsulates +// any user state required to get the private keys for an address. +type KeyDB interface { + GetKey(btcutil.Address) (*ecdsa.PrivateKey, bool, error) +} + +// KeyClosure implements ScriptDB with a closure +type KeyClosure func(btcutil.Address) (*ecdsa.PrivateKey, bool, error) + +// GetKey implements KeyDB by returning the result of calling the closure +func (kc KeyClosure) GetKey(address btcutil.Address) (*ecdsa.PrivateKey, + bool, error) { + return kc(address) +} + +// ScriptDB is an interface type provided to SignTxOutput, it encapsulates +// any user state required to get the scripts for an pay-to-script-hash address. +type ScriptDB interface { + GetScript(btcutil.Address) ([]byte, error) +} + +// ScriptClosure implements ScriptDB with a closure +type ScriptClosure func(btcutil.Address) ([]byte, error) + +// GetScript implements ScriptDB by returning the result of calling the closure +func (sc ScriptClosure) GetScript(address btcutil.Address) ([]byte, error) { + return sc(address) +} + +// SignTxOutput signs output idx of the given tx to resolve the script given in +// pkScript with a signature type of hashType. Any keys required will be +// looked up by calling getKey() with the string of the given address. +// Any pay-to-script-hash signatures will be similarly lookedu p by calling +// getScript. If previousScript is provided then the results in previousScript +// will be merged in a type-dependant manner with the newly generated. +// signature script. +func SignTxOutput(net btcwire.BitcoinNet, tx *btcwire.MsgTx, idx int, + pkScript []byte, hashType byte, kdb KeyDB, sdb ScriptDB, + previousScript []byte) ([]byte, error) { + + sigScript, class, addresses, nrequired, err := sign(net, tx, idx, + pkScript, hashType, kdb, sdb) + if err != nil { + return nil, err + } + + if class == ScriptHashTy { + // TODO keep the sub addressed and pass down to merge. + realSigScript, _, _, _, err := sign(net, tx, idx, sigScript, + hashType, kdb, sdb) + if err != nil { + return nil, err + } + + // This is a bad thing. Append the p2sh script as the last + // push in the script. + builder := NewScriptBuilder() + builder.script = realSigScript + builder.AddData(sigScript) + + sigScript = builder.Script() + // TODO keep a copy of the script for merging. + } + + // Merge scripts. with any previous data, if any. + mergedScript := mergeScripts(net, tx, idx, pkScript, class, addresses, + nrequired, sigScript, previousScript) + if err != nil { + return nil, err + } + + return mergedScript, nil +} + // expectedInputs returns the number of arguments required by a script. // If the script is of unnown type such that the number can not be determined // then -1 is returned. We are an internal function and thus assume that class diff --git a/script_test.go b/script_test.go index abebc26b..54b98b3f 100644 --- a/script_test.go +++ b/script_test.go @@ -7,6 +7,9 @@ package btcscript_test import ( "bytes" "crypto/ecdsa" + "crypto/rand" + "errors" + "fmt" "github.com/conformal/btcec" "github.com/conformal/btcscript" "github.com/conformal/btcutil" @@ -2463,7 +2466,6 @@ type TstInput struct { sigscriptGenerates bool inputValidates bool indexOutOfRange bool - invalidReader bool } var coinbaseOutPoint = &btcwire.OutPoint{ @@ -2694,19 +2696,6 @@ var SigScriptTests = []TstSigScript{ compress: false, scriptAtWrongIndex: true, }, - { - name: "invalid reader", - inputs: []TstInput{ - { - txout: btcwire.NewTxOut(coinbaseVal, uncompressedPkScript), - invalidReader: true, - sigscriptGenerates: false, - }, - }, - hashtype: btcscript.SigHashAll, - compress: false, - scriptAtWrongIndex: true, - }, } // Test the sigscript generation for valid and invalid inputs, all @@ -2749,20 +2738,10 @@ nexttest: } else { idx = j } - if SigScriptTests[i].inputs[j].invalidReader { - script, err = btcscript.TstSignatureScriptCustomReader( - new(bytes.Buffer), tx, idx, - SigScriptTests[i].inputs[j].txout.PkScript, - SigScriptTests[i].hashtype, - privkey, - SigScriptTests[i].compress) - } else { - script, err = btcscript.SignatureScript(tx, idx, - SigScriptTests[i].inputs[j].txout.PkScript, - SigScriptTests[i].hashtype, - privkey, - SigScriptTests[i].compress) - } + script, err = btcscript.SignatureScript(tx, idx, + SigScriptTests[i].inputs[j].txout.PkScript, + SigScriptTests[i].hashtype, privkey, + SigScriptTests[i].compress) if (err == nil) != SigScriptTests[i].inputs[j].sigscriptGenerates { if err == nil { @@ -3190,3 +3169,1389 @@ func TestMultiSigScript(t *testing.T) { } } } + +func signAndCheck(msg string, tx *btcwire.MsgTx, idx int, pkScript []byte, + hashType byte, kdb btcscript.KeyDB, sdb btcscript.ScriptDB, + previousScript []byte) error { + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, idx, pkScript, hashType, + kdb, sdb, []byte{}) + if err != nil { + return fmt.Errorf("failed to sign output %s: %v", msg, err) + } + + return checkScripts(msg, tx, idx, sigScript, pkScript) +} + +func checkScripts(msg string, tx *btcwire.MsgTx, idx int, + sigScript, pkScript []byte) error { + engine, err := btcscript.NewScript(sigScript, pkScript, idx, tx, + btcscript.ScriptBip16| + btcscript.ScriptCanonicalSignatures) + if err != nil { + return fmt.Errorf("failed to make script engine for %s: %v", + msg, err) + } + + err = engine.Execute() + if err != nil { + return fmt.Errorf("invalid script signature for %s: %v", msg, + err) + } + + return nil +} + +type addressToKey struct { + key *ecdsa.PrivateKey + compressed bool +} + +func mkGetKey(keys map[string]addressToKey) btcscript.KeyDB { + if keys == nil { + return btcscript.KeyClosure(func(addr btcutil.Address) (*ecdsa.PrivateKey, + bool, error) { + return nil, false, errors.New("nope") + }) + } + return btcscript.KeyClosure(func(addr btcutil.Address) (*ecdsa.PrivateKey, + bool, error) { + a2k, ok := keys[addr.EncodeAddress()] + if !ok { + return nil, false, errors.New("nope") + } + return a2k.key, a2k.compressed, nil + }) +} + +func mkGetScript(scripts map[string][]byte) btcscript.ScriptDB { + if scripts == nil { + return btcscript.ScriptClosure(func(addr btcutil.Address) ( + []byte, error) { + return nil, errors.New("nope") + }) + } + return btcscript.ScriptClosure(func(addr btcutil.Address) ([]byte, + error) { + script, ok := scripts[addr.EncodeAddress()] + if !ok { + return nil, errors.New("nope") + } + return script, nil + }) +} + +func TestSignTxOutput(t *testing.T) { + // make key + // make script based on key. + // sign with magic pixie dust. + hashTypes := []byte{ + btcscript.SigHashOld, // no longer used but should act like all + btcscript.SigHashAll, + btcscript.SigHashNone, + btcscript.SigHashSingle, + btcscript.SigHashAll | btcscript.SigHashAnyOneCanPay, + btcscript.SigHashNone | btcscript.SigHashAnyOneCanPay, + btcscript.SigHashSingle | btcscript.SigHashAnyOneCanPay, + } + tx := &btcwire.MsgTx{ + Version: 1, + TxIn: []*btcwire.TxIn{ + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 0, + }, + Sequence: 4294967295, + }, + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 1, + }, + Sequence: 4294967295, + }, + &btcwire.TxIn{ + PreviousOutpoint: btcwire.OutPoint{ + Hash: btcwire.ShaHash{}, + Index: 2, + }, + Sequence: 4294967295, + }, + }, + TxOut: []*btcwire.TxOut{ + &btcwire.TxOut{ + Value: 1, + }, + &btcwire.TxOut{ + Value: 2, + }, + &btcwire.TxOut{ + Value: 3, + }, + }, + LockTime: 0, + } + + // Pay to Pubkey Hash (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, pkScript, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) (merging with correct) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, pkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, pkScript, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, pkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to PubKey (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, pkScript, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, pkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to PubKey (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + if err := signAndCheck(msg, tx, i, pkScript, hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, pkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(nil), sigScript) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, pkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // As before, but with p2sh now. + // Pay to Pubkey Hash (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptPkScript, + hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (uncompressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + break + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptPkScript, + hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to Pubkey Hash (compressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKeyHash( + btcutil.Hash160(pk), btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to PubKey (uncompressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptPkScript, + hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (uncompressed) with duplicate merge + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeUncompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, false}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Pay to PubKey (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptPkScript, + hashType, + mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Pay to PubKey (compressed) + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk := (*btcec.PublicKey)(&key.PublicKey). + SerializeCompressed() + address, err := btcutil.NewAddressPubKey(pk, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.PayToAddrScript(address) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // by the above loop, this should be valid, now sign + // again and merge. + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address.EncodeAddress(): {key, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s a "+ + "second time: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, scriptPkScript) + if err != nil { + t.Errorf("twice signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Basic Multisig + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key1, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + if err := signAndCheck(msg, tx, i, scriptPkScript, + hashType, + mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}); err != nil { + t.Error(err) + break + } + } + } + + // Two part multisig, sign with one key then the other. + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key1, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // Only 1 out of 2 signed, this *should* fail. + if checkScripts(msg, tx, i, sigScript, + scriptPkScript) == nil { + t.Errorf("part signed script valid for %s", msg) + break + } + + // Sign with the other key and merge + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), sigScript) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, err) + break + } + + err = checkScripts(msg, tx, i, sigScript, + scriptPkScript) + if err != nil { + t.Errorf("fully signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } + + // Two part multisig, sign with one key then both, check key dedup + // correctly. + for _, hashType := range hashTypes { + for i := range tx.TxIn { + msg := fmt.Sprintf("%d:%d", hashType, i) + + key1, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey for %s: %v", + msg, err) + break + } + + pk1 := (*btcec.PublicKey)(&key1.PublicKey). + SerializeCompressed() + address1, err := btcutil.NewAddressPubKey(pk1, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address for %s: %v", + msg, err) + break + } + + key2, err := ecdsa.GenerateKey(btcec.S256(), + rand.Reader) + if err != nil { + t.Errorf("failed to make privkey 2 for %s: %v", + msg, err) + break + } + + pk2 := (*btcec.PublicKey)(&key2.PublicKey). + SerializeCompressed() + address2, err := btcutil.NewAddressPubKey(pk2, + btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make address 2 for %s: %v", + msg, err) + break + } + + pkScript, err := btcscript.MultiSigScript( + []*btcutil.AddressPubKey{address1, address2}, + 2) + if err != nil { + t.Errorf("failed to make pkscript "+ + "for %s: %v", msg, err) + } + + scriptAddr, err := btcutil.NewAddressScriptHash( + pkScript, btcwire.TestNet3) + if err != nil { + t.Errorf("failed to make p2sh addr for %s: %v", + msg, err) + break + } + + scriptPkScript, err := btcscript.PayToAddrScript( + scriptAddr) + if err != nil { + t.Errorf("failed to make script pkscript for "+ + "%s: %v", msg, err) + break + } + + sigScript, err := btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), []byte{}) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, + err) + break + } + + // Only 1 out of 2 signed, this *should* fail. + if checkScripts(msg, tx, i, sigScript, + scriptPkScript) == nil { + t.Errorf("part signed script valid for %s", msg) + break + } + + // Sign with the other key and merge + sigScript, err = btcscript.SignTxOutput( + btcwire.TestNet3, tx, i, scriptPkScript, + hashType, mkGetKey(map[string]addressToKey{ + address1.EncodeAddress(): {key1, true}, + address2.EncodeAddress(): {key2, true}, + }), mkGetScript(map[string][]byte{ + scriptAddr.EncodeAddress(): pkScript, + }), sigScript) + if err != nil { + t.Errorf("failed to sign output %s: %v", msg, err) + break + } + + // Now we should pass. + err = checkScripts(msg, tx, i, sigScript, + scriptPkScript) + if err != nil { + t.Errorf("fully signed script invalid for "+ + "%s: %v", msg, err) + break + } + } + } +} diff --git a/test_coverage.txt b/test_coverage.txt index 51ea818b..5c92a4bd 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -1,8 +1,9 @@ github.com/conformal/btcscript/opcode.go opcodeCheckMultiSig 100.00% (60/60) +github.com/conformal/btcscript/script.go calcScriptHash 100.00% (39/39) github.com/conformal/btcscript/script.go Script.Step 100.00% (37/37) -github.com/conformal/btcscript/opcode.go opcodeCheckSig 100.00% (29/29) github.com/conformal/btcscript/script.go parseScriptTemplate 100.00% (29/29) +github.com/conformal/btcscript/opcode.go opcodeCheckSig 100.00% (29/29) github.com/conformal/btcscript/address.go ExtractPkScriptAddrs 100.00% (27/27) github.com/conformal/btcscript/script.go CalcScriptInfo 100.00% (25/25) github.com/conformal/btcscript/script.go NewScript 100.00% (23/23) @@ -12,160 +13,169 @@ github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddData 100.00% github.com/conformal/btcscript/opcode.go parsedOpcode.disabled 100.00% (17/17) github.com/conformal/btcscript/opcode.go parsedOpcode.print 100.00% (16/16) github.com/conformal/btcscript/stack.go Stack.nipN 100.00% (15/15) -github.com/conformal/btcscript/script.go signatureScriptCustomReader 100.00% (15/15) github.com/conformal/btcscript/stack.go fromInt 100.00% (14/14) -github.com/conformal/btcscript/script.go isMultiSig 100.00% (13/13) github.com/conformal/btcscript/opcode.go parsedOpcode.exec 100.00% (13/13) -github.com/conformal/btcscript/opcode.go opcodeWithin 100.00% (13/13) github.com/conformal/btcscript/script.go GetPreciseSigOpCount 100.00% (13/13) -github.com/conformal/btcscript/opcode.go opcodeIf 100.00% (11/11) -github.com/conformal/btcscript/opcode.go opcodeNotIf 100.00% (11/11) -github.com/conformal/btcscript/script.go typeOfScript 100.00% (11/11) +github.com/conformal/btcscript/opcode.go opcodeWithin 100.00% (13/13) +github.com/conformal/btcscript/script.go isMultiSig 100.00% (13/13) github.com/conformal/btcscript/script.go PayToAddrScript 100.00% (11/11) -github.com/conformal/btcscript/opcode.go opcodeMax 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeLessThanOrEqual 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeLessThan 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeNumEqual 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeBoolOr 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeBoolAnd 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeGreaterThanOrEqual 100.00% (10/10) -github.com/conformal/btcscript/stack.go Stack.Tuck 100.00% (10/10) -github.com/conformal/btcscript/script.go getSigOpCount 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeGreaterThan 100.00% (10/10) -github.com/conformal/btcscript/opcode.go opcodeNumNotEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeNotIf 100.00% (11/11) +github.com/conformal/btcscript/opcode.go opcodeIf 100.00% (11/11) +github.com/conformal/btcscript/script.go typeOfScript 100.00% (11/11) github.com/conformal/btcscript/opcode.go opcodeMin 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeBoolAnd 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeBoolOr 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeNumEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeNumNotEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeLessThan 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeGreaterThan 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeLessThanOrEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeGreaterThanOrEqual 100.00% (10/10) +github.com/conformal/btcscript/opcode.go opcodeMax 100.00% (10/10) +github.com/conformal/btcscript/script.go getSigOpCount 100.00% (10/10) +github.com/conformal/btcscript/stack.go Stack.Tuck 100.00% (10/10) +github.com/conformal/btcscript/stack.go Stack.RotN 100.00% (9/9) +github.com/conformal/btcscript/script.go SignatureScript 100.00% (9/9) +github.com/conformal/btcscript/script.go Script.CheckErrorCondition 100.00% (9/9) github.com/conformal/btcscript/stack.go Stack.OverN 100.00% (9/9) github.com/conformal/btcscript/stack.go Stack.SwapN 100.00% (9/9) github.com/conformal/btcscript/script.go DisasmString 100.00% (9/9) -github.com/conformal/btcscript/script.go Script.CheckErrorCondition 100.00% (9/9) -github.com/conformal/btcscript/stack.go Stack.RotN 100.00% (9/9) -github.com/conformal/btcscript/script.go Script.Execute 100.00% (8/8) -github.com/conformal/btcscript/stack.go Stack.DupN 100.00% (8/8) -github.com/conformal/btcscript/script.go MultiSigScript 100.00% (8/8) -github.com/conformal/btcscript/opcode.go opcodeEqual 100.00% (8/8) -github.com/conformal/btcscript/opcode.go opcodeSub 100.00% (8/8) github.com/conformal/btcscript/opcode.go opcodeAdd 100.00% (8/8) -github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddInt64 100.00% (7/7) -github.com/conformal/btcscript/opcode.go opcode0NotEqual 100.00% (7/7) -github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddUint64 100.00% (7/7) -github.com/conformal/btcscript/stack.go Stack.DropN 100.00% (7/7) +github.com/conformal/btcscript/stack.go Stack.DupN 100.00% (8/8) +github.com/conformal/btcscript/opcode.go opcodeSub 100.00% (8/8) +github.com/conformal/btcscript/opcode.go opcodeEqual 100.00% (8/8) +github.com/conformal/btcscript/script.go MultiSigScript 100.00% (8/8) +github.com/conformal/btcscript/script.go Script.Execute 100.00% (8/8) github.com/conformal/btcscript/opcode.go opcodeNot 100.00% (7/7) -github.com/conformal/btcscript/opcode.go opcodeEndif 100.00% (6/6) -github.com/conformal/btcscript/opcode.go parsedOpcode.conditional 100.00% (6/6) -github.com/conformal/btcscript/opcode.go opcodeElse 100.00% (6/6) -github.com/conformal/btcscript/opcode.go opcodeVerify 100.00% (6/6) +github.com/conformal/btcscript/stack.go Stack.DropN 100.00% (7/7) +github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddInt64 100.00% (7/7) +github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddUint64 100.00% (7/7) +github.com/conformal/btcscript/opcode.go opcode0NotEqual 100.00% (7/7) github.com/conformal/btcscript/opcode.go opcodeIfDup 100.00% (6/6) -github.com/conformal/btcscript/stack.go Stack.RollN 100.00% (5/5) -github.com/conformal/btcscript/opcode.go opcodeToAltStack 100.00% (5/5) -github.com/conformal/btcscript/opcode.go opcodeFromAltStack 100.00% (5/5) +github.com/conformal/btcscript/opcode.go parsedOpcode.conditional 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcodeEndif 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcodeVerify 100.00% (6/6) +github.com/conformal/btcscript/opcode.go opcodeElse 100.00% (6/6) +github.com/conformal/btcscript/script.go Script.DisasmScript 100.00% (5/5) +github.com/conformal/btcscript/script.go removeOpcodeByData 100.00% (5/5) +github.com/conformal/btcscript/script.go removeOpcode 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeRoll 100.00% (5/5) +github.com/conformal/btcscript/script.go Script.validPC 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeSize 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcode1Add 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcode1Sub 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeNegate 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeAbs 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeFromAltStack 100.00% (5/5) +github.com/conformal/btcscript/stack.go Stack.RollN 100.00% (5/5) +github.com/conformal/btcscript/opcode.go opcodeToAltStack 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeRipemd160 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeSha1 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeSha256 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeHash160 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodeHash256 100.00% (5/5) github.com/conformal/btcscript/opcode.go opcodePick 100.00% (5/5) -github.com/conformal/btcscript/script.go Script.validPC 100.00% (5/5) -github.com/conformal/btcscript/script.go Script.DisasmScript 100.00% (5/5) -github.com/conformal/btcscript/script.go removeOpcode 100.00% (5/5) -github.com/conformal/btcscript/script.go removeOpcodeByData 100.00% (5/5) github.com/conformal/btcscript/stack.go Stack.PickN 100.00% (5/5) +github.com/conformal/btcscript/stack.go Stack.PopBool 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeNumEqualVerify 100.00% (4/4) +github.com/conformal/btcscript/stack.go asBool 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeCheckSigVerify 100.00% (4/4) +github.com/conformal/btcscript/opcode.go parsedOpcode.alwaysIllegal 100.00% (4/4) github.com/conformal/btcscript/opcode.go opcodeCheckMultiSigVerify 100.00% (4/4) +github.com/conformal/btcscript/script.go Script.curPC 100.00% (4/4) +github.com/conformal/btcscript/script.go Script.DisasmPC 100.00% (4/4) +github.com/conformal/btcscript/script.go GetScriptClass 100.00% (4/4) +github.com/conformal/btcscript/script.go @1320:17 100.00% (4/4) +github.com/conformal/btcscript/script.go getStack 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PeekByteArray 100.00% (4/4) +github.com/conformal/btcscript/opcode.go opcodeEqualVerify 100.00% (4/4) +github.com/conformal/btcscript/script.go IsPayToScriptHash 100.00% (4/4) +github.com/conformal/btcscript/stack.go Stack.PeekInt 100.00% (4/4) github.com/conformal/btcscript/script.go isNullData 100.00% (4/4) github.com/conformal/btcscript/script.go isPushOnly 100.00% (4/4) -github.com/conformal/btcscript/opcode.go opcodeEqualVerify 100.00% (4/4) -github.com/conformal/btcscript/script.go getStack 100.00% (4/4) -github.com/conformal/btcscript/opcode.go opcodeNumEqualVerify 100.00% (4/4) github.com/conformal/btcscript/stack.go Stack.PeekBool 100.00% (4/4) -github.com/conformal/btcscript/script.go GetScriptClass 100.00% (4/4) -github.com/conformal/btcscript/stack.go asBool 100.00% (4/4) -github.com/conformal/btcscript/stack.go Stack.PeekInt 100.00% (4/4) -github.com/conformal/btcscript/stack.go Stack.PeekByteArray 100.00% (4/4) -github.com/conformal/btcscript/stack.go Stack.PopBool 100.00% (4/4) github.com/conformal/btcscript/stack.go Stack.PopInt 100.00% (4/4) -github.com/conformal/btcscript/opcode.go opcodeCheckSigVerify 100.00% (4/4) -github.com/conformal/btcscript/script.go Script.curPC 100.00% (4/4) -github.com/conformal/btcscript/opcode.go parsedOpcode.alwaysIllegal 100.00% (4/4) -github.com/conformal/btcscript/script.go IsPayToScriptHash 100.00% (4/4) -github.com/conformal/btcscript/script.go Script.DisasmPC 100.00% (4/4) github.com/conformal/btcscript/script.go ScriptClass.String 100.00% (3/3) +github.com/conformal/btcscript/script.go isSmallInt 100.00% (3/3) +github.com/conformal/btcscript/script.go setStack 100.00% (3/3) github.com/conformal/btcscript/stack.go fromBool 100.00% (3/3) github.com/conformal/btcscript/script.go asSmallInt 100.00% (3/3) -github.com/conformal/btcscript/script.go setStack 100.00% (3/3) -github.com/conformal/btcscript/script.go isSmallInt 100.00% (3/3) -github.com/conformal/btcscript/opcode.go opcodeFalse 100.00% (2/2) -github.com/conformal/btcscript/stack.go Stack.Depth 100.00% (2/2) -github.com/conformal/btcscript/script.go GetSigOpCount 100.00% (2/2) -github.com/conformal/btcscript/opcode.go opcodePushData 100.00% (2/2) -github.com/conformal/btcscript/opcode.go calcHash 100.00% (2/2) -github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddOp 100.00% (2/2) -github.com/conformal/btcscript/opcode.go opcodeDepth 100.00% (2/2) -github.com/conformal/btcscript/opcode.go opcode1Negate 100.00% (2/2) -github.com/conformal/btcscript/stack.go Stack.NipN 100.00% (2/2) -github.com/conformal/btcscript/opcode.go opcodeCodeSeparator 100.00% (2/2) -github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.Reset 100.00% (2/2) github.com/conformal/btcscript/opcode.go opcodeN 100.00% (2/2) -github.com/conformal/btcscript/script.go Script.SetAltStack 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeNop 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeOver 100.00% (1/1) -github.com/conformal/btcscript/script.go isPubkey 100.00% (1/1) -github.com/conformal/btcscript/script.go isPubkeyHash 100.00% (1/1) -github.com/conformal/btcscript/script.go isScriptHash 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeNip 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeDup 100.00% (1/1) -github.com/conformal/btcscript/script.go parseScript 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode2Swap 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode2Rot 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode2Over 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeDepth 100.00% (2/2) +github.com/conformal/btcscript/stack.go Stack.NipN 100.00% (2/2) +github.com/conformal/btcscript/stack.go Stack.Depth 100.00% (2/2) +github.com/conformal/btcscript/opcode.go calcHash 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodeCodeSeparator 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcode1Negate 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodePushData 100.00% (2/2) +github.com/conformal/btcscript/opcode.go opcodeFalse 100.00% (2/2) +github.com/conformal/btcscript/script.go GetSigOpCount 100.00% (2/2) +github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.AddOp 100.00% (2/2) +github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.Reset 100.00% (2/2) +github.com/conformal/btcscript/log.go newLogClosure 100.00% (1/1) github.com/conformal/btcscript/log.go DisableLog 100.00% (1/1) -github.com/conformal/btcscript/script.go Script.disasm 100.00% (1/1) -github.com/conformal/btcscript/script.go Script.subScript 100.00% (1/1) -github.com/conformal/btcscript/log.go init 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode3Dup 100.00% (1/1) -github.com/conformal/btcscript/script.go Script.GetStack 100.00% (1/1) -github.com/conformal/btcscript/script.go Script.SetStack 100.00% (1/1) -github.com/conformal/btcscript/script.go Script.GetAltStack 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode2Dup 100.00% (1/1) -github.com/conformal/btcscript/script.go payToPubKeyHashScript 100.00% (1/1) -github.com/conformal/btcscript/script.go payToScriptHashScript 100.00% (1/1) -github.com/conformal/btcscript/script.go payToPubKeyScript 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcode2Drop 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeReturn 100.00% (1/1) -github.com/conformal/btcscript/script.go SignatureScript 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeInvalid 100.00% (1/1) -github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.Script 100.00% (1/1) -github.com/conformal/btcscript/scriptbuilder.go NewScriptBuilder 100.00% (1/1) -github.com/conformal/btcscript/opcode.go opcodeReserved 100.00% (1/1) +github.com/conformal/btcscript/opcode.go calcHash160 100.00% (1/1) github.com/conformal/btcscript/stack.go Stack.PushByteArray 100.00% (1/1) github.com/conformal/btcscript/stack.go Stack.PushInt 100.00% (1/1) github.com/conformal/btcscript/stack.go Stack.PushBool 100.00% (1/1) github.com/conformal/btcscript/stack.go Stack.PopByteArray 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Swap 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeNop 100.00% (1/1) +github.com/conformal/btcscript/log.go init 100.00% (1/1) github.com/conformal/btcscript/opcode.go opcodeTuck 100.00% (1/1) github.com/conformal/btcscript/opcode.go opcodeSwap 100.00% (1/1) github.com/conformal/btcscript/opcode.go opcodeRot 100.00% (1/1) -github.com/conformal/btcscript/opcode.go calcHash160 100.00% (1/1) -github.com/conformal/btcscript/opcode.go init 100.00% (1/1) -github.com/conformal/btcscript/log.go newLogClosure 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeOver 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeNip 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeReturn 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeDup 100.00% (1/1) github.com/conformal/btcscript/opcode.go opcodeDrop 100.00% (1/1) -github.com/conformal/btcscript/script.go calcScriptHash 97.44% (38/39) +github.com/conformal/btcscript/opcode.go opcode2Drop 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Dup 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.disasm 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.subScript 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeReserved 100.00% (1/1) +github.com/conformal/btcscript/script.go parseScript 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.GetStack 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.SetStack 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.GetAltStack 100.00% (1/1) +github.com/conformal/btcscript/script.go Script.SetAltStack 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode3Dup 100.00% (1/1) +github.com/conformal/btcscript/opcode.go init 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcodeInvalid 100.00% (1/1) +github.com/conformal/btcscript/script.go payToPubKeyHashScript 100.00% (1/1) +github.com/conformal/btcscript/script.go payToScriptHashScript 100.00% (1/1) +github.com/conformal/btcscript/script.go payToPubKeyScript 100.00% (1/1) +github.com/conformal/btcscript/script.go signTxOutput 100.00% (1/1) +github.com/conformal/btcscript/script.go isScriptHash 100.00% (1/1) +github.com/conformal/btcscript/script.go KeyClosure.GetKey 100.00% (1/1) +github.com/conformal/btcscript/script.go ScriptClosure.GetScript 100.00% (1/1) +github.com/conformal/btcscript/scriptbuilder.go NewScriptBuilder 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Over 100.00% (1/1) +github.com/conformal/btcscript/script.go isPubkeyHash 100.00% (1/1) +github.com/conformal/btcscript/script.go isPubkey 100.00% (1/1) +github.com/conformal/btcscript/opcode.go opcode2Rot 100.00% (1/1) +github.com/conformal/btcscript/scriptbuilder.go ScriptBuilder.Script 100.00% (1/1) +github.com/conformal/btcscript/script.go mergeScripts 95.00% (19/20) github.com/conformal/btcscript/script.go canonicalPush 92.86% (13/14) -github.com/conformal/btcscript/script.go unparseScript 85.71% (6/7) +github.com/conformal/btcscript/script.go signMultiSig 92.86% (13/14) +github.com/conformal/btcscript/script.go mergeMultiSig 88.10% (37/42) +github.com/conformal/btcscript/script.go signTxOutputCustomReader 87.50% (7/8) github.com/conformal/btcscript/script.go expectedInputs 85.71% (6/7) +github.com/conformal/btcscript/script.go unparseScript 85.71% (6/7) +github.com/conformal/btcscript/script.go SignTxOutput 80.00% (12/15) +github.com/conformal/btcscript/script.go p2pkSignatureScript 75.00% (3/4) github.com/conformal/btcscript/script.go IsPushOnlyScript 75.00% (3/4) github.com/conformal/btcscript/script.go HasCanonicalPushes 71.43% (5/7) +github.com/conformal/btcscript/script.go sign 69.23% (18/26) github.com/conformal/btcscript/log.go SetLogWriter 0.00% (0/10) github.com/conformal/btcscript/script.go CalcMultiSigStats 0.00% (0/8) github.com/conformal/btcscript/script.go @577:34 0.00% (0/6) github.com/conformal/btcscript/script.go @565:34 0.00% (0/4) github.com/conformal/btcscript/script.go @610:34 0.00% (0/3) -github.com/conformal/btcscript/log.go UseLogger 0.00% (0/1) github.com/conformal/btcscript/log.go logClosure.String 0.00% (0/1) github.com/conformal/btcscript/opcode.go @1803:33 0.00% (0/1) github.com/conformal/btcscript/opcode.go opcodeDisabled 0.00% (0/1) -github.com/conformal/btcscript --------------------------- 96.23% (1073/1115) +github.com/conformal/btcscript/log.go UseLogger 0.00% (0/1) +github.com/conformal/btcscript -------------------------- 95.10% (1183/1244)