-
Notifications
You must be signed in to change notification settings - Fork 912
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
coin moves #3614
coin moves #3614
Conversation
a6ad47c
to
9fe8217
Compare
3b74b16
to
5a99ad9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I took a careful look since this is a brand new API. Though to be fair I didn't review all the calling paths in sufficient detail (there are many!).
So mainly minor (though, sorry, invasive) proposals. I really like this idea of a comprehensive API for coin moves, though, and it'd be great to get it in 0.8.2.
lightningd/htlc_end.c
Outdated
@@ -292,6 +292,9 @@ struct htlc_out *new_htlc_out(const tal_t *ctx, | |||
hout->am_origin = am_origin; | |||
if (am_origin) | |||
hout->partid = partid; | |||
else | |||
hout->partid = 0; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I disagree with this: partid should never be accessed if !am_origin. Your API should reflect this, too: don't output partid at all if there's no corresponding payment.
wallet/db.c
Outdated
@@ -596,6 +596,9 @@ static struct migration dbmigrations[] = { | |||
* Turn anything in transition into a WIRE_TEMPORARY_NODE_FAILURE. */ | |||
{SQL("ALTER TABLE channel_htlcs ADD localfailmsg BLOB;"), NULL}, | |||
{SQL("UPDATE channel_htlcs SET localfailmsg=decode('2002', 'hex') WHERE malformed_onion != 0 AND direction = 1;"), NULL}, | |||
/* For incoming HTLCs, we now keep track of whether or not we provided | |||
* the preimage for it, or not. */ | |||
{SQL("ALTER TABLE channel_htlcs ADD we_filled INT;"), NULL}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think INTEGER
is preferred to INT in SQL, but I note we use both so maybe I'm wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sqlite3
is very lax with regards to its type-system, they're effectively aliases. In postgres
this would be an s32
, so we need to make sure that doesn't break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has been addressed. @rustyrussell was there another comment that was overlooked?
db_bind_int(stmt, 5, 1); | ||
else | ||
db_bind_null(stmt, 5); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer 0/1 rather than NULL/1? Make we_filled a bool *
and then do:
/* Set for htlc_in */
if (we_filled)
db_bind_int(stmt, 5, *we_filled);
else
db_bind_null(stmt, 5);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has been updated!
wallet/wallet.c
Outdated
@@ -1867,6 +1874,8 @@ static bool wallet_stmt2htlc_in(struct channel *channel, | |||
} | |||
#endif | |||
|
|||
in->we_filled = !db_column_is_null(stmt, 13); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... then fix this.
common/coin_mvt.c
Outdated
return mvt_units[unit]; | ||
} | ||
|
||
static u64 mvt_count = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This surely belongs in a db somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated in 1cbd8cf
wire/towire.c
Outdated
if (tx->input_amounts[i]) | ||
towire_amount_sat(pptr, *tx->input_amounts[i]); | ||
else | ||
towire_amount_sat(pptr, AMOUNT_SAT(0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can have zero-valued inputs though. I think we should do all or nothing: either assert() that all input_amounts are set if tx->input_amounts is non-NULL, or don't include any if we don't have all of them.
onchaind/onchaind.c
Outdated
@@ -81,6 +81,9 @@ static struct amount_msat our_msat; | |||
/* Does option_static_remotekey apply to this commitment tx? */ | |||
bool option_static_remotekey; | |||
|
|||
/* We don't want to log any downstream coin moves if this is a known replay */ | |||
bool open_is_replay; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be "initial_tx_is_replay" perhaps? open is confusing here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated in 6c8d7ef to be pass through instead of a global (having a global didn't get me much of anything).
lightningd/coin_mvts.c
Outdated
/* This could be written more concisely as | ||
* count = ++ld->coin_moves_count; | ||
* however I believe that's contra-code conventions */ | ||
ld->coin_moves_count++; | ||
count = ld->coin_moves_count; | ||
db_set_intvar(ld->wallet->db, "coin_moves_count", | ||
count); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I have no problem with ++
prefix when necessary (it's just that C++ programmers have been trained to use it by default). I'd just do this all as a one-liner...
lightningd/coin_mvts.c
Outdated
{ | ||
s64 count; | ||
bool db_in_tx; | ||
db_in_tx = db_in_transaction(ld->wallet->db); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about what callchains do this outside a transaction? That has a weird smell to it...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm I can't seem to find them. I'm going to push the non-switched version up and see if Travis can find them for me :)
lightningd/coin_mvts.c
Outdated
"coin_moves_count", -1); | ||
db_commit_transaction(ld->wallet->db); | ||
if (count == -1) | ||
fatal("Something went wrong attmepting to fetch" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo, but maybe it's not? :)
Didn't get too far with
It results in
In this configuration
|
The other configuration is the following:
Surprisingly little errors despite the chain being different. It also produces the above
|
9a19635
to
9a6f307
Compare
20dfc7b
to
4b47525
Compare
@cdecker tests seem to be a bit evened out now, if it's not too much trouble would you mind re-checking the liquid + alt-arch tests you ran? |
a00f221
to
75e5a8e
Compare
Ignored outputs don't end up in the same 'resolved' pathway as other tracked outputs do, so we mark them as moved when proposed/broadcast instead of when resolved (since they'll never flow through as resolved)
62e7599
to
0585717
Compare
For cheats, we do a little bit of weird accounting. First we 'update' our on-ledger balance to be the entirety of the channel's balance. Then, as outputs get resolved, we record the fees and outputs as withdrawals from this amount. It's possible that they might successfully 'cheat', in which case we record those as 'penalty' but debits (not credits).
We record htlcs when they're fulfilled as 'withdrawals' that are onchain. This should make use of the payment_hash that we stashed. Additionally, if an htlc spend comes through that's not ours, it's probably them resolving our attempted cheat; we should allow it to proceed without bombing, and just do our accounting as necessary. It'll all come out in the wash.
Fixes the tx type annotation in a few places.
For every withdrawal transaction emitted, we record each of the outputs plus the fees paid for this transaction.
If we don't save to disk, if the node restarts we'll lose them all and the resulting balance check at the end will be incorrect.
Mostly we update existing tests to account for channel balances. In a few places, new tests were needed as there wasn't an existing pathway that tested the chain-fees for a few penalty cases
Check that we account for push_msat and wallet withdrawal/deposits correctly
On node start we replay onchaind's transactions from the database/from our loaded htlc table. To keep things tidy, we shouldn't notify the ledger about these, so we wrap pretty much everything in a flag that tells us whether or not this is a replay. There's a very small corner case where dust transactions will get missed if the node crashes after the htlc has been added to the database but before we've successfully notified onchaind about it. Notably, most of the obtrusive updates to onchaind wrappings are due to the fact that we record dust (ignored outputs) before we receive confirmation of its confirmation.
Previously we were annotating every movement with the blockheight of lightningd at notification time. Which is lossy in terms of info, and won't be helpful for reorg reconciliation. Here we switch over to logging chain moves iff they've been confirmed. Next PR will fix this up for withdrawals, which are currently tagged with a blockheight of zero, since we log on successful send.
This moves the notification for our coin spends from when it's successfully submited to the mempool to when they're confirmed in a block. We also add an 'informational' notice tagged as `spend_track` which can be used to track which transaction a wallet output was spent in.
Changelog-Added: Plugins: new notification type, `coin_movement`, which tracks all fund movements for a node
Should make it easier to track when coin moves in the plugin are disjoint from what c-lightning says it's broadcast already.
It's possible for our peer to publish a commitment tx that has already updated our balance for an htlc before we've completed removing it from our commitment tx (aka before we've updated our balance). This used to crash, now we just update our balance (and the channel balance logs!) and keep going. If they've removed anything from our balance, we'll end up counting it as chain_fees below. Not ideal but fine... probably.
Wrap up more logic internally to the method call for htlcs. Also, don't touch part id if we're not the 'origin' Suggested-By: @rustyrussell
note that 'null' 'we_fulfilled's are going to be legacy from this release forward.
Canonicalize the signature for the 'tag-type' of coin moves by unique constructor/method calls. Suggested-By: @rustyrussell
Updates the unit of account to be the chain_id, which is the BIP173 name of the chain that the coins moved on. Suggested-By: @rustyrussell
These guys take a while to run, so let's just skip them on the valgrind/slow-machine combos :/
0585717
to
bc80300
Compare
Ack bc80300 |
This PR adds a new notification type,
coin_movement
to c-ligthtning. By recording these notifications one may construct a canonical ledger for coin movements through a c-lightning node.Cribbed from the PLUGINS.md update that this PR entails:
coin_movement
A notification for topic
coin_movement
is sent to record themovement of coins. It is only triggered by finalized ledger updates,
i.e. only definitively resolved HTLCs or confirmed bitcoin transactions.
version
indicates which version of the coin movement data struct thisnotification adheres to.
node_id
specifies the node issuing the coin movement.movement_idx
is an increment-only counter for coin moves emitted by this node.type
marks the underlying mechanism which moved these coins. There are two'types' of
coin_movements
:channel_mvt
s, which occur as a result of htlcs being resolved and,chain_mvt
s, which occur as a result of bitcoin txs being mined.account_id
is the name of this account. The node's wallet is named 'wallet',all channel funds' account are the channel id.
txid
is the transaction id of the bitcoin transaction that triggered thisledger event.
utxo_txid
andvout
identify the bitcoin output which triggeredthis notification. (
chain_mvt
only) In most cases, theutxo_txid
will be thesame as the
txid
, except forspend_track
notficiations. Notifications taggedchain_fees
andjournal_entry
do not have autxo_txid
as they're notrepresented in the utxo set.
payment_hash
is the hash of the preimage used to move this payment. Onlypresent for HTLC mediated moves (both
chain_mvt
andchannel_mvt
)A
chain_mvt
will have apayment_hash
iff it's recording an htlc that wasfulfilled onchain.
part_id
is an identifier for parts of a multi-part payment. useful foraggregating payments for an invoice or to indicate why a payment hash appears
multiple times.
channel_mvt
onlycredit
anddebit
are millisatoshi denominated amounts of the fund movement. A'credit' is funds deposited into an account; a
debit
is funds withdrawn.tag
is a movement descriptor. Current tags are as follows:deposit
: funds depositedwithdrawal
: funds withdrawnchain_fees
: funds paid for onchain fees.chain_mvt
onlypenalty
: funds paid or gained from a penalty tx.chain_mvt
onlyinvoice
: funds paid to or recieved from an invoice.channel_mvt
onlyrouted
: funds routed through this node.channel_mvt
onlyjournal_entry
: a balance reconciliation event, typically triggeredby a penalty tx onchain.
chain_mvt
onlyonchain_htlc
: funds moved via an htlc onchain.chain_mvt
onlypushed
: funds pushed to peer.channel_mvt
only.spend_track
: informational notification about a wallet utxo spend.chain_mvt
only.blockheight
is the block the txid is included in.chain_mvt
only. In thecase that an output is considered dust, c-lightning does not track its return to
our wallet. In those cases, the blockheight will be
null
, as they're recordedbefore confirmation.
The
timestamp
is seconds since Unix epoch of the node's machine timeat the time lightningd broadcasts the notification.
unit_of_account
is the 'currency' this coin movememnt is denominated in.Resolves #3588