Skip to content

Commit

Permalink
recvpsbt: New onchain command for PSBT output
Browse files Browse the repository at this point in the history
ChangeLog-Added: New `recvpsbt` command for creating a PSBT that can receive funds to the on-chain wallet.
  • Loading branch information
ddustin committed Sep 14, 2023
1 parent e66d642 commit 524de4a
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 0 deletions.
11 changes: 11 additions & 0 deletions contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,17 @@ def fundpsbt(self, satoshi, feerate, startweight, minconf=None, reserve=None, lo
}
return self.call("fundpsbt", payload)

def recvpsbt(self, satoshi, initialpsbt=None, locktime=None):
"""
Create a PSBT with an output of amount satoshi leading to the on-chain wallet
"""
payload = {
"satoshi": satoshi,
"initialpsbt": initialpsbt,
"locktime": locktime,
}
return self.call("recvpsbt", payload)

def utxopsbt(self, satoshi, feerate, startweight, utxos, reserve=None, reservedok=False, locktime=None, min_witness_weight=None, excess_as_change=False):
"""
Create a PSBT with given inputs, to give an output of satoshi.
Expand Down
1 change: 1 addition & 0 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ MANPAGES := doc/lightning-cli.1 \
doc/lightning-fundchannel_complete.7 \
doc/lightning-fundchannel_cancel.7 \
doc/lightning-funderupdate.7 \
doc/lightning-recvpsbt.7 \
doc/lightning-fundpsbt.7 \
doc/lightning-getroute.7 \
doc/lightning-hsmtool.8 \
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Core Lightning Documentation
lightning-preapproveinvoice <lightning-preapproveinvoice.7.md>
lightning-preapprovekeysend <lightning-preapprovekeysend.7.md>
lightning-recoverchannel <lightning-recoverchannel.7.md>
lightning-recvpsbt <lightning-recvpsbt.7.md>
lightning-renepay <lightning-renepay.7.md>
lightning-renepaystatus <lightning-renepaystatus.7.md>
lightning-reserveinputs <lightning-reserveinputs.7.md>
Expand Down
65 changes: 65 additions & 0 deletions doc/lightning-recvpsbt.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
lightning-recvpsbt -- Command to populate PSBT outputs from the wallet
================================================================

SYNOPSIS
--------

**recvpsbt** *satoshi* [*initialpsbt*] [*locktime*]

DESCRIPTION
-----------

`recvpsbt` is a low-level RPC command which creates or modifies a PSBT
by adding a single output of amount *satoshi*.

This is used to receive funds into the on-chain wallet interactively
using PSBTs.

*satoshi* is the satoshi value of the output. It can
be a whole number, a whole number ending in *sat*, a whole number
ending in *000msat*, or a number with 1 to 8 decimal places ending in
*btc*.

*initialpsbt* is a PSBT to add the output to. If not speciifed, a PSBt
will be created automatically.

*locktime* is an optional locktime: if not set, it is set to a recent
block height (if no initial psbt is specified).

EXAMPLE USAGE
-------------

Here is a command to make a PSBT with a 100,000 sat output that leads
to the on-chain wallet.
```shell
lightning-cli recvpsbt 100000sat
```

RETURN VALUE
------------

[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object is returned, containing:

- **psbt** (string): Unsigned PSBT which fulfills the parameters given
- **estimated\_added\_weight** (u32): The estimated weight of the added output
- **outnum** (u32): The 0-based number where the output was placed

[comment]: # (GENERATE-FROM-SCHEMA-END)

AUTHOR
------

@dusty\_daemon

SEE ALSO
--------

lightning-fundpsbt(7), lightning-utxopsbt(7)

RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:fad04aa277b18392570cc1ba6302506dadc71d1e3844720cb5c1b74812d731f2)
21 changes: 21 additions & 0 deletions doc/schemas/recvpsbt.request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"satoshi"
],
"added": "v23.08.1",
"properties": {
"satoshi": {
"type": "msat"
},
"locktime": {
"type": "u32"
},
"initialpsbt": {
"type": "string",
"description": "the (optional) base 64 encoded PSBT to begin with. If not specified, one will be generated automatically"
}
}
}
25 changes: 25 additions & 0 deletions doc/schemas/recvpsbt.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": [
"psbt",
"estimated_added_weight",
"outnum"
],
"added": "v23.08.1",
"properties": {
"psbt": {
"type": "string",
"description": "Unsigned PSBT which fulfills the parameters given"
},
"estimated_added_weight": {
"type": "u32",
"description": "The estimated weight of the added output"
},
"outnum": {
"type": "u32",
"description": "The 0-based number where the output was placed"
}
}
}
27 changes: 27 additions & 0 deletions tests/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,33 @@ def test_fundpsbt(node_factory, bitcoind, chainparams):
l1.rpc.fundpsbt(amount // 2, feerate, 0)


@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
def test_recvpsbt(node_factory, bitcoind, chainparams):
amount1 = 1000000
amount2 = 3333333
locktime = 111
l1 = node_factory.get_node()

result = l1.rpc.recvpsbt(amount1, locktime=locktime)
assert result['outnum'] == 0

psbt_info = bitcoind.rpc.decodepsbt(l1.rpc.setpsbtversion(result['psbt'], 0)['psbt'])

assert len(psbt_info['tx']['vout']) == 1
assert psbt_info['tx']['vout'][0]['n'] == result['outnum']
assert psbt_info['tx']['vout'][0]['value'] * 100000000 == amount1
assert psbt_info['tx']['locktime'] == locktime

result = l1.rpc.recvpsbt(amount2, result['psbt'])
n = result['outnum']

psbt_info = bitcoind.rpc.decodepsbt(l1.rpc.setpsbtversion(result['psbt'], 0)['psbt'])

assert len(psbt_info['tx']['vout']) == 2
assert psbt_info['tx']['vout'][n]['value'] * 100000000 == amount2
assert psbt_info['tx']['vout'][n]['n'] == result['outnum']


def test_utxopsbt(node_factory, bitcoind, chainparams):
amount = 1000000
l1 = node_factory.get_node()
Expand Down
88 changes: 88 additions & 0 deletions wallet/reservation.c
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,94 @@ static const struct json_command fundpsbt_command = {
};
AUTODATA(json_command, &fundpsbt_command);

static struct command_result *json_recvpsbt(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct json_stream *response;
struct amount_sat *amount;
struct wally_psbt *psbt;
u32 *locktime;
ssize_t outnum;
u32 weight;
struct pubkey pubkey;
s64 keyidx;
u8 *b32script;

if (!param(cmd, buffer, params,
p_req("satoshi", param_sat, &amount),
p_opt("initialpsbt", param_psbt, &psbt),
p_opt("locktime", param_number, &locktime),
NULL))
return command_param_failed();

if (!psbt) {
if (!locktime) {
locktime = tal(cmd, u32);
*locktime = default_locktime(cmd->ld->topology);
}
psbt = create_psbt(cmd, 0, 0, *locktime);
}
else if(locktime) {
return command_fail(cmd, FUNDING_PSBT_INVALID,
"Can't set locktime of an existing {initialpsbt}");
}

if (!validate_psbt(psbt))
return command_fail(cmd,
FUNDING_PSBT_INVALID,
"PSBT failed to validate.");

if (amount_sat_less(*amount, chainparams->dust_limit))
return command_fail(cmd, FUND_OUTPUT_IS_DUST,
"Receive amount is below dust limit (%s)",
type_to_string(tmpctx,
struct amount_sat,
&chainparams->dust_limit));

/* Get a change adddress */
keyidx = wallet_get_newindex(cmd->ld);
if (keyidx < 0)
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys exhausted.");

if (chainparams->is_elements) {
bip32_pubkey(cmd->ld, &pubkey, keyidx);
b32script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
} else {
b32script = p2tr_for_keyidx(tmpctx, cmd->ld, keyidx);
}
if (!b32script) {
return command_fail(cmd, LIGHTNINGD,
"Failed to generate change address."
" Keys generation failure");
}
txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);

outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, *amount);
/* Add additional weight of output */
weight = bitcoin_tx_output_weight(
chainparams->is_elements ? BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN : BITCOIN_SCRIPTPUBKEY_P2TR_LEN);

response = json_stream_success(cmd);
json_add_psbt(response, "psbt", psbt);
json_add_num(response, "estimated_added_weight", weight);
json_add_num(response, "outnum", outnum);
return command_success(cmd, response);
}

static const struct json_command recvpsbt_command = {
"recvpsbt",
"bitcoin",
json_recvpsbt,
"Create a PSBT (or modify existing {initialpsbt}) with an output receiving {satoshi} amount.",
false
};
AUTODATA(json_command, &recvpsbt_command);

static struct command_result *param_txout(struct command *cmd,
const char *name,
const char *buffer,
Expand Down

0 comments on commit 524de4a

Please sign in to comment.