// 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, txscript.DefaultScriptVersion) 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 }
// 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: chainhash.Hash([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, 0, nil) if err != txscript.ErrInvalidFlags { t.Fatalf("TestInvalidFlagCombinations #%d unexpected "+ "error: %v", i, err) } } }
// 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: chainhash.Hash([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, 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: chainhash.Hash([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, 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) } } }
// 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 := chainec.Secp256k1.PrivKeyFromBytes(privKeyBytes) pubKeyHash := dcrutil.Hash160(pubKey.SerializeCompressed()) addr, err := dcrutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams, chainec.ECTypeSecp256k1) 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 DCR. originTx := wire.NewMsgTx() prevOut := wire.NewOutPoint(&chainhash.Hash{}, ^uint32(0), dcrutil.TxTreeRegular) 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, dcrutil.TxTreeRegular) 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 dcrutil.Address) (chainec.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, secp) 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.ScriptDiscourageUpgradableNops vm, err := txscript.NewEngine(originTx.TxOut[0].PkScript, redeemTx, 0, flags, 0) 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 }
// 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 originTxIndex := txIn.PreviousOutPoint.Index txEntry := v.utxoView.LookupEntry(originTxHash) if txEntry == 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 } // Ensure the referenced input transaction public key // script is available. pkScript := txEntry.PkScriptByIndex(originTxIndex) if pkScript == nil { str := fmt.Sprintf("unable to find unspent "+ "output %v script referenced from "+ "transaction %s:%d", txIn.PreviousOutPoint, txVI.tx.Sha(), txVI.txInIndex) err := ruleError(ErrBadTxInput, str) v.sendResult(err) break out } // Create a new script engine for the script pair. sigScript := txIn.SignatureScript version := txEntry.ScriptVersionByIndex(originTxIndex) vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), txVI.txInIndex, v.flags, version, 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 } } }
// SignVRTransaction signs a vote (SSGen) or revocation (SSRtx) // transaction. isSSGen indicates if it is an SSGen; if it's not, // it's an SSRtx. func (s *StakeStore) SignVRTransaction(waddrmgrNs walletdb.ReadBucket, msgTx *wire.MsgTx, sstx *dcrutil.Tx, isSSGen bool) error { if s.isClosed { str := "stake store is closed" return stakeStoreError(ErrStoreClosed, str, nil) } txInNumToSign := 0 hashType := txscript.SigHashAll if isSSGen { // For an SSGen tx, skip the first input as it is a stake base // and doesn't need to be signed. msgTx.TxIn[0].SignatureScript = s.Params.StakeBaseSigScript txInNumToSign = 1 } // Get the script for the OP_SSTX tagged output that we need // to sign. sstxOutScript := sstx.MsgTx().TxOut[0].PkScript // Set up our callbacks that we pass to dcrscript so it can // look up the appropriate keys and scripts by address. getKey := txscript.KeyClosure(func(addr dcrutil.Address) ( chainec.PrivateKey, bool, error) { address, err := s.Manager.Address(waddrmgrNs, addr) if err != nil { return nil, false, err } pka, ok := address.(waddrmgr.ManagedPubKeyAddress) if !ok { return nil, false, fmt.Errorf("address is not " + "a pubkey address") } key, err := pka.PrivKey() if err != nil { return nil, false, err } return key, pka.Compressed(), nil }) getScript := txscript.ScriptClosure(func( addr dcrutil.Address) ([]byte, error) { address, err := s.Manager.Address(waddrmgrNs, addr) if err != nil { return nil, err } sa, ok := address.(waddrmgr.ManagedScriptAddress) if !ok { return nil, fmt.Errorf("address is not a script" + " address") } return sa.Script() }) // Attempt to generate the signed txin. signedScript, err := txscript.SignTxOutput(s.Params, msgTx, txInNumToSign, sstxOutScript, hashType, getKey, getScript, msgTx.TxIn[txInNumToSign].SignatureScript, chainec.ECTypeSecp256k1) if err != nil { return fmt.Errorf("failed to sign ssgen or "+ "ssrtx, error: %v", err.Error()) } msgTx.TxIn[txInNumToSign].SignatureScript = signedScript // Either it was already signed or we just signed it. // Find out if it is completely satisfied or still needs more. // Decred: Needed?? flags := txscript.ScriptBip16 engine, err := txscript.NewEngine(sstxOutScript, msgTx, txInNumToSign, flags, txscript.DefaultScriptVersion, nil) if err != nil { return fmt.Errorf("failed to generate signature script engine for "+ "ssgen or ssrtx, error: %v", err.Error()) } err = engine.Execute() if err != nil { return fmt.Errorf("failed to generate correct signature script for "+ "ssgen or ssrtx: %v", err.Error()) } return nil }
// 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 version := originMsgTx.TxOut[originTxIndex].Version vm, err := txscript.NewEngine(pkScript, txVI.tx.MsgTx(), txVI.txInIndex, v.flags, version) 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 } } }