mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
onchaind: extract payment_preimage from onchain HTLC redemption.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
f96a04e2a6
commit
cbe72b658e
@ -497,9 +497,78 @@ static void unwatch_tx(const struct bitcoin_tx *tx)
|
||||
}
|
||||
|
||||
static void handle_htlc_onchain_fulfill(struct tracked_output *out,
|
||||
const struct bitcoin_tx *tx)
|
||||
const struct bitcoin_tx *tx)
|
||||
{
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR, "FIXME: %s", __func__);
|
||||
const u8 *witness_preimage;
|
||||
struct preimage preimage;
|
||||
struct sha256 sha;
|
||||
struct ripemd160 ripemd;
|
||||
|
||||
/* Our HTLC, they filled (must be a HTLC-success tx). */
|
||||
if (out->tx_type == THEIR_UNILATERAL) {
|
||||
/* BOLT #3:
|
||||
*
|
||||
* ## HTLC-Timeout and HTLC-Success Transactions
|
||||
*
|
||||
* ... `txin[0]` witness stack: `0 <remotesig> <localsig>
|
||||
* <payment_preimage>` for HTLC-Success
|
||||
*/
|
||||
if (tal_count(tx->input[0].witness) != 5) /* +1 for wscript */
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"%s/%s spent with weird witness %zu",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type),
|
||||
tal_count(tx->input[0].witness));
|
||||
|
||||
witness_preimage = tx->input[0].witness[3];
|
||||
} else if (out->tx_type == OUR_UNILATERAL) {
|
||||
/* BOLT #3:
|
||||
*
|
||||
* The remote node can redeem the HTLC with the witness:
|
||||
*
|
||||
* <remotesig> <payment_preimage>
|
||||
*/
|
||||
if (tal_count(tx->input[0].witness) != 3) /* +1 for wscript */
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"%s/%s spent with weird witness %zu",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type),
|
||||
tal_count(tx->input[0].witness));
|
||||
|
||||
witness_preimage = tx->input[0].witness[1];
|
||||
} else
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"onchain_fulfill for %s/%s?",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type));
|
||||
|
||||
if (tal_len(witness_preimage) != sizeof(preimage))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"%s/%s spent with bad witness length %zu",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type),
|
||||
tal_len(witness_preimage));
|
||||
memcpy(&preimage, witness_preimage, sizeof(preimage));
|
||||
sha256(&sha, &preimage, sizeof(preimage));
|
||||
ripemd160(&ripemd, &sha, sizeof(sha));
|
||||
|
||||
if (!structeq(&ripemd, &out->htlc->ripemd))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"%s/%s spent with bad preimage %s (ripemd not %s)",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type),
|
||||
type_to_string(trc, struct preimage, &preimage),
|
||||
type_to_string(trc, struct ripemd160,
|
||||
&out->htlc->ripemd));
|
||||
|
||||
/* Tell master we found a preimage. */
|
||||
status_trace("%s/%s gave us preimage %s",
|
||||
tx_type_name(out->tx_type),
|
||||
output_type_name(out->output_type),
|
||||
type_to_string(trc, struct preimage, &preimage));
|
||||
wire_sync_write(REQ_FD,
|
||||
take(towire_onchain_extracted_preimage(NULL,
|
||||
&preimage)));
|
||||
}
|
||||
|
||||
static void resolve_htlc_tx(struct tracked_output ***outs,
|
||||
|
@ -7,6 +7,7 @@ from lightning import LightningRpc
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import queue
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
@ -472,8 +473,14 @@ class LightningDTests(BaseLightningDTests):
|
||||
route = l1.rpc.getroute(l3.info['id'], 10**8, 1)["route"]
|
||||
assert len(route) == 2
|
||||
|
||||
q = queue.Queue()
|
||||
|
||||
def try_pay():
|
||||
l1.rpc.sendpay(to_json(route), rhash, async=False)
|
||||
try:
|
||||
l1.rpc.sendpay(to_json(route), rhash, async=False)
|
||||
q.put(None)
|
||||
except Exception as err:
|
||||
q.put(err)
|
||||
|
||||
t = threading.Thread(target=try_pay)
|
||||
t.daemon = True
|
||||
@ -490,11 +497,16 @@ class LightningDTests(BaseLightningDTests):
|
||||
l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/THEIR_HTLC by OUR_HTLC_SUCCESS_TX .* in 0 blocks')
|
||||
l2.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
||||
# Payment should succeed.
|
||||
l1.bitcoin.rpc.generate(1)
|
||||
# FIXME: l1 should get preimage off the chain!
|
||||
l1.daemon.wait_for_log('FIXME: handle_htlc_onchain_fulfill')
|
||||
l1.has_failed()
|
||||
|
||||
l1.daemon.wait_for_log('THEIR_UNILATERAL/OUR_HTLC gave us preimage')
|
||||
err = q.get(timeout = 10)
|
||||
if err:
|
||||
print("Got err from sendpay thread")
|
||||
raise err
|
||||
t.join(timeout=1)
|
||||
assert not t.isAlive()
|
||||
|
||||
# After 4 more blocks, l2 can spend to-us.
|
||||
l1.bitcoin.rpc.generate(4)
|
||||
l2.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET .* to resolve OUR_UNILATERAL/DELAYED_OUTPUT_TO_US')
|
||||
@ -509,8 +521,6 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.bitcoin.rpc.generate(100)
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
# FIXME: kill thread?
|
||||
|
||||
def test_permfail_new_commit(self):
|
||||
# Test case where we have two possible commits: it will use new one.
|
||||
disconnects = ['-WIRE_REVOKE_AND_ACK', 'permfail']
|
||||
@ -532,8 +542,6 @@ class LightningDTests(BaseLightningDTests):
|
||||
l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/THEIR_HTLC by THEIR_HTLC_TIMEOUT_TO_THEM \\(IGNORING\\) in 5 blocks')
|
||||
l1.daemon.wait_for_log('Propose handling THEIR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US (.*) in 5 blocks')
|
||||
|
||||
# FIXME: Implement FULFILL!
|
||||
|
||||
# OK, time out HTLC.
|
||||
bitcoind.rpc.generate(5)
|
||||
l1.daemon.wait_for_log('sendrawtx exit 0')
|
||||
@ -541,8 +549,6 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.daemon.wait_for_log('Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US')
|
||||
l2.daemon.wait_for_log('Ignoring output.*: OUR_UNILATERAL/THEIR_HTLC')
|
||||
|
||||
# FIXME: This doesn't work :(
|
||||
# FIXME: sendpay command should time out!
|
||||
t.cancel()
|
||||
|
||||
# Now, 100 blocks it should be done.
|
||||
@ -570,28 +576,25 @@ class LightningDTests(BaseLightningDTests):
|
||||
l2.daemon.wait_for_log('-> ONCHAIND_OUR_UNILATERAL')
|
||||
l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/THEIR_HTLC by THEIR_HTLC_TIMEOUT_TO_THEM \\(IGNORING\\) in 5 blocks')
|
||||
l1.daemon.wait_for_log('Propose handling THEIR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US (.*) in 5 blocks')
|
||||
|
||||
# OK, time out HTLC.
|
||||
bitcoind.rpc.generate(5)
|
||||
l1.daemon.wait_for_log('FIXME: handle_htlc_onchain_fulfill')
|
||||
l1.has_failed()
|
||||
|
||||
# l1.daemon.wait_for_log('sendrawtx exit 0')
|
||||
bitcoind.rpc.generate(1)
|
||||
# l1.daemon.wait_for_log('Resolved THEIR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US')
|
||||
# l2 then gets preimage, uses it instead of ignoring
|
||||
l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/THEIR_HTLC by OUR_HTLC_SUCCESS_TX .* in 0 blocks')
|
||||
l2.daemon.wait_for_log('sendrawtx exit 0')
|
||||
bitcoind.rpc.generate(1)
|
||||
|
||||
# OK, l1 sees l2 fulfill htlc.
|
||||
l1.daemon.wait_for_log('THEIR_UNILATERAL/OUR_HTLC gave us preimage')
|
||||
l2.daemon.wait_for_log('Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* in 6 blocks')
|
||||
bitcoind.rpc.generate(6)
|
||||
l2.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
||||
# FIXME: This doesn't work :(
|
||||
# FIXME: sendpay command should time out!
|
||||
l2.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
||||
t.cancel()
|
||||
|
||||
# Now, 100 blocks it should be done.
|
||||
bitcoind.rpc.generate(100)
|
||||
# l1.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
bitcoind.rpc.generate(94)
|
||||
l1.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
assert not l2.daemon.is_in_log('onchaind complete, forgetting peer')
|
||||
bitcoind.rpc.generate(6)
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
def test_permfail_htlc_out(self):
|
||||
@ -612,27 +615,36 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.daemon.wait_for_log('Their unilateral tx, old commit point')
|
||||
l1.daemon.wait_for_log('-> ONCHAIND_THEIR_UNILATERAL')
|
||||
l2.daemon.wait_for_log('-> ONCHAIND_OUR_UNILATERAL')
|
||||
l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US \\(.*\\) in 5 blocks')
|
||||
l2.daemon.wait_for_logs(['Propose handling OUR_UNILATERAL/OUR_HTLC by OUR_HTLC_TIMEOUT_TO_US \\(.*\\) in 5 blocks',
|
||||
'Propose handling OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* in 6 blocks'])
|
||||
|
||||
l1.daemon.wait_for_log('Propose handling THEIR_UNILATERAL/THEIR_HTLC by THEIR_HTLC_TIMEOUT_TO_THEM \\(IGNORING\\) in 5 blocks')
|
||||
|
||||
# OK, time out HTLC.
|
||||
bitcoind.rpc.generate(5)
|
||||
|
||||
l2.daemon.wait_for_log('FIXME: handle_htlc_onchain_fulfill')
|
||||
l2.has_failed()
|
||||
|
||||
bitcoind.rpc.generate(1)
|
||||
# l1 then gets preimage, uses it instead of ignoring
|
||||
l1.daemon.wait_for_log('Propose handling THEIR_UNILATERAL/THEIR_HTLC by THEIR_HTLC_FULFILL_TO_US .* in 0 blocks')
|
||||
# l2.daemon.wait_for_log('Resolved OUR_UNILATERAL/OUR_HTLC by our proposal OUR_HTLC_TIMEOUT_TO_US')
|
||||
l1.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
||||
# FIXME: This doesn't work :(
|
||||
# FIXME: sendpay command should time out!
|
||||
# l2 sees l1 fulfill tx.
|
||||
bitcoind.rpc.generate(1)
|
||||
|
||||
l2.daemon.wait_for_log('OUR_UNILATERAL/OUR_HTLC gave us preimage')
|
||||
t.cancel()
|
||||
|
||||
# Now, 100 blocks it should be done.
|
||||
bitcoind.rpc.generate(100)
|
||||
# l2 can send OUR_DELAYED_RETURN_TO_WALLET after 5 more blocks.
|
||||
bitcoind.rpc.generate(5)
|
||||
l2.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET .* to resolve OUR_UNILATERAL/DELAYED_OUTPUT_TO_US')
|
||||
l2.daemon.wait_for_log('sendrawtx exit 0')
|
||||
|
||||
# Now, 100 blocks they should be done.
|
||||
bitcoind.rpc.generate(93)
|
||||
assert not l1.daemon.is_in_log('onchaind complete, forgetting peer')
|
||||
assert not l2.daemon.is_in_log('onchaind complete, forgetting peer')
|
||||
bitcoind.rpc.generate(1)
|
||||
l1.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
# l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
assert not l2.daemon.is_in_log('onchaind complete, forgetting peer')
|
||||
bitcoind.rpc.generate(5)
|
||||
assert not l2.daemon.is_in_log('onchaind complete, forgetting peer')
|
||||
bitcoind.rpc.generate(1)
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
def test_gossip_jsonrpc(self):
|
||||
l1,l2 = self.connect()
|
||||
|
Loading…
Reference in New Issue
Block a user