mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
daemon: have user supply UTXO for enchor input.
This lets us ensure that anchor tx has witness scripts for inputs, and thus is immalleable. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -438,149 +438,6 @@ void bitcoind_send_tx(struct lightningd_state *dstate,
|
||||
tal_free(raw);
|
||||
}
|
||||
|
||||
struct funding_process {
|
||||
struct peer *peer;
|
||||
void (*cb)(struct lightningd_state *state,
|
||||
const struct bitcoin_tx *tx,
|
||||
int change_output,
|
||||
struct peer *peer);
|
||||
int change_output;
|
||||
};
|
||||
|
||||
static void process_signrawtransaction(struct bitcoin_cli *bcli)
|
||||
{
|
||||
const jsmntok_t *tokens, *hex, *complete;
|
||||
bool valid;
|
||||
struct bitcoin_tx *tx;
|
||||
struct funding_process *f = bcli->cb_arg;
|
||||
|
||||
/* Output:
|
||||
"{\n"
|
||||
" \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n"
|
||||
" \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
|
||||
...*/
|
||||
if (!bcli->output)
|
||||
fatal("%s signrawtransaction %s: failed",
|
||||
bcli->args[0], bcli->args[3]);
|
||||
|
||||
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
|
||||
if (!tokens)
|
||||
fatal("%s signrawtransaction %s: %s response (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
valid ? "partial" : "invalid",
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
if (tokens[0].type != JSMN_OBJECT)
|
||||
fatal("%s signrawtransaction %s: gave non-object (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
|
||||
complete = json_get_member(bcli->output, tokens, "complete");
|
||||
if (!complete)
|
||||
fatal("%s signrawtransaction %s had no complete member (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
if (complete->end - complete->start != strlen("true")
|
||||
|| strncmp(bcli->output + complete->start, "true", strlen("true")))
|
||||
fatal("%s signrawtransaction %s not complete (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
complete->end - complete->start,
|
||||
bcli->output + complete->start);
|
||||
|
||||
hex = json_get_member(bcli->output, tokens, "hex");
|
||||
if (!hex)
|
||||
fatal("%s signrawtransaction %s had no hex member (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
|
||||
tx = bitcoin_tx_from_hex(bcli, bcli->output + hex->start,
|
||||
hex->end - hex->start);
|
||||
if (!tx)
|
||||
fatal("%s signrawtransaction %s had bad hex member (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
hex->end - hex->start, bcli->output + hex->start);
|
||||
|
||||
f->cb(bcli->dstate, tx, f->change_output, f->peer);
|
||||
}
|
||||
|
||||
/* FIXME: handle lack of funds! */
|
||||
static void process_fundrawtransaction(struct bitcoin_cli *bcli)
|
||||
{
|
||||
const jsmntok_t *tokens, *hex, *changepos;
|
||||
char *hexstr, *end;
|
||||
bool valid;
|
||||
struct funding_process *f = bcli->cb_arg;
|
||||
|
||||
/* Output:
|
||||
"{\n"
|
||||
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
|
||||
" \"fee\": n, (numeric) Fee the resulting transaction pays\n"
|
||||
" \"changepos\": n (numeric) The position of the added change output, or -1\n"
|
||||
"}\n"
|
||||
*/
|
||||
if (!bcli->output)
|
||||
fatal("%s fundrawtransaction %s: failed",
|
||||
bcli->args[0], bcli->args[3]);
|
||||
|
||||
tokens = json_parse_input(bcli->output, bcli->output_bytes, &valid);
|
||||
if (!tokens)
|
||||
fatal("%s fundrawtransaction %s: %s response (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
valid ? "partial" : "invalid",
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
if (tokens[0].type != JSMN_OBJECT)
|
||||
fatal("%s fundrawtransaction %s: gave non-object (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
hex = json_get_member(bcli->output, tokens, "hex");
|
||||
if (!hex)
|
||||
fatal("%s fundrawtransaction %s had no hex member (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
|
||||
changepos = json_get_member(bcli->output, tokens, "changepos");
|
||||
if (!changepos)
|
||||
fatal("%s fundrawtransaction %s had no changepos member (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
(int)bcli->output_bytes, bcli->output);
|
||||
f->change_output = strtol(bcli->output + changepos->start, &end, 0);
|
||||
if (end != bcli->output + changepos->end)
|
||||
fatal("%s fundrawtransaction %s had bad changepos (%.*s)?",
|
||||
bcli->args[0], bcli->args[2],
|
||||
changepos->end - changepos->start,
|
||||
bcli->output + changepos->start);
|
||||
|
||||
/* We need a nul-terminated string. */
|
||||
hexstr = tal_strndup(bcli, bcli->output + hex->start,
|
||||
hex->end - hex->start);
|
||||
/* Now we need to sign those inputs! */
|
||||
start_bitcoin_cli(bcli->dstate, process_signrawtransaction, NULL,
|
||||
f, "signrawtransaction", hexstr, NULL);
|
||||
}
|
||||
|
||||
/* Adds and signs inputs to this tx from wallet. */
|
||||
void bitcoind_fund_transaction(struct lightningd_state *dstate,
|
||||
struct bitcoin_tx *tx_no_inputs,
|
||||
void (*cb)(struct lightningd_state *dstate,
|
||||
const struct bitcoin_tx *tx,
|
||||
int change_output,
|
||||
struct peer *peer),
|
||||
struct peer *peer)
|
||||
{
|
||||
struct funding_process *f = tal(peer, struct funding_process);
|
||||
u8 *raw = linearize_tx_force_extended(dstate, tx_no_inputs);
|
||||
char *hex = tal_arr(raw, char, hex_str_size(tal_count(raw)));
|
||||
|
||||
assert(tx_no_inputs->input_count == 0);
|
||||
|
||||
hex_encode(raw, tal_count(raw), hex, tal_count(hex));
|
||||
|
||||
f->peer = peer;
|
||||
f->cb = cb;
|
||||
start_bitcoin_cli(dstate, process_fundrawtransaction, NULL, f,
|
||||
"fundrawtransaction", hex, NULL);
|
||||
tal_free(raw);
|
||||
}
|
||||
|
||||
static void process_getblock(struct bitcoin_cli *bcli)
|
||||
{
|
||||
const jsmntok_t *tokens, *mediantime;
|
||||
|
||||
@@ -53,14 +53,6 @@ void bitcoind_estimate_fee_(struct lightningd_state *dstate,
|
||||
void bitcoind_send_tx(struct lightningd_state *dstate,
|
||||
const struct bitcoin_tx *tx);
|
||||
|
||||
void bitcoind_fund_transaction(struct lightningd_state *dstate,
|
||||
struct bitcoin_tx *tx_no_inputs,
|
||||
void (*cb)(struct lightningd_state *dstate,
|
||||
const struct bitcoin_tx *tx,
|
||||
int change_output,
|
||||
struct peer *peer),
|
||||
struct peer *peer);
|
||||
|
||||
void bitcoind_get_mediantime(struct lightningd_state *dstate,
|
||||
const struct sha256_double *blockid,
|
||||
u32 *mediantime);
|
||||
|
||||
105
daemon/peer.c
105
daemon/peer.c
@@ -14,6 +14,7 @@
|
||||
#include "secrets.h"
|
||||
#include "state.h"
|
||||
#include "timeout.h"
|
||||
#include "wallet.h"
|
||||
#include <bitcoin/base58.h>
|
||||
#include <bitcoin/script.h>
|
||||
#include <bitcoin/tx.h>
|
||||
@@ -39,7 +40,7 @@ struct json_connecting {
|
||||
/* This owns us, so we're freed after command_fail or command_success */
|
||||
struct command *cmd;
|
||||
const char *name, *port;
|
||||
u64 satoshis;
|
||||
struct anchor_input *input;
|
||||
};
|
||||
|
||||
struct pending_cmd {
|
||||
@@ -450,7 +451,8 @@ static struct io_plan *peer_connected_out(struct io_conn *conn,
|
||||
}
|
||||
log_info(peer->log, "Connected out to %s:%s",
|
||||
connect->name, connect->port);
|
||||
peer->anchor.satoshis = connect->satoshis;
|
||||
|
||||
peer->anchor.input = tal_steal(peer, connect->input);
|
||||
|
||||
command_success(connect->cmd, null_response(connect));
|
||||
return peer_crypto_setup(conn, peer, peer_crypto_on);
|
||||
@@ -565,14 +567,17 @@ static void json_connect(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
struct json_connecting *connect;
|
||||
jsmntok_t *host, *port, *satoshis;
|
||||
jsmntok_t *host, *port, *txtok;
|
||||
struct bitcoin_tx *tx;
|
||||
int output;
|
||||
size_t txhexlen;
|
||||
|
||||
if (!json_get_params(buffer, params,
|
||||
"host", &host,
|
||||
"port", &port,
|
||||
"satoshis", &satoshis,
|
||||
"tx", &txtok,
|
||||
NULL)) {
|
||||
command_fail(cmd, "Need host, port and satoshis");
|
||||
command_fail(cmd, "Need host, port and tx to a wallet address");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -582,10 +587,35 @@ static void json_connect(struct command *cmd,
|
||||
host->end - host->start);
|
||||
connect->port = tal_strndup(connect, buffer + port->start,
|
||||
port->end - port->start);
|
||||
if (!json_tok_u64(buffer, satoshis, &connect->satoshis))
|
||||
command_fail(cmd, "'%.*s' is not a valid number",
|
||||
(int)(satoshis->end - satoshis->start),
|
||||
buffer + satoshis->start);
|
||||
connect->input = tal(connect, struct anchor_input);
|
||||
|
||||
txhexlen = txtok->end - txtok->start;
|
||||
tx = bitcoin_tx_from_hex(connect->input, buffer + txtok->start,
|
||||
txhexlen);
|
||||
if (!tx) {
|
||||
command_fail(cmd, "'%.*s' is not a valid transaction",
|
||||
txtok->end - txtok->start,
|
||||
buffer + txtok->start);
|
||||
return;
|
||||
}
|
||||
|
||||
bitcoin_txid(tx, &connect->input->txid);
|
||||
|
||||
/* Find an output we know how to spend. */
|
||||
connect->input->w = NULL;
|
||||
for (output = 0; output < tx->output_count; output++) {
|
||||
connect->input->w
|
||||
= wallet_can_spend(cmd->dstate, &tx->output[output]);
|
||||
if (connect->input->w)
|
||||
break;
|
||||
}
|
||||
if (!connect->input->w) {
|
||||
command_fail(cmd, "Tx doesn't send to wallet address");
|
||||
return;
|
||||
}
|
||||
|
||||
connect->input->index = output;
|
||||
connect->input->amount = tx->output[output].amount;
|
||||
if (!dns_resolve_and_connect(cmd->dstate, connect->name, connect->port,
|
||||
peer_connected_out, peer_failed, connect)) {
|
||||
command_fail(cmd, "DNS failed");
|
||||
@@ -1219,45 +1249,52 @@ const struct bitcoin_tx *bitcoin_htlc_spend(const struct peer *peer,
|
||||
FIXME_STUB(peer);
|
||||
}
|
||||
|
||||
static void created_anchor(struct lightningd_state *dstate,
|
||||
const struct bitcoin_tx *tx,
|
||||
int change_output,
|
||||
struct peer *peer)
|
||||
/* Now we can create anchor tx. */
|
||||
static void got_feerate(struct lightningd_state *dstate,
|
||||
u64 rate, struct peer *peer)
|
||||
{
|
||||
size_t real_out;
|
||||
u64 fee;
|
||||
struct bitcoin_tx *tx = bitcoin_tx(peer, 1, 1);
|
||||
|
||||
tx->output[0].script = scriptpubkey_p2sh(tx, peer->anchor.redeemscript);
|
||||
tx->output[0].script_length = tal_count(tx->output[0].script);
|
||||
|
||||
/* Add input script length. FIXME: This is normal case, not exact. */
|
||||
fee = fee_by_feerate(measure_tx_len(tx) + 1+73 + 1+33 + 1, rate);
|
||||
if (fee >= peer->anchor.input->amount)
|
||||
/* FIXME: Report an error here!
|
||||
* We really should set this when they do command, but
|
||||
* we need to modify state to allow immediate anchor
|
||||
* creation: using estimate_fee is a convenient workaround. */
|
||||
fatal("Amount %"PRIu64" below fee %"PRIu64,
|
||||
peer->anchor.input->amount, fee);
|
||||
|
||||
tx->output[0].amount = peer->anchor.input->amount - fee;
|
||||
|
||||
tx->input[0].txid = peer->anchor.input->txid;
|
||||
tx->input[0].index = peer->anchor.input->index;
|
||||
tx->input[0].amount = tal_dup(tx->input, u64,
|
||||
&peer->anchor.input->amount);
|
||||
|
||||
wallet_add_signed_input(peer->dstate, peer->anchor.input->w, tx, 0);
|
||||
|
||||
bitcoin_txid(tx, &peer->anchor.txid);
|
||||
if (change_output == -1)
|
||||
real_out = 0;
|
||||
else
|
||||
real_out = !change_output;
|
||||
|
||||
assert(find_p2sh_out(tx, peer->anchor.redeemscript) == real_out);
|
||||
peer->anchor.index = real_out;
|
||||
assert(peer->anchor.satoshis == tx->output[peer->anchor.index].amount);
|
||||
peer->anchor.tx = tx;
|
||||
peer->anchor.index = 0;
|
||||
/* We'll need this later, when we're told to broadcast it. */
|
||||
peer->anchor.tx = tal_steal(peer, tx);
|
||||
peer->anchor.satoshis = tx->output[0].amount;
|
||||
|
||||
state_event(peer, BITCOIN_ANCHOR_CREATED, NULL);
|
||||
}
|
||||
|
||||
/* Start creation of the bitcoin anchor tx. */
|
||||
/* Creation the bitcoin anchor tx, spending output user provided. */
|
||||
void bitcoin_create_anchor(struct peer *peer, enum state_input done)
|
||||
{
|
||||
struct bitcoin_tx *template = bitcoin_tx(peer, 0, 1);
|
||||
|
||||
/* We must be offering anchor for us to try creating it */
|
||||
assert(peer->us.offer_anchor);
|
||||
|
||||
template->output[0].amount = peer->anchor.satoshis;
|
||||
template->output[0].script
|
||||
= scriptpubkey_p2sh(template, peer->anchor.redeemscript);
|
||||
template->output[0].script_length
|
||||
= tal_count(template->output[0].script);
|
||||
|
||||
assert(done == BITCOIN_ANCHOR_CREATED);
|
||||
|
||||
bitcoind_fund_transaction(peer->dstate, template, created_anchor, peer);
|
||||
bitcoind_estimate_fee(peer->dstate, got_feerate, peer);
|
||||
}
|
||||
|
||||
/* We didn't end up broadcasting the anchor: release the utxos.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define LIGHTNING_DAEMON_PEER_H
|
||||
#include "config.h"
|
||||
#include "bitcoin/locktime.h"
|
||||
#include "bitcoin/privkey.h"
|
||||
#include "bitcoin/pubkey.h"
|
||||
#include "bitcoin/script.h"
|
||||
#include "bitcoin/shadouble.h"
|
||||
@@ -42,6 +43,15 @@ union htlc_staging {
|
||||
struct htlc_fail fail;
|
||||
};
|
||||
|
||||
struct anchor_input {
|
||||
struct sha256_double txid;
|
||||
unsigned int index;
|
||||
/* Amount of input (satoshis) */
|
||||
u64 amount;
|
||||
/* Wallet entry to use to spend. */
|
||||
struct wallet *w;
|
||||
};
|
||||
|
||||
struct commit_info {
|
||||
/* Previous one, if any. */
|
||||
struct commit_info *prev;
|
||||
@@ -131,6 +141,10 @@ struct peer {
|
||||
unsigned int index;
|
||||
u64 satoshis;
|
||||
u8 *redeemscript;
|
||||
|
||||
/* If we're creating anchor, this tells us where to source it */
|
||||
struct anchor_input *input;
|
||||
|
||||
/* If we created it, we keep entire tx. */
|
||||
const struct bitcoin_tx *tx;
|
||||
struct anchor_watch *watches;
|
||||
|
||||
@@ -16,8 +16,9 @@ REDIRERR1="$DIR1/errors"
|
||||
REDIRERR2="$DIR2/errors"
|
||||
FGREP="fgrep -q"
|
||||
|
||||
# setup.sh gives us 0.00999999 bitcoin, = 999999 satoshi = 999999000 millisatoshi
|
||||
AMOUNT=999999000
|
||||
# We inject 0.01 bitcoin, but then fees (estimatefee fails and we use a
|
||||
# fee rate as per the close tx).
|
||||
AMOUNT=996160000
|
||||
|
||||
# Default fee rate per kb.
|
||||
FEE_RATE=200000
|
||||
@@ -129,7 +130,7 @@ check_staged()
|
||||
check_tx_spend()
|
||||
{
|
||||
$CLI generate 1
|
||||
if [ $($CLI getblock $($CLI getbestblockhash) | grep -c '^ "') = 2 ]; then
|
||||
if [ $($CLI getblock $($CLI getbestblockhash) | grep -c '^ "') -gt 1 ]; then
|
||||
:
|
||||
else
|
||||
echo "Block didn't include tx:" >&2
|
||||
@@ -189,7 +190,12 @@ ID2=`$LCLI2 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'`
|
||||
|
||||
PORT2=`$LCLI2 getlog | sed -n 's/.*on port \([0-9]*\).*/\1/p'`
|
||||
|
||||
lcli1 connect localhost $PORT2 999999
|
||||
# Make a payment into a P2SH for anchor.
|
||||
P2SHADDR=`$LCLI1 newaddr | sed -n 's/{ "address" : "\(.*\)" }/\1/p'`
|
||||
TXID=`$CLI sendtoaddress $P2SHADDR 0.01`
|
||||
TX=`$CLI getrawtransaction $TXID`
|
||||
|
||||
lcli1 connect localhost $PORT2 $TX
|
||||
sleep 2
|
||||
|
||||
# Expect them to be waiting for anchor.
|
||||
|
||||
Reference in New Issue
Block a user