coin_mvts: rewrite how onchain events are recorded, update tests

The old model of coin movements attempted to compute fees etc and log
amounts, not utxos. This is not as robust, as multi-party opens and dual
funded channels make it hard to account for fees etc correctly.

Instead, we move towards a 'utxo' view of the onchain events. Every
event is either the creation or 'destruction' of a utxo. For cases where
the value of the utxo is not (fully) debited/credited to our account, we
also record the output_value. E.g. channel closings spend a utxo who's
entire value we may not own.

Since we're now tracking UTXOs onchain, we can now do more complex
assertions about the onchain footprint of them. The integration tests
have been updated to now use more 'chain aware' assertions about the
ending state.
This commit is contained in:
niftynei
2021-12-01 09:32:55 -06:00
committed by Rusty Russell
parent 07039fc2b4
commit d2c4d4aec2
16 changed files with 1056 additions and 667 deletions

View File

@@ -7,6 +7,9 @@
#include <common/type_to_string.h>
#include <wire/wire.h>
#define WALLET "wallet"
#define EXTERNAL "external"
static const char *mvt_types[] = { "chain_mvt", "channel_mvt" };
const char *mvt_type_str(enum mvt_type type)
{
@@ -16,12 +19,27 @@ const char *mvt_type_str(enum mvt_type type)
static const char *mvt_tags[] = {
"deposit",
"withdrawal",
NULL,
"penalty",
"invoice",
"routed",
"journal_entry",
"onchain_htlc",
NULL,
"pushed",
NULL,
"channel_open",
"channel_close",
"delayed_to_us",
"htlc_timeout",
"htlc_fulfill",
"htlc_tx",
"to_wallet",
"ignored",
"anchor",
"to_them",
"penalized",
"stolen",
"to_miner",
};
const char *mvt_tag_str(enum mvt_tag tag)
{
@@ -112,124 +130,176 @@ static struct chain_coin_mvt *new_chain_coin_mvt_sat(const tal_t *ctx,
return new_chain_coin_mvt(ctx, account_name, tx_txid,
outpoint, payment_hash,
blockheight, tag, amt_msat, is_credit);
blockheight, tag, amt_msat, is_credit,
/* All amounts that are sat are
* on-chain output values */
amt_sat);
}
struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *tx_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount)
struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *spend_txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
assert(!amount_msat_eq(amount, AMOUNT_MSAT(7206000)));
return new_chain_coin_mvt(ctx, account_name, tx_txid,
outpoint, NULL, blockheight,
WITHDRAWAL, amount, false);
return new_chain_coin_mvt_sat(ctx, NULL, spend_txid,
outpoint, NULL,
blockheight, tag,
amount, false);
}
struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *tx_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount)
struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
struct amount_msat amt_msat;
bool ok;
ok = amount_sat_to_msat(&amt_msat, amount);
assert(ok);
return new_coin_withdrawal(ctx, account_name, tx_txid, outpoint,
blockheight, amt_msat);
return new_chain_coin_mvt_sat(ctx, NULL, NULL,
outpoint, NULL,
blockheight, tag,
amount, true);
}
struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount,
bool is_credit)
{
return new_chain_coin_mvt(ctx, account_name, txid,
return new_chain_coin_mvt(ctx, NULL, txid,
outpoint, NULL,
blockheight, JOURNAL,
amount, is_credit);
amount, is_credit, AMOUNT_SAT(0));
}
struct chain_coin_mvt *new_coin_deposit(const tal_t *ctx,
const char *account_name,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount)
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *out,
u32 blockheight,
const struct amount_msat amount,
const struct amount_sat output_val)
{
/* FIXME: Why dup txid here? */
return new_chain_coin_mvt(ctx, account_name, &outpoint->txid, outpoint,
NULL, blockheight, DEPOSIT,
amount, true);
return new_chain_coin_mvt(ctx, NULL, txid,
out, NULL, blockheight,
CHANNEL_CLOSE, amount, false,
output_val);
}
struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount)
struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx,
const struct channel_id *chan_id,
const struct bitcoin_outpoint *out,
u32 blockheight,
const struct amount_msat amount,
const struct amount_sat output_val)
{
struct amount_msat amt_msat;
bool ok;
ok = amount_sat_to_msat(&amt_msat, amount);
assert(ok);
return new_coin_deposit(ctx, account_name, outpoint,
blockheight, amt_msat);
struct chain_coin_mvt *mvt;
mvt = new_chain_coin_mvt(ctx, NULL, NULL, out, NULL, blockheight,
CHANNEL_OPEN, amount, true, output_val);
mvt->account_name = type_to_string(mvt, struct channel_id, chan_id);
return mvt;
}
struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
struct sha256 *payment_hash)
{
return new_chain_coin_mvt_sat(ctx, NULL, NULL,
outpoint, payment_hash,
blockheight, HTLC_FULFILL,
amount, true);
}
struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
struct sha256 *payment_hash)
{
/* An onchain htlc fulfillment to peer is a *deposit* of
* that output into their (external) account */
return new_chain_coin_mvt_sat(ctx, EXTERNAL, NULL,
outpoint, payment_hash,
blockheight, HTLC_FULFILL,
amount, false);
}
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
return new_chain_coin_mvt(ctx, EXTERNAL, txid,
outpoint, NULL, blockheight,
tag, AMOUNT_MSAT(0), true, amount);
}
struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
return new_chain_coin_mvt(ctx, EXTERNAL, NULL,
outpoint, NULL,
blockheight, tag,
AMOUNT_MSAT(0), true, amount);
}
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
return new_chain_coin_mvt_sat(ctx, WALLET, NULL,
outpoint, NULL,
blockheight, tag,
amount, true);
}
struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx,
const struct bitcoin_txid *spend_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag)
{
return new_chain_coin_mvt_sat(ctx, WALLET, spend_txid,
outpoint, NULL,
blockheight, tag,
amount, false);
}
struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount)
{
struct amount_msat amt_msat;
bool ok;
ok = amount_sat_to_msat(&amt_msat, amount);
assert(ok);
return new_chain_coin_mvt(ctx, account_name,
txid, outpoint, NULL,
blockheight, PENALTY,
amt_msat, false);
}
struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *outpoint,
struct sha256 payment_hash,
u32 blockheight,
struct amount_sat amount,
bool is_credit)
{
return new_chain_coin_mvt_sat(ctx, account_name,
txid, outpoint,
take(tal_dup(NULL, struct sha256,
&payment_hash)), blockheight,
ONCHAIN_HTLC, amount, is_credit);
txid, outpoint, NULL,
blockheight, PENALTY,
amount, false);
}
struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_msat amount)
struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx,
const struct channel_id *cid,
struct amount_msat amount)
{
return new_chain_coin_mvt(ctx, account_name, txid, NULL,
NULL, blockheight, PUSHED, amount,
false);
struct sha256 empty_hash;
/* Use a 0'd out payment hash */
memset(&empty_hash, 0, sizeof(empty_hash));
return new_channel_coin_mvt(ctx, cid, empty_hash,
NULL, amount, PUSHED, false);
}
struct coin_mvt *finalize_chain_mvt(const tal_t *ctx,

View File

@@ -22,9 +22,22 @@ enum mvt_tag {
INVOICE = 4,
ROUTED = 5,
JOURNAL = 6,
ONCHAIN_HTLC = 7,
/* 7, ONCHAIN_HTLC has been removed */
PUSHED = 8,
/* 9, SPEND_TRACK has been removed */
CHANNEL_OPEN = 10,
CHANNEL_CLOSE = 11,
CHANNEL_TO_US = 12,
HTLC_TIMEOUT = 13,
HTLC_FULFILL = 14,
HTLC_TX = 15,
TO_WALLET = 16,
IGNORED = 17,
ANCHOR = 18,
TO_THEM = 19,
PENALIZED = 20,
STOLEN = 21,
TO_MINER = 22,
};
struct channel_coin_mvt {
@@ -123,35 +136,77 @@ struct channel_coin_mvt *new_channel_coin_mvt(const tal_t *ctx,
enum mvt_tag tag,
bool is_credit);
struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *tx_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount);
struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *tx_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount);
struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *spend_txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount,
bool is_credit);
struct chain_coin_mvt *new_coin_deposit(const tal_t *ctx,
const char *account_name,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_msat amount);
struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount);
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *out,
u32 blockheight,
const struct amount_msat amount,
const struct amount_sat output_val);
struct chain_coin_mvt *new_coin_channel_open(const tal_t *ctx,
const struct channel_id *chan_id,
const struct bitcoin_outpoint *out,
u32 blockheight,
const struct amount_msat amount,
const struct amount_sat output_val);
struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
struct sha256 *payment_hash);
struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
struct sha256 *payment_hash);
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_coin_wallet_withdraw(const tal_t *ctx,
const struct bitcoin_txid *spend_txid,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx,
const struct bitcoin_outpoint *outpoint,
u32 blockheight,
struct amount_sat amount,
enum mvt_tag tag);
struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
@@ -159,25 +214,17 @@ struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx,
u32 blockheight,
struct amount_sat amount);
struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
const struct bitcoin_outpoint *outpoint,
struct sha256 payment_hash,
u32 blockheight,
struct amount_sat amount,
bool is_credit);
struct chain_coin_mvt *new_coin_pushed(const tal_t *ctx,
const char *account_name,
const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_msat amount);
struct channel_coin_mvt *new_coin_pushed(const tal_t *ctx,
const struct channel_id *cid,
struct amount_msat amount);
struct coin_mvt *finalize_chain_mvt(const tal_t *ctx,
const struct chain_coin_mvt *chain_mvt,
const char *bip173_name,
u32 timestamp,
struct node_id *node_id,
s64 mvt_count);
struct coin_mvt *finalize_channel_mvt(const tal_t *ctx,
const struct channel_coin_mvt *chan_mvt,
const char *bip173_name,

View File

@@ -130,46 +130,40 @@ void notify_feerate_change(struct lightningd *ld)
void channel_record_open(struct channel *channel)
{
struct chain_coin_mvt *mvt;
struct amount_msat channel_open_amt;
u32 blockheight;
u8 *ctx = tal(NULL, u8);
struct amount_msat start_balance;
bool we_pushed = channel->opener == LOCAL
&& !amount_msat_zero(channel->push);
blockheight = short_channel_id_blocknum(channel->scid);
/* FIXME: logic here will change for dual funded channels */
if (channel->opener == LOCAL) {
if (!amount_sat_to_msat(&channel_open_amt,
channel->funding_sats))
fatal("Unable to convert funding %s to msat",
type_to_string(tmpctx, struct amount_sat,
&channel->funding_sats));
/* If we pushed funds, add them back into the starting balance */
if (we_pushed) {
if (!amount_msat_add(&start_balance,
channel->push, channel->our_msat))
fatal("Unable to add push_msat (%s) + our_msat (%s)",
type_to_string(tmpctx, struct amount_msat,
&channel->push),
type_to_string(tmpctx, struct amount_msat,
&channel->our_msat));
/* if we pushed sats, we should decrement that
* from the channel balance */
if (amount_msat_greater(channel->push, AMOUNT_MSAT(0))) {
mvt = new_coin_pushed(ctx,
type_to_string(tmpctx,
struct channel_id,
&channel->cid),
&channel->funding.txid,
blockheight, channel->push);
notify_chain_mvt(channel->peer->ld, mvt);
}
} else {
/* we're not the funder, we record our 'opening balance'
* anyway (there's a small chance we were pushed some
* satoshis, otherwise it's zero) */
channel_open_amt = channel->our_msat;
}
} else
start_balance = channel->our_msat;
mvt = new_coin_channel_open(tmpctx,
&channel->cid,
&channel->funding,
blockheight,
start_balance,
channel->funding_sats);
mvt = new_coin_deposit(ctx,
type_to_string(tmpctx, struct channel_id,
&channel->cid),
&channel->funding,
blockheight, channel_open_amt);
notify_chain_mvt(channel->peer->ld, mvt);
tal_free(ctx);
/* If we pushed sats, *now* record them as a withdrawal */
if (we_pushed)
notify_channel_mvt(channel->peer->ld,
new_coin_pushed(tmpctx, &channel->cid,
channel->push));
}
static void lockin_complete(struct channel *channel)

View File

@@ -479,7 +479,8 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg)
commitment_point,
csv_lock);
mvt = new_coin_deposit_sat(msg, "wallet", &outpoint, blockheight, amount);
mvt = new_coin_wallet_deposit(msg, &outpoint, blockheight,
amount, CHANNEL_CLOSE);
notify_chain_mvt(channel->peer->ld, mvt);
}

View File

@@ -212,84 +212,104 @@ static void send_coin_mvt(struct chain_coin_mvt *mvt TAKES)
tal_free(mvt);
}
static void record_their_successful_cheat(const struct bitcoin_txid *txid,
u32 blockheight,
struct tracked_output *out)
{
struct chain_coin_mvt *mvt;
/* They successfully spent a delayed_to_them output
* that we were expecting to revoke */
mvt = new_coin_penalty_sat(NULL, NULL,
txid, &out->outpoint,
blockheight, out->sat);
send_coin_mvt(take(mvt));
}
static void record_htlc_fulfilled(const struct bitcoin_txid *txid,
struct tracked_output *out,
u32 blockheight,
bool we_fulfilled)
{
struct chain_coin_mvt *mvt;
/* we're recording the *deposit* of a utxo which contained channel
* funds (htlc).
*
* since we really don't know if this was a 'routed' or 'destination'
* htlc here, we record it as a 'deposit/withdrawal' type */
mvt = new_coin_onchain_htlc_sat(NULL, NULL, txid, &out->outpoint,
out->payment_hash,
blockheight, out->sat, we_fulfilled);
send_coin_mvt(take(mvt));
}
static void add_amt(struct amount_sat *sum, struct amount_sat amt)
{
if (!amount_sat_add(sum, *sum, amt))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"unable to add %s to %s",
type_to_string(tmpctx, struct amount_sat,
&amt),
type_to_string(tmpctx, struct amount_sat,
sum));
}
static void record_coin_loss(const struct bitcoin_txid *txid,
u32 blockheight,
struct tracked_output *out)
{
struct chain_coin_mvt *mvt;
/* We don't for sure know that it's a 'penalty'
* but we write it as that anyway... */
mvt = new_coin_penalty_sat(NULL, NULL, txid, &out->outpoint,
blockheight, out->sat);
send_coin_mvt(take(mvt));
}
static void record_channel_withdrawal(const struct bitcoin_txid *tx_txid,
struct tracked_output *out,
u32 blockheight)
u32 blockheight,
enum mvt_tag tag)
{
send_coin_mvt(take(new_coin_withdrawal_sat(
NULL, NULL, tx_txid, &out->outpoint,
blockheight, out->sat)));
send_coin_mvt(take(new_onchaind_withdraw(NULL, &out->outpoint, tx_txid,
blockheight, out->sat, tag)));
}
static bool is_our_htlc_tx(struct tracked_output *out)
static void record_external_spend(const struct bitcoin_txid *txid,
struct tracked_output *out,
u32 blockheight,
enum mvt_tag tag)
{
return out->resolved &&
(out->resolved->tx_type == OUR_HTLC_TIMEOUT_TX
|| out->resolved->tx_type == OUR_HTLC_SUCCESS_TX);
send_coin_mvt(take(new_coin_external_spend(NULL, &out->outpoint,
txid, blockheight,
out->sat, tag)));
}
static bool is_channel_deposit(struct tracked_output *out)
static void record_external_output(const struct bitcoin_outpoint *out,
struct amount_sat amount,
u32 blockheight,
enum mvt_tag tag)
{
return out->resolved &&
(out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US
|| out->resolved->tx_type == OUR_HTLC_SUCCESS_TX);
send_coin_mvt(take(new_coin_external_deposit(NULL, out, blockheight,
amount, tag)));
}
static void record_external_deposit(const struct tracked_output *out,
u32 blockheight,
enum mvt_tag tag)
{
record_external_output(&out->outpoint, out->sat, blockheight, tag);
}
static void record_channel_deposit(struct tracked_output *out,
u32 blockheight, enum mvt_tag tag)
{
send_coin_mvt(take(new_onchaind_deposit(NULL,
&out->outpoint,
blockheight, out->sat,
tag)));
}
static void record_to_us_htlc_fulfilled(struct tracked_output *out,
u32 blockheight)
{
send_coin_mvt(take(new_onchain_htlc_deposit(NULL,
&out->outpoint,
blockheight,
out->sat,
&out->payment_hash)));
}
static void record_to_them_htlc_fulfilled(struct tracked_output *out,
u32 blockheight)
{
send_coin_mvt(take(new_onchain_htlc_withdraw(NULL,
&out->outpoint,
blockheight,
out->sat,
&out->payment_hash)));
}
static void record_ignored_wallet_deposit(struct tracked_output *out)
{
struct bitcoin_outpoint outpoint;
/* Every spend tx we construct has a single output. */
bitcoin_txid(out->proposal->tx, &outpoint.txid);
outpoint.n = 0;
enum mvt_tag tag = TO_WALLET;
if (out->tx_type == OUR_HTLC_TIMEOUT_TX
|| out->tx_type == OUR_HTLC_SUCCESS_TX)
tag = HTLC_TX;
else if (out->tx_type == THEIR_REVOKED_UNILATERAL)
tag = PENALTY;
else if (out->tx_type == OUR_UNILATERAL
|| out->tx_type == THEIR_UNILATERAL) {
if (out->output_type == OUR_HTLC)
tag = HTLC_TIMEOUT;
}
if (out->output_type == DELAYED_OUTPUT_TO_US)
tag = CHANNEL_TO_US;
/* Record the in/out through the channel */
record_channel_deposit(out, out->tx_blockheight, tag);
record_channel_withdrawal(&outpoint.txid, out, 0, IGNORED);
}
static void record_anchor(struct tracked_output *out)
{
send_coin_mvt(take(new_coin_wallet_deposit(NULL,
&out->outpoint,
out->tx_blockheight,
out->sat, ANCHOR)));
}
static void record_coin_movements(struct tracked_output *out,
@@ -297,17 +317,53 @@ static void record_coin_movements(struct tracked_output *out,
const struct bitcoin_tx *tx,
const struct bitcoin_txid *txid)
{
/* For 'timeout' htlcs, we re-record them as a deposit
* before we withdraw them again. When the channel closed,
* we reported this as withdrawn (since we didn't know the
* total amount of pending htlcs that are to-them). So
* we have to "deposit" it again before we withdraw it.
* This is just to make the channel account close out nicely
* AND so we can accurately calculate our on-chain fee burden */
if (out->tx_type == OUR_HTLC_TIMEOUT_TX
|| out->tx_type == OUR_HTLC_SUCCESS_TX)
record_channel_deposit(out, blockheight, HTLC_TX);
if (out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US)
record_channel_deposit(out, blockheight, HTLC_TIMEOUT);
/* there is a case where we've fulfilled an htlc onchain,
* in which case we log a deposit to the channel */
if (is_channel_deposit(out))
record_htlc_fulfilled(txid, out, blockheight, true);
if (out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US
|| out->resolved->tx_type == OUR_HTLC_SUCCESS_TX)
record_to_us_htlc_fulfilled(out, blockheight);
/* If it's our to-us and our close, we publish *another* tx
* which spends the output when the timeout ends */
if (out->tx_type == OUR_UNILATERAL) {
if (out->output_type == DELAYED_OUTPUT_TO_US)
record_channel_deposit(out, blockheight, CHANNEL_TO_US);
else if (out->output_type == OUR_HTLC) {
record_channel_deposit(out, blockheight, HTLC_TIMEOUT);
record_channel_withdrawal(txid, out, blockheight, HTLC_TIMEOUT);
} else if (out->output_type == THEIR_HTLC)
record_channel_withdrawal(txid, out, blockheight, HTLC_FULFILL);
}
/* we don't record a channel withdrawal until we get to
* the 'exit' utxo, which for local commitment htlc txs
* is the child htlc_tx's output */
if (!is_our_htlc_tx(out))
record_channel_withdrawal(txid, out, blockheight);
if (out->tx_type == THEIR_REVOKED_UNILATERAL
|| out->resolved->tx_type == OUR_PENALTY_TX)
record_channel_deposit(out, blockheight, PENALTY);
if (out->resolved->tx_type == OUR_DELAYED_RETURN_TO_WALLET
|| out->resolved->tx_type == THEIR_HTLC_FULFILL_TO_US
|| out->output_type == DELAYED_OUTPUT_TO_US
|| out->resolved->tx_type == OUR_HTLC_TIMEOUT_TO_US
|| out->resolved->tx_type == OUR_PENALTY_TX) {
/* penalty rbf cases, the amount might be zero */
if (amount_sat_zero(out->sat))
record_channel_withdrawal(txid, out, blockheight, TO_MINER);
else
record_channel_withdrawal(txid, out, blockheight, TO_WALLET);
}
}
/* We vary feerate until signature they offered matches. */
@@ -616,7 +672,7 @@ replace_penalty_tx_to_us(const tal_t *ctx,
struct bitcoin_tx *tx,
const u8 *wscript),
const struct bitcoin_tx *penalty_tx,
struct amount_sat output_amount)
struct amount_sat *output_amount)
{
struct bitcoin_tx *tx;
@@ -657,17 +713,19 @@ replace_penalty_tx_to_us(const tal_t *ctx,
BITCOIN_TX_RBF_SEQUENCE,
NULL, input_amount, NULL, input_wscript);
/* Reconstruct the output with a smaller amount. */
if (amount_sat_greater(output_amount, dust_limit))
if (amount_sat_greater(*output_amount, dust_limit))
bitcoin_tx_add_output(tx,
scriptpubkey_p2wpkh(tx,
&our_wallet_pubkey),
NULL,
output_amount);
else
*output_amount);
else {
bitcoin_tx_add_output(tx,
scriptpubkey_opreturn_padded(tx),
NULL,
AMOUNT_SAT(0));
*output_amount = AMOUNT_SAT(0);
}
/* Finalize the transaction. */
bitcoin_tx_finalize(tx);
@@ -1001,7 +1059,11 @@ static void proposal_should_rbf(struct tracked_output *out, bool is_replay)
/* Recreate the penalty tx. */
tx = replace_penalty_tx_to_us(tmpctx,
&penalty_to_us,
out->proposal->tx, new_amount);
out->proposal->tx, &new_amount);
/* We also update the output's value, so our accounting
* is correct. */
out->sat = new_amount;
status_debug("Created RBF OUR_PENALTY_TX with output %s "
"(originally %s) for depth %"PRIu32"/%"PRIu32".",
@@ -1040,6 +1102,11 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay)
/* If we simply wanted to ignore it after some depth */
if (!out->proposal->tx) {
ignore_output(out);
if (out->proposal->tx_type == THEIR_HTLC_TIMEOUT_TO_THEM)
record_external_deposit(out, out->tx_blockheight,
HTLC_TIMEOUT);
return;
}
@@ -1062,16 +1129,8 @@ static void proposal_meets_depth(struct tracked_output *out, bool is_replay)
/* Don't wait for this if we're ignoring the tiny payment. */
if (out->proposal->tx_type == IGNORING_TINY_PAYMENT) {
struct bitcoin_txid txid;
ignore_output(out);
if (!is_replay) {
/* log the coin movements here, since we're not
* going to wait til we hear about it */
bitcoin_txid(out->proposal->tx, &txid);
record_channel_withdrawal(&txid, out, 0);
}
record_ignored_wallet_deposit(out);
}
/* Otherwise we will get a callback when it's in a block. */
@@ -1629,10 +1688,9 @@ static void output_spent(struct tracked_output ***outs,
resolve_htlc_tx(outs, i, tx_parts, input_num,
tx_blockheight, is_replay);
if (!is_replay)
record_coin_movements(out, tx_blockheight,
out->proposal->tx,
&tx_parts->txid);
record_coin_movements(out, tx_blockheight,
out->proposal->tx,
&tx_parts->txid);
return;
}
@@ -1643,12 +1701,15 @@ static void output_spent(struct tracked_output ***outs,
case OUTPUT_TO_US:
case DELAYED_OUTPUT_TO_US:
unknown_spend(out, tx_parts);
if (!is_replay)
record_coin_loss(&tx_parts->txid,
tx_blockheight, out);
record_external_deposit(out, tx_blockheight, PENALIZED);
break;
case THEIR_HTLC:
record_external_deposit(out, out->tx_blockheight,
HTLC_TIMEOUT);
record_external_spend(&tx_parts->txid, out,
tx_blockheight, HTLC_TIMEOUT);
if (out->tx_type == THEIR_REVOKED_UNILATERAL) {
/* we've actually got a 'new' output here */
steal_htlc_tx(out, outs, tx_parts,
@@ -1682,6 +1743,11 @@ static void output_spent(struct tracked_output ***outs,
*/
handle_htlc_onchain_fulfill(out, tx_parts,
&htlc_outpoint);
record_to_them_htlc_fulfilled(out, tx_blockheight);
record_external_spend(&tx_parts->txid, out,
tx_blockheight, HTLC_FULFILL);
if (out->tx_type == THEIR_REVOKED_UNILATERAL) {
steal_htlc_tx(out, outs, tx_parts,
tx_blockheight,
@@ -1700,11 +1766,6 @@ static void output_spent(struct tracked_output ***outs,
*/
ignore_output(out);
if (!is_replay)
record_htlc_fulfilled(&tx_parts->txid,
out,
tx_blockheight,
false);
onchain_annotate_txout(
&htlc_outpoint,
TX_CHANNEL_HTLC_SUCCESS | TX_THEIRS);
@@ -1721,9 +1782,8 @@ static void output_spent(struct tracked_output ***outs,
/* They successfully spent a delayed revoked output */
resolved_by_other(out, &tx_parts->txid,
THEIR_DELAYED_CHEAT);
if (!is_replay)
record_their_successful_cheat(&tx_parts->txid,
tx_blockheight, out);
record_external_deposit(out, tx_blockheight, STOLEN);
break;
/* Um, we don't track these! */
case OUTPUT_TO_THEM:
@@ -1957,11 +2017,6 @@ static void handle_preimage(struct tracked_output **outs,
htlc_feerate);
propose_resolution(outs[i], tx, 0, tx_type,
is_replay);
if (!is_replay && tx_type == IGNORING_TINY_PAYMENT) {
struct bitcoin_txid txid;
bitcoin_txid(tx, &txid);
record_htlc_fulfilled(&txid, outs[i], 0, true);
}
}
}
@@ -2517,7 +2572,6 @@ static void handle_our_unilateral(const struct tx_parts *tx,
struct pubkey local_per_commitment_point;
struct keyset *ks;
size_t i;
struct amount_sat their_outs = AMOUNT_SAT(0), our_outs = AMOUNT_SAT(0);
struct htlcs_info *htlcs_info;
htlcs_info = init_reply(tx, "Tracking our own unilateral close");
@@ -2631,7 +2685,6 @@ static void handle_our_unilateral(const struct tx_parts *tx,
local_wscript, is_replay);
script[LOCAL] = NULL;
add_amt(&our_outs, amt);
continue;
}
if (script[REMOTE]
@@ -2651,8 +2704,8 @@ static void handle_our_unilateral(const struct tx_parts *tx,
OUTPUT_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
record_external_deposit(out, tx_blockheight, TO_THEM);
script[REMOTE] = NULL;
add_amt(&their_outs, amt);
continue;
}
if (anchor[LOCAL]
@@ -2666,6 +2719,7 @@ static void handle_our_unilateral(const struct tx_parts *tx,
ANCHOR_TO_US,
NULL, NULL, NULL);
ignore_output(out);
record_anchor(out);
anchor[LOCAL] = NULL;
continue;
}
@@ -2679,6 +2733,7 @@ static void handle_our_unilateral(const struct tx_parts *tx,
ANCHOR_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
record_external_deposit(out, tx_blockheight, ANCHOR);
anchor[REMOTE] = NULL;
continue;
}
@@ -2718,7 +2773,6 @@ static void handle_our_unilateral(const struct tx_parts *tx,
is_replay);
script[LOCAL] = NULL;
add_amt(&our_outs, amt);
found = true;
break;
}
@@ -2752,8 +2806,10 @@ static void handle_our_unilateral(const struct tx_parts *tx,
OUTPUT_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
record_external_deposit(out,
tx_blockheight,
TO_THEM);
script[REMOTE] = NULL;
add_amt(&their_outs, amt);
found = true;
break;
}
@@ -2764,6 +2820,10 @@ static void handle_our_unilateral(const struct tx_parts *tx,
continue;
onchain_annotate_txout(&outpoint, TX_CHANNEL_PENALTY | TX_THEIRS);
record_external_output(&outpoint, amt,
tx_blockheight,
PENALTY);
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not find resolution for output %zu",
i);
@@ -2788,7 +2848,6 @@ static void handle_our_unilateral(const struct tx_parts *tx,
htlcs_info->htlcs,
htlc_scripts,
is_replay);
add_amt(&our_outs, amt);
} else {
out = new_tracked_output(&outs, &outpoint,
tx_blockheight,
@@ -2808,7 +2867,6 @@ static void handle_our_unilateral(const struct tx_parts *tx,
htlcs_info->htlcs,
htlc_scripts,
is_replay);
add_amt(&their_outs, amt);
}
out->htlc = htlcs_info->htlcs[which_htlc];
out->wscript = tal_steal(out, htlc_scripts[which_htlc]);
@@ -2904,37 +2962,6 @@ static void tell_wallet_to_remote(const struct tx_parts *tx,
csv_lock)));
}
/* When a 'cheat' transaction comes through, our accounting is
* going to be off, as it's publishing/finalizing old state.
* To compensate for this, we count *all* of the channel funds
* as ours; any subsequent handling of utxos on this tx
* will correctly mark the funds as a 'channel withdrawal'
*/
static void update_ledger_cheat(const struct bitcoin_txid *txid,
u32 blockheight,
const struct tracked_output *out)
{
/* how much of a difference should we update the
* channel account ledger by? */
struct amount_msat amt;
if (amount_msat_eq_sat(our_msat, out->sat))
return;
if (!amount_sat_sub_msat(&amt, out->sat, our_msat))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"unable to subtract our balance %s from channel total %s",
type_to_string(tmpctx, struct amount_msat,
&our_msat),
type_to_string(tmpctx, struct amount_sat,
&out->sat));
/* add the difference to our ledger balance */
send_coin_mvt(take(new_coin_journal_entry(NULL, NULL, txid,
&out->outpoint,
blockheight, amt, true)));
}
static void their_unilateral_local(struct tracked_output ***outs,
const struct tx_parts *tx,
const struct bitcoin_outpoint *outpoint,
@@ -2964,9 +2991,6 @@ static void their_unilateral_local(struct tracked_output ***outs,
NULL);
ignore_output(out);
if (!is_replay)
record_channel_withdrawal(&tx->txid, out, tx_blockheight);
tell_wallet_to_remote(tx, outpoint,
tx_blockheight,
local_scriptpubkey,
@@ -2996,10 +3020,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
struct keyset *ks;
struct pubkey *k;
size_t i;
/* We need to figure out what the 'chain fees'
* for this unilateral tx are */
struct amount_sat total_outs = AMOUNT_SAT(0), fee_cost;
bool amt_ok;
struct htlcs_info *htlcs_info;
htlcs_info = init_reply(tx,
@@ -3007,9 +3027,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
onchain_annotate_txin(
&tx->txid, 0, TX_CHANNEL_UNILATERAL | TX_CHANNEL_CHEAT | TX_THEIRS);
if (!is_replay)
update_ledger_cheat(&tx->txid, tx_blockheight, outs[0]);
/* BOLT #5:
*
* Once a node discovers a commitment transaction for which *it* has a
@@ -3157,7 +3174,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
THEIR_REVOKED_UNILATERAL,
is_replay, 1);
script[LOCAL] = NULL;
add_amt(&total_outs, amt);
continue;
}
if (script[REMOTE]
@@ -3176,7 +3192,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
NULL, NULL, NULL);
steal_to_them_output(out, 1, is_replay);
script[REMOTE] = NULL;
add_amt(&total_outs, amt);
continue;
}
if (anchor[LOCAL]
@@ -3190,6 +3205,7 @@ static void handle_their_cheat(const struct tx_parts *tx,
ANCHOR_TO_US,
NULL, NULL, NULL);
ignore_output(out);
record_anchor(out);
anchor[LOCAL] = NULL;
continue;
}
@@ -3203,6 +3219,7 @@ static void handle_their_cheat(const struct tx_parts *tx,
ANCHOR_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
record_external_deposit(out, tx_blockheight, ANCHOR);
anchor[REMOTE] = NULL;
continue;
}
@@ -3235,7 +3252,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
is_replay,
csv);
script[LOCAL] = NULL;
add_amt(&total_outs, amt);
found = true;
break;
}
@@ -3267,16 +3283,19 @@ static void handle_their_cheat(const struct tx_parts *tx,
steal_to_them_output(out, csv,
is_replay);
script[REMOTE] = NULL;
add_amt(&total_outs, amt);
found = true;
break;
}
}
if (!found)
if (!found) {
record_external_output(&outpoint, amt,
tx_blockheight,
PENALTY);
status_broken("Could not find resolution"
" for output %zu: did"
" *we* cheat?", i);
}
continue;
}
@@ -3300,7 +3319,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
htlc_scripts[which_htlc],
NULL);
steal_htlc(out, is_replay);
add_amt(&total_outs, amt);
} else {
out = new_tracked_output(&outs, &outpoint,
tx_blockheight,
@@ -3318,7 +3336,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
* * spend the *HTLC-timeout tx*, if the remote node has published it.
*/
steal_htlc(out, is_replay);
add_amt(&total_outs, amt);
}
htlc_scripts[which_htlc] = NULL;
}
@@ -3326,12 +3343,6 @@ static void handle_their_cheat(const struct tx_parts *tx,
note_missing_htlcs(htlc_scripts, htlcs_info);
tal_free(htlcs_info);
/* Record the fee cost for this tx, deducting it from channel balance */
amt_ok = amount_sat_sub(&fee_cost, outs[0]->sat, total_outs);
assert(amt_ok);
status_debug("recording chain fees for their cheat %s",
type_to_string(tmpctx, struct amount_sat, &fee_cost));
wait_for_resolved(outs);
}
@@ -3347,7 +3358,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
u8 *remote_wscript, *script[NUM_SIDES], *anchor[NUM_SIDES];
struct keyset *ks;
size_t i;
struct amount_sat their_outs = AMOUNT_SAT(0), our_outs = AMOUNT_SAT(0);
struct htlcs_info *htlcs_info;
htlcs_info = init_reply(tx, "Tracking their unilateral close");
@@ -3490,7 +3500,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
is_replay, 1);
script[LOCAL] = NULL;
add_amt(&our_outs, amt);
continue;
}
if (script[REMOTE]
@@ -3511,7 +3520,7 @@ static void handle_their_unilateral(const struct tx_parts *tx,
DELAYED_OUTPUT_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
add_amt(&their_outs, amt);
record_external_deposit(out, tx_blockheight, TO_THEM);
continue;
}
if (anchor[LOCAL]
@@ -3524,7 +3533,9 @@ static void handle_their_unilateral(const struct tx_parts *tx,
amt,
ANCHOR_TO_US,
NULL, NULL, NULL);
ignore_output(out);
record_anchor(out);
anchor[LOCAL] = NULL;
continue;
}
@@ -3539,6 +3550,7 @@ static void handle_their_unilateral(const struct tx_parts *tx,
NULL, NULL, NULL);
ignore_output(out);
anchor[REMOTE] = NULL;
record_external_deposit(out, tx_blockheight, ANCHOR);
continue;
}
@@ -3571,7 +3583,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
THEIR_UNILATERAL,
is_replay, csv);
script[LOCAL] = NULL;
add_amt(&our_outs, amt);
found = true;
break;
}
@@ -3602,7 +3613,9 @@ static void handle_their_unilateral(const struct tx_parts *tx,
DELAYED_OUTPUT_TO_THEM,
NULL, NULL, NULL);
ignore_output(out);
add_amt(&their_outs, amt);
record_external_deposit(out,
tx_blockheight,
TO_THEM);
found = true;
break;
}
@@ -3611,6 +3624,9 @@ static void handle_their_unilateral(const struct tx_parts *tx,
if (found)
continue;
record_external_output(&outpoint, amt,
tx_blockheight,
PENALTY);
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"Could not find resolution for output %zu",
i);
@@ -3635,7 +3651,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
htlcs_info->htlcs,
htlc_scripts,
is_replay);
add_amt(&our_outs, amt);
} else {
out = new_tracked_output(&outs, &outpoint,
tx_blockheight,
@@ -3653,7 +3668,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
which_htlc = resolve_their_htlc(out, matches,
htlcs_info->htlcs,
htlc_scripts, is_replay);
add_amt(&their_outs, amt);
}
out->htlc = htlcs_info->htlcs[which_htlc];
out->wscript = tal_steal(out, htlc_scripts[which_htlc]);
@@ -3666,42 +3680,6 @@ static void handle_their_unilateral(const struct tx_parts *tx,
wait_for_resolved(outs);
}
static void update_ledger_unknown(const struct bitcoin_txid *txid,
u32 blockheight,
struct amount_sat amt_salvaged)
{
/* ideally, we'd be able to capture the loss to fees (if we funded
* the channel) here separately, but given that we don't know the htlc
* set (and thus which outputs are trimmed), this is difficult.
*
* instead, we count the difference between any recoverable output
* and our current channel balance as a loss (or gain) */
bool is_credit;
struct amount_msat diff;
/* we do nothing if the amount withdrawn via 'salvage' is
* the same as our channel balance */
if (amount_msat_eq_sat(our_msat, amt_salvaged))
return;
/* if we've withdrawn *less* in salvage than we have on the books
* as being ours, we record the difference as a debit */
if (!amount_msat_sub_sat(&diff, our_msat, amt_salvaged)) {
is_credit = false;
if (!amount_sat_sub_msat(&diff, amt_salvaged, our_msat))
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"overflow subtracting %s from %s",
type_to_string(tmpctx, struct amount_msat,
&our_msat),
type_to_string(tmpctx, struct amount_sat,
&amt_salvaged));
} else
is_credit = true;
send_coin_mvt(take(new_coin_journal_entry(NULL, NULL, txid, NULL,
blockheight, diff, is_credit)));
}
static void handle_unknown_commitment(const struct tx_parts *tx,
u32 tx_blockheight,
const struct pubkey *possible_remote_per_commitment_point,
@@ -3712,7 +3690,6 @@ static void handle_unknown_commitment(const struct tx_parts *tx,
int to_us_output = -1;
/* We have two possible local scripts, depending on options */
u8 *local_scripts[2];
struct amount_sat amt_salvaged = AMOUNT_SAT(0);
struct htlcs_info *htlcs_info;
onchain_annotate_txin(&tx->txid, 0, TX_CHANNEL_UNILATERAL | TX_THEIRS);
@@ -3768,8 +3745,15 @@ static void handle_unknown_commitment(const struct tx_parts *tx,
&& wally_tx_output_scripteq(tx->outputs[i],
local_scripts[1]))
which_script = 1;
else
else {
/* Record every output on this tx as an
* external 'penalty' */
record_external_output(&outpoint, amt,
tx_blockheight,
PENALTY);
continue;
}
/* BOLT #5:
*
@@ -3786,12 +3770,6 @@ static void handle_unknown_commitment(const struct tx_parts *tx,
OUTPUT_TO_US, NULL, NULL, NULL);
ignore_output(out);
if (!is_replay)
record_channel_withdrawal(&tx->txid, out,
tx_blockheight);
add_amt(&amt_salvaged, amt);
tell_wallet_to_remote(tx, &outpoint,
tx_blockheight,
local_scripts[which_script],
@@ -3800,11 +3778,11 @@ static void handle_unknown_commitment(const struct tx_parts *tx,
csv);
local_scripts[0] = local_scripts[1] = NULL;
to_us_output = i;
goto script_found;
/* Even though we're finished, we keep rolling
* so we log all the outputs */
}
}
script_found:
if (to_us_output == -1) {
status_broken("FUNDS LOST. Unknown commitment #%"PRIu64"!",
commit_num);
@@ -3816,11 +3794,6 @@ script_found:
htlcs_info = init_reply(tx, "ERROR: Unknown commitment, recovering our funds!");
}
/* update our accounting notions for this channel.
* should result in a channel balance of zero */
if (!is_replay)
update_ledger_unknown(&tx->txid, tx_blockheight, amt_salvaged);
/* Tell master to give up on HTLCs immediately. */
for (size_t i = 0; i < tal_count(htlcs_info->htlcs); i++) {
u8 *msg;
@@ -3919,13 +3892,11 @@ int main(int argc, char *argv[])
funding_sats,
FUNDING_OUTPUT, NULL, NULL, NULL);
/* Record the funding output being spent */
/* FIXME: use channel_id as "account name" */
send_coin_mvt(take(new_coin_withdrawal(NULL, NULL, &tx->txid,
&funding, tx_blockheight,
our_msat)));
/* Record funding output spent */
send_coin_mvt(take(new_coin_channel_close(NULL, &tx->txid,
&funding, tx_blockheight,
our_msat,
funding_sats)));
status_debug("Remote per-commit point: %s",
type_to_string(tmpctx, struct pubkey,

View File

@@ -113,49 +113,65 @@ struct bitcoin_tx *htlc_success_tx(const tal_t *ctx UNNEEDED,
/* Generated stub for master_badmsg */
void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg)
{ fprintf(stderr, "master_badmsg called!\n"); abort(); }
/* Generated stub for new_coin_journal_entry */
struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
/* Generated stub for new_coin_channel_close */
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_outpoint *out UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_msat amount UNNEEDED,
bool is_credit UNNEEDED)
{ fprintf(stderr, "new_coin_journal_entry called!\n"); abort(); }
/* Generated stub for new_coin_onchain_htlc_sat */
struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct amount_msat amount UNNEEDED,
const struct amount_sat output_val UNNEEDED)
{ fprintf(stderr, "new_coin_channel_close called!\n"); abort(); }
/* Generated stub for new_coin_external_deposit */
struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
struct sha256 payment_hash UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
bool is_credit UNNEEDED)
{ fprintf(stderr, "new_coin_onchain_htlc_sat called!\n"); abort(); }
/* Generated stub for new_coin_penalty_sat */
struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED)
{ fprintf(stderr, "new_coin_penalty_sat called!\n"); abort(); }
/* Generated stub for new_coin_withdrawal */
struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *tx_txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_msat amount UNNEEDED)
{ fprintf(stderr, "new_coin_withdrawal called!\n"); abort(); }
/* Generated stub for new_coin_withdrawal_sat */
struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *tx_txid UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); }
/* Generated stub for new_coin_external_spend */
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit */
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED)
{ fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); }
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); }
/* Generated stub for new_onchain_htlc_deposit */
struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
struct sha256 *payment_hash UNNEEDED)
{ fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); }
/* Generated stub for new_onchain_htlc_withdraw */
struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
struct sha256 *payment_hash UNNEEDED)
{ fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); }
/* Generated stub for new_onchaind_deposit */
struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); }
/* Generated stub for new_onchaind_withdraw */
struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *spend_txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); }
/* Generated stub for notleak_ */
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
{ fprintf(stderr, "notleak_ called!\n"); abort(); }

View File

@@ -136,49 +136,65 @@ struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED,
void memleak_remove_region(struct htable *memtable UNNEEDED,
const void *p UNNEEDED, size_t bytelen UNNEEDED)
{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); }
/* Generated stub for new_coin_journal_entry */
struct chain_coin_mvt *new_coin_journal_entry(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
/* Generated stub for new_coin_channel_close */
struct chain_coin_mvt *new_coin_channel_close(const tal_t *ctx UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_outpoint *out UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_msat amount UNNEEDED,
bool is_credit UNNEEDED)
{ fprintf(stderr, "new_coin_journal_entry called!\n"); abort(); }
/* Generated stub for new_coin_onchain_htlc_sat */
struct chain_coin_mvt *new_coin_onchain_htlc_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct amount_msat amount UNNEEDED,
const struct amount_sat output_val UNNEEDED)
{ fprintf(stderr, "new_coin_channel_close called!\n"); abort(); }
/* Generated stub for new_coin_external_deposit */
struct chain_coin_mvt *new_coin_external_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
struct sha256 payment_hash UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
bool is_credit UNNEEDED)
{ fprintf(stderr, "new_coin_onchain_htlc_sat called!\n"); abort(); }
/* Generated stub for new_coin_penalty_sat */
struct chain_coin_mvt *new_coin_penalty_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED)
{ fprintf(stderr, "new_coin_penalty_sat called!\n"); abort(); }
/* Generated stub for new_coin_withdrawal */
struct chain_coin_mvt *new_coin_withdrawal(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *tx_txid UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_msat amount UNNEEDED)
{ fprintf(stderr, "new_coin_withdrawal called!\n"); abort(); }
/* Generated stub for new_coin_withdrawal_sat */
struct chain_coin_mvt *new_coin_withdrawal_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_txid *tx_txid UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_external_deposit called!\n"); abort(); }
/* Generated stub for new_coin_external_spend */
struct chain_coin_mvt *new_coin_external_spend(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_external_spend called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit */
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED)
{ fprintf(stderr, "new_coin_withdrawal_sat called!\n"); abort(); }
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); }
/* Generated stub for new_onchain_htlc_deposit */
struct chain_coin_mvt *new_onchain_htlc_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
struct sha256 *payment_hash UNNEEDED)
{ fprintf(stderr, "new_onchain_htlc_deposit called!\n"); abort(); }
/* Generated stub for new_onchain_htlc_withdraw */
struct chain_coin_mvt *new_onchain_htlc_withdraw(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
struct sha256 *payment_hash UNNEEDED)
{ fprintf(stderr, "new_onchain_htlc_withdraw called!\n"); abort(); }
/* Generated stub for new_onchaind_deposit */
struct chain_coin_mvt *new_onchaind_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_onchaind_deposit called!\n"); abort(); }
/* Generated stub for new_onchaind_withdraw */
struct chain_coin_mvt *new_onchaind_withdraw(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
const struct bitcoin_txid *spend_txid UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_onchaind_withdraw called!\n"); abort(); }
/* Generated stub for notleak_ */
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
{ fprintf(stderr, "notleak_ called!\n"); abort(); }

View File

@@ -22,21 +22,7 @@ def init(configuration, options, plugin):
@plugin.subscribe("coin_movement")
def notify_coin_movement(plugin, coin_movement, **kwargs):
idx = coin_movement['movement_idx']
plugin.log("{} coins movement version: {}".format(idx, coin_movement['version']))
plugin.log("{} coins node: {}".format(idx, coin_movement['node_id']))
plugin.log("{} coins mvt_type: {}".format(idx, coin_movement['type']))
plugin.log("{} coins account: {}".format(idx, coin_movement['account_id']))
plugin.log("{} coins credit: {}".format(idx, coin_movement['credit']))
plugin.log("{} coins debit: {}".format(idx, coin_movement['debit']))
plugin.log("{} coins tag: {}".format(idx, coin_movement['tag']))
plugin.log("{} coins timestamp: {}".format(idx, coin_movement['timestamp']))
plugin.log("{} coins coin_type: {}".format(idx, coin_movement['coin_type']))
for f in ['payment_hash', 'utxo_txid', 'vout', 'txid', 'part_id', 'blockheight']:
if f in coin_movement:
plugin.log("{} coins {}: {}".format(idx, f, coin_movement[f]))
plugin.log("coin movement: {}".format(coin_movement))
plugin.coin_moves.append(coin_movement)
# we save to disk so that we don't get borked if the node restarts

View File

@@ -6,7 +6,8 @@ from pyln.testing.utils import SLOW_MACHINE
from utils import (
only_one, sync_blockheight, wait_for, TIMEOUT,
account_balance, first_channel_id, closing_fee, TEST_NETWORK,
scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES
scriptpubkey_addr, calc_lease_fee, EXPERIMENTAL_FEATURES,
check_utxos_channel, anchor_expected
)
import os
@@ -19,9 +20,11 @@ import unittest
@pytest.mark.developer("Too slow without --dev-bitcoind-poll")
def test_closing(node_factory, bitcoind, chainparams):
l1, l2 = node_factory.line_graph(2)
def test_closing_simple(node_factory, bitcoind, chainparams):
coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
l1, l2 = node_factory.line_graph(2, opts={'plugin': coin_mvt_plugin})
chan = l1.get_channel_scid(l2)
channel_id = first_channel_id(l1, l2)
fee = closing_fee(3750, 2) if not chainparams['elements'] else 3603
l1.pay(l2, 200000000)
@@ -98,6 +101,22 @@ def test_closing(node_factory, bitcoind, chainparams):
assert l1.db_query("SELECT count(*) as c FROM channels;")[0]['c'] == 1
assert l2.db_query("SELECT count(*) as c FROM channels;")[0]['c'] == 1
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'deposit', None, None)],
}
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'deposit', None, None)],
}
tags = check_utxos_channel(l1, [channel_id], expected_1)
check_utxos_channel(l2, [channel_id], expected_2, tags)
def test_closing_while_disconnected(node_factory, bitcoind, executor):
l1, l2 = node_factory.line_graph(2, opts={'may_reconnect': True})
@@ -604,8 +623,34 @@ def test_penalty_inhtlc(node_factory, bitcoind, executor, chainparams):
assert [o['status'] for o in outputs] == ['confirmed'] * 2
assert set([o['txid'] for o in outputs]) == txids
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# l1 loses all of their channel balance to the peer, as penalties
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None)],
}
# l2 sweeps all of l1's closing outputs
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')],
'C': [('wallet', 'deposit', None, None)],
'D': [('wallet', 'deposit', None, None)]
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
# We use a subset of tags in expected_2 that are used in expected_1
tags = check_utxos_channel(l1, [channel_id], expected_1)
check_utxos_channel(l2, [channel_id], expected_2, tags)
@pytest.mark.developer("needs DEVELOPER=1")
def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams):
@@ -705,8 +750,34 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams):
assert [o['status'] for o in outputs] == ['confirmed'] * 3
assert set([o['txid'] for o in outputs]) == txids
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# l1 loses all of their channel balance to the peer, as penalties
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'penalty', None, None), ('external', 'penalty', None, None), ('external', 'penalty', None, None)],
}
# l2 sweeps all of l1's closing outputs
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'channel_close', None, None), ('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')],
'C': [('wallet', 'deposit', None, None)],
'D': [('wallet', 'deposit', None, None)]
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
# We use a subset of tags in expected_2 that are used in expected_1
tags = check_utxos_channel(l1, [channel_id], expected_1)
check_utxos_channel(l2, [channel_id], expected_2, tags)
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
@pytest.mark.openchannel('v2')
@@ -1173,7 +1244,6 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams):
# reconnect with l1, which will fulfill the payment
l2.rpc.connect(l1.info['id'], 'localhost', l1.port)
l2.daemon.wait_for_log('got commitsig .*: feerate 11000, blockheight: 0, 0 added, 1 fulfilled, 0 failed, 0 changed')
l2.daemon.wait_for_log('coins payment_hash: {}'.format(sticky_inv['payment_hash']))
# l2 moves on for closed l3
bitcoind.generate_block(1)
@@ -1212,6 +1282,29 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams):
assert account_balance(l3, channel_id) == 0
assert account_balance(l2, channel_id) == 0
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('external', 'penalized', None, None)],
'C': [('external', 'penalized', None, None)],
}
expected_3 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'penalty', 'to_wallet', 'E')],
'C': [('cid1', 'penalty', 'to_wallet', 'D')],
'D': [('wallet', 'deposit', None, None)],
'E': [('wallet', 'deposit', None, None)]
}
if anchor_expected():
expected_2['B'].append(('external', 'anchor', None, None))
expected_3['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
expected_3['B'].append(('wallet', 'anchor', None, None))
tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id)
check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id)
@pytest.mark.developer("needs DEVELOPER=1")
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db")
@@ -1329,7 +1422,6 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams):
# reconnect with l1, which will fulfill the payment
l2.rpc.connect(l1.info['id'], 'localhost', l1.port)
l2.daemon.wait_for_log('got commitsig .*: feerate 11000, blockheight: 0, 0 added, 1 fulfilled, 0 failed, 0 changed')
l2.daemon.wait_for_log('coins payment_hash: {}'.format(sticky_inv_2['payment_hash']))
# l2 moves on for closed l3
bitcoind.generate_block(1, wait_for_mempool=1)
@@ -1396,12 +1488,40 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams):
assert account_balance(l3, channel_id) == 0
assert account_balance(l2, channel_id) == 0
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('cid1', 'delayed_to_us', 'to_wallet', 'F'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'C')],
'C': [('external', 'penalized', None, None)],
'E': [('cid1', 'htlc_tx', 'to_wallet', 'G')],
'F': [('wallet', 'deposit', None, None)],
'G': [('wallet', 'deposit', None, None)]
}
expected_3 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'channel_close', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'E'), ('external', 'stolen', None, None), ('external', 'htlc_timeout', 'htlc_timeout', 'C')],
'C': [('cid1', 'penalty', 'to_wallet', 'D')],
'D': [('wallet', 'deposit', None, None)],
'E': [('external', 'stolen', None, None)]
}
if anchor_expected():
expected_2['B'].append(('external', 'anchor', None, None))
expected_3['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
expected_3['B'].append(('wallet', 'anchor', None, None))
tags = check_utxos_channel(l2, [channel_id], expected_2, filter_channel=channel_id)
check_utxos_channel(l3, [channel_id], expected_3, tags, filter_channel=channel_id)
@pytest.mark.developer("uses dev_sign_last_tx")
def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams):
'''
Test that penalty transactions are RBFed.
'''
# We track channel balances, to verify that accounting is ok.
coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
to_self_delay = 10
# l1 is the thief, which causes our honest upstanding lightningd
# code to break, so l1 can fail.
@@ -1409,10 +1529,12 @@ def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams):
l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'],
may_fail=True, allow_broken_log=True)
l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'],
options={'watchtime-blocks': to_self_delay})
options={'watchtime-blocks': to_self_delay,
'plugin': coin_mvt_plugin})
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l1.fundchannel(l2, 10**7)
channel_id = first_channel_id(l1, l2)
# Trigger an HTLC being added.
t = executor.submit(l1.pay, l2, 1000000 * 1000)
@@ -1501,6 +1623,21 @@ def test_penalty_rbf_normal(node_factory, bitcoind, executor, chainparams):
# And l2 should consider it in its listfunds.
assert(len(l2.rpc.listfunds()['outputs']) >= 1)
assert account_balance(l2, channel_id) == 0
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('cid1', 'penalty', 'to_wallet', 'C'), ('cid1', 'penalty', 'to_wallet', 'D')],
'C': [('wallet', 'deposit', None, None)],
'D': [('wallet', 'deposit', None, None)]
}
if anchor_expected():
expected_2['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
check_utxos_channel(l2, [channel_id], expected_2)
@pytest.mark.developer("uses dev_sign_last_tx")
def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams):
@@ -1508,6 +1645,8 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams):
Test that penalty transactions are RBFed and we are willing to burn
it all up to spite the thief.
'''
# We track channel balances, to verify that accounting is ok.
coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
to_self_delay = 10
# l1 is the thief, which causes our honest upstanding lightningd
# code to break, so l1 can fail.
@@ -1515,10 +1654,12 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams):
l1 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'],
may_fail=True, allow_broken_log=True)
l2 = node_factory.get_node(disconnect=['=WIRE_COMMITMENT_SIGNED-nocommit'],
options={'watchtime-blocks': to_self_delay})
options={'watchtime-blocks': to_self_delay,
'plugin': coin_mvt_plugin})
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
l1.fundchannel(l2, 10**7)
channel_id = first_channel_id(l1, l2)
# Trigger an HTLC being added.
t = executor.submit(l1.pay, l2, 1000000 * 1000)
@@ -1605,6 +1746,18 @@ def test_penalty_rbf_burn(node_factory, bitcoind, executor, chainparams):
# l2 donated it to the miners, so it owns nothing
assert(len(l2.rpc.listfunds()['outputs']) == 0)
assert account_balance(l2, channel_id) == 0
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('cid1', 'penalty', 'to_miner', 'C'), ('cid1', 'penalty', 'to_miner', 'D')],
}
if anchor_expected():
expected_2['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
check_utxos_channel(l2, [channel_id], expected_2)
@pytest.mark.developer("needs DEVELOPER=1")
@@ -1912,9 +2065,37 @@ def test_onchain_timeout(node_factory, bitcoind, executor):
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# Graph of coin_move events we expect
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_timeout', 'htlc_timeout', 'D')],
'C': [('wallet', 'deposit', None, None)],
'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')],
'E': [('wallet', 'deposit', None, None)]
}
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)]
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
# We use a subset of tags in expected_2 that are used in expected_1
tags = check_utxos_channel(l1, [channel_id], expected_1)
# Passing the same tags in to the check again will verify that the
# txids 'unify' across both event sets (in other words, we're talking
# about the same tx's when we say 'A' in each
check_utxos_channel(l2, [channel_id], expected_2, tags)
@pytest.mark.developer("needs DEVELOPER=1")
def test_onchain_middleman(node_factory, bitcoind):
def test_onchain_middleman_simple(node_factory, bitcoind):
# We track channel balances, to verify that accounting is ok.
coin_mvt_plugin = os.path.join(os.getcwd(), 'tests/plugins/coin_movements.py')
@@ -1999,6 +2180,35 @@ def test_onchain_middleman(node_factory, bitcoind):
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# Graph of coin_move events we expect
expected_2 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
# This is ugly, but this wallet deposit is either unspent or used
# in the next channel open
'A': [('wallet', 'deposit', [('withdrawal', 'F'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')],
'1': [('wallet', 'deposit', 'withdrawal', 'F')],
'B': [('cid1', 'delayed_to_us', 'to_wallet', 'C'), ('cid1', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('external', 'to_them', None, None)],
'C': [('wallet', 'deposit', None, None)],
'D': [('cid1', 'htlc_tx', 'to_wallet', 'E')],
'E': [('wallet', 'deposit', None, None)],
'F': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)]
}
expected_1 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'D'), ('wallet', 'channel_close', None, None)]
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
chan2_id = first_channel_id(l2, l3)
tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2)
check_utxos_channel(l1, [channel_id, chan2_id], expected_1, tags)
@pytest.mark.developer("needs DEVELOPER=1")
def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind):
@@ -2091,6 +2301,34 @@ def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind):
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# Graph of coin_move events we expect
expected_2 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
# This is ugly, but this wallet deposit is either unspent or used
# in the next channel open
'A': [('wallet', 'deposit', [('withdrawal', 'D'), (None, None)]), ('cid1', 'channel_open', 'channel_close', 'B')],
'1': [('wallet', 'deposit', 'withdrawal', 'D')],
'B': [('external', 'to_them', None, None), ('wallet', 'channel_close', None, None), ('cid1', 'htlc_fulfill', 'to_wallet', 'C')],
'C': [('wallet', 'deposit', None, None)],
'D': [('wallet', 'deposit', None, None), ('cid2', 'channel_open', None, None)]
}
expected_1 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('external', 'htlc_fulfill', 'htlc_fulfill', 'C'), ('cid1', 'delayed_to_us', 'to_wallet', 'E')],
'E': [('wallet', 'deposit', None, None)]
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
chan2_id = first_channel_id(l2, l3)
tags = check_utxos_channel(l2, [channel_id, chan2_id], expected_2)
check_utxos_channel(l1, [channel_id, chan2_id], expected_1, tags)
@pytest.mark.developer("needs DEVELOPER=1")
def test_onchain_their_unilateral_out(node_factory, bitcoind):
@@ -2156,6 +2394,30 @@ def test_onchain_their_unilateral_out(node_factory, bitcoind):
assert account_balance(l2, channel_id) == 0
assert account_balance(l1, channel_id) == 0
# Graph of coin_move events we expect
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
# This is ugly, but this wallet deposit is either unspent or used
# in the next channel open
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'to_wallet', 'C')],
'C': [('wallet', 'deposit', None, None)],
}
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)],
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
tags = check_utxos_channel(l1, [channel_id], expected_1)
check_utxos_channel(l2, [channel_id], expected_2, tags)
def test_listfunds_after_their_unilateral(node_factory, bitcoind):
"""We keep spending info around for their unilateral closes.
@@ -2336,6 +2598,28 @@ def test_onchain_all_dust(node_factory, bitcoind, executor):
assert account_balance(l1, channel_id) == 0
assert account_balance(l2, channel_id) == 0
# Graph of coin_move events we expect
expected_1 = {
'0': [('wallet', 'deposit', 'withdrawal', 'A')],
'A': [('wallet', 'deposit', None, None), ('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('wallet', 'channel_close', None, None), ('cid1', 'htlc_timeout', 'ignored', 'C')],
'C': [('wallet', 'deposit', None, None)],
}
expected_2 = {
'A': [('cid1', 'channel_open', 'channel_close', 'B')],
'B': [('external', 'to_them', None, None), ('external', 'htlc_timeout', None, None)],
}
if anchor_expected():
expected_1['B'].append(('external', 'anchor', None, None))
expected_2['B'].append(('external', 'anchor', None, None))
expected_1['B'].append(('wallet', 'anchor', None, None))
expected_2['B'].append(('wallet', 'anchor', None, None))
tags = check_utxos_channel(l1, [channel_id], expected_1)
check_utxos_channel(l2, [channel_id], expected_2, tags)
@pytest.mark.developer("needs DEVELOPER=1 for dev_fail")
def test_onchain_different_fees(node_factory, bitcoind, executor):

View File

@@ -1101,12 +1101,11 @@ def test_funding_push(node_factory, bitcoind, chainparams):
assert funds['channel_sat'] + push_sat == funds['channel_total_sat']
chanid = first_channel_id(l2, l1)
l1.daemon.wait_for_log('coins account: {}'.format(chanid))
# give the file write a second
time.sleep(1)
channel_mvts = [
{'type': 'chain_mvt', 'credit': 0, 'debit': 20000000, 'tag': 'pushed'},
{'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 16777215000, 'debit': 0, 'tag': 'channel_open'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 20000000, 'tag': 'pushed'},
]
check_coin_moves(l1, chanid, channel_mvts, chainparams)
assert account_balance(l1, chanid) == (amount - push_sat) * 1000

View File

@@ -10,7 +10,7 @@ from pyln.testing.utils import (
wait_for, TailableProc, env
)
from utils import (
check_coin_moves, account_balance, scriptpubkey_addr,
account_balance, scriptpubkey_addr
)
from ephemeral_port_reserve import reserve
from utils import EXPERIMENTAL_FEATURES
@@ -623,64 +623,6 @@ def test_withdraw_misc(node_factory, bitcoind, chainparams):
sync_blockheight(bitcoind, [l1])
assert account_balance(l1, 'wallet') == 0
wallet_moves = [
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1993760000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 6240000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 1993760000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 1993400000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 2000000000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 6600000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 1993400000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 11961240000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 13440000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 11961240000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 11957603000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3637000, 'tag': 'chain_fees'},
]
check_coin_moves(l1, 'wallet', wallet_moves, chainparams)
def test_io_logging(node_factory, executor):
l1 = node_factory.get_node(options={'log-level': 'io'})

View File

@@ -10,7 +10,7 @@ from utils import (
DEPRECATED_APIS, expected_peer_features, expected_node_features,
expected_channel_features, account_balance,
check_coin_moves, first_channel_id, check_coin_moves_idx,
EXPERIMENTAL_FEATURES, EXPERIMENTAL_DUAL_FUND
EXPERIMENTAL_DUAL_FUND
)
import ast
@@ -1885,84 +1885,23 @@ def test_plugin_fail(node_factory):
@pytest.mark.openchannel('v1')
@pytest.mark.openchannel('v2')
def test_coin_movement_notices(node_factory, bitcoind, chainparams):
"""Verify that coin movements are triggered correctly.
"""
"""Verify that channel coin movements are triggered correctly. """
l1_l2_mvts = [
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'channel_open'},
{'type': 'channel_mvt', 'credit': 100001001, 'debit': 0, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 100000000, 'debit': 0, 'tag': 'invoice'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 50000000, 'tag': 'invoice'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 100001000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 100001001, 'tag': 'channel_close'},
]
if chainparams['elements']:
l2_l3_mvts = [
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 4271501, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 945729000, 'tag': 'withdrawal'},
]
l2_wallet_mvts = [
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 991908000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 8092000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 991908000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 945729000, 'debit': 0, 'tag': 'deposit'},
]
elif EXPERIMENTAL_FEATURES:
# option_anchor_outputs
l2_l3_mvts = [
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'},
]
l2_wallet_mvts = [
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
# Could go in either order
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 995433000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 4567000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 995433000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'},
]
else:
l2_l3_mvts = [
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'},
]
l2_wallet_mvts = [
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
# Could go in either order
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 995433000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 4567000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 995433000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'},
]
l2_l3_mvts = [
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'channel_open'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 950000501, 'tag': 'channel_close'},
]
l1, l2, l3 = node_factory.line_graph(3, opts=[
{'may_reconnect': True},
@@ -1970,30 +1909,6 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams):
{'may_reconnect': True},
], wait_for_announce=True)
# Special case for dual-funded channel opens
if l2.config('experimental-dual-fund'):
# option_anchor_outputs
l2_l3_mvts = [
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'channel_mvt', 'credit': 0, 'debit': 100000000, 'tag': 'routed'},
{'type': 'channel_mvt', 'credit': 50000501, 'debit': 0, 'tag': 'routed'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 2520501, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 947480000, 'tag': 'withdrawal'},
]
l2_wallet_mvts = [
{'type': 'chain_mvt', 'credit': 2000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
# Could go in either order
[
{'type': 'chain_mvt', 'credit': 0, 'debit': 995410000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
],
{'type': 'chain_mvt', 'credit': 0, 'debit': 4590000, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 995410000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 100001000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 947480000, 'debit': 0, 'tag': 'deposit'},
]
bitcoind.generate_block(5)
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4)
amount = 10**8
@@ -2071,7 +1986,6 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams):
# Verify we recorded all the movements we expect
check_coin_moves(l2, chanid_1, l1_l2_mvts, chainparams)
check_coin_moves(l2, chanid_3, l2_l3_mvts, chainparams)
check_coin_moves(l2, 'wallet', l2_wallet_mvts, chainparams)
check_coin_moves_idx(l2)

View File

@@ -862,18 +862,14 @@ def test_sign_and_send_psbt(node_factory, bitcoind, chainparams):
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 1000000000, 'debit': 0, 'tag': 'deposit'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 3000000000 + int(out_1_ms), 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000 - int(out_1_ms), 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'spend_track'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 4000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 0, 'tag': 'chain_fees'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
{'type': 'chain_mvt', 'credit': 0, 'debit': 1000000000, 'tag': 'withdrawal'},
]
check_coin_moves(l1, 'wallet', wallet_coin_mvts, chainparams)

View File

@@ -8,6 +8,10 @@ EXPERIMENTAL_FEATURES = env("EXPERIMENTAL_FEATURES", "0") == "1"
COMPAT = env("COMPAT", "1") == "1"
def anchor_expected():
return EXPERIMENTAL_FEATURES or EXPERIMENTAL_DUAL_FUND
def hex_bits(features):
# We always to full bytes
flen = (max(features + [0]) + 7) // 8 * 8
@@ -149,6 +153,155 @@ def account_balance(n, account_id):
return m_sum
def extract_utxos(moves):
utxos = {}
for m in moves:
if 'utxo_txid' not in m:
continue
txid = m['utxo_txid']
if txid not in utxos:
utxos[txid] = []
if 'txid' not in m:
utxos[txid].append([m, None])
else:
evs = utxos[txid]
# it's a withdrawal, find the deposit and add to the pair
for ev in evs:
if ev[0]['vout'] == m['vout']:
ev[1] = m
assert ev[0]['output_value'] == m['output_value']
break
return utxos
def print_utxos(utxos):
for k, us in utxos.items():
print(k)
for u in us:
if u[1]:
print('\t', u[0]['account_id'], u[0]['tag'], u[1]['tag'], u[1]['txid'])
else:
print('\t', u[0]['account_id'], u[0]['tag'], None, None)
def utxos_for_channel(utxoset, channel_id):
relevant_txids = []
chan_utxos = {}
def _add_relevant(txid, utxo):
if txid not in chan_utxos:
chan_utxos[txid] = []
chan_utxos[txid].append(utxo)
for txid, utxo_list in utxoset.items():
for utxo in utxo_list:
if utxo[0]['account_id'] == channel_id:
_add_relevant(txid, utxo)
relevant_txids.append(txid)
if utxo[1]:
relevant_txids.append(utxo[1]['txid'])
elif txid in relevant_txids:
_add_relevant(txid, utxo)
if utxo[1]:
relevant_txids.append(utxo[1]['txid'])
# if they're not well ordered, we'll leave some txids out
for txid in relevant_txids:
if txid not in chan_utxos:
chan_utxos[txid] = utxoset[txid]
return chan_utxos
def matchup_events(u_set, evs, chans, tag_list):
assert len(u_set) == len(evs) and len(u_set) > 0
txid = u_set[0][0]['utxo_txid']
for ev in evs:
found = False
for u in u_set:
# We use 'cid' as a placeholder for the channel id, since it's
# dyanmic, but we need to sub it in. 'chans' is a list of cids,
# which are mapped to `cid` tags' suffixes. eg. 'cid1' is the
# first cid in the chans list
if ev[0][:3] == 'cid':
idx = int(ev[0][3:])
acct = chans[idx - 1]
else:
acct = ev[0]
if u[0]['account_id'] != acct or u[0]['tag'] != ev[1]:
continue
if ev[2] is None:
assert u[1] is None
found = True
u_set.remove(u)
break
# ugly hack to annotate two possible futures for a utxo
if type(ev[2]) is list:
tag = u[1]['tag'] if u[1] else u[1]
assert tag in [x[0] for x in ev[2]]
if not u[1]:
found = True
u_set.remove(u)
break
for x in ev[2]:
if x[0] == u[1]['tag'] and u[1]['tag'] != 'to_miner':
# Save the 'spent to' txid in the tag-list
tag_list[x[1]] = u[1]['txid']
else:
assert ev[2] == u[1]['tag']
# Save the 'spent to' txid in the tag-list
if u[1]['tag'] != 'to_miner':
tag_list[ev[3]] = u[1]['txid']
found = True
u_set.remove(u)
assert found
# Verify we used them all up
assert len(u_set) == 0
return txid
def check_utxos_channel(n, chans, expected, exp_tag_list=None, filter_channel=None):
tag_list = {}
moves = n.rpc.call('listcoinmoves_plugin')['coin_moves']
utxos = extract_utxos(moves)
if filter_channel:
utxos = utxos_for_channel(utxos, filter_channel)
for tag, evs in expected.items():
if tag not in tag_list:
u_set = list(utxos.values())[0]
elif tag in tag_list:
u_set = utxos[tag_list[tag]]
txid = matchup_events(u_set, evs, chans, tag_list)
if tag not in tag_list:
tag_list[tag] = txid
# Remove checked set from utxos
del utxos[txid]
# Verify that we went through all of the utxos
assert len(utxos) == 0
# Verify that the expected tags match the found tags
if exp_tag_list:
for tag, txid in tag_list.items():
if tag in exp_tag_list:
assert exp_tag_list[tag] == txid
return tag_list
def first_channel_id(n1, n2):
return only_one(only_one(n1.rpc.listpeers(n2.info['id'])['peers'])['channels'])['channel_id']

View File

@@ -441,13 +441,13 @@ struct channel_coin_mvt *new_channel_mvt_routed_hout(const tal_t *ctx UNNEEDED,
struct htlc_out *hout UNNEEDED,
struct channel *channel UNNEEDED)
{ fprintf(stderr, "new_channel_mvt_routed_hout called!\n"); abort(); }
/* Generated stub for new_coin_deposit_sat */
struct chain_coin_mvt *new_coin_deposit_sat(const tal_t *ctx UNNEEDED,
const char *account_name UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED)
{ fprintf(stderr, "new_coin_deposit_sat called!\n"); abort(); }
/* Generated stub for new_coin_wallet_deposit */
struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED,
const struct bitcoin_outpoint *outpoint UNNEEDED,
u32 blockheight UNNEEDED,
struct amount_sat amount UNNEEDED,
enum mvt_tag tag UNNEEDED)
{ fprintf(stderr, "new_coin_wallet_deposit called!\n"); abort(); }
/* Generated stub for notify_chain_mvt */
void notify_chain_mvt(struct lightningd *ld UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED)
{ fprintf(stderr, "notify_chain_mvt called!\n"); abort(); }

View File

@@ -2253,10 +2253,10 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct wally_tx *wtx,
/* We only record final ledger movements */
if (blockheight) {
mvt = new_coin_deposit_sat(utxo, "wallet",
&utxo->outpoint,
blockheight ? *blockheight : 0,
utxo->amount);
mvt = new_coin_wallet_deposit(tmpctx, &utxo->outpoint,
*blockheight,
utxo->amount,
DEPOSIT);
notify_chain_mvt(w->ld, mvt);
}