Rusty Russell
7 years ago
committed by
Christian Decker
68 changed files with 75 additions and 14951 deletions
@ -1 +1 @@ |
|||||
from .lightning import LightningRpc, LegacyLightningRpc |
from .lightning import LightningRpc |
||||
|
@ -1,359 +0,0 @@ |
|||||
#include "channel.h" |
|
||||
#include "htlc.h" |
|
||||
#include "remove_dust.h" |
|
||||
#include "type_to_string.h" |
|
||||
#include <assert.h> |
|
||||
#include <ccan/array_size/array_size.h> |
|
||||
#include <ccan/mem/mem.h> |
|
||||
#include <ccan/structeq/structeq.h> |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <inttypes.h> |
|
||||
#include <string.h> |
|
||||
|
|
||||
uint64_t fee_by_feerate(size_t txsize, uint64_t fee_rate) |
|
||||
{ |
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* The fee for a transaction MUST be calculated by multiplying this |
|
||||
* bytecount by the fee rate, dividing by 1000 and truncating |
|
||||
* (rounding down) the result to an even number of satoshis. |
|
||||
*/ |
|
||||
return txsize * fee_rate / 2000 * 2; |
|
||||
} |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node MUST use the formula 338 + 32 bytes for every non-dust HTLC |
|
||||
* as the bytecount for calculating commitment transaction fees. Note |
|
||||
* that the fee requirement is unchanged, even if the elimination of |
|
||||
* dust HTLC outputs has caused a non-zero fee already. |
|
||||
*/ |
|
||||
static size_t tx_bytes(size_t num_nondust_htlcs) |
|
||||
{ |
|
||||
return 338 + 32 * num_nondust_htlcs; |
|
||||
} |
|
||||
|
|
||||
static uint64_t calculate_fee_msat(size_t num_nondust_htlcs, |
|
||||
uint64_t fee_rate) |
|
||||
{ |
|
||||
/* milli-satoshis */ |
|
||||
return fee_by_feerate(tx_bytes(num_nondust_htlcs), fee_rate) * 1000; |
|
||||
} |
|
||||
|
|
||||
/* Pay this much fee, if possible. Return amount unpaid. */ |
|
||||
static uint64_t pay_fee(struct channel_oneside *side, uint64_t fee_msat) |
|
||||
{ |
|
||||
if (side->pay_msat >= fee_msat) { |
|
||||
side->pay_msat -= fee_msat; |
|
||||
side->fee_msat += fee_msat; |
|
||||
return 0; |
|
||||
} else { |
|
||||
uint64_t remainder = fee_msat - side->pay_msat; |
|
||||
side->fee_msat += side->pay_msat; |
|
||||
side->pay_msat = 0; |
|
||||
return remainder; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Charge the fee as per FIXME-OLD #2 */ |
|
||||
static void recalculate_fees(struct channel_oneside *a, |
|
||||
struct channel_oneside *b, |
|
||||
uint64_t fee_msat) |
|
||||
{ |
|
||||
uint64_t remainder; |
|
||||
|
|
||||
/* Fold in fees, to recalcuate again below. */ |
|
||||
a->pay_msat += a->fee_msat; |
|
||||
b->pay_msat += b->fee_msat; |
|
||||
a->fee_msat = b->fee_msat = 0; |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* 1. If each nodes can afford half the fee from their |
|
||||
* to-`final_key` output, reduce the two to-`final_key` |
|
||||
* outputs accordingly. |
|
||||
* |
|
||||
* 2. Otherwise, reduce the to-`final_key` output of one node |
|
||||
* which cannot afford the fee to zero (resulting in that |
|
||||
* entire output paying fees). If the remaining |
|
||||
* to-`final_key` output is greater than the fee remaining, |
|
||||
* reduce it accordingly, otherwise reduce it to zero to |
|
||||
* pay as much fee as possible. |
|
||||
*/ |
|
||||
remainder = pay_fee(a, fee_msat / 2) + pay_fee(b, fee_msat / 2); |
|
||||
|
|
||||
/* If there's anything left, the other side tries to pay for it. */ |
|
||||
remainder = pay_fee(a, remainder); |
|
||||
pay_fee(b, remainder); |
|
||||
} |
|
||||
|
|
||||
/* a transfers htlc_msat to a HTLC (gains it, if -ve) */ |
|
||||
static bool change_funding(uint64_t anchor_satoshis, |
|
||||
uint64_t fee_rate, |
|
||||
int64_t htlc_msat, |
|
||||
struct channel_oneside *a, |
|
||||
struct channel_oneside *b, |
|
||||
size_t num_nondust_htlcs, |
|
||||
bool must_afford_fee) |
|
||||
{ |
|
||||
uint64_t fee_msat; |
|
||||
uint64_t htlcs_total; |
|
||||
|
|
||||
htlcs_total = anchor_satoshis * 1000 |
|
||||
- (a->pay_msat + a->fee_msat + b->pay_msat + b->fee_msat); |
|
||||
|
|
||||
fee_msat = calculate_fee_msat(num_nondust_htlcs, fee_rate); |
|
||||
|
|
||||
/* If A is paying, can it afford it? */ |
|
||||
if (htlc_msat >= 0) { |
|
||||
uint64_t cost = htlc_msat; |
|
||||
if (must_afford_fee) |
|
||||
cost += fee_msat / 2; |
|
||||
if (cost > a->pay_msat + a->fee_msat) |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/* OK, now adjust funds for A, then recalculate fees. */ |
|
||||
a->pay_msat -= htlc_msat; |
|
||||
recalculate_fees(a, b, fee_msat); |
|
||||
|
|
||||
htlcs_total += htlc_msat; |
|
||||
assert(htlcs_total == anchor_satoshis * 1000 |
|
||||
- (a->pay_msat + a->fee_msat + b->pay_msat + b->fee_msat)); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
bool anchor_too_large(uint64_t anchor_satoshis) |
|
||||
{ |
|
||||
/* Anchor must fit in 32 bit. */ |
|
||||
return anchor_satoshis >= (1ULL << 32) / 1000; |
|
||||
} |
|
||||
|
|
||||
struct channel_state *initial_cstate(const tal_t *ctx, |
|
||||
uint64_t anchor_satoshis, |
|
||||
uint64_t fee_rate, |
|
||||
enum side funding) |
|
||||
{ |
|
||||
uint64_t fee_msat; |
|
||||
struct channel_state *cstate = talz(ctx, struct channel_state); |
|
||||
struct channel_oneside *funder, *fundee; |
|
||||
|
|
||||
cstate->fee_rate = fee_rate; |
|
||||
cstate->anchor = anchor_satoshis; |
|
||||
cstate->num_nondust = 0; |
|
||||
|
|
||||
/* Anchor must fit in 32 bit. */ |
|
||||
assert(!anchor_too_large(anchor_satoshis)); |
|
||||
|
|
||||
fee_msat = calculate_fee_msat(0, fee_rate); |
|
||||
if (fee_msat > anchor_satoshis * 1000) |
|
||||
return tal_free(cstate); |
|
||||
|
|
||||
funder = &cstate->side[funding]; |
|
||||
fundee = &cstate->side[!funding]; |
|
||||
|
|
||||
/* Neither side has HTLCs. */ |
|
||||
funder->num_htlcs = fundee->num_htlcs = 0; |
|
||||
|
|
||||
/* Initially, all goes back to funder. */ |
|
||||
funder->pay_msat = anchor_satoshis * 1000 - fee_msat; |
|
||||
funder->fee_msat = fee_msat; |
|
||||
|
|
||||
/* Make sure it checks out. */ |
|
||||
assert(change_funding(anchor_satoshis, fee_rate, 0, funder, fundee, 0, false)); |
|
||||
assert(funder->fee_msat == fee_msat); |
|
||||
assert(fundee->fee_msat == 0); |
|
||||
|
|
||||
return cstate; |
|
||||
} |
|
||||
|
|
||||
/* FIXME: Write exact variant! */ |
|
||||
uint64_t approx_max_feerate(const struct channel_state *cstate, |
|
||||
enum side side) |
|
||||
{ |
|
||||
uint64_t max_funds; |
|
||||
|
|
||||
max_funds = cstate->side[side].pay_msat + cstate->side[side].fee_msat; |
|
||||
|
|
||||
return max_funds / tx_bytes(cstate->num_nondust); |
|
||||
} |
|
||||
|
|
||||
bool can_afford_feerate(const struct channel_state *cstate, uint64_t fee_rate, |
|
||||
enum side side) |
|
||||
{ |
|
||||
u64 fee_msat = calculate_fee_msat(cstate->num_nondust, fee_rate); |
|
||||
|
|
||||
return cstate->side[side].pay_msat + cstate->side[side].fee_msat |
|
||||
>= fee_msat; |
|
||||
} |
|
||||
|
|
||||
void adjust_fee(struct channel_state *cstate, uint64_t fee_rate) |
|
||||
{ |
|
||||
uint64_t fee_msat; |
|
||||
|
|
||||
fee_msat = calculate_fee_msat(cstate->num_nondust, fee_rate); |
|
||||
|
|
||||
recalculate_fees(&cstate->side[LOCAL], &cstate->side[REMOTE], fee_msat); |
|
||||
} |
|
||||
|
|
||||
bool force_fee(struct channel_state *cstate, uint64_t fee) |
|
||||
{ |
|
||||
/* Beware overflow! */ |
|
||||
if (fee > 0xFFFFFFFFFFFFFFFFULL / 1000) |
|
||||
return false; |
|
||||
recalculate_fees(&cstate->side[LOCAL], &cstate->side[REMOTE], fee * 1000); |
|
||||
return cstate->side[LOCAL].fee_msat + cstate->side[REMOTE].fee_msat == fee * 1000; |
|
||||
} |
|
||||
|
|
||||
/* Add a HTLC to @creator if it can afford it. */ |
|
||||
bool cstate_add_htlc(struct channel_state *cstate, const struct htlc *htlc, |
|
||||
bool must_afford_fee) |
|
||||
{ |
|
||||
size_t nondust; |
|
||||
struct channel_oneside *creator, *recipient; |
|
||||
|
|
||||
creator = &cstate->side[htlc_owner(htlc)]; |
|
||||
recipient = &cstate->side[!htlc_owner(htlc)]; |
|
||||
|
|
||||
/* Remember to count the new one in total txsize if not dust! */ |
|
||||
nondust = cstate->num_nondust; |
|
||||
if (!is_dust(htlc->msatoshi / 1000)) |
|
||||
nondust++; |
|
||||
|
|
||||
if (!change_funding(cstate->anchor, cstate->fee_rate, |
|
||||
htlc->msatoshi, creator, recipient, nondust, |
|
||||
must_afford_fee)) |
|
||||
return false; |
|
||||
|
|
||||
cstate->num_nondust = nondust; |
|
||||
creator->num_htlcs++; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
/* Remove htlc from creator, credit it to beneficiary. */ |
|
||||
static void remove_htlc(struct channel_state *cstate, |
|
||||
enum side creator, |
|
||||
enum side beneficiary, |
|
||||
const struct htlc *htlc) |
|
||||
{ |
|
||||
size_t nondust; |
|
||||
|
|
||||
/* Remember to remove this one in total txsize if not dust! */ |
|
||||
nondust = cstate->num_nondust; |
|
||||
if (!is_dust(htlc->msatoshi / 1000)) { |
|
||||
assert(nondust > 0); |
|
||||
nondust--; |
|
||||
} |
|
||||
|
|
||||
/* Can't fail since msatoshi is positive. */ |
|
||||
if (!change_funding(cstate->anchor, cstate->fee_rate, |
|
||||
-(int64_t)htlc->msatoshi, |
|
||||
&cstate->side[beneficiary], |
|
||||
&cstate->side[!beneficiary], nondust, false)) |
|
||||
abort(); |
|
||||
|
|
||||
/* Actually remove the HTLC. */ |
|
||||
assert(cstate->side[creator].num_htlcs > 0); |
|
||||
cstate->side[creator].num_htlcs--; |
|
||||
cstate->num_nondust = nondust; |
|
||||
} |
|
||||
|
|
||||
void cstate_fail_htlc(struct channel_state *cstate, const struct htlc *htlc) |
|
||||
{ |
|
||||
remove_htlc(cstate, htlc_owner(htlc), htlc_owner(htlc), htlc); |
|
||||
} |
|
||||
|
|
||||
void cstate_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc) |
|
||||
{ |
|
||||
remove_htlc(cstate, htlc_owner(htlc), !htlc_owner(htlc), htlc); |
|
||||
} |
|
||||
|
|
||||
struct channel_state *copy_cstate(const tal_t *ctx, |
|
||||
const struct channel_state *cstate) |
|
||||
{ |
|
||||
return tal_dup(ctx, struct channel_state, cstate); |
|
||||
} |
|
||||
|
|
||||
void force_add_htlc(struct channel_state *cstate, const struct htlc *htlc) |
|
||||
{ |
|
||||
struct channel_oneside *creator; |
|
||||
|
|
||||
creator = &cstate->side[htlc_owner(htlc)]; |
|
||||
creator->num_htlcs++; |
|
||||
creator->pay_msat -= htlc->msatoshi; |
|
||||
|
|
||||
/* Remember to count the new one in total txsize if not dust! */ |
|
||||
if (!is_dust(htlc->msatoshi / 1000)) |
|
||||
cstate->num_nondust++; |
|
||||
} |
|
||||
|
|
||||
static void force_remove_htlc(struct channel_state *cstate, |
|
||||
enum side beneficiary, |
|
||||
const struct htlc *htlc) |
|
||||
{ |
|
||||
cstate->side[beneficiary].pay_msat += htlc->msatoshi; |
|
||||
cstate->side[htlc_owner(htlc)].num_htlcs--; |
|
||||
if (!is_dust(htlc->msatoshi / 1000)) |
|
||||
cstate->num_nondust--; |
|
||||
} |
|
||||
|
|
||||
void force_fail_htlc(struct channel_state *cstate, const struct htlc *htlc) |
|
||||
{ |
|
||||
force_remove_htlc(cstate, htlc_owner(htlc), htlc); |
|
||||
} |
|
||||
|
|
||||
void force_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc) |
|
||||
{ |
|
||||
force_remove_htlc(cstate, !htlc_owner(htlc), htlc); |
|
||||
} |
|
||||
|
|
||||
bool balance_after_force(struct channel_state *cstate) |
|
||||
{ |
|
||||
/* We should not spend more than anchor */ |
|
||||
if (cstate->side[LOCAL].pay_msat + cstate->side[REMOTE].pay_msat |
|
||||
> cstate->anchor * 1000) |
|
||||
return false; |
|
||||
|
|
||||
/* Check for wrap. */ |
|
||||
if (cstate->side[LOCAL].pay_msat > cstate->anchor * 1000) |
|
||||
return false; |
|
||||
if (cstate->side[REMOTE].pay_msat > cstate->anchor * 1000) |
|
||||
return false; |
|
||||
|
|
||||
if (cstate->num_nondust |
|
||||
> cstate->side[LOCAL].num_htlcs + cstate->side[REMOTE].num_htlcs) |
|
||||
return false; |
|
||||
|
|
||||
/* Recalc fees. */ |
|
||||
adjust_fee(cstate, cstate->fee_rate); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static char *fmt_channel_oneside(const tal_t *ctx, |
|
||||
const struct channel_oneside *co) |
|
||||
{ |
|
||||
return tal_fmt(ctx, "{ pay_msat=%u" |
|
||||
" fee_msat=%u" |
|
||||
" num_htlcs=%u }", |
|
||||
co->pay_msat, |
|
||||
co->fee_msat, |
|
||||
co->num_htlcs); |
|
||||
} |
|
||||
|
|
||||
static char *fmt_channel_state(const tal_t *ctx, |
|
||||
const struct channel_state *cs) |
|
||||
{ |
|
||||
return tal_fmt(ctx, "{ anchor=%"PRIu64 |
|
||||
" fee_rate=%"PRIu64 |
|
||||
" num_nondust=%u" |
|
||||
" ours=%s" |
|
||||
" theirs=%s }", |
|
||||
cs->anchor, |
|
||||
cs->fee_rate, |
|
||||
cs->num_nondust, |
|
||||
fmt_channel_oneside(ctx, &cs->side[LOCAL]), |
|
||||
fmt_channel_oneside(ctx, &cs->side[REMOTE])); |
|
||||
} |
|
||||
|
|
||||
REGISTER_TYPE_TO_STRING(channel_oneside, fmt_channel_oneside); |
|
||||
REGISTER_TYPE_TO_STRING(channel_state, fmt_channel_state); |
|
@ -1,140 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_CHANNEL_H |
|
||||
#define LIGHTNING_DAEMON_CHANNEL_H |
|
||||
#include "config.h" |
|
||||
#include "bitcoin/locktime.h" |
|
||||
#include "daemon/htlc.h" |
|
||||
#include <assert.h> |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <stdbool.h> |
|
||||
|
|
||||
struct channel_oneside { |
|
||||
/* Payment and fee is in millisatoshi. */ |
|
||||
uint32_t pay_msat, fee_msat; |
|
||||
/* Number of HTLCs (required for limiting total number) */ |
|
||||
unsigned int num_htlcs; |
|
||||
}; |
|
||||
|
|
||||
struct channel_state { |
|
||||
/* Satoshis paid by anchor. */ |
|
||||
uint64_t anchor; |
|
||||
/* Satoshis per 1000 bytes. */ |
|
||||
uint64_t fee_rate; |
|
||||
/* Number of non-dust htlcs (to calculate txsize) */ |
|
||||
unsigned int num_nondust; |
|
||||
struct channel_oneside side[2]; |
|
||||
}; |
|
||||
|
|
||||
/**
|
|
||||
* initial_cstate: Given initial fees and funding anchor, what is initial state? |
|
||||
* @ctx: tal context to allocate return value from. |
|
||||
* @anchor_satoshis: The anchor amount. |
|
||||
* @fee_rate: amount to pay in fees per kb (in satoshi). |
|
||||
* @dir: which side paid for the anchor. |
|
||||
* |
|
||||
* Returns state, or NULL if malformed. |
|
||||
*/ |
|
||||
struct channel_state *initial_cstate(const tal_t *ctx, |
|
||||
uint64_t anchor_satoshis, |
|
||||
uint64_t fee_rate, |
|
||||
enum side side); |
|
||||
|
|
||||
/**
|
|
||||
* copy_cstate: Make a deep copy of channel_state |
|
||||
* @ctx: tal context to allocate return value from. |
|
||||
* @cstate: state to copy. |
|
||||
*/ |
|
||||
struct channel_state *copy_cstate(const tal_t *ctx, |
|
||||
const struct channel_state *cstate); |
|
||||
|
|
||||
/**
|
|
||||
* cstate_add_htlc: append an HTLC to cstate if it can afford it |
|
||||
* @cstate: The channel state |
|
||||
* @htlc: the htlc pointer. |
|
||||
* @must_afford_fee: true if payer must meet fee. |
|
||||
* |
|
||||
* If that direction can't afford the HTLC this will return false and |
|
||||
* leave @cstate unchanged. If @must_afford_fee is true, and the |
|
||||
* direction can't afford its half of the fees, it will also return |
|
||||
* false and leave @cstate unchanged. Otherwise, pay_msat and fee_msat |
|
||||
* are adjusted accordingly; true is returned. |
|
||||
*/ |
|
||||
bool cstate_add_htlc(struct channel_state *cstate, const struct htlc *htlc, |
|
||||
bool must_afford_fee); |
|
||||
|
|
||||
/**
|
|
||||
* cstate_fail_htlc: remove an HTLC, funds to the side which offered it. |
|
||||
* @cstate: The channel state |
|
||||
* @htlc: the htlc to remove. |
|
||||
* |
|
||||
* This will remove the @index'th entry in cstate->side[dir].htlcs[], and credit |
|
||||
* the value of the HTLC (back) to cstate->side[dir]. |
|
||||
*/ |
|
||||
void cstate_fail_htlc(struct channel_state *cstate, const struct htlc *htlc); |
|
||||
|
|
||||
/**
|
|
||||
* cstate_fulfill_htlc: remove an HTLC, funds to side which accepted it. |
|
||||
* @cstate: The channel state |
|
||||
* @htlc: the htlc to remove |
|
||||
* |
|
||||
* This will remove the @index'th entry in cstate->side[dir].htlcs[], and credit |
|
||||
* the value of the HTLC to cstate->side[!dir]. |
|
||||
*/ |
|
||||
void cstate_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc); |
|
||||
|
|
||||
/**
|
|
||||
* approx_max_feerate: what's the most side could raise fee rate to? |
|
||||
* @cstate: The channel state |
|
||||
* @side: LOCAL or REMOTE |
|
||||
* |
|
||||
* This is not exact! To check if their offer is valid, use can_afford_feerate. |
|
||||
*/ |
|
||||
uint64_t approx_max_feerate(const struct channel_state *cstate, |
|
||||
enum side side); |
|
||||
|
|
||||
/**
|
|
||||
* can_afford_feerate: could this side pay for the fee if changed to fee_rate? |
|
||||
* @cstate: The channel state |
|
||||
* @fee_rate: the new fee rate proposed |
|
||||
* @side: LOCAL or REMOTE |
|
||||
*/ |
|
||||
bool can_afford_feerate(const struct channel_state *cstate, uint64_t fee_rate, |
|
||||
enum side side); |
|
||||
|
|
||||
/**
|
|
||||
* adjust_fee: Change fee rate. |
|
||||
* @cstate: The channel state |
|
||||
* @fee_rate: fee in satoshi per 1000 bytes. |
|
||||
*/ |
|
||||
void adjust_fee(struct channel_state *cstate, uint64_t fee_rate); |
|
||||
|
|
||||
/**
|
|
||||
* force_fee: Change fee to a specific value. |
|
||||
* @cstate: The channel state |
|
||||
* @fee: fee in satoshi. |
|
||||
* |
|
||||
* This is used for the close transaction, which specifies an exact fee. |
|
||||
* If the fee cannot be paid in full, this return false (but cstate will |
|
||||
* still be altered). |
|
||||
*/ |
|
||||
bool force_fee(struct channel_state *cstate, uint64_t fee); |
|
||||
|
|
||||
/**
|
|
||||
* fee_for_feerate: calculate the fee (in satoshi) for a given fee_rate. |
|
||||
* @txsize: transaction size in bytes. |
|
||||
* @fee_rate: satoshi per 1000 bytes. |
|
||||
*/ |
|
||||
uint64_t fee_by_feerate(size_t txsize, uint64_t fee_rate); |
|
||||
|
|
||||
/**
|
|
||||
* anchor_too_large: does anchor amount fit in 32-bits of millisatoshi. |
|
||||
* @anchor_satoshis: amount in satoshis |
|
||||
*/ |
|
||||
bool anchor_too_large(uint64_t anchor_satoshis); |
|
||||
|
|
||||
/* Routines to db to force HTLC changes out-of-order which may wrap. */ |
|
||||
void force_add_htlc(struct channel_state *cstate, const struct htlc *htlc); |
|
||||
void force_fail_htlc(struct channel_state *cstate, const struct htlc *htlc); |
|
||||
void force_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc); |
|
||||
bool balance_after_force(struct channel_state *cstate); |
|
||||
#endif /* LIGHTNING_DAEMON_CHANNEL_H */ |
|
@ -1,225 +0,0 @@ |
|||||
#include "bitcoin/locktime.h" |
|
||||
#include "bitcoin/pubkey.h" |
|
||||
#include "bitcoin/script.h" |
|
||||
#include "bitcoin/shadouble.h" |
|
||||
#include "bitcoin/tx.h" |
|
||||
#include "channel.h" |
|
||||
#include "commit_tx.h" |
|
||||
#include "htlc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "overflows.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "permute_tx.h" |
|
||||
#include "remove_dust.h" |
|
||||
#include "utils.h" |
|
||||
#include <assert.h> |
|
||||
#include <inttypes.h> |
|
||||
|
|
||||
u8 *wscript_for_htlc(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct htlc *h, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side) |
|
||||
{ |
|
||||
const struct peer_visible_state *this_side, *other_side; |
|
||||
u8 *(*fn)(const tal_t *, |
|
||||
const struct pubkey *, const struct pubkey *, |
|
||||
const struct abs_locktime *, const struct rel_locktime *, |
|
||||
const struct sha256 *, const struct sha256 *); |
|
||||
|
|
||||
/* scripts are different for htlcs offered vs accepted */ |
|
||||
if (side == htlc_owner(h)) |
|
||||
fn = bitcoin_redeem_htlc_send; |
|
||||
else |
|
||||
fn = bitcoin_redeem_htlc_recv; |
|
||||
|
|
||||
if (side == LOCAL) { |
|
||||
this_side = &peer->local; |
|
||||
other_side = &peer->remote; |
|
||||
} else { |
|
||||
this_side = &peer->remote; |
|
||||
other_side = &peer->local; |
|
||||
} |
|
||||
|
|
||||
return fn(ctx, |
|
||||
&this_side->finalkey, &other_side->finalkey, |
|
||||
&h->expiry, &this_side->locktime, rhash, &h->rhash); |
|
||||
} |
|
||||
|
|
||||
static size_t count_htlcs(const struct htlc_map *htlcs, int flag) |
|
||||
{ |
|
||||
struct htlc_map_iter it; |
|
||||
struct htlc *h; |
|
||||
size_t n = 0; |
|
||||
|
|
||||
for (h = htlc_map_first(htlcs, &it); h; h = htlc_map_next(htlcs, &it)) { |
|
||||
if (htlc_has(h, flag)) |
|
||||
n++; |
|
||||
} |
|
||||
return n; |
|
||||
} |
|
||||
|
|
||||
u8 *commit_output_to_us(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
u8 **wscript) |
|
||||
{ |
|
||||
u8 *tmp; |
|
||||
if (!wscript) |
|
||||
wscript = &tmp; |
|
||||
|
|
||||
/* Our output to ourself is encumbered by delay. */ |
|
||||
if (side == LOCAL) { |
|
||||
*wscript = bitcoin_redeem_secret_or_delay(ctx, |
|
||||
&peer->local.finalkey, |
|
||||
&peer->remote.locktime, |
|
||||
&peer->remote.finalkey, |
|
||||
rhash); |
|
||||
return scriptpubkey_p2wsh(ctx, *wscript); |
|
||||
} else { |
|
||||
/* Their output to us is a simple p2wpkh */ |
|
||||
*wscript = NULL; |
|
||||
return scriptpubkey_p2wpkh(ctx, &peer->local.finalkey); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
u8 *commit_output_to_them(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
u8 **wscript) |
|
||||
{ |
|
||||
u8 *tmp; |
|
||||
if (!wscript) |
|
||||
wscript = &tmp; |
|
||||
|
|
||||
/* Their output to themselves is encumbered by delay. */ |
|
||||
if (side == REMOTE) { |
|
||||
*wscript = bitcoin_redeem_secret_or_delay(ctx, |
|
||||
&peer->remote.finalkey, |
|
||||
&peer->local.locktime, |
|
||||
&peer->local.finalkey, |
|
||||
rhash); |
|
||||
return scriptpubkey_p2wsh(ctx, *wscript); |
|
||||
} else { |
|
||||
/* Our output to them is a simple p2wpkh */ |
|
||||
*wscript = NULL; |
|
||||
return scriptpubkey_p2wpkh(ctx, &peer->remote.finalkey); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Takes ownership of script. */ |
|
||||
static bool add_output(struct bitcoin_tx *tx, u8 *script, u64 amount, |
|
||||
size_t *output_count, |
|
||||
u64 *total) |
|
||||
{ |
|
||||
assert(*output_count < tal_count(tx->output)); |
|
||||
if (is_dust(amount)) |
|
||||
return false; |
|
||||
tx->output[*output_count].script = tal_steal(tx, script); |
|
||||
tx->output[*output_count].amount = amount; |
|
||||
(*output_count)++; |
|
||||
(*total) += amount; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
struct bitcoin_tx *create_commit_tx(const tal_t *ctx, |
|
||||
struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
const struct channel_state *cstate, |
|
||||
enum side side, |
|
||||
bool *otherside_only) |
|
||||
{ |
|
||||
const tal_t *tmpctx = tal_tmpctx(ctx); |
|
||||
struct bitcoin_tx *tx; |
|
||||
uint64_t total = 0; |
|
||||
struct htlc_map_iter it; |
|
||||
struct htlc *h; |
|
||||
size_t output_count; |
|
||||
bool pays_to[2]; |
|
||||
int committed_flag = HTLC_FLAG(side,HTLC_F_COMMITTED); |
|
||||
|
|
||||
/* Now create commitment tx: one input, two outputs (plus htlcs) */ |
|
||||
tx = bitcoin_tx(ctx, 1, 2 + count_htlcs(&peer->htlcs, committed_flag)); |
|
||||
|
|
||||
log_debug(peer->log, "Creating commitment tx:"); |
|
||||
log_add_struct(peer->log, " rhash = %s", struct sha256, rhash); |
|
||||
log_add_struct(peer->log, " My finalkey = %s", struct pubkey, |
|
||||
&peer->local.finalkey); |
|
||||
log_add_struct(peer->log, " Their finalkey = %s", struct pubkey, |
|
||||
&peer->remote.finalkey); |
|
||||
log_add_struct(peer->log, " My locktime = %s", struct rel_locktime, |
|
||||
&peer->local.locktime); |
|
||||
log_add_struct(peer->log, " Their locktime = %s", struct rel_locktime, |
|
||||
&peer->remote.locktime); |
|
||||
|
|
||||
/* Our input spends the anchor tx output. */ |
|
||||
tx->input[0].txid = peer->anchor.txid; |
|
||||
tx->input[0].index = peer->anchor.index; |
|
||||
tx->input[0].amount = tal_dup(tx->input, u64, &peer->anchor.satoshis); |
|
||||
|
|
||||
output_count = 0; |
|
||||
pays_to[LOCAL] = add_output(tx, commit_output_to_us(tmpctx, peer, rhash, |
|
||||
side, NULL), |
|
||||
cstate->side[LOCAL].pay_msat / 1000, |
|
||||
&output_count, |
|
||||
&total); |
|
||||
if (pays_to[LOCAL]) |
|
||||
log_debug(peer->log, "Pays %u to local: %s", |
|
||||
cstate->side[LOCAL].pay_msat / 1000, |
|
||||
tal_hex(tmpctx, tx->output[output_count-1].script)); |
|
||||
else |
|
||||
log_debug(peer->log, "DOES NOT pay %u to local", |
|
||||
cstate->side[LOCAL].pay_msat / 1000); |
|
||||
pays_to[REMOTE] = add_output(tx, commit_output_to_them(tmpctx, peer, |
|
||||
rhash, side, |
|
||||
NULL), |
|
||||
cstate->side[REMOTE].pay_msat / 1000, |
|
||||
&output_count, |
|
||||
&total); |
|
||||
if (pays_to[REMOTE]) |
|
||||
log_debug(peer->log, "Pays %u to remote: %s", |
|
||||
cstate->side[REMOTE].pay_msat / 1000, |
|
||||
tal_hex(tmpctx, tx->output[output_count-1].script)); |
|
||||
else |
|
||||
log_debug(peer->log, "DOES NOT pay %u to remote", |
|
||||
cstate->side[REMOTE].pay_msat / 1000); |
|
||||
|
|
||||
/* If their tx doesn't pay to them, or our tx doesn't pay to us... */ |
|
||||
*otherside_only = !pays_to[side]; |
|
||||
|
|
||||
/* First two outputs done, now for the HTLCs. */ |
|
||||
for (h = htlc_map_first(&peer->htlcs, &it); |
|
||||
h; |
|
||||
h = htlc_map_next(&peer->htlcs, &it)) { |
|
||||
const u8 *wscript; |
|
||||
|
|
||||
if (!htlc_has(h, committed_flag)) |
|
||||
continue; |
|
||||
wscript = wscript_for_htlc(tmpctx, peer, h, rhash, side); |
|
||||
/* If we pay any HTLC, it's txout is not just to other side. */ |
|
||||
if (add_output(tx, scriptpubkey_p2wsh(tmpctx, wscript), |
|
||||
h->msatoshi / 1000, &output_count, &total)) { |
|
||||
*otherside_only = false; |
|
||||
log_debug(peer->log, "Pays %"PRIu64" to htlc %"PRIu64, |
|
||||
h->msatoshi / 1000, h->id); |
|
||||
log_add_struct(peer->log, " expiry %s", |
|
||||
struct abs_locktime, &h->expiry); |
|
||||
log_add_struct(peer->log, " rhash %s", struct sha256, |
|
||||
&h->rhash); |
|
||||
log_debug(peer->log, "Script: %s", |
|
||||
tal_hex(tmpctx, wscript)); |
|
||||
} else |
|
||||
log_debug(peer->log, "DOES NOT pay %"PRIu64" to htlc %"PRIu64, |
|
||||
h->msatoshi / 1000, h->id); |
|
||||
} |
|
||||
assert(total <= peer->anchor.satoshis); |
|
||||
|
|
||||
tal_resize(&tx->output, output_count); |
|
||||
permute_outputs(tx->output, tal_count(tx->output), NULL); |
|
||||
tal_free(tmpctx); |
|
||||
return tx; |
|
||||
} |
|
@ -1,40 +0,0 @@ |
|||||
#ifndef LIGHTNING_COMMIT_TX_H |
|
||||
#define LIGHTNING_COMMIT_TX_H |
|
||||
#include "config.h" |
|
||||
#include "htlc.h" |
|
||||
#include <ccan/tal/tal.h> |
|
||||
|
|
||||
struct channel_state; |
|
||||
struct sha256; |
|
||||
struct pubkey; |
|
||||
struct peer; |
|
||||
|
|
||||
u8 *wscript_for_htlc(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct htlc *h, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side); |
|
||||
|
|
||||
/* Returns scriptpubkey: *wscript is NULL if it's a direct p2wpkh. */ |
|
||||
u8 *commit_output_to_us(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
u8 **wscript); |
|
||||
|
|
||||
/* Returns scriptpubkey: *wscript is NULL if it's a direct p2wpkh. */ |
|
||||
u8 *commit_output_to_them(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
u8 **wscript); |
|
||||
|
|
||||
/* Create commitment tx to spend the anchor tx output; doesn't fill in
|
|
||||
* input scriptsig. */ |
|
||||
struct bitcoin_tx *create_commit_tx(const tal_t *ctx, |
|
||||
struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
const struct channel_state *cstate, |
|
||||
enum side side, |
|
||||
bool *otherside_only); |
|
||||
#endif |
|
@ -1,656 +0,0 @@ |
|||||
#include "bitcoin/shadouble.h" |
|
||||
#include "bitcoin/signature.h" |
|
||||
#include "cryptopkt.h" |
|
||||
#include "lightning.pb-c.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "names.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "protobuf_convert.h" |
|
||||
#include "secrets.h" |
|
||||
#include "utils.h" |
|
||||
#include <ccan/build_assert/build_assert.h> |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/endian/endian.h> |
|
||||
#include <ccan/mem/mem.h> |
|
||||
#include <ccan/short_types/short_types.h> |
|
||||
#include <ccan/structeq/structeq.h> |
|
||||
#include <inttypes.h> |
|
||||
#include <secp256k1.h> |
|
||||
#include <secp256k1_ecdh.h> |
|
||||
#include <sodium/crypto_aead_chacha20poly1305.h> |
|
||||
#include <sodium/randombytes.h> |
|
||||
|
|
||||
#define MAX_PKT_LEN (1024 * 1024) |
|
||||
|
|
||||
/* FIXME-OLD#1:
|
|
||||
`length` is a 4-byte little-endian field indicating the size of the unencrypted body. |
|
||||
*/ |
|
||||
|
|
||||
struct crypto_pkt { |
|
||||
le32 length; |
|
||||
u8 auth_tag[crypto_aead_chacha20poly1305_ABYTES]; |
|
||||
|
|
||||
/* ... contents... */ |
|
||||
u8 data[]; |
|
||||
}; |
|
||||
|
|
||||
/* Temporary structure for negotiation */ |
|
||||
struct key_negotiate { |
|
||||
struct lightningd_state *dstate; |
|
||||
|
|
||||
/* Our session secret key. */ |
|
||||
u8 seckey[32]; |
|
||||
|
|
||||
/* Our pubkey, their pubkey. */ |
|
||||
le32 keylen; |
|
||||
u8 our_sessionpubkey[33], their_sessionpubkey[33]; |
|
||||
|
|
||||
/* After DH key exchange, we create io_data to check auth. */ |
|
||||
struct io_data *iod; |
|
||||
|
|
||||
/* Logging structure we're using. */ |
|
||||
struct log *log; |
|
||||
|
|
||||
/* Did we expect a particular ID? */ |
|
||||
const struct pubkey *expected_id; |
|
||||
|
|
||||
/* Callback once it's all done. */ |
|
||||
struct io_plan *(*cb)(struct io_conn *conn, |
|
||||
struct lightningd_state *dstate, |
|
||||
struct io_data *iod, |
|
||||
struct log *log, |
|
||||
const struct pubkey *id, |
|
||||
void *arg); |
|
||||
void *arg; |
|
||||
}; |
|
||||
|
|
||||
struct enckey { |
|
||||
struct sha256 k; |
|
||||
}; |
|
||||
|
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* * sending-key: SHA256(shared-secret || sending-node-session-pubkey) |
|
||||
* * receiving-key: SHA256(shared-secret || receiving-node-session-pubkey) |
|
||||
*/ |
|
||||
static struct enckey enckey_from_secret(const unsigned char secret[32], |
|
||||
const unsigned char serial_pubkey[33]) |
|
||||
{ |
|
||||
struct sha256_ctx ctx; |
|
||||
struct enckey enckey; |
|
||||
|
|
||||
sha256_init(&ctx); |
|
||||
sha256_update(&ctx, memcheck(secret, 32), 32); |
|
||||
sha256_update(&ctx, memcheck(serial_pubkey, 33), 33); |
|
||||
sha256_done(&ctx, &enckey.k); |
|
||||
|
|
||||
return enckey; |
|
||||
} |
|
||||
|
|
||||
struct dir_state { |
|
||||
u64 nonce; |
|
||||
struct enckey enckey; |
|
||||
|
|
||||
/* Current packet (encrypted). */ |
|
||||
struct crypto_pkt *cpkt; |
|
||||
size_t pkt_len; |
|
||||
}; |
|
||||
|
|
||||
static void setup_crypto(struct dir_state *dir, |
|
||||
u8 shared_secret[32], u8 serial_pubkey[33]) |
|
||||
{ |
|
||||
/* FIXME-OLD #1: Nonces...MUST begin at 0 */ |
|
||||
dir->nonce = 0; |
|
||||
|
|
||||
dir->enckey = enckey_from_secret(shared_secret, serial_pubkey); |
|
||||
|
|
||||
dir->cpkt = NULL; |
|
||||
} |
|
||||
|
|
||||
struct io_data { |
|
||||
/* Stuff we need to keep around to talk to peer. */ |
|
||||
struct dir_state in, out; |
|
||||
|
|
||||
/* Callback once packet decrypted. */ |
|
||||
struct io_plan *(*cb)(struct io_conn *, struct peer *); |
|
||||
|
|
||||
/* Once peer is assigned, this is set. */ |
|
||||
struct peer *peer; |
|
||||
|
|
||||
/* Length we're currently reading. */ |
|
||||
struct crypto_pkt hdr_in; |
|
||||
}; |
|
||||
|
|
||||
static void *proto_tal_alloc(void *allocator_data, size_t size) |
|
||||
{ |
|
||||
return tal_arr(allocator_data, char, size); |
|
||||
} |
|
||||
|
|
||||
static void proto_tal_free(void *allocator_data, void *pointer) |
|
||||
{ |
|
||||
tal_free(pointer); |
|
||||
} |
|
||||
|
|
||||
static void le64_nonce(unsigned char *npub, u64 nonce) |
|
||||
{ |
|
||||
/* FIXME-OLD #1: Nonces are 64-bit little-endian numbers */ |
|
||||
le64 le_nonce = cpu_to_le64(nonce); |
|
||||
memcpy(npub, &le_nonce, sizeof(le_nonce)); |
|
||||
BUILD_ASSERT(crypto_aead_chacha20poly1305_NPUBBYTES == sizeof(le_nonce)); |
|
||||
} |
|
||||
|
|
||||
/* Encrypts data..data + len - 1 inclusive into data..data + len - 1 and
|
|
||||
* then writes the authentication tag at data+len. |
|
||||
* |
|
||||
* This increments nonce every time. |
|
||||
*/ |
|
||||
static void encrypt_in_place(void *data, size_t len, |
|
||||
u64 *nonce, const struct enckey *enckey) |
|
||||
{ |
|
||||
int ret; |
|
||||
unsigned long long clen; |
|
||||
unsigned char npub[crypto_aead_chacha20poly1305_NPUBBYTES]; |
|
||||
|
|
||||
le64_nonce(npub, *nonce); |
|
||||
ret = crypto_aead_chacha20poly1305_encrypt(data, &clen, |
|
||||
memcheck(data, len), len, |
|
||||
NULL, 0, NULL, |
|
||||
npub, enckey->k.u.u8); |
|
||||
assert(ret == 0); |
|
||||
assert(clen == len + crypto_aead_chacha20poly1305_ABYTES); |
|
||||
(*nonce)++; |
|
||||
} |
|
||||
|
|
||||
/* Checks authentication tag at data+len, then
|
|
||||
* decrypts data..data + len - 1 inclusive into data..data + len - 1. |
|
||||
* |
|
||||
* This increments nonce every time. |
|
||||
*/ |
|
||||
static bool decrypt_in_place(void *data, size_t len, |
|
||||
u64 *nonce, const struct enckey *enckey) |
|
||||
{ |
|
||||
int ret; |
|
||||
unsigned long long mlen; |
|
||||
unsigned char npub[crypto_aead_chacha20poly1305_NPUBBYTES]; |
|
||||
|
|
||||
le64_nonce(npub, *nonce); |
|
||||
mlen = len + crypto_aead_chacha20poly1305_ABYTES; |
|
||||
|
|
||||
ret = crypto_aead_chacha20poly1305_decrypt(data, &mlen, NULL, |
|
||||
memcheck(data, mlen), mlen, |
|
||||
NULL, 0, |
|
||||
npub, enckey->k.u.u8); |
|
||||
if (ret == 0) { |
|
||||
assert(mlen == len); |
|
||||
(*nonce)++; |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
static Pkt *decrypt_body(const tal_t *ctx, struct io_data *iod, struct log *log) |
|
||||
{ |
|
||||
struct ProtobufCAllocator prototal; |
|
||||
Pkt *ret; |
|
||||
size_t data_len = le32_to_cpu(iod->hdr_in.length); |
|
||||
|
|
||||
if (!decrypt_in_place(iod->in.cpkt->data, data_len, |
|
||||
&iod->in.nonce, &iod->in.enckey)) { |
|
||||
/* Free encrypted packet. */ |
|
||||
iod->in.cpkt = tal_free(iod->in.cpkt); |
|
||||
log_unusual(log, "Body decryption failed"); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
/* De-protobuf it. */ |
|
||||
prototal.alloc = proto_tal_alloc; |
|
||||
prototal.free = proto_tal_free; |
|
||||
prototal.allocator_data = tal(ctx, char); |
|
||||
|
|
||||
ret = pkt__unpack(&prototal, data_len, iod->in.cpkt->data); |
|
||||
if (!ret) { |
|
||||
log_unusual(log, "Packet failed to unpack!"); |
|
||||
tal_free(prototal.allocator_data); |
|
||||
} else { |
|
||||
/* Make sure packet owns contents */ |
|
||||
tal_steal(ctx, ret); |
|
||||
tal_steal(ret, prototal.allocator_data); |
|
||||
|
|
||||
log_debug(log, "Received packet LEN=%zu, type=%s", |
|
||||
data_len, |
|
||||
ret->pkt_case == PKT__PKT_AUTH ? "PKT_AUTH" |
|
||||
: pkt_name(ret->pkt_case)); |
|
||||
} |
|
||||
|
|
||||
/* Free encrypted packet. */ |
|
||||
iod->in.cpkt = tal_free(iod->in.cpkt); |
|
||||
|
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
static struct crypto_pkt *encrypt_pkt(struct io_data *iod, const Pkt *pkt, |
|
||||
size_t *totlen) |
|
||||
{ |
|
||||
struct crypto_pkt *cpkt; |
|
||||
size_t len; |
|
||||
|
|
||||
len = pkt__get_packed_size(pkt); |
|
||||
*totlen = sizeof(*cpkt) + len + crypto_aead_chacha20poly1305_ABYTES; |
|
||||
|
|
||||
cpkt = (struct crypto_pkt *)tal_arr(iod, char, *totlen); |
|
||||
cpkt->length = cpu_to_le32(len); |
|
||||
|
|
||||
/* Encrypt header. */ |
|
||||
encrypt_in_place(cpkt, sizeof(cpkt->length), |
|
||||
&iod->out.nonce, &iod->out.enckey); |
|
||||
|
|
||||
/* Encrypt body. */ |
|
||||
pkt__pack(pkt, cpkt->data); |
|
||||
encrypt_in_place(cpkt->data, len, &iod->out.nonce, &iod->out.enckey); |
|
||||
|
|
||||
return cpkt; |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *recv_body(struct io_conn *conn, struct peer *peer) |
|
||||
{ |
|
||||
struct io_data *iod = peer->io_data; |
|
||||
|
|
||||
assert(!peer->inpkt); |
|
||||
|
|
||||
/* We have full packet. */ |
|
||||
peer->inpkt = decrypt_body(iod, iod, peer->log); |
|
||||
if (!peer->inpkt) |
|
||||
return io_close(conn); |
|
||||
|
|
||||
return iod->cb(conn, peer); |
|
||||
} |
|
||||
|
|
||||
static bool decrypt_header(struct log *log, struct io_data *iod, |
|
||||
size_t *body_len) |
|
||||
{ |
|
||||
/* We have length: Check it. */ |
|
||||
if (!decrypt_in_place(&iod->hdr_in.length, sizeof(iod->hdr_in.length), |
|
||||
&iod->in.nonce, &iod->in.enckey)) { |
|
||||
log_unusual(log, "Header decryption failed"); |
|
||||
return false; |
|
||||
} |
|
||||
log_debug(log, "Decrypted header len %u", |
|
||||
le32_to_cpu(iod->hdr_in.length)); |
|
||||
|
|
||||
/* FIXME-OLD #1: `length` MUST NOT exceed 1MB (1048576 bytes). */ |
|
||||
if (le32_to_cpu(iod->hdr_in.length) > MAX_PKT_LEN) { |
|
||||
log_unusual(log, |
|
||||
"Packet overlength: %"PRIu64, |
|
||||
le64_to_cpu(iod->hdr_in.length)); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/* Allocate room for body, copy header. */ |
|
||||
*body_len = le32_to_cpu(iod->hdr_in.length) |
|
||||
+ crypto_aead_chacha20poly1305_ABYTES; |
|
||||
|
|
||||
iod->in.cpkt = (struct crypto_pkt *) |
|
||||
tal_arr(iod, char, sizeof(iod->hdr_in) + *body_len); |
|
||||
*iod->in.cpkt = iod->hdr_in; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *recv_header(struct io_conn *conn, struct peer *peer) |
|
||||
{ |
|
||||
struct io_data *iod = peer->io_data; |
|
||||
size_t body_len; |
|
||||
|
|
||||
if (!decrypt_header(peer->log, iod, &body_len)) |
|
||||
return io_close(conn); |
|
||||
|
|
||||
return io_read(conn, iod->in.cpkt->data, body_len, recv_body, peer); |
|
||||
} |
|
||||
|
|
||||
struct io_plan *peer_read_packet(struct io_conn *conn, |
|
||||
struct peer *peer, |
|
||||
struct io_plan *(*cb)(struct io_conn *, |
|
||||
struct peer *)) |
|
||||
{ |
|
||||
struct io_data *iod = peer->io_data; |
|
||||
|
|
||||
iod->cb = cb; |
|
||||
return io_read(conn, &iod->hdr_in, sizeof(iod->hdr_in), |
|
||||
recv_header, peer); |
|
||||
} |
|
||||
|
|
||||
/* Caller must free data! */ |
|
||||
struct io_plan *peer_write_packet(struct io_conn *conn, |
|
||||
struct peer *peer, |
|
||||
const Pkt *pkt, |
|
||||
struct io_plan *(*next)(struct io_conn *, |
|
||||
struct peer *)) |
|
||||
{ |
|
||||
struct io_data *iod = peer->io_data; |
|
||||
size_t totlen; |
|
||||
|
|
||||
/* We free previous packet here, rather than doing indirection
|
|
||||
* via io_write */ |
|
||||
tal_free(iod->out.cpkt); |
|
||||
|
|
||||
iod->out.cpkt = encrypt_pkt(iod, pkt, &totlen); |
|
||||
/* Free unencrypted packet. */ |
|
||||
tal_free(pkt); |
|
||||
|
|
||||
return io_write(conn, iod->out.cpkt, totlen, next, peer); |
|
||||
} |
|
||||
|
|
||||
static void *pkt_unwrap(Pkt *inpkt, struct log *log, Pkt__PktCase which) |
|
||||
{ |
|
||||
size_t i; |
|
||||
const ProtobufCMessage *base; |
|
||||
|
|
||||
if (inpkt->pkt_case != which) { |
|
||||
log_unusual(log, "Expected %u, got %u", |
|
||||
which, inpkt->pkt_case); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
/* It's a union, and each member starts with base. Pick one */ |
|
||||
base = &inpkt->error->base; |
|
||||
|
|
||||
/* Look for unknown fields. Remember, "It's OK to be odd!" */ |
|
||||
for (i = 0; i < base->n_unknown_fields; i++) { |
|
||||
log_debug(log, "Unknown field in %u: %u", |
|
||||
which, base->unknown_fields[i].tag); |
|
||||
/* Odd is OK */ |
|
||||
if (base->unknown_fields[i].tag & 1) |
|
||||
continue; |
|
||||
log_unusual(log, "Unknown field %u in %u", |
|
||||
base->unknown_fields[i].tag, which); |
|
||||
return NULL; |
|
||||
} |
|
||||
return inpkt->error; |
|
||||
} |
|
||||
|
|
||||
static bool check_proof(struct key_negotiate *neg, struct log *log, |
|
||||
Pkt *inpkt, |
|
||||
const struct pubkey *expected_id, |
|
||||
struct pubkey *id) |
|
||||
{ |
|
||||
struct sha256_double sha; |
|
||||
secp256k1_ecdsa_signature sig; |
|
||||
Authenticate *auth; |
|
||||
|
|
||||
auth = pkt_unwrap(inpkt, log, PKT__PKT_AUTH); |
|
||||
if (!auth) |
|
||||
return false; |
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* |
|
||||
* The receiving node MUST check that: |
|
||||
* |
|
||||
* 1. `node_id` is the expected value for the sending node. |
|
||||
*/ |
|
||||
if (!proto_to_pubkey(auth->node_id, id)) { |
|
||||
log_unusual(log, "Invalid auth id"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (expected_id && !structeq(id, expected_id)) { |
|
||||
log_unusual(log, "Incorrect auth id"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* |
|
||||
* 2. `session_sig` is a valid secp256k1 ECDSA signature encoded as |
|
||||
* a 32-byte big endian R value, followed by a 32-byte big |
|
||||
* endian S value. |
|
||||
*/ |
|
||||
if (!proto_to_signature(auth->session_sig, &sig)) { |
|
||||
log_unusual(log, "Invalid auth signature"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* |
|
||||
* 3. `session_sig` is the signature of the SHA256 of SHA256 of the |
|
||||
* its own sessionpubkey, using the secret key corresponding to |
|
||||
* the sender's `node_id`. |
|
||||
*/ |
|
||||
sha256_double(&sha, neg->our_sessionpubkey, |
|
||||
sizeof(neg->our_sessionpubkey)); |
|
||||
|
|
||||
if (!check_signed_hash(&sha, &sig, id)) { |
|
||||
log_unusual(log, "Bad auth signature"); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *recv_body_negotiate(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
struct io_data *iod = neg->iod; |
|
||||
struct io_plan *plan; |
|
||||
Pkt *pkt; |
|
||||
struct pubkey id; |
|
||||
|
|
||||
/* We have full packet. */ |
|
||||
pkt = decrypt_body(neg, iod, neg->log); |
|
||||
if (!pkt) |
|
||||
return io_close(conn); |
|
||||
|
|
||||
if (!check_proof(neg, neg->log, pkt, neg->expected_id, &id)) |
|
||||
return io_close(conn); |
|
||||
|
|
||||
/* Steal so that the callback may not accidentally free it for us */ |
|
||||
tal_steal(NULL, neg); |
|
||||
|
|
||||
plan = neg->cb(conn, neg->dstate, neg->iod, neg->log, &id, neg->arg); |
|
||||
tal_free(neg); |
|
||||
return plan; |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *recv_header_negotiate(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
size_t body_len; |
|
||||
struct io_data *iod = neg->iod; |
|
||||
|
|
||||
if (!decrypt_header(neg->log, iod, &body_len)) |
|
||||
return io_close(conn); |
|
||||
|
|
||||
return io_read(conn, iod->in.cpkt->data, body_len, recv_body_negotiate, |
|
||||
neg); |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *receive_proof(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
return io_read(conn, &neg->iod->hdr_in, sizeof(neg->iod->hdr_in), |
|
||||
recv_header_negotiate, neg); |
|
||||
} |
|
||||
|
|
||||
/* Steals w onto the returned Pkt */ |
|
||||
static Pkt *pkt_wrap(const tal_t *ctx, void *w, Pkt__PktCase pkt_case) |
|
||||
{ |
|
||||
Pkt *pkt = tal(ctx, Pkt); |
|
||||
pkt__init(pkt); |
|
||||
pkt->pkt_case = pkt_case; |
|
||||
/* Union, so any will do */ |
|
||||
pkt->error = tal_steal(pkt, w); |
|
||||
return pkt; |
|
||||
} |
|
||||
|
|
||||
static Pkt *authenticate_pkt(const tal_t *ctx, |
|
||||
const struct pubkey *node_id, |
|
||||
const secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
Authenticate *auth = tal(ctx, Authenticate); |
|
||||
authenticate__init(auth); |
|
||||
auth->node_id = pubkey_to_proto(auth, node_id); |
|
||||
auth->session_sig = signature_to_proto(auth, sig); |
|
||||
return pkt_wrap(ctx, auth, PKT__PKT_AUTH); |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *keys_exchanged(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
u8 shared_secret[32]; |
|
||||
struct pubkey sessionkey; |
|
||||
secp256k1_ecdsa_signature sig; |
|
||||
Pkt *auth; |
|
||||
size_t totlen; |
|
||||
|
|
||||
if (!pubkey_from_der(neg->their_sessionpubkey, |
|
||||
sizeof(neg->their_sessionpubkey), |
|
||||
&sessionkey)) { |
|
||||
log_unusual_blob(neg->log, "Bad sessionkey %s", |
|
||||
neg->their_sessionpubkey, |
|
||||
sizeof(neg->their_sessionpubkey)); |
|
||||
return io_close(conn); |
|
||||
} |
|
||||
|
|
||||
/* Derive shared secret. */ |
|
||||
if (!secp256k1_ecdh(secp256k1_ctx, shared_secret, |
|
||||
&sessionkey.pubkey, neg->seckey)) { |
|
||||
log_unusual(neg->log, "Bad ECDH"); |
|
||||
return io_close(conn); |
|
||||
} |
|
||||
|
|
||||
/* Each side combines with their OWN session key to SENDING crypto. */ |
|
||||
neg->iod = tal(neg, struct io_data); |
|
||||
setup_crypto(&neg->iod->in, shared_secret, neg->their_sessionpubkey); |
|
||||
setup_crypto(&neg->iod->out, shared_secret, neg->our_sessionpubkey); |
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* |
|
||||
* `session_sig` is the signature of the SHA256 of SHA256 of the its |
|
||||
* own sessionpubkey, using the secret key corresponding to the |
|
||||
* sender's `node_id`. |
|
||||
*/ |
|
||||
privkey_sign(neg->dstate, neg->their_sessionpubkey, |
|
||||
sizeof(neg->their_sessionpubkey), &sig); |
|
||||
|
|
||||
auth = authenticate_pkt(neg, &neg->dstate->id, &sig); |
|
||||
|
|
||||
neg->iod->out.cpkt = encrypt_pkt(neg->iod, auth, &totlen); |
|
||||
return io_write(conn, neg->iod->out.cpkt, totlen, receive_proof, neg); |
|
||||
} |
|
||||
|
|
||||
/* Read and ignore any extra bytes... */ |
|
||||
static struct io_plan *discard_extra(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
size_t len = le32_to_cpu(neg->keylen); |
|
||||
|
|
||||
/* FIXME-OLD#1: Additional fields MAY be added, and MUST be
|
|
||||
* included in the `length` field. These MUST be ignored by |
|
||||
* implementations which do not understand them. */ |
|
||||
if (len > sizeof(neg->their_sessionpubkey)) { |
|
||||
char *discard; |
|
||||
|
|
||||
len -= sizeof(neg->their_sessionpubkey); |
|
||||
discard = tal_arr(neg, char, len); |
|
||||
log_unusual(neg->log, |
|
||||
"Ignoring %zu extra handshake bytes", |
|
||||
len); |
|
||||
return io_read(conn, discard, len, keys_exchanged, neg); |
|
||||
} |
|
||||
|
|
||||
return keys_exchanged(conn, neg); |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *session_key_receive(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
/* FIXME-OLD#1: The `length` field is the length after the field
|
|
||||
itself, and MUST be 33 or greater. */ |
|
||||
if (le32_to_cpu(neg->keylen) < sizeof(neg->their_sessionpubkey)) { |
|
||||
log_unusual(neg->log, "short session key length %u", |
|
||||
le32_to_cpu(neg->keylen)); |
|
||||
return io_close(conn); |
|
||||
} |
|
||||
|
|
||||
/* FIXME-OLD#1: `length` MUST NOT exceed 1MB (1048576 bytes). */ |
|
||||
if (le32_to_cpu(neg->keylen) > 1048576) { |
|
||||
log_unusual(neg->log, |
|
||||
"Oversize session key length %u", |
|
||||
le32_to_cpu(neg->keylen)); |
|
||||
return io_close(conn); |
|
||||
} |
|
||||
|
|
||||
log_debug(neg->log, "Session key length %u", le32_to_cpu(neg->keylen)); |
|
||||
|
|
||||
/* Now read their key. */ |
|
||||
return io_read(conn, neg->their_sessionpubkey, |
|
||||
sizeof(neg->their_sessionpubkey), discard_extra, neg); |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *session_key_len_receive(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
/* Read the amount of data they will send.. */ |
|
||||
return io_read(conn, &neg->keylen, sizeof(neg->keylen), |
|
||||
session_key_receive, neg); |
|
||||
} |
|
||||
|
|
||||
static void gen_sessionkey(u8 seckey[32], |
|
||||
secp256k1_pubkey *pubkey) |
|
||||
{ |
|
||||
do { |
|
||||
randombytes_buf(seckey, 32); |
|
||||
} while (!secp256k1_ec_pubkey_create(secp256k1_ctx, pubkey, seckey)); |
|
||||
} |
|
||||
|
|
||||
static struct io_plan *write_sessionkey(struct io_conn *conn, |
|
||||
struct key_negotiate *neg) |
|
||||
{ |
|
||||
return io_write(conn, neg->our_sessionpubkey, |
|
||||
sizeof(neg->our_sessionpubkey), |
|
||||
session_key_len_receive, neg); |
|
||||
} |
|
||||
|
|
||||
struct io_plan *peer_crypto_setup_(struct io_conn *conn, |
|
||||
struct lightningd_state *dstate, |
|
||||
const struct pubkey *id, |
|
||||
struct log *log, |
|
||||
struct io_plan *(*cb)(struct io_conn *conn, |
|
||||
struct lightningd_state *dstate, |
|
||||
struct io_data *iod, |
|
||||
struct log *log, |
|
||||
const struct pubkey *id, |
|
||||
void *arg), |
|
||||
void *arg) |
|
||||
{ |
|
||||
size_t outputlen; |
|
||||
secp256k1_pubkey sessionkey; |
|
||||
struct key_negotiate *neg; |
|
||||
|
|
||||
/* FIXME-OLD #1:
|
|
||||
* |
|
||||
* The 4-byte length for each message is encrypted separately |
|
||||
* (resulting in a 20 byte header when the authentication tag |
|
||||
* is appended) */ |
|
||||
BUILD_ASSERT(sizeof(struct crypto_pkt) == 20); |
|
||||
|
|
||||
/* We store negotiation state here. */ |
|
||||
neg = tal(conn, struct key_negotiate); |
|
||||
neg->cb = cb; |
|
||||
neg->arg = arg; |
|
||||
neg->dstate = dstate; |
|
||||
neg->expected_id = id; |
|
||||
neg->log = log; |
|
||||
|
|
||||
gen_sessionkey(neg->seckey, &sessionkey); |
|
||||
|
|
||||
outputlen = sizeof(neg->our_sessionpubkey); |
|
||||
secp256k1_ec_pubkey_serialize(secp256k1_ctx, |
|
||||
neg->our_sessionpubkey, &outputlen, |
|
||||
&sessionkey, |
|
||||
SECP256K1_EC_COMPRESSED); |
|
||||
assert(outputlen == sizeof(neg->our_sessionpubkey)); |
|
||||
neg->keylen = cpu_to_le32(sizeof(neg->our_sessionpubkey)); |
|
||||
return io_write(conn, &neg->keylen, sizeof(neg->keylen), |
|
||||
write_sessionkey, neg); |
|
||||
} |
|
@ -1,48 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_CRYPTOPKT_H |
|
||||
#define LIGHTNING_DAEMON_CRYPTOPKT_H |
|
||||
#include "config.h" |
|
||||
#include "lightning.pb-c.h" |
|
||||
#include <ccan/io/io.h> |
|
||||
#include <ccan/typesafe_cb/typesafe_cb.h> |
|
||||
|
|
||||
struct io_data; |
|
||||
struct json_connecting; |
|
||||
struct lightningd_state; |
|
||||
struct log; |
|
||||
struct peer; |
|
||||
|
|
||||
struct io_plan *peer_crypto_setup_(struct io_conn *conn, |
|
||||
struct lightningd_state *dstate, |
|
||||
const struct pubkey *id, |
|
||||
struct log *log, |
|
||||
struct io_plan *(*cb)(struct io_conn *conn, |
|
||||
struct lightningd_state *dstate, |
|
||||
struct io_data *iod, |
|
||||
struct log *log, |
|
||||
const struct pubkey *id, |
|
||||
void *arg), |
|
||||
void *arg); |
|
||||
|
|
||||
#define peer_crypto_setup(conn, dstate, id, log_, cb, arg) \ |
|
||||
peer_crypto_setup_((conn), (dstate), (id), (log_), \ |
|
||||
typesafe_cb_preargs(struct io_plan *, void *, \ |
|
||||
(cb), (arg), \ |
|
||||
struct io_conn *, \ |
|
||||
struct lightningd_state *, \ |
|
||||
struct io_data *, \ |
|
||||
struct log *, \ |
|
||||
const struct pubkey *), \ |
|
||||
(arg)) |
|
||||
|
|
||||
/* Reads packet into peer->inpkt/peer->inpkt_len */ |
|
||||
struct io_plan *peer_read_packet(struct io_conn *conn, |
|
||||
struct peer *peer, |
|
||||
struct io_plan *(*cb)(struct io_conn *, |
|
||||
struct peer *)); |
|
||||
|
|
||||
struct io_plan *peer_write_packet(struct io_conn *conn, |
|
||||
struct peer *peer, |
|
||||
const Pkt *pkt, |
|
||||
struct io_plan *(*next)(struct io_conn *, |
|
||||
struct peer *)); |
|
||||
#endif /* LIGHTNING_DAEMON_CRYPTOPKT_H */ |
|
File diff suppressed because it is too large
@ -1,76 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_DB_H |
|
||||
#define LIGHTNING_DAEMON_DB_H |
|
||||
#include "config.h" |
|
||||
#include "peer.h" |
|
||||
#include <stdbool.h> |
|
||||
|
|
||||
void db_init(struct lightningd_state *dstate); |
|
||||
|
|
||||
void db_start_transaction(struct peer *peer); |
|
||||
void db_abort_transaction(struct peer *peer); |
|
||||
const char *db_commit_transaction(struct peer *peer); |
|
||||
|
|
||||
void db_add_wallet_privkey(struct lightningd_state *dstate, |
|
||||
const struct privkey *privkey); |
|
||||
|
|
||||
bool db_add_peer_address(struct lightningd_state *dstate, |
|
||||
const struct peer_address *addr); |
|
||||
|
|
||||
/* Must NOT be inside transaction. */ |
|
||||
bool db_update_their_closing(struct peer *peer); |
|
||||
bool db_new_pay_command(struct lightningd_state *dstate, |
|
||||
const struct sha256 *rhash, |
|
||||
const struct pubkey *ids, |
|
||||
u64 msatoshi, |
|
||||
const struct htlc *htlc); |
|
||||
bool db_replace_pay_command(struct lightningd_state *dstate, |
|
||||
const struct sha256 *rhash, |
|
||||
const struct pubkey *ids, |
|
||||
u64 msatoshi, |
|
||||
const struct htlc *htlc); |
|
||||
bool db_new_invoice(struct lightningd_state *dstate, |
|
||||
u64 msatoshi, |
|
||||
const char *label, |
|
||||
const struct preimage *r); |
|
||||
|
|
||||
bool db_remove_invoice(struct lightningd_state *dstate, |
|
||||
const char *label); |
|
||||
|
|
||||
/* FIXME: save error handling until db_commit_transaction for calls
|
|
||||
* which have to be inside transaction anyway. */ |
|
||||
|
|
||||
/* Must be inside transaction. */ |
|
||||
void db_create_peer(struct peer *peer); |
|
||||
void db_set_visible_state(struct peer *peer); |
|
||||
void db_set_anchor(struct peer *peer); |
|
||||
void db_new_htlc(struct peer *peer, const struct htlc *htlc); |
|
||||
void db_new_feechange(struct peer *peer, const struct feechange *feechange); |
|
||||
void db_htlc_fulfilled(struct peer *peer, const struct htlc *htlc); |
|
||||
void db_htlc_failed(struct peer *peer, const struct htlc *htlc); |
|
||||
void db_update_htlc_state(struct peer *peer, const struct htlc *htlc, |
|
||||
enum htlc_state oldstate); |
|
||||
void db_complete_pay_command(struct lightningd_state *dstate, |
|
||||
const struct htlc *htlc); |
|
||||
void db_resolve_invoice(struct lightningd_state *dstate, |
|
||||
const char *label, u64 paid_num); |
|
||||
void db_update_feechange_state(struct peer *peer, |
|
||||
const struct feechange *f, |
|
||||
enum feechange_state oldstate); |
|
||||
void db_remove_feechange(struct peer *peer, const struct feechange *feechange, |
|
||||
enum feechange_state oldstate); |
|
||||
void db_new_commit_info(struct peer *peer, enum side side, |
|
||||
const struct sha256 *prev_rhash); |
|
||||
void db_remove_their_prev_revocation_hash(struct peer *peer); |
|
||||
void db_update_next_revocation_hash(struct peer *peer); |
|
||||
void db_save_shachain(struct peer *peer); |
|
||||
void db_update_state(struct peer *peer); |
|
||||
void db_begin_shutdown(struct peer *peer); |
|
||||
void db_set_our_closing_script(struct peer *peer); |
|
||||
void db_update_our_closing(struct peer *peer); |
|
||||
void db_set_their_closing_script(struct peer *peer); |
|
||||
|
|
||||
void db_add_commit_map(struct peer *peer, |
|
||||
const struct sha256_double *txid, u64 commit_num); |
|
||||
|
|
||||
void db_forget_peer(struct peer *peer); |
|
||||
#endif /* LIGHTNING_DAEMON_DB_H */ |
|
@ -1,40 +0,0 @@ |
|||||
#include "failure.h" |
|
||||
#include "protobuf_convert.h" |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
|
|
||||
/* FIXME: Crypto! */ |
|
||||
const u8 *failinfo_create(const tal_t *ctx, |
|
||||
const struct pubkey *id, |
|
||||
u32 error_code, |
|
||||
const char *reason) |
|
||||
{ |
|
||||
FailInfo *f = tal(ctx, FailInfo); |
|
||||
u8 *arr; |
|
||||
|
|
||||
fail_info__init(f); |
|
||||
f->id = pubkey_to_proto(f, id); |
|
||||
f->error_code = error_code; |
|
||||
if (reason) |
|
||||
f->reason = tal_strdup(f, reason); |
|
||||
else |
|
||||
f->reason = NULL; |
|
||||
|
|
||||
arr = tal_arr(ctx, u8, fail_info__get_packed_size(f)); |
|
||||
fail_info__pack(f, arr); |
|
||||
tal_free(f); |
|
||||
return arr; |
|
||||
} |
|
||||
|
|
||||
FailInfo *failinfo_unwrap(const tal_t *ctx, const void *data, size_t len) |
|
||||
{ |
|
||||
struct ProtobufCAllocator *prototal = make_prototal(ctx); |
|
||||
FailInfo *f; |
|
||||
|
|
||||
f = fail_info__unpack(prototal, len, data); |
|
||||
if (f) |
|
||||
steal_from_prototal(ctx, prototal, f); |
|
||||
else |
|
||||
tal_free(prototal); |
|
||||
|
|
||||
return f; |
|
||||
} |
|
@ -1,36 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_FAILURE_H |
|
||||
#define LIGHTNING_DAEMON_FAILURE_H |
|
||||
#include "config.h" |
|
||||
#include "lightning.pb-c.h" |
|
||||
#include <ccan/short_types/short_types.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <secp256k1.h> |
|
||||
|
|
||||
struct pubkey; |
|
||||
|
|
||||
enum fail_error { |
|
||||
BAD_REQUEST_400 = 400, |
|
||||
UNAUTHORIZED_401 = 401, |
|
||||
PAYMENT_REQUIRED_402 = 402, |
|
||||
FORBIDDEN_403 = 403, |
|
||||
NOT_FOUND_404 = 404, |
|
||||
METHOD_NOT_ALLOWED_405 = 405, |
|
||||
REQUEST_TIMEOUT_408 = 408, |
|
||||
GONE_410 = 410, |
|
||||
IM_A_TEAPOT_418 = 418, |
|
||||
INTERNAL_SERVER_ERROR_500 = 500, |
|
||||
NOT_IMPLEMENTED_501 = 501, |
|
||||
BAD_GATEWAY_502 = 502, |
|
||||
SERVICE_UNAVAILABLE_503 = 503, |
|
||||
GATEWAY_TIMEOUT_504 = 504, |
|
||||
VERSION_NOT_SUPPORTED_505 = 505 |
|
||||
}; |
|
||||
|
|
||||
const u8 *failinfo_create(const tal_t *ctx, |
|
||||
const struct pubkey *id, |
|
||||
enum fail_error error_code, |
|
||||
const char *reason); |
|
||||
|
|
||||
FailInfo *failinfo_unwrap(const tal_t *ctx, const void *data, size_t len); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_FAILURE_H */ |
|
@ -1,144 +0,0 @@ |
|||||
#include "db.h" |
|
||||
#include "feechange.h" |
|
||||
#include "log.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include <ccan/array_size/array_size.h> |
|
||||
#include <inttypes.h> |
|
||||
#include "gen_feechange_state_names.h" |
|
||||
|
|
||||
/* This is the HTLC-like flags for each state. */ |
|
||||
static const int per_state_bits[] = { |
|
||||
[SENT_FEECHANGE] = HTLC_ADDING + HTLC_LOCAL_F_OWNER |
|
||||
+ HTLC_REMOTE_F_PENDING, |
|
||||
|
|
||||
[SENT_FEECHANGE_COMMIT] = HTLC_ADDING + HTLC_LOCAL_F_OWNER |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
|
|
||||
[RCVD_FEECHANGE_REVOCATION] = HTLC_ADDING + HTLC_LOCAL_F_OWNER |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_REMOTE_F_REVOKED |
|
||||
+ HTLC_LOCAL_F_PENDING |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
|
|
||||
[RCVD_FEECHANGE_ACK_COMMIT] = HTLC_ADDING + HTLC_LOCAL_F_OWNER |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_REMOTE_F_REVOKED |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
|
|
||||
[SENT_FEECHANGE_ACK_REVOCATION] = HTLC_LOCAL_F_OWNER |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_REMOTE_F_REVOKED |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_REVOKED |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
|
|
||||
[RCVD_FEECHANGE] = HTLC_ADDING + HTLC_REMOTE_F_OWNER |
|
||||
+ HTLC_LOCAL_F_PENDING, |
|
||||
|
|
||||
[RCVD_FEECHANGE_COMMIT] = HTLC_ADDING + HTLC_REMOTE_F_OWNER |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED, |
|
||||
|
|
||||
[SENT_FEECHANGE_REVOCATION] = HTLC_ADDING + HTLC_REMOTE_F_OWNER |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_REVOKED |
|
||||
+ HTLC_REMOTE_F_PENDING |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED, |
|
||||
|
|
||||
[SENT_FEECHANGE_ACK_COMMIT] = HTLC_ADDING + HTLC_REMOTE_F_OWNER |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_REVOKED |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
|
|
||||
[RCVD_FEECHANGE_ACK_REVOCATION] = HTLC_REMOTE_F_OWNER |
|
||||
+ HTLC_LOCAL_F_COMMITTED |
|
||||
+ HTLC_LOCAL_F_REVOKED |
|
||||
+ HTLC_REMOTE_F_COMMITTED |
|
||||
+ HTLC_REMOTE_F_REVOKED |
|
||||
+ HTLC_LOCAL_F_WAS_COMMITTED |
|
||||
+ HTLC_REMOTE_F_WAS_COMMITTED, |
|
||||
}; |
|
||||
|
|
||||
int feechange_state_flags(enum feechange_state state) |
|
||||
{ |
|
||||
assert(state < ARRAY_SIZE(per_state_bits)); |
|
||||
assert(per_state_bits[state]); |
|
||||
return per_state_bits[state]; |
|
||||
} |
|
||||
|
|
||||
const char *feechange_state_name(enum feechange_state s) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; enum_feechange_state_names[i].name; i++) |
|
||||
if (enum_feechange_state_names[i].v == s) |
|
||||
return enum_feechange_state_names[i].name; |
|
||||
return "unknown"; |
|
||||
} |
|
||||
|
|
||||
enum feechange_state feechange_state_from_name(const char *name) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; enum_feechange_state_names[i].name; i++) |
|
||||
if (streq(enum_feechange_state_names[i].name, name)) |
|
||||
return enum_feechange_state_names[i].v; |
|
||||
return FEECHANGE_STATE_INVALID; |
|
||||
} |
|
||||
|
|
||||
struct feechange *new_feechange(struct peer *peer, |
|
||||
u64 fee_rate, |
|
||||
enum feechange_state state) |
|
||||
{ |
|
||||
struct feechange *f = tal(peer, struct feechange); |
|
||||
f->state = state; |
|
||||
f->fee_rate = fee_rate; |
|
||||
|
|
||||
return f; |
|
||||
} |
|
||||
|
|
||||
void feechange_changestate(struct peer *peer, |
|
||||
struct feechange *f, |
|
||||
enum feechange_state oldstate, |
|
||||
enum feechange_state newstate, |
|
||||
bool db_commit) |
|
||||
{ |
|
||||
peer_debug(peer, "feechange: %s->%s", |
|
||||
feechange_state_name(f->state), |
|
||||
feechange_state_name(newstate)); |
|
||||
assert(f->state == oldstate); |
|
||||
assert(peer->feechanges[f->state] == f); |
|
||||
|
|
||||
/* You can only go to consecutive states. */ |
|
||||
assert(newstate == f->state + 1); |
|
||||
|
|
||||
/* You can't change sides. */ |
|
||||
assert(feechange_side(f->state) == feechange_side(newstate)); |
|
||||
|
|
||||
f->state = newstate; |
|
||||
|
|
||||
/* We can have multiple dead feestates, but only one in any other */ |
|
||||
if (!feechange_is_dead(f)) |
|
||||
assert(!peer->feechanges[f->state]); |
|
||||
|
|
||||
peer->feechanges[oldstate] = NULL; |
|
||||
peer->feechanges[newstate] = f; |
|
||||
|
|
||||
if (db_commit) { |
|
||||
if (newstate == RCVD_FEECHANGE_COMMIT |
|
||||
|| newstate == SENT_FEECHANGE_COMMIT) |
|
||||
db_new_feechange(peer, f); |
|
||||
else if (newstate == RCVD_FEECHANGE_ACK_REVOCATION |
|
||||
|| newstate == SENT_FEECHANGE_ACK_REVOCATION) |
|
||||
db_remove_feechange(peer, f, oldstate); |
|
||||
else |
|
||||
db_update_feechange_state(peer, f, oldstate); |
|
||||
} |
|
||||
} |
|
@ -1,52 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_FEECHANGE_H |
|
||||
#define LIGHTNING_DAEMON_FEECHANGE_H |
|
||||
#include "config.h" |
|
||||
#include "channel.h" |
|
||||
#include "feechange_state.h" |
|
||||
|
|
||||
struct peer; |
|
||||
|
|
||||
struct feechange { |
|
||||
/* What's the status */ |
|
||||
enum feechange_state state; |
|
||||
/* The rate. */ |
|
||||
u64 fee_rate; |
|
||||
}; |
|
||||
|
|
||||
static inline enum side feechange_side(enum feechange_state state) |
|
||||
{ |
|
||||
if (state <= SENT_FEECHANGE_ACK_REVOCATION) { |
|
||||
return LOCAL; |
|
||||
} else { |
|
||||
assert(state < FEECHANGE_STATE_INVALID); |
|
||||
return REMOTE; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void feechange_changestate(struct peer *peer, |
|
||||
struct feechange *feechange, |
|
||||
enum feechange_state oldstate, |
|
||||
enum feechange_state newstate, |
|
||||
bool db_commit); |
|
||||
|
|
||||
struct feechange *new_feechange(struct peer *peer, |
|
||||
u64 fee_rate, |
|
||||
enum feechange_state state); |
|
||||
|
|
||||
const char *feechange_state_name(enum feechange_state s); |
|
||||
enum feechange_state feechange_state_from_name(const char *name); |
|
||||
|
|
||||
/* HTLC-add-style bitflags for each feechange state */ |
|
||||
int feechange_state_flags(enum feechange_state state); |
|
||||
|
|
||||
static inline bool feechange_has(const struct feechange *f, int flag) |
|
||||
{ |
|
||||
return feechange_state_flags(f->state) & flag; |
|
||||
} |
|
||||
|
|
||||
static inline bool feechange_is_dead(const struct feechange *feechange) |
|
||||
{ |
|
||||
return feechange->state == SENT_FEECHANGE_ACK_REVOCATION |
|
||||
|| feechange->state == RCVD_FEECHANGE_ACK_REVOCATION; |
|
||||
} |
|
||||
#endif /* LIGHTNING_DAEMON_FEECHANGE_H */ |
|
@ -1,23 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_FEECHANGE_STATE_H |
|
||||
#define LIGHTNING_DAEMON_FEECHANGE_STATE_H |
|
||||
#include "config.h" |
|
||||
|
|
||||
/* Like HTLCs, but only adding; we never "remove" a feechange. */ |
|
||||
enum feechange_state { |
|
||||
/* When we add a new feechange, it goes in this order. */ |
|
||||
SENT_FEECHANGE, |
|
||||
SENT_FEECHANGE_COMMIT, |
|
||||
RCVD_FEECHANGE_REVOCATION, |
|
||||
RCVD_FEECHANGE_ACK_COMMIT, |
|
||||
SENT_FEECHANGE_ACK_REVOCATION, |
|
||||
|
|
||||
/* When they add a new feechange, it goes in this order. */ |
|
||||
RCVD_FEECHANGE, |
|
||||
RCVD_FEECHANGE_COMMIT, |
|
||||
SENT_FEECHANGE_REVOCATION, |
|
||||
SENT_FEECHANGE_ACK_COMMIT, |
|
||||
RCVD_FEECHANGE_ACK_REVOCATION, |
|
||||
|
|
||||
FEECHANGE_STATE_INVALID |
|
||||
}; |
|
||||
#endif /* LIGHTNING_DAEMON_FEECHANGE_STATE_H */ |
|
@ -1,81 +0,0 @@ |
|||||
#include "db.h" |
|
||||
#include "htlc.h" |
|
||||
#include "log.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "type_to_string.h" |
|
||||
#include <bitcoin/preimage.h> |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <inttypes.h> |
|
||||
|
|
||||
void htlc_changestate(struct htlc *h, |
|
||||
enum htlc_state oldstate, |
|
||||
enum htlc_state newstate, |
|
||||
bool db_commit) |
|
||||
{ |
|
||||
peer_debug(h->peer, "htlc %"PRIu64": %s->%s", h->id, |
|
||||
htlc_state_name(h->state), htlc_state_name(newstate)); |
|
||||
assert(h->state == oldstate); |
|
||||
|
|
||||
/* You can only go to consecutive states. */ |
|
||||
assert(newstate == h->state + 1); |
|
||||
|
|
||||
/* You can't change sides. */ |
|
||||
assert((htlc_state_flags(h->state)&(HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER)) |
|
||||
== (htlc_state_flags(newstate)&(HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER))); |
|
||||
|
|
||||
h->state = newstate; |
|
||||
|
|
||||
if (db_commit) { |
|
||||
if (newstate == RCVD_ADD_COMMIT || newstate == SENT_ADD_COMMIT) { |
|
||||
db_new_htlc(h->peer, h); |
|
||||
return; |
|
||||
} |
|
||||
/* These never hit the database. */ |
|
||||
if (oldstate == RCVD_REMOVE_HTLC) |
|
||||
oldstate = SENT_ADD_ACK_REVOCATION; |
|
||||
else if (oldstate == SENT_REMOVE_HTLC) |
|
||||
oldstate = RCVD_ADD_ACK_REVOCATION; |
|
||||
db_update_htlc_state(h->peer, h, oldstate); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void htlc_undostate(struct htlc *h, |
|
||||
enum htlc_state oldstate, |
|
||||
enum htlc_state newstate) |
|
||||
{ |
|
||||
log_debug(h->peer->log, "htlc %"PRIu64": %s->%s", h->id, |
|
||||
htlc_state_name(h->state), htlc_state_name(newstate)); |
|
||||
assert(h->state == oldstate); |
|
||||
|
|
||||
/* You can only return to previous state. */ |
|
||||
assert(newstate == h->state - 1); |
|
||||
|
|
||||
/* And must only be proposal, not commit. */ |
|
||||
assert(h->state == SENT_REMOVE_HTLC || h->state == RCVD_REMOVE_HTLC); |
|
||||
|
|
||||
/* You can't change sides. */ |
|
||||
assert((htlc_state_flags(h->state)&(HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER)) |
|
||||
== (htlc_state_flags(newstate)&(HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER))); |
|
||||
|
|
||||
h->state = newstate; |
|
||||
} |
|
||||
|
|
||||
static char *fmt_htlc(const tal_t *ctx, const struct htlc *h) |
|
||||
{ |
|
||||
return tal_fmt(ctx, "{ id=%"PRIu64 |
|
||||
" msatoshi=%"PRIu64 |
|
||||
" expiry=%s" |
|
||||
" rhash=%s" |
|
||||
" rval=%s" |
|
||||
" src=%s }", |
|
||||
h->id, h->msatoshi, |
|
||||
type_to_string(ctx, struct abs_locktime, &h->expiry), |
|
||||
type_to_string(ctx, struct sha256, &h->rhash), |
|
||||
h->r ? tal_hexstr(ctx, h->r, sizeof(*h->r)) |
|
||||
: "UNKNOWN", |
|
||||
h->src ? type_to_string(ctx, struct pubkey, |
|
||||
h->src->peer->id) |
|
||||
: "local"); |
|
||||
} |
|
||||
REGISTER_TYPE_TO_STRING(htlc, fmt_htlc); |
|
@ -1,280 +0,0 @@ |
|||||
#include "bitcoin/privkey.h" |
|
||||
#include "bitcoin/signature.h" |
|
||||
#include "daemon/chaintopology.h" |
|
||||
#include "daemon/irc_announce.h" |
|
||||
#include "daemon/lightningd.h" |
|
||||
#include "daemon/log.h" |
|
||||
#include "daemon/peer.h" |
|
||||
#include "daemon/peer_internal.h" |
|
||||
#include "daemon/routing.h" |
|
||||
#include "daemon/secrets.h" |
|
||||
#include "daemon/timeout.h" |
|
||||
#include "utils.h" |
|
||||
|
|
||||
#include <ccan/list/list.h> |
|
||||
#include <ccan/str/hex/hex.h> |
|
||||
|
|
||||
/* Sign a privmsg by prepending the signature to the message */ |
|
||||
static void sign_privmsg(struct ircstate *state, struct privmsg *msg) |
|
||||
{ |
|
||||
int siglen; |
|
||||
u8 der[72]; |
|
||||
secp256k1_ecdsa_signature sig; |
|
||||
privkey_sign(state->dstate, msg->msg, strlen(msg->msg), &sig); |
|
||||
siglen = signature_to_der(der, &sig); |
|
||||
msg->msg = tal_fmt(msg, "%s %s", tal_hexstr(msg, der, siglen), msg->msg); |
|
||||
} |
|
||||
|
|
||||
static bool announce_channel(const tal_t *ctx, struct ircstate *state, struct peer *p) |
|
||||
{ |
|
||||
char txid[65]; |
|
||||
struct privmsg *msg = talz(ctx, struct privmsg); |
|
||||
struct txlocator *loc = locate_tx(ctx, state->dstate->topology, &p->anchor.txid); |
|
||||
|
|
||||
if (loc == NULL) |
|
||||
return false; |
|
||||
|
|
||||
bitcoin_txid_to_hex(&p->anchor.txid, txid, sizeof(txid)); |
|
||||
msg->channel = "#lightning-nodes"; |
|
||||
msg->msg = tal_fmt( |
|
||||
msg, "CHAN %s %s %s %d %d %d %d %d", |
|
||||
pubkey_to_hexstr(msg, &state->dstate->id), |
|
||||
pubkey_to_hexstr(msg, p->id), |
|
||||
txid, |
|
||||
loc->blkheight, |
|
||||
loc->index, |
|
||||
state->dstate->config.fee_base, |
|
||||
state->dstate->config.fee_per_satoshi, |
|
||||
state->dstate->config.min_htlc_expiry |
|
||||
); |
|
||||
sign_privmsg(state, msg); |
|
||||
irc_send_msg(state, msg); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
/* Send an announcement for this node to the channel, including its
|
|
||||
* hostname, port and ID */ |
|
||||
static void announce_node(const tal_t *ctx, struct ircstate *state) |
|
||||
{ |
|
||||
char *hostname = state->dstate->external_ip; |
|
||||
int port = state->dstate->portnum; |
|
||||
struct privmsg *msg = talz(ctx, struct privmsg); |
|
||||
|
|
||||
if (hostname == NULL) { |
|
||||
//FIXME: log that we don't know our IP yet.
|
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
msg->channel = "#lightning-nodes"; |
|
||||
msg->msg = tal_fmt( |
|
||||
msg, "NODE %s %s %d", |
|
||||
pubkey_to_hexstr(msg, &state->dstate->id), |
|
||||
hostname, |
|
||||
port |
|
||||
); |
|
||||
|
|
||||
sign_privmsg(state, msg); |
|
||||
irc_send_msg(state, msg); |
|
||||
} |
|
||||
|
|
||||
/* Announce the node's contact information and all of its channels */ |
|
||||
static void announce(struct ircstate *state) |
|
||||
{ |
|
||||
|
|
||||
tal_t *ctx = tal(state, tal_t); |
|
||||
struct peer *p; |
|
||||
|
|
||||
announce_node(ctx, state); |
|
||||
|
|
||||
list_for_each(&state->dstate->peers, p, list) { |
|
||||
|
|
||||
if (!state_is_normal(p->state)) |
|
||||
continue; |
|
||||
announce_channel(ctx, state, p); |
|
||||
} |
|
||||
tal_free(ctx); |
|
||||
|
|
||||
/* By default we announce every 6 hours, otherwise when someone joins */ |
|
||||
log_debug(state->log, "Setting long announce time: 6 hours"); |
|
||||
state->dstate->announce = new_reltimer(&state->dstate->timers, state, |
|
||||
time_from_sec(3600 * 6), |
|
||||
announce, state); |
|
||||
} |
|
||||
|
|
||||
/* Reconnect to IRC server upon disconnection. */ |
|
||||
static void handle_irc_disconnect(struct ircstate *state) |
|
||||
{ |
|
||||
/* Stop announcing. */ |
|
||||
state->dstate->announce = tal_free(state->dstate->announce); |
|
||||
new_reltimer(&state->dstate->timers, state, state->reconnect_timeout, |
|
||||
irc_connect, state); |
|
||||
} |
|
||||
|
|
||||
/* Verify a signed privmsg */ |
|
||||
static bool verify_signed_privmsg( |
|
||||
struct ircstate *istate, |
|
||||
const struct pubkey *pk, |
|
||||
const struct privmsg *msg) |
|
||||
{ |
|
||||
secp256k1_ecdsa_signature sig; |
|
||||
struct sha256_double hash; |
|
||||
const char *m = msg->msg + 1; |
|
||||
int siglen = strchr(m, ' ') - m; |
|
||||
const char *content = m + siglen + 1; |
|
||||
u8 *der = tal_hexdata(msg, m, siglen); |
|
||||
|
|
||||
siglen = hex_data_size(siglen); |
|
||||
if (der == NULL) |
|
||||
return false; |
|
||||
|
|
||||
if (!signature_from_der(der, siglen, &sig)) |
|
||||
return false; |
|
||||
sha256_double(&hash, content, strlen(content)); |
|
||||
return check_signed_hash(&hash, &sig, pk); |
|
||||
} |
|
||||
|
|
||||
static void handle_irc_channel_announcement( |
|
||||
struct ircstate *istate, |
|
||||
const struct privmsg *msg, |
|
||||
char **splits) |
|
||||
{ |
|
||||
struct pubkey *pk1 = talz(msg, struct pubkey); |
|
||||
struct pubkey *pk2 = talz(msg, struct pubkey); |
|
||||
struct sha256_double *txid = talz(msg, struct sha256_double); |
|
||||
int index; |
|
||||
bool ok = true; |
|
||||
int blkheight; |
|
||||
|
|
||||
ok &= pubkey_from_hexstr(splits[1], strlen(splits[1]), pk1); |
|
||||
ok &= pubkey_from_hexstr(splits[2], strlen(splits[2]), pk2); |
|
||||
ok &= bitcoin_txid_from_hex(splits[3], strlen(splits[3]), txid); |
|
||||
blkheight = atoi(splits[4]); |
|
||||
index = atoi(splits[5]); |
|
||||
if (!ok || index < 0 || blkheight < 0) { |
|
||||
log_debug(istate->dstate->base_log, "Unable to parse channel announcent."); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!verify_signed_privmsg(istate, pk1, msg)) { |
|
||||
log_debug(istate->log, |
|
||||
"Ignoring announcement from %s, signature check failed.", |
|
||||
splits[1]); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* FIXME Check in topology that the tx is in the block and |
|
||||
* that the endpoints match. |
|
||||
*/ |
|
||||
|
|
||||
add_connection(istate->dstate->rstate, pk1, pk2, atoi(splits[6]), |
|
||||
atoi(splits[7]), atoi(splits[8]), 6); |
|
||||
} |
|
||||
|
|
||||
static void handle_irc_node_announcement( |
|
||||
struct ircstate *istate, |
|
||||
const struct privmsg *msg, |
|
||||
char **splits) |
|
||||
{ |
|
||||
struct pubkey *pk = talz(msg, struct pubkey); |
|
||||
if (!pubkey_from_hexstr(splits[1], strlen(splits[1]), pk)) |
|
||||
return; |
|
||||
|
|
||||
if (!verify_signed_privmsg(istate, pk, msg)) { |
|
||||
log_debug(istate->log, "Ignoring node announcement from %s, signature check failed.", |
|
||||
splits[1]); |
|
||||
return; |
|
||||
} else if(splits[4] != NULL && strlen(splits[4]) > 64) { |
|
||||
log_debug(istate->log, "Ignoring node announcement from %s, alias too long", |
|
||||
splits[1]); |
|
||||
} |
|
||||
|
|
||||
struct node *node = add_node(istate->dstate->rstate, pk); |
|
||||
if (splits[4] != NULL){ |
|
||||
tal_free(node->alias); |
|
||||
node->alias = tal_hexdata(node, splits[4], strlen(splits[4])); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* Handle an incoming message by checking if it is a channel |
|
||||
* announcement, parse it and add the channel to the topology if yes. |
|
||||
* |
|
||||
* The format for a valid announcement is: |
|
||||
* <sig> CHAN <pk1> <pk2> <anchor txid> <block height> <tx position> <base_fee> |
|
||||
* <proportional_fee> <locktime> |
|
||||
*/ |
|
||||
static void handle_irc_privmsg(struct ircstate *istate, const struct privmsg *msg) |
|
||||
{ |
|
||||
char **splits = tal_strsplit(msg, msg->msg + 1, " ", STR_NO_EMPTY); |
|
||||
int splitcount = tal_count(splits) - 1; |
|
||||
|
|
||||
if (splitcount < 2) |
|
||||
return; |
|
||||
|
|
||||
char *type = splits[1]; |
|
||||
|
|
||||
if (splitcount == 10 && streq(type, "CHAN")) |
|
||||
handle_irc_channel_announcement(istate, msg, splits + 1); |
|
||||
else if (splitcount >= 5 && streq(type, "NODE")) |
|
||||
handle_irc_node_announcement(istate, msg, splits + 1); |
|
||||
} |
|
||||
|
|
||||
static void handle_irc_command(struct ircstate *istate, const struct irccommand *cmd) |
|
||||
{ |
|
||||
struct lightningd_state *dstate = istate->dstate; |
|
||||
char **params = tal_strsplit(cmd, cmd->params, " ", STR_NO_EMPTY); |
|
||||
|
|
||||
if (streq(cmd->command, "338") && tal_count(params) >= 4) { |
|
||||
dstate->external_ip = tal_strdup( |
|
||||
istate->dstate, params[3]); |
|
||||
log_debug(dstate->base_log, "Detected my own IP as %s", dstate->external_ip); |
|
||||
|
|
||||
// Add our node to the node_map for completeness
|
|
||||
add_node(istate->dstate->rstate, &dstate->id); |
|
||||
} else if (streq(cmd->command, "JOIN")) { |
|
||||
unsigned int delay; |
|
||||
|
|
||||
/* Throw away any existing announce timer, and announce within
|
|
||||
* 60 seconds. */ |
|
||||
dstate->announce = tal_free(dstate->announce); |
|
||||
|
|
||||
delay = pseudorand(60000000); |
|
||||
log_debug(istate->log, "Setting new announce time %u sec", |
|
||||
delay / 1000000); |
|
||||
dstate->announce = new_reltimer(&dstate->timers, istate, |
|
||||
time_from_usec(delay), |
|
||||
announce, istate); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static void handle_irc_connected(struct ircstate *istate) |
|
||||
{ |
|
||||
irc_send(istate, "JOIN", "#lightning-nodes"); |
|
||||
irc_send(istate, "WHOIS", "%s", istate->nick); |
|
||||
} |
|
||||
|
|
||||
void setup_irc_connection(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
// Register callback
|
|
||||
irc_privmsg_cb = *handle_irc_privmsg; |
|
||||
irc_connect_cb = *handle_irc_connected; |
|
||||
irc_disconnect_cb = *handle_irc_disconnect; |
|
||||
irc_command_cb = *handle_irc_command; |
|
||||
|
|
||||
struct ircstate *state = talz(dstate, struct ircstate); |
|
||||
state->dstate = dstate; |
|
||||
state->server = "irc.lfnet.org"; |
|
||||
state->reconnect_timeout = time_from_sec(15); |
|
||||
state->log = new_log(state, state->dstate->log_book, "%s:irc", |
|
||||
log_prefix(state->dstate->base_log)); |
|
||||
|
|
||||
/* Truncate nick at 13 bytes, would be imposed by freenode anyway */ |
|
||||
state->nick = tal_fmt( |
|
||||
state, |
|
||||
"N%.12s", |
|
||||
pubkey_to_hexstr(state, &dstate->id) + 1); |
|
||||
|
|
||||
/* We will see our own JOIN message, which will trigger announce */ |
|
||||
irc_connect(state); |
|
||||
} |
|
@ -1,9 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_IRC_ANNOUNCE_H |
|
||||
#define LIGHTNING_DAEMON_IRC_ANNOUNCE_H |
|
||||
#include "config.h" |
|
||||
#include "irc.h" |
|
||||
|
|
||||
// Main entrypoint for the lightning daemon
|
|
||||
void setup_irc_connection(struct lightningd_state *dstate); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_IRC_ANNOUNCE_H */ |
|
@ -1,174 +0,0 @@ |
|||||
#include "bitcoind.h" |
|
||||
#include "chaintopology.h" |
|
||||
#include "db.h" |
|
||||
#include "invoice.h" |
|
||||
#include "irc_announce.h" |
|
||||
#include "jsonrpc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "options.h" |
|
||||
#include "p2p_announce.h" |
|
||||
#include "peer.h" |
|
||||
#include "routing.h" |
|
||||
#include "secrets.h" |
|
||||
#include "timeout.h" |
|
||||
#include "utils.h" |
|
||||
#include <bitcoin/chainparams.h> |
|
||||
#include <ccan/container_of/container_of.h> |
|
||||
#include <ccan/err/err.h> |
|
||||
#include <ccan/io/io.h> |
|
||||
#include <ccan/opt/opt.h> |
|
||||
#include <ccan/tal/grab_file/grab_file.h> |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <ccan/time/time.h> |
|
||||
#include <ccan/timer/timer.h> |
|
||||
#include <errno.h> |
|
||||
#include <inttypes.h> |
|
||||
#include <signal.h> |
|
||||
#include <unistd.h> |
|
||||
#include <version.h> |
|
||||
|
|
||||
static struct lightningd_state *lightningd_state(void) |
|
||||
{ |
|
||||
struct lightningd_state *dstate = tal(NULL, struct lightningd_state); |
|
||||
struct sha256_double unused; |
|
||||
|
|
||||
dstate->log_book = new_log_book(dstate, 20*1024*1024, LOG_INFORM); |
|
||||
dstate->base_log = new_log(dstate, dstate->log_book, |
|
||||
"lightningd(%u):", (int)getpid()); |
|
||||
|
|
||||
list_head_init(&dstate->peers); |
|
||||
list_head_init(&dstate->pay_commands); |
|
||||
dstate->portnum = 0; |
|
||||
dstate->testnet = true; |
|
||||
timers_init(&dstate->timers, time_mono()); |
|
||||
list_head_init(&dstate->wallet); |
|
||||
list_head_init(&dstate->addresses); |
|
||||
dstate->dev_never_routefail = false; |
|
||||
dstate->rstate = new_routing_state(dstate, dstate->base_log, &unused); |
|
||||
dstate->reexec = NULL; |
|
||||
dstate->external_ip = NULL; |
|
||||
dstate->announce = NULL; |
|
||||
dstate->invoices = invoices_init(dstate); |
|
||||
return dstate; |
|
||||
} |
|
||||
|
|
||||
static void json_lightningd_dev_broadcast(struct command *cmd, |
|
||||
const char *buffer, |
|
||||
const jsmntok_t *params) |
|
||||
{ |
|
||||
json_dev_broadcast(cmd, cmd->dstate->topology, buffer, params); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command dev_broadcast_command = { |
|
||||
"dev-broadcast", |
|
||||
json_lightningd_dev_broadcast, |
|
||||
"Pretend we broadcast txs, but don't send to bitcoind", |
|
||||
"Returns an empty result on success (waits for flush if enabled)" |
|
||||
}; |
|
||||
AUTODATA(json_command, &dev_broadcast_command); |
|
||||
|
|
||||
int main(int argc, char *argv[]) |
|
||||
{ |
|
||||
struct lightningd_state *dstate = lightningd_state(); |
|
||||
|
|
||||
err_set_progname(argv[0]); |
|
||||
|
|
||||
if (!streq(protobuf_c_version(), PROTOBUF_C_VERSION)) |
|
||||
errx(1, "Compiled against protobuf %s, but have %s", |
|
||||
PROTOBUF_C_VERSION, protobuf_c_version()); |
|
||||
|
|
||||
secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY |
|
||||
| SECP256K1_CONTEXT_SIGN); |
|
||||
|
|
||||
dstate->topology = new_topology(dstate, dstate->base_log); |
|
||||
dstate->bitcoind = new_bitcoind(dstate, dstate->base_log); |
|
||||
dstate->bitcoind->chainparams = chainparams_for_network("regtest"); |
|
||||
|
|
||||
/* Handle options and config; move to .lightningd */ |
|
||||
register_opts(dstate); |
|
||||
handle_opts(dstate, argc, argv); |
|
||||
|
|
||||
/* Now we can set chain_hash properly. */ |
|
||||
dstate->rstate->chain_hash |
|
||||
= dstate->bitcoind->chainparams->genesis_blockhash; |
|
||||
|
|
||||
/* Activate crash log now we're in the right place. */ |
|
||||
crashlog_activate(dstate->base_log); |
|
||||
|
|
||||
/* Ignore SIGPIPE: we look at our write return values*/ |
|
||||
signal(SIGPIPE, SIG_IGN); |
|
||||
|
|
||||
/* Set up node ID and private key. */ |
|
||||
secrets_init(dstate); |
|
||||
new_node(dstate->rstate, &dstate->id); |
|
||||
|
|
||||
/* Read or create database. */ |
|
||||
db_init(dstate); |
|
||||
|
|
||||
/* Initialize block topology. */ |
|
||||
setup_topology(dstate->topology, dstate->bitcoind, &dstate->timers, |
|
||||
dstate->config.poll_time, |
|
||||
get_peer_min_block(dstate)); |
|
||||
|
|
||||
/* Create RPC socket (if any) */ |
|
||||
setup_jsonrpc(dstate, dstate->rpc_filename); |
|
||||
|
|
||||
/* Set up connections from peers (if dstate->portnum is set) */ |
|
||||
setup_listeners(dstate); |
|
||||
|
|
||||
/* set up IRC peer discovery */ |
|
||||
if (dstate->config.use_irc) |
|
||||
setup_irc_connection(dstate); |
|
||||
|
|
||||
/* set up P2P gossip protocol */ |
|
||||
setup_p2p_announce(dstate); |
|
||||
|
|
||||
log_info(dstate->base_log, "Hello world!"); |
|
||||
|
|
||||
/* If we loaded peers from database, reconnect now. */ |
|
||||
reconnect_peers(dstate); |
|
||||
|
|
||||
/* And send out anchors again if we're waiting. */ |
|
||||
rebroadcast_anchors(dstate); |
|
||||
|
|
||||
for (;;) { |
|
||||
struct timer *expired; |
|
||||
void *v = io_loop(&dstate->timers, &expired); |
|
||||
|
|
||||
/* We use io_break(dstate) to shut down. */ |
|
||||
if (v == dstate) |
|
||||
break; |
|
||||
|
|
||||
if (expired) |
|
||||
timer_expired(dstate, expired); |
|
||||
else |
|
||||
cleanup_peers(dstate); |
|
||||
} |
|
||||
|
|
||||
if (dstate->reexec) { |
|
||||
int fd; |
|
||||
|
|
||||
log_unusual(dstate->base_log, "Restart at user request"); |
|
||||
fflush(stdout); |
|
||||
fflush(stderr); |
|
||||
|
|
||||
/* Manually close all fds (or near enough!) */ |
|
||||
for (fd = 3; fd < 1024; fd++) |
|
||||
close(fd); |
|
||||
|
|
||||
if (dstate->dev_never_routefail) { |
|
||||
size_t n = tal_count(dstate->reexec); |
|
||||
tal_resizez(&dstate->reexec, n+1); |
|
||||
dstate->reexec[n-1] = "--dev-no-routefail"; |
|
||||
} |
|
||||
execvp(dstate->reexec[0], dstate->reexec); |
|
||||
fatal("Exec '%s' failed: %s", |
|
||||
dstate->reexec[0], strerror(errno)); |
|
||||
} |
|
||||
|
|
||||
tal_free(dstate); |
|
||||
opt_free_table(); |
|
||||
return 0; |
|
||||
} |
|
@ -1,36 +0,0 @@ |
|||||
#include "names.h" |
|
||||
#include <ccan/str/str.h> |
|
||||
/* Indented for 'check-source' because it has to be included after names.h */ |
|
||||
#include "daemon/gen_state_names.h" |
|
||||
#include "daemon/gen_pkt_names.h" |
|
||||
|
|
||||
const char *state_name(enum state s) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; enum_state_names[i].name; i++) |
|
||||
if (enum_state_names[i].v == s) |
|
||||
return enum_state_names[i].name; |
|
||||
return "unknown"; |
|
||||
} |
|
||||
|
|
||||
enum state name_to_state(const char *name) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; enum_state_names[i].name; i++) |
|
||||
if (streq(name, enum_state_names[i].name)) |
|
||||
return enum_state_names[i].v; |
|
||||
|
|
||||
return STATE_MAX; |
|
||||
} |
|
||||
|
|
||||
const char *pkt_name(Pkt__PktCase pkt) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; enum_PktCase_names[i].name; i++) |
|
||||
if (enum_PktCase_names[i].v == pkt) |
|
||||
return enum_PktCase_names[i].name; |
|
||||
return "unknown"; |
|
||||
} |
|
@ -1,10 +0,0 @@ |
|||||
#ifndef LIGHTNING_NAMES_H |
|
||||
#define LIGHTNING_NAMES_H |
|
||||
#include "config.h" |
|
||||
#include "lightning.pb-c.h" |
|
||||
#include "state_types.h" |
|
||||
|
|
||||
const char *state_name(enum state s); |
|
||||
enum state name_to_state(const char *name); |
|
||||
const char *pkt_name(Pkt__PktCase pkt); |
|
||||
#endif /* LIGHTNING_NAMES_H */ |
|
@ -1,80 +0,0 @@ |
|||||
#include "commit_tx.h" |
|
||||
#include "output_to_htlc.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
|
|
||||
/* FIXME: Array makes this O(n^2). Use a hash table. */ |
|
||||
struct wscript_by_wpkh { |
|
||||
struct htlc *h; |
|
||||
const u8 *wscript; |
|
||||
struct sha256 hash; |
|
||||
}; |
|
||||
|
|
||||
struct htlc_output_map { |
|
||||
struct wscript_by_wpkh *wpkh; |
|
||||
}; |
|
||||
|
|
||||
struct htlc_output_map *get_htlc_output_map(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
unsigned int commit_num) |
|
||||
{ |
|
||||
struct htlc_map_iter it; |
|
||||
struct htlc *h; |
|
||||
size_t i; |
|
||||
struct htlc_output_map *omap = tal(ctx, struct htlc_output_map); |
|
||||
|
|
||||
/* FIXME: use commit_num to filter htlcs. */ |
|
||||
if (side == LOCAL) |
|
||||
assert(commit_num <= peer->local.commit->commit_num); |
|
||||
else |
|
||||
assert(commit_num <= peer->remote.commit->commit_num); |
|
||||
|
|
||||
omap->wpkh = tal_arr(omap, struct wscript_by_wpkh, |
|
||||
htlc_map_count(&peer->htlcs)); |
|
||||
|
|
||||
for (i = 0, h = htlc_map_first(&peer->htlcs, &it); |
|
||||
h; |
|
||||
h = htlc_map_next(&peer->htlcs, &it)) { |
|
||||
omap->wpkh[i].h = h; |
|
||||
omap->wpkh[i].wscript = wscript_for_htlc(omap, peer, h, rhash, |
|
||||
side); |
|
||||
sha256(&omap->wpkh[i].hash, |
|
||||
omap->wpkh[i].wscript, |
|
||||
tal_count(omap->wpkh[i].wscript)); |
|
||||
i++; |
|
||||
} |
|
||||
tal_resize(&omap->wpkh, i); |
|
||||
return omap; |
|
||||
} |
|
||||
|
|
||||
static struct wscript_by_wpkh *get_wpkh(struct htlc_output_map *omap, |
|
||||
const u8 *script) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
if (!is_p2wsh(script)) |
|
||||
return NULL; |
|
||||
|
|
||||
for (i = 0; i < tal_count(omap->wpkh); i++) { |
|
||||
if (!memcmp(script + 2, omap->wpkh[i].hash.u.u8, |
|
||||
sizeof(omap->wpkh[i].hash))) |
|
||||
return &omap->wpkh[i]; |
|
||||
} |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
/* Which wscript does this pay to? */ |
|
||||
struct htlc *txout_get_htlc(struct htlc_output_map *omap, |
|
||||
const u8 *script, |
|
||||
const u8 **wscript) |
|
||||
{ |
|
||||
struct wscript_by_wpkh *wpkh = get_wpkh(omap, script); |
|
||||
|
|
||||
if (wpkh) { |
|
||||
*wscript = wpkh->wscript; |
|
||||
return wpkh->h; |
|
||||
} |
|
||||
return NULL; |
|
||||
} |
|
@ -1,20 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_OUTPUT_TO_HTLC_H |
|
||||
#define LIGHTNING_DAEMON_OUTPUT_TO_HTLC_H |
|
||||
#include "config.h" |
|
||||
#include "htlc.h" |
|
||||
|
|
||||
struct peer; |
|
||||
struct sha256; |
|
||||
|
|
||||
/* Get a map of HTLCs (including at least those at the given commit_num). */ |
|
||||
struct htlc_output_map *get_htlc_output_map(const tal_t *ctx, |
|
||||
const struct peer *peer, |
|
||||
const struct sha256 *rhash, |
|
||||
enum side side, |
|
||||
unsigned int commit_num); |
|
||||
|
|
||||
/* If this scriptPubkey pays to a HTLC, get the full wscript */ |
|
||||
struct htlc *txout_get_htlc(struct htlc_output_map *omap, |
|
||||
const u8 *script, const u8 **wscript); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_OUTPUT_TO_HTLC_H */ |
|
@ -1,231 +0,0 @@ |
|||||
#include "daemon/broadcast.h" |
|
||||
#include "daemon/chaintopology.h" |
|
||||
#include "daemon/log.h" |
|
||||
#include "daemon/p2p_announce.h" |
|
||||
#include "daemon/packets.h" |
|
||||
#include "daemon/peer.h" |
|
||||
#include "daemon/peer_internal.h" |
|
||||
#include "daemon/routing.h" |
|
||||
#include "daemon/secrets.h" |
|
||||
#include "daemon/timeout.h" |
|
||||
#include "utils.h" |
|
||||
|
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <secp256k1.h> |
|
||||
|
|
||||
static void broadcast_channel_update(struct lightningd_state *dstate, struct peer *peer) |
|
||||
{ |
|
||||
struct txlocator *loc; |
|
||||
u8 *serialized; |
|
||||
secp256k1_ecdsa_signature signature; |
|
||||
struct short_channel_id short_channel_id; |
|
||||
u32 timestamp = time_now().ts.tv_sec; |
|
||||
const tal_t *tmpctx = tal_tmpctx(dstate); |
|
||||
|
|
||||
loc = locate_tx(tmpctx, dstate->topology, &peer->anchor.txid); |
|
||||
short_channel_id.blocknum = loc->blkheight; |
|
||||
short_channel_id.txnum = loc->index; |
|
||||
short_channel_id.outnum = peer->anchor.index; |
|
||||
|
|
||||
/* Avoid triggering memcheck */ |
|
||||
memset(&signature, 0, sizeof(signature)); |
|
||||
|
|
||||
serialized = towire_channel_update(tmpctx, &signature, |
|
||||
&dstate->rstate->chain_hash, |
|
||||
&short_channel_id, |
|
||||
timestamp, |
|
||||
pubkey_cmp(&dstate->id, peer->id) > 0, |
|
||||
dstate->config.min_htlc_expiry, |
|
||||
//FIXME(cdecker) Make the minimum HTLC configurable
|
|
||||
1, |
|
||||
dstate->config.fee_base, |
|
||||
dstate->config.fee_per_satoshi); |
|
||||
privkey_sign(dstate, serialized + 66, tal_count(serialized) - 66, |
|
||||
&signature); |
|
||||
serialized = towire_channel_update(tmpctx, &signature, |
|
||||
&dstate->rstate->chain_hash, |
|
||||
&short_channel_id, |
|
||||
timestamp, |
|
||||
pubkey_cmp(&dstate->id, peer->id) > 0, |
|
||||
dstate->config.min_htlc_expiry, |
|
||||
1, |
|
||||
dstate->config.fee_base, |
|
||||
dstate->config.fee_per_satoshi); |
|
||||
u8 *tag = tal_arr(tmpctx, u8, 0); |
|
||||
towire_short_channel_id(&tag, &short_channel_id); |
|
||||
queue_broadcast(dstate->rstate->broadcasts, WIRE_CHANNEL_UPDATE, tag, serialized); |
|
||||
tal_free(tmpctx); |
|
||||
} |
|
||||
|
|
||||
static void broadcast_node_announcement(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
u8 *serialized; |
|
||||
secp256k1_ecdsa_signature signature; |
|
||||
static const u8 rgb_color[3]; |
|
||||
static const u8 alias[32]; |
|
||||
u32 timestamp = time_now().ts.tv_sec; |
|
||||
const tal_t *tmpctx = tal_tmpctx(dstate); |
|
||||
u8 *address; |
|
||||
|
|
||||
/* Are we listening for incoming connections at all? */ |
|
||||
if (!dstate->external_ip || !dstate->portnum) { |
|
||||
tal_free(tmpctx); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
/* Avoid triggering memcheck */ |
|
||||
memset(&signature, 0, sizeof(signature)); |
|
||||
|
|
||||
address = write_ip(tmpctx, dstate->external_ip, dstate->portnum); |
|
||||
serialized = towire_node_announcement(tmpctx, &signature, |
|
||||
NULL, |
|
||||
timestamp, |
|
||||
&dstate->id, rgb_color, alias, |
|
||||
address); |
|
||||
privkey_sign(dstate, serialized + 66, tal_count(serialized) - 66, |
|
||||
&signature); |
|
||||
serialized = towire_node_announcement(tmpctx, &signature, |
|
||||
NULL, |
|
||||
timestamp, |
|
||||
&dstate->id, rgb_color, alias, |
|
||||
address); |
|
||||
u8 *tag = tal_arr(tmpctx, u8, 0); |
|
||||
towire_pubkey(&tag, &dstate->id); |
|
||||
queue_broadcast(dstate->rstate->broadcasts, WIRE_NODE_ANNOUNCEMENT, tag, |
|
||||
serialized); |
|
||||
tal_free(tmpctx); |
|
||||
} |
|
||||
|
|
||||
static void broadcast_channel_announcement(struct lightningd_state *dstate, struct peer *peer) |
|
||||
{ |
|
||||
struct txlocator *loc; |
|
||||
struct short_channel_id short_channel_id; |
|
||||
secp256k1_ecdsa_signature node_signature[2]; |
|
||||
secp256k1_ecdsa_signature bitcoin_signature[2]; |
|
||||
const struct pubkey *node_id[2]; |
|
||||
const struct pubkey *bitcoin_key[2]; |
|
||||
secp256k1_ecdsa_signature *my_node_signature; |
|
||||
secp256k1_ecdsa_signature *my_bitcoin_signature; |
|
||||
u8 *serialized; |
|
||||
const tal_t *tmpctx = tal_tmpctx(dstate); |
|
||||
|
|
||||
loc = locate_tx(tmpctx, dstate->topology, &peer->anchor.txid); |
|
||||
|
|
||||
short_channel_id.blocknum = loc->blkheight; |
|
||||
short_channel_id.txnum = loc->index; |
|
||||
short_channel_id.outnum = peer->anchor.index; |
|
||||
|
|
||||
/* Set all sigs to zero */ |
|
||||
memset(node_signature, 0, sizeof(node_signature)); |
|
||||
memset(bitcoin_signature, 0, sizeof(bitcoin_signature)); |
|
||||
|
|
||||
//FIXME(cdecker) Copy remote stored signatures into place
|
|
||||
if (pubkey_cmp(&dstate->id, peer->id) > 0) { |
|
||||
node_id[0] = peer->id; |
|
||||
node_id[1] = &dstate->id; |
|
||||
bitcoin_key[0] = peer->id; |
|
||||
bitcoin_key[1] = &dstate->id; |
|
||||
my_node_signature = &node_signature[1]; |
|
||||
my_bitcoin_signature = &bitcoin_signature[1]; |
|
||||
} else { |
|
||||
node_id[1] = peer->id; |
|
||||
node_id[0] = &dstate->id; |
|
||||
bitcoin_key[1] = peer->id; |
|
||||
bitcoin_key[0] = &dstate->id; |
|
||||
my_node_signature = &node_signature[0]; |
|
||||
my_bitcoin_signature = &bitcoin_signature[0]; |
|
||||
} |
|
||||
|
|
||||
/* Sign the node_id with the bitcoin_key, proves delegation */ |
|
||||
serialized = tal_arr(tmpctx, u8, 0); |
|
||||
towire_pubkey(&serialized, &dstate->id); |
|
||||
privkey_sign(dstate, serialized, tal_count(serialized), my_bitcoin_signature); |
|
||||
|
|
||||
/* BOLT #7:
|
|
||||
* |
|
||||
* The creating node MUST compute the double-SHA256 hash `h` of the |
|
||||
* message, starting at offset 256, up to the end of the message. |
|
||||
*/ |
|
||||
serialized = towire_channel_announcement(tmpctx, &node_signature[0], |
|
||||
&node_signature[1], |
|
||||
&bitcoin_signature[0], |
|
||||
&bitcoin_signature[1], |
|
||||
NULL, |
|
||||
&dstate->rstate->chain_hash, |
|
||||
&short_channel_id, |
|
||||
node_id[0], |
|
||||
node_id[1], |
|
||||
bitcoin_key[0], |
|
||||
bitcoin_key[1]); |
|
||||
privkey_sign(dstate, serialized + 256, tal_count(serialized) - 256, my_node_signature); |
|
||||
|
|
||||
serialized = towire_channel_announcement(tmpctx, &node_signature[0], |
|
||||
&node_signature[1], |
|
||||
&bitcoin_signature[0], |
|
||||
&bitcoin_signature[1], |
|
||||
NULL, |
|
||||
&dstate->rstate->chain_hash, |
|
||||
&short_channel_id, |
|
||||
node_id[0], |
|
||||
node_id[1], |
|
||||
bitcoin_key[0], |
|
||||
bitcoin_key[1]); |
|
||||
u8 *tag = tal_arr(tmpctx, u8, 0); |
|
||||
towire_short_channel_id(&tag, &short_channel_id); |
|
||||
queue_broadcast(dstate->rstate->broadcasts, WIRE_CHANNEL_ANNOUNCEMENT, |
|
||||
tag, serialized); |
|
||||
tal_free(tmpctx); |
|
||||
} |
|
||||
|
|
||||
static void announce(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
struct peer *p; |
|
||||
int nchan = 0; |
|
||||
|
|
||||
new_reltimer(&dstate->timers, dstate, time_from_sec(5*60*60), announce, dstate); |
|
||||
|
|
||||
list_for_each(&dstate->peers, p, list) { |
|
||||
if (state_is_normal(p->state)) { |
|
||||
broadcast_channel_announcement(dstate, p); |
|
||||
broadcast_channel_update(dstate, p); |
|
||||
nchan += 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* No point in broadcasting our node if we don't have a channel */ |
|
||||
if (nchan > 0) |
|
||||
broadcast_node_announcement(dstate); |
|
||||
} |
|
||||
|
|
||||
void announce_channel(struct lightningd_state *dstate, struct peer *peer) |
|
||||
{ |
|
||||
broadcast_channel_announcement(dstate, peer); |
|
||||
broadcast_channel_update(dstate, peer); |
|
||||
broadcast_node_announcement(dstate); |
|
||||
|
|
||||
} |
|
||||
|
|
||||
static void process_broadcast_queue(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
struct peer *p; |
|
||||
struct queued_message *msg; |
|
||||
new_reltimer(&dstate->timers, dstate, time_from_sec(30), process_broadcast_queue, dstate); |
|
||||
list_for_each(&dstate->peers, p, list) { |
|
||||
if (!state_is_normal(p->state)) |
|
||||
continue; |
|
||||
msg = next_broadcast_message(dstate->rstate->broadcasts, |
|
||||
&p->broadcast_index); |
|
||||
while (msg != NULL) { |
|
||||
queue_pkt_nested(p, msg->type, msg->payload); |
|
||||
msg = next_broadcast_message(dstate->rstate->broadcasts, |
|
||||
&p->broadcast_index); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void setup_p2p_announce(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
new_reltimer(&dstate->timers, dstate, time_from_sec(5*60*60), announce, dstate); |
|
||||
new_reltimer(&dstate->timers, dstate, time_from_sec(30), process_broadcast_queue, dstate); |
|
||||
} |
|
@ -1,15 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_P2P_ANNOUNCE_H |
|
||||
#define LIGHTNING_DAEMON_P2P_ANNOUNCE_H |
|
||||
#include "config.h" |
|
||||
#include "daemon/broadcast.h" |
|
||||
#include "daemon/lightningd.h" |
|
||||
#include "daemon/routing.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "wire/gen_peer_wire.h" |
|
||||
|
|
||||
void setup_p2p_announce(struct lightningd_state *dstate); |
|
||||
|
|
||||
/* Used to announce the existence of a channel and the endpoints */ |
|
||||
void announce_channel(struct lightningd_state *dstate, struct peer *peer); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_P2P_ANNOUNCE_H */ |
|
@ -1,599 +0,0 @@ |
|||||
#include "bitcoin/preimage.h" |
|
||||
#include "bitcoin/script.h" |
|
||||
#include "bitcoin/tx.h" |
|
||||
#include "chaintopology.h" |
|
||||
#include "close_tx.h" |
|
||||
#include "commit_tx.h" |
|
||||
#include "cryptopkt.h" |
|
||||
#include "htlc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "names.h" |
|
||||
#include "packets.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "protobuf_convert.h" |
|
||||
#include "secrets.h" |
|
||||
#include "state.h" |
|
||||
#include "utils.h" |
|
||||
#include <ccan/array_size/array_size.h> |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/io/io.h> |
|
||||
#include <ccan/mem/mem.h> |
|
||||
#include <ccan/ptrint/ptrint.h> |
|
||||
#include <ccan/str/hex/hex.h> |
|
||||
#include <ccan/structeq/structeq.h> |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <inttypes.h> |
|
||||
|
|
||||
/* Wrap (and own!) member inside Pkt */ |
|
||||
static Pkt *make_pkt(const tal_t *ctx, Pkt__PktCase type, const void *msg) |
|
||||
{ |
|
||||
Pkt *pkt = tal(ctx, Pkt); |
|
||||
|
|
||||
pkt__init(pkt); |
|
||||
pkt->pkt_case = type; |
|
||||
/* This is a union, so doesn't matter which we assign. */ |
|
||||
pkt->error = (Error *)tal_steal(pkt, msg); |
|
||||
|
|
||||
/* This makes sure all packets are valid. */ |
|
||||
#ifndef NDEBUG |
|
||||
{ |
|
||||
size_t len; |
|
||||
u8 *packed; |
|
||||
Pkt *cpy; |
|
||||
|
|
||||
len = pkt__get_packed_size(pkt); |
|
||||
packed = tal_arr(pkt, u8, len); |
|
||||
pkt__pack(pkt, packed); |
|
||||
cpy = pkt__unpack(NULL, len, memcheck(packed, len)); |
|
||||
assert(cpy); |
|
||||
pkt__free_unpacked(cpy, NULL); |
|
||||
tal_free(packed); |
|
||||
} |
|
||||
#endif |
|
||||
return pkt; |
|
||||
} |
|
||||
|
|
||||
static void queue_raw_pkt(struct peer *peer, Pkt *pkt) |
|
||||
{ |
|
||||
size_t n = tal_count(peer->outpkt); |
|
||||
tal_resize(&peer->outpkt, n+1); |
|
||||
peer->outpkt[n] = pkt; |
|
||||
|
|
||||
log_debug(peer->log, "Queued pkt %s (order=%"PRIu64")", |
|
||||
pkt_name(pkt->pkt_case), peer->order_counter); |
|
||||
|
|
||||
/* In case it was waiting for output. */ |
|
||||
io_wake(peer); |
|
||||
} |
|
||||
|
|
||||
static void queue_pkt(struct peer *peer, Pkt__PktCase type, const void *msg) |
|
||||
{ |
|
||||
queue_raw_pkt(peer, make_pkt(peer, type, msg)); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_open(struct peer *peer, bool offer_anchor) |
|
||||
{ |
|
||||
OpenChannel *o = tal(peer, OpenChannel); |
|
||||
|
|
||||
open_channel__init(o); |
|
||||
o->revocation_hash = sha256_to_proto(o, &peer->local.commit->revocation_hash); |
|
||||
o->next_revocation_hash = sha256_to_proto(o, &peer->local.next_revocation_hash); |
|
||||
o->commit_key = pubkey_to_proto(o, &peer->local.commitkey); |
|
||||
o->final_key = pubkey_to_proto(o, &peer->local.finalkey); |
|
||||
o->delay = tal(o, Locktime); |
|
||||
locktime__init(o->delay); |
|
||||
o->delay->locktime_case = LOCKTIME__LOCKTIME_BLOCKS; |
|
||||
o->delay->blocks = rel_locktime_to_blocks(&peer->local.locktime); |
|
||||
o->initial_fee_rate = peer->local.commit_fee_rate; |
|
||||
if (offer_anchor) |
|
||||
o->anch = OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR; |
|
||||
else |
|
||||
o->anch = OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR; |
|
||||
o->min_depth = peer->local.mindepth; |
|
||||
queue_pkt(peer, PKT__PKT_OPEN, o); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_anchor(struct peer *peer) |
|
||||
{ |
|
||||
OpenAnchor *a = tal(peer, OpenAnchor); |
|
||||
|
|
||||
open_anchor__init(a); |
|
||||
a->txid = sha256_to_proto(a, &peer->anchor.txid.sha); |
|
||||
a->output_index = peer->anchor.index; |
|
||||
a->amount = peer->anchor.satoshis; |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_OPEN_ANCHOR, a); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_open_commit_sig(struct peer *peer) |
|
||||
{ |
|
||||
OpenCommitSig *s = tal(peer, OpenCommitSig); |
|
||||
|
|
||||
open_commit_sig__init(s); |
|
||||
|
|
||||
s->sig = signature_to_proto(s, peer->remote.commit->sig); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_OPEN_COMMIT_SIG, s); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_open_complete(struct peer *peer) |
|
||||
{ |
|
||||
OpenComplete *o = tal(peer, OpenComplete); |
|
||||
|
|
||||
open_complete__init(o); |
|
||||
queue_pkt(peer, PKT__PKT_OPEN_COMPLETE, o); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_htlc_add(struct peer *peer, struct htlc *htlc) |
|
||||
{ |
|
||||
UpdateAddHtlc *u = tal(peer, UpdateAddHtlc); |
|
||||
|
|
||||
update_add_htlc__init(u); |
|
||||
|
|
||||
u->id = htlc->id; |
|
||||
u->amount_msat = htlc->msatoshi; |
|
||||
u->r_hash = sha256_to_proto(u, &htlc->rhash); |
|
||||
u->expiry = abs_locktime_to_proto(u, &htlc->expiry); |
|
||||
u->route = tal(u, Routing); |
|
||||
routing__init(u->route); |
|
||||
u->route->info.data = tal_dup_arr(u, u8, |
|
||||
htlc->routing, |
|
||||
tal_count(htlc->routing), |
|
||||
0); |
|
||||
u->route->info.len = tal_count(u->route->info.data); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_ADD_HTLC, u); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_htlc_fulfill(struct peer *peer, struct htlc *htlc) |
|
||||
{ |
|
||||
UpdateFulfillHtlc *f = tal(peer, UpdateFulfillHtlc); |
|
||||
|
|
||||
update_fulfill_htlc__init(f); |
|
||||
f->id = htlc->id; |
|
||||
f->r = preimage_to_proto(f, htlc->r); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_FULFILL_HTLC, f); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_htlc_fail(struct peer *peer, struct htlc *htlc) |
|
||||
{ |
|
||||
UpdateFailHtlc *f = tal(peer, UpdateFailHtlc); |
|
||||
|
|
||||
update_fail_htlc__init(f); |
|
||||
f->id = htlc->id; |
|
||||
|
|
||||
f->reason = tal(f, FailReason); |
|
||||
fail_reason__init(f->reason); |
|
||||
f->reason->info.len = tal_count(htlc->fail); |
|
||||
f->reason->info.data = tal_dup_arr(f->reason, u8, |
|
||||
htlc->fail, f->reason->info.len, 0); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_FAIL_HTLC, f); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_feechange(struct peer *peer, u64 feerate) |
|
||||
{ |
|
||||
UpdateFee *f = tal(peer, UpdateFee); |
|
||||
|
|
||||
update_fee__init(f); |
|
||||
f->fee_rate = feerate; |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_FEE, f); |
|
||||
} |
|
||||
|
|
||||
/* OK, we're sending a signature for their pending changes. */ |
|
||||
void queue_pkt_commit(struct peer *peer, const secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
UpdateCommit *u = tal(peer, UpdateCommit); |
|
||||
|
|
||||
/* Now send message */ |
|
||||
update_commit__init(u); |
|
||||
if (sig) |
|
||||
u->sig = signature_to_proto(u, sig); |
|
||||
else |
|
||||
u->sig = NULL; |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_COMMIT, u); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/* Send a preimage for the old commit tx. The one we've just committed to is
|
|
||||
* in peer->local.commit. */ |
|
||||
void queue_pkt_revocation(struct peer *peer, |
|
||||
const struct sha256 *preimage, |
|
||||
const struct sha256 *next_hash) |
|
||||
{ |
|
||||
UpdateRevocation *u = tal(peer, UpdateRevocation); |
|
||||
|
|
||||
update_revocation__init(u); |
|
||||
|
|
||||
u->revocation_preimage = sha256_to_proto(u, preimage); |
|
||||
u->next_revocation_hash = sha256_to_proto(u, next_hash); |
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_REVOCATION, u); |
|
||||
} |
|
||||
|
|
||||
/* Send a serialized nested packet. */ |
|
||||
void queue_pkt_nested(struct peer *peer, |
|
||||
int type, |
|
||||
const u8 *nested_pkt) |
|
||||
{ |
|
||||
NestedPkt *pb = tal(peer, NestedPkt); |
|
||||
nested_pkt__init(pb); |
|
||||
pb->type = type; |
|
||||
pb->inner_pkt.len = tal_count(nested_pkt); |
|
||||
pb->inner_pkt.data = tal_dup_arr(pb, u8, nested_pkt, pb->inner_pkt.len, 0); |
|
||||
queue_pkt(peer, PKT__PKT_NESTED, pb); |
|
||||
} |
|
||||
|
|
||||
Pkt *pkt_err(struct peer *peer, const char *msg, ...) |
|
||||
{ |
|
||||
Error *e = tal(peer, Error); |
|
||||
va_list ap; |
|
||||
|
|
||||
error__init(e); |
|
||||
va_start(ap, msg); |
|
||||
e->problem = tal_vfmt(e, msg, ap); |
|
||||
va_end(ap); |
|
||||
|
|
||||
log_unusual(peer->log, "Sending PKT_ERROR: %s", e->problem); |
|
||||
return make_pkt(peer, PKT__PKT_ERROR, e); |
|
||||
} |
|
||||
|
|
||||
Pkt *pkt_init(struct peer *peer, u64 ack) |
|
||||
{ |
|
||||
Init *i = tal(peer, Init); |
|
||||
init__init(i); |
|
||||
i->ack = ack; |
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node SHOULD set the `features` field of the `init` |
|
||||
* message to a bitset representing features it supports. |
|
||||
*/ |
|
||||
/* No features yet! */ |
|
||||
return make_pkt(peer, PKT__PKT_INIT, i); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_err(struct peer *peer, Pkt *err) |
|
||||
{ |
|
||||
queue_raw_pkt(peer, err); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_close_shutdown(struct peer *peer) |
|
||||
{ |
|
||||
CloseShutdown *c = tal(peer, CloseShutdown); |
|
||||
|
|
||||
close_shutdown__init(c); |
|
||||
c->scriptpubkey.data = tal_dup_arr(c, u8, |
|
||||
peer->closing.our_script, |
|
||||
tal_count(peer->closing.our_script), |
|
||||
0); |
|
||||
c->scriptpubkey.len = tal_count(c->scriptpubkey.data); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_CLOSE_SHUTDOWN, c); |
|
||||
} |
|
||||
|
|
||||
void queue_pkt_close_signature(struct peer *peer) |
|
||||
{ |
|
||||
CloseSignature *c = tal(peer, CloseSignature); |
|
||||
struct bitcoin_tx *close_tx; |
|
||||
secp256k1_ecdsa_signature our_close_sig; |
|
||||
|
|
||||
close_signature__init(c); |
|
||||
close_tx = peer_create_close_tx(c, peer, peer->closing.our_fee); |
|
||||
|
|
||||
peer_sign_mutual_close(peer, close_tx, &our_close_sig); |
|
||||
c->sig = signature_to_proto(c, &our_close_sig); |
|
||||
c->close_fee = peer->closing.our_fee; |
|
||||
log_info(peer->log, "queue_pkt_close_signature: offered close fee %" |
|
||||
PRIu64, c->close_fee); |
|
||||
|
|
||||
queue_pkt(peer, PKT__PKT_CLOSE_SIGNATURE, c); |
|
||||
} |
|
||||
|
|
||||
Pkt *pkt_err_unexpected(struct peer *peer, const Pkt *pkt) |
|
||||
{ |
|
||||
return pkt_err(peer, "Unexpected packet %s", pkt_name(pkt->pkt_case)); |
|
||||
} |
|
||||
|
|
||||
/* Process various packets: return an error packet on failure. */ |
|
||||
Pkt *accept_pkt_open(struct peer *peer, const Pkt *pkt, |
|
||||
struct sha256 *revocation_hash, |
|
||||
struct sha256 *next_revocation_hash) |
|
||||
{ |
|
||||
struct rel_locktime locktime; |
|
||||
const OpenChannel *o = pkt->open; |
|
||||
u64 feerate = get_feerate(peer->dstate->topology); |
|
||||
|
|
||||
if (!proto_to_rel_locktime(o->delay, &locktime)) |
|
||||
return pkt_err(peer, "Invalid delay"); |
|
||||
if (o->delay->locktime_case != LOCKTIME__LOCKTIME_BLOCKS) |
|
||||
return pkt_err(peer, "Delay in seconds not accepted"); |
|
||||
if (o->delay->blocks > peer->dstate->config.locktime_max) |
|
||||
return pkt_err(peer, "Delay %u too great", o->delay->blocks); |
|
||||
if (o->min_depth > peer->dstate->config.anchor_confirms_max) |
|
||||
return pkt_err(peer, "min_depth %u too great", o->min_depth); |
|
||||
if (o->initial_fee_rate |
|
||||
< feerate * peer->dstate->config.commitment_fee_min_percent / 100) |
|
||||
return pkt_err(peer, "Commitment fee %u below %"PRIu64" x %u%%", |
|
||||
o->initial_fee_rate, feerate, |
|
||||
peer->dstate->config.commitment_fee_min_percent); |
|
||||
if (peer->dstate->config.commitment_fee_max_percent != 0 |
|
||||
&& (o->initial_fee_rate |
|
||||
> feerate * peer->dstate->config.commitment_fee_max_percent/100)) |
|
||||
return pkt_err(peer, "Commitment fee %u above %"PRIu64" x %u%%", |
|
||||
o->initial_fee_rate, feerate, |
|
||||
peer->dstate->config.commitment_fee_max_percent); |
|
||||
if (o->anch == OPEN_CHANNEL__ANCHOR_OFFER__WILL_CREATE_ANCHOR) |
|
||||
peer->remote.offer_anchor = true; |
|
||||
else if (o->anch == OPEN_CHANNEL__ANCHOR_OFFER__WONT_CREATE_ANCHOR) |
|
||||
peer->remote.offer_anchor = false; |
|
||||
else |
|
||||
return pkt_err(peer, "Unknown offer anchor value %u", |
|
||||
o->anch); |
|
||||
|
|
||||
if (peer->remote.offer_anchor == peer->local.offer_anchor) |
|
||||
return pkt_err(peer, "Exactly one side can offer anchor (we %s)", |
|
||||
peer->local.offer_anchor ? "do" : "don't"); |
|
||||
|
|
||||
if (!proto_to_rel_locktime(o->delay, &peer->remote.locktime)) |
|
||||
return pkt_err(peer, "Malformed locktime"); |
|
||||
peer->remote.mindepth = o->min_depth; |
|
||||
peer->remote.commit_fee_rate = o->initial_fee_rate; |
|
||||
if (!proto_to_pubkey(o->commit_key, &peer->remote.commitkey)) |
|
||||
return pkt_err(peer, "Bad commitkey"); |
|
||||
if (!proto_to_pubkey(o->final_key, &peer->remote.finalkey)) |
|
||||
return pkt_err(peer, "Bad finalkey"); |
|
||||
|
|
||||
proto_to_sha256(o->revocation_hash, revocation_hash); |
|
||||
proto_to_sha256(o->next_revocation_hash, next_revocation_hash); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_anchor(struct peer *peer, const Pkt *pkt) |
|
||||
{ |
|
||||
const OpenAnchor *a = pkt->open_anchor; |
|
||||
|
|
||||
/* They must be offering anchor for us to try accepting */ |
|
||||
assert(!peer->local.offer_anchor); |
|
||||
assert(peer->remote.offer_anchor); |
|
||||
|
|
||||
if (anchor_too_large(a->amount)) |
|
||||
return pkt_err(peer, "Anchor millisatoshis exceeds 32 bits"); |
|
||||
|
|
||||
proto_to_sha256(a->txid, &peer->anchor.txid.sha); |
|
||||
peer->anchor.index = a->output_index; |
|
||||
peer->anchor.satoshis = a->amount; |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_open_commit_sig(struct peer *peer, const Pkt *pkt, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
const OpenCommitSig *s = pkt->open_commit_sig; |
|
||||
|
|
||||
if (!proto_to_signature(s->sig, sig)) |
|
||||
return pkt_err(peer, "Malformed signature"); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_open_complete(struct peer *peer, const Pkt *pkt) |
|
||||
{ |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* We add changes to both our staging cstate (as they did when they sent |
|
||||
* it) and theirs (as they will when we ack it). |
|
||||
*/ |
|
||||
Pkt *accept_pkt_htlc_add(struct peer *peer, const Pkt *pkt, struct htlc **h) |
|
||||
{ |
|
||||
const UpdateAddHtlc *u = pkt->update_add_htlc; |
|
||||
struct sha256 rhash; |
|
||||
struct abs_locktime expiry; |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* `amount_msat` MUST BE greater than 0. |
|
||||
*/ |
|
||||
if (u->amount_msat == 0) |
|
||||
return pkt_err(peer, "Invalid amount_msat"); |
|
||||
|
|
||||
proto_to_sha256(u->r_hash, &rhash); |
|
||||
if (!proto_to_abs_locktime(u->expiry, &expiry)) |
|
||||
return pkt_err(peer, "Invalid HTLC expiry"); |
|
||||
|
|
||||
if (abs_locktime_is_seconds(&expiry)) |
|
||||
return pkt_err(peer, "HTLC expiry in seconds not supported!"); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node MUST NOT add a HTLC if it would result in it |
|
||||
* offering more than 300 HTLCs in the remote commitment transaction. |
|
||||
*/ |
|
||||
if (peer->remote.staging_cstate->side[REMOTE].num_htlcs == 300) |
|
||||
return pkt_err(peer, "Too many HTLCs"); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node MUST set `id` to a unique identifier for this HTLC |
|
||||
* amongst all past or future `update_add_htlc` messages. |
|
||||
*/ |
|
||||
/* Note that it's not *our* problem if they do this, it's
|
|
||||
* theirs (future confusion). Nonetheless, we detect and |
|
||||
* error for them. */ |
|
||||
if (htlc_get(&peer->htlcs, u->id, REMOTE)) |
|
||||
return pkt_err(peer, "HTLC id %"PRIu64" clashes for you", u->id); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* ...and the receiving node MUST add the HTLC addition to the |
|
||||
* unacked changeset for its local commitment. */ |
|
||||
*h = peer_new_htlc(peer, u->id, u->amount_msat, &rhash, |
|
||||
abs_locktime_to_blocks(&expiry), |
|
||||
u->route->info.data, u->route->info.len, |
|
||||
NULL, RCVD_ADD_HTLC); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
static Pkt *find_commited_htlc(struct peer *peer, uint64_t id, |
|
||||
struct htlc **local_htlc) |
|
||||
{ |
|
||||
*local_htlc = htlc_get(&peer->htlcs, id, LOCAL); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node MUST check that `id` corresponds to an HTLC in its |
|
||||
* current commitment transaction, and MUST fail the |
|
||||
* connection if it does not. |
|
||||
*/ |
|
||||
if (!(*local_htlc)) |
|
||||
return pkt_err(peer, "Did not find HTLC %"PRIu64, id); |
|
||||
|
|
||||
if ((*local_htlc)->state != SENT_ADD_ACK_REVOCATION) |
|
||||
return pkt_err(peer, "HTLC %"PRIu64" state %s", id, |
|
||||
htlc_state_name((*local_htlc)->state)); |
|
||||
|
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_htlc_fail(struct peer *peer, const Pkt *pkt, struct htlc **h, |
|
||||
u8 **fail) |
|
||||
{ |
|
||||
const UpdateFailHtlc *f = pkt->update_fail_htlc; |
|
||||
Pkt *err; |
|
||||
|
|
||||
err = find_commited_htlc(peer, f->id, h); |
|
||||
if (err) |
|
||||
return err; |
|
||||
|
|
||||
if ((*h)->r) |
|
||||
return pkt_err(peer, "HTLC %"PRIu64" already fulfilled", |
|
||||
(*h)->id); |
|
||||
|
|
||||
*fail = tal_dup_arr(*h, u8, f->reason->info.data, f->reason->info.len,0); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt, struct htlc **h, |
|
||||
struct preimage *r) |
|
||||
{ |
|
||||
const UpdateFulfillHtlc *f = pkt->update_fulfill_htlc; |
|
||||
struct sha256 rhash; |
|
||||
Pkt *err; |
|
||||
|
|
||||
err = find_commited_htlc(peer, f->id, h); |
|
||||
if (err) |
|
||||
return err; |
|
||||
|
|
||||
/* Now, it must solve the HTLC rhash puzzle. */ |
|
||||
proto_to_preimage(f->r, r); |
|
||||
sha256(&rhash, r, sizeof(*r)); |
|
||||
|
|
||||
if (!structeq(&rhash, &(*h)->rhash)) |
|
||||
return pkt_err(peer, "Invalid r for %"PRIu64, f->id); |
|
||||
|
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_update_fee(struct peer *peer, const Pkt *pkt, u64 *feerate) |
|
||||
{ |
|
||||
const UpdateFee *f = pkt->update_fee; |
|
||||
|
|
||||
*feerate = f->fee_rate; |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
const UpdateCommit *c = pkt->update_commit; |
|
||||
|
|
||||
if (!c->sig && sig) |
|
||||
return pkt_err(peer, "Expected signature"); |
|
||||
|
|
||||
if (!sig && c->sig) |
|
||||
return pkt_err(peer, "Unexpected signature"); |
|
||||
|
|
||||
if (!sig && !c->sig) |
|
||||
return NULL; |
|
||||
|
|
||||
if (!proto_to_signature(c->sig, sig)) |
|
||||
return pkt_err(peer, "Malformed signature"); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt) |
|
||||
{ |
|
||||
const UpdateRevocation *r = pkt->update_revocation; |
|
||||
struct sha256 h, preimage; |
|
||||
|
|
||||
assert(peer->their_prev_revocation_hash); |
|
||||
proto_to_sha256(r->revocation_preimage, &preimage); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* The receiver of `update_revocation` MUST check that the |
|
||||
* SHA256 hash of `revocation_preimage` matches the previous commitment |
|
||||
* transaction, and MUST fail if it does not. |
|
||||
*/ |
|
||||
sha256(&h, &preimage, sizeof(preimage)); |
|
||||
if (!structeq(&h, peer->their_prev_revocation_hash)) { |
|
||||
log_unusual(peer->log, "Incorrect preimage for %"PRIu64, |
|
||||
peer->remote.commit->commit_num - 1); |
|
||||
return pkt_err(peer, "complete preimage incorrect"); |
|
||||
} |
|
||||
|
|
||||
// save revocation preimages in shachain
|
|
||||
if (!shachain_add_hash(&peer->their_preimages, |
|
||||
0xFFFFFFFFFFFFFFFFL |
|
||||
- (peer->remote.commit->commit_num - 1), |
|
||||
&preimage)) |
|
||||
return pkt_err(peer, "preimage not next in shachain"); |
|
||||
|
|
||||
log_debug(peer->log, "Got revocation preimage %"PRIu64, |
|
||||
peer->remote.commit->commit_num - 1); |
|
||||
|
|
||||
/* Clear the previous revocation hash. */ |
|
||||
peer->their_prev_revocation_hash |
|
||||
= tal_free(peer->their_prev_revocation_hash); |
|
||||
|
|
||||
/* Save next revocation hash. */ |
|
||||
proto_to_sha256(r->next_revocation_hash, |
|
||||
&peer->remote.next_revocation_hash); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
Pkt *accept_pkt_close_shutdown(struct peer *peer, const Pkt *pkt) |
|
||||
{ |
|
||||
const CloseShutdown *c = pkt->close_shutdown; |
|
||||
|
|
||||
peer->closing.their_script = tal_dup_arr(peer, u8, |
|
||||
c->scriptpubkey.data, |
|
||||
c->scriptpubkey.len, 0); |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* 1. `OP_DUP` `OP_HASH160` `20` 20-bytes `OP_EQUALVERIFY` `OP_CHECKSIG` |
|
||||
* (pay to pubkey hash), OR |
|
||||
* 2. `OP_HASH160` `20` 20-bytes `OP_EQUAL` (pay to script hash), OR |
|
||||
* 3. `OP_0` `20` 20-bytes (version 0 pay to witness pubkey), OR |
|
||||
* 4. `OP_0` `32` 32-bytes (version 0 pay to witness script hash) |
|
||||
* |
|
||||
* A node receiving `close_shutdown` SHOULD fail the connection |
|
||||
* `script_pubkey` is not one of those forms. |
|
||||
*/ |
|
||||
if (!is_p2pkh(peer->closing.their_script) |
|
||||
&& !is_p2sh(peer->closing.their_script) |
|
||||
&& !is_p2wpkh(peer->closing.their_script) |
|
||||
&& !is_p2wsh(peer->closing.their_script)) { |
|
||||
log_broken_blob(peer->log, "Bad script_pubkey %s", |
|
||||
peer->closing.their_script, |
|
||||
tal_count(peer->closing.their_script)); |
|
||||
return pkt_err(peer, "Bad script_pubkey"); |
|
||||
} |
|
||||
|
|
||||
return NULL; |
|
||||
} |
|
@ -1,478 +0,0 @@ |
|||||
#include "chaintopology.h" |
|
||||
#include "db.h" |
|
||||
#include "failure.h" |
|
||||
#include "jsonrpc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "pay.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "routing.h" |
|
||||
#include "sphinx.h" |
|
||||
#include <bitcoin/preimage.h> |
|
||||
#include <ccan/str/hex/hex.h> |
|
||||
#include <ccan/structeq/structeq.h> |
|
||||
#include <inttypes.h> |
|
||||
#include <sodium/randombytes.h> |
|
||||
|
|
||||
/* Outstanding "pay" commands. */ |
|
||||
struct pay_command { |
|
||||
struct list_node list; |
|
||||
struct sha256 rhash; |
|
||||
u64 msatoshi; |
|
||||
const struct pubkey *ids; |
|
||||
/* Set if this is in progress. */ |
|
||||
struct htlc *htlc; |
|
||||
/* Preimage if this succeeded. */ |
|
||||
const struct preimage *rval; |
|
||||
struct command *cmd; |
|
||||
}; |
|
||||
static void json_pay_success(struct command *cmd, const struct preimage *rval) |
|
||||
{ |
|
||||
struct json_result *response; |
|
||||
|
|
||||
response = new_json_result(cmd); |
|
||||
json_object_start(response, NULL); |
|
||||
json_add_hex(response, "preimage", rval, sizeof(*rval)); |
|
||||
json_object_end(response); |
|
||||
command_success(cmd, response); |
|
||||
} |
|
||||
|
|
||||
static void handle_json(struct command *cmd, const struct htlc *htlc, |
|
||||
const FailInfo *f) |
|
||||
{ |
|
||||
struct pubkey id; |
|
||||
const char *idstr = "INVALID"; |
|
||||
|
|
||||
if (htlc->r) { |
|
||||
json_pay_success(cmd, htlc->r); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!f) { |
|
||||
command_fail(cmd, "failed (bad message)"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (proto_to_pubkey(f->id, &id)) |
|
||||
idstr = pubkey_to_hexstr(cmd, &id); |
|
||||
|
|
||||
command_fail(cmd, |
|
||||
"failed: error code %u node %s reason %s", |
|
||||
f->error_code, idstr, f->reason ? f->reason : "unknown"); |
|
||||
} |
|
||||
|
|
||||
static void check_routing_failure(struct lightningd_state *dstate, |
|
||||
const struct pay_command *pc, |
|
||||
const FailInfo *f) |
|
||||
{ |
|
||||
size_t i; |
|
||||
struct pubkey id; |
|
||||
|
|
||||
if (!f) |
|
||||
return; |
|
||||
|
|
||||
/* FIXME: We remove route on *any* failure. */ |
|
||||
log_debug(dstate->base_log, "Seeking route for fail code %u", |
|
||||
f->error_code); |
|
||||
if (!proto_to_pubkey(f->id, &id)) { |
|
||||
log_add(dstate->base_log, " - bad node"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
log_add_struct(dstate->base_log, " node %s", struct pubkey, &id); |
|
||||
|
|
||||
/* Don't remove route if it's last node (obviously) */ |
|
||||
for (i = 0; i+1 < tal_count(pc->ids); i++) { |
|
||||
if (structeq(&pc->ids[i], &id)) { |
|
||||
remove_connection(dstate->rstate, &pc->ids[i], &pc->ids[i+1]); |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (structeq(&pc->ids[i], &id)) |
|
||||
log_debug(dstate->base_log, "Final node: ignoring"); |
|
||||
else |
|
||||
log_debug(dstate->base_log, "Node not on route: ignoring"); |
|
||||
} |
|
||||
|
|
||||
void complete_pay_command(struct lightningd_state *dstate, |
|
||||
const struct htlc *htlc) |
|
||||
{ |
|
||||
struct pay_command *i; |
|
||||
|
|
||||
list_for_each(&dstate->pay_commands, i, list) { |
|
||||
if (i->htlc == htlc) { |
|
||||
FailInfo *f = NULL; |
|
||||
|
|
||||
db_complete_pay_command(dstate, htlc); |
|
||||
|
|
||||
if (htlc->r) |
|
||||
i->rval = tal_dup(i, struct preimage, htlc->r); |
|
||||
else { |
|
||||
f = failinfo_unwrap(i->cmd, htlc->fail, |
|
||||
tal_count(htlc->fail)); |
|
||||
check_routing_failure(dstate, i, f); |
|
||||
} |
|
||||
|
|
||||
/* No longer connected to live HTLC. */ |
|
||||
i->htlc = NULL; |
|
||||
|
|
||||
/* Can be NULL if JSON RPC goes away. */ |
|
||||
if (i->cmd) |
|
||||
handle_json(i->cmd, htlc, f); |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Can happen with testing low-level commands. */ |
|
||||
log_unusual(dstate->base_log, "No command for HTLC %"PRIu64" %s", |
|
||||
htlc->id, htlc->r ? "fulfill" : "fail"); |
|
||||
} |
|
||||
|
|
||||
/* When JSON RPC goes away, cmd is freed: detach from any running paycommand */ |
|
||||
static void remove_cmd_from_pc(struct command *cmd) |
|
||||
{ |
|
||||
struct pay_command *pc; |
|
||||
|
|
||||
list_for_each(&cmd->dstate->pay_commands, pc, list) { |
|
||||
if (pc->cmd == cmd) { |
|
||||
pc->cmd = NULL; |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
/* We can reach here, in the case where another pay command
|
|
||||
* re-uses the pc->cmd before we get around to cleaning up. */ |
|
||||
} |
|
||||
|
|
||||
static struct pay_command *find_pay_command(struct lightningd_state *dstate, |
|
||||
const struct sha256 *rhash) |
|
||||
{ |
|
||||
struct pay_command *pc; |
|
||||
|
|
||||
list_for_each(&dstate->pay_commands, pc, list) { |
|
||||
if (structeq(rhash, &pc->rhash)) |
|
||||
return pc; |
|
||||
} |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
/* For database restore. */ |
|
||||
bool pay_add(struct lightningd_state *dstate, |
|
||||
const struct sha256 *rhash, |
|
||||
u64 msatoshi, |
|
||||
const struct pubkey *ids, |
|
||||
struct htlc *htlc, |
|
||||
const u8 *fail UNNEEDED, |
|
||||
const struct preimage *r) |
|
||||
{ |
|
||||
struct pay_command *pc; |
|
||||
|
|
||||
if (find_pay_command(dstate, rhash)) |
|
||||
return false; |
|
||||
|
|
||||
pc = tal(dstate, struct pay_command); |
|
||||
pc->rhash = *rhash; |
|
||||
pc->msatoshi = msatoshi; |
|
||||
pc->ids = tal_dup_arr(pc, struct pubkey, ids, tal_count(ids), 0); |
|
||||
pc->htlc = htlc; |
|
||||
if (r) |
|
||||
pc->rval = tal_dup(pc, struct preimage, r); |
|
||||
else |
|
||||
pc->rval = NULL; |
|
||||
pc->cmd = NULL; |
|
||||
|
|
||||
list_add_tail(&dstate->pay_commands, &pc->list); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static void json_add_route(struct json_result *response, |
|
||||
const struct pubkey *id, |
|
||||
u64 amount, unsigned int delay) |
|
||||
{ |
|
||||
json_object_start(response, NULL); |
|
||||
json_add_pubkey(response, "id", id); |
|
||||
json_add_u64(response, "msatoshi", amount); |
|
||||
json_add_num(response, "delay", delay); |
|
||||
json_object_end(response); |
|
||||
} |
|
||||
|
|
||||
static void json_getroute(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
struct pubkey id; |
|
||||
jsmntok_t *idtok, *msatoshitok, *riskfactortok; |
|
||||
struct json_result *response; |
|
||||
size_t i; |
|
||||
u64 msatoshi; |
|
||||
double riskfactor; |
|
||||
|
|
||||
if (!json_get_params(buffer, params, |
|
||||
"id", &idtok, |
|
||||
"msatoshi", &msatoshitok, |
|
||||
"riskfactor", &riskfactortok, |
|
||||
NULL)) { |
|
||||
command_fail(cmd, "Need id, msatoshi and riskfactor"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!pubkey_from_hexstr(buffer + idtok->start, |
|
||||
idtok->end - idtok->start, &id)) { |
|
||||
command_fail(cmd, "Invalid id"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!json_tok_u64(buffer, msatoshitok, &msatoshi)) { |
|
||||
command_fail(cmd, "'%.*s' is not a valid number", |
|
||||
(int)(msatoshitok->end - msatoshitok->start), |
|
||||
buffer + msatoshitok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!json_tok_double(buffer, riskfactortok, &riskfactor)) { |
|
||||
command_fail(cmd, "'%.*s' is not a valid double", |
|
||||
(int)(riskfactortok->end - riskfactortok->start), |
|
||||
buffer + riskfactortok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
struct route_hop *hops = get_route(cmd, cmd->dstate->rstate, &cmd->dstate->id, &id, msatoshi, riskfactor); |
|
||||
|
|
||||
if (!hops) { |
|
||||
command_fail(cmd, "no route found"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
response = new_json_result(cmd); |
|
||||
json_object_start(response, NULL); |
|
||||
json_array_start(response, "route"); |
|
||||
for (i = 0; i < tal_count(hops); i++) |
|
||||
json_add_route(response, |
|
||||
&hops[i].nodeid, hops[i].amount, hops[i].delay); |
|
||||
json_array_end(response); |
|
||||
json_object_end(response); |
|
||||
command_success(cmd, response); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command getroute_command = { |
|
||||
"getroute", |
|
||||
json_getroute, |
|
||||
"Return route to {id} for {msatoshi}, using {riskfactor}", |
|
||||
"Returns a {route} array of {id} {msatoshi} {delay}: msatoshi and delay (in blocks) is cumulative." |
|
||||
}; |
|
||||
AUTODATA(json_command, &getroute_command); |
|
||||
|
|
||||
static void json_sendpay(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
struct pubkey *ids; |
|
||||
jsmntok_t *routetok, *rhashtok; |
|
||||
const jsmntok_t *t, *end; |
|
||||
unsigned int delay; |
|
||||
size_t n_hops; |
|
||||
struct sha256 rhash; |
|
||||
struct peer *peer; |
|
||||
struct pay_command *pc; |
|
||||
bool replacing = false; |
|
||||
const u8 *onion; |
|
||||
u8 sessionkey[32]; |
|
||||
enum fail_error error_code; |
|
||||
const char *err; |
|
||||
struct hoppayload *hoppayloads; |
|
||||
u64 amount, lastamount; |
|
||||
struct onionpacket *packet; |
|
||||
|
|
||||
if (!json_get_params(buffer, params, |
|
||||
"route", &routetok, |
|
||||
"rhash", &rhashtok, |
|
||||
NULL)) { |
|
||||
command_fail(cmd, "Need route and rhash"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!hex_decode(buffer + rhashtok->start, |
|
||||
rhashtok->end - rhashtok->start, |
|
||||
&rhash, sizeof(rhash))) { |
|
||||
command_fail(cmd, "'%.*s' is not a valid sha256 hash", |
|
||||
(int)(rhashtok->end - rhashtok->start), |
|
||||
buffer + rhashtok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (routetok->type != JSMN_ARRAY) { |
|
||||
command_fail(cmd, "'%.*s' is not an array", |
|
||||
(int)(routetok->end - routetok->start), |
|
||||
buffer + routetok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
end = json_next(routetok); |
|
||||
n_hops = 0; |
|
||||
ids = tal_arr(cmd, struct pubkey, n_hops); |
|
||||
hoppayloads = tal_arr(cmd, struct hoppayload, 0); |
|
||||
for (t = routetok + 1; t < end; t = json_next(t)) { |
|
||||
const jsmntok_t *amttok, *idtok, *delaytok; |
|
||||
|
|
||||
if (t->type != JSMN_OBJECT) { |
|
||||
command_fail(cmd, "route %zu '%.*s' is not an object", |
|
||||
n_hops, |
|
||||
(int)(t->end - t->start), |
|
||||
buffer + t->start); |
|
||||
return; |
|
||||
} |
|
||||
amttok = json_get_member(buffer, t, "msatoshi"); |
|
||||
idtok = json_get_member(buffer, t, "id"); |
|
||||
delaytok = json_get_member(buffer, t, "delay"); |
|
||||
if (!amttok || !idtok || !delaytok) { |
|
||||
command_fail(cmd, "route %zu needs msatoshi/id/delay", |
|
||||
n_hops); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (n_hops == 0) { |
|
||||
/* What we will send */ |
|
||||
if (!json_tok_u64(buffer, amttok, &amount)) { |
|
||||
command_fail(cmd, "route %zu invalid msatoshi", n_hops); |
|
||||
return; |
|
||||
} |
|
||||
lastamount = amount; |
|
||||
} else{ |
|
||||
/* What that hop will forward */ |
|
||||
tal_resize(&hoppayloads, n_hops); |
|
||||
memset(&hoppayloads[n_hops-1], 0, sizeof(struct hoppayload)); |
|
||||
if (!json_tok_u64(buffer, amttok, &hoppayloads[n_hops-1].amt_to_forward)) { |
|
||||
command_fail(cmd, "route %zu invalid msatoshi", n_hops); |
|
||||
return; |
|
||||
} |
|
||||
/* FIXME: Populate outgoing_cltv_value */ |
|
||||
lastamount = hoppayloads[n_hops-1].amt_to_forward; |
|
||||
} |
|
||||
|
|
||||
tal_resize(&ids, n_hops+1); |
|
||||
memset(&ids[n_hops], 0, sizeof(ids[n_hops])); |
|
||||
if (!pubkey_from_hexstr(buffer + idtok->start, |
|
||||
idtok->end - idtok->start, |
|
||||
&ids[n_hops])) { |
|
||||
command_fail(cmd, "route %zu invalid id", n_hops); |
|
||||
return; |
|
||||
} |
|
||||
/* Only need first delay. */ |
|
||||
if (n_hops == 0 && !json_tok_number(buffer, delaytok, &delay)) { |
|
||||
command_fail(cmd, "route %zu invalid delay", n_hops); |
|
||||
return; |
|
||||
} |
|
||||
n_hops++; |
|
||||
} |
|
||||
|
|
||||
/* Add payload for final hop */ |
|
||||
tal_resize(&hoppayloads, n_hops); |
|
||||
memset(&hoppayloads[n_hops-1], 0, sizeof(struct hoppayload)); |
|
||||
|
|
||||
if (n_hops == 0) { |
|
||||
command_fail(cmd, "Empty route"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
pc = find_pay_command(cmd->dstate, &rhash); |
|
||||
if (pc) { |
|
||||
replacing = true; |
|
||||
log_debug(cmd->dstate->base_log, "json_sendpay: found previous"); |
|
||||
if (pc->htlc) { |
|
||||
log_add(cmd->dstate->base_log, "... still in progress"); |
|
||||
command_fail(cmd, "still in progress"); |
|
||||
return; |
|
||||
} |
|
||||
if (pc->rval) { |
|
||||
size_t old_nhops = tal_count(pc->ids); |
|
||||
log_add(cmd->dstate->base_log, "... succeeded"); |
|
||||
/* Must match successful payment parameters. */ |
|
||||
if (pc->msatoshi != lastamount) { |
|
||||
command_fail(cmd, |
|
||||
"already succeeded with amount %" |
|
||||
PRIu64, pc->msatoshi); |
|
||||
return; |
|
||||
} |
|
||||
if (!structeq(&pc->ids[old_nhops-1], &ids[n_hops-1])) { |
|
||||
char *previd; |
|
||||
previd = pubkey_to_hexstr(cmd, |
|
||||
&pc->ids[old_nhops-1]); |
|
||||
command_fail(cmd, |
|
||||
"already succeeded to %s", |
|
||||
previd); |
|
||||
return; |
|
||||
} |
|
||||
json_pay_success(cmd, pc->rval); |
|
||||
return; |
|
||||
} |
|
||||
log_add(cmd->dstate->base_log, "... retrying"); |
|
||||
} |
|
||||
|
|
||||
peer = find_peer(cmd->dstate, &ids[0]); |
|
||||
if (!peer) { |
|
||||
command_fail(cmd, "no connection to first peer found"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
randombytes_buf(&sessionkey, sizeof(sessionkey)); |
|
||||
|
|
||||
/* Onion will carry us from first peer onwards. */ |
|
||||
packet = create_onionpacket(cmd, ids, hoppayloads, sessionkey, |
|
||||
rhash.u.u8, sizeof(struct sha256)); |
|
||||
onion = serialize_onionpacket(cmd, packet); |
|
||||
|
|
||||
if (pc) |
|
||||
pc->ids = tal_free(pc->ids); |
|
||||
else |
|
||||
pc = tal(cmd->dstate, struct pay_command); |
|
||||
pc->cmd = cmd; |
|
||||
pc->rhash = rhash; |
|
||||
pc->rval = NULL; |
|
||||
pc->ids = tal_steal(pc, ids); |
|
||||
pc->msatoshi = lastamount; |
|
||||
|
|
||||
/* Expiry for HTLCs is absolute. And add one to give some margin. */ |
|
||||
err = command_htlc_add(peer, amount, |
|
||||
delay + get_block_height(cmd->dstate->topology) |
|
||||
+ 1, |
|
||||
&rhash, NULL, |
|
||||
onion, &error_code, &pc->htlc); |
|
||||
if (err) { |
|
||||
command_fail(cmd, "could not add htlc: %u: %s", error_code, err); |
|
||||
tal_free(pc); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (replacing) { |
|
||||
if (!db_replace_pay_command(cmd->dstate, &pc->rhash, |
|
||||
pc->ids, pc->msatoshi, |
|
||||
pc->htlc)) { |
|
||||
command_fail(cmd, "database error"); |
|
||||
/* We could reconnect, but db error is *bad*. */ |
|
||||
peer_fail(peer, __func__); |
|
||||
tal_free(pc); |
|
||||
return; |
|
||||
} |
|
||||
} else { |
|
||||
if (!db_new_pay_command(cmd->dstate, &pc->rhash, |
|
||||
pc->ids, pc->msatoshi, |
|
||||
pc->htlc)) { |
|
||||
command_fail(cmd, "database error"); |
|
||||
/* We could reconnect, but db error is *bad*. */ |
|
||||
peer_fail(peer, __func__); |
|
||||
tal_free(pc); |
|
||||
return; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Wait until we get response. */ |
|
||||
list_add_tail(&cmd->dstate->pay_commands, &pc->list); |
|
||||
tal_add_destructor(cmd, remove_cmd_from_pc); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command sendpay_command = { |
|
||||
"sendpay", |
|
||||
json_sendpay, |
|
||||
"Send along {route} in return for preimage of {rhash}", |
|
||||
"Returns the {preimage} on success" |
|
||||
}; |
|
||||
AUTODATA(json_command, &sendpay_command); |
|
@ -1,19 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_PAY_H |
|
||||
#define LIGHTNING_DAEMON_PAY_H |
|
||||
#include "config.h" |
|
||||
|
|
||||
struct htlc; |
|
||||
struct lightningd_state; |
|
||||
struct preimage; |
|
||||
|
|
||||
void complete_pay_command(struct lightningd_state *dstate, |
|
||||
const struct htlc *htlc); |
|
||||
|
|
||||
bool pay_add(struct lightningd_state *dstate, |
|
||||
const struct sha256 *rhash, |
|
||||
u64 msatoshi, |
|
||||
const struct pubkey *ids, |
|
||||
struct htlc *htlc, |
|
||||
const u8 *fail, |
|
||||
const struct preimage *r); |
|
||||
#endif /* LIGHTNING_DAEMON_PAY_H */ |
|
File diff suppressed because it is too large
@ -1,91 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_PEER_H |
|
||||
#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" |
|
||||
#include "channel.h" |
|
||||
#include "failure.h" |
|
||||
#include "feechange.h" |
|
||||
#include "htlc.h" |
|
||||
#include "lightning.pb-c.h" |
|
||||
#include "netaddr.h" |
|
||||
#include "protobuf_convert.h" |
|
||||
#include "state.h" |
|
||||
#include "wire/gen_peer_wire.h" |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/crypto/shachain/shachain.h> |
|
||||
#include <ccan/list/list.h> |
|
||||
#include <ccan/time/time.h> |
|
||||
|
|
||||
struct log; |
|
||||
struct lightningd_state; |
|
||||
struct peer; |
|
||||
|
|
||||
/* Mapping for id -> network address. */ |
|
||||
struct peer_address { |
|
||||
struct list_node list; |
|
||||
struct pubkey id; |
|
||||
struct netaddr addr; |
|
||||
}; |
|
||||
|
|
||||
void setup_listeners(struct lightningd_state *dstate); |
|
||||
|
|
||||
void peer_debug(struct peer *peer, const char *fmt, ...) |
|
||||
PRINTF_FMT(2,3); |
|
||||
|
|
||||
struct peer *find_peer(struct lightningd_state *dstate, const struct pubkey *id); |
|
||||
struct peer *find_peer_by_pkhash(struct lightningd_state *dstate, const u8 *pkhash); |
|
||||
|
|
||||
struct peer *new_peer(struct lightningd_state *dstate, |
|
||||
struct log *log, |
|
||||
enum state state, |
|
||||
bool offer_anchor); |
|
||||
|
|
||||
/* Populates very first peer->{local,remote}.commit->{tx,cstate} */ |
|
||||
bool setup_first_commit(struct peer *peer); |
|
||||
|
|
||||
/* Whenever we send a signature, remember the txid -> commit_num mapping */ |
|
||||
void peer_add_their_commit(struct peer *peer, |
|
||||
const struct sha256_double *txid, u64 commit_num); |
|
||||
|
|
||||
/* Allocate a new commit_info struct. */ |
|
||||
struct commit_info *new_commit_info(const tal_t *ctx, u64 commit_num); |
|
||||
|
|
||||
/* Freeing removes from map, too */ |
|
||||
struct htlc *peer_new_htlc(struct peer *peer, |
|
||||
u64 id, |
|
||||
u64 msatoshi, |
|
||||
const struct sha256 *rhash, |
|
||||
u32 expiry, |
|
||||
const u8 *route, |
|
||||
size_t route_len, |
|
||||
struct htlc *src, |
|
||||
enum htlc_state state); |
|
||||
|
|
||||
const char *command_htlc_add(struct peer *peer, u64 msatoshi, |
|
||||
unsigned int expiry, |
|
||||
const struct sha256 *rhash, |
|
||||
struct htlc *src, |
|
||||
const u8 *route, |
|
||||
enum fail_error *error_code, |
|
||||
struct htlc **htlc); |
|
||||
|
|
||||
/* Peer has an issue, breakdown and fail. */ |
|
||||
void peer_fail(struct peer *peer, const char *caller); |
|
||||
|
|
||||
void peer_watch_anchor(struct peer *peer, int depth); |
|
||||
|
|
||||
struct bitcoin_tx *peer_create_close_tx(const tal_t *ctx, |
|
||||
struct peer *peer, u64 fee); |
|
||||
|
|
||||
u32 get_peer_min_block(struct lightningd_state *dstate); |
|
||||
|
|
||||
void debug_dump_peers(struct lightningd_state *dstate); |
|
||||
|
|
||||
void reconnect_peers(struct lightningd_state *dstate); |
|
||||
void rebroadcast_anchors(struct lightningd_state *dstate); |
|
||||
void cleanup_peers(struct lightningd_state *dstate); |
|
||||
#endif /* LIGHTNING_DAEMON_PEER_H */ |
|
@ -1,210 +0,0 @@ |
|||||
/* This header holds structure definitions for struct peer, which must
|
|
||||
* not be exposed to ../lightningd/ */ |
|
||||
#ifndef LIGHTNING_DAEMON_PEER_INTERNAL_H |
|
||||
#define LIGHTNING_DAEMON_PEER_INTERNAL_H |
|
||||
#include "config.h" |
|
||||
|
|
||||
struct anchor_input { |
|
||||
struct sha256_double txid; |
|
||||
unsigned int index; |
|
||||
/* Amount of input (satoshis), and output (satoshis) */ |
|
||||
u64 in_amount, out_amount; |
|
||||
/* Wallet entry to use to spend. */ |
|
||||
struct pubkey walletkey; |
|
||||
}; |
|
||||
|
|
||||
/* Information we remember for their commitment txs which we signed.
|
|
||||
* |
|
||||
* Given the commit_num, we can use shachain to derive the revocation preimage |
|
||||
* (if we've received it yet: we might have not, for the last). |
|
||||
*/ |
|
||||
struct their_commit { |
|
||||
struct list_node list; |
|
||||
|
|
||||
struct sha256_double txid; |
|
||||
u64 commit_num; |
|
||||
}; |
|
||||
|
|
||||
struct commit_info { |
|
||||
/* Commit number (0 == from open) */ |
|
||||
u64 commit_num; |
|
||||
/* Revocation hash. */ |
|
||||
struct sha256 revocation_hash; |
|
||||
/* Commit tx & txid */ |
|
||||
struct bitcoin_tx *tx; |
|
||||
struct sha256_double txid; |
|
||||
/* Channel state for this tx. */ |
|
||||
struct channel_state *cstate; |
|
||||
/* Other side's signature for last commit tx (if known) */ |
|
||||
secp256k1_ecdsa_signature *sig; |
|
||||
/* Order which commit was sent (theirs) / revocation was sent (ours) */ |
|
||||
s64 order; |
|
||||
}; |
|
||||
|
|
||||
struct peer_visible_state { |
|
||||
/* Is this side funding the channel? */ |
|
||||
bool offer_anchor; |
|
||||
/* Key for commitment tx inputs, then key for commitment tx outputs */ |
|
||||
struct pubkey commitkey, finalkey; |
|
||||
/* How long to they want the other's outputs locked (blocks) */ |
|
||||
struct rel_locktime locktime; |
|
||||
/* Minimum depth of anchor before channel usable. */ |
|
||||
unsigned int mindepth; |
|
||||
/* Commitment fee they're offering (satoshi). */ |
|
||||
u64 commit_fee_rate; |
|
||||
/* Revocation hash for next commit tx. */ |
|
||||
struct sha256 next_revocation_hash; |
|
||||
/* Commit txs: last one is current. */ |
|
||||
struct commit_info *commit; |
|
||||
|
|
||||
/* cstate to generate next commitment tx. */ |
|
||||
struct channel_state *staging_cstate; |
|
||||
}; |
|
||||
|
|
||||
struct peer { |
|
||||
/* dstate->peers list */ |
|
||||
struct list_node list; |
|
||||
|
|
||||
/* State in state machine. */ |
|
||||
enum state state; |
|
||||
|
|
||||
/* Network connection. */ |
|
||||
struct io_conn *conn; |
|
||||
|
|
||||
/* Are we connected now? (Crypto handshake completed). */ |
|
||||
bool connected; |
|
||||
|
|
||||
/* If we're doing an open, this is the command which triggered it */ |
|
||||
struct command *open_jsoncmd; |
|
||||
|
|
||||
/* If we're doing a commit, this is the command which triggered it */ |
|
||||
struct command *commit_jsoncmd; |
|
||||
|
|
||||
/* Global state. */ |
|
||||
struct lightningd_state *dstate; |
|
||||
|
|
||||
/* Their ID. */ |
|
||||
struct pubkey *id; |
|
||||
|
|
||||
/* Order counter for transmission of revocations/commitments. */ |
|
||||
s64 order_counter; |
|
||||
|
|
||||
/* Current received packet. */ |
|
||||
Pkt *inpkt; |
|
||||
|
|
||||
/* Queue of output packets. */ |
|
||||
Pkt **outpkt; |
|
||||
|
|
||||
/* Their commitments we have signed (which could appear on chain). */ |
|
||||
struct list_head their_commits; |
|
||||
|
|
||||
/* Number of commitment signatures we've received. */ |
|
||||
u64 their_commitsigs; |
|
||||
|
|
||||
/* Anchor tx output */ |
|
||||
struct { |
|
||||
struct sha256_double txid; |
|
||||
unsigned int index; |
|
||||
u64 satoshis; |
|
||||
u8 *witnessscript; |
|
||||
|
|
||||
/* Minimum possible depth for anchor */ |
|
||||
unsigned int min_depth; |
|
||||
|
|
||||
/* 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; |
|
||||
|
|
||||
/* Depth to trigger anchor if still opening, or -1. */ |
|
||||
int ok_depth; |
|
||||
|
|
||||
/* Did we create anchor? */ |
|
||||
bool ours; |
|
||||
} anchor; |
|
||||
|
|
||||
struct { |
|
||||
/* Their signature for our current commit sig. */ |
|
||||
secp256k1_ecdsa_signature theirsig; |
|
||||
/* The watch we have on a live commit tx. */ |
|
||||
struct txwatch *watch; |
|
||||
} cur_commit; |
|
||||
|
|
||||
/* Counter to make unique HTLC ids. */ |
|
||||
u64 htlc_id_counter; |
|
||||
|
|
||||
/* Mutual close info. */ |
|
||||
struct { |
|
||||
/* Our last suggested closing fee. */ |
|
||||
u64 our_fee; |
|
||||
/* If they've offered a signature, these are set: */ |
|
||||
secp256k1_ecdsa_signature *their_sig; |
|
||||
/* If their_sig is non-NULL, this is the fee. */ |
|
||||
u64 their_fee; |
|
||||
/* scriptPubKey we/they want for closing. */ |
|
||||
u8 *our_script, *their_script; |
|
||||
/* Last sent (in case we need to retransmit) */ |
|
||||
s64 shutdown_order, closing_order; |
|
||||
/* How many closing sigs have we receieved? */ |
|
||||
u32 sigs_in; |
|
||||
} closing; |
|
||||
|
|
||||
/* If we're closing on-chain */ |
|
||||
struct { |
|
||||
/* Everything (watches, resolved[], etc) tal'ed off this:
|
|
||||
* The commit which spends the anchor tx. */ |
|
||||
const struct bitcoin_tx *tx; |
|
||||
struct sha256_double txid; |
|
||||
|
|
||||
/* If >= 0, indicates which txout is to us and to them. */ |
|
||||
int to_us_idx, to_them_idx; |
|
||||
/* Maps what txouts are HTLCs (NULL implies to_us/them_idx). */ |
|
||||
struct htlc **htlcs; |
|
||||
/* Witness scripts for each output (where appropriate) */ |
|
||||
const u8 **wscripts; |
|
||||
/* The tx which resolves each txout. */ |
|
||||
const struct bitcoin_tx **resolved; |
|
||||
} onchain; |
|
||||
|
|
||||
/* All HTLCs. */ |
|
||||
struct htlc_map htlcs; |
|
||||
|
|
||||
/* We only track one feechange per state: last one counts. */ |
|
||||
struct feechange *feechanges[FEECHANGE_STATE_INVALID]; |
|
||||
|
|
||||
/* Current ongoing packetflow */ |
|
||||
struct io_data *io_data; |
|
||||
|
|
||||
/* What happened. */ |
|
||||
struct log *log; |
|
||||
|
|
||||
/* Things we're watching for (see watches.c) */ |
|
||||
struct list_head watches; |
|
||||
|
|
||||
/* Timeout for collecting changes before sending commit. */ |
|
||||
struct oneshot *commit_timer; |
|
||||
|
|
||||
/* Private keys for dealing with this peer. */ |
|
||||
struct peer_secrets *secrets; |
|
||||
|
|
||||
/* Our route connection to peer: NULL until we are in normal mode. */ |
|
||||
struct node_connection *nc; |
|
||||
|
|
||||
/* For testing. */ |
|
||||
bool fake_close; |
|
||||
bool output_enabled; |
|
||||
|
|
||||
/* Stuff we have in common. */ |
|
||||
struct peer_visible_state local, remote; |
|
||||
|
|
||||
/* If we have sent a new commit tx, but not received their revocation */ |
|
||||
struct sha256 *their_prev_revocation_hash; |
|
||||
|
|
||||
/* this is where we will store their revocation preimages*/ |
|
||||
struct shachain their_preimages; |
|
||||
|
|
||||
/* High water mark for the staggered broadcast */ |
|
||||
u64 broadcast_index; |
|
||||
}; |
|
||||
#endif /* LIGHTNING_DAEMON_PEER_INTERNAL_H */ |
|
@ -1,169 +0,0 @@ |
|||||
#include "jsonrpc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "routing.h" |
|
||||
|
|
||||
static void json_add_route(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
jsmntok_t *srctok, *dsttok, *basetok, *vartok, *delaytok, *minblockstok; |
|
||||
struct pubkey src, dst; |
|
||||
u32 base, var, delay, minblocks; |
|
||||
|
|
||||
if (!json_get_params(buffer, params, |
|
||||
"src", &srctok, |
|
||||
"dst", &dsttok, |
|
||||
"base", &basetok, |
|
||||
"var", &vartok, |
|
||||
"delay", &delaytok, |
|
||||
"minblocks", &minblockstok, |
|
||||
NULL)) { |
|
||||
command_fail(cmd, "Need src, dst, base, var, delay & minblocks"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!pubkey_from_hexstr(buffer + srctok->start, |
|
||||
srctok->end - srctok->start, &src)) { |
|
||||
command_fail(cmd, "src %.*s not valid", |
|
||||
srctok->end - srctok->start, |
|
||||
buffer + srctok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!pubkey_from_hexstr(buffer + dsttok->start, |
|
||||
dsttok->end - dsttok->start, &dst)) { |
|
||||
command_fail(cmd, "dst %.*s not valid", |
|
||||
dsttok->end - dsttok->start, |
|
||||
buffer + dsttok->start); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!json_tok_number(buffer, basetok, &base) |
|
||||
|| !json_tok_number(buffer, vartok, &var) |
|
||||
|| !json_tok_number(buffer, delaytok, &delay) |
|
||||
|| !json_tok_number(buffer, minblockstok, &minblocks)) { |
|
||||
command_fail(cmd, |
|
||||
"base, var, delay and minblocks must be numbers"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
add_connection(cmd->dstate->rstate, &src, &dst, base, var, delay, minblocks); |
|
||||
command_success(cmd, null_response(cmd)); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command dev_add_route_command = { |
|
||||
"dev-add-route", |
|
||||
json_add_route, |
|
||||
"Add route from {src} to {dst}, {base} rate in msatoshi, {var} rate in msatoshi, {delay} blocks delay and {minblocks} minimum timeout", |
|
||||
"Returns an empty result on success" |
|
||||
}; |
|
||||
AUTODATA(json_command, &dev_add_route_command); |
|
||||
|
|
||||
static void json_getchannels(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
struct json_result *response = new_json_result(cmd); |
|
||||
struct node_map_iter it; |
|
||||
struct node *n; |
|
||||
struct node_map *nodes = cmd->dstate->rstate->nodes; |
|
||||
struct node_connection *c; |
|
||||
int num_conn, i; |
|
||||
|
|
||||
json_object_start(response, NULL); |
|
||||
json_array_start(response, "channels"); |
|
||||
for (n = node_map_first(nodes, &it); n; n = node_map_next(nodes, &it)) { |
|
||||
num_conn = tal_count(n->out); |
|
||||
for (i = 0; i < num_conn; i++){ |
|
||||
c = n->out[i]; |
|
||||
json_object_start(response, NULL); |
|
||||
json_add_pubkey(response, "from", &n->id); |
|
||||
json_add_pubkey(response, "to", &c->dst->id); |
|
||||
json_add_num(response, "base_fee", c->base_fee); |
|
||||
json_add_num(response, "proportional_fee", c->proportional_fee); |
|
||||
json_add_num(response, "expiry", c->delay); |
|
||||
json_add_bool(response, "active", c->active); |
|
||||
json_object_end(response); |
|
||||
} |
|
||||
} |
|
||||
json_array_end(response); |
|
||||
json_object_end(response); |
|
||||
command_success(cmd, response); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command getchannels_command = { |
|
||||
"getchannels", |
|
||||
json_getchannels, |
|
||||
"List all known channels.", |
|
||||
"Returns a 'channels' array with all known channels including their fees." |
|
||||
}; |
|
||||
AUTODATA(json_command, &getchannels_command); |
|
||||
|
|
||||
static void json_routefail(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
jsmntok_t *enabletok; |
|
||||
bool enable; |
|
||||
|
|
||||
if (!json_get_params(buffer, params, |
|
||||
"enable", &enabletok, |
|
||||
NULL)) { |
|
||||
command_fail(cmd, "Need enable"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
if (!json_tok_bool(buffer, enabletok, &enable)) { |
|
||||
command_fail(cmd, "enable must be true or false"); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
log_debug(cmd->dstate->base_log, "dev-routefail: routefail %s", |
|
||||
enable ? "enabled" : "disabled"); |
|
||||
cmd->dstate->dev_never_routefail = !enable; |
|
||||
|
|
||||
command_success(cmd, null_response(cmd)); |
|
||||
} |
|
||||
static const struct json_command dev_routefail_command = { |
|
||||
"dev-routefail", |
|
||||
json_routefail, |
|
||||
"FAIL htlcs that we can't route if {enable}", |
|
||||
"Returns an empty result on success" |
|
||||
}; |
|
||||
AUTODATA(json_command, &dev_routefail_command); |
|
||||
|
|
||||
static void json_getnodes(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
struct json_result *response = new_json_result(cmd); |
|
||||
struct node *n; |
|
||||
struct node_map_iter i; |
|
||||
size_t j; |
|
||||
|
|
||||
n = node_map_first(cmd->dstate->rstate->nodes, &i); |
|
||||
|
|
||||
json_object_start(response, NULL); |
|
||||
json_array_start(response, "nodes"); |
|
||||
|
|
||||
while (n != NULL) { |
|
||||
json_object_start(response, NULL); |
|
||||
json_add_pubkey(response, "nodeid", &n->id); |
|
||||
json_array_start(response, "addresses"); |
|
||||
for (j=0; j<tal_count(n->addresses); j++) { |
|
||||
json_add_address(response, NULL, &n->addresses[j]); |
|
||||
} |
|
||||
json_array_end(response); |
|
||||
json_object_end(response); |
|
||||
n = node_map_next(cmd->dstate->rstate->nodes, &i); |
|
||||
} |
|
||||
|
|
||||
json_array_end(response); |
|
||||
json_object_end(response); |
|
||||
command_success(cmd, response); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command getnodes_command = { |
|
||||
"getnodes", |
|
||||
json_getnodes, |
|
||||
"List all known nodes in the network.", |
|
||||
"Returns a 'nodes' array" |
|
||||
}; |
|
||||
AUTODATA(json_command, &getnodes_command); |
|
@ -1,251 +0,0 @@ |
|||||
#include "bitcoin/privkey.h" |
|
||||
#include "bitcoin/shadouble.h" |
|
||||
#include "bitcoin/signature.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "peer.h" |
|
||||
#include "peer_internal.h" |
|
||||
#include "secrets.h" |
|
||||
#include "utils.h" |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/crypto/shachain/shachain.h> |
|
||||
#include <ccan/mem/mem.h> |
|
||||
#include <ccan/noerr/noerr.h> |
|
||||
#include <ccan/read_write_all/read_write_all.h> |
|
||||
#include <ccan/short_types/short_types.h> |
|
||||
#include <ccan/tal/str/str.h> |
|
||||
#include <errno.h> |
|
||||
#include <fcntl.h> |
|
||||
#include <secp256k1.h> |
|
||||
#include <sodium/randombytes.h> |
|
||||
#include <sys/stat.h> |
|
||||
#include <sys/types.h> |
|
||||
#include <unistd.h> |
|
||||
|
|
||||
void privkey_sign(struct lightningd_state *dstate, const void *src, size_t len, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
struct sha256_double h; |
|
||||
|
|
||||
sha256_double(&h, memcheck(src, len), len); |
|
||||
sign_hash(dstate->privkey, &h, sig); |
|
||||
} |
|
||||
|
|
||||
struct peer_secrets { |
|
||||
/* Two private keys, one for commit txs, one for final output. */ |
|
||||
struct privkey commit, final; |
|
||||
/* Seed from which we generate revocation hashes. */ |
|
||||
struct sha256 revocation_seed; |
|
||||
}; |
|
||||
|
|
||||
void peer_sign_theircommit(const struct peer *peer, |
|
||||
struct bitcoin_tx *commit, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Commit tx only has one input: that of the anchor. */ |
|
||||
sign_tx_input(commit, 0, |
|
||||
NULL, |
|
||||
peer->anchor.witnessscript, |
|
||||
&peer->secrets->commit, |
|
||||
&peer->local.commitkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_ourcommit(const struct peer *peer, |
|
||||
struct bitcoin_tx *commit, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Commit tx only has one input: that of the anchor. */ |
|
||||
sign_tx_input(commit, 0, |
|
||||
NULL, |
|
||||
peer->anchor.witnessscript, |
|
||||
&peer->secrets->commit, |
|
||||
&peer->local.commitkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_spend(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *commit_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Spend tx only has one input: that of the commit tx. */ |
|
||||
sign_tx_input(spend, 0, |
|
||||
NULL, |
|
||||
commit_witnessscript, |
|
||||
&peer->secrets->final, |
|
||||
&peer->local.finalkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_htlc_refund(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *htlc_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Spend tx only has one input: that of the commit tx. */ |
|
||||
sign_tx_input(spend, 0, |
|
||||
NULL, |
|
||||
htlc_witnessscript, |
|
||||
&peer->secrets->final, |
|
||||
&peer->local.finalkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_htlc_fulfill(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *htlc_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Spend tx only has one input: that of the commit tx. */ |
|
||||
sign_tx_input(spend, 0, |
|
||||
NULL, |
|
||||
htlc_witnessscript, |
|
||||
&peer->secrets->final, |
|
||||
&peer->local.finalkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_mutual_close(const struct peer *peer, |
|
||||
struct bitcoin_tx *close, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
sign_tx_input(close, 0, |
|
||||
NULL, |
|
||||
peer->anchor.witnessscript, |
|
||||
&peer->secrets->commit, |
|
||||
&peer->local.commitkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
void peer_sign_steal_input(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
size_t i, |
|
||||
const u8 *witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig) |
|
||||
{ |
|
||||
/* Spend tx only has one input: that of the commit tx. */ |
|
||||
sign_tx_input(spend, i, |
|
||||
NULL, |
|
||||
witnessscript, |
|
||||
&peer->secrets->final, |
|
||||
&peer->local.finalkey, |
|
||||
sig); |
|
||||
} |
|
||||
|
|
||||
static void new_keypair(struct lightningd_state *dstate, |
|
||||
struct privkey *privkey, struct pubkey *pubkey) |
|
||||
{ |
|
||||
do { |
|
||||
randombytes_buf(privkey->secret.data, |
|
||||
sizeof(privkey->secret.data)); |
|
||||
} while (!pubkey_from_privkey(privkey, pubkey)); |
|
||||
} |
|
||||
|
|
||||
void peer_secrets_init(struct peer *peer) |
|
||||
{ |
|
||||
peer->secrets = tal(peer, struct peer_secrets); |
|
||||
|
|
||||
new_keypair(peer->dstate, &peer->secrets->commit, &peer->local.commitkey); |
|
||||
new_keypair(peer->dstate, &peer->secrets->final, &peer->local.finalkey); |
|
||||
randombytes_buf(peer->secrets->revocation_seed.u.u8, sizeof(peer->secrets->revocation_seed.u.u8)); |
|
||||
} |
|
||||
|
|
||||
void peer_get_revocation_preimage(const struct peer *peer, u64 index, |
|
||||
struct sha256 *preimage) |
|
||||
{ |
|
||||
// generate hashes in reverse order, otherwise the first hash gives away everything
|
|
||||
shachain_from_seed(&peer->secrets->revocation_seed, 0xFFFFFFFFFFFFFFFFL - index, preimage); |
|
||||
} |
|
||||
|
|
||||
void peer_get_revocation_hash(const struct peer *peer, u64 index, |
|
||||
struct sha256 *rhash) |
|
||||
{ |
|
||||
struct sha256 preimage; |
|
||||
|
|
||||
peer_get_revocation_preimage(peer, index, &preimage); |
|
||||
sha256(rhash, preimage.u.u8, sizeof(preimage.u.u8)); |
|
||||
} |
|
||||
|
|
||||
const char *peer_secrets_for_db(const tal_t *ctx, struct peer *peer) |
|
||||
{ |
|
||||
const struct peer_secrets *ps = peer->secrets; |
|
||||
return tal_fmt(ctx, "x'%s', x'%s', x'%s'", |
|
||||
tal_hexstr(ctx, &ps->commit, sizeof(ps->commit)), |
|
||||
tal_hexstr(ctx, &ps->final, sizeof(ps->final)), |
|
||||
tal_hexstr(ctx, &ps->revocation_seed, |
|
||||
sizeof(ps->revocation_seed))); |
|
||||
} |
|
||||
|
|
||||
void peer_set_secrets_from_db(struct peer *peer, |
|
||||
const void *commit_privkey, |
|
||||
size_t commit_privkey_len, |
|
||||
const void *final_privkey, |
|
||||
size_t final_privkey_len, |
|
||||
const void *revocation_seed, |
|
||||
size_t revocation_seed_len) |
|
||||
{ |
|
||||
struct peer_secrets *ps = tal(peer, struct peer_secrets); |
|
||||
|
|
||||
assert(!peer->secrets); |
|
||||
peer->secrets = ps; |
|
||||
|
|
||||
if (commit_privkey_len != sizeof(ps->commit) |
|
||||
|| final_privkey_len != sizeof(ps->final) |
|
||||
|| revocation_seed_len != sizeof(ps->revocation_seed)) |
|
||||
fatal("peer_set_secrets_from_db: bad lengths %zu/%zu/%zu", |
|
||||
commit_privkey_len, final_privkey_len, |
|
||||
revocation_seed_len); |
|
||||
|
|
||||
memcpy(&ps->commit, commit_privkey, commit_privkey_len); |
|
||||
memcpy(&ps->final, final_privkey, final_privkey_len); |
|
||||
memcpy(&ps->revocation_seed, revocation_seed, revocation_seed_len); |
|
||||
|
|
||||
if (!pubkey_from_privkey(&ps->commit, &peer->local.commitkey)) |
|
||||
fatal("peer_set_secrets_from_db:bad commit privkey"); |
|
||||
if (!pubkey_from_privkey(&ps->final, &peer->local.finalkey)) |
|
||||
fatal("peer_set_secrets_from_db:bad final privkey"); |
|
||||
} |
|
||||
|
|
||||
void secrets_init(struct lightningd_state *dstate) |
|
||||
{ |
|
||||
int fd; |
|
||||
|
|
||||
dstate->privkey = tal(dstate, struct privkey); |
|
||||
|
|
||||
fd = open("privkey", O_RDONLY); |
|
||||
if (fd < 0) { |
|
||||
if (errno != ENOENT) |
|
||||
fatal("Failed to open privkey: %s", strerror(errno)); |
|
||||
|
|
||||
log_unusual(dstate->base_log, "Creating privkey file"); |
|
||||
new_keypair(dstate, dstate->privkey, &dstate->id); |
|
||||
|
|
||||
fd = open("privkey", O_CREAT|O_EXCL|O_WRONLY, 0400); |
|
||||
if (fd < 0) |
|
||||
fatal("Failed to create privkey file: %s", |
|
||||
strerror(errno)); |
|
||||
if (!write_all(fd, &dstate->privkey->secret, |
|
||||
sizeof(dstate->privkey->secret))) { |
|
||||
unlink_noerr("privkey"); |
|
||||
fatal("Failed to write to privkey file: %s", |
|
||||
strerror(errno)); |
|
||||
} |
|
||||
if (fsync(fd) != 0) |
|
||||
fatal("Failed to sync to privkey file: %s", |
|
||||
strerror(errno)); |
|
||||
close(fd); |
|
||||
|
|
||||
fd = open("privkey", O_RDONLY); |
|
||||
if (fd < 0) |
|
||||
fatal("Failed to reopen privkey: %s", strerror(errno)); |
|
||||
} |
|
||||
if (!read_all(fd, &dstate->privkey->secret, |
|
||||
sizeof(dstate->privkey->secret))) |
|
||||
fatal("Failed to read privkey: %s", strerror(errno)); |
|
||||
close(fd); |
|
||||
if (!pubkey_from_privkey(dstate->privkey, &dstate->id)) |
|
||||
fatal("Invalid privkey"); |
|
||||
|
|
||||
log_info_struct(dstate->base_log, "ID: %s", struct pubkey, &dstate->id); |
|
||||
} |
|
@ -1,68 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_SECRETS_H |
|
||||
#define LIGHTNING_DAEMON_SECRETS_H |
|
||||
/* Routines to handle private keys. */ |
|
||||
#include "config.h" |
|
||||
#include <ccan/short_types/short_types.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <secp256k1.h> |
|
||||
|
|
||||
struct peer; |
|
||||
struct lightningd_state; |
|
||||
struct sha256; |
|
||||
|
|
||||
void privkey_sign(struct lightningd_state *dstate, const void *src, size_t len, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_theircommit(const struct peer *peer, |
|
||||
struct bitcoin_tx *commit, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_ourcommit(const struct peer *peer, |
|
||||
struct bitcoin_tx *commit, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_spend(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *commit_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_htlc_refund(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *htlc_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_htlc_fulfill(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
const u8 *htlc_witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_mutual_close(const struct peer *peer, |
|
||||
struct bitcoin_tx *close, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
void peer_sign_steal_input(const struct peer *peer, |
|
||||
struct bitcoin_tx *spend, |
|
||||
size_t i, |
|
||||
const u8 *witnessscript, |
|
||||
secp256k1_ecdsa_signature *sig); |
|
||||
|
|
||||
const char *peer_secrets_for_db(const tal_t *ctx, struct peer *peer); |
|
||||
|
|
||||
void peer_set_secrets_from_db(struct peer *peer, |
|
||||
const void *commit_privkey, |
|
||||
size_t commit_privkey_len, |
|
||||
const void *final_privkey, |
|
||||
size_t final_privkey_len, |
|
||||
const void *revocation_seed, |
|
||||
size_t revocation_seed_len); |
|
||||
|
|
||||
void peer_secrets_init(struct peer *peer); |
|
||||
|
|
||||
void peer_get_revocation_hash(const struct peer *peer, u64 index, |
|
||||
struct sha256 *rhash); |
|
||||
void peer_get_revocation_preimage(const struct peer *peer, u64 index, |
|
||||
struct sha256 *preimage); |
|
||||
|
|
||||
void secrets_init(struct lightningd_state *dstate); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_SECRETS_H */ |
|
@ -1,479 +0,0 @@ |
|||||
#include "sphinx.h" |
|
||||
#include "utils.h" |
|
||||
#include <assert.h> |
|
||||
|
|
||||
#include <bitcoin/address.h> |
|
||||
|
|
||||
#include <ccan/crypto/ripemd160/ripemd160.h> |
|
||||
#include <ccan/crypto/sha256/sha256.h> |
|
||||
#include <ccan/mem/mem.h> |
|
||||
|
|
||||
#include <err.h> |
|
||||
|
|
||||
#include <secp256k1_ecdh.h> |
|
||||
|
|
||||
#include <sodium/crypto_auth_hmacsha256.h> |
|
||||
#include <sodium/crypto_stream_chacha20.h> |
|
||||
|
|
||||
#define BLINDING_FACTOR_SIZE 32 |
|
||||
#define SHARED_SECRET_SIZE 32 |
|
||||
#define NUM_STREAM_BYTES ((2 * NUM_MAX_HOPS + 2) * SECURITY_PARAMETER) |
|
||||
#define KEY_LEN 32 |
|
||||
|
|
||||
struct hop_params { |
|
||||
u8 secret[SHARED_SECRET_SIZE]; |
|
||||
u8 blind[BLINDING_FACTOR_SIZE]; |
|
||||
secp256k1_pubkey ephemeralkey; |
|
||||
}; |
|
||||
|
|
||||
struct keyset { |
|
||||
u8 pi[KEY_LEN]; |
|
||||
u8 mu[KEY_LEN]; |
|
||||
u8 rho[KEY_LEN]; |
|
||||
u8 gamma[KEY_LEN]; |
|
||||
}; |
|
||||
|
|
||||
/* Small helper to append data to a buffer and update the position
|
|
||||
* into the buffer |
|
||||
*/ |
|
||||
static void write_buffer(u8 *dst, const void *src, const size_t len, int *pos) |
|
||||
{ |
|
||||
memcpy(dst + *pos, src, len); |
|
||||
*pos += len; |
|
||||
} |
|
||||
|
|
||||
/* Read len bytes from the source at position pos into dst and update
|
|
||||
* the position pos accordingly. |
|
||||
*/ |
|
||||
static void read_buffer(void *dst, const u8 *src, const size_t len, int *pos) |
|
||||
{ |
|
||||
memcpy(dst, src + *pos, len); |
|
||||
*pos += len; |
|
||||
} |
|
||||
|
|
||||
u8 *serialize_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
const struct onionpacket *m) |
|
||||
{ |
|
||||
u8 *dst = tal_arr(ctx, u8, TOTAL_PACKET_SIZE); |
|
||||
|
|
||||
u8 der[33]; |
|
||||
size_t outputlen = 33; |
|
||||
int p = 0; |
|
||||
|
|
||||
secp256k1_ec_pubkey_serialize(secp256k1_ctx, |
|
||||
der, |
|
||||
&outputlen, |
|
||||
&m->ephemeralkey, |
|
||||
SECP256K1_EC_COMPRESSED); |
|
||||
|
|
||||
write_buffer(dst, &m->version, 1, &p); |
|
||||
write_buffer(dst, der, outputlen, &p); |
|
||||
write_buffer(dst, m->mac, sizeof(m->mac), &p); |
|
||||
write_buffer(dst, m->routinginfo, ROUTING_INFO_SIZE, &p); |
|
||||
write_buffer(dst, m->hoppayloads, TOTAL_HOP_PAYLOAD_SIZE, &p); |
|
||||
return dst; |
|
||||
} |
|
||||
|
|
||||
struct onionpacket *parse_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
const void *src, |
|
||||
const size_t srclen |
|
||||
) |
|
||||
{ |
|
||||
struct onionpacket *m; |
|
||||
int p = 0; |
|
||||
u8 rawEphemeralkey[33]; |
|
||||
|
|
||||
if (srclen != TOTAL_PACKET_SIZE) |
|
||||
return NULL; |
|
||||
|
|
||||
m = talz(ctx, struct onionpacket); |
|
||||
|
|
||||
read_buffer(&m->version, src, 1, &p); |
|
||||
if (m->version != 0x01) { |
|
||||
// FIXME add logging
|
|
||||
return tal_free(m); |
|
||||
} |
|
||||
read_buffer(rawEphemeralkey, src, 33, &p); |
|
||||
|
|
||||
if (secp256k1_ec_pubkey_parse(secp256k1_ctx, &m->ephemeralkey, rawEphemeralkey, 33) != 1) |
|
||||
return tal_free(m); |
|
||||
|
|
||||
read_buffer(&m->mac, src, 20, &p); |
|
||||
read_buffer(&m->routinginfo, src, ROUTING_INFO_SIZE, &p); |
|
||||
read_buffer(&m->hoppayloads, src, TOTAL_HOP_PAYLOAD_SIZE, &p); |
|
||||
return m; |
|
||||
} |
|
||||
|
|
||||
static struct hoppayload *parse_hoppayload(const tal_t *ctx, u8 *src) |
|
||||
{ |
|
||||
int p = 0; |
|
||||
struct hoppayload *result = talz(ctx, struct hoppayload); |
|
||||
|
|
||||
read_buffer(&result->realm, src, sizeof(result->realm), &p); |
|
||||
read_buffer(&result->amt_to_forward, |
|
||||
src, sizeof(result->amt_to_forward), &p); |
|
||||
read_buffer(&result->outgoing_cltv_value, |
|
||||
src, sizeof(result->outgoing_cltv_value), &p); |
|
||||
read_buffer(&result->unused_with_v0_version_on_header, |
|
||||
src, sizeof(result->unused_with_v0_version_on_header), &p); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
static void serialize_hoppayload(u8 *dst, struct hoppayload *hp) |
|
||||
{ |
|
||||
int p = 0; |
|
||||
|
|
||||
write_buffer(dst, &hp->realm, sizeof(hp->realm), &p); |
|
||||
write_buffer(dst, &hp->amt_to_forward, sizeof(hp->amt_to_forward), &p); |
|
||||
write_buffer(dst, &hp->outgoing_cltv_value, |
|
||||
sizeof(hp->outgoing_cltv_value), &p); |
|
||||
write_buffer(dst, &hp->unused_with_v0_version_on_header, |
|
||||
sizeof(hp->unused_with_v0_version_on_header), &p); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static void xorbytes(uint8_t *d, const uint8_t *a, const uint8_t *b, size_t len) |
|
||||
{ |
|
||||
size_t i; |
|
||||
|
|
||||
for (i = 0; i < len; i++) |
|
||||
d[i] = a[i] ^ b[i]; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* Generate a pseudo-random byte stream of length `dstlen` from key `k` and |
|
||||
* store it in `dst`. `dst must be at least `dstlen` bytes long. |
|
||||
*/ |
|
||||
static void generate_cipher_stream(void *dst, const u8 *k, size_t dstlen) |
|
||||
{ |
|
||||
u8 nonce[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
|
||||
|
|
||||
crypto_stream_chacha20(dst, dstlen, nonce, k); |
|
||||
} |
|
||||
|
|
||||
static bool compute_hmac( |
|
||||
void *dst, |
|
||||
const void *src, |
|
||||
size_t len, |
|
||||
const void *key, |
|
||||
size_t keylen) |
|
||||
{ |
|
||||
crypto_auth_hmacsha256_state state; |
|
||||
|
|
||||
crypto_auth_hmacsha256_init(&state, key, keylen); |
|
||||
crypto_auth_hmacsha256_update(&state, memcheck(src, len), len); |
|
||||
crypto_auth_hmacsha256_final(&state, dst); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static void compute_packet_hmac(const struct onionpacket *packet, |
|
||||
const u8 *assocdata, const size_t assocdatalen, |
|
||||
u8 *mukey, u8 *hmac) |
|
||||
{ |
|
||||
u8 mactemp[ROUTING_INFO_SIZE + TOTAL_HOP_PAYLOAD_SIZE + assocdatalen]; |
|
||||
u8 mac[32]; |
|
||||
int pos = 0; |
|
||||
|
|
||||
write_buffer(mactemp, packet->routinginfo, ROUTING_INFO_SIZE, &pos); |
|
||||
write_buffer(mactemp, packet->hoppayloads, TOTAL_HOP_PAYLOAD_SIZE, &pos); |
|
||||
write_buffer(mactemp, assocdata, assocdatalen, &pos); |
|
||||
|
|
||||
compute_hmac(mac, mactemp, sizeof(mactemp), mukey, KEY_LEN); |
|
||||
memcpy(hmac, mac, 20); |
|
||||
} |
|
||||
|
|
||||
static bool generate_key(void *k, const char *t, u8 tlen, const u8 *s) |
|
||||
{ |
|
||||
return compute_hmac(k, s, KEY_LEN, t, tlen); |
|
||||
} |
|
||||
|
|
||||
static bool generate_header_padding( |
|
||||
void *dst, size_t dstlen, |
|
||||
const size_t hopsize, |
|
||||
const char *keytype, |
|
||||
size_t keytypelen, |
|
||||
const u8 numhops, |
|
||||
struct hop_params *params |
|
||||
) |
|
||||
{ |
|
||||
int i; |
|
||||
u8 cipher_stream[(NUM_MAX_HOPS + 1) * hopsize]; |
|
||||
u8 key[KEY_LEN]; |
|
||||
|
|
||||
memset(dst, 0, dstlen); |
|
||||
for (i = 1; i < numhops; i++) { |
|
||||
if (!generate_key(&key, keytype, keytypelen, params[i - 1].secret)) |
|
||||
return false; |
|
||||
|
|
||||
generate_cipher_stream(cipher_stream, key, sizeof(cipher_stream)); |
|
||||
int pos = ((NUM_MAX_HOPS - i) + 1) * hopsize; |
|
||||
xorbytes(dst, dst, cipher_stream + pos, sizeof(cipher_stream) - pos); |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static void compute_blinding_factor(const secp256k1_pubkey *key, |
|
||||
const u8 sharedsecret[SHARED_SECRET_SIZE], |
|
||||
u8 res[BLINDING_FACTOR_SIZE]) |
|
||||
{ |
|
||||
struct sha256_ctx ctx; |
|
||||
u8 der[33]; |
|
||||
size_t outputlen = 33; |
|
||||
struct sha256 temp; |
|
||||
|
|
||||
secp256k1_ec_pubkey_serialize(secp256k1_ctx, der, &outputlen, key, |
|
||||
SECP256K1_EC_COMPRESSED); |
|
||||
sha256_init(&ctx); |
|
||||
sha256_update(&ctx, der, sizeof(der)); |
|
||||
sha256_update(&ctx, sharedsecret, SHARED_SECRET_SIZE); |
|
||||
sha256_done(&ctx, &temp); |
|
||||
memcpy(res, &temp, 32); |
|
||||
} |
|
||||
|
|
||||
static bool blind_group_element( |
|
||||
secp256k1_pubkey *blindedelement, |
|
||||
const secp256k1_pubkey *pubkey, |
|
||||
const u8 blind[BLINDING_FACTOR_SIZE]) |
|
||||
{ |
|
||||
/* tweak_mul is inplace so copy first. */ |
|
||||
if (pubkey != blindedelement) |
|
||||
*blindedelement = *pubkey; |
|
||||
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, blindedelement, blind) != 1) |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static bool create_shared_secret( |
|
||||
u8 *secret, |
|
||||
const secp256k1_pubkey *pubkey, |
|
||||
const u8 *sessionkey) |
|
||||
{ |
|
||||
|
|
||||
if (secp256k1_ecdh(secp256k1_ctx, secret, pubkey, sessionkey) != 1) |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
bool onion_shared_secret( |
|
||||
u8 *secret, |
|
||||
const struct onionpacket *packet, |
|
||||
const struct privkey *privkey) |
|
||||
{ |
|
||||
return create_shared_secret(secret, &packet->ephemeralkey, |
|
||||
privkey->secret.data); |
|
||||
} |
|
||||
|
|
||||
static void generate_key_set(const u8 secret[SHARED_SECRET_SIZE], |
|
||||
struct keyset *keys) |
|
||||
{ |
|
||||
generate_key(keys->rho, "rho", 3, secret); |
|
||||
generate_key(keys->pi, "pi", 2, secret); |
|
||||
generate_key(keys->mu, "mu", 2, secret); |
|
||||
generate_key(keys->gamma, "gamma", 5, secret); |
|
||||
} |
|
||||
|
|
||||
static struct hop_params *generate_hop_params( |
|
||||
const tal_t *ctx, |
|
||||
const u8 *sessionkey, |
|
||||
struct pubkey path[]) |
|
||||
{ |
|
||||
int i, j, num_hops = tal_count(path); |
|
||||
secp256k1_pubkey temp; |
|
||||
u8 blind[BLINDING_FACTOR_SIZE]; |
|
||||
struct hop_params *params = tal_arr(ctx, struct hop_params, num_hops); |
|
||||
|
|
||||
/* Initialize the first hop with the raw information */ |
|
||||
if (secp256k1_ec_pubkey_create( |
|
||||
secp256k1_ctx, ¶ms[0].ephemeralkey, sessionkey) != 1) |
|
||||
return NULL; |
|
||||
|
|
||||
if (!create_shared_secret( |
|
||||
params[0].secret, &path[0].pubkey, sessionkey)) |
|
||||
return NULL; |
|
||||
|
|
||||
compute_blinding_factor( |
|
||||
¶ms[0].ephemeralkey, params[0].secret, |
|
||||
params[0].blind); |
|
||||
|
|
||||
/* Recursively compute all following ephemeral public keys,
|
|
||||
* secrets and blinding factors |
|
||||
*/ |
|
||||
for (i = 1; i < num_hops; i++) { |
|
||||
if (!blind_group_element( |
|
||||
¶ms[i].ephemeralkey, |
|
||||
¶ms[i - 1].ephemeralkey, |
|
||||
params[i - 1].blind)) |
|
||||
return NULL; |
|
||||
|
|
||||
/* Blind this hop's point with all previous blinding factors
|
|
||||
* Order is indifferent, multiplication is commutative. |
|
||||
*/ |
|
||||
memcpy(&blind, sessionkey, 32); |
|
||||
temp = path[i].pubkey; |
|
||||
if (!blind_group_element(&temp, &temp, blind)) |
|
||||
return NULL; |
|
||||
for (j = 0; j < i; j++) |
|
||||
if (!blind_group_element( |
|
||||
&temp, |
|
||||
&temp, |
|
||||
params[j].blind)) |
|
||||
return NULL; |
|
||||
|
|
||||
/* Now hash temp and store it. This requires us to
|
|
||||
* DER-serialize first and then skip the sign byte. |
|
||||
*/ |
|
||||
u8 der[33]; |
|
||||
size_t outputlen = 33; |
|
||||
secp256k1_ec_pubkey_serialize( |
|
||||
secp256k1_ctx, der, &outputlen, &temp, |
|
||||
SECP256K1_EC_COMPRESSED); |
|
||||
struct sha256 h; |
|
||||
sha256(&h, der, sizeof(der)); |
|
||||
memcpy(¶ms[i].secret, &h, sizeof(h)); |
|
||||
|
|
||||
compute_blinding_factor( |
|
||||
¶ms[i].ephemeralkey, |
|
||||
params[i].secret, params[i].blind); |
|
||||
} |
|
||||
return params; |
|
||||
} |
|
||||
|
|
||||
struct onionpacket *create_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
struct pubkey *path, |
|
||||
struct hoppayload hoppayloads[], |
|
||||
const u8 *sessionkey, |
|
||||
const u8 *assocdata, |
|
||||
const size_t assocdatalen |
|
||||
) |
|
||||
{ |
|
||||
struct onionpacket *packet = talz(ctx, struct onionpacket); |
|
||||
int i, num_hops = tal_count(path); |
|
||||
u8 filler[2 * (num_hops - 1) * SECURITY_PARAMETER]; |
|
||||
u8 hopfiller[(num_hops - 1) * HOP_PAYLOAD_SIZE]; |
|
||||
struct keyset keys; |
|
||||
struct bitcoin_address nextaddr; |
|
||||
u8 nexthmac[SECURITY_PARAMETER]; |
|
||||
u8 stream[ROUTING_INFO_SIZE], hopstream[TOTAL_HOP_PAYLOAD_SIZE]; |
|
||||
struct hop_params *params = generate_hop_params(ctx, sessionkey, path); |
|
||||
u8 binhoppayloads[tal_count(path)][HOP_PAYLOAD_SIZE]; |
|
||||
|
|
||||
for (i = 0; i < num_hops; i++) |
|
||||
serialize_hoppayload(binhoppayloads[i], &hoppayloads[i]); |
|
||||
|
|
||||
if (!params) |
|
||||
return NULL; |
|
||||
packet->version = 1; |
|
||||
memset(&nextaddr, 0, 20); |
|
||||
memset(nexthmac, 0, 20); |
|
||||
memset(packet->routinginfo, 0, ROUTING_INFO_SIZE); |
|
||||
|
|
||||
generate_header_padding(filler, sizeof(filler), 2 * SECURITY_PARAMETER, |
|
||||
"rho", 3, num_hops, params); |
|
||||
generate_header_padding(hopfiller, sizeof(hopfiller), HOP_PAYLOAD_SIZE, |
|
||||
"gamma", 5, num_hops, params); |
|
||||
|
|
||||
for (i = num_hops - 1; i >= 0; i--) { |
|
||||
generate_key_set(params[i].secret, &keys); |
|
||||
generate_cipher_stream(stream, keys.rho, ROUTING_INFO_SIZE); |
|
||||
|
|
||||
/* Rightshift mix-header by 2*SECURITY_PARAMETER */ |
|
||||
memmove(packet->routinginfo + 2 * SECURITY_PARAMETER, packet->routinginfo, |
|
||||
ROUTING_INFO_SIZE - 2 * SECURITY_PARAMETER); |
|
||||
memcpy(packet->routinginfo, &nextaddr, SECURITY_PARAMETER); |
|
||||
memcpy(packet->routinginfo + SECURITY_PARAMETER, nexthmac, SECURITY_PARAMETER); |
|
||||
xorbytes(packet->routinginfo, packet->routinginfo, stream, ROUTING_INFO_SIZE); |
|
||||
|
|
||||
/* Rightshift hop-payloads and obfuscate */ |
|
||||
memmove(packet->hoppayloads + HOP_PAYLOAD_SIZE, packet->hoppayloads, |
|
||||
TOTAL_HOP_PAYLOAD_SIZE - HOP_PAYLOAD_SIZE); |
|
||||
memcpy(packet->hoppayloads, binhoppayloads[i], HOP_PAYLOAD_SIZE); |
|
||||
generate_cipher_stream(hopstream, keys.gamma, TOTAL_HOP_PAYLOAD_SIZE); |
|
||||
xorbytes(packet->hoppayloads, packet->hoppayloads, hopstream, |
|
||||
TOTAL_HOP_PAYLOAD_SIZE); |
|
||||
|
|
||||
if (i == num_hops - 1) { |
|
||||
size_t len = (NUM_MAX_HOPS - num_hops + 1) * 2 * SECURITY_PARAMETER; |
|
||||
memcpy(packet->routinginfo + len, filler, sizeof(filler)); |
|
||||
len = (NUM_MAX_HOPS - num_hops + 1) * HOP_PAYLOAD_SIZE; |
|
||||
memcpy(packet->hoppayloads + len, hopfiller, sizeof(hopfiller)); |
|
||||
} |
|
||||
|
|
||||
compute_packet_hmac(packet, assocdata, assocdatalen, keys.mu, |
|
||||
nexthmac); |
|
||||
pubkey_to_hash160(&path[i], &nextaddr.addr); |
|
||||
} |
|
||||
memcpy(packet->mac, nexthmac, sizeof(nexthmac)); |
|
||||
memcpy(&packet->ephemeralkey, ¶ms[0].ephemeralkey, sizeof(secp256k1_pubkey)); |
|
||||
return packet; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* Given a onionpacket msg extract the information for the current |
|
||||
* node and unwrap the remainder so that the node can forward it. |
|
||||
*/ |
|
||||
struct route_step *process_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
const struct onionpacket *msg, |
|
||||
const u8 *shared_secret, |
|
||||
const u8 *assocdata, |
|
||||
const size_t assocdatalen |
|
||||
) |
|
||||
{ |
|
||||
struct route_step *step = talz(ctx, struct route_step); |
|
||||
u8 hmac[20]; |
|
||||
struct keyset keys; |
|
||||
u8 paddedhoppayloads[TOTAL_HOP_PAYLOAD_SIZE + HOP_PAYLOAD_SIZE]; |
|
||||
u8 hopstream[TOTAL_HOP_PAYLOAD_SIZE + HOP_PAYLOAD_SIZE]; |
|
||||
u8 blind[BLINDING_FACTOR_SIZE]; |
|
||||
u8 stream[NUM_STREAM_BYTES]; |
|
||||
u8 paddedheader[ROUTING_INFO_SIZE + 2 * SECURITY_PARAMETER]; |
|
||||
|
|
||||
step->next = talz(step, struct onionpacket); |
|
||||
step->next->version = msg->version; |
|
||||
generate_key_set(shared_secret, &keys); |
|
||||
|
|
||||
compute_packet_hmac(msg, assocdata, assocdatalen, keys.mu, hmac); |
|
||||
|
|
||||
if (memcmp(msg->mac, hmac, sizeof(hmac)) != 0) { |
|
||||
warnx("Computed MAC does not match expected MAC, the message was modified."); |
|
||||
return tal_free(step); |
|
||||
} |
|
||||
|
|
||||
//FIXME:store seen secrets to avoid replay attacks
|
|
||||
generate_cipher_stream(stream, keys.rho, sizeof(stream)); |
|
||||
|
|
||||
memset(paddedheader, 0, sizeof(paddedheader)); |
|
||||
memcpy(paddedheader, msg->routinginfo, ROUTING_INFO_SIZE); |
|
||||
xorbytes(paddedheader, paddedheader, stream, sizeof(stream)); |
|
||||
|
|
||||
/* Extract the per-hop payload */ |
|
||||
generate_cipher_stream(hopstream, keys.gamma, sizeof(hopstream)); |
|
||||
|
|
||||
memset(paddedhoppayloads, 0, sizeof(paddedhoppayloads)); |
|
||||
memcpy(paddedhoppayloads, msg->hoppayloads, TOTAL_HOP_PAYLOAD_SIZE); |
|
||||
xorbytes(paddedhoppayloads, paddedhoppayloads, hopstream, sizeof(hopstream)); |
|
||||
step->hoppayload = parse_hoppayload(step, paddedhoppayloads); |
|
||||
memcpy(&step->next->hoppayloads, paddedhoppayloads + HOP_PAYLOAD_SIZE, |
|
||||
TOTAL_HOP_PAYLOAD_SIZE); |
|
||||
|
|
||||
compute_blinding_factor(&msg->ephemeralkey, shared_secret, blind); |
|
||||
if (!blind_group_element(&step->next->ephemeralkey, &msg->ephemeralkey, blind)) |
|
||||
return tal_free(step); |
|
||||
memcpy(&step->next->nexthop, paddedheader, SECURITY_PARAMETER); |
|
||||
memcpy(&step->next->mac, |
|
||||
paddedheader + SECURITY_PARAMETER, |
|
||||
SECURITY_PARAMETER); |
|
||||
|
|
||||
memcpy(&step->next->routinginfo, paddedheader + 2 * SECURITY_PARAMETER, ROUTING_INFO_SIZE); |
|
||||
|
|
||||
if (memeqzero(step->next->mac, sizeof(step->next->mac))) { |
|
||||
step->nextcase = ONION_END; |
|
||||
} else { |
|
||||
step->nextcase = ONION_FORWARD; |
|
||||
} |
|
||||
|
|
||||
return step; |
|
||||
} |
|
@ -1,136 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_SPHINX_H |
|
||||
#define LIGHTNING_DAEMON_SPHINX_H |
|
||||
|
|
||||
#include "config.h" |
|
||||
#include "bitcoin/privkey.h" |
|
||||
#include "bitcoin/pubkey.h" |
|
||||
|
|
||||
#include <ccan/short_types/short_types.h> |
|
||||
#include <ccan/tal/tal.h> |
|
||||
#include <secp256k1.h> |
|
||||
#include <sodium/randombytes.h> |
|
||||
|
|
||||
#define SECURITY_PARAMETER 20 |
|
||||
#define NUM_MAX_HOPS 20 |
|
||||
#define HOP_PAYLOAD_SIZE 20 |
|
||||
#define TOTAL_HOP_PAYLOAD_SIZE (NUM_MAX_HOPS * HOP_PAYLOAD_SIZE) |
|
||||
#define ROUTING_INFO_SIZE (2 * NUM_MAX_HOPS * SECURITY_PARAMETER) |
|
||||
#define TOTAL_PACKET_SIZE (1 + 33 + SECURITY_PARAMETER + ROUTING_INFO_SIZE + \ |
|
||||
TOTAL_HOP_PAYLOAD_SIZE) |
|
||||
|
|
||||
struct onionpacket { |
|
||||
/* Cleartext information */ |
|
||||
u8 version; |
|
||||
u8 nexthop[20]; |
|
||||
u8 mac[20]; |
|
||||
secp256k1_pubkey ephemeralkey; |
|
||||
|
|
||||
/* Encrypted information */ |
|
||||
u8 routinginfo[ROUTING_INFO_SIZE]; |
|
||||
u8 hoppayloads[TOTAL_HOP_PAYLOAD_SIZE]; |
|
||||
}; |
|
||||
|
|
||||
enum route_next_case { |
|
||||
ONION_END = 0, |
|
||||
ONION_FORWARD = 1, |
|
||||
}; |
|
||||
|
|
||||
/* FIXME-OLD #4:
|
|
||||
* |
|
||||
* The format of the per-hop-payload for a version 0 packet is as follows: |
|
||||
``` |
|
||||
+----------------+--------------------------+-------------------------------+--------------------------------------------+ |
|
||||
| realm (1 byte) | amt_to_forward (8 bytes) | outgoing_cltv_value (4 bytes) | unused_with_v0_version_on_header (7 bytes) | |
|
||||
+----------------+--------------------------+-------------------------------+--------------------------------------------+ |
|
||||
``` |
|
||||
*/ |
|
||||
struct hoppayload { |
|
||||
u8 realm; |
|
||||
u64 amt_to_forward; |
|
||||
u32 outgoing_cltv_value; |
|
||||
u8 unused_with_v0_version_on_header[7]; |
|
||||
}; |
|
||||
|
|
||||
struct route_step { |
|
||||
enum route_next_case nextcase; |
|
||||
struct onionpacket *next; |
|
||||
struct hoppayload *hoppayload; |
|
||||
}; |
|
||||
|
|
||||
/**
|
|
||||
* create_onionpacket - Create a new onionpacket that can be routed |
|
||||
* over a path of intermediate nodes. |
|
||||
* |
|
||||
* @ctx: tal context to allocate from |
|
||||
* @path: public keys of nodes along the path. |
|
||||
* @hoppayloads: payloads destined for individual hosts (limited to |
|
||||
* HOP_PAYLOAD_SIZE bytes) |
|
||||
* @num_hops: path length in nodes |
|
||||
* @sessionkey: 20 byte random session key to derive secrets from |
|
||||
* @assocdata: associated data to commit to in HMACs |
|
||||
* @assocdatalen: length of the assocdata |
|
||||
*/ |
|
||||
struct onionpacket *create_onionpacket( |
|
||||
const tal_t * ctx, |
|
||||
struct pubkey path[], |
|
||||
struct hoppayload hoppayloads[], |
|
||||
const u8 * sessionkey, |
|
||||
const u8 *assocdata, |
|
||||
const size_t assocdatalen |
|
||||
); |
|
||||
|
|
||||
/**
|
|
||||
* onion_shared_secret - calculate ECDH shared secret between nodes. |
|
||||
* |
|
||||
* @secret: the shared secret (32 bytes long) |
|
||||
* @pubkey: the public key of the other node |
|
||||
* @privkey: the private key of this node (32 bytes long) |
|
||||
*/ |
|
||||
bool onion_shared_secret( |
|
||||
u8 *secret, |
|
||||
const struct onionpacket *packet, |
|
||||
const struct privkey *privkey); |
|
||||
|
|
||||
/**
|
|
||||
* process_onionpacket - process an incoming packet by stripping one |
|
||||
* onion layer and return the packet for the next hop. |
|
||||
* |
|
||||
* @ctx: tal context to allocate from |
|
||||
* @packet: incoming packet being processed |
|
||||
* @shared_secret: the result of onion_shared_secret. |
|
||||
* @hoppayload: the per-hop payload destined for the processing node. |
|
||||
* @assocdata: associated data to commit to in HMACs |
|
||||
* @assocdatalen: length of the assocdata |
|
||||
*/ |
|
||||
struct route_step *process_onionpacket( |
|
||||
const tal_t * ctx, |
|
||||
const struct onionpacket *packet, |
|
||||
const u8 *shared_secret, |
|
||||
const u8 *assocdata, |
|
||||
const size_t assocdatalen |
|
||||
); |
|
||||
|
|
||||
/**
|
|
||||
* serialize_onionpacket - Serialize an onionpacket to a buffer. |
|
||||
* |
|
||||
* @ctx: tal context to allocate from |
|
||||
* @packet: the packet to serialize |
|
||||
*/ |
|
||||
u8 *serialize_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
const struct onionpacket *packet); |
|
||||
|
|
||||
/**
|
|
||||
* parese_onionpacket - Parse an onionpacket from a buffer. |
|
||||
* |
|
||||
* @ctx: tal context to allocate from |
|
||||
* @src: buffer to read the packet from |
|
||||
* @srclen: length of the @src |
|
||||
*/ |
|
||||
struct onionpacket *parse_onionpacket( |
|
||||
const tal_t *ctx, |
|
||||
const void *src, |
|
||||
const size_t srclen |
|
||||
); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_SPHINX_H */ |
|
@ -1,77 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_STATE_H |
|
||||
#define LIGHTNING_DAEMON_STATE_H |
|
||||
#include "config.h" |
|
||||
|
|
||||
#include "daemon/state_types.h" |
|
||||
#include <stdbool.h> |
|
||||
|
|
||||
static inline bool state_is_error(enum state s) |
|
||||
{ |
|
||||
return s >= STATE_ERR_BREAKDOWN && s <= STATE_ERR_INTERNAL; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_shutdown(enum state s) |
|
||||
{ |
|
||||
return s == STATE_SHUTDOWN || s == STATE_SHUTDOWN_COMMITTING; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_onchain(enum state s) |
|
||||
{ |
|
||||
return s >= STATE_CLOSE_ONCHAIN_CHEATED |
|
||||
&& s <= STATE_CLOSE_ONCHAIN_MUTUAL; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_normal(enum state s) |
|
||||
{ |
|
||||
return s == STATE_NORMAL || s == STATE_NORMAL_COMMITTING; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_waiting_for_anchor(enum state s) |
|
||||
{ |
|
||||
return s == STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
|| s == STATE_OPEN_WAIT_ANCHORDEPTH; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_openwait(enum state s) |
|
||||
{ |
|
||||
return s == STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
|| s == STATE_OPEN_WAIT_ANCHORDEPTH |
|
||||
|| s == STATE_OPEN_WAIT_THEIRCOMPLETE; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_is_opening(enum state s) |
|
||||
{ |
|
||||
return s <= STATE_OPEN_WAIT_THEIRCOMPLETE; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_can_io(enum state s) |
|
||||
{ |
|
||||
if (state_is_error(s)) |
|
||||
return false; |
|
||||
if (s == STATE_CLOSED) |
|
||||
return false; |
|
||||
if (state_is_onchain(s)) |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static inline bool state_can_commit(enum state s) |
|
||||
{ |
|
||||
return s == STATE_NORMAL || s == STATE_SHUTDOWN; |
|
||||
} |
|
||||
|
|
||||
/* FIXME-OLD #2:
|
|
||||
* |
|
||||
* A node MUST NOT send a `update_add_htlc` after a `close_shutdown` |
|
||||
*/ |
|
||||
static inline bool state_can_add_htlc(enum state s) |
|
||||
{ |
|
||||
return state_is_normal(s); |
|
||||
} |
|
||||
|
|
||||
static inline bool state_can_remove_htlc(enum state s) |
|
||||
{ |
|
||||
return state_is_normal(s) || state_is_shutdown(s); |
|
||||
} |
|
||||
|
|
||||
#endif /* LIGHTNING_STATE_H */ |
|
@ -1,99 +0,0 @@ |
|||||
#ifndef LIGHTNING_STATE_TYPES_H |
|
||||
#define LIGHTNING_STATE_TYPES_H |
|
||||
#include "config.h" |
|
||||
/* FIXME: cdump is really dumb, so we put these in their own header. */ |
|
||||
#include "lightning.pb-c.h" |
|
||||
|
|
||||
enum state { |
|
||||
STATE_INIT, |
|
||||
|
|
||||
/*
|
|
||||
* Opening. |
|
||||
*/ |
|
||||
STATE_OPEN_WAIT_FOR_OPENPKT, |
|
||||
STATE_OPEN_WAIT_FOR_ANCHORPKT, |
|
||||
STATE_OPEN_WAIT_FOR_COMMIT_SIGPKT, |
|
||||
|
|
||||
/* We're waiting for depth+their complete. */ |
|
||||
STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE, |
|
||||
/* Got their pkt_complete. */ |
|
||||
STATE_OPEN_WAIT_ANCHORDEPTH, |
|
||||
/* Got anchor depth. */ |
|
||||
STATE_OPEN_WAIT_THEIRCOMPLETE, |
|
||||
|
|
||||
/*
|
|
||||
* Normal state. |
|
||||
*/ |
|
||||
STATE_NORMAL, |
|
||||
STATE_NORMAL_COMMITTING, |
|
||||
|
|
||||
/*
|
|
||||
* Closing (handled outside state machine). |
|
||||
*/ |
|
||||
STATE_SHUTDOWN, |
|
||||
STATE_SHUTDOWN_COMMITTING, |
|
||||
STATE_MUTUAL_CLOSING, |
|
||||
|
|
||||
/* Four states to represent closing onchain (for getpeers) */ |
|
||||
STATE_CLOSE_ONCHAIN_CHEATED, |
|
||||
STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL, |
|
||||
STATE_CLOSE_ONCHAIN_OUR_UNILATERAL, |
|
||||
STATE_CLOSE_ONCHAIN_MUTUAL, |
|
||||
|
|
||||
/* All closed. */ |
|
||||
STATE_CLOSED, |
|
||||
|
|
||||
/*
|
|
||||
* Where angels fear to tread. |
|
||||
*/ |
|
||||
/* Bad packet from them / protocol breakdown. */ |
|
||||
STATE_ERR_BREAKDOWN, |
|
||||
/* The anchor didn't reach blockchain in reasonable time. */ |
|
||||
STATE_ERR_ANCHOR_TIMEOUT, |
|
||||
/* We saw a tx we didn't sign. */ |
|
||||
STATE_ERR_INFORMATION_LEAK, |
|
||||
/* We ended up in an unexpected state. */ |
|
||||
STATE_ERR_INTERNAL, |
|
||||
|
|
||||
STATE_MAX |
|
||||
}; |
|
||||
|
|
||||
enum state_input { |
|
||||
/*
|
|
||||
* Packet inputs. |
|
||||
*/ |
|
||||
PKT_OPEN = PKT__PKT_OPEN, |
|
||||
PKT_OPEN_ANCHOR = PKT__PKT_OPEN_ANCHOR, |
|
||||
PKT_OPEN_COMMIT_SIG = PKT__PKT_OPEN_COMMIT_SIG, |
|
||||
PKT_OPEN_COMPLETE = PKT__PKT_OPEN_COMPLETE, |
|
||||
|
|
||||
/* Updating the commit transaction: new HTLC */ |
|
||||
PKT_UPDATE_ADD_HTLC = PKT__PKT_UPDATE_ADD_HTLC, |
|
||||
/* Updating the commit transaction: I have your R value! */ |
|
||||
PKT_UPDATE_FULFILL_HTLC = PKT__PKT_UPDATE_FULFILL_HTLC, |
|
||||
/* Updating the commit transaction: your HTLC failed upstream */ |
|
||||
PKT_UPDATE_FAIL_HTLC = PKT__PKT_UPDATE_FAIL_HTLC, |
|
||||
|
|
||||
/* Committing updates */ |
|
||||
PKT_UPDATE_COMMIT = PKT__PKT_UPDATE_COMMIT, |
|
||||
PKT_UPDATE_REVOCATION = PKT__PKT_UPDATE_REVOCATION, |
|
||||
|
|
||||
/* If they want to close. */ |
|
||||
PKT_CLOSE_SHUTDOWN = PKT__PKT_CLOSE_SHUTDOWN, |
|
||||
|
|
||||
/* Something unexpected went wrong. */ |
|
||||
PKT_ERROR = PKT__PKT_ERROR, |
|
||||
|
|
||||
/*
|
|
||||
* Non-packet inputs. |
|
||||
*/ |
|
||||
INPUT_NONE, |
|
||||
|
|
||||
/*
|
|
||||
* Timeouts. |
|
||||
*/ |
|
||||
INPUT_CLOSE_COMPLETE_TIMEOUT, |
|
||||
|
|
||||
INPUT_MAX |
|
||||
}; |
|
||||
#endif /* LIGHTNING_STATE_TYPES_H */ |
|
@ -1,73 +0,0 @@ |
|||||
check: daemon-tests |
|
||||
|
|
||||
# We run three different bitcoinds, for different types of tests.
|
|
||||
# Provides limited paralellism.
|
|
||||
daemon-test-0-%: |
|
||||
NO_VALGRIND=$(NO_VALGRIND) VARIANT=0 daemon/test/test-$* |
|
||||
daemon-test-1-%: |
|
||||
NO_VALGRIND=$(NO_VALGRIND) VARIANT=1 daemon/test/test-$* |
|
||||
daemon-test-2-%: |
|
||||
NO_VALGRIND=$(NO_VALGRIND) VARIANT=2 daemon/test/test-$* |
|
||||
|
|
||||
# These don't work in parallel, so chain the deps
|
|
||||
daemon-test-0-steal: daemon-test-0-unilateral |
|
||||
daemon-test-0-unilateral: daemon-test-0-funding-timeout |
|
||||
daemon-test-0-funding-timeout: daemon-test-0-mutual-close-with-htlcs |
|
||||
daemon-test-0-mutual-close-with-htlcs: daemon-test-0-different-fees |
|
||||
daemon-test-0-different-fees: daemon-test-0-routing |
|
||||
daemon-test-0-routing: daemon-test-0-invoice |
|
||||
daemon-test-0-invoice: daemon-test-0-basic\ manual-commit |
|
||||
daemon-test-0-basic\ manual-commit: daemon-test-0-basic |
|
||||
daemon-test-0-basic: daemon-test-setup-0 |
|
||||
|
|
||||
daemon-test-2-steal\ --reconnect: daemon-test-2-unilateral\ --reconnect |
|
||||
daemon-test-2-unilateral\ --reconnect: daemon-test-2-funding-timeout\ --reconnect |
|
||||
daemon-test-2-funding-timeout\ --reconnect: daemon-test-2-mutual-close-with-htlcs\ --reconnect |
|
||||
daemon-test-2-mutual-close-with-htlcs\ --reconnect: daemon-test-2-different-fees\ --reconnect |
|
||||
daemon-test-2-different-fees\ --reconnect: daemon-test-2-routing\ --reconnect |
|
||||
daemon-test-2-routing\ --reconnect: daemon-test-2-invoice\ --reconnect |
|
||||
daemon-test-2-invoice\ --reconnect: daemon-test-2-basic\ manual-commit\ --reconnect |
|
||||
daemon-test-2-basic\ manual-commit\ --reconnect: daemon-test-2-basic\ --reconnect |
|
||||
daemon-test-2-basic\ --reconnect: daemon-test-setup-2 |
|
||||
|
|
||||
daemon-test-1-steal\ --restart: daemon-test-1-unilateral\ --restart |
|
||||
daemon-test-1-unilateral\ --restart: daemon-test-1-funding-timeout\ --restart |
|
||||
daemon-test-1-funding-timeout\ --restart: daemon-test-1-mutual-close-with-htlcs\ --restart |
|
||||
daemon-test-1-mutual-close-with-htlcs\ --restart: daemon-test-1-different-fees\ --restart |
|
||||
daemon-test-1-different-fees\ --restart: daemon-test-1-routing\ --restart |
|
||||
daemon-test-1-routing\ --restart: daemon-test-1-invoice\ --restart |
|
||||
daemon-test-1-invoice\ --restart: daemon-test-1-basic\ manual-commit\ --restart |
|
||||
daemon-test-1-basic\ manual-commit\ --restart: daemon-test-1-basic\ --restart |
|
||||
daemon-test-1-basic\ --restart: daemon-test-setup-1 |
|
||||
|
|
||||
# We shutdown first in case something is left over.
|
|
||||
daemon-test-setup-%: daemon-all |
|
||||
VARIANT=$* daemon/test/scripts/shutdown.sh 2>/dev/null || true |
|
||||
VARIANT=$* daemon/test/scripts/setup.sh |
|
||||
|
|
||||
daemon-test-shutdown-0: daemon-test-0-steal |
|
||||
VARIANT=0 daemon/test/scripts/shutdown.sh |
|
||||
daemon-test-shutdown-1: daemon-test-1-steal\ --restart |
|
||||
VARIANT=1 daemon/test/scripts/shutdown.sh |
|
||||
daemon-test-shutdown-2: daemon-test-2-steal\ --reconnect |
|
||||
VARIANT=2 daemon/test/scripts/shutdown.sh |
|
||||
|
|
||||
# Forms long dependency chains.
|
|
||||
daemon-all-test: daemon-test-shutdown-0 daemon-test-shutdown-1 daemon-test-shutdown-2 |
|
||||
|
|
||||
# Note that these actually #include everything they need, except ccan/ and bitcoin/.
|
|
||||
# That allows for unit testing of statics, and special effects.
|
|
||||
DAEMON_TEST_SRC := $(wildcard daemon/test/run-*.c) |
|
||||
DAEMON_TEST_OBJS := $(DAEMON_TEST_SRC:.c=.o) |
|
||||
DAEMON_TEST_PROGRAMS := $(DAEMON_TEST_OBJS:.o=) |
|
||||
|
|
||||
update-mocks: $(DAEMON_TEST_SRC:%=update-mocks/%) |
|
||||
|
|
||||
$(DAEMON_TEST_PROGRAMS): $(CCAN_OBJS) $(BITCOIN_OBJS) $(CORE_OBJS) $(CORE_TX_OBJS) $(CORE_PROTOBUF_OBJS) $(LIBBASE58_OBJS) $(WIRE_OBJS) libsecp256k1.a libsodium.a utils.o |
|
||||
|
|
||||
$(DAEMON_TEST_OBJS): $(DAEMON_HEADERS) $(DAEMON_JSMN_HEADERS) $(BITCOIN_HEADERS) $(CORE_HEADERS) $(CORE_TX_HEADERS) $(GEN_HEADERS) $(DAEMON_GEN_HEADERS) $(CCAN_HEADERS) $(WIRE_HEADERS) |
|
||||
|
|
||||
daemon-unit-tests: $(DAEMON_TEST_PROGRAMS:%=unittest/%) |
|
||||
|
|
||||
daemon-tests: daemon-unit-tests daemon-all-test |
|
||||
|
|
@ -1,52 +0,0 @@ |
|||||
#include "daemon/channel.c" |
|
||||
#include "daemon/htlc.c" |
|
||||
#include "daemon/htlc_state.c" |
|
||||
#include <assert.h> |
|
||||
#include <stdio.h> |
|
||||
|
|
||||
/* AUTOGENERATED MOCKS START */ |
|
||||
/* Generated stub for db_new_htlc */ |
|
||||
void db_new_htlc(struct peer *peer UNNEEDED, const struct htlc *htlc UNNEEDED) |
|
||||
{ fprintf(stderr, "db_new_htlc called!\n"); abort(); } |
|
||||
/* Generated stub for db_update_htlc_state */ |
|
||||
void db_update_htlc_state(struct peer *peer UNNEEDED, const struct htlc *htlc UNNEEDED, |
|
||||
enum htlc_state oldstate UNNEEDED) |
|
||||
{ fprintf(stderr, "db_update_htlc_state called!\n"); abort(); } |
|
||||
/* Generated stub for log_ */ |
|
||||
void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) |
|
||||
|
|
||||
{ fprintf(stderr, "log_ called!\n"); abort(); } |
|
||||
/* Generated stub for peer_debug */ |
|
||||
void peer_debug(struct peer *peer UNNEEDED, const char *fmt UNNEEDED, ...) |
|
||||
|
|
||||
{ fprintf(stderr, "peer_debug called!\n"); abort(); } |
|
||||
/* Could not find declaration for tal_hexstr */ |
|
||||
/* Could not find declaration for type_to_string_ */ |
|
||||
/* AUTOGENERATED MOCKS END */ |
|
||||
|
|
||||
static void test_maxfee(size_t htlcs, u64 funds) |
|
||||
{ |
|
||||
struct channel_state cstate; |
|
||||
uint64_t maxrate; |
|
||||
|
|
||||
cstate.side[LOCAL].pay_msat = funds; |
|
||||
cstate.side[LOCAL].fee_msat = 0; |
|
||||
cstate.num_nondust = htlcs; |
|
||||
|
|
||||
maxrate = approx_max_feerate(&cstate, LOCAL); |
|
||||
assert(fee_by_feerate(tx_bytes(htlcs), maxrate) <= funds); |
|
||||
} |
|
||||
|
|
||||
int main(void) |
|
||||
{ |
|
||||
size_t htlcs, i; |
|
||||
for (htlcs = 0; htlcs < 600; htlcs++) { |
|
||||
for (i = 0; i < 32; i++) { |
|
||||
test_maxfee(htlcs, i); |
|
||||
test_maxfee(htlcs, 1ULL << i); |
|
||||
test_maxfee(htlcs, (1ULL << i) - 1); |
|
||||
test_maxfee(htlcs, (1ULL << i) + 1); |
|
||||
} |
|
||||
} |
|
||||
return 0; |
|
||||
} |
|
@ -1,21 +0,0 @@ |
|||||
#! /bin/sh |
|
||||
# Generate a block. |
|
||||
|
|
||||
set -e |
|
||||
|
|
||||
. `dirname $0`/vars.sh |
|
||||
INIT=$1 |
|
||||
|
|
||||
# Initially we need 100 blocks so coinbase matures, giving us funds. |
|
||||
if [ -n "$INIT" ]; then |
|
||||
# To activate segwit via BIP9, we need at least 432 blocks! |
|
||||
$CLI generate 432 > /dev/null |
|
||||
if $CLI getblockchaininfo | tr -s '\012\011 ' ' ' | grep -q '"segwit": { "status": "active",'; then : |
|
||||
else |
|
||||
echo "Segwit not activated after 432 blocks?" >&2 |
|
||||
$CLI getblockchaininfo >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
else |
|
||||
$CLI generate 1 > /dev/null |
|
||||
fi |
|
@ -1,29 +0,0 @@ |
|||||
#! /bin/sh |
|
||||
# Query bitcoind to get (first) unspent output to spend. |
|
||||
|
|
||||
### |
|
||||
# Nobody should *EVER* write code like this. EVER!! |
|
||||
### |
|
||||
set -e |
|
||||
|
|
||||
. `dirname $0`/vars.sh |
|
||||
|
|
||||
NUM=1 |
|
||||
if [ $# = 1 ]; then |
|
||||
NUM=$1 |
|
||||
shift |
|
||||
fi |
|
||||
|
|
||||
if [ $# -gt 0 ]; then |
|
||||
echo "Usage: getinput.sh [INPUT-INDEX]" |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
TXID=`$CLI listunspent | sed -n 's/^ *"txid" *: *"\([0-9a-f]*\)",$/\1/p' | tail -n +$NUM | head -n1` |
|
||||
OUTNUM=`$CLI listunspent | sed -n 's/^ *"vout" *: *\([0-9]*\),$/\1/p' | tail -n +$NUM | head -n1` |
|
||||
AMOUNT=`$CLI listunspent | sed -n 's/^ *"amount" *: *\([0-9.]*\),$/\1/p' | tail -n +$NUM | head -n1 | tr -d . | sed 's/^0*//'` |
|
||||
SCRIPT=`$CLI listunspent | sed -n 's/^ *"scriptPubKey" *: *"\([0-9a-f]*\)",$/\1/p' | tail -n +$NUM | head -n1` |
|
||||
ADDR=`$CLI listunspent | sed -n 's/^ *"address" *: *"\([0-9a-zA-Z]*\)",$/\1/p' | tail -n +$NUM | head -n1` |
|
||||
PRIVKEY=`$CLI dumpprivkey $ADDR` |
|
||||
|
|
||||
echo $TXID/$OUTNUM/$AMOUNT/$SCRIPT/$PRIVKEY |
|
@ -1,467 +0,0 @@ |
|||||
#! /bin/sh |
|
||||
# Sourced by test script. |
|
||||
|
|
||||
# Takes the number of lightningd's we're going to start (2 or 3), then args |
|
||||
parse_cmdline() |
|
||||
{ |
|
||||
NUM_LIGHTNINGD=$1 |
|
||||
shift |
|
||||
|
|
||||
DIR1=/tmp/lightning.$$.1 |
|
||||
DIR2=/tmp/lightning.$$.2 |
|
||||
REDIR1="$DIR1/output" |
|
||||
REDIR2="$DIR2/output" |
|
||||
REDIRERR1="$DIR1/errors" |
|
||||
REDIRERR2="$DIR2/errors" |
|
||||
|
|
||||
if [ $NUM_LIGHTNINGD = 3 ]; then |
|
||||
DIR3=/tmp/lightning.$$.3 |
|
||||
REDIR3="$DIR3/output" |
|
||||
REDIRERR3="$DIR3/errors" |
|
||||
fi |
|
||||
|
|
||||
while [ $# != 0 ]; do |
|
||||
case x"$1" in |
|
||||
x"--valgrind-vgdb") |
|
||||
[ -n "$NO_VALGRIND" ] || PREFIX="$PREFIX --vgdb-error=1" |
|
||||
REDIR1="/dev/tty" |
|
||||
REDIRERR1="/dev/tty" |
|
||||
REDIR2="/dev/tty" |
|
||||
REDIRERR2="/dev/tty" |
|
||||
if [ $NUM_LIGHTNINGD = 3 ]; then |
|
||||
REDIR3="/dev/tty" |
|
||||
REDIRERR3="/dev/tty" |
|
||||
fi |
|
||||
;; |
|
||||
x"--gdb1") |
|
||||
GDB1=1 |
|
||||
;; |
|
||||
x"--gdb2") |
|
||||
GDB2=1 |
|
||||
;; |
|
||||
x"--gdb3") |
|
||||
GDB3=1 |
|
||||
if [ $NUM_LIGHTNINGD -lt 3 ]; then |
|
||||
echo "$1" invalid with only 2 lightning daemons >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
;; |
|
||||
x"--gdb1="*) |
|
||||
DAEMON1_EXTRA=--dev-debugger=${1#--gdb1=} |
|
||||
;; |
|
||||
x"--gdb2="*) |
|
||||
DAEMON2_EXTRA=--dev-debugger=${1#--gdb2=} |
|
||||
;; |
|
||||
x"--reconnect") |
|
||||
RECONNECT=reconnect |
|
||||
;; |
|
||||
x"--restart") |
|
||||
RECONNECT=restart |
|
||||
;; |
|
||||
x"--crash") |
|
||||
CRASH_ON_FAIL=1 |
|
||||
;; |
|
||||
x"--verbose") |
|
||||
VERBOSE=1 |
|
||||
;; |
|
||||
*) |
|
||||
echo Unknown arg "$1" >&2 |
|
||||
exit 1 |
|
||||
esac |
|
||||
shift |
|
||||
done |
|
||||
|
|
||||
if [ -n "$VERBOSE" ]; then |
|
||||
FGREP="fgrep" |
|
||||
else |
|
||||
FGREP="fgrep -q" |
|
||||
# Suppress command output. |
|
||||
exec >/dev/null |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
failed() |
|
||||
{ |
|
||||
if [ -n "$CRASH_ON_FAIL" ]; then |
|
||||
$LCLI1 dev-crash 2>/dev/null || true |
|
||||
$LCLI2 dev-crash 2>/dev/null || true |
|
||||
echo -n Crash results in $DIR1 and $DIR2 >&2 |
|
||||
if [ -n "$LCLI3" ]; then |
|
||||
$LCLI3 dev-crash 2>/dev/null || true |
|
||||
echo and $DIR3 >&2 |
|
||||
else |
|
||||
echo >&2 |
|
||||
fi |
|
||||
fi |
|
||||
cat $DIR1/errors $DIR2/errors $DIR3/errors 2>/dev/null || true |
|
||||
exit 1 |
|
||||
} |
|
||||
|
|
||||
setup_lightning() |
|
||||
{ |
|
||||
NUM_LIGHTNINGD=$1 |
|
||||
|
|
||||
LCLI1="../lightning-cli --lightning-dir=$DIR1" |
|
||||
LCLI2="../lightning-cli --lightning-dir=$DIR2" |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || LCLI3="../lightning-cli --lightning-dir=$DIR3" |
|
||||
|
|
||||
trap failed EXIT |
|
||||
mkdir $DIR1 $DIR2 |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || mkdir $DIR3 |
|
||||
|
|
||||
cat > $DIR1/config <<EOF |
|
||||
disable-irc |
|
||||
log-level=debug |
|
||||
bitcoind-poll=5s |
|
||||
deadline-blocks=5 |
|
||||
min-htlc-expiry=6 |
|
||||
bitcoin-datadir=$DATADIR |
|
||||
locktime-blocks=6 |
|
||||
EOF |
|
||||
|
|
||||
cp $DIR1/config $DIR2/config |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || cp $DIR1/config $DIR3/config |
|
||||
|
|
||||
# Find a free TCP port. |
|
||||
echo port=`findport 4000 $VARIANT` >> $DIR2/config |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || echo port=`findport 4010 $VARIANT` >> $DIR3/config |
|
||||
} |
|
||||
|
|
||||
# Use DIR REDIR REDIRERR GDBFLAG BINARY EXTRAARGS |
|
||||
start_one_lightningd() |
|
||||
{ |
|
||||
# Need absolute path for re-exec testing. |
|
||||
local CMD |
|
||||
CMD="$(readlink -f `pwd`/../../$5) --lightning-dir=$1" |
|
||||
if [ -n "$4" ]; then |
|
||||
echo Press return once you run: gdb --args $CMD $6 >&2 |
|
||||
|
|
||||
read REPLY |
|
||||
else |
|
||||
CMD="$PREFIX $CMD" |
|
||||
$CMD $6 > $2 2> $3 & |
|
||||
fi |
|
||||
echo $CMD $6 |
|
||||
} |
|
||||
|
|
||||
start_lightningd() |
|
||||
{ |
|
||||
NUM_LIGHTNINGD=$1 |
|
||||
BINARY=${2:-daemon/lightningd} |
|
||||
|
|
||||
# If bitcoind not already running, start it. |
|
||||
if ! $CLI getinfo >/dev/null 2>&1; then |
|
||||
echo Starting bitcoind... |
|
||||
scripts/setup.sh |
|
||||
SHUTDOWN_BITCOIN=scripts/shutdown.sh |
|
||||
else |
|
||||
SHUTDOWN_BITCOIN=/bin/true |
|
||||
fi |
|
||||
|
|
||||
LIGHTNINGD1=`start_one_lightningd $DIR1 $REDIR1 $REDIRERR1 "$GDB1" $BINARY $DAEMON1_EXTRA` |
|
||||
LIGHTNINGD2=`start_one_lightningd $DIR2 $REDIR2 $REDIRERR2 "$GDB2" $BINARY $DAEMON2_EXTRA` |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || LIGHTNINGD3=`start_one_lightningd $DIR3 $REDIR3 $REDIRERR3 "$GDB3" $BINARY` |
|
||||
|
|
||||
if ! check "$LCLI1 getlog 2>/dev/null | $FGREP Hello"; then |
|
||||
echo Failed to start daemon 1 >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
if ! check "$LCLI2 getlog 2>/dev/null | $FGREP Hello"; then |
|
||||
echo Failed to start daemon 2 >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
if [ $NUM_LIGHTNINGD = 3 ] && ! check "$LCLI3 getlog 2>/dev/null | $FGREP Hello"; then |
|
||||
echo Failed to start daemon 3 >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# Version should match binary version |
|
||||
GETINFO_VERSION=`$LCLI1 getinfo | sed -n 's/.*"version" : "\([^"]*\)".*/\1/p'` |
|
||||
LCLI_VERSION=$($LCLI1 --version | head -n1) |
|
||||
LDAEMON_VERSION=$($LIGHTNINGD1 --version | head -n1) |
|
||||
if [ $GETINFO_VERSION != $LCLI_VERSION -o $GETINFO_VERSION != $LDAEMON_VERSION ]; then |
|
||||
echo Wrong versions: getinfo gave $GETINFO_VERSION, cli gave $LCLI_VERSION, daemon gave $LDAEMON_VERSION >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
ID1=`get_info_field "$LCLI1" id` |
|
||||
ID2=`get_info_field "$LCLI2" id` |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || ID3=`get_info_field "$LCLI3" id` |
|
||||
|
|
||||
PORT2=`get_info_field "$LCLI2" port` |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || PORT3=`get_info_field "$LCLI3" port` |
|
||||
} |
|
||||
|
|
||||
fund_lightningd() |
|
||||
{ |
|
||||
# Make a payment into a P2SH for anchor. |
|
||||
P2SHADDR=`$LCLI1 newaddr | sed -n 's/{ "address" : "\(.*\)" }/\1/p'` |
|
||||
FUND_INPUT_TXID=`$CLI sendtoaddress $P2SHADDR 0.01` |
|
||||
FUND_INPUT_TX=`$CLI getrawtransaction $FUND_INPUT_TXID` |
|
||||
|
|
||||
# Mine it so check_tx_spend doesn't see it (breaks some tests). |
|
||||
$CLI generate 1 |
|
||||
} |
|
||||
|
|
||||
lcli1() |
|
||||
{ |
|
||||
if [ -n "$VERBOSE" ]; then |
|
||||
echo $LCLI1 "$@" >&2 |
|
||||
fi |
|
||||
# Make sure we output if it fails; we need to capture it otherwise. |
|
||||
if ! OUT=`$LCLI1 "$@"`; then |
|
||||
echo "$OUT" |
|
||||
return 1 |
|
||||
fi |
|
||||
echo "$OUT" |
|
||||
if [ -n "$DO_RECONNECT" ]; then |
|
||||
case "$1" in |
|
||||
# Don't restart on every get* command. |
|
||||
get*) |
|
||||
;; |
|
||||
dev-disconnect) |
|
||||
;; |
|
||||
stop) |
|
||||
;; |
|
||||
*) |
|
||||
case "$RECONNECT" in |
|
||||
reconnect) |
|
||||
[ -z "$VERBOSE" ] || echo RECONNECTING >&2 |
|
||||
$LCLI1 dev-reconnect $ID2 >/dev/null |
|
||||
;; |
|
||||
restart) |
|
||||
[ -z "$VERBOSE" ] || echo RESTARTING >&2 |
|
||||
$LCLI1 -- dev-restart $LIGHTNINGD1 >/dev/null 2>&1 || true |
|
||||
if ! check "$LCLI1 getlog 2>/dev/null | fgrep -q Hello"; then |
|
||||
echo "dev-restart failed!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
;; |
|
||||
esac |
|
||||
# Wait for reconnect (if peer2 still there) |
|
||||
if [ -z "$NO_PEER2" ] && ! check "$LCLI1 getpeers | tr -s '\012\011\" ' ' ' | fgrep -q 'connected : true'"; then |
|
||||
echo "Failed to reconnect!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
if [ "$1" = "dev-newhtlc" ]; then |
|
||||
# It might have gotten committed, or might be forgotten. |
|
||||
ID=`echo "$OUT" | extract_id` |
|
||||
if ! htlc_exists "$LCLI1" $2 $ID; then |
|
||||
if [ -z "$VERBOSE" ]; then |
|
||||
$LCLI1 "$@" >/dev/null 2>&1 || true |
|
||||
else |
|
||||
echo "Rerunning $LCLI1 $@" >&2 |
|
||||
$LCLI1 "$@" >&2 || true |
|
||||
fi |
|
||||
fi |
|
||||
# Make sure it's confirmed before we run next command, |
|
||||
# in case *that* restarts (unless manual commit) |
|
||||
[ -n "$MANUALCOMMIT" ] || check ! htlc_is_state \'"$LCLI1"\' $2 $ID SENT_ADD_HTLC |
|
||||
# Removals may also be forgotten. |
|
||||
elif [ "$1" = "fulfillhtlc" -o "$1" = "failhtlc" ]; then |
|
||||
ID="$3" |
|
||||
if htlc_is_state "$LCLI1" $2 $ID RCVD_ADD_ACK_REVOCATION; then |
|
||||
if [ -z "$VERBOSE" ]; then |
|
||||
$LCLI1 "$@" >/dev/null 2>&1 || true |
|
||||
else |
|
||||
echo "Rerunning $LCLI1 $@" >&2 |
|
||||
$LCLI1 "$@" >&2 || true |
|
||||
fi |
|
||||
# Make sure it's confirmed before we run next command, |
|
||||
# in case *that* restarts. |
|
||||
[ -n "$MANUALCOMMIT" ] || check ! htlc_is_state \'"$LCLI1"\' $2 $ID SENT_REMOVE_HTLC |
|
||||
fi |
|
||||
fi |
|
||||
;; |
|
||||
esac |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
lcli2() |
|
||||
{ |
|
||||
if [ -n "$VERBOSE" ]; then |
|
||||
echo $LCLI2 "$@" >&2 |
|
||||
fi |
|
||||
$LCLI2 "$@" |
|
||||
} |
|
||||
|
|
||||
lcli3() |
|
||||
{ |
|
||||
if [ -n "$VERBOSE" ]; then |
|
||||
echo $LCLI3 "$@" >&2 |
|
||||
fi |
|
||||
$LCLI3 "$@" |
|
||||
} |
|
||||
|
|
||||
all_ok() |
|
||||
{ |
|
||||
# Look for valgrind errors. |
|
||||
if grep ^== $DIR1/errors; then exit 1; fi |
|
||||
if grep ^== $DIR2/errors; then exit 1; fi |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || if grep ^== $DIR3/errors; then exit 1; fi |
|
||||
|
|
||||
# Look for unknown logging types. |
|
||||
if grep "UNKNOWN TYPE" $DIR1/output >&2; then exit 1; fi |
|
||||
if grep "UNKNOWN TYPE" $DIR2/output >&2; then exit 1; fi |
|
||||
[ $NUM_LIGHTNINGD = 2 ] || if grep "UNKNOWN TYPE" $DIR3/output >&2; then exit 1; fi |
|
||||
$SHUTDOWN_BITCOIN |
|
||||
|
|
||||
trap "rm -rf $DIR1 $DIR2 $DIR3" EXIT |
|
||||
exit 0 |
|
||||
} |
|
||||
|
|
||||
# If result is in quotes, those are stripped. Spaces in quotes not handled |
|
||||
get_field() |
|
||||
{ |
|
||||
tr -s '\012\011" ' ' ' | sed 's/.* '$1' : \([^, }]*\).*/\1/' |
|
||||
} |
|
||||
|
|
||||
# If result is in quotes, those are stripped. Spaces in quotes not handled |
|
||||
get_info_field() |
|
||||
{ |
|
||||
$1 getinfo | tr -s '\012\011" ' ' ' | sed 's/.* '$2' : \([^, }]*\).*/\1/' |
|
||||
} |
|
||||
|
|
||||
# Peer $1 -> $2's htlc $3 is in state $4 |
|
||||
htlc_is_state() |
|
||||
{ |
|
||||
if [ $# != 4 ]; then echo "htlc_is_state got $# ARGS: $@" >&2; exit 1; fi |
|
||||
$1 gethtlcs $2 true | tr -s '\012\011\" ' ' ' | $FGREP "id : $3, state : $4 ," >&2 |
|
||||
} |
|
||||
|
|
||||
# Peer $1 -> $2's htlc $3 exists |
|
||||
htlc_exists() |
|
||||
{ |
|
||||
$1 gethtlcs $2 true | tr -s '\012\011\" ' ' ' | $FGREP "id : $3," >&2 |
|
||||
} |
|
||||
|
|
||||
blockheight() |
|
||||
{ |
|
||||
$CLI getblockcount |
|
||||
} |
|
||||
|
|
||||
# Usage: <cmd to test>... |
|
||||
check() |
|
||||
{ |
|
||||
local i=0 |
|
||||
while ! eval "$@"; do |
|
||||
sleep 1 |
|
||||
i=$(($i + 1)) |
|
||||
if [ $i = 60 ]; then |
|
||||
return 1 |
|
||||
fi |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
check_balance_single() |
|
||||
{ |
|
||||
lcli="$1" |
|
||||
us_pay=$2 |
|
||||
us_fee=$3 |
|
||||
them_pay=$4 |
|
||||
them_fee=$5 |
|
||||
|
|
||||
if check "$lcli getpeers | tr -s '\012\011\" ' ' ' | $FGREP \"our_amount : $us_pay, our_fee : $us_fee, their_amount : $them_pay, their_fee : $them_fee,\""; then :; else |
|
||||
echo Cannot find $lcli output: "our_amount : $us_pay, our_fee : $us_fee, their_amount : $them_pay, their_fee : $them_fee," >&2 |
|
||||
$lcli getpeers | tr -s '\012\011" ' ' ' >&2 |
|
||||
return 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
check_status_single() |
|
||||
{ |
|
||||
lcli="$1" |
|
||||
us_pay=$2 |
|
||||
us_fee=$3 |
|
||||
us_htlcs="$4" |
|
||||
them_pay=$5 |
|
||||
them_fee=$6 |
|
||||
them_htlcs="$7" |
|
||||
|
|
||||
check_balance_single "$lcli" $us_pay $us_fee $them_pay $them_fee |
|
||||
|
|
||||
if check "$lcli getpeers | tr -s '\012\011\" ' ' ' | $FGREP \"our_htlcs : [ $us_htlcs], their_htlcs : [ $them_htlcs]\""; then :; else |
|
||||
echo Cannot find $lcli output: "our_htlcs : [ $us_htlcs], their_htlcs : [ $them_htlcs]" >&2 |
|
||||
$lcli getpeers | tr -s '\012\011" ' ' ' >&2 |
|
||||
return 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
# SEND_ -> RCVD_ and RCVD_ -> SEND_ |
|
||||
swap_status() |
|
||||
{ |
|
||||
echo "$@" | sed -e 's/state : RCVD_/@@/g' -e 's/state : SENT_/state : RCVD_/g' -e 's/@@/state : SENT_/g' |
|
||||
} |
|
||||
|
|
||||
check_status() |
|
||||
{ |
|
||||
us_pay=$1 |
|
||||
us_fee=$2 |
|
||||
us_htlcs="$3" |
|
||||
them_pay=$4 |
|
||||
them_fee=$5 |
|
||||
them_htlcs="$6" |
|
||||
|
|
||||
check_status_single lcli1 "$us_pay" "$us_fee" "$us_htlcs" "$them_pay" "$them_fee" "$them_htlcs" |
|
||||
check_status_single lcli2 "$them_pay" "$them_fee" "`swap_status \"$them_htlcs\"`" "$us_pay" "$us_fee" "`swap_status \"$us_htlcs\"`" |
|
||||
} |
|
||||
|
|
||||
check_tx_spend() |
|
||||
{ |
|
||||
local FAIL |
|
||||
FAIL=0 |
|
||||
if [ $# = 1 ]; then |
|
||||
check "$CLI getrawmempool | $FGREP $1" || FAIL=1 |
|
||||
else |
|
||||
check "$CLI getrawmempool | $FGREP '\"'" || FAIL=1 |
|
||||
fi |
|
||||
if [ $FAIL = 1 ]; then |
|
||||
echo "No tx $1 in mempool:" >&2 |
|
||||
$CLI getrawmempool >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
check_peerstate() |
|
||||
{ |
|
||||
if check "$1 getpeers | $FGREP -w $2"; then : |
|
||||
else |
|
||||
echo "$1" not in state "$2": >&2 |
|
||||
$1 getpeers >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
check_peerconnected() |
|
||||
{ |
|
||||
if check "$1 getpeers | tr -s '\012\011\" ' ' ' | $FGREP -w 'connected : '$2"; then : |
|
||||
else |
|
||||
echo "$1" not connected "$2": >&2 |
|
||||
$1 getpeers >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
check_no_peers() |
|
||||
{ |
|
||||
if check "$1 getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'peers : [ ]'"; then : |
|
||||
else |
|
||||
echo "$1" still has peers: >&2 |
|
||||
$1 getpeers >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
extract_id() |
|
||||
{ |
|
||||
XID=`tr -s '\012\011\" ' ' ' | sed -n 's/{ id : \([0-9]*\) }/\1/p'` |
|
||||
case "$XID" in |
|
||||
[0-9]*) |
|
||||
echo $XID;; |
|
||||
*) |
|
||||
return 1;; |
|
||||
esac |
|
||||
} |
|
@ -1,47 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
. `dirname $0`/vars.sh |
|
||||
|
|
||||
VERSION=$(`dirname $0`/../../lightning-cli --version | head -n1) |
|
||||
[ $VERSION = `git describe --always --dirty` ] || (echo Wrong version $VERSION >&2; exit 1) |
|
||||
|
|
||||
# Start clean |
|
||||
rm -rf $DATADIR |
|
||||
mkdir $DATADIR |
|
||||
|
|
||||
# Find a free port (racy, but hey) |
|
||||
PORT=`findport 18332 $VARIANT` |
|
||||
RPCPORT=`findport $(($PORT + 1))` |
|
||||
|
|
||||
# Create appropriate config file so cmdline matches. |
|
||||
cat > $DATADIR/bitcoin.conf <<EOF |
|
||||
regtest=1 |
|
||||
testnet=0 |
|
||||
rpcport=$RPCPORT |
|
||||
port=$PORT |
|
||||
EOF |
|
||||
|
|
||||
$DAEMON & |
|
||||
i=0 |
|
||||
while ! $CLI getinfo >/dev/null 2>&1; do |
|
||||
if [ $i -gt 60 ]; then |
|
||||
echo $DAEMON start failed? >&1 |
|
||||
exit 1 |
|
||||
fi |
|
||||
sleep 1 |
|
||||
i=$(($i + 1)) |
|
||||
done |
|
||||
|
|
||||
# Make sure they have segwit support! |
|
||||
if $CLI getblockchaininfo | grep -q '"segwit"'; then : |
|
||||
else |
|
||||
echo This bitcoind does not have segwit support. >&2 |
|
||||
echo Please install a recent one >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
`dirname $0`/generate-block.sh init |
|
||||
|
|
||||
A1=$($CLI getnewaddress) |
|
||||
TX=`$CLI sendmany "" "{ \"$A1\":0.01 }"` |
|
||||
`dirname $0`/generate-block.sh |
|
@ -1,11 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
. `dirname $0`/vars.sh |
|
||||
|
|
||||
[ ! -f $DATADIR/regtest/bitcoind.pid ] || BITCOIN_PID=`cat $DATADIR/regtest/bitcoind.pid` |
|
||||
|
|
||||
$CLI stop |
|
||||
sleep 1 # Make sure socket is closed. |
|
||||
|
|
||||
# Now make sure it's dead. |
|
||||
if [ -n "$BITCOIN_PID" ]; then kill -9 $BITCOIN_PID 2>/dev/null || true; fi |
|
@ -1,40 +0,0 @@ |
|||||
# Sourced by other scripts |
|
||||
|
|
||||
# Bash variables for in-depth debugging. |
|
||||
#set -vx |
|
||||
#export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' |
|
||||
|
|
||||
# Suppress sync if we can, for speedup. |
|
||||
if which eatmydata >/dev/null; then EATMYDATA=eatmydata; fi |
|
||||
|
|
||||
DATADIR=/tmp/bitcoin-lightning$VARIANT |
|
||||
CLI="bitcoin-cli -datadir=$DATADIR" |
|
||||
REGTESTDIR=regtest |
|
||||
DAEMON="$EATMYDATA bitcoind -datadir=$DATADIR" |
|
||||
|
|
||||
PREFIX=$EATMYDATA |
|
||||
|
|
||||
# Always use valgrind if available (unless NO_VALGRIND=1 set) |
|
||||
if which valgrind >/dev/null; then :; else NO_VALGRIND=1; fi |
|
||||
[ -n "$NO_VALGRIND" ] || PREFIX="$EATMYDATA valgrind -q $VG_TRACE_CHILDREN --trace-children-skip=*bitcoin-cli* --error-exitcode=7" |
|
||||
|
|
||||
# We inject 0.01 bitcoin, but then fees (estimatefee fails and we use a |
|
||||
# fee rate as per the default). |
|
||||
AMOUNT=991880000 |
|
||||
|
|
||||
# Default fee rate per kb. |
|
||||
FEE_RATE=200000 |
|
||||
|
|
||||
# Fee in millisatoshi if we have no htlcs (note rounding to make it even) |
|
||||
NO_HTLCS_FEE=$((338 * $FEE_RATE / 2000 * 2000)) |
|
||||
ONE_HTLCS_FEE=$(( (338 + 32) * $FEE_RATE / 2000 * 2000)) |
|
||||
EXTRA_FEE=$(($ONE_HTLCS_FEE - $NO_HTLCS_FEE)) |
|
||||
|
|
||||
findport() |
|
||||
{ |
|
||||
PORT=$1 |
|
||||
# Give two ports per variant. |
|
||||
if [ x"$2" != x ]; then PORT=$(($PORT + $2 * 2)); fi |
|
||||
while netstat -ntl | grep -q ":$PORT "; do PORT=$(($PORT + 1)); done |
|
||||
echo $PORT |
|
||||
} |
|
@ -1,359 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
if [ x"$1" = x"manual-commit" ]; then |
|
||||
MANUALCOMMIT=1 |
|
||||
shift |
|
||||
fi |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
|
|
||||
if [ -n "$MANUALCOMMIT" ]; then |
|
||||
# Aka. never. |
|
||||
echo 'commit-time=1h' >> $DIR1/config |
|
||||
echo 'commit-time=1h' >> $DIR2/config |
|
||||
fi |
|
||||
|
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
# Check IDs match logs |
|
||||
[ `$LCLI1 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'` = $ID1 ] |
|
||||
[ `$LCLI2 getlog | sed -n 's/.*"ID: \([0-9a-f]*\)".*/\1/p'` = $ID2 ] |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Expect them to be waiting for anchor, and ack from other side. |
|
||||
check_peerstate lcli1 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
check_peerstate lcli2 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
|
|
||||
# Enable reconnect from here. |
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
# We turn off routing failure for the moment. |
|
||||
lcli1 dev-routefail false |
|
||||
lcli2 dev-routefail false |
|
||||
|
|
||||
A_AMOUNT=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE=$NO_HTLCS_FEE |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# This is 10,000 satoshi, so not dust! |
|
||||
HTLC_AMOUNT=10000000 |
|
||||
|
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd |
|
||||
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
if [ -n "$MANUALCOMMIT" ]; then |
|
||||
# They should register a staged htlc. |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_HTLC } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Now commit it. |
|
||||
lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# Node 1 hasn't got it committed, but node2 should have told it to stage. |
|
||||
check_status_single lcli1 $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Check channel status |
|
||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE + $EXTRA_FEE)) |
|
||||
|
|
||||
# Node 2 has it committed. |
|
||||
check_status_single lcli2 $B_AMOUNT $B_FEE "" $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_REVOCATION } " |
|
||||
|
|
||||
# There should be no "both committed" here yet |
|
||||
if lcli1 getlog debug | $FGREP "Both committed"; then |
|
||||
echo "Node1 thinks they are both committed"; |
|
||||
exit 1 |
|
||||
fi |
|
||||
if lcli2 getlog debug | $FGREP "Both committed"; then |
|
||||
echo "Node2 thinks they are both committed"; |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# Now node2 gives commitment to node1. |
|
||||
lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# After revocation, they should know they're both committed. |
|
||||
check lcli1 "getlog debug | $FGREP 'Both committed to ADD of our HTLC'" |
|
||||
check lcli2 "getlog debug | $FGREP 'Both committed to ADD of their HTLC'" |
|
||||
else |
|
||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE + $EXTRA_FEE)) |
|
||||
fi |
|
||||
|
|
||||
# Both should have committed tx. |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli2 dev-fulfillhtlc $ID1 $HTLCID $SECRET |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# Without manual commit, this check is racy. |
|
||||
if [ -n "$MANUALCOMMIT" ]; then |
|
||||
if lcli1 getlog debug | $FGREP 'Both committed to FULFILL'; then |
|
||||
echo "Node1 thinks they are both committed"; |
|
||||
exit 1 |
|
||||
fi |
|
||||
if lcli2 getlog debug | $FGREP 'Both committed to FULFILL'; then |
|
||||
echo "Node2 thinks they are both committed"; |
|
||||
exit 1 |
|
||||
fi |
|
||||
fi |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# If we're very slow, manually committed above, and we're restarting, |
|
||||
# we may restart *after* this and thus not see it in the log. |
|
||||
[ "$RECONNECT$MANUALCOMMIT" = restart1 ] || check lcli1 "getlog debug | $FGREP 'Both committed to FULFILL of our HTLC'" |
|
||||
check lcli2 "getlog debug | $FGREP 'Both committed to FULFILL of their HTLC'" |
|
||||
|
|
||||
# We've transferred the HTLC amount to 2, who now has to pay fees, |
|
||||
# so no net change for A who saves on fees. |
|
||||
B_FEE=$HTLC_AMOUNT |
|
||||
# With no HTLCs, extra fee no longer required. |
|
||||
A_FEE=$(($A_FEE - $EXTRA_FEE - $B_FEE)) |
|
||||
A_AMOUNT=$(($A_AMOUNT + $EXTRA_FEE + $HTLC_AMOUNT)) |
|
||||
|
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# A new one, at 10x the amount. |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
|
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# Check channel status |
|
||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE + $EXTRA_FEE)) |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli2 dev-failhtlc $ID1 $HTLCID 695 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# Back to how we were before. |
|
||||
A_AMOUNT=$(($A_AMOUNT + $EXTRA_FEE + $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE - $EXTRA_FEE)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Same again, but this time it expires. |
|
||||
HTLC_AMOUNT=10000001 |
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# Check channel status |
|
||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE + $EXTRA_FEE)) |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Make sure node1 accepts the expiry packet. |
|
||||
while [ $(blockheight) != $EXPIRY ]; do |
|
||||
$CLI generate 1 |
|
||||
done |
|
||||
|
|
||||
# This should make node2 send it. |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
if [ -n "$MANUALCOMMIT" ]; then |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_REMOVE_HTLC } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli2 dev-commit $ID1 |
|
||||
lcli1 dev-commit $ID2 |
|
||||
fi |
|
||||
|
|
||||
# Back to how we were before. |
|
||||
A_AMOUNT=$(($A_AMOUNT + $EXTRA_FEE + $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE - $EXTRA_FEE)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# First, give more money to node2, so it can offer HTLCs. |
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
check_status $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli2 dev-fulfillhtlc $ID1 $HTLCID $SECRET |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# Both now pay equal fees. |
|
||||
A_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
B_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
# We transferred 10000000 before, and $HTLC_AMOUNT now. |
|
||||
A_AMOUNT=$(($AMOUNT - 10000000 - $HTLC_AMOUNT - $A_FEE)) |
|
||||
B_AMOUNT=$((10000000 + $HTLC_AMOUNT - $B_FEE)) |
|
||||
|
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Two failures crossover |
|
||||
SECRET2=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfe |
|
||||
RHASH2=`lcli1 dev-rhash $SECRET2 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
# This means B will *just* afford it (but can't cover increased fees) |
|
||||
HTLC_AMOUNT=$(($B_AMOUNT - $EXTRA_FEE / 2)) |
|
||||
HTLCID=`lcli2 dev-newhtlc $ID1 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
# Make sure that's committed, in case lcli1 restarts. |
|
||||
lcli2 dev-commit $ID1 >/dev/null || true |
|
||||
|
|
||||
HTLCID2=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH2 | extract_id` |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# A covers the extra part of the fee. |
|
||||
check_status $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE - $EXTRA_FEE / 2)) $(($A_FEE + $EXTRA_FEE + $EXTRA_FEE / 2)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : SENT_ADD_ACK_REVOCATION } " 0 $(($B_FEE + $EXTRA_FEE / 2)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_ACK_REVOCATION } " |
|
||||
|
|
||||
# Fail both, to reset. |
|
||||
lcli1 dev-failhtlc $ID2 $HTLCID 830 |
|
||||
lcli2 dev-failhtlc $ID1 $HTLCID2 829 |
|
||||
|
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Now, two HTLCs at once, one from each direction. |
|
||||
# Both sides can afford this. |
|
||||
HTLC_AMOUNT=1000000 |
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
HTLCID2=`lcli2 dev-newhtlc $ID1 $HTLC_AMOUNT $EXPIRY $RHASH2 | extract_id` |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
check_status $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $(($B_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($B_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : RCVD_ADD_ACK_REVOCATION } " |
|
||||
|
|
||||
lcli1 dev-fulfillhtlc $ID2 $HTLCID2 $SECRET2 |
|
||||
lcli2 dev-failhtlc $ID1 $HTLCID 849 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
# We transferred amount from B to A. |
|
||||
A_AMOUNT=$(($A_AMOUNT + $HTLC_AMOUNT)) |
|
||||
B_AMOUNT=$(($B_AMOUNT - $HTLC_AMOUNT)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Now, test making more changes before receiving commit reply. |
|
||||
DO_RECONNECT="" |
|
||||
lcli2 dev-output $ID1 false |
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
# Make sure node1 sends commit (in the background, since it will block!) |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 & |
|
||||
|
|
||||
if [ -n "$MANUALCOMMIT" ]; then |
|
||||
# node2 will consider this committed. |
|
||||
check_status_single lcli2 $(($B_AMOUNT - $EXTRA_FEE/2)) $(($B_FEE + $EXTRA_FEE/2)) "" $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE/2)) $(($A_FEE + $EXTRA_FEE/2)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_REVOCATION } " |
|
||||
else |
|
||||
# It will start committing by itself |
|
||||
check_status_single lcli2 $(($B_AMOUNT - $EXTRA_FEE/2)) $(($B_FEE + $EXTRA_FEE/2)) "" $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE/2)) $(($A_FEE + $EXTRA_FEE/2)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_COMMIT } " |
|
||||
fi |
|
||||
|
|
||||
# node1 will still be awaiting node2's revocation reply. |
|
||||
check_status_single lcli1 $(($A_AMOUNT)) $(($A_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_COMMIT } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Now send another offer, and enable node2 output. |
|
||||
HTLCID2=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH2 | extract_id` |
|
||||
lcli2 dev-output $ID1 true |
|
||||
|
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
# Both sides should be committed to htlcs |
|
||||
# We open-code check_status here: HTLCs could be in either order. |
|
||||
check_balance_single lcli1 $(($A_AMOUNT - $HTLC_AMOUNT*2 - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) $(($B_AMOUNT - $EXTRA_FEE)) $(($B_FEE + $EXTRA_FEE)) |
|
||||
check_balance_single lcli2 $(($B_AMOUNT - $EXTRA_FEE)) $(($B_FEE + $EXTRA_FEE)) $(($A_AMOUNT - $HTLC_AMOUNT*2 - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) |
|
||||
|
|
||||
# Once both balances are correct, this should be right. |
|
||||
lcli1 getpeers | tr -s '\012\011" ' ' ' | $FGREP "our_htlcs : [ { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION }, { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : SENT_ADD_ACK_REVOCATION } ], their_htlcs : [ ]" || lcli1 getpeers | tr -s '\012\011" ' ' ' | $FGREP "our_htlcs : [ { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : SENT_ADD_ACK_REVOCATION }, { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } ], their_htlcs : [ ]" |
|
||||
|
|
||||
lcli2 getpeers | tr -s '\012\011" ' ' ' | $FGREP "our_htlcs : [ ], their_htlcs : [ { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_ACK_REVOCATION }, { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : RCVD_ADD_ACK_REVOCATION } ]" || lcli2 getpeers | tr -s '\012\011" ' ' ' | $FGREP "our_htlcs : [ ], their_htlcs : [ { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH2 , state : RCVD_ADD_ACK_REVOCATION }, { msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_ACK_REVOCATION } ]" |
|
||||
|
|
||||
# Just for once, reconnect/restart node 2. |
|
||||
case "$RECONNECT" in |
|
||||
reconnect) |
|
||||
echo RECONNECTING NODE2 |
|
||||
$LCLI2 dev-reconnect $ID1 >/dev/null |
|
||||
sleep 1 |
|
||||
;; |
|
||||
restart) |
|
||||
echo RESTARTING NODE2 |
|
||||
$LCLI2 -- dev-restart $LIGHTNINGD2 >/dev/null 2>&1 || true |
|
||||
if ! check "$LCLI2 getlog 2>/dev/null | fgrep -q Hello"; then |
|
||||
echo "Node2 dev-restart failed!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
;; |
|
||||
esac |
|
||||
|
|
||||
if ! check "$LCLI2 getpeers | tr -s '\012\011\" ' ' ' | fgrep -q 'connected : true'"; then |
|
||||
echo "Failed to reconnect!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# Node2 collects the HTLCs. |
|
||||
lcli2 dev-fulfillhtlc $ID1 $HTLCID $SECRET |
|
||||
lcli2 dev-fulfillhtlc $ID1 $HTLCID2 $SECRET2 |
|
||||
|
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
[ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# We transferred 2 * amount from A to B. |
|
||||
A_AMOUNT=$(($A_AMOUNT - $HTLC_AMOUNT * 2)) |
|
||||
B_AMOUNT=$(($B_AMOUNT + $HTLC_AMOUNT * 2)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli1 close $ID2 |
|
||||
|
|
||||
# They should be negotiating the close. |
|
||||
check_peerstate lcli1 STATE_MUTUAL_CLOSING |
|
||||
check_peerstate lcli2 STATE_MUTUAL_CLOSING |
|
||||
|
|
||||
$CLI generate 1 |
|
||||
|
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
check_peerstate lcli2 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
|
|
||||
# Give it forever-1 blocks. |
|
||||
$CLI generate 8 |
|
||||
|
|
||||
# Make sure they saw it! |
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
check_peerstate lcli2 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
|
|
||||
# Now the final one. |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
check_no_peers lcli1 |
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
|
|
||||
all_ok |
|
@ -1,86 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
|
|
||||
# Simply override default fee (estimatefee fails on regtest anyway) |
|
||||
DEFAULT_FEE_RATE2=50000 |
|
||||
# We use 5x fee rate for commits, by defailt. |
|
||||
FEE_RATE2=$(($DEFAULT_FEE_RATE2 * 5)) |
|
||||
echo "default-fee-rate=$DEFAULT_FEE_RATE2" >> $DIR2/config |
|
||||
|
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
# Asymmetry, since fee rates different. |
|
||||
NO_HTLCS_FEE2=$((338 * $FEE_RATE2 / 2000 * 2000)) |
|
||||
ONE_HTLCS_FEE2=$(( (338 + 32) * $FEE_RATE2 / 2000 * 2000)) |
|
||||
|
|
||||
A_AMOUNT1=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE1=$NO_HTLCS_FEE |
|
||||
A_AMOUNT2=$(($AMOUNT - $NO_HTLCS_FEE2)) |
|
||||
A_FEE2=$NO_HTLCS_FEE2 |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
|
|
||||
check_status_single lcli1 $A_AMOUNT1 $A_FEE1 "" $B_AMOUNT $B_FEE "" |
|
||||
check_status_single lcli2 $B_AMOUNT $B_FEE "" $(($A_AMOUNT2)) $(($A_FEE2)) "" |
|
||||
|
|
||||
# This is 100,000 satoshi, so covers fees. |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
RHASH=`lcli2 invoice $HTLC_AMOUNT RHASH3 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
ROUTE=`lcli1 getroute $ID2 $HTLC_AMOUNT 1` |
|
||||
ROUTE=`echo $ROUTE | sed 's/^{ "route" : \(.*\) }$/\1/'` |
|
||||
lcli1 sendpay "$ROUTE" $RHASH |
|
||||
|
|
||||
# They should not split fees. |
|
||||
check_status_single lcli1 $(($AMOUNT - $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" $(($HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" |
|
||||
check_status_single lcli2 $(($HTLC_AMOUNT - $NO_HTLCS_FEE2 / 2)) $(($NO_HTLCS_FEE2 / 2)) "" $(($AMOUNT - $HTLC_AMOUNT - $NO_HTLCS_FEE2 / 2)) $(($NO_HTLCS_FEE2 / 2)) "" |
|
||||
|
|
||||
# FIXME: reactivate feechanges! |
|
||||
# # Change fee rate on node2 to same as node1. |
|
||||
# lcli2 dev-feerate 40000 |
|
||||
# $CLI generate 1 |
|
||||
# [ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
# [ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# check_status $(($AMOUNT - $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" $(($HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" |
|
||||
|
|
||||
# # Change back. |
|
||||
# lcli2 dev-feerate 50000 |
|
||||
# $CLI generate 1 |
|
||||
# [ ! -n "$MANUALCOMMIT" ] || lcli2 dev-commit $ID1 |
|
||||
# [ ! -n "$MANUALCOMMIT" ] || lcli1 dev-commit $ID2 |
|
||||
|
|
||||
# check_status_single lcli1 $(($AMOUNT - $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" $(($HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) $(($NO_HTLCS_FEE / 2)) "" |
|
||||
# check_status_single lcli2 $(($HTLC_AMOUNT - $NO_HTLCS_FEE2 / 2)) $(($NO_HTLCS_FEE2 / 2)) "" $(($AMOUNT - $HTLC_AMOUNT - $NO_HTLCS_FEE2 / 2)) $(($NO_HTLCS_FEE2 / 2)) "" |
|
||||
|
|
||||
lcli1 close $ID2 |
|
||||
check_tx_spend |
|
||||
|
|
||||
# Give it 10 blocks ie "forever" |
|
||||
$CLI generate 10 |
|
||||
check_no_peers lcli1 |
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
|
|
||||
all_ok |
|
@ -1,82 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
# Prevent anchor broadcast |
|
||||
lcli1 dev-broadcast false |
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Expect them to be waiting for anchor, and ack from other side. |
|
||||
check_peerstate lcli1 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
check_peerstate lcli2 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
|
|
||||
# Enable reconnect from here. |
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
$CLI generate 99 |
|
||||
|
|
||||
# Still waiting. |
|
||||
check_peerstate lcli1 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
check_peerstate lcli2 STATE_OPEN_WAIT_ANCHORDEPTH_AND_THEIRCOMPLETE |
|
||||
|
|
||||
# Make sure whichever times out first doesn't tell the other. |
|
||||
lcli1 dev-output $ID2 false |
|
||||
lcli2 dev-output $ID1 false |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
# Node1 should have gone into STATE_ERR_ANCHOR_TIMEOUT. |
|
||||
check "lcli1 getlog debug | $FGREP STATE_ERR_ANCHOR_TIMEOUT" |
|
||||
|
|
||||
# Don't try to reconnect any more if we are. |
|
||||
if [ x"$RECONNECT" = xreconnect ]; then DO_RECONNECT=""; fi |
|
||||
|
|
||||
# If we're restarting, don't expect peer. |
|
||||
NO_PEER2=1 |
|
||||
|
|
||||
# Now let them send errors if they're still trying. |
|
||||
lcli2 dev-output $ID1 true || true |
|
||||
lcli1 dev-output $ID2 true || true |
|
||||
|
|
||||
# Peer 2 should give up, and have forgotten all about it. |
|
||||
check "lcli2 getlog debug | $FGREP STATE_CLOSED" |
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
# Node1 should be disconnected. |
|
||||
check_peerconnected lcli1 false |
|
||||
|
|
||||
# Now let node1 broadcast anchor and unilateral close belatedly! |
|
||||
lcli1 dev-broadcast true |
|
||||
|
|
||||
# Now mine that transaction so they see it. |
|
||||
$CLI generate 1 |
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_OUR_UNILATERAL |
|
||||
|
|
||||
# Now move bitcoind 1 day, which is what node2 asked for on commit. |
|
||||
# Get current time from last block (works if we run this twice). |
|
||||
CURTIME=$($CLI getblock $($CLI getblockhash $(($BLOCKHEIGHT + 100))) | sed -n 's/ "time": \([0-9]*\),/\1/p') |
|
||||
$CLI setmocktime $(($CURTIME + 24 * 60 * 60)) |
|
||||
|
|
||||
# Move average so CSV moves. |
|
||||
$CLI generate 6 |
|
||||
|
|
||||
# Now it should have spent the commit tx. |
|
||||
check_tx_spend |
|
||||
|
|
||||
# 100 blocks pass |
|
||||
$CLI generate 100 |
|
||||
|
|
||||
# Considers it all done now. |
|
||||
check_no_peers lcli1 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
all_ok |
|
@ -1,87 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
A_AMOUNT=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE=$NO_HTLCS_FEE |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# 100k satoshi should cover fees. |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
RHASH3=`lcli2 invoice $HTLC_AMOUNT RHASH3 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
lcli2 listinvoice |
|
||||
[ "`lcli2 listinvoice | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH3 , rhash : $RHASH3 , msatoshi : $HTLC_AMOUNT, complete : false } ] " ] |
|
||||
|
|
||||
HTLCID3=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH3 | extract_id` |
|
||||
|
|
||||
# We transferred amount from A to B, but fee now split evenly. |
|
||||
A_AMOUNT=$(($A_AMOUNT - $HTLC_AMOUNT + $NO_HTLCS_FEE / 2)) |
|
||||
B_AMOUNT=$(($B_AMOUNT + $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) |
|
||||
A_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
B_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
[ "`lcli2 listinvoice | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH3 , rhash : $RHASH3 , msatoshi : $HTLC_AMOUNT, complete : true } ] " ] |
|
||||
|
|
||||
# Now, failed payment (didn't pay enough) |
|
||||
RHASH4=`lcli2 invoice $HTLC_AMOUNT RHASH4 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
# Shouldn't have this already. |
|
||||
if lcli2 getlog | $FGREP 'Short payment for'; then exit 1; fi |
|
||||
|
|
||||
# Test listinvoice with both, or subset (either order possible!) |
|
||||
INVOICES=`lcli2 listinvoice | tr -s '\012\011\" ' ' '` |
|
||||
[ "$INVOICES" = "[ { label : RHASH3 , rhash : $RHASH3 , msatoshi : $HTLC_AMOUNT, complete : true }, { label : RHASH4 , rhash : $RHASH4 , msatoshi : $HTLC_AMOUNT, complete : false } ] " ] || [ "$INVOICES" = "[ { label : RHASH4 , rhash : $RHASH4 , msatoshi : $HTLC_AMOUNT, complete : false }, { label : RHASH3 , rhash : $RHASH3 , msatoshi : $HTLC_AMOUNT, complete : true } ] " ] |
|
||||
[ "`lcli2 listinvoice RHASH3 | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH3 , rhash : $RHASH3 , msatoshi : $HTLC_AMOUNT, complete : true } ] " ] |
|
||||
[ "`lcli2 listinvoice RHASH4 | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH4 , rhash : $RHASH4 , msatoshi : $HTLC_AMOUNT, complete : false } ] " ] |
|
||||
|
|
||||
HTLCID4=`lcli1 dev-newhtlc $ID2 $(($HTLC_AMOUNT - 1)) $EXPIRY $RHASH4 | extract_id` |
|
||||
|
|
||||
check lcli2 "getlog | $FGREP 'Short payment for'" |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
lcli2 delinvoice RHASH4 |
|
||||
if lcli2 delinvoice RHASH3 >/dev/null; then |
|
||||
echo "Should not be able to delete completed invoice!" >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
lcli1 close $ID2 |
|
||||
|
|
||||
# They should be negotiate the close. |
|
||||
check_tx_spend |
|
||||
|
|
||||
# Bury it in "forever" blocks. |
|
||||
$CLI generate 10 |
|
||||
|
|
||||
check_no_peers lcli1 |
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
|
|
||||
all_ok |
|
@ -1,96 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
A_AMOUNT=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE=$NO_HTLCS_FEE |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
|
|
||||
# Send funds to 2 so it can offer HTLCS |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
RHASH=`lcli2 invoice $HTLC_AMOUNT RHASH3 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
ROUTE=`lcli1 getroute $ID2 $HTLC_AMOUNT 1` |
|
||||
ROUTE=`echo $ROUTE | sed 's/^{ "route" : \(.*\) }$/\1/'` |
|
||||
lcli1 sendpay "$ROUTE" $RHASH |
|
||||
|
|
||||
# They pay half fees each. |
|
||||
A_AMOUNT=$(($A_AMOUNT - $HTLC_AMOUNT + $NO_HTLCS_FEE / 2)) |
|
||||
A_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
B_AMOUNT=$(($B_AMOUNT + $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) |
|
||||
B_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# For next step, make sure neither node fails HTLCs we're about to set up. |
|
||||
lcli1 dev-routefail false |
|
||||
lcli2 dev-routefail false |
|
||||
|
|
||||
# Set up HTLCs both ways. |
|
||||
|
|
||||
# This is 10,000 satoshi, so not dust! |
|
||||
HTLC_AMOUNT=10000000 |
|
||||
|
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd |
|
||||
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
HTLCID1=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
# FIXME: Test with dust htlc, too. |
|
||||
HTLCID2=`lcli2 dev-newhtlc $ID1 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
check_status $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $(($B_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($B_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_ACK_REVOCATION } " |
|
||||
|
|
||||
# Now begin close |
|
||||
lcli1 close $ID2 |
|
||||
|
|
||||
# They should be waiting for it to clear up. |
|
||||
check_peerstate lcli1 STATE_SHUTDOWN |
|
||||
check_peerstate lcli2 STATE_SHUTDOWN |
|
||||
|
|
||||
# Fail one, still waiting. |
|
||||
lcli2 dev-failhtlc $ID1 $HTLCID1 800 |
|
||||
check_peerstate lcli1 STATE_SHUTDOWN |
|
||||
check_peerstate lcli2 STATE_SHUTDOWN |
|
||||
|
|
||||
# Fulfill the other causes them to actually complete the close. |
|
||||
lcli1 dev-fulfillhtlc $ID2 $HTLCID2 $SECRET |
|
||||
check_peerstate lcli1 STATE_MUTUAL_CLOSING |
|
||||
check_peerstate lcli2 STATE_MUTUAL_CLOSING |
|
||||
|
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
check_peerstate lcli2 STATE_CLOSE_ONCHAIN_MUTUAL |
|
||||
|
|
||||
# Give it "forever" blocks. |
|
||||
$CLI generate 9 |
|
||||
|
|
||||
check_no_peers lcli1 |
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
|
|
||||
all_ok |
|
@ -1,146 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 3 "$@" |
|
||||
setup_lightning 3 |
|
||||
start_lightningd 3 |
|
||||
fund_lightningd |
|
||||
|
|
||||
# We connect 1->2->3 |
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
P2SHADDR2=`$LCLI2 newaddr | sed -n 's/{ "address" : "\(.*\)" }/\1/p'` |
|
||||
TXID2=`$CLI sendtoaddress $P2SHADDR2 0.01` |
|
||||
FUND_INPUT_TX2=`$CLI getrawtransaction $TXID2` |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
lcli2 connect localhost $PORT3 $FUND_INPUT_TX2 & |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
# Make sure all in STATE_NORMAL. |
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli3 STATE_NORMAL |
|
||||
|
|
||||
# More than enough to cover commit fees. |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
|
|
||||
# Tell node 1 about the 2->3 route. |
|
||||
# Add to config in case we are restaring. |
|
||||
echo "add-route=$ID2/$ID3/546000/10/36/36" >> $DIR1/config |
|
||||
lcli1 dev-add-route $ID2 $ID3 546000 10 36 36 |
|
||||
RHASH=`lcli3 invoice $HTLC_AMOUNT RHASH | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
BAD_RHASH=`echo $RHASH | tr '0-9a-f' 'a-f0-9'` |
|
||||
|
|
||||
# Get route. |
|
||||
ROUTE=`lcli1 getroute $ID3 $HTLC_AMOUNT 1` |
|
||||
ROUTE=`echo $ROUTE | sed 's/^{ "route" : \(.*\) }$/\1/'` |
|
||||
|
|
||||
# Try wrong hash. |
|
||||
if lcli1 sendpay "$ROUTE" $BAD_RHASH; then |
|
||||
echo Paid with wrong hash? >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# Try underpaying. |
|
||||
PAID=`echo "$ROUTE" | sed -n 's/.*"msatoshi" : \([0-9]*\),.*/\1/p'` |
|
||||
UNDERPAY=`echo "$ROUTE" | sed "s/: $PAID,/: $(($PAID - 1)),/"` |
|
||||
if lcli1 sendpay "$UNDERPAY" $RHASH; then |
|
||||
echo Paid with too little? >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# If restarting, make sure node3 remembers incoming payment. |
|
||||
if [ "$RECONNECT" = restart ]; then |
|
||||
$LCLI3 -- dev-restart $LIGHTNINGD3 >/dev/null 2>&1 || true |
|
||||
if ! check "$LCLI3 getpeers 2>/dev/null | tr -s '\012\011\" ' ' ' | fgrep -q 'connected : true'"; then |
|
||||
echo "Failed to reconnect!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
fi |
|
||||
|
|
||||
[ "`lcli3 listinvoice RHASH | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH , rhash : $RHASH , msatoshi : $HTLC_AMOUNT, complete : false } ] " ] |
|
||||
# Pay correctly. |
|
||||
lcli1 sendpay "$ROUTE" $RHASH |
|
||||
|
|
||||
# Node 3 should end up with that amount (minus 1/2 tx fee) |
|
||||
# Note that it is delayed a little, since node2 fulfils as soon as fulfill |
|
||||
# starts. |
|
||||
check lcli3 "getpeers | $FGREP \"\\\"our_amount\\\" : $(($HTLC_AMOUNT - $NO_HTLCS_FEE / 2))\"" |
|
||||
|
|
||||
# If restarting, make sure node3 remembers completed payment. |
|
||||
if [ "$RECONNECT" = restart ]; then |
|
||||
echo RESTARTING NODE3 |
|
||||
$LCLI3 -- dev-restart $LIGHTNINGD3 >/dev/null 2>&1 || true |
|
||||
sleep 5 |
|
||||
$LCLI2 -- dev-restart $LIGHTNINGD2 >/dev/null 2>&1 || true |
|
||||
if ! check "$LCLI3 getpeers 2>/dev/null | tr -s '\012\011\" ' ' ' | fgrep -q 'connected : true'"; then |
|
||||
echo "Failed to reconnect!">&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
fi |
|
||||
|
|
||||
[ "`lcli3 listinvoice RHASH | tr -s '\012\011\" ' ' '`" = "[ { label : RHASH , rhash : $RHASH , msatoshi : $HTLC_AMOUNT, complete : true } ] " ] |
|
||||
|
|
||||
[ "`lcli3 waitanyinvoice | tr -s '\012\011\" ' ' '`" = "{ label : RHASH , rhash : $RHASH , msatoshi : $HTLC_AMOUNT } " ] |
|
||||
|
|
||||
# Can't pay twice (try from node2) |
|
||||
ROUTE2=`lcli2 getroute $ID3 $HTLC_AMOUNT 1` |
|
||||
ROUTE2=`echo $ROUTE2 | sed 's/^{ "route" : \(.*\) }$/\1/'` |
|
||||
if lcli2 sendpay "$ROUTE2" $RHASH; then |
|
||||
echo "Paying twice worked?" >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
lcli3 close $ID2 |
|
||||
check_peerstate lcli3 STATE_MUTUAL_CLOSING |
|
||||
|
|
||||
# Re-send should be a noop (doesn't matter that node3 is down!) |
|
||||
lcli1 sendpay "$ROUTE" $RHASH |
|
||||
|
|
||||
# Re-send to different id or amount should complain. |
|
||||
SHORTROUTE=`echo "$ROUTE" | sed 's/, { "id" : .* }//' | sed 's/"msatoshi" : [0-9]*,/"msatoshi" : '$HTLC_AMOUNT,/` |
|
||||
lcli1 sendpay "$SHORTROUTE" $RHASH | $FGREP "already succeeded to $ID3" |
|
||||
lcli1 sendpay "$UNDERPAY" $RHASH | $FGREP "already succeeded with amount $HTLC_AMOUNT" |
|
||||
|
|
||||
# Now node2 should fail to route. |
|
||||
if lcli1 sendpay "$ROUTE" $BAD_RHASH | $FGREP "failed: error code 404 node $ID2 reason Unknown peer"; then : ; |
|
||||
else |
|
||||
echo "Pay to node3 didn't give 404" >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
# Now node1 should fail to route (route deleted) |
|
||||
if lcli1 getroute $ID3 $HTLC_AMOUNT 1 | $FGREP "no route found"; then : ; |
|
||||
else |
|
||||
echo "Pay to node3 didn't fail instantly second time" >&2 |
|
||||
exit 1 |
|
||||
fi |
|
||||
|
|
||||
lcli1 close $ID2 |
|
||||
check_peerstate lcli1 STATE_MUTUAL_CLOSING |
|
||||
|
|
||||
# Make sure both txs broadcast. |
|
||||
check '[ `$CLI getrawmempool | egrep -c "[a-f0-9]{32}"` = 2 ]' |
|
||||
|
|
||||
# Bury them in "forever" blocks. |
|
||||
$CLI generate 10 |
|
||||
|
|
||||
check_no_peers lcli1 |
|
||||
check_no_peers lcli2 |
|
||||
check_no_peers lcli3 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
lcli3 stop |
|
||||
|
|
||||
all_ok |
|
@ -1,91 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
A_AMOUNT=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE=$NO_HTLCS_FEE |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
|
|
||||
# Send funds to 2 so it can offer HTLCS |
|
||||
HTLC_AMOUNT=100000000 |
|
||||
RHASH=`lcli2 invoice $HTLC_AMOUNT RHASH3 | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
ROUTE=`lcli1 getroute $ID2 $HTLC_AMOUNT 1` |
|
||||
ROUTE=`echo $ROUTE | sed 's/^{ "route" : \(.*\) }$/\1/'` |
|
||||
lcli1 sendpay "$ROUTE" $RHASH |
|
||||
|
|
||||
# They pay half fees each noe. |
|
||||
A_AMOUNT=$(($A_AMOUNT - $HTLC_AMOUNT + $NO_HTLCS_FEE / 2)) |
|
||||
A_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
B_AMOUNT=$(($B_AMOUNT + $HTLC_AMOUNT - $NO_HTLCS_FEE / 2)) |
|
||||
B_FEE=$(($NO_HTLCS_FEE / 2)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# For next step, make sure neither node fails HTLCs we're about to set up. |
|
||||
lcli1 dev-routefail false |
|
||||
lcli2 dev-routefail false |
|
||||
|
|
||||
# Set up HTLCs both ways. |
|
||||
|
|
||||
# This is 10,000 satoshi, so not dust! |
|
||||
HTLC_AMOUNT=10000000 |
|
||||
|
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd |
|
||||
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
HTLCID1=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
# FIXME: Test with dust htlc, too. |
|
||||
HTLCID2=`lcli2 dev-newhtlc $ID1 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
check_status $(($A_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($A_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $(($B_AMOUNT - $HTLC_AMOUNT - $EXTRA_FEE)) $(($B_FEE + $EXTRA_FEE)) "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : RCVD_ADD_ACK_REVOCATION } " |
|
||||
|
|
||||
# This is the tx we're going to try to spend. |
|
||||
STEAL_TX=`$LCLI1 dev-signcommit $ID2 | cut -d\" -f4` |
|
||||
|
|
||||
# Fail one, succeed 2->1 payment |
|
||||
lcli2 dev-failhtlc $ID1 $HTLCID1 800 |
|
||||
lcli1 dev-fulfillhtlc $ID2 $HTLCID2 $SECRET |
|
||||
|
|
||||
A_AMOUNT=$(($A_AMOUNT + $HTLC_AMOUNT)) |
|
||||
B_AMOUNT=$(($B_AMOUNT - $HTLC_AMOUNT)) |
|
||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Send out old commit tx from peer 1. |
|
||||
$CLI sendrawtransaction $STEAL_TX |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
# Node1 should get really upset; node2 should steal the transaction. |
|
||||
check_peerstate lcli1 STATE_ERR_INFORMATION_LEAK |
|
||||
check_peerstate lcli2 STATE_CLOSE_ONCHAIN_CHEATED |
|
||||
check_tx_spend |
|
||||
|
|
||||
# Give it "forever" blocks. |
|
||||
$CLI generate 10 |
|
||||
|
|
||||
check_no_peers lcli2 |
|
||||
|
|
||||
lcli1 stop |
|
||||
lcli2 stop |
|
||||
all_ok |
|
@ -1,87 +0,0 @@ |
|||||
#! /bin/sh -e |
|
||||
|
|
||||
# Wherever we are, we want to be in daemon/test dir. |
|
||||
cd `git rev-parse --show-toplevel`/daemon/test |
|
||||
|
|
||||
. scripts/vars.sh |
|
||||
. scripts/helpers.sh |
|
||||
|
|
||||
parse_cmdline 2 "$@" |
|
||||
setup_lightning 2 |
|
||||
start_lightningd 2 |
|
||||
fund_lightningd |
|
||||
|
|
||||
lcli1 connect localhost $PORT2 $FUND_INPUT_TX & |
|
||||
|
|
||||
# Now make it pass anchor (should be in mempool: one block to bury it) |
|
||||
check_tx_spend |
|
||||
$CLI generate 1 |
|
||||
|
|
||||
DO_RECONNECT=$RECONNECT |
|
||||
|
|
||||
check_peerstate lcli1 STATE_NORMAL |
|
||||
check_peerstate lcli2 STATE_NORMAL |
|
||||
|
|
||||
# Make sure node2 doesn't fail |
|
||||
lcli2 dev-routefail false |
|
||||
|
|
||||
A_AMOUNT=$(($AMOUNT - $NO_HTLCS_FEE)) |
|
||||
A_FEE=$NO_HTLCS_FEE |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
|
|
||||
# This is 10,000 satoshi, so not dust! |
|
||||
HTLC_AMOUNT=10000000 |
|
||||
|
|
||||
EXPIRY=$(( $(blockheight) + 10)) |
|
||||
SECRET=1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd |
|
||||
RHASH=`lcli1 dev-rhash $SECRET | sed 's/.*"\([0-9a-f]*\)".*/\1/'` |
|
||||
|
|
||||
HTLCID=`lcli1 dev-newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH | extract_id` |
|
||||
|
|
||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT)) |
|
||||
A_FEE=$(($A_FEE + $EXTRA_FEE)) |
|
||||
B_AMOUNT=0 |
|
||||
B_FEE=0 |
|
||||
|
|
||||
# Wait for both to committed. |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# make node1 disconnect with node2. |
|
||||
lcli1 dev-disconnect $ID2 |
|
||||
check_peerconnected lcli1 false |
|
||||
|
|
||||
# lcli1 should have sent out commitment tx |
|
||||
check_peerstate lcli1 STATE_ERR_BREAKDOWN |
|
||||
check_tx_spend |
|
||||
|
|
||||
# Mine it. |
|
||||
$CLI generate 1 |
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_OUR_UNILATERAL |
|
||||
check_peerstate lcli2 STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL |
|
||||
|
|
||||
# both still know about htlc |
|
||||
check_status $A_AMOUNT $A_FEE "{ msatoshi : $HTLC_AMOUNT, expiry : { block : $EXPIRY }, rhash : $RHASH , state : SENT_ADD_ACK_REVOCATION } " $B_AMOUNT $B_FEE "" |
|
||||
|
|
||||
# Generate 6 blocks so CSV timeout has expired. |
|
||||
$CLI generate 6 |
|
||||
|
|
||||
# Now, lcli1 should spend its own output. |
|
||||
check_tx_spend |
|
||||
check_peerstate lcli1 STATE_CLOSE_ONCHAIN_OUR_UNILATERAL |
|
||||
|
|
||||
while [ $(blockheight) != $EXPIRY ]; do |
|
||||
$CLI generate 1 |
|
||||
done |
|
||||
|
|
||||
# lcli1 should have gotten HTLC back. |
|
||||
check_tx_spend |
|
||||
|
|
||||
# Now, after "forever" blocks, should all be concluded. |
|
||||
$CLI generate 10 |
|
||||
|
|
||||
# Both consider it all done now. |
|
||||
check_no_peers lcli1 |
|
||||
|
|
||||
lcli1 stop |
|
||||
all_ok |
|
@ -1,144 +0,0 @@ |
|||||
/* Poor man's wallet.
|
|
||||
* Needed because bitcoind doesn't (yet) produce segwit outputs, and we need |
|
||||
* such outputs for our anchor tx to make it immalleable. |
|
||||
*/ |
|
||||
#include "bitcoin/base58.h" |
|
||||
#include "bitcoin/privkey.h" |
|
||||
#include "bitcoin/script.h" |
|
||||
#include "bitcoin/signature.h" |
|
||||
#include "bitcoin/tx.h" |
|
||||
#include "db.h" |
|
||||
#include "jsonrpc.h" |
|
||||
#include "lightningd.h" |
|
||||
#include "log.h" |
|
||||
#include "wallet.h" |
|
||||
#include <ccan/structeq/structeq.h> |
|
||||
#include <sodium/randombytes.h> |
|
||||
|
|
||||
struct wallet { |
|
||||
struct list_node list; |
|
||||
struct privkey privkey; |
|
||||
struct pubkey pubkey; |
|
||||
struct ripemd160 p2sh; |
|
||||
}; |
|
||||
|
|
||||
bool restore_wallet_address(struct lightningd_state *dstate, |
|
||||
const struct privkey *privkey) |
|
||||
{ |
|
||||
struct wallet *w = tal(dstate, struct wallet); |
|
||||
u8 *redeemscript; |
|
||||
struct sha256 h; |
|
||||
|
|
||||
w->privkey = *privkey; |
|
||||
if (!pubkey_from_privkey(&w->privkey, &w->pubkey)) |
|
||||
return false; |
|
||||
|
|
||||
redeemscript = bitcoin_redeem_p2sh_p2wpkh(w, &w->pubkey); |
|
||||
sha256(&h, redeemscript, tal_count(redeemscript)); |
|
||||
ripemd160(&w->p2sh, h.u.u8, sizeof(h)); |
|
||||
|
|
||||
list_add_tail(&dstate->wallet, &w->list); |
|
||||
tal_free(redeemscript); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static void new_keypair(struct privkey *privkey, struct pubkey *pubkey) |
|
||||
{ |
|
||||
do { |
|
||||
randombytes_buf(privkey->secret.data, |
|
||||
sizeof(privkey->secret.data)); |
|
||||
} while (!pubkey_from_privkey(privkey, pubkey)); |
|
||||
} |
|
||||
|
|
||||
static struct wallet *find_by_pubkey(struct lightningd_state *dstate, |
|
||||
const struct pubkey *walletkey) |
|
||||
{ |
|
||||
struct wallet *w; |
|
||||
|
|
||||
list_for_each(&dstate->wallet, w, list) { |
|
||||
if (pubkey_eq(walletkey, &w->pubkey)) |
|
||||
return w; |
|
||||
} |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
bool wallet_add_signed_input(struct lightningd_state *dstate, |
|
||||
const struct pubkey *walletkey, |
|
||||
struct bitcoin_tx *tx, |
|
||||
unsigned int input_num) |
|
||||
{ |
|
||||
u8 *redeemscript; |
|
||||
secp256k1_ecdsa_signature sig; |
|
||||
struct wallet *w = find_by_pubkey(dstate, walletkey); |
|
||||
|
|
||||
assert(input_num < tal_count(tx->input)); |
|
||||
if (!w) |
|
||||
return false; |
|
||||
|
|
||||
redeemscript = bitcoin_redeem_p2sh_p2wpkh(tx, &w->pubkey); |
|
||||
|
|
||||
sign_tx_input(tx, input_num, |
|
||||
redeemscript, |
|
||||
p2wpkh_scriptcode(redeemscript, &w->pubkey), |
|
||||
&w->privkey, |
|
||||
&w->pubkey, |
|
||||
&sig); |
|
||||
|
|
||||
bitcoin_witness_p2sh_p2wpkh(tx->input, |
|
||||
&tx->input[input_num], |
|
||||
&sig, |
|
||||
&w->pubkey); |
|
||||
tal_free(redeemscript); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
bool wallet_can_spend(struct lightningd_state *dstate, |
|
||||
const struct bitcoin_tx_output *output, |
|
||||
struct pubkey *walletkey) |
|
||||
{ |
|
||||
struct ripemd160 h; |
|
||||
struct wallet *w; |
|
||||
|
|
||||
if (!is_p2sh(output->script)) |
|
||||
return NULL; |
|
||||
|
|
||||
memcpy(&h, output->script + 2, 20); |
|
||||
list_for_each(&dstate->wallet, w, list) { |
|
||||
if (structeq(&h, &w->p2sh)) { |
|
||||
*walletkey = w->pubkey; |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
static void json_newaddr(struct command *cmd, |
|
||||
const char *buffer, const jsmntok_t *params) |
|
||||
{ |
|
||||
struct json_result *response = new_json_result(cmd); |
|
||||
struct wallet *w = tal(cmd->dstate, struct wallet); |
|
||||
u8 *redeemscript; |
|
||||
struct sha256 h; |
|
||||
|
|
||||
new_keypair(&w->privkey, &w->pubkey); |
|
||||
redeemscript = bitcoin_redeem_p2sh_p2wpkh(cmd, &w->pubkey); |
|
||||
sha256(&h, redeemscript, tal_count(redeemscript)); |
|
||||
ripemd160(&w->p2sh, h.u.u8, sizeof(h)); |
|
||||
|
|
||||
list_add_tail(&cmd->dstate->wallet, &w->list); |
|
||||
db_add_wallet_privkey(cmd->dstate, &w->privkey); |
|
||||
|
|
||||
json_object_start(response, NULL); |
|
||||
json_add_string(response, "address", |
|
||||
p2sh_to_base58(cmd, cmd->dstate->testnet, &w->p2sh)); |
|
||||
json_object_end(response); |
|
||||
command_success(cmd, response); |
|
||||
} |
|
||||
|
|
||||
static const struct json_command newaddr_command = { |
|
||||
"newaddr", |
|
||||
json_newaddr, |
|
||||
"Get a new address to fund a channel", |
|
||||
"Returns {address} a p2sh address" |
|
||||
}; |
|
||||
AUTODATA(json_command, &newaddr_command); |
|
@ -1,21 +0,0 @@ |
|||||
#ifndef LIGHTNING_DAEMON_WALLET_H |
|
||||
#define LIGHTNING_DAEMON_WALLET_H |
|
||||
#include "config.h" |
|
||||
|
|
||||
struct lightningd_state; |
|
||||
struct bitcoin_tx; |
|
||||
struct bitcoin_tx_output; |
|
||||
|
|
||||
bool restore_wallet_address(struct lightningd_state *dstate, |
|
||||
const struct privkey *privkey); |
|
||||
|
|
||||
bool wallet_add_signed_input(struct lightningd_state *dstate, |
|
||||
const struct pubkey *walletkey, |
|
||||
struct bitcoin_tx *tx, |
|
||||
unsigned int input_num); |
|
||||
|
|
||||
bool wallet_can_spend(struct lightningd_state *dstate, |
|
||||
const struct bitcoin_tx_output *output, |
|
||||
struct pubkey *walletkey); |
|
||||
|
|
||||
#endif /* LIGHTNING_DAEMON_WALLET_H */ |
|
Loading…
Reference in new issue