// validateSigScripts executes the signature script of the tx input with the // given index, returning an error if it fails. func validateSigScript(msgtx *wire.MsgTx, idx int, pkScript []byte) error { vm, err := txscript.NewEngine(pkScript, msgtx, idx, txscript.StandardVerifyFlags, nil) if err != nil { return newError(ErrTxSigning, "cannot create script engine", err) } if err = vm.Execute(); err != nil { return newError(ErrTxSigning, "cannot validate tx signature", err) } return nil }
func validateMsgTx(msgtx *wire.MsgTx, prevOutputs []wtxmgr.Credit) error { for i := range msgtx.TxIn { vm, err := txscript.NewEngine(prevOutputs[i].PkScript, msgtx, i, txscript.StandardVerifyFlags, nil) if err != nil { return fmt.Errorf("cannot create script engine: %s", err) } if err = vm.Execute(); err != nil { return fmt.Errorf("cannot validate transaction: %s", err) } } return nil }
// TestInvalidFlagCombinations ensures the script engine returns the expected // error when disallowed flag combinations are specified. func TestInvalidFlagCombinations(t *testing.T) { t.Parallel() tests := []txscript.ScriptFlags{ txscript.ScriptVerifyCleanStack, } // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{txscript.OP_NOP}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{txscript.OP_NOP} for i, test := range tests { _, err := txscript.NewEngine(pkScript, tx, 0, test, nil) if err != txscript.ErrInvalidFlags { t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ "error: %v", i, err) } } }
func checkScripts(msg string, tx *wire.MsgTx, idx int, sigScript, pkScript []byte) error { tx.TxIn[idx].SignatureScript = sigScript vm, err := txscript.NewEngine(pkScript, tx, idx, txscript.ScriptBip16|txscript.ScriptVerifyDERSignatures, nil) if err != nil { return fmt.Errorf("failed to make script engine for %s: %v", msg, err) } err = vm.Execute() if err != nil { return fmt.Errorf("invalid script signature for %s: %v", msg, err) } return nil }
// This example demonstrates manually creating and signing a redeem transaction. func ExampleSignTxOutput() { // Ordinarily the private key would come from whatever storage mechanism // is being used, but for this example just hard code it. privKeyBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2" + "d4f8720ee63e502ee2869afab7de234b80c") if err != nil { fmt.Println(err) return } privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) pubKeyHash := coinutil.Hash160(pubKey.SerializeCompressed()) addr, err := coinutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams) if err != nil { fmt.Println(err) return } // For this example, create a fake transaction that represents what // would ordinarily be the real transaction that is being spent. It // contains a single output that pays to address in the amount of 1 BTC. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&wire.ShaHash{}, ^uint32(0)) txIn := wire.NewTxIn(prevOut, []byte{txscript.OP_0, txscript.OP_0}) originTx.AddTxIn(txIn) pkScript, err := txscript.PayToAddrScript(addr) if err != nil { fmt.Println(err) return } txOut := wire.NewTxOut(100000000, pkScript) originTx.AddTxOut(txOut) originTxHash := originTx.TxSha() // Create the transaction to redeem the fake transaction. redeemTx := wire.NewMsgTx() // Add the input(s) the redeeming transaction will spend. There is no // 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) redeemTx.AddTxIn(txIn) // Ordinarily this would contain that actual destination of the funds, // but for this example don't bother. txOut = wire.NewTxOut(0, nil) redeemTx.AddTxOut(txOut) // Sign the redeeming transaction. lookupKey := func(a coinutil.Address) (*btcec.PrivateKey, bool, error) { // Ordinarily this function would involve looking up the private // key for the provided address, but since the only thing being // signed in this example uses the address associated with the // private key from above, simply return it with the compressed // flag set since the address is using the associated compressed // public key. // // NOTE: If you want to prove the code is actually signing the // transaction properly, uncomment the following line which // intentionally returns an invalid key to sign with, which in // turn will result in a failure during the script execution // when verifying the signature. // // privKey.D.SetInt64(12345) // return privKey, true, nil } // Notice that the script database parameter is nil here since it isn't // used. It must be specified when pay-to-script-hash transactions are // being signed. sigScript, err := txscript.SignTxOutput(&chaincfg.MainNetParams, redeemTx, 0, originTx.TxOut[0].PkScript, txscript.SigHashAll, txscript.KeyClosure(lookupKey), nil, nil) if err != nil { fmt.Println(err) return } redeemTx.TxIn[0].SignatureScript = sigScript // Prove that the transaction has been validly signed by executing the // script pair. flags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures | txscript.ScriptStrictMultiSig | txscript.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, nil) if err != nil { fmt.Println(err) return } if err := vm.Execute(); err != nil { fmt.Println(err) return } fmt.Println("Transaction successfully signed") // Output: // Transaction successfully signed }
// TestCheckErrorCondition tests the execute early test in CheckErrorCondition() // since most code paths are tested elsewhere. func TestCheckErrorCondition(t *testing.T) { t.Parallel() // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{ txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_NOP, txscript.OP_TRUE, } vm, err := txscript.NewEngine(pkScript, tx, 0, 0, nil) if err != nil { t.Errorf("failed to create script: %v", err) } for i := 0; i < len(pkScript)-1; i++ { done, err := vm.Step() if err != nil { t.Errorf("failed to step %dth time: %v", i, err) return } if done { t.Errorf("finshed early on %dth time", i) return } err = vm.CheckErrorCondition(false) if err != txscript.ErrStackScriptUnfinished { t.Errorf("got unexepected error %v on %dth iteration", err, i) return } } done, err := vm.Step() if err != nil { t.Errorf("final step failed %v", err) return } if !done { t.Errorf("final step isn't done!") return } err = vm.CheckErrorCondition(false) if err != nil { t.Errorf("unexpected error %v on final check", err) } }
// TestBadPC sets the pc to a deliberately bad result then confirms that Step() // and Disasm fail correctly. func TestBadPC(t *testing.T) { t.Parallel() type pcTest struct { script, off int } pcTests := []pcTest{ { script: 2, off: 0, }, { script: 0, off: 2, }, } // tx with almost empty scripts. tx := &wire.MsgTx{ Version: 1, TxIn: []*wire.TxIn{ { PreviousOutPoint: wire.OutPoint{ Hash: wire.ShaHash([32]byte{ 0xc9, 0x97, 0xa5, 0xe5, 0x6e, 0x10, 0x41, 0x02, 0xfa, 0x20, 0x9c, 0x6a, 0x85, 0x2d, 0xd9, 0x06, 0x60, 0xa2, 0x0b, 0x2d, 0x9c, 0x35, 0x24, 0x23, 0xed, 0xce, 0x25, 0x85, 0x7f, 0xcd, 0x37, 0x04, }), Index: 0, }, SignatureScript: []uint8{txscript.OP_NOP}, Sequence: 4294967295, }, }, TxOut: []*wire.TxOut{ { Value: 1000000000, PkScript: nil, }, }, LockTime: 0, } pkScript := []byte{txscript.OP_NOP} for _, test := range pcTests { vm, err := txscript.NewEngine(pkScript, tx, 0, 0, nil) if err != nil { t.Errorf("Failed to create script: %v", err) } // set to after all scripts vm.TstSetPC(test.script, test.off) _, err = vm.Step() if err == nil { t.Errorf("Step with invalid pc (%v) succeeds!", test) continue } _, err = vm.DisasmPC() if err == nil { t.Errorf("DisasmPC with invalid pc (%v) succeeds!", test) } } }
// Test the sigscript generation for valid and invalid inputs, all // hashTypes, and with and without compression. This test creates // sigscripts to spend fake coinbase inputs, as sigscripts cannot be // created for the MsgTxs in txTests, since they come from the blockchain // and we don't have the private keys. func TestSignatureScript(t *testing.T) { t.Parallel() privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyD) nexttest: for i := range sigScriptTests { tx := wire.NewMsgTx() output := wire.NewTxOut(500, []byte{txscript.OP_RETURN}) tx.AddTxOut(output) for _ = range sigScriptTests[i].inputs { txin := wire.NewTxIn(coinbaseOutPoint, nil) tx.AddTxIn(txin) } var script []byte var err error for j := range tx.TxIn { var idx int if sigScriptTests[i].inputs[j].indexOutOfRange { t.Errorf("at test %v", sigScriptTests[i].name) idx = len(sigScriptTests[i].inputs) } else { idx = j } script, err = txscript.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 { t.Errorf("passed test '%v' incorrectly", sigScriptTests[i].name) } else { t.Errorf("failed test '%v': %v", sigScriptTests[i].name, err) } continue nexttest } if !sigScriptTests[i].inputs[j].sigscriptGenerates { // done with this test continue nexttest } tx.TxIn[j].SignatureScript = script } // If testing using a correct sigscript but for an incorrect // index, use last input script for first input. Requires > 0 // inputs for test. if sigScriptTests[i].scriptAtWrongIndex { tx.TxIn[0].SignatureScript = script sigScriptTests[i].inputs[0].inputValidates = false } // Validate tx input scripts scriptFlags := txscript.ScriptBip16 | txscript.ScriptVerifyDERSignatures for j := range tx.TxIn { vm, err := txscript.NewEngine(sigScriptTests[i]. inputs[j].txout.PkScript, tx, j, scriptFlags, nil) if err != nil { t.Errorf("cannot create script vm for test %v: %v", sigScriptTests[i].name, err) continue nexttest } err = vm.Execute() if (err == nil) != sigScriptTests[i].inputs[j].inputValidates { if err == nil { t.Errorf("passed test '%v' validation incorrectly: %v", sigScriptTests[i].name, err) } else { t.Errorf("failed test '%v' validation: %v", sigScriptTests[i].name, err) } continue nexttest } } } }
// validateHandler consumes items to validate from the internal validate channel // and returns the result of the validation on the internal result channel. It // must be run as a goroutine. func (v *txValidator) validateHandler() { out: for { select { case txVI := <-v.validateChan: // Ensure the referenced input transaction is available. txIn := txVI.txIn originTxHash := &txIn.PreviousOutPoint.Hash originTx, exists := v.txStore[*originTxHash] if !exists || originTx.Err != nil || originTx.Tx == nil { str := fmt.Sprintf("unable to find input "+ "transaction %v referenced from "+ "transaction %v", originTxHash, txVI.tx.Sha()) err := ruleError(ErrMissingTx, str) v.sendResult(err) break out } originMsgTx := originTx.Tx.MsgTx() // Ensure the output index in the referenced transaction // is available. originTxIndex := txIn.PreviousOutPoint.Index if originTxIndex >= uint32(len(originMsgTx.TxOut)) { str := fmt.Sprintf("out of bounds "+ "input index %d in transaction %v "+ "referenced from transaction %v", originTxIndex, originTxHash, txVI.tx.Sha()) err := ruleError(ErrBadTxInput, str) v.sendResult(err) break out } // Create a new script engine for the script pair. sigScript := txIn.SignatureScript pkScript := originMsgTx.TxOut[originTxIndex].PkScript vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), txVI.txInIndex, v.flags, v.sigCache) if err != nil { str := fmt.Sprintf("failed to parse input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Sha(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptMalformed, str) v.sendResult(err) break out } // Execute the script pair. if err := vm.Execute(); err != nil { str := fmt.Sprintf("failed to validate input "+ "%s:%d which references output %s:%d - "+ "%v (input script bytes %x, prev output "+ "script bytes %x)", txVI.tx.Sha(), txVI.txInIndex, originTxHash, originTxIndex, err, sigScript, pkScript) err := ruleError(ErrScriptValidation, str) v.sendResult(err) break out } // Validation succeeded. v.sendResult(nil) case <-v.quitChan: break out } } }