Example #1
1
// 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
}
Example #2
0
// signP2PKHTransaction signs a raw P2PKH transaction, given a private key and the scriptPubKey, inputTx and amount
// to construct the final transaction.
func signP2PKHTransaction(rawTransaction []byte, privateKey []byte, scriptPubKey []byte, inputTx string, amount int) ([]byte, error) {
	publicKey, err := btcutils.NewPublicKey(privateKey)
	if err != nil {
		return nil, err
	}
	signature, err := btcutils.NewSignature(rawTransaction, privateKey)
	if err != nil {
		return nil, err
	}
	hashCodeType, err := hex.DecodeString("01")
	if err != nil {
		return nil, err
	}
	//signatureLength is +1 to add hashCodeType
	signatureLength := byte(len(signature) + 1)
	//Create scriptSig
	var buffer bytes.Buffer
	buffer.WriteByte(signatureLength)
	buffer.Write(signature)
	buffer.WriteByte(hashCodeType[0])
	buffer.WriteByte(byte(len(publicKey)))
	buffer.Write(publicKey)
	scriptSig := buffer.Bytes()
	//Finally create transaction with actual scriptSig
	signedRawTransaction, err := btcutils.NewRawTransaction(inputTx, amount, scriptSig, scriptPubKey)
	if err != nil {
		return nil, err
	}
	return signedRawTransaction, nil
}
Example #3
0
// 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
}
Example #4
0
// signMultisigTransaction signs a raw P2PKH transaction, given slice of private keys and the scriptPubKey, inputTx,
// redeemScript and amount to construct the final transaction.
func signMultisigTransaction(rawTransaction []byte, orderedPrivateKeys [][]byte, scriptPubKey []byte, redeemScript []byte, inputTx string, amount int) ([]byte, error) {
	//Hash type SIGHASH_ALL
	hashCodeType, err := hex.DecodeString("01")
	if err != nil {
		return nil, err
	}
	//Generate signatures for each provided key
	signatures := make([][]byte, len(orderedPrivateKeys))
	for i, privateKey := range orderedPrivateKeys {
		signatures[i], err = btcutils.NewSignature(rawTransaction, privateKey)
		if err != nil {
			return nil, err
		}
	}
	//redeemScript length. To allow redeemScript > 255 bytes, we use OP_PUSHDATA2 and use two bytes to specify length
	var redeemScriptLengthBytes []byte
	var requiredOP_PUSHDATA int
	if len(redeemScript) < 255 {
		requiredOP_PUSHDATA = btcutils.OP_PUSHDATA1 //OP_PUSHDATA1 specifies next *one byte* will be length to be pushed to stack
		redeemScriptLengthBytes = []byte{byte(len(redeemScript))}
	} else {
		requiredOP_PUSHDATA = btcutils.OP_PUSHDATA2 //OP_PUSHDATA2 specifies next *two bytes* will be length to be pushed to stack
		redeemScriptLengthBytes = make([]byte, 2)
		binary.LittleEndian.PutUint16(redeemScriptLengthBytes, uint16(len(redeemScript)))
	}
	//Create scriptSig
	var buffer bytes.Buffer
	buffer.WriteByte(byte(btcutils.OP_0)) //OP_0 for Multisig off-by-one error
	for _, signature := range signatures {
		buffer.WriteByte(byte(len(signature) + 1)) //PUSH each signature. Add one for hash type byte
		buffer.Write(signature)                    // Signature bytes
		buffer.WriteByte(hashCodeType[0])          //hash type
	}
	buffer.WriteByte(byte(requiredOP_PUSHDATA)) //OP_PUSHDATA1 or OP_PUSHDATA2 depending on size of redeemScript
	buffer.Write(redeemScriptLengthBytes)       //PUSH redeemScript
	buffer.Write(redeemScript)                  //redeemScript
	scriptSig := buffer.Bytes()
	//Finally create transaction with actual scriptSig
	signedRawTransaction, err := btcutils.NewRawTransaction(inputTx, amount, scriptSig, scriptPubKey)
	if err != nil {
		return nil, err
	}
	return signedRawTransaction, nil
}