// AddAllInputScripts modifies transaction a transaction by adding inputs // scripts for each input. Previous output scripts being redeemed by each input // are passed in prevPkScripts and the slice length must match the number of // inputs. Private keys and redeem scripts are looked up using a SecretsSource // based on the previous output script. func AddAllInputScripts(tx *wire.MsgTx, prevPkScripts [][]byte, secrets SecretsSource) error { inputs := tx.TxIn chainParams := secrets.ChainParams() if len(inputs) != len(prevPkScripts) { return errors.New("tx.TxIn and prevPkScripts slices must " + "have equal length") } for i := range inputs { pkScript := prevPkScripts[i] sigScript := inputs[i].SignatureScript script, err := txscript.SignTxOutput(chainParams, tx, i, pkScript, txscript.SigHashAll, secrets, secrets, sigScript, chainec.ECTypeSecp256k1) if err != nil { return err } inputs[i].SignatureScript = script } 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 := 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 }
// 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 }