Node crashes when processing partial transaction
I made the node crash while trying to blind the following pset:
$ e1 walletprocesspsbt cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEDAfsEAgAAAAABDiAqrJ9c31tcJHnWDjQWJNUfArZMJr1w1h7/S6GIMG31DwEPBAAAAAABEAT/////AAEEFgAUBFTPa7NoO3V+jzsjbmvRlQwcOWoBAwgA4fUFAAAAAAf8BHBzZXQCINMRFPznA5TB+dNUdQG0udNvQgI27GQZkVRWZDSIWs8tAAEEFgAU+a3P0cB6H1+r8pR099KXbz20PbwBAwgAAAAAAAAAAAf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAYhAv2YM9xdvR/Mxx+ulKcSzcom+JQpzsV/hrxKHexk7BYYB/wEcHNldAgEAAAAAAABBAABAwjoAwAAAAAAAAf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaAA== false
The input of the tx is confidential and its amount is exactly 0.00001, therefore it's spent entirely by the fee out.
The first output of the tx is a non-LBTC unblinded one, therefore i tried to add a dummy LBTC blinded output to balance the tx as {"el1qqt7esv7utk73lnx8r7hfffcjeh9zd7y5988v2luxh39pmmryastp37ddelguq7slt74l99r57lffwmeaks7mc883qj7565fdg": 0, "blinder_index": 0} in createpsbt.
These are the last logs of the node:
2022-04-15T15:39:50Z [default wallet] AddToWallet 02338810e562a4fe99d555e616932f76dbb8301e9e264fcf6e9a3cf56ce5322d new
2022-04-15T15:39:50Z [default wallet] AddToWallet 0ff56d3088a14bff1ed670bd264cb6021fd52416340ed679245c5bdf5c9fac2a update
elementsd: blindpsbt.cpp:554: BlindingStatus BlindPSBT(PartiallySignedTransaction&, std::map<unsigned int, std::tuple<long int, CAsset, uint256, uint256> >, std::map<unsigned int, std::pair<CKey, CKey> >): Assertion `rangeresult' failed.
Seems like the blinder role fails to create a rangeproof for the dummy output. Am I doing something wrong?
My bad, the right way of adding a dummy output is by defining it as {"data": "00", "blinder_index": 0} instead of {"${confAddress}": 0, "blinder_index": 0}.
There doesn't seem to be a way to add a blinding pubkey to the dummy output though. If that's missing, the output is considered unblinded, therefore no commitments and proof are generated for it, making the definition of the blinder index useless.
Thanks for the bug report. Regardless of whether you were using the correct workflow, we definitely shouldn't be crashing :)
Yeah definitely. :)
Also, it would be great to have a way to add a dummy confidential output to a partial transaction, like being able to define a blinding key for such output like {"data": "00", "blinder_index": <n>, "blinding_pubkey": "<pubkey>"}.
Without the blinding pubkey, the dummy output is not considered as one to be blinded even if it has a blinder index set, therefore it's pretty useless.
I had to manually do that to reach the goal. It would be really helpful to make this possible by using the CLI instead.
Seems like I'm not able to reproduce the crash. When I tried I got this response { “psbt”: “cHNldP8BAgQCAAAAAQMEAAAAAAEEAQEBBQEDAfsEAgAAAAABDiAqrJ9c31tcJHnWDjQWJNUfArZMJr1w1h7/S6GIMG31DwEPBAAAAAABEAT/////AAEEFgAUBFTPa7NoO3V+jzsjbmvRlQwcOWoBAwgA4fUFAAAAAAf8BHBzZXQCINMRFPznA5TB+dNUdQG0udNvQgI27GQZkVRWZDSIWs8tAAEEFgAU+a3P0cB6H1+r8pR099KXbz20PbwBAwgAAAAAAAAAAAf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaB/wEcHNldAYhAv2YM9xdvR/Mxx+ulKcSzcom+JQpzsV/hrxKHexk7BYYB/wEcHNldAgEAAAAAAABBAABAwjoAwAAAAAAAAf8BHBzZXQCICWyUQcOKcoZBDzzPM1zJOLdqwPsxK4LXnfE/A5c9slaAA==”, “complete”: false }
I tested with elements 0.21.0.2, in regtest
What version of elements are you using? Which network?
I'm using elements v0.21.0.1 on regtest.
I tried again with 0.21.0.1 and still no crash, I'm wondering what the difference is... What's your elements config and command line for the daemon?
I'm using nigiri, therefore a dockerized elements node with the following configuration:
chain=liquidregtest
listen=1
txindex=1
[liquidregtest]
port=18886
rpcport=18884
rpcuser=admin1
rpcpassword=123
rpcallowip=0.0.0.0/0
rpcbind=0.0.0.0
pchmessagestart=12345678
blockfilterindex=1
peerblockfilters=1
mainchainrpcport=18443
mainchainrpcuser=admin1
mainchainrpcpassword=123
# This is the script that controls pegged in funds in Bitcoin network
# Users will be pegging into a P2SH of this, and the "watchmen"
# can then recover these funds and send them to users who desire to peg out.
# This template is 1-of-1 checkmultisig
#fedpegscript=5121<pubkey>51ae
# This is the script that controls how blocks are made
# We have to supply a signature that satisfies this to create
# a valid block.
#signblockscript=5121<pubkey2>51ae
# We want to validate pegins by checking with bitcoind if header exists
# in best known chain, and how deep. We combine this with pegin
# proof included in the pegin to get full security.
validatepegin=0
initialfreecoins=2100000000000000
fallbackfee=0.000001
## Dynafed
con_dyna_deploy_start=0
con_dyna_deploy_signal=1
con_nminerconfirmationwindow=1
con_nrulechangeactivationthreshold=1
## Taproot signaling
con_taproot_signal_start=0
## ZMQ
zmqpubrawblock=tcp://0.0.0.0:38332
zmqpubrawtx=tcp://0.0.0.0:38333
Steps to reproduce:
addr=$(e1 getnewaddress)
otherAddr=$(e1 getnewaddress)
e1 sendtoaddress ${addr} 0.00001
e1 generatetoaddress 1 $(e1 getnewaddress)
# retrieve utxo's txid and vout then create the "crashing" pset
pset=$(e1 createpsbt '[{"txid": "'${txid}'", "vout": '${vout}'}]' '[{"'${otherAddr}'": 0, "blinder_index": 0}, {"fee": 0.00001}]')
# crash here
e1 wallletprocesspsbt ${pset}
here's a more complete script to reproduce it:
#!/bin/bash -x
set -e
shopt -s expand_aliases
alias e1="$HOME/opt/elements/bin/elements-cli -datadir=$HOME/elementsdir1"
alias e1-dae="$HOME/opt/elements/bin/elementsd -datadir=$HOME/elementsdir1"
e1 stop || true
rm -rf "$HOME/elementsdir1/elementsregtest"
mkdir -p "$HOME/elementsdir1/elementsregtest"
echo "chain=elementsregtest
rpcuser=user1
rpcpassword=password1
daemon=0
server=1
listen=1
txindex=1
validatepegin=0
mainchainrpcport=18888
mainchainrpcuser=user3
mainchainrpcpassword=password3
initialfreecoins=2100000000000000
fallbackfee=0.0002
[elementsregtest]
rpcport=18884
port=18886
anyonecanspendaremine=1
connect=localhost:18887
" > ~/elementsdir1/elements.conf
e1-dae &
e1 -rpcwait createwallet ""
e1 rescanblockchain
addr=$(e1 getnewaddress)
otherAddr=$(e1 getnewaddress)
txid=$(e1 sendtoaddress ${addr} 0.00001)
e1 generatetoaddress 1 $(e1 getnewaddress)
# retrieve utxo's txid and vout then create the "crashing" pset
vout=0
pset=$(e1 createpsbt '[{"txid": "'${txid}'", "vout": '${vout}'}]' '[{"'${otherAddr}'": 0, "blinder_index": 0}, {"fee": 0.00001}]')
# crash here
e1 walletprocesspsbt ${pset}
and the result I got:

Here's a back trace of the crash:
(gdb) bt
#0 BlindPSBT (psbt=..., our_input_data=..., our_issuances_to_blind=...) at blindpsbt.cpp:554
#1 0x0000555555a4be09 in CWallet::WalletBlindPSBT (this=<optimized out>, psbtx=...) at wallet/wallet.cpp:2823
#2 0x00005555559e9dd3 in <lambda(const RPCHelpMan&, const JSONRPCRequest&)>::operator()(const JSONRPCRequest &, const RPCHelpMan &) (request=..., self=..., __closure=<optimized out>) at wallet/rpcwallet.cpp:4745
#3 0x00005555559ea480 in std::_Function_handler<UniValue(const RPCHelpMan&, const JSONRPCRequest&), walletprocesspsbt()::<lambda(const RPCHelpMan&, const JSONRPCRequest&)> >::_M_invoke(const std::_Any_data &, const RPCHelpMan &, const JSONRPCRequest &) (
__functor=..., __args#0=..., __args#1=...) at /usr/include/c++/7/bits/std_function.h:302
#4 0x000055555575aae8 in std::function<UniValue (RPCHelpMan const&, JSONRPCRequest const&)>::operator()(RPCHelpMan const&, JSONRPCRequest const&) const (__args#1=..., __args#0=..., this=0x7fffa6ffbb60) at /usr/include/c++/7/bits/std_function.h:706
#5 RPCHelpMan::HandleRequest (request=..., this=0x7fffa6ffbb40) at ./rpc/util.h:348
#6 CRPCCommand::CRPCCommand(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, RPCHelpMan (*)(), std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)::{lambda(JSONRPCRequest const&, UniValue&, bool)#1}::operator()(JSONRPCRequest const&, UniValue&, bool) const (
__closure=<optimized out>, request=..., result=...) at ./rpc/server.h:111
#7 0x00005555559470b3 in std::function<bool (JSONRPCRequest const&, UniValue&, bool)>::operator()(JSONRPCRequest const&, UniValue&, bool) const (__args#2=<optimized out>, __args#1=..., __args#0=..., this=0x5555564de0e0 <GetWalletRPCCommands()::commands+7616>)
at /usr/include/c++/7/bits/std_function.h:706
#8 interfaces::(anonymous namespace)::WalletClientImpl::<lambda(const JSONRPCRequest&, UniValue&, bool)>::operator()(const JSONRPCRequest &, UniValue &, bool) const (__closure=<optimized out>, request=..., result=..., last_handler=<optimized out>)
at interfaces/wallet.cpp:520
#9 0x0000555555652177 in std::function<bool (JSONRPCRequest const&, UniValue&, bool)>::operator()(JSONRPCRequest const&, UniValue&, bool) const (__args#2=<optimized out>, __args#1=..., __args#0=..., this=<optimized out>)
at /usr/include/c++/7/bits/std_function.h:706
#10 interfaces::(anonymous namespace)::RpcHandlerImpl::<lambda(const JSONRPCRequest&, UniValue&, bool)>::operator() (
last_handler=true, result=..., request=..., __closure=<optimized out>) at interfaces/chain.cpp:113
#11 std::_Function_handler<bool(const JSONRPCRequest&, UniValue&, bool), interfaces::(anonymous namespace)::RpcHandlerImpl::RpcHandlerImpl(const CRPCCommand&)::<lambda(const JSONRPCRequest&, UniValue&, bool)> >::_M_invoke(const std::_Any_data &, const JSONRPCRequest &, UniValue &, bool &&) (__functor=..., __args#0=..., __args#1=..., __args#2=<optimized out>)
at /usr/include/c++/7/bits/std_function.h:302
#12 0x0000555555828299 in std::function<bool (JSONRPCRequest const&, UniValue&, bool)>::operator()(JSONRPCRequest const&, UniValue&, bool) const (__args#2=<optimized out>, __args#1=..., __args#0=..., this=0x5555566b0688)
at /usr/include/c++/7/bits/std_function.h:706
#13 ExecuteCommand (last_handler=<optimized out>, result=..., request=..., command=...) at rpc/server.cpp:466
#14 CRPCTable::execute (this=<optimized out>, request=...) at rpc/server.cpp:449
#15 0x000055555590287f in HTTPReq_JSONRPC (context=..., req=0x7fff84001a70) at httprpc.cpp:207
#16 0x0000555555907109 in std::function<bool (HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)>::operator()(HTTPRequest*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const (__args#1=..., __args#0=<optimized out>, this=0x7fff84004a00) at /usr/include/c++/7/bits/std_function.h:706
#17 HTTPWorkItem::operator() (this=0x7fff840049d0) at httpserver.cpp:56
#18 WorkQueue<HTTPClosure>::Run (this=0x555556683960) at httpserver.cpp:115
#19 HTTPWorkQueueRun (queue=0x555556683960, worker_num=<optimized out>) at httpserver.cpp:343
#20 0x0000555555f9723f in execute_native_thread_routine ()
#21 0x00007ffff7d2bb1a in start_thread (arg=<optimized out>) at pthread_create.c:443
#22 0x00007ffff7db0660 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
it stops at the assert because CreateValueRangeProof returns false so the problem might be in the parameters (I guess that's as far as I can go...)
This was mistakenly closed by #1134 , I meant to close #1125