mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
onchain: implement penalty transaction.
Fixes: #242 Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
d640dc3a6f
commit
1ac9e0b532
@ -45,6 +45,12 @@ static struct pubkey our_wallet_pubkey;
|
||||
/* Private keys for spending HTLC outputs via HTLC txs, and directly. */
|
||||
static struct privkey delayed_payment_privkey, payment_privkey;
|
||||
|
||||
/* Private keys for spending HTLC for penalty (only if they cheated). */
|
||||
static struct privkey *revocation_privkey;
|
||||
|
||||
/* one value is useful for a few witness scripts */
|
||||
static const u8 ONE = 0x1;
|
||||
|
||||
/* If we broadcast a tx, or need a delay to resolve the output. */
|
||||
struct proposed_resolution {
|
||||
/* This can be NULL if our proposal is to simply ignore it after depth */
|
||||
@ -197,6 +203,8 @@ static const char *output_type_name(enum output_type output_type)
|
||||
* 1. to-us output spend (`<local_delayedsig> 0`)
|
||||
* 2. the their-commitment, our HTLC timeout case (`<remotesig> 0`),
|
||||
* 3. the their-commitment, our HTLC redeem case (`<remotesig> <payment_preimage>`)
|
||||
* 4. the their-revoked-commitment, to-local (`<revocation_sig> 1`)
|
||||
* 5. the their-revoked-commitment, htlc (`<revocation_sig> <revocationkey>`)
|
||||
*/
|
||||
static struct bitcoin_tx *tx_to_us(const tal_t *ctx,
|
||||
struct tracked_output *out,
|
||||
@ -621,6 +629,35 @@ static void resolve_htlc_tx(struct tracked_output ***outs,
|
||||
OUR_DELAYED_RETURN_TO_WALLET);
|
||||
}
|
||||
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 5. _B's HTLC-timeout transaction_: The node MUST *resolve* this by
|
||||
* spending using the revocation key.
|
||||
*/
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 6. _B's HTLC-success transaction_: The node MUST *resolve* this by
|
||||
* spending using the revocation key. The node SHOULD extract
|
||||
* the payment preimage from the transaction input witness if not
|
||||
* already known.
|
||||
*/
|
||||
static void steal_htlc_tx(struct tracked_output *out)
|
||||
{
|
||||
struct bitcoin_tx *tx;
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* To spend this via penalty, the remote node uses a witness stack
|
||||
* `<revocationsig> 1`
|
||||
*/
|
||||
tx = tx_to_us(out, out, 0xFFFFFFFF, 0,
|
||||
&ONE, sizeof(ONE),
|
||||
out->wscript,
|
||||
revocation_privkey,
|
||||
&keyset->self_revocation_key);
|
||||
propose_resolution(out, tx, 0, OUR_PENALTY_TX);
|
||||
}
|
||||
|
||||
/* An output has been spent: see if it resolves something we care about. */
|
||||
static void output_spent(struct tracked_output ***outs,
|
||||
const struct bitcoin_tx *tx,
|
||||
@ -658,13 +695,26 @@ static void output_spent(struct tracked_output ***outs,
|
||||
break;
|
||||
|
||||
case THEIR_HTLC:
|
||||
/* We ignore this timeout tx, since we should
|
||||
* resolve by ignoring once we reach depth. */
|
||||
if (out->tx_type == THEIR_REVOKED_UNILATERAL) {
|
||||
steal_htlc_tx(out);
|
||||
} else {
|
||||
/* We ignore this timeout tx, since we should
|
||||
* resolve by ignoring once we reach depth. */
|
||||
}
|
||||
break;
|
||||
|
||||
case OUR_HTLC:
|
||||
/* The only way they can spend this: fulfill */
|
||||
/* The only way they can spend this: fulfill; even
|
||||
* if it's revoked: */
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 6. _B's HTLC-success transaction_: ... The node
|
||||
* SHOULD extract the payment preimage from the
|
||||
* transaction input witness if not already known.
|
||||
*/
|
||||
handle_htlc_onchain_fulfill(out, tx);
|
||||
if (out->tx_type == THEIR_REVOKED_UNILATERAL)
|
||||
steal_htlc_tx(out);
|
||||
break;
|
||||
|
||||
case FUNDING_OUTPUT:
|
||||
@ -701,7 +751,9 @@ static void tx_new_depth(struct tracked_output **outs,
|
||||
/* Is this tx resolving an output? */
|
||||
if (outs[i]->resolved) {
|
||||
if (structeq(&outs[i]->resolved->txid, txid)) {
|
||||
status_trace("%s depth %u",
|
||||
status_trace("%s/%s->%s depth %u",
|
||||
tx_type_name(outs[i]->tx_type),
|
||||
output_type_name(outs[i]->output_type),
|
||||
tx_type_name(outs[i]->resolved->tx_type),
|
||||
depth);
|
||||
outs[i]->resolved->depth = depth;
|
||||
@ -1222,14 +1274,304 @@ static void handle_our_unilateral(const struct bitcoin_tx *tx,
|
||||
tal_free(tmpctx);
|
||||
}
|
||||
|
||||
/* We produce individual penalty txs. It's less efficient, but avoids them
|
||||
* using HTLC txs to block our penalties for long enough to pass the CSV
|
||||
* delay */
|
||||
static void steal_to_them_output(struct tracked_output *out)
|
||||
{
|
||||
const tal_t *tmpctx = tal_tmpctx(NULL);
|
||||
u8 *wscript;
|
||||
struct bitcoin_tx *tx;
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* If a revoked commitment transaction is published, the other party
|
||||
* can spend this output immediately with the following witness:
|
||||
*
|
||||
* <revocation_sig> 1
|
||||
*/
|
||||
wscript = bitcoin_wscript_to_local(tmpctx, to_self_delay[REMOTE],
|
||||
&keyset->self_revocation_key,
|
||||
&keyset->self_delayed_payment_key);
|
||||
|
||||
tx = tx_to_us(tmpctx, out, 0xFFFFFFFF, 0,
|
||||
&ONE, sizeof(ONE),
|
||||
wscript,
|
||||
revocation_privkey,
|
||||
&keyset->self_revocation_key);
|
||||
|
||||
propose_resolution(out, tx, 0, OUR_PENALTY_TX);
|
||||
tal_free(tmpctx);
|
||||
}
|
||||
|
||||
static void steal_htlc(struct tracked_output *out)
|
||||
{
|
||||
struct bitcoin_tx *tx;
|
||||
u8 der[PUBKEY_DER_LEN];
|
||||
|
||||
/* BOLT #3:
|
||||
*
|
||||
* If a revoked commitment transaction is published, the remote node
|
||||
* can spend this output immediately with the following witness:
|
||||
*
|
||||
* <revocation_sig> <revocationkey>
|
||||
*/
|
||||
pubkey_to_der(der, &keyset->self_revocation_key);
|
||||
tx = tx_to_us(out, out, 0xFFFFFFFF, 0,
|
||||
der, sizeof(der),
|
||||
out->wscript,
|
||||
revocation_privkey,
|
||||
&keyset->self_revocation_key);
|
||||
|
||||
propose_resolution(out, tx, 0, OUR_PENALTY_TX);
|
||||
}
|
||||
|
||||
/* BOLT #5:
|
||||
*
|
||||
* If a node tries to broadcast old state, we can use the revocation key to
|
||||
* claim all the funds.
|
||||
*/
|
||||
static void handle_their_cheat(const struct bitcoin_tx *tx,
|
||||
u64 commit_index,
|
||||
const struct sha256_double *txid,
|
||||
u32 tx_blockheight,
|
||||
const struct sha256 *revocation_preimage,
|
||||
const struct secrets *secrets,
|
||||
const struct pubkey *local_revocation_basepoint,
|
||||
const struct pubkey *local_payment_basepoint,
|
||||
const struct pubkey *remote_payment_basepoint,
|
||||
const struct pubkey *remote_delayed_payment_basepoint,
|
||||
u64 commit_num,
|
||||
const struct htlc_stub *htlcs,
|
||||
struct tracked_output **outs)
|
||||
{
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"FIXME: Implement penalty transaction");
|
||||
const tal_t *tmpctx = tal_tmpctx(NULL);
|
||||
u8 **htlc_scripts;
|
||||
u8 *remote_wscript, *script[NUM_SIDES];
|
||||
struct keyset *ks;
|
||||
size_t i;
|
||||
struct secret per_commitment_secret;
|
||||
struct privkey per_commitment_privkey;
|
||||
struct pubkey per_commitment_point;
|
||||
|
||||
set_state(ONCHAIND_CHEATED);
|
||||
|
||||
init_feerate_range(outs[0]->satoshi, tx);
|
||||
|
||||
/* BOLT #5:
|
||||
*
|
||||
* If a node sees a *commitment transaction* for which it has a
|
||||
* revocation key, that *resolves* the funding transaction output.
|
||||
*/
|
||||
resolved_by_other(outs[0], txid, THEIR_REVOKED_UNILATERAL);
|
||||
|
||||
/* FIXME: Types. */
|
||||
BUILD_ASSERT(sizeof(per_commitment_secret)
|
||||
== sizeof(*revocation_preimage));
|
||||
memcpy(&per_commitment_secret, revocation_preimage,
|
||||
sizeof(per_commitment_secret));
|
||||
BUILD_ASSERT(sizeof(per_commitment_privkey)
|
||||
== sizeof(*revocation_preimage));
|
||||
memcpy(&per_commitment_privkey, revocation_preimage,
|
||||
sizeof(per_commitment_privkey));
|
||||
if (!pubkey_from_privkey(&per_commitment_privkey, &per_commitment_point))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Failed derivea from per_commitment_secret %s",
|
||||
type_to_string(trc, struct privkey,
|
||||
&per_commitment_privkey));
|
||||
|
||||
status_trace("Deriving keyset %"PRIu64
|
||||
": per_commit_point=%s"
|
||||
" self_payment_basepoint=%s"
|
||||
" other_payment_basepoint=%s"
|
||||
" self_delayed_basepoint=%s"
|
||||
" other_revocation_basepoint=%s",
|
||||
commit_num,
|
||||
type_to_string(trc, struct pubkey,
|
||||
&per_commitment_point),
|
||||
type_to_string(trc, struct pubkey,
|
||||
remote_payment_basepoint),
|
||||
type_to_string(trc, struct pubkey,
|
||||
local_payment_basepoint),
|
||||
type_to_string(trc, struct pubkey,
|
||||
remote_delayed_payment_basepoint),
|
||||
type_to_string(trc, struct pubkey,
|
||||
local_revocation_basepoint));
|
||||
|
||||
/* keyset is const, we need a non-const ptr to set it up */
|
||||
keyset = ks = tal(tx, struct keyset);
|
||||
if (!derive_keyset(&per_commitment_point,
|
||||
remote_payment_basepoint,
|
||||
local_payment_basepoint,
|
||||
remote_delayed_payment_basepoint,
|
||||
local_revocation_basepoint,
|
||||
ks))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Deriving keyset for %"PRIu64, commit_num);
|
||||
|
||||
status_trace("Deconstructing revoked unilateral tx: %"PRIu64
|
||||
" using keyset: "
|
||||
" self_revocation_key: %s"
|
||||
" self_delayed_payment_key: %s"
|
||||
" self_payment_key: %s"
|
||||
" other_payment_key: %s",
|
||||
commit_num,
|
||||
type_to_string(trc, struct pubkey,
|
||||
&keyset->self_revocation_key),
|
||||
type_to_string(trc, struct pubkey,
|
||||
&keyset->self_delayed_payment_key),
|
||||
type_to_string(trc, struct pubkey,
|
||||
&keyset->self_payment_key),
|
||||
type_to_string(trc, struct pubkey,
|
||||
&keyset->other_payment_key));
|
||||
|
||||
revocation_privkey = tal(tx, struct privkey);
|
||||
if (!derive_revocation_privkey(&secrets->revocation_basepoint_secret,
|
||||
&per_commitment_secret,
|
||||
local_revocation_basepoint,
|
||||
&per_commitment_point,
|
||||
revocation_privkey))
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Deriving revocation_privkey for %"PRIu64,
|
||||
commit_num);
|
||||
|
||||
remote_wscript = to_self_wscript(tmpctx, to_self_delay[REMOTE], keyset);
|
||||
|
||||
/* Figure out what to-them output looks like. */
|
||||
script[REMOTE] = scriptpubkey_p2wsh(tmpctx, remote_wscript);
|
||||
|
||||
/* Figure out what direct to-us output looks like. */
|
||||
script[LOCAL] = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key);
|
||||
|
||||
/* Calculate all the HTLC scripts so we can match them */
|
||||
htlc_scripts = derive_htlc_scripts(htlcs, REMOTE);
|
||||
|
||||
status_trace("Script to-them: %u: %s (%s)",
|
||||
to_self_delay[REMOTE],
|
||||
tal_hex(trc, script[REMOTE]),
|
||||
tal_hex(trc, remote_wscript));
|
||||
status_trace("Script to-me: %s",
|
||||
tal_hex(trc, script[LOCAL]));
|
||||
|
||||
for (i = 0; i < tal_count(tx->output); i++) {
|
||||
status_trace("Output %zu: %s",
|
||||
i, tal_hex(trc, tx->output[i].script));
|
||||
}
|
||||
|
||||
/* BOLT #5:
|
||||
*
|
||||
* A node MUST resolve all unresolved outputs as follows:
|
||||
*
|
||||
* 1. _A's main output_: No action is required; this is a simple
|
||||
* P2WPKH output. This output is considered *resolved* by the
|
||||
* *commitment transaction*.
|
||||
*
|
||||
* 2. _B's main output_: The node MUST *resolve* this by spending
|
||||
* using the revocation key.
|
||||
*
|
||||
* 3. _A's offered HTLCs_: The node MUST *resolve* this in one of three
|
||||
* ways by spending:
|
||||
* * the *commitment tx* using the payment revocation
|
||||
* * the *commitment tx* using the payment preimage if known
|
||||
* * the *HTLC-timeout tx* if B publishes them
|
||||
*
|
||||
* 4. _B's offered HTLCs_: The node MUST *resolve* this in one of two
|
||||
* ways by spending:
|
||||
* * the *commitment tx* using the payment revocation
|
||||
* * the *commitment tx* once the HTLC timeout has passed.
|
||||
*
|
||||
* 5. _B's HTLC-timeout transaction_: The node MUST *resolve* this by
|
||||
* spending using the revocation key.
|
||||
*
|
||||
* 6. _B's HTLC-success transaction_: The node MUST *resolve* this by
|
||||
* spending using the revocation key. The node SHOULD extract
|
||||
* the payment preimage from the transaction input witness if not
|
||||
* already known.
|
||||
*/
|
||||
for (i = 0; i < tal_count(tx->output); i++) {
|
||||
struct tracked_output *out;
|
||||
int j;
|
||||
|
||||
if (script[LOCAL]
|
||||
&& scripteq(tx->output[i].script, script[LOCAL])) {
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 1. _A's main output_: No action is required; this
|
||||
* is a simple P2WPKH output. This output is
|
||||
* considered *resolved* by the *commitment
|
||||
* transaction* itself.
|
||||
*/
|
||||
out = new_tracked_output(&outs, txid, tx_blockheight,
|
||||
THEIR_REVOKED_UNILATERAL,
|
||||
i, tx->output[i].amount,
|
||||
OUTPUT_TO_US, NULL, NULL, NULL);
|
||||
ignore_output(out);
|
||||
script[LOCAL] = NULL;
|
||||
continue;
|
||||
}
|
||||
if (script[REMOTE]
|
||||
&& scripteq(tx->output[i].script, script[REMOTE])) {
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 2. _B's main output_: The node MUST *resolve* this
|
||||
* by spending using the revocation key. */
|
||||
out = new_tracked_output(&outs, txid, tx_blockheight,
|
||||
THEIR_REVOKED_UNILATERAL, i,
|
||||
tx->output[i].amount,
|
||||
DELAYED_OUTPUT_TO_THEM,
|
||||
NULL, NULL, NULL);
|
||||
steal_to_them_output(out);
|
||||
script[REMOTE] = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
j = match_htlc_output(tx, i, htlc_scripts);
|
||||
if (j == -1)
|
||||
status_failed(STATUS_FAIL_INTERNAL_ERROR,
|
||||
"Could not find resolution for output %zu",
|
||||
i);
|
||||
|
||||
if (htlcs[j].owner == LOCAL) {
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 3. _A's offered HTLCs_: The node MUST *resolve* this
|
||||
* in one of three ways by spending:
|
||||
* * the *commitment tx* using the payment revocation
|
||||
* * the *commitment tx* using the payment preimage if
|
||||
* known
|
||||
* * the *HTLC-timeout tx* if B publishes them
|
||||
*/
|
||||
out = new_tracked_output(&outs, txid,
|
||||
tx_blockheight,
|
||||
THEIR_REVOKED_UNILATERAL, i,
|
||||
tx->output[i].amount,
|
||||
OUR_HTLC,
|
||||
&htlcs[j], htlc_scripts[j],
|
||||
NULL);
|
||||
steal_htlc(out);
|
||||
} else {
|
||||
out = new_tracked_output(&outs, txid,
|
||||
tx_blockheight,
|
||||
THEIR_REVOKED_UNILATERAL, i,
|
||||
tx->output[i].amount,
|
||||
THEIR_HTLC,
|
||||
&htlcs[j], htlc_scripts[j],
|
||||
NULL);
|
||||
/* BOLT #5:
|
||||
*
|
||||
* 4. _B's offered HTLCs_: The node MUST *resolve*
|
||||
* this in one of two ways by spending:
|
||||
*
|
||||
* * the *commitment tx* using the payment revocation
|
||||
* * the *commitment tx* once the HTLC timeout has
|
||||
* passed.
|
||||
*/
|
||||
steal_htlc(out);
|
||||
}
|
||||
htlc_scripts[j] = NULL;
|
||||
}
|
||||
|
||||
wait_for_resolved(outs);
|
||||
tal_free(tmpctx);
|
||||
}
|
||||
|
||||
static void handle_their_unilateral(const struct bitcoin_tx *tx,
|
||||
@ -1568,8 +1910,15 @@ int main(int argc, char *argv[])
|
||||
else if (shachain_get_hash(&shachain,
|
||||
shachain_index(commit_num),
|
||||
&revocation_preimage)) {
|
||||
handle_their_cheat(tx, commit_num,
|
||||
handle_their_cheat(tx, &txid,
|
||||
tx_blockheight,
|
||||
&revocation_preimage,
|
||||
&secrets,
|
||||
&basepoints.revocation,
|
||||
&basepoints.payment,
|
||||
&remote_payment_basepoint,
|
||||
&remote_delayed_payment_basepoint,
|
||||
commit_num,
|
||||
htlcs, outs);
|
||||
/* BOLT #5:
|
||||
*
|
||||
|
@ -16,6 +16,9 @@ enum tx_type {
|
||||
/* Our unilateral: spends funding */
|
||||
OUR_UNILATERAL,
|
||||
|
||||
/* Their old unilateral: spends funding */
|
||||
THEIR_REVOKED_UNILATERAL,
|
||||
|
||||
/* The 2 different types of HTLC transaction, each way */
|
||||
THEIR_HTLC_TIMEOUT_TO_THEM,
|
||||
THEIR_HTLC_FULFILL_TO_US,
|
||||
@ -29,6 +32,9 @@ enum tx_type {
|
||||
/* When we spend a delayed output (after cltv_expiry) */
|
||||
OUR_DELAYED_RETURN_TO_WALLET,
|
||||
|
||||
/* When we use revocation key to take output. */
|
||||
OUR_PENALTY_TX,
|
||||
|
||||
/* Special type for marking outputs as resolved by self. */
|
||||
SELF,
|
||||
|
||||
|
@ -443,8 +443,8 @@ class LightningDTests(BaseLightningDTests):
|
||||
bitcoind.rpc.generate(94)
|
||||
l1.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
# Now, 100 blocks it should be done.
|
||||
bitcoind.rpc.generate(100)
|
||||
# Now, 100 blocks l2 should be done.
|
||||
bitcoind.rpc.generate(6)
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
def test_onchain_middleman(self):
|
||||
@ -521,6 +521,128 @@ class LightningDTests(BaseLightningDTests):
|
||||
l1.bitcoin.rpc.generate(100)
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
def test_penalty_inhtlc(self):
|
||||
"""Test penalty transaction with an incoming HTLC"""
|
||||
# We suppress each one after first commit; HTLC gets added not fulfilled.
|
||||
l1 = self.node_factory.get_node(disconnect=['_WIRE_COMMITMENT_SIGNED'])
|
||||
l2 = self.node_factory.get_node(disconnect=['_WIRE_COMMITMENT_SIGNED'])
|
||||
|
||||
l1.rpc.connect('localhost', l2.info['port'], l2.info['id'])
|
||||
self.fund_channel(l1, l2, 10**6)
|
||||
|
||||
# Now, this will get stuck due to l1 commit being disabled..
|
||||
t = self.pay(l1,l2,100000000,async=True)
|
||||
|
||||
# They should both have commitments blocked now.
|
||||
l1.daemon.wait_for_log('_WIRE_COMMITMENT_SIGNED')
|
||||
l2.daemon.wait_for_log('_WIRE_COMMITMENT_SIGNED')
|
||||
|
||||
# Make sure l1 got l2's commitment to the HTLC, and sent to master.
|
||||
l1.daemon.wait_for_log('UPDATE WIRE_CHANNEL_GOT_COMMITSIG')
|
||||
|
||||
# Take our snapshot.
|
||||
tx = l1.rpc.dev_sign_last_tx(l2.info['id'])['tx']
|
||||
|
||||
# Let them continue
|
||||
l1.rpc.dev_reenable_commit(l2.info['id'])
|
||||
l2.rpc.dev_reenable_commit(l1.info['id'])
|
||||
|
||||
# Should fulfill.
|
||||
l1.daemon.wait_for_log('peer_in WIRE_UPDATE_FULFILL_HTLC')
|
||||
l1.daemon.wait_for_log('peer_out WIRE_REVOKE_AND_ACK')
|
||||
|
||||
l2.daemon.wait_for_log('peer_out WIRE_UPDATE_FULFILL_HTLC')
|
||||
l1.daemon.wait_for_log('peer_in WIRE_REVOKE_AND_ACK')
|
||||
|
||||
# Payment should now complete.
|
||||
t.result(timeout=10)
|
||||
|
||||
# Now we really mess things up!
|
||||
bitcoind.rpc.sendrawtransaction(tx)
|
||||
bitcoind.rpc.generate(1)
|
||||
|
||||
l2.daemon.wait_for_log('-> ONCHAIND_CHEATED')
|
||||
# FIXME: l1 should try to stumble along!
|
||||
l1.has_failed()
|
||||
|
||||
# l2 should spend all of the outputs (except to-us).
|
||||
# Could happen in any order, depending on commitment tx.
|
||||
l2.daemon.wait_for_logs(['Propose handling THEIR_REVOKED_UNILATERAL/DELAYED_OUTPUT_TO_THEM by OUR_PENALTY_TX .* in 0 blocks',
|
||||
'sendrawtx exit 0',
|
||||
'Propose handling THEIR_REVOKED_UNILATERAL/THEIR_HTLC by OUR_PENALTY_TX .* in 0 blocks',
|
||||
'sendrawtx exit 0'])
|
||||
|
||||
# FIXME: test HTLC tx race!
|
||||
|
||||
# 100 blocks later, all resolved.
|
||||
bitcoind.rpc.generate(100)
|
||||
|
||||
# FIXME: Test wallet balance...
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
def test_penalty_outhtlc(self):
|
||||
"""Test penalty transaction with an outgoing HTLC"""
|
||||
# First we need to get funds to l2, so suppress after second.
|
||||
l1 = self.node_factory.get_node(disconnect=['_WIRE_COMMITMENT_SIGNED*3'])
|
||||
l2 = self.node_factory.get_node(disconnect=['_WIRE_COMMITMENT_SIGNED*3'])
|
||||
|
||||
l1.rpc.connect('localhost', l2.info['port'], l2.info['id'])
|
||||
self.fund_channel(l1, l2, 10**6)
|
||||
|
||||
# Move some across to l2.
|
||||
self.pay(l1,l2,200000000)
|
||||
|
||||
assert not l1.daemon.is_in_log('_WIRE_COMMITMENT_SIGNED')
|
||||
assert not l2.daemon.is_in_log('_WIRE_COMMITMENT_SIGNED')
|
||||
|
||||
# Now, this will get stuck due to l1 commit being disabled..
|
||||
t = self.pay(l2,l1,100000000,async=True)
|
||||
# Make sure we get signature from them.
|
||||
l1.daemon.wait_for_log('peer_in WIRE_UPDATE_ADD_HTLC')
|
||||
l1.daemon.wait_for_log('peer_in WIRE_COMMITMENT_SIGNED')
|
||||
|
||||
# They should both have commitments blocked now.
|
||||
l1.daemon.wait_for_log('dev_disconnect: _WIRE_COMMITMENT_SIGNED')
|
||||
l2.daemon.wait_for_log('dev_disconnect: _WIRE_COMMITMENT_SIGNED')
|
||||
|
||||
# Take our snapshot.
|
||||
tx = l1.rpc.dev_sign_last_tx(l2.info['id'])['tx']
|
||||
|
||||
# Let the continue
|
||||
l1.rpc.dev_reenable_commit(l2.info['id'])
|
||||
l2.rpc.dev_reenable_commit(l1.info['id'])
|
||||
|
||||
# Thread should complete.
|
||||
t.result(timeout=10)
|
||||
|
||||
# Make sure both sides got revoke_and_ack for final.
|
||||
l1.daemon.wait_for_log('peer_in WIRE_REVOKE_AND_ACK')
|
||||
l2.daemon.wait_for_log('peer_in WIRE_REVOKE_AND_ACK')
|
||||
|
||||
# Now we really mess things up!
|
||||
bitcoind.rpc.sendrawtransaction(tx)
|
||||
bitcoind.rpc.generate(1)
|
||||
|
||||
l2.daemon.wait_for_log('-> ONCHAIND_CHEATED')
|
||||
# FIXME: l1 should try to stumble along!
|
||||
l1.has_failed()
|
||||
|
||||
# l2 should spend all of the outputs (except to-us).
|
||||
# Could happen in any order, depending on commitment tx.
|
||||
l2.daemon.wait_for_logs(['Ignoring output.*: THEIR_REVOKED_UNILATERAL/OUTPUT_TO_US',
|
||||
'Propose handling THEIR_REVOKED_UNILATERAL/DELAYED_OUTPUT_TO_THEM by OUR_PENALTY_TX .* in 0 blocks',
|
||||
'sendrawtx exit 0',
|
||||
'Propose handling THEIR_REVOKED_UNILATERAL/OUR_HTLC by OUR_PENALTY_TX .* in 0 blocks',
|
||||
'sendrawtx exit 0'])
|
||||
|
||||
# FIXME: test HTLC tx race!
|
||||
|
||||
# 100 blocks later, all resolved.
|
||||
bitcoind.rpc.generate(100)
|
||||
|
||||
# FIXME: Test wallet balance...
|
||||
l2.daemon.wait_for_log('onchaind complete, forgetting peer')
|
||||
|
||||
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']
|
||||
|
Loading…
Reference in New Issue
Block a user