signrawtransactionwithwallet fails with signed non-wallet inputs and breaks the existing signatures #26385

issue andreabonel opened this issue on October 25, 2022
  1. andreabonel commented at 1:29 AM on October 25, 2022: none

    The error returned by the RPC is: Unable to sign input, invalid stack size (possibly missing key)

    It seems that this rpc alters the scriptSig of all of the inputs that were already signed.

    The problem is very similar to #21151, which is already solved.

    Here's an example of the broken signatures after signing:

    --- 0_before.txt	2022-10-24 17:46:31.105518559 -0300
    +++ 1_after.txt	2022-10-24 17:46:31.146517797 -0300
    @@ -1,18 +1,18 @@
     {
    -  "txid": "7107a82fd2d783d32e2926bf91b5dea5908203a3d81f8bb67b267f9aeab3c16d",
    -  "hash": "7107a82fd2d783d32e2926bf91b5dea5908203a3d81f8bb67b267f9aeab3c16d",
    +  "txid": "b4b09eebed1f427f7ae80004ca1e55688567bc8e01af7608311c7d0a31ffcf83",
    +  "hash": "685100e81d34140941b3c15af9598e0d2ffdbee92b3706c82561ff2232c94cbc",
       "version": 1,
    -  "size": 406,
    -  "vsize": 406,
    -  "weight": 1624,
    +  "size": 373,
    +  "vsize": 291,
    +  "weight": 1162,
       "locktime": 0,
       "vin": [
         {
           "txid": "c2afc743b00acb7a9a9cccc9e9b2751f0ae17206a9492dc0537ec72f44c7f6e0",
           "vout": 1,
           "scriptSig": {
    -        "asm": "0 30440220201b3d70770c2b325c8cac16593a4606cdcb29c094f67ec700d6aaadfdcc3bdf022057b70804b24d66664f1a82f7936773fbc3bcb15740a0e7b772ddf7bfe486b6fe[SINGLE|ANYONECANPAY] 3044022034eaf58c896885fc1597a848d268ad618e621b33be77bed67bcfa6180611ac31022073f947c7d2ebd81c1d52236030dd1a5d6b178c9361786ff9d6d3ef9740be1c76[SINGLE|ANYONECANPAY] 522103d28e83bfc9d2ab30f26f7b62ac4b64a932272ba124c6c4d29e901eaf8724aab7210366dc61fb53bce09777fe7c4766a55af9649cc26deb21b204ae6bf4e93413a46c2102439d6a6bc199460e4e0a6e5fdcb87162cafa5cae6ee4845d23d9dc5e840ffdb153ae",
    -        "hex": "004730440220201b3d70770c2b325c8cac16593a4606cdcb29c094f67ec700d6aaadfdcc3bdf022057b70804b24d66664f1a82f7936773fbc3bcb15740a0e7b772ddf7bfe486b6fe83473044022034eaf58c896885fc1597a848d268ad618e621b33be77bed67bcfa6180611ac31022073f947c7d2ebd81c1d52236030dd1a5d6b178c9361786ff9d6d3ef9740be1c76834c69522103d28e83bfc9d2ab30f26f7b62ac4b64a932272ba124c6c4d29e901eaf8724aab7210366dc61fb53bce09777fe7c4766a55af9649cc26deb21b204ae6bf4e93413a46c2102439d6a6bc199460e4e0a6e5fdcb87162cafa5cae6ee4845d23d9dc5e840ffdb153ae"
    +        "asm": "0 0 522103d28e83bfc9d2ab30f26f7b62ac4b64a932272ba124c6c4d29e901eaf8724aab7210366dc61fb53bce09777fe7c4766a55af9649cc26deb21b204ae6bf4e93413a46c2102439d6a6bc199460e4e0a6e5fdcb87162cafa5cae6ee4845d23d9dc5e840ffdb153ae",
    +        "hex": "00004c69522103d28e83bfc9d2ab30f26f7b62ac4b64a932272ba124c6c4d29e901eaf8724aab7210366dc61fb53bce09777fe7c4766a55af9649cc26deb21b204ae6bf4e93413a46c2102439d6a6bc199460e4e0a6e5fdcb87162cafa5cae6ee4845d23d9dc5e840ffdb153ae"
           },
           "sequence": 0
         },
    @@ -23,6 +23,10 @@
             "asm": "",
             "hex": ""
           },
    +      "txinwitness": [
    +        "304402202dc3e510dc053dcdd29be701441337c93f6923686cc5ba4a915dbc17073dd26c02207aa8b76a447bde52b4cb1146781745826692d4484327f2a78a73818183e5f37f01",
    +        "0379d702db49e91dd63127278c06ed99ef05b43d15f2583bb28b4f0e9b49b9f50c"
    +      ],
           "sequence": 0
         }
       ],
    

    I've prepared a bash script that reproduces it (I used hal to merge the transactions):

    #!/bin/bash
    
    #set -x
    set -e
    
    shopt -s expand_aliases
    alias b-dae="~/opt/bitcoin/bin/bitcoind -datadir=$PWD -daemon=1"
    alias b-cli="~/opt/bitcoin/bin/bitcoin-cli -datadir=$PWD"
    
    declare -a privkeys
    declare -a pubkeys
    declare -a multisigs
    declare -a addresses
    declare -a redeemScripts
    declare -a txs
    declare -a scriptPubKeys
    declare -a vouts
    declare -a mergetxs
    
    #parameters for the multisig addreesses
    addresstype=legacy
    multisig_n=2
    multisig_m=3
    multisig_count=1
    #create the keypairs
    for i in $(seq 0 $((multisig_count * multisig_m - 1)));do
        KEYPAIR=$(hal key generate --regtest 2>/dev/null)
        privkeys[$i]=$(echo $KEYPAIR | jq -r .wif_private_key)
        pubkeys[$i]=$(echo $KEYPAIR | jq -r .public_key)
    done
    
    b-cli stop && sleep 5 || true
    rm -rf regtest
    b-dae
    b-cli -rpcwait createwallet ""
    echo
    
    #create the multisig addresses
    for i in $(seq 0 $((multisig_count - 1)));do
        mspubkeys='['
        sep=
        for j in $(seq $((i * multisig_m)) $((i * multisig_m + multisig_m -1)));do
            mspubkeys+="$sep\"${pubkeys[$j]}\""
            sep=','
        done
        mspubkeys+=']'
        echo b-cli createmultisig ${multisig_n} "${mspubkeys}" $addresstype
        multisigs[$i]=$(b-cli -rpcwait createmultisig ${multisig_n} "${mspubkeys}" $addresstype)
        addresses[$i]=$(echo "${multisigs[$i]}" | jq -r '.address')
        redeemScripts[$i]=$(echo "${multisigs[$i]}" | jq -r '.redeemScript')
    done
    
    echo "pubkeys: ${pubkeys[@]}"
    echo "privkeys: ${privkeys[@]}"
    #generate enough blocks to unlock the reward
    DEST=$(b-cli getnewaddress)
    b-cli generatetoaddress 101 $DEST >/dev/null 2>&1
    #send 1 btc to each multisig address
    for i in $(seq 0 $((multisig_count - 1)));do
        txs[$i]=$(b-cli sendtoaddress "${addresses[$i]}" 1)
        scriptPubKeys[$i]=$(b-cli decoderawtransaction $(b-cli getrawtransaction ${txs[$i]})| jq -r ".vout[] .scriptPubKey | select(.address == \"${addresses[$i]}\") .hex")
        vouts[$i]=$(b-cli decoderawtransaction $(b-cli getrawtransaction ${txs[$i]})| jq -r ".vout[] | select(.scriptPubKey.address == \"${addresses[$i]}\") .n")
        #echo ${txs[$i]} ${scriptPubKeys[$i]}
        b-cli generatetoaddress 1 $DEST >/dev/null 2>&1
    done
    echo
    
    #each multisig sends its coins to a new wallet (destwallet) and signs the transaction using signrawtransactionwithkey
    echo createwallet destwallet
    b-cli createwallet destwallet
    destaddress=$(b-cli -rpcwallet=destwallet getnewaddress)
    echo unloadwallet destwallet
    b-cli unloadwallet destwallet
    for i in $(seq 0 $((multisig_count - 1)));do
        echo "Sending utxo ${txs[$i]} to final address $destaddress"
        tx=$(b-cli createrawtransaction "[{\"txid\":\"${txs[$i]}\",\"vout\":${vouts[$i]}}]" "[{\"$destaddress\":1}]")
    
        prevtxs="[{\"txid\":\"${txs[$i]}\",\"vout\":${vouts[$i]},\"scriptPubKey\":\"${scriptPubKeys[$i]}\",\"redeemScript\":\"${redeemScripts[$i]}\",\"amount\":1}]"
        #sign with keys one by one
        for j in $(seq $((i * multisig_m)) $((i * multisig_m + multisig_n - 1)));do
            tx=$(b-cli signrawtransactionwithkey $tx "[\"${privkeys[$j]}\"]" "$prevtxs" "SINGLE|ANYONECANPAY")
            tx=$(echo $tx|jq -r '.hex')
        done
        mergetxs[$i]=$tx
    done
    echo
    
    finalinputs=""
    finaloutputs=""
    
    #create the transaction to provide the funds to pay for the previous transactions
    fee_input=$(b-cli listunspent|jq -r '.[0]')
    fee_input_amount=$(echo $fee_input|jq -r '.amount')
    fee=0.0001
    change_amount=$(bc -l <<< "$fee_input_amount - $fee")
    changeaddress=$(b-cli getnewaddress)
    change_tx=$(b-cli createrawtransaction "[$fee_input]" "[{\"$changeaddress\":$change_amount}]")
    mergetxs[$multisig_count]=$change_tx
    
    sepin=""
    sepout=""
    
    #prepare a Json with the inputs, and another one with the outputs
    for i in $(seq 0 $((multisig_count)));do
        decodedtx=$(hal tx decode ${mergetxs[$i]})
        inputno=$(echo $decodedtx| jq -r ".inputs | length -1")
        for k in $(seq 0 $inputno);do
            input=$(echo $decodedtx| jq -r ".inputs[$k] | del(.sequence)")
            finalinputs+="$sepin$input"
            sepin=","
        done
        outputno=$(echo $decodedtx| jq -r ".outputs | length -1")
        for k in $(seq 0 $outputno);do
            output=$(echo $decodedtx| jq -r ".outputs[$k] | del(.n)")
            finaloutputs+="$sepout$output"
            sepout=","
        done
    done
    
    fulltx="
    {
      \"version\": 1,
      \"locktime\": 0,
      \"inputs\": [ $finalinputs ],
      \"outputs\": [ $finaloutputs ]
    }
    "
    echo $fulltx|jq >fulltx.txt
    
    #create the transaction with all the inputs and outpts, and sign it using signrawtransactionwithwallet (fails...)
    finaltx=$(echo $fulltx|jq|hal tx create 2>/dev/null)
    b-cli decoderawtransaction $finaltx >0_before.txt
    echo "tx BEFORE signrawtransactionwithwallet: $finaltx"
    signedfinaltx=$(b-cli signrawtransactionwithwallet $finaltx)
    echo "tx AFTER signrawtransactionwithwallet: $(echo $signedfinaltx|jq -r '.hex')"
    b-cli decoderawtransaction $(echo $signedfinaltx|jq -r '.hex') >1_after.txt
    diff -Naurwp 0_before.txt 1_after.txt >0.diff || true
    signerror=$(echo $signedfinaltx|jq -r '.errors[0].error//empty')
    if [ -n "$signerror" ];then
        echo "Error signing with wallet ($signerror)"
        exit -1
    fi
    
    senttx=$(b-cli sendrawtransaction $(echo $signedfinaltx|jq -r '.hex'))
    b-cli gettransaction $senttx
    b-cli generatetoaddress 1 $DEST >/dev/null 2>&1
    b-cli gettransaction $senttx
    

    I've tested with Bitcoin Core 23.0. Please let me know if there's anything else I can test.

  2. andreabonel added the label Bug on Oct 25, 2022
  3. willcl-ark commented at 12:42 PM on October 25, 2022: member

    Thank you for the bug report and repro steps.

    Might be related to #21151 ?

    I have reproduced with your script on v24.0rc2, but not yet gone through the script logic in detail to see why this is failing since #21166:

    will@ubuntu in ~/temp via 🐍 v3.6.12 took 16s
    ❯ ./26385.sh
    Bitcoin Core starting
    {
      "name": "",
      "warning": ""
    }
    
    b-cli createmultisig 2 ["02204cc848e8ac548b5e716e9c06eaf8bc84f896d08f5c5e21b97f6d5e5f88c95d","030297346cd7b3236e844fc8ba63793f4677fab9eb0282abec99c00b42770d051a","03d9a9029dec2e0d62f3f5bedd686b919bba7ce7bd36c2406e1b8e7ebeea1b637c"] legacy
    pubkeys: 02204cc848e8ac548b5e716e9c06eaf8bc84f896d08f5c5e21b97f6d5e5f88c95d 030297346cd7b3236e844fc8ba63793f4677fab9eb0282abec99c00b42770d051a 03d9a9029dec2e0d62f3f5bedd686b919bba7ce7bd36c2406e1b8e7ebeea1b637c
    privkeys: cW3np3Apfi1dbvV9WxabWn1BVBkrce2nbbYYE34DkQRdZFNiNYoz cUePsEDPDouj2eb6jhYRmxhutdoMQFCiwQN1B9ErtKdmXKYx8Xjg cMjnX31zMHDQoeXfoCCgug2bjLHTX3Bihtg56re6UQicshbBcfdT
    
    createwallet destwallet
    {
      "name": "destwallet",
      "warning": ""
    }
    unloadwallet destwallet
    {
      "warning": ""
    }
    Sending utxo d4b5e918f5e8296b74a003037c21680b893d4161bcbf6fd9ee909537f561a259 to final address bcrt1qcysp6g4kmhk8c39yf7xm6vkqxmg8k4ljf8prtn
    
    tx BEFORE signrawtransactionwithwallet: 010000000259a261f5379590eed96fbfbc61413d890b68217c0303a0746b29e8f518e9b5d400000000fc00473044022029458f221b23dff4171a492fea136e7a6d70214aa249be6b770accf3df49511e02207a84db1a6c012106a6e3fcfe37fff4c0171995e9f99e00bbb0f3bb335789689483473044022051d3fc59fc06b82ed0b5b4812a567eed0ef597dab3f4ef67f7154b181790b7c00220528a7b1febf010fbbdd682e68ac400fe635769542323adf6e129c23c19143c51834c69522102204cc848e8ac548b5e716e9c06eaf8bc84f896d08f5c5e21b97f6d5e5f88c95d21030297346cd7b3236e844fc8ba63793f4677fab9eb0282abec99c00b42770d051a2103d9a9029dec2e0d62f3f5bedd686b919bba7ce7bd36c2406e1b8e7ebeea1b637c53ae0000000059a261f5379590eed96fbfbc61413d890b68217c0303a0746b29e8f518e9b5d40100000000000000000200e1f50500000000160014c1201d22b6ddec7c44a44f8dbd32c036d07b57f25ae40f2401000000160014d3d3201f954e2344958af90ddaa2be84b6928a0100000000
    tx AFTER signrawtransactionwithwallet: 0100000000010259a261f5379590eed96fbfbc61413d890b68217c0303a0746b29e8f518e9b5d4000000006d00004c69522102204cc848e8ac548b5e716e9c06eaf8bc84f896d08f5c5e21b97f6d5e5f88c95d21030297346cd7b3236e844fc8ba63793f4677fab9eb0282abec99c00b42770d051a2103d9a9029dec2e0d62f3f5bedd686b919bba7ce7bd36c2406e1b8e7ebeea1b637c53ae0000000059a261f5379590eed96fbfbc61413d890b68217c0303a0746b29e8f518e9b5d40100000017160014bafa6a4a429e1e06cd4ea7a81c8a40f5a9e73e29000000000200e1f50500000000160014c1201d22b6ddec7c44a44f8dbd32c036d07b57f25ae40f2401000000160014d3d3201f954e2344958af90ddaa2be84b6928a010002473044022012427cd5dce2b32deb820c880ed43d0f4c30f5475d783d9a1ab63df4a94c5ecd02200b962e4386dc66b9e3bcf1888a07e9a782d92473a78de36163bc4f640adc6aa30121020ada311431cadab2788e59d7e419d64859a31dc76a19e2f84926d5b7b47876ab00000000
    Error signing with wallet (Unable to sign input, invalid stack size (possibly missing key))
    
  4. achow101 commented at 7:17 PM on October 25, 2022: member

    It looks like $finaltx comes from hal. Are you certain that that transaction is valid? I think the only way that this issue can occur is if the signatures in $finaltx are invalid. In that case, the signature extractor will fail to find the signatures as it is detecting them to be invalid and so ignores them. Then when it "signs" the input, it doesn't have anything to fill in and ends up clearing it.

    I don't have some of the tools needed to run the script installed on my system. Can you reproduce this issue with only Bitcoin Core commands? What happens if you do testmempoolaccept $finaltx before doing signrawtransactionwithwallet?

  5. andreabonel commented at 12:52 AM on October 26, 2022: none

    I'm not certain that that transaction is valid tbh, but I have no reason to suspect it isn't (hal is based on rust-bitcoin). By the way: shouldn't decoderawtransaction fail if that were the case?

    To try to reproduce this issue with only Bitcoin Core commands: do you know which rpc I could use to merge the transactions? I tried with testmempoolaccept $finaltx before doing signrawtransactionwithwallet but it fails since the last transaction isn't signed yet. I also tried testmempoolaccept with only the first transaction, and it fails because it doesn't have fees ("min relay fee not met").

  6. achow101 commented at 1:51 AM on October 26, 2022: member

    By the way: shouldn't decoderawtransaction fail if that were the case?

    No, decoderawtransaction does not check signatures.

    To try to reproduce this issue with only Bitcoin Core commands: do you know which rpc I could use to merge the transactions?

    You could use combinerawtransaction.

    I also tried testmempoolaccept with only the first transaction, and it fails because it doesn't have fees ("min relay fee not met").

    The output has the same amount as the input, so the fee is 0. Try setting the output amount to be 0.9999

  7. meglio commented at 1:32 PM on March 29, 2023: contributor

    Am I correct to assume this is still a bug awaiting a fix?

  8. willcl-ark commented at 3:40 PM on September 21, 2023: member

    The problem is not easily reproducible.

    Please open a new issue (or leave a comment in here if you want this re-opened) if you experience the problem again.

  9. willcl-ark closed this on Sep 21, 2023

  10. bitcoin locked this on Sep 20, 2024

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-14 00:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me