elements icon indicating copy to clipboard operation
elements copied to clipboard

Node crashes when processing partial transaction

Open altafan opened this issue 3 years ago • 13 comments

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?

altafan avatar Apr 15 '22 15:04 altafan

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.

altafan avatar Apr 15 '22 16:04 altafan

Thanks for the bug report. Regardless of whether you were using the correct workflow, we definitely shouldn't be crashing :)

apoelstra avatar Apr 15 '22 19:04 apoelstra

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.

altafan avatar Apr 17 '22 13:04 altafan

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 }

andreabonel avatar Apr 18 '22 20:04 andreabonel

I tested with elements 0.21.0.2, in regtest
What version of elements are you using? Which network?

andreabonel avatar Apr 18 '22 20:04 andreabonel

I'm using elements v0.21.0.1 on regtest.

altafan avatar Apr 19 '22 10:04 altafan

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?

andreabonel avatar Apr 19 '22 13:04 andreabonel

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

altafan avatar Apr 20 '22 10:04 altafan

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}

altafan avatar Apr 22 '22 21:04 altafan

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}

andreabonel avatar Apr 27 '22 20:04 andreabonel

and the result I got:

image

andreabonel avatar Apr 27 '22 20:04 andreabonel

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...)

andreabonel avatar May 03 '22 14:05 andreabonel

This was mistakenly closed by #1134 , I meant to close #1125

sanket1729 avatar Dec 05 '22 02:12 sanket1729