Created
January 15, 2019 11:49
-
-
Save davecgh/4eb33e4e080eb34ab5532f7d7a0a0776 to your computer and use it in GitHub Desktop.
Sample code to create and sign an offline p2sh 2-of-3 multisig transaction given WIF-encoded private keys
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/hex" | |
"fmt" | |
"github.com/decred/dcrd/chaincfg" | |
"github.com/decred/dcrd/dcrec/secp256k1" | |
"github.com/decred/dcrd/dcrutil" | |
"github.com/decred/dcrd/mempool" | |
"github.com/decred/dcrd/txscript" | |
"github.com/decred/dcrd/wire" | |
) | |
func panicOnErr(err error) { | |
if err != nil { | |
panic(err) | |
} | |
} | |
func hexToBytes(hexStr string) []byte { | |
b, err := hex.DecodeString(hexStr) | |
panicOnErr(err) | |
return b | |
} | |
func main() { | |
// Private keys. | |
wif1, err := dcrutil.DecodeWIF("PmQeFWS7F4Geh7ShLrfNTVeWGq6yXUeHmK9eUecNbfD97TZaixNxT") | |
panicOnErr(err) | |
wif2, err := dcrutil.DecodeWIF("PmQdwa6Bz94wc9b7PKyCmFcJoTsqx5vwLwQQ9kvybaJa3b9gkHh2S") | |
panicOnErr(err) | |
wif3, err := dcrutil.DecodeWIF("PmQfCoLv8vmjyc3bK6xKAtvarGVJ2nyFUH28wqt3ASxZzZN4C92x2") | |
panicOnErr(err) | |
// Create addresses for generating the script. | |
pubkey1Addr, err := dcrutil.NewAddressSecpPubKey(wif1.SerializePubKey(), &chaincfg.MainNetParams) | |
panicOnErr(err) | |
pubkey2Addr, err := dcrutil.NewAddressSecpPubKey(wif2.SerializePubKey(), &chaincfg.MainNetParams) | |
panicOnErr(err) | |
pubkey3Addr, err := dcrutil.NewAddressSecpPubKey(wif3.SerializePubKey(), &chaincfg.MainNetParams) | |
panicOnErr(err) | |
// Generate 2-of-3 multisig script. | |
redeemScript, err := txscript.MultiSigScript([]*dcrutil.AddressSecpPubKey{ | |
pubkey1Addr, pubkey2Addr, pubkey3Addr}, 2) | |
fmt.Printf("redeemScript: %x\n", redeemScript) | |
var tx wire.MsgTx | |
unsignedTxBytes := hexToBytes("0100000001d1bab9f5cb33e74e2f49d4443fefdaf487e4b56887f8b2dea1d8532aa5a555530100000000ffffffff0138199a0000000000000017a914a3c79cf163c07ac6eb15e553ee177936e4e777da87000000000000000001ffffffffffffffff00000000ffffffff00") | |
panicOnErr(tx.FromBytes(unsignedTxBytes)) | |
// Generate the p2sh public key script. | |
pkScript, err := txscript.PayToScriptHashScript(dcrutil.Hash160(redeemScript)) | |
panicOnErr(err) | |
fmt.Printf("pkScript: %x\n", pkScript) | |
// Calculate the signature hash for signing all inputs and outputs. | |
sigHashType := txscript.SigHashAll | |
hash, err := txscript.CalcSignatureHash(redeemScript, sigHashType, &tx, 0, nil) | |
panicOnErr(err) | |
fmt.Printf("Signature hash: %x\n", hash) | |
// Generate a signature. | |
privKey1 := wif1.PrivKey.(*secp256k1.PrivateKey) | |
sig1, err := privKey1.Sign(hash) | |
panicOnErr(err) | |
sig1WithHT := append(sig1.Serialize(), byte(sigHashType)) | |
fmt.Printf("Signature 1: %x\n", sig1WithHT) | |
// Generate a second signature. | |
privKey2 := wif2.PrivKey.(*secp256k1.PrivateKey) | |
sig2, err := privKey2.Sign(hash) | |
panicOnErr(err) | |
sig2WithHT := append(sig2.Serialize(), byte(sigHashType)) | |
fmt.Printf("Signature 2: %x\n", sig2WithHT) | |
// Generate the final signature script and update the transaction with it. | |
sigScript, err := txscript.NewScriptBuilder().AddData(sig1WithHT).AddData(sig2WithHT).AddData(redeemScript).Script() | |
panicOnErr(err) | |
fmt.Printf("sigScript: %x\n", sigScript) | |
tx.TxIn[0].SignatureScript = sigScript | |
// Dump the final serialized signed tx. | |
var serializedSignedTx bytes.Buffer | |
panicOnErr(tx.BtcEncode(&serializedSignedTx, 0)) | |
fmt.Printf("signed tx: %x\n", serializedSignedTx.Bytes()) | |
// Verify the transaction is signed correctly by executing its script. | |
vm, err := txscript.NewEngine(pkScript, &tx, 0, mempool.BaseStandardVerifyFlags|txscript.ScriptVerifySHA256, 0, nil) | |
panicOnErr(err) | |
panicOnErr(vm.Execute()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment