Skip to content

Commit

Permalink
elementsd: add a fee output to a fund/utxopsbt for elements transactions
Browse files Browse the repository at this point in the history
fundpsbt / utxopsbt create a (typically) output-less PSBT,
however for elements we require the fees to be encapsulated in an
output.

this patch updates fundpsbt / utxopsbt to add a fee output for elements
transactions. includes test updates.

Fixes ElementsProject#3998
  • Loading branch information
niftynei authored and rustyrussell committed Sep 9, 2020
1 parent 171b985 commit 437ca83
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 14 deletions.
39 changes: 25 additions & 14 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
feerates=(7500, 7500, 7500, 7500))
l2 = node_factory.get_node()
addr = chainparams['example_addr']
out_total = Millisatoshi(amount * 3 * 1000)

# Add a medley of funds to withdraw later, bech32 + p2sh-p2wpkh
for i in range(total_outs // 2):
Expand All @@ -640,7 +641,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == total_outs)

# Make a PSBT out of our inputs
funding = l1.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
funding = l1.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42,
reserve=True)
Expand All @@ -665,10 +666,13 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
# Now we unreserve the singleton, so we can reserve it again
l1.rpc.unreserveinputs(psbt)

# Now add an output.
output_pbst = bitcoind.rpc.createpsbt([],
[{addr: 3 * amount / 10**8}])
fullpsbt = bitcoind.rpc.joinpsbts([funding['psbt'], output_pbst])
# Now add an output. Note, we add the 'excess msat' to the output so
# that our feerate is 'correct'. This is of particular importance to elementsd,
# who requires that every satoshi be accounted for in a tx.
out_1_ms = Millisatoshi(funding['excess_msat'])
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float((out_total + out_1_ms).to_btc())}])
fullpsbt = bitcoind.rpc.joinpsbts([funding['psbt'], output_psbt])

# We re-reserve the first set...
l1.rpc.reserveinputs(fullpsbt)
Expand Down Expand Up @@ -702,7 +706,7 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
# Create a PSBT using L2
bitcoind.generate_block(1)
wait_for(lambda: len(l2.rpc.listfunds()['outputs']) == total_outs)
l2_funding = l2.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
l2_funding = l2.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42,
reserve=True)
Expand All @@ -716,16 +720,20 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
l1.rpc.signpsbt(l2_funding['psbt'], signonly=[0])

# Add some of our own PSBT inputs to it
l1_funding = l1.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
l1_funding = l1.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42,
reserve=True)
l1_num_inputs = len(bitcoind.rpc.decodepsbt(l1_funding['psbt'])['tx']['vin'])
l2_num_inputs = len(bitcoind.rpc.decodepsbt(l2_funding['psbt'])['tx']['vin'])

# Join and add an output (reorders!)
out_2_ms = Millisatoshi(l1_funding['excess_msat'])
out_amt = out_2_ms + Millisatoshi(l2_funding['excess_msat']) + out_total + out_total
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float(out_amt.to_btc())}])
joint_psbt = bitcoind.rpc.joinpsbts([l1_funding['psbt'], l2_funding['psbt'],
output_pbst])
output_psbt])

# Ask it to sign inputs it doesn't know, it will fail.
with pytest.raises(RpcError, match=r"is unknown"):
Expand Down Expand Up @@ -753,11 +761,14 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
l1.daemon.wait_for_log(r'sendrawtx exit 0 .* sendrawtransaction {}'.format(broadcast_tx['tx']))

# Send a PSBT that's not ours
l2_funding = l2.rpc.fundpsbt(satoshi=Millisatoshi(3 * amount * 1000),
l2_funding = l2.rpc.fundpsbt(satoshi=out_total,
feerate=7500,
startweight=42,
reserve=True)
psbt = bitcoind.rpc.joinpsbts([l2_funding['psbt'], output_pbst])
out_amt = Millisatoshi(l2_funding['excess_msat'])
output_psbt = bitcoind.rpc.createpsbt([],
[{addr: float((out_total + out_amt).to_btc())}])
psbt = bitcoind.rpc.joinpsbts([l2_funding['psbt'], output_psbt])
l2_signed_psbt = l2.rpc.signpsbt(psbt)['signed_psbt']
l1.rpc.sendpsbt(l2_signed_psbt)

Expand Down Expand Up @@ -796,14 +807,14 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000 + int(out_1_ms), 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000 - int(out_1_ms), 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'},
]

check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)
Expand Down
6 changes: 6 additions & 0 deletions wallet/reservation.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ static struct command_result *finish_psbt(struct command *cmd,
psbt = psbt_using_utxos(cmd, utxos, cmd->ld->wallet->bip32_base,
*locktime, BITCOIN_TX_RBF_SEQUENCE);

/* Add a fee output if this is elements */
if (is_elements(chainparams)) {
struct amount_sat est_fee =
amount_tx_fee(feerate_per_kw, weight);
psbt_append_output(psbt, NULL, est_fee);
}
response = json_stream_success(cmd);
json_add_psbt(response, "psbt", psbt);
json_add_num(response, "feerate_per_kw", feerate_per_kw);
Expand Down

0 comments on commit 437ca83

Please sign in to comment.