mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 15:14:23 +01:00
API: txprepare now support mutiple outputs
This commit is contained in:
@@ -60,6 +60,16 @@ bool json_to_sat(const char *buffer, const jsmntok_t *tok,
|
|||||||
return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start);
|
return parse_amount_sat(sat, buffer + tok->start, tok->end - tok->start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok,
|
||||||
|
struct amount_sat *sat)
|
||||||
|
{
|
||||||
|
if (json_tok_streq(buffer, tok, "all")) {
|
||||||
|
*sat = AMOUNT_SAT(-1ULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return json_to_sat(buffer, tok, sat);
|
||||||
|
}
|
||||||
|
|
||||||
bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok,
|
bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok,
|
||||||
struct short_channel_id *scid,
|
struct short_channel_id *scid,
|
||||||
bool may_be_deprecated_form)
|
bool may_be_deprecated_form)
|
||||||
|
|||||||
@@ -32,6 +32,11 @@ bool json_to_short_channel_id(const char *buffer, const jsmntok_t *tok,
|
|||||||
bool json_to_sat(const char *buffer, const jsmntok_t *tok,
|
bool json_to_sat(const char *buffer, const jsmntok_t *tok,
|
||||||
struct amount_sat *sat);
|
struct amount_sat *sat);
|
||||||
|
|
||||||
|
/* Extract a satoshis amount from this */
|
||||||
|
/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */
|
||||||
|
bool json_to_sat_or_all(const char *buffer, const jsmntok_t *tok,
|
||||||
|
struct amount_sat *sat);
|
||||||
|
|
||||||
/* Extract a millisatoshis amount from this */
|
/* Extract a millisatoshis amount from this */
|
||||||
bool json_to_msat(const char *buffer, const jsmntok_t *tok,
|
bool json_to_msat(const char *buffer, const jsmntok_t *tok,
|
||||||
struct amount_msat *msat);
|
struct amount_msat *msat);
|
||||||
|
|||||||
@@ -183,7 +183,19 @@ struct command_result *param_sat(struct command *cmd, const char *name,
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"'%s' should be a satoshi amount, not '%.*s'",
|
"%s should be a satoshi amount, not '%.*s'",
|
||||||
name, tok->end - tok->start, buffer + tok->start);
|
name ? name : "amount field",
|
||||||
|
tok->end - tok->start, buffer + tok->start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct command_result *param_sat_or_all(struct command *cmd, const char *name,
|
||||||
|
const char *buffer, const jsmntok_t *tok,
|
||||||
|
struct amount_sat **sat)
|
||||||
|
{
|
||||||
|
if (json_tok_streq(buffer, tok, "all")) {
|
||||||
|
*sat = tal(cmd, struct amount_sat);
|
||||||
|
**sat = AMOUNT_SAT(-1ULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return param_sat(cmd, name, buffer, tok, sat);
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,12 @@ struct command_result *param_sat(struct command *cmd, const char *name,
|
|||||||
const char *buffer, const jsmntok_t *tok,
|
const char *buffer, const jsmntok_t *tok,
|
||||||
struct amount_sat **sat);
|
struct amount_sat **sat);
|
||||||
|
|
||||||
|
/* Extract satoshi amount from this string. */
|
||||||
|
/* If the string is "all", set amonut as AMOUNT_SAT(-1ULL). */
|
||||||
|
struct command_result *param_sat_or_all(struct command *cmd, const char *name,
|
||||||
|
const char *buffer, const jsmntok_t *tok,
|
||||||
|
struct amount_sat **sat);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set the address of @out to @tok. Used as a callback by handlers that
|
* Set the address of @out to @tok. Used as a callback by handlers that
|
||||||
* want to unmarshal @tok themselves.
|
* want to unmarshal @tok themselves.
|
||||||
|
|||||||
@@ -878,18 +878,18 @@ class LightningRpc(UnixDomainSocketRpc):
|
|||||||
}
|
}
|
||||||
return self.call("withdraw", payload)
|
return self.call("withdraw", payload)
|
||||||
|
|
||||||
def txprepare(self, destination, satoshi, feerate=None, minconf=None):
|
def txprepare(self, outputs, feerate=None, minconf=None):
|
||||||
"""
|
"""
|
||||||
Prepare a bitcoin transaction which sends to {destination} address
|
Prepare a bitcoin transaction which sends to [outputs].
|
||||||
{satoshi} (or "all") amount via Bitcoin transaction. Only select outputs
|
The format of output is like [{address1: amount1},
|
||||||
with {minconf} confirmations.
|
{address2: amount2}], or [{address: "all"}]).
|
||||||
|
Only select outputs with {minconf} confirmations.
|
||||||
|
|
||||||
Outputs will be reserved until you call txdiscard or txsend, or
|
Outputs will be reserved until you call txdiscard or txsend, or
|
||||||
lightningd restarts.
|
lightningd restarts.
|
||||||
"""
|
"""
|
||||||
payload = {
|
payload = {
|
||||||
"destination": destination,
|
"outputs": outputs,
|
||||||
"satoshi": satoshi,
|
|
||||||
"feerate": feerate,
|
"feerate": feerate,
|
||||||
"minconf": minconf,
|
"minconf": minconf,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ msgtype,hsm_sign_withdrawal,7
|
|||||||
msgdata,hsm_sign_withdrawal,satoshi_out,amount_sat,
|
msgdata,hsm_sign_withdrawal,satoshi_out,amount_sat,
|
||||||
msgdata,hsm_sign_withdrawal,change_out,amount_sat,
|
msgdata,hsm_sign_withdrawal,change_out,amount_sat,
|
||||||
msgdata,hsm_sign_withdrawal,change_keyindex,u32,
|
msgdata,hsm_sign_withdrawal,change_keyindex,u32,
|
||||||
msgdata,hsm_sign_withdrawal,scriptpubkey_len,u16,
|
msgdata,hsm_sign_withdrawal,num_outputs,u16,
|
||||||
msgdata,hsm_sign_withdrawal,scriptpubkey,u8,scriptpubkey_len
|
msgdata,hsm_sign_withdrawal,outputs,bitcoin_tx_output,num_outputs
|
||||||
msgdata,hsm_sign_withdrawal,num_inputs,u16,
|
msgdata,hsm_sign_withdrawal,num_inputs,u16,
|
||||||
msgdata,hsm_sign_withdrawal,inputs,utxo,num_inputs
|
msgdata,hsm_sign_withdrawal,inputs,utxo,num_inputs
|
||||||
|
|
||||||
|
|||||||
|
@@ -1514,24 +1514,17 @@ static struct io_plan *handle_sign_withdrawal_tx(struct io_conn *conn,
|
|||||||
struct utxo **utxos;
|
struct utxo **utxos;
|
||||||
struct bitcoin_tx *tx;
|
struct bitcoin_tx *tx;
|
||||||
struct pubkey changekey;
|
struct pubkey changekey;
|
||||||
u8 *scriptpubkey;
|
|
||||||
struct bitcoin_tx_output **outputs;
|
struct bitcoin_tx_output **outputs;
|
||||||
|
|
||||||
outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 0);
|
|
||||||
if (!fromwire_hsm_sign_withdrawal(tmpctx, msg_in, &satoshi_out,
|
if (!fromwire_hsm_sign_withdrawal(tmpctx, msg_in, &satoshi_out,
|
||||||
&change_out, &change_keyindex,
|
&change_out, &change_keyindex,
|
||||||
&scriptpubkey, &utxos))
|
&outputs, &utxos))
|
||||||
return bad_req(conn, c, msg_in);
|
return bad_req(conn, c, msg_in);
|
||||||
|
|
||||||
if (!bip32_pubkey(&secretstuff.bip32, &changekey, change_keyindex))
|
if (!bip32_pubkey(&secretstuff.bip32, &changekey, change_keyindex))
|
||||||
return bad_req_fmt(conn, c, msg_in,
|
return bad_req_fmt(conn, c, msg_in,
|
||||||
"Failed to get key %u", change_keyindex);
|
"Failed to get key %u", change_keyindex);
|
||||||
|
|
||||||
struct bitcoin_tx_output *output = tal(outputs,
|
|
||||||
struct bitcoin_tx_output);
|
|
||||||
output->script = tal_steal(output, scriptpubkey);
|
|
||||||
output->amount = satoshi_out;
|
|
||||||
tal_arr_expand(&outputs, output);
|
|
||||||
tx = withdraw_tx(tmpctx, c->chainparams,
|
tx = withdraw_tx(tmpctx, c->chainparams,
|
||||||
cast_const2(const struct utxo **, utxos), outputs,
|
cast_const2(const struct utxo **, utxos), outputs,
|
||||||
&changekey, change_out, NULL, NULL);
|
&changekey, change_out, NULL, NULL);
|
||||||
|
|||||||
@@ -207,8 +207,7 @@ def test_txprepare(node_factory, bitcoind):
|
|||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
||||||
|
|
||||||
prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)}])
|
||||||
Millisatoshi(amount * 3 * 1000))
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
||||||
assert decode['txid'] == prep['txid']
|
assert decode['txid'] == prep['txid']
|
||||||
# 4 inputs, 2 outputs.
|
# 4 inputs, 2 outputs.
|
||||||
@@ -231,8 +230,7 @@ def test_txprepare(node_factory, bitcoind):
|
|||||||
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
assert decode['vout'][changenum]['scriptPubKey']['type'] == 'witness_v0_keyhash'
|
||||||
|
|
||||||
# Now prepare one with no change.
|
# Now prepare one with no change.
|
||||||
prep2 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep2 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep2['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep2['unsigned_tx'])
|
||||||
assert decode['txid'] == prep2['txid']
|
assert decode['txid'] == prep2['txid']
|
||||||
# 6 inputs, 1 outputs.
|
# 6 inputs, 1 outputs.
|
||||||
@@ -250,8 +248,7 @@ def test_txprepare(node_factory, bitcoind):
|
|||||||
assert discard['txid'] == prep['txid']
|
assert discard['txid'] == prep['txid']
|
||||||
assert discard['unsigned_tx'] == prep['unsigned_tx']
|
assert discard['unsigned_tx'] == prep['unsigned_tx']
|
||||||
|
|
||||||
prep3 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep3 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep3['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep3['unsigned_tx'])
|
||||||
assert decode['txid'] == prep3['txid']
|
assert decode['txid'] == prep3['txid']
|
||||||
# 4 inputs, 1 outputs.
|
# 4 inputs, 1 outputs.
|
||||||
@@ -271,8 +268,7 @@ def test_txprepare(node_factory, bitcoind):
|
|||||||
# Discard everything, we should now spend all inputs.
|
# Discard everything, we should now spend all inputs.
|
||||||
l1.rpc.txdiscard(prep2['txid'])
|
l1.rpc.txdiscard(prep2['txid'])
|
||||||
l1.rpc.txdiscard(prep3['txid'])
|
l1.rpc.txdiscard(prep3['txid'])
|
||||||
prep4 = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep4 = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep4['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep4['unsigned_tx'])
|
||||||
assert decode['txid'] == prep4['txid']
|
assert decode['txid'] == prep4['txid']
|
||||||
# 10 inputs, 1 outputs.
|
# 10 inputs, 1 outputs.
|
||||||
@@ -299,8 +295,7 @@ def test_txsend(node_factory, bitcoind):
|
|||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 10)
|
||||||
|
|
||||||
prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': Millisatoshi(amount * 3 * 1000)}])
|
||||||
Millisatoshi(amount * 3 * 1000))
|
|
||||||
out = l1.rpc.txsend(prep['txid'])
|
out = l1.rpc.txsend(prep['txid'])
|
||||||
|
|
||||||
# Cannot discard after send!
|
# Cannot discard after send!
|
||||||
@@ -343,8 +338,7 @@ def test_txprepare_restart(node_factory, bitcoind):
|
|||||||
bitcoind.generate_block(1)
|
bitcoind.generate_block(1)
|
||||||
wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10)
|
wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 10)
|
||||||
|
|
||||||
prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
||||||
assert decode['txid'] == prep['txid']
|
assert decode['txid'] == prep['txid']
|
||||||
# All 10 inputs
|
# All 10 inputs
|
||||||
@@ -359,8 +353,7 @@ def test_txprepare_restart(node_factory, bitcoind):
|
|||||||
with pytest.raises(RpcError, match=r'not an unreleased txid'):
|
with pytest.raises(RpcError, match=r'not an unreleased txid'):
|
||||||
l1.rpc.txdiscard(prep['txid'])
|
l1.rpc.txdiscard(prep['txid'])
|
||||||
|
|
||||||
prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
||||||
assert decode['txid'] == prep['txid']
|
assert decode['txid'] == prep['txid']
|
||||||
@@ -377,8 +370,7 @@ def test_txprepare_restart(node_factory, bitcoind):
|
|||||||
for i in decode['vin']:
|
for i in decode['vin']:
|
||||||
assert l1.daemon.is_in_log('wallet: reserved output {}/{} reset to available'.format(i['txid'], i['vout']))
|
assert l1.daemon.is_in_log('wallet: reserved output {}/{} reset to available'.format(i['txid'], i['vout']))
|
||||||
|
|
||||||
prep = l1.rpc.txprepare('bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg',
|
prep = l1.rpc.txprepare([{'bcrt1qeyyk6sl5pr49ycpqyckvmttus5ttj25pd0zpvg': 'all'}])
|
||||||
'all')
|
|
||||||
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
decode = bitcoind.rpc.decoderawtransaction(prep['unsigned_tx'])
|
||||||
assert decode['txid'] == prep['txid']
|
assert decode['txid'] == prep['txid']
|
||||||
# All 10 inputs
|
# All 10 inputs
|
||||||
|
|||||||
@@ -216,6 +216,7 @@ class Type(FieldSet):
|
|||||||
'bitcoin_tx',
|
'bitcoin_tx',
|
||||||
'wirestring',
|
'wirestring',
|
||||||
'per_peer_state',
|
'per_peer_state',
|
||||||
|
'bitcoin_tx_output',
|
||||||
]
|
]
|
||||||
|
|
||||||
# Some BOLT types are re-typed based on their field name
|
# Some BOLT types are re-typed based on their field name
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ struct unreleased_tx {
|
|||||||
struct list_node list;
|
struct list_node list;
|
||||||
/* All the utxos. */
|
/* All the utxos. */
|
||||||
struct wallet_tx *wtx;
|
struct wallet_tx *wtx;
|
||||||
/* Scriptpubkey this pays to. */
|
/* Outputs(scriptpubkey and satoshi) this pays to. */
|
||||||
const u8 *destination;
|
struct bitcoin_tx_output **outputs;
|
||||||
/* The tx itself (unsigned initially) */
|
/* The tx itself (unsigned initially) */
|
||||||
struct bitcoin_tx *tx;
|
struct bitcoin_tx *tx;
|
||||||
struct bitcoin_txid txid;
|
struct bitcoin_txid txid;
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ static struct command_result *param_bitcoin_address(struct command *cmd,
|
|||||||
scriptpubkey)) {
|
scriptpubkey)) {
|
||||||
case ADDRESS_PARSE_UNRECOGNIZED:
|
case ADDRESS_PARSE_UNRECOGNIZED:
|
||||||
return command_fail(cmd, LIGHTNINGD,
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
"Could not parse destination address");
|
"Could not parse destination address, "
|
||||||
|
"%s should be a valid address",
|
||||||
|
name ? name : "address field");
|
||||||
case ADDRESS_PARSE_WRONG_NETWORK:
|
case ADDRESS_PARSE_WRONG_NETWORK:
|
||||||
return command_fail(cmd, LIGHTNINGD,
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
"Destination address is not on network %s",
|
"Destination address is not on network %s",
|
||||||
@@ -117,7 +119,8 @@ static struct command_result *broadcast_and_wait(struct command *cmd,
|
|||||||
utx->wtx->amount,
|
utx->wtx->amount,
|
||||||
utx->wtx->change,
|
utx->wtx->change,
|
||||||
utx->wtx->change_key_index,
|
utx->wtx->change_key_index,
|
||||||
utx->destination,
|
cast_const2(const struct bitcoin_tx_output **,
|
||||||
|
utx->outputs),
|
||||||
utx->wtx->utxos);
|
utx->wtx->utxos);
|
||||||
|
|
||||||
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
|
if (!wire_sync_write(cmd->ld->hsm_fd, take(msg)))
|
||||||
@@ -152,36 +155,65 @@ static struct command_result *broadcast_and_wait(struct command *cmd,
|
|||||||
|
|
||||||
/* Common code for withdraw and txprepare.
|
/* Common code for withdraw and txprepare.
|
||||||
*
|
*
|
||||||
* Returns NULL on success, and fills in wtx, destination and
|
* Returns NULL on success, and fills in wtx, output and
|
||||||
* maybe changekey (owned by cmd). Otherwise, cmd has failed, so don't
|
* maybe changekey (owned by cmd). Otherwise, cmd has failed, so don't
|
||||||
* access it! (It's been freed). */
|
* access it! (It's been freed). */
|
||||||
static struct command_result *json_prepare_tx(struct command *cmd,
|
static struct command_result *json_prepare_tx(struct command *cmd,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *params,
|
const jsmntok_t *params,
|
||||||
struct unreleased_tx **utx)
|
struct unreleased_tx **utx,
|
||||||
|
bool for_withdraw)
|
||||||
{
|
{
|
||||||
u32 *feerate_per_kw;
|
u32 *feerate_per_kw;
|
||||||
struct command_result *res;
|
struct command_result *res;
|
||||||
u32 *minconf, maxheight;
|
u32 *minconf, maxheight;
|
||||||
struct pubkey *changekey;
|
struct pubkey *changekey;
|
||||||
struct bitcoin_tx_output **outputs;
|
struct bitcoin_tx_output **outputs;
|
||||||
|
const jsmntok_t *outputstok, *t;
|
||||||
|
const u8 *destination = NULL;
|
||||||
|
size_t out_len, i;
|
||||||
|
|
||||||
*utx = tal(cmd, struct unreleased_tx);
|
*utx = tal(cmd, struct unreleased_tx);
|
||||||
(*utx)->wtx = tal(*utx, struct wallet_tx);
|
(*utx)->wtx = tal(*utx, struct wallet_tx);
|
||||||
wtx_init(cmd, (*utx)->wtx, AMOUNT_SAT(-1ULL));
|
wtx_init(cmd, (*utx)->wtx, AMOUNT_SAT(-1ULL));
|
||||||
outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 0);
|
|
||||||
|
|
||||||
|
if (!for_withdraw) {
|
||||||
|
/* From v0.7.3, the new style for *txprepare* use array of outputs
|
||||||
|
* to replace original 'destination' and 'satoshi' parameters.*/
|
||||||
|
if (!param(cmd, buffer, params,
|
||||||
|
p_req("outputs", param_array, &outputstok),
|
||||||
|
p_opt("feerate", param_feerate, &feerate_per_kw),
|
||||||
|
p_opt_def("minconf", param_number, &minconf, 1),
|
||||||
|
NULL)) {
|
||||||
|
|
||||||
|
/* For generating help, give new-style. */
|
||||||
|
if (!params || !deprecated_apis)
|
||||||
|
return command_param_failed();
|
||||||
|
|
||||||
|
/* For the old style:
|
||||||
|
* *txprepare* 'destination' 'satoshi' ['feerate'] ['minconf'] */
|
||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
p_req("destination", param_bitcoin_address,
|
p_req("destination", param_bitcoin_address,
|
||||||
&(*utx)->destination),
|
&destination),
|
||||||
|
p_req("satoshi", param_wtx, (*utx)->wtx),
|
||||||
|
p_opt("feerate", param_feerate, &feerate_per_kw),
|
||||||
|
p_opt_def("minconf", param_number, &minconf, 1),
|
||||||
|
NULL))
|
||||||
|
/* If the parameters mixed the new style and the old style,
|
||||||
|
* fail it. */
|
||||||
|
return command_param_failed();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* *withdraw* command still use 'destination' and 'satoshi' as parameters. */
|
||||||
|
if (!param(cmd, buffer, params,
|
||||||
|
p_req("destination", param_bitcoin_address,
|
||||||
|
&destination),
|
||||||
p_req("satoshi", param_wtx, (*utx)->wtx),
|
p_req("satoshi", param_wtx, (*utx)->wtx),
|
||||||
p_opt("feerate", param_feerate, &feerate_per_kw),
|
p_opt("feerate", param_feerate, &feerate_per_kw),
|
||||||
p_opt_def("minconf", param_number, &minconf, 1),
|
p_opt_def("minconf", param_number, &minconf, 1),
|
||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
return command_param_failed();
|
||||||
|
}
|
||||||
/* Destination is owned by cmd: change that to be owned by utx. */
|
|
||||||
tal_steal(*utx, (*utx)->destination);
|
|
||||||
|
|
||||||
if (!feerate_per_kw) {
|
if (!feerate_per_kw) {
|
||||||
res = param_feerate_estimate(cmd, &feerate_per_kw,
|
res = param_feerate_estimate(cmd, &feerate_per_kw,
|
||||||
@@ -191,11 +223,97 @@ static struct command_result *json_prepare_tx(struct command *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
|
maxheight = minconf_to_maxheight(*minconf, cmd->ld);
|
||||||
|
|
||||||
|
/* *withdraw* command or old *txprepare* command.
|
||||||
|
* Support only one output. */
|
||||||
|
if (destination) {
|
||||||
|
outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, 1);
|
||||||
|
outputs[0] = tal(outputs, struct bitcoin_tx_output);
|
||||||
|
outputs[0]->script = tal_steal(outputs[0],
|
||||||
|
cast_const(u8 *, destination));
|
||||||
|
outputs[0]->amount = (*utx)->wtx->amount;
|
||||||
|
out_len = tal_count(outputs[0]->script);
|
||||||
|
|
||||||
|
goto create_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputstok->size == 0)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Empty outputs");
|
||||||
|
|
||||||
|
outputs = tal_arr(tmpctx, struct bitcoin_tx_output *, outputstok->size);
|
||||||
|
out_len = 0;
|
||||||
|
(*utx)->wtx->all_funds = false;
|
||||||
|
(*utx)->wtx->amount = AMOUNT_SAT(0);
|
||||||
|
json_for_each_arr(i, t, outputstok) {
|
||||||
|
struct amount_sat *amount;
|
||||||
|
const u8 *destination;
|
||||||
|
enum address_parse_result res;
|
||||||
|
|
||||||
|
/* output format: {destination: amount} */
|
||||||
|
if (t->type != JSMN_OBJECT)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"The output format must be "
|
||||||
|
"{destination: amount}");;
|
||||||
|
|
||||||
|
res = json_tok_address_scriptpubkey(cmd,
|
||||||
|
get_chainparams(cmd->ld),
|
||||||
|
buffer, &t[1],
|
||||||
|
&destination);
|
||||||
|
if (res == ADDRESS_PARSE_UNRECOGNIZED)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"Could not parse destination address");
|
||||||
|
else if (res == ADDRESS_PARSE_WRONG_NETWORK)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"Destination address is not on network %s",
|
||||||
|
get_chainparams(cmd->ld)->network_name);
|
||||||
|
|
||||||
|
amount = tal(tmpctx, struct amount_sat);
|
||||||
|
if (!json_to_sat_or_all(buffer, &t[2], amount))
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"'%.*s' is a invalid satoshi amount",
|
||||||
|
t[2].end - t[2].start, buffer + t[2].start);
|
||||||
|
|
||||||
|
out_len += tal_count(destination);
|
||||||
|
outputs[i] = tal(outputs, struct bitcoin_tx_output);
|
||||||
|
outputs[i]->amount = *amount;
|
||||||
|
outputs[i]->script = tal_steal(outputs[i],
|
||||||
|
cast_const(u8 *, destination));
|
||||||
|
|
||||||
|
/* In fact, the maximum amount of bitcoin satoshi is 2.1e15.
|
||||||
|
* It can't be equal to/bigger than 2^64.
|
||||||
|
* On the hand, the maximum amount of litoshi is 8.4e15,
|
||||||
|
* which also can't overflow. */
|
||||||
|
/* This means this destination need "all" satoshi we have. */
|
||||||
|
if (amount_sat_eq(*amount, AMOUNT_SAT(-1ULL))) {
|
||||||
|
if (outputstok->size > 1)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"outputs[%zi]: this destination wants"
|
||||||
|
" all satoshi. The count of outputs"
|
||||||
|
" can't be more than 1. ", i);
|
||||||
|
(*utx)->wtx->all_funds = true;
|
||||||
|
/* `AMOUNT_SAT(-1ULL)` is the max permissible for `wallet_select_all`. */
|
||||||
|
(*utx)->wtx->amount = *amount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amount_sat_add(&(*utx)->wtx->amount, (*utx)->wtx->amount, *amount))
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"outputs: The sum of first %zi outputs"
|
||||||
|
" overflow. ", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_tx:
|
||||||
|
(*utx)->outputs = tal_steal(*utx, outputs);
|
||||||
res = wtx_select_utxos((*utx)->wtx, *feerate_per_kw,
|
res = wtx_select_utxos((*utx)->wtx, *feerate_per_kw,
|
||||||
tal_count((*utx)->destination), maxheight);
|
out_len, maxheight);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
/* Because of the max limit of AMOUNT_SAT(-1ULL),
|
||||||
|
* `(*utx)->wtx->all_funds` won't change in `wtx_select_utxos()` */
|
||||||
|
if ((*utx)->wtx->all_funds)
|
||||||
|
outputs[0]->amount = (*utx)->wtx->amount;
|
||||||
|
|
||||||
if (!amount_sat_eq((*utx)->wtx->change, AMOUNT_SAT(0))) {
|
if (!amount_sat_eq((*utx)->wtx->change, AMOUNT_SAT(0))) {
|
||||||
changekey = tal(tmpctx, struct pubkey);
|
changekey = tal(tmpctx, struct pubkey);
|
||||||
if (!bip32_pubkey(cmd->ld->wallet->bip32_base, changekey,
|
if (!bip32_pubkey(cmd->ld->wallet->bip32_base, changekey,
|
||||||
@@ -203,15 +321,8 @@ static struct command_result *json_prepare_tx(struct command *cmd,
|
|||||||
return command_fail(cmd, LIGHTNINGD, "Keys generation failure");
|
return command_fail(cmd, LIGHTNINGD, "Keys generation failure");
|
||||||
} else
|
} else
|
||||||
changekey = NULL;
|
changekey = NULL;
|
||||||
|
|
||||||
struct bitcoin_tx_output *output = tal(outputs,
|
|
||||||
struct bitcoin_tx_output);
|
|
||||||
output->script = tal_dup_arr(output, u8, (*utx)->destination,
|
|
||||||
tal_count((*utx)->destination), 0);
|
|
||||||
output->amount = (*utx)->wtx->amount;
|
|
||||||
tal_arr_expand(&outputs, output);
|
|
||||||
(*utx)->tx = withdraw_tx(*utx, get_chainparams(cmd->ld),
|
(*utx)->tx = withdraw_tx(*utx, get_chainparams(cmd->ld),
|
||||||
(*utx)->wtx->utxos, outputs,
|
(*utx)->wtx->utxos, (*utx)->outputs,
|
||||||
changekey, (*utx)->wtx->change,
|
changekey, (*utx)->wtx->change,
|
||||||
cmd->ld->wallet->bip32_base,
|
cmd->ld->wallet->bip32_base,
|
||||||
&(*utx)->change_outnum);
|
&(*utx)->change_outnum);
|
||||||
@@ -229,7 +340,7 @@ static struct command_result *json_txprepare(struct command *cmd,
|
|||||||
struct command_result *res;
|
struct command_result *res;
|
||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
|
|
||||||
res = json_prepare_tx(cmd, buffer, params, &utx);
|
res = json_prepare_tx(cmd, buffer, params, &utx, false);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
@@ -352,7 +463,7 @@ static struct command_result *json_withdraw(struct command *cmd,
|
|||||||
struct command_result *res;
|
struct command_result *res;
|
||||||
struct bitcoin_txid txid;
|
struct bitcoin_txid txid;
|
||||||
|
|
||||||
res = json_prepare_tx(cmd, buffer, params, &utx);
|
res = json_prepare_tx(cmd, buffer, params, &utx, true);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
|||||||
@@ -378,3 +378,14 @@ void fromwire_bip32_key_version(const u8** cursor, size_t *max,
|
|||||||
version->bip32_pubkey_version = fromwire_u32(cursor, max);
|
version->bip32_pubkey_version = fromwire_u32(cursor, max);
|
||||||
version->bip32_privkey_version = fromwire_u32(cursor, max);
|
version->bip32_privkey_version = fromwire_u32(cursor, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx,
|
||||||
|
const u8 **cursor, size_t *max)
|
||||||
|
{
|
||||||
|
struct bitcoin_tx_output *output = tal(ctx, struct bitcoin_tx_output);
|
||||||
|
output->amount = fromwire_amount_sat(cursor, max);
|
||||||
|
u16 script_len = fromwire_u16(cursor, max);
|
||||||
|
output->script = tal_arr(output, u8, script_len);
|
||||||
|
fromwire_u8_array(cursor, max, output->script, script_len);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|||||||
@@ -249,3 +249,10 @@ void towire_bip32_key_version(u8 **pptr, const struct bip32_key_version *version
|
|||||||
towire_u32(pptr, version->bip32_pubkey_version);
|
towire_u32(pptr, version->bip32_pubkey_version);
|
||||||
towire_u32(pptr, version->bip32_privkey_version);
|
towire_u32(pptr, version->bip32_privkey_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output)
|
||||||
|
{
|
||||||
|
towire_amount_sat(pptr, output->amount);
|
||||||
|
towire_u16(pptr, tal_count(output->script));
|
||||||
|
towire_u8_array(pptr, output->script, tal_count(output->script));
|
||||||
|
}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ void towire_wirestring(u8 **pptr, const char *str);
|
|||||||
void towire_siphash_seed(u8 **cursor, const struct siphash_seed *seed);
|
void towire_siphash_seed(u8 **cursor, const struct siphash_seed *seed);
|
||||||
|
|
||||||
void towire_bip32_key_version(u8 **cursor, const struct bip32_key_version *version);
|
void towire_bip32_key_version(u8 **cursor, const struct bip32_key_version *version);
|
||||||
|
void towire_bitcoin_tx_output(u8 **pptr, const struct bitcoin_tx_output *output);
|
||||||
|
|
||||||
const u8 *fromwire(const u8 **cursor, size_t *max, void *copy, size_t n);
|
const u8 *fromwire(const u8 **cursor, size_t *max, void *copy, size_t n);
|
||||||
u8 fromwire_u8(const u8 **cursor, size_t *max);
|
u8 fromwire_u8(const u8 **cursor, size_t *max);
|
||||||
@@ -138,4 +139,6 @@ void fromwire_siphash_seed(const u8 **cursor, size_t *max,
|
|||||||
struct siphash_seed *seed);
|
struct siphash_seed *seed);
|
||||||
void fromwire_bip32_key_version(const u8 **cursor, size_t *max,
|
void fromwire_bip32_key_version(const u8 **cursor, size_t *max,
|
||||||
struct bip32_key_version *version);
|
struct bip32_key_version *version);
|
||||||
|
struct bitcoin_tx_output *fromwire_bitcoin_tx_output(const tal_t *ctx,
|
||||||
|
const u8 **cursor, size_t *max);
|
||||||
#endif /* LIGHTNING_WIRE_WIRE_H */
|
#endif /* LIGHTNING_WIRE_WIRE_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user