I created a 2/3 multisig script with timelock,the admin can withdraw BTC after timelock.
But the bitcoin node return an error:code:-26,message:mandatory-script-verify-flag-failed (Non-canonical DER signature).
func createMultisigLocktimeRedeemScript(pk1, pk2, pk3, pkAdmin []byte, locktime uint32) ([]byte, error) {
builder := txscript.NewScriptBuilder()
builder.AddOp(txscript.OP_IF)
// multisig
{
// add the minimum number of needed signatures
builder.AddOp(txscript.OP_2)
builder.AddData(pk1)
builder.AddData(pk2)
builder.AddData(pk3)
// add the total number of public keys in the multi-sig screipt
builder.AddOp(txscript.OP_3)
// add the check-multi-sig op-code
builder.AddOp(txscript.OP_CHECKMULTISIG)
}
builder.AddOp(txscript.OP_ELSE)
// locktime
{
var bz = make([]byte, 4)
binary.LittleEndian.PutUint32(bz, locktime) //
builder.AddData(bz)
builder.AddOp(txscript.OP_CHECKLOCKTIMEVERIFY)
builder.AddOp(txscript.OP_DROP)
}
builder.AddOp(txscript.OP_ENDIF)
// admin
{
builder.AddData(pkAdmin)
builder.AddOp(txscript.OP_CHECKSIG)
}
// redeem script is the script program in the format of []byte
redeemScript, err := builder.Script()
if err != nil {
return nil, err
}
redeemStr, err := txscript.DisasmString(redeemScript)
if err != nil {
return nil, err
}
fmt.Println("script:", redeemStr)
{
redeemHash := btcutil.Hash160(redeemScript)
scriptAddr, err := btcutil.NewAddressScriptHashFromHash(redeemHash, &chaincfg.TestNet3Params) //P2SH
if err != nil {
return nil, err
}
fmt.Printf("network %s script address: %s\n", "testnet3", scriptAddr.EncodeAddress())
}
return redeemScript, nil
}
func multisigLockTimeTx(sk1, sk2, sk3, skAdmin string, locktime uint32) error {
wif1, err := btcutil.DecodeWIF(sk1)
if err != nil {
return err
}
pk1 := wif1.PrivKey.PubKey().SerializeCompressed()
wif2, err := btcutil.DecodeWIF(sk2)
if err != nil {
return err
}
pk2 := wif2.PrivKey.PubKey().SerializeCompressed()
wif3, err := btcutil.DecodeWIF(sk3)
if err != nil {
return err
}
pk3 := wif3.PrivKey.PubKey().SerializeCompressed()
wifAdmin, err := btcutil.DecodeWIF(skAdmin)
if err != nil {
return err
}
pkAdmin := wifAdmin.PrivKey.PubKey().SerializeCompressed()
redeemScript, err := createMultisigLocktimeRedeemScript(pk1, pk2, pk3, pkAdmin, locktime)
if err != nil {
return err
}
// build tx
redeemTx := wire.NewMsgTx(wire.TxVersion)
// you should provide your UTXO hash
utxoHash, err := chainhash.NewHashFromStr("deabddd6f7ebed78fe91b71a34ec7167360a49016be039fd2b053ac02388e449")
if err != nil {
return nil
}
// and add the index of the UTXO
outPoint := wire.NewOutPoint(utxoHash, 0)
txIn := wire.NewTxIn(outPoint, nil, nil)
redeemTx.AddTxIn(txIn)
// adding the output to tx
decodedAddr, err := btcutil.DecodeAddress("tb1ph8jzyv68rwx436wtnr760tsg3ut07ml8zueuqcwww04fdmrp8ntqaqjmsp", &chaincfg.TestNet3Params)
if err != nil {
return err
}
destinationAddrByte, err := txscript.PayToAddrScript(decodedAddr)
if err != nil {
return err
}
// adding the destination address and the amount to the transaction
redeemTxOut := wire.NewTxOut(5000, destinationAddrByte)
redeemTx.AddTxOut(redeemTxOut)
sig1, err := txscript.RawTxInSignature(redeemTx, 0, redeemScript, txscript.SigHashAll, wif1.PrivKey)
if err != nil {
return err
}
_ = sig1
sig2, err := txscript.RawTxInSignature(redeemTx, 0, redeemScript, txscript.SigHashAll, wif2.PrivKey)
if err != nil {
return err
}
_ = sig2
sig3, err := txscript.RawTxInSignature(redeemTx, 0, redeemScript, txscript.SigHashAll, wif3.PrivKey)
if err != nil {
fmt.Println("got error in constructing sig3")
return err
}
_ = sig3
sigAdmin, err := txscript.RawTxInSignature(redeemTx, 0, redeemScript, txscript.SigHashAll, wifAdmin.PrivKey)
if err != nil {
fmt.Println("got error in constructing sigAdmin")
return err
}
_ = sigAdmin
fmt.Println("sig1:", hex.EncodeToString(sig1))
fmt.Println("sig2:", hex.EncodeToString(sig2))
fmt.Println("sig3:", hex.EncodeToString(sig3))
fmt.Println("sigAdmin:", hex.EncodeToString(sigAdmin))
if !isValidSignatur(sig1) {
fmt.Println("signature invalid")
}
if !isValidSignatur(sig2) {
fmt.Println("signature invalid")
}
if !isValidSignatur(sig3) {
fmt.Println("signature invalid")
}
if !isValidSignatur(sigAdmin) {
fmt.Println("signature invalid")
}
signature := txscript.NewScriptBuilder()
// multisig
{
signature.AddOp(txscript.OP_0)
signature.AddData(sig2)
signature.AddData(sig3)
signature.AddOp(txscript.OP_TRUE)
signature.AddData(redeemScript)
}
{ // admin
//signature.AddData(sigAdmin)
//signature.AddOp(txscript.OP_FALSE)
//signature.AddData(redeemScript)
}
signatureScript, err := signature.Script()
if err != nil {
// Handle the error.
return err
}
signatureScriptStr, err := txscript.DisasmString(signatureScript)
if err != nil {
return err
}
fmt.Println("signatureScript:", signatureScriptStr)
redeemTx.TxIn[0].SignatureScript = signatureScript
var signedTx bytes.Buffer
if err := redeemTx.Serialize(&signedTx); err != nil {
return err
}
fmt.Println("TxHash:", redeemTx.TxHash().String())
hexSignedTx := hex.EncodeToString(signedTx.Bytes())
fmt.Println("signedTx:", hexSignedTx)
var multi_expporer_api = []string{"https://blockstream.info/testnet/api/"}
cli := bitcoinlib.NewSmartClient(multi_expporer_api)
txid, err := cli.PostTransaction(context.Background(), hexSignedTx)
if err != nil {
return err
}
fmt.Println("txid:", txid)
return nil
}
script: OP_IF 2 03f5d066de8a9d8476f8eab2f507f2858ef0914ccf96bf3752f3f4652553c52b6d 023fe85a4a866ef5f4af2d1904e1ac3bec518542b13837ae1159d707650796bbe5 022205cdcd869d9dbec279dadceeef7777e39d1915e2f36ec0f6e3fa50bb024c5c 3 OP_CHECKMULTISIG OP_ELSE 900c1a67 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF 035d911aded72d77f12e5d200d37f968438c6c7d532b2dbf50a80516ab1014e52b OP_CHECKSIG
network testnet3 script address: 2MsWVE37DGR7adHWiZpUh7va71qkX26Vbp8
sig1: 3044022017a98dfea76746e9b7f0f785c7d92625289b0b0b5b95b47072511c24f5f3769302201033ff85eec8ce951100c3f6459a439bdb13e24acd7d59609bc2b76a7b24503c01
sig2: 3045022100fc71b478e03ce44c8148bfd19a69175ce744d15f822851ca18d4c95cca08dcec02202dd451b493e8ffe64fb672a9740b49af95cb731a60fc386612983304f656681901
sig3: 3045022100f815e290b3c83160f49b56b9c18b0834f25f1752883ae83399cdfc34646a0cdb022051a687e58f88ebac3dd0430be22d9b0e1270bcd25e19732baf612ecd113da86801
sigAdmin: 3045022100a85f88f394cabcf2659fa62f98ca56b26be5f7c4f9645c2922f6e52d01ef527d02207075c8af70d358f04b6fb1dfc0c19faaa274577ccc68f411370fbbae60aee79101
signatureScript: 0 3045022100fc71b478e03ce44c8148bfd19a69175ce744d15f822851ca18d4c95cca08dcec02202dd451b493e8ffe64fb672a9740b49af95cb731a60fc386612983304f656681901 3045022100f815e290b3c83160f49b56b9c18b0834f25f1752883ae83399cdfc34646a0cdb022051a687e58f88ebac3dd0430be22d9b0e1270bcd25e19732baf612ecd113da86801 1 63522103f5d066de8a9d8476f8eab2f507f2858ef0914ccf96bf3752f3f4652553c52b6d21023fe85a4a866ef5f4af2d1904e1ac3bec518542b13837ae1159d707650796bbe521022205cdcd869d9dbec279dadceeef7777e39d1915e2f36ec0f6e3fa50bb024c5c53ae6704900c1a67b1756821035d911aded72d77f12e5d200d37f968438c6c7d532b2dbf50a80516ab1014e52bac
TxHash: 03386f8a777ae44b2ee6ab4d494b46a55655fa7825c04dd9b7cf993086976669
signedTx: 010000000149e48823c03a052bfd39e06b01490a366771ec341ab791fe78edebf7d6ddabde00000000fd2c0100483045022100fc71b478e03ce44c8148bfd19a69175ce744d15f822851ca18d4c95cca08dcec02202dd451b493e8ffe64fb672a9740b49af95cb731a60fc386612983304f656681901483045022100f815e290b3c83160f49b56b9c18b0834f25f1752883ae83399cdfc34646a0cdb022051a687e58f88ebac3dd0430be22d9b0e1270bcd25e19732baf612ecd113da86801514c9663522103f5d066de8a9d8476f8eab2f507f2858ef0914ccf96bf3752f3f4652553c52b6d21023fe85a4a866ef5f4af2d1904e1ac3bec518542b13837ae1159d707650796bbe521022205cdcd869d9dbec279dadceeef7777e39d1915e2f36ec0f6e3fa50bb024c5c53ae6704900c1a67b1756821035d911aded72d77f12e5d200d37f968438c6c7d532b2dbf50a80516ab1014e52bacffffffff018813000000000000225120b9e42233471b8d58e9cb98fda7ae088f16ff6fe71733c061ce73ea96ec613cd600000000