// generateFund is the high-level logic for funding any P2SH address with the 'go-bitcoin-multisig fund' subcommand. // Takes flagPrivateKey (private key of input Bitcoins to fund with), flagInputTx (input transaction hash of // Bitcoins to fund with), flagAmount (amount in Satoshis to send, with balance left over from input being used // as transaction fee) and flagP2SHDestination (destination P2SH multisig address which is being funded) as arguments. func generateFund(flagPrivateKey string, flagInputTx string, flagAmount int, flagP2SHDestination string) string { //Get private key as decoded raw bytes privateKey := base58check.Decode(flagPrivateKey) //In order to construct the raw transaction we need the input transaction hash, //the P2SH destination address, the number of satoshis to send, and the scriptSig //which is temporarily (prior to signing) the ScriptPubKey of the input transaction. publicKey, err := btcutils.NewPublicKey(privateKey) if err != nil { log.Fatal(err) } publicKeyHash, err := btcutils.Hash160(publicKey) if err != nil { log.Fatal(err) } tempScriptSig, err := btcutils.NewP2PKHScriptPubKey(publicKeyHash) if err != nil { log.Fatal(err) } redeemScriptHash := base58check.Decode(flagP2SHDestination) //Create our scriptPubKey scriptPubKey, err := btcutils.NewP2SHScriptPubKey(redeemScriptHash) if err != nil { log.Fatal(err) } //Create unsigned raw transaction rawTransaction, err := btcutils.NewRawTransaction(flagInputTx, flagAmount, tempScriptSig, scriptPubKey) if err != nil { log.Fatal(err) } //After completing the raw transaction, we append //SIGHASH_ALL in little-endian format to the end of the raw transaction. hashCodeType, err := hex.DecodeString("01000000") if err != nil { log.Fatal(err) } var rawTransactionBuffer bytes.Buffer rawTransactionBuffer.Write(rawTransaction) rawTransactionBuffer.Write(hashCodeType) rawTransactionWithHashCodeType := rawTransactionBuffer.Bytes() //Sign the raw transaction, and output it to the console. finalTransaction, err := signP2PKHTransaction(rawTransactionWithHashCodeType, privateKey, scriptPubKey, flagInputTx, flagAmount) if err != nil { log.Fatal(err) } finalTransactionHex := hex.EncodeToString(finalTransaction) return finalTransactionHex }
// generateSpend is the high-level logic for spending from a P2SH multisig address with the 'go-bitcoin-multisig spend' subcommand. // Takes flagPrivateKeys (comma separated list of M private keys), flagDestination (destination address of spent funds), // flagRedeemScript (redeemScript that matches P2SH script), flagInputTx (input transaction hash of P2SH input to spend) // and flagAmount (amount in Satoshis to send, with balance left over from input being used as transaction fee) as arguments. func generateSpend(flagPrivateKeys string, flagDestination string, flagRedeemScript string, flagInputTx string, flagAmount int) string { //First we create the raw transaction. //In order to construct the raw transaction we need the input transaction hash, //the destination address, the number of satoshis to send, and the scriptSig //which is temporarily (prior to signing) the redeemScript of the input P2SH transaction. //Convert redeemScript hex to raw bytes redeemScript, err := hex.DecodeString(flagRedeemScript) if err != nil { log.Fatal(err) } //Convert private-keys argument into slice of private key bytes with necessary tidying flagPrivateKeys = strings.Replace(flagPrivateKeys, "'", "\"", -1) //Replace single quotes with double since csv package only recognizes double quotes privateKeyStrings, err := csv.NewReader(strings.NewReader(flagPrivateKeys)).Read() if err != nil { log.Fatal(err) } privateKeys := make([][]byte, len(privateKeyStrings)) for i, privateKeyString := range privateKeyStrings { privateKeyString = strings.TrimSpace(privateKeyString) //Trim whitespace if privateKeyString == "" { log.Fatal("Provided private key cannot be empty.") } privateKeys[i] = base58check.Decode(privateKeyString) //Get private keys as slice of raw bytes } //Create scriptPubKey with provided destination public key publicKeyHash := base58check.Decode(flagDestination) scriptPubKey, err := btcutils.NewP2PKHScriptPubKey(publicKeyHash) if err != nil { log.Fatal(err) } //Create unsigned raw transaction //scriptSig in unsigned transaction is serialized redeemScript of input P2SH transaction. rawTransaction, err := btcutils.NewRawTransaction(flagInputTx, flagAmount, redeemScript, scriptPubKey) if err != nil { log.Fatal(err) } //After completing the raw transaction, we append //SIGHASH_ALL in little-endian format to the end of the raw transaction. hashCodeType, err := hex.DecodeString("01000000") if err != nil { log.Fatal(err) } var rawTransactionBuffer bytes.Buffer rawTransactionBuffer.Write(rawTransaction) rawTransactionBuffer.Write(hashCodeType) rawTransactionWithHashCodeType := rawTransactionBuffer.Bytes() //Sign transaction finalTransaction, err := signMultisigTransaction(rawTransactionWithHashCodeType, privateKeys, scriptPubKey, redeemScript, flagInputTx, flagAmount) if err != nil { log.Fatal(err) } finalTransactionHex := hex.EncodeToString(finalTransaction) return finalTransactionHex }
func TestNewP2PKHScriptPubKey(t *testing.T) { testPublicAddressString := "13LSqJeZBpqLHzmLkJ5mvRHiM11waShFUP" testPublicKeyHash := base58check.Decode(testPublicAddressString) testScriptPubKeyHex := "76a914199db810a3c8ae5e55c0432d2b72e55b0634f79088ac" scriptPubKey, err := NewP2PKHScriptPubKey(testPublicKeyHash) if err != nil { t.Error(err) } scriptPubKeyHex := hex.EncodeToString(scriptPubKey) if scriptPubKeyHex != testScriptPubKeyHex { testutils.CompareError(t, "P2PKH scriptPubKey different from expected script.", testScriptPubKeyHex, scriptPubKeyHex) } }