From a55b58d0d505b7498cb6c9c841d0d65a75cef4d9 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 20 Jun 2017 15:29:03 +0930 Subject: [PATCH] lightningd: track balance in way which matches channeld. Currently it's fairly ad-hoc, but we need to tell it to channeld when it restarts, so we define it as the non-HTLC balance. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 16 +++++++++------- lightningd/peer_control.h | 2 +- lightningd/peer_htlcs.c | 33 +++++++++++++++++++++++---------- tests/test_lightningd.py | 32 ++++++++++++++++++++++++++++++-- 4 files changed, 63 insertions(+), 20 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 8f12bd665..33da80fd1 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -587,11 +587,11 @@ static void json_getpeers(struct command *cmd, if (p->scid) json_add_short_channel_id(response, "channel", p->scid); if (p->balance) { - json_add_u64(response, "msatoshi_to_us", - p->balance[LOCAL]); - json_add_u64(response, "msatoshi_to_them", - p->balance[REMOTE]); + json_add_u64(response, "msatoshi_to_us", *p->balance); + json_add_u64(response, "msatoshi_total", + p->funding_satoshi * 1000); } + if (leveltok) { info.response = response; json_array_start(response, "log"); @@ -982,9 +982,11 @@ static void peer_start_channeld(struct peer *peer, enum peer_state oldstate, log_debug(peer->log, "Waiting for HSM file descriptor"); /* Now we can consider balance set. */ - peer->balance = tal_arr(peer, u64, NUM_SIDES); - peer->balance[peer->funder] = peer->funding_satoshi * 1000 - peer->push_msat; - peer->balance[!peer->funder] = peer->push_msat; + peer->balance = tal(peer, u64); + if (peer->funder == LOCAL) + *peer->balance = peer->funding_satoshi * 1000 - peer->push_msat; + else + *peer->balance = peer->push_msat; peer_set_condition(peer, oldstate, GETTING_HSMFD); diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 1f7ed0c24..408bbf8c2 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -69,7 +69,7 @@ struct peer { u16 funding_outnum; u64 funding_satoshi, push_msat; - /* Channel balance (LOCAL and REMOTE); if we have one. */ + /* Amount going to us, not counting unfinished HTLCs; if we have one. */ u64 *balance; /* Keys for channel. */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index da82789d3..fd5e67f20 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -175,8 +175,8 @@ static void fulfill_htlc(struct htlc_in *hin, const struct preimage *preimage) { u8 *msg; - hin->key.peer->balance[LOCAL] += hin->msatoshi; - hin->key.peer->balance[REMOTE] -= hin->msatoshi; + hin->preimage = tal_dup(hin, struct preimage, preimage); + htlc_in_check(hin, __func__); /* FIXME: fail the peer if it doesn't tell us that htlc fulfill is * committed before deadline. @@ -628,10 +628,6 @@ static bool peer_fulfilled_our_htlc(struct peer *peer, /* FIXME: Save to db */ - /* They fulfilled our HTLC. Credit them, forward immediately. */ - peer->balance[REMOTE] += hout->msatoshi; - peer->balance[LOCAL] -= hout->msatoshi; - if (hout->in) fulfill_htlc(hout->in, &fulfilled->payment_preimage); else @@ -665,16 +661,30 @@ static bool peer_failed_our_htlc(struct peer *peer, static void remove_htlc_in(struct peer *peer, struct htlc_in *hin) { htlc_in_check(hin, __func__); - log_debug(peer->log, "Removing in HTLC %"PRIu64" state %s", - hin->key.id, htlc_state_name(hin->hstate)); + assert(hin->failuremsg || hin->preimage); + + log_debug(peer->log, "Removing in HTLC %"PRIu64" state %s %s", + hin->key.id, htlc_state_name(hin->hstate), + hin->failuremsg ? "FAILED" : "FULFILLED"); + + /* If we fulfilled their HTLC, credit us. */ + if (hin->preimage) { + log_debug(peer->log, "Balance %"PRIu64" -> %"PRIu64, + *peer->balance, + *peer->balance + hin->msatoshi); + *peer->balance += hin->msatoshi; + } + tal_free(hin); } static void remove_htlc_out(struct peer *peer, struct htlc_out *hout) { htlc_out_check(hout, __func__); - log_debug(peer->log, "Removing out HTLC %"PRIu64" state %s", - hout->key.id, htlc_state_name(hout->hstate)); + assert(hout->failuremsg || hout->preimage); + log_debug(peer->log, "Removing out HTLC %"PRIu64" state %s %s", + hout->key.id, htlc_state_name(hout->hstate), + hout->failuremsg ? "FAILED" : "FULFILLED"); /* If it's failed, now we can forward since it's completely locked-in */ if (hout->failuremsg) { @@ -687,6 +697,9 @@ static void remove_htlc_out(struct peer *peer, struct htlc_out *hout) } else { payment_failed(peer->ld, hout); } + } else { + /* We paid for this HTLC, so deduct balance. */ + *peer->balance -= hout->msatoshi; } tal_free(hout); diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 8f47c180f..8d44dfa93 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -204,6 +204,18 @@ class LightningDTests(BaseLightningDTests): assert p1['owner'] == 'lightningd_gossip' assert p2['owner'] == 'lightningd_gossip' + def test_balance(self): + l1,l2 = self.connect() + + self.fund_channel(l1, l2, 10**6) + + p1 = l1.rpc.getpeer(l2.info['id'], 'info') + p2 = l2.rpc.getpeer(l1.info['id'], 'info') + assert p1['msatoshi_to_us'] == 10**6 * 1000 + assert p1['msatoshi_total'] == 10**6 * 1000 + assert p2['msatoshi_to_us'] == 0 + assert p2['msatoshi_total'] == 10**6 * 1000 + def test_sendpay(self): l1,l2 = self.connect() @@ -246,10 +258,27 @@ class LightningDTests(BaseLightningDTests): self.assertRaises(ValueError, l1.rpc.sendpay, to_json([rs]), rhash) assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == False + # FIXME: test paying via another node, should fail to pay twice. + p1 = l1.rpc.getpeer(l2.info['id'], 'info') + p2 = l2.rpc.getpeer(l1.info['id'], 'info') + assert p1['msatoshi_to_us'] == 10**6 * 1000 + assert p1['msatoshi_total'] == 10**6 * 1000 + assert p2['msatoshi_to_us'] == 0 + assert p2['msatoshi_total'] == 10**6 * 1000 + # This works. l1.rpc.sendpay(to_json([routestep]), rhash) assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True - + + # Balances should reflect it. + time.sleep(1) + p1 = l1.rpc.getpeer(l2.info['id'], 'info') + p2 = l2.rpc.getpeer(l1.info['id'], 'info') + assert p1['msatoshi_to_us'] == 10**6 * 1000 - amt + assert p1['msatoshi_total'] == 10**6 * 1000 + assert p2['msatoshi_to_us'] == amt + assert p2['msatoshi_total'] == 10**6 * 1000 + # Repeat will "succeed", but won't actually send anything (duplicate) assert not l1.daemon.is_in_log('... succeeded') l1.rpc.sendpay(to_json([routestep]), rhash) @@ -263,7 +292,6 @@ class LightningDTests(BaseLightningDTests): l1.rpc.sendpay(to_json([routestep]), rhash) assert l2.rpc.listinvoice('testpayment3')[0]['complete'] == True - # FIXME: test paying via another node, should fail to pay twice. def test_gossip_jsonrpc(self): l1,l2 = self.connect()