Another penalty transaction on regtest with Core Lightning
In this episode 9 we see how a node l1
can try cheat a node l2
by doing a snapshot of its database when the balances of the channel benefit him then spending some sats and finally closing unilaterally the channel using the snapshot database where the last commitment transaction has been already revocated.
Transcript with corrections and improvements
Hi guys, welcome to the LN Room, I'm Tony Aldon and today in this episode 9 we are going to talk about penalty transactions and Core Lightning.
The question is: When does a penalty transaction happens?
It happens for instance when a node l1
with a channel opened with a
node l2
try to cheat the node l2
by sending a revocated commitment
transaction to the blockchain that benefits him and when the node l2
see the fraud on chain early enough to reply by sending a penalty
transaction that spends to him the output that the node l1
can't
spend yet (because of a time lock).
In this episode we see how a node l1
can try cheat a node l2
by doing
a snapshot of its database when the balances of the channel benefit
him then spendind some sats and finally closing unilaterally the
channel using the snapshot database where the last commitment
transaction has been already revocated.
In the episode a penalty transaction managed by Core Lightning, we
also looked at a node l1
trying to cheat another node by sending a
revocated commitment transaction but with a different approach. You
might find it interesting too.
Let's go.
Set up a Lightning Network on regtest with 2 nodes and a channel
As we did in all the previous videos, we use the script
contrib/startup_regtest.sh
to set up a Lightning Network running on
regtest
. This network has 2 nodes l1
and l2
. The node l1
starts with
1btc and 1000000sat locked in a channel opened with the node l2
:
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ source contrib/startup_regtest.sh
lightning-cli is /home/tony/lnroom/lightning/cli/lightning-cli
lightningd is /home/tony/lnroom/lightning/lightningd/lightningd
Useful commands:
start_ln 3: start three nodes, l1, l2, l3
connect 1 2: connect l1 and l2
fund_nodes: connect all nodes with channels, in a row
stop_ln: shutdown
destroy_ln: remove ln directories
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ start_ln 2
Bitcoin Core starting
awaiting bitcoind...
Making "default" bitcoind wallet.
[1] 28341
[2] 28376
WARNING: eatmydata not found: instal it for faster testing
Commands:
l1-cli, l1-log,
l2-cli, l2-log,
bt-cli, stop_ln, fund_nodes
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ alias l1-cli
alias l1-cli='/home/tony/lnroom/lightning/cli/lightning-cli --lightning-dir=/tmp/l1-regtest'
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ fund_nodes
Mining into address bcrt1ql0337077n934he8l0hwn2js52294yp3fzjmwlc... done.
bitcoind balance: 50.00000000
Waiting for lightning node funds... found.
Funding channel from node 1 to node 2. Waiting for confirmation... done.
Database snapshot of the node l1
Before we do a snapshot of l1
's database, we take a look of the funds
in the lightning wallets and the balances of the channel 103x1x0
opened between the node l1
and l2
using the sub-command listfunds
of
lightning-cli
. We see that the node l1
has 98999846sat (1btc minus
1000000sat plus fees to fund the channel) in its wallet and 1000000sat
in his side of the channel 103x1x0
with a total amount of 1000000sat:
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l1-cli listfunds | jq .outputs
[
{
"txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"output": 1,
"amount_msat": 98999846000,
"scriptpubkey": "0014b3aa21fdb4a48726eaa4e1b0f886df042cce226f",
"address": "bcrt1qkw4zrld55jrjd64yuxc03pklqskvugn079prvj",
"status": "confirmed",
"blockheight": 103,
"reserved": false
}
]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 1000000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
The node l2
has no sats in its wallet and no sats in his side of the
channel 103x1x0
opened with the node l1
:
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l2-cli listfunds | jq .outputs
[]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 0,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
Now we do a snapshot of l1
's database that we call
lightningd.sqlite3.bak
. This is a good move for the node l1
that want
to try to cheat the node l2
in a moment because the state of the
channel right now if it goes online by signing the last commitment
transaction will send it back to him the total amount of the channel
(by spending his output after a time lock in case of an unilateral
close).
Note that the base directory of the node l1
is /tmp/l1-regtest/
:
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ cd /tmp/l1-regtest/regtest/
◉ tony@tony:/tmp/l1-regtest/regtest:
$ ls -1
accounts.sqlite3
emergency.recover
gossip_store
hsm_secret
lightningd.sqlite3
lightning-rpc
◉ tony@tony:/tmp/l1-regtest/regtest:
$ cp lightningd.sqlite3 lightningd.sqlite3.bak
We keep the database snapshot for later.
The node l1 pays an invoice to the node l2
In this section, the node l2
creates an invoice for an amount of
20000sat and the node l1
pays that invoice as we can see below:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli invoice 20000sat inv "pizza" | jq .bolt11
"lnbcrt200u1p36kfdesp5qlsckdq0se4mw4shzxumx50ndwpu44lu7z007yntuhjjq5y0vn5qpp5pqwtdy4z47zs9pd3e062xvvpv0dn0m2dwj0pp38s3nfqzjrpgkdqdqgwp5h57npxqyjw5qcqp29qx3qysgqza5f5u5efj5a7qlswuxn2qh955696pxnfg43cj07xg529gnhyem5ymq58tujdz6st5g5ap7yrzgczfvnft0unhke7fhhdd0euyqfcpqppu7wua"
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli pay lnbcrt200u1p36kfdesp5qlsckdq0se4mw4shzxumx50ndwpu44lu7z007yntuhjjq5y0vn5qpp5pqwtdy4z47zs9pd3e062xvvpv0dn0m2dwj0pp38s3nfqzjrpgkdqdqgwp5h57npxqyjw5qcqp29qx3qysgqza5f5u5efj5a7qlswuxn2qh955696pxnfg43cj07xg529gnhyem5ymq58tujdz6st5g5ap7yrzgczfvnft0unhke7fhhdd0euyqfcpqppu7wua
{
"destination": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"payment_hash": "081cb692a2af850285b1cbf4a3318163db37ed4d749e10c4f08cd2014861459a",
"created_at": 1672160710.331,
"parts": 1,
"amount_msat": 20000000,
"amount_sent_msat": 20000000,
"payment_preimage": "3fd9b15590a5a20ea55b2f8a58e02f0adac0778fda8a5983946906ed9455d022",
"status": "complete"
}
This has the effect of advancing the state of the channel. Now,
the node l2
has 20000sat it can spend in that channel and the node l1
980000sat as we can see using the sub-command listfunds
like this:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 20000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 980000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
If the nodes l1
and l2
close the channel mutually (or unilaterally with
one of the last commitment transactions) 980000sat (minus fees) would
be transfered to the node l1
and 20000sat to the node l2
.
But this is not what we are going to do.
The node l1 tries to cheat the node l2 by doing a unilateral close of the channel with the snapshot database
In section, the node l1
will try to cheat the node l2
by sending to
the regtest
chain the revocated commitment transaction that benefits
him (with the whole amount of the channel). To do so the node l1
will
close the channel unilaterally with a wrong state of the channel while
being disconnected from the node l2
.
First, we stop the node l1
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli stop
"Shutdown complete"
[1]- Done test -f "/tmp/l$i-$network/lightningd-$network.pid" || $EATMYDATA "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" (wd: ~/lnroom/lightning)
(wd now: /tmp/l1-regtest/regtest)
Then we roll back the l1
's database:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ cp lightningd.sqlite3.bak lightningd.sqlite3
Now we restart the node l1
using the flag --offline
of lightningd
in
order not to reconnect neither communicate with the node l2
(remember
that we try to cheat him):
◉ tony@tony:/tmp/l1-regtest/regtest:
$ /home/tony/lnroom/lightning/lightningd/lightningd \
► --lightning-dir=/tmp/l1-regtest \
► --offline \
► --daemon
We can verify that we are effectively not connected to the node l2
like this:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listpeers -F | grep connected
peers[0].connected=false
Now the node l1
running on the snapshot database has an older state of
the channel being the current state. Specifically he still has the
whole amount of the channel 103x1x0
on his side:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": false,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 1000000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
Now the node l1
close unilaterally the channel with an old state of
the channel which has for effect to send to the regtest
chain a
revocated commitment transaction in that case that benefit the node
l1
. Long story short l1
tries to cheat l2
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli close 103x1x0 1
# peer is offline, will negotiate once they reconnect (1 seconds before unilateral close).
# Timed out, forcing close.
{
"tx": "02000000017422ae17e0a21a7f8ca0945944ef6ed6d38db8779ad7643be58bf9b46bbce93e0000000000854d7580024a01000000000000220020d7e74f80605c585b4dd6d0304c626beed9f1d14cc7c738e5a890945a72947a08903e0f00000000002200209d8427536e0c80e41eceb6388627bfda1dcb9f114b03a59ce3f816fccbbbd2132c2eca20",
"txid": "589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1",
"type": "unilateral"
}
The node l2 watching for the funding transaction to be spent sends its penalty transaction
As we are running the regtest
chain ourselves the cheated transaction
is siting in the mempool waiting for a block including it to be mined.
So, node l2
does not suspect anything yet. It has still 20000sat on
his side of the channel with the node l1
with which it is no longer
connected as we can see below:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 20000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
Specifically the node l2
is trying to reconnect to the node l1
as we
can read in the attribute status
:
"status": [
"CHANNELD_NORMAL:Will attempt reconnect in 128 seconds"
],
of the object returned by the sub-command listpeers
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "CHANNELD_NORMAL",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
}
],
"status": [
"CHANNELD_NORMAL:Will attempt reconnect in 128 seconds"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
Let's mine one block on regtest
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest -rpcwallet=default getnewaddress
bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
The cheated transaction being mined, the node l1
is now waiting 5 more
blocks (corresponding to "our_to_self_delay": 6
minus the previous
mined block) as we can read in the attribute status
:
"status": [
"ONCHAIN:Tracking our own unilateral close",
"ONCHAIN:3 outputs unresolved: in 5 blocks will spend DELAYED_OUTPUT_TO_US (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_DELAYED_RETURN_TO_WALLET"
],
of the object returned by the sub-command listpeers
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listpeers | jq
{
"peers": [
{
"id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1",
"last_tx_fee_msat": 614000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 0,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qde5hpdkqfnaxsxtg45xr6cypncdwn60sv6s9e0",
"close_to": "00146e6970b6c04cfa681968ad0c3d60819e1ae9e9f0",
"private": false,
"opener": "local",
"closer": "local",
"alias": {
"local": "5744536x13446725x44002"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "1000000000msat",
"remote_funds_msat": "0msat",
"pushed_msat": 0
},
"to_us_msat": 1000000000,
"min_to_us_msat": 1000000000,
"max_to_us_msat": 1000000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 988598000,
"receivable_msat": 0,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "user",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.230Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "user",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:09:42.006Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "CHANNELD_SHUTTING_DOWN",
"cause": "user",
"message": "User or plugin invoked close command"
},
{
"timestamp": "2022-12-27T17:09:43.009Z",
"old_state": "CHANNELD_SHUTTING_DOWN",
"new_state": "AWAITING_UNILATERAL",
"cause": "user",
"message": "Forcibly closed by `close` command timeout"
},
{
"timestamp": "2022-12-27T17:12:01.964Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "user",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:02.018Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "user",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking our own unilateral close",
"ONCHAIN:3 outputs unresolved: in 5 blocks will spend DELAYED_OUTPUT_TO_US (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_DELAYED_RETURN_TO_WALLET"
],
"in_payments_offered": 0,
"in_offered_msat": 0,
"in_payments_fulfilled": 0,
"in_fulfilled_msat": 0,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
While the node l1
is waiting 5 blocks to conclude is fraud, the node
l2
, which was still running, has already counterattacked by sending
its penalty transaction as we can read in the attribute status
:
"status": [
"ONCHAIN:Tracking their illegal close: taking all funds",
"ONCHAIN:3 outputs unresolved: waiting confirmation that we spent DELAYED_CHEAT_OUTPUT_TO_THEM (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_PENALTY_TX"
],
of the object returned by the sub-command listpeers
:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"closer": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:12:04.519Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "AWAITING_UNILATERAL",
"cause": "onchain",
"message": "Funding transaction spent"
},
{
"timestamp": "2022-12-27T17:12:04.520Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "onchain",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:04.746Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "onchain",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking their illegal close: taking all funds",
"ONCHAIN:3 outputs unresolved: waiting confirmation that we spent DELAYED_CHEAT_OUTPUT_TO_THEM (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_PENALTY_TX"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
After mining one block
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
we check by looking at the status
attribute of listpeers
sub-command
that the penalty transaction has been applied:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"closer": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:12:04.519Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "AWAITING_UNILATERAL",
"cause": "onchain",
"message": "Funding transaction spent"
},
{
"timestamp": "2022-12-27T17:12:04.520Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "onchain",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:04.746Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "onchain",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking their illegal close: taking all funds",
"ONCHAIN:All outputs resolved: waiting 99 more blocks before forgetting channel"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
After mining 99 more blocks the 2 nodes can forget the channel:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 99 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
And the final repartition of the sats is the following:
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .outputs
[
{
"txid": "0c5a6189149e3900ffb8c4e4c050ba081ce3c2da55402e8d47272a87d0a544cc",
"output": 0,
"amount_msat": 998935000,
"scriptpubkey": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"address": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"status": "confirmed",
"blockheight": 110,
"reserved": false
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .outputs
[
{
"txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"output": 1,
"amount_msat": 98999846000,
"scriptpubkey": "0014b3aa21fdb4a48726eaa4e1b0f886df042cce226f",
"address": "bcrt1qkw4zrld55jrjd64yuxc03pklqskvugn079prvj",
"status": "confirmed",
"blockheight": 103,
"reserved": false
}
]
We are done with this part of the video where the node l1
tried to
cheat the node l2
and failed due to the penalty transaction broadcast
by the node l2
.
test_channel_lease_lessor_cheat test function
In the following test (test_channel_lease_lessor_cheat) a dual funded
channel is opened between a node l1
and l2
with 500000sat in both
sides. l1
pays an invoice of 10000sat to l2
. l2
makes a snapshot of
its database. l2
pays an invoice of 100000sat to l1
. Then both nodes
are stopped. l2
roll back its database to use the snapshot database
where the balances of the channel benefit him. While the node l1
is
still "sleeping", the node l2
close unilaterally the channel which has
for effect to try to cheat the node l1
by sending a revocated
transaction. Finally, the node l1
restarted it sends its penalty
transaction.
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
@pytest.mark.openchannel('v2')
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db")
@pytest.mark.developer("requres 'dev-queryrates'")
def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams):
'''
Check that lessee can recover funds if lessor cheats
'''
balance_snaps = os.path.join(os.getcwd(), 'tests/plugins/balance_snaps.py')
opts = [{'funder-policy': 'match', 'funder-policy-mod': 100,
'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100,
'may_reconnect': True, 'allow_warning': True,
'plugin': balance_snaps},
{'funder-policy': 'match', 'funder-policy-mod': 100,
'lease-fee-base-sat': '100sat', 'lease-fee-basis': 100,
'may_reconnect': True, 'allow_broken_log': True,
'plugin': balance_snaps}]
l1, l2, = node_factory.get_nodes(2, opts=opts)
amount = 500000
feerate = 2000
l1.fundwallet(20000000)
l2.fundwallet(20000000)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount)
wait_for(lambda: len(l1.rpc.listpeers(l2.info['id'])['peers']) == 0)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
# l1 leases a channel from l2
l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount,
feerate='{}perkw'.format(feerate),
compact_lease=rates['compact_lease'])
bitcoind.generate_block(6)
l1.daemon.wait_for_log('to CHANNELD_NORMAL')
wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True])
wait_for(lambda: [c['active'] for c in l2.rpc.listchannels(l2.get_channel_scid(l1))['channels']] == [True, True])
# send some payments, mine a block or two
inv = l2.rpc.invoice(10**4, '1', 'no_1')
l1.rpc.pay(inv['bolt11'])
bitcoind.generate_block(1)
# make database snapshot of l2
l2.stop()
l2_db_path = os.path.join(l2.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3')
l2_db_path_bak = os.path.join(l2.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3.bak')
copyfile(l2_db_path, l2_db_path_bak)
l2.start(wait_for_bitcoind_sync=True)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
sync_blockheight(bitcoind, [l2])
# push some money from l2->l1, so the commit counter advances
inv = l1.rpc.invoice(10**5, '2', 'no_2')
l2.rpc.pay(inv['bolt11'])
# stop both nodes, roll back l2's database
l2.stop()
l1.stop()
copyfile(l2_db_path_bak, l2_db_path)
# start l2 and force close channel with l1 while l1 is still offline
l2.start()
sync_blockheight(bitcoind, [l2])
l2.rpc.close(l1.info['id'], 1, force_lease_closed=True)
bitcoind.generate_block(1, wait_for_mempool=1)
l1.start()
sync_blockheight(bitcoind, [l1])
l1.daemon.wait_for_logs(['Broadcasting OUR_PENALTY_TX',
' Propose handling THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM by OUR_PENALTY_TX'])
bitcoind.generate_block(1, wait_for_mempool=1)
# l2 sees that l1 has spent their coins!
l2.daemon.wait_for_log('Unknown spend of OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by')
We are done.
I hope you enjoyed the video.
See next time.
Terminal sessions
We ran the following commands in this order:
$ source contrib/startup_regtest.sh
$ start_ln 2
$ alias l1-cli
$ fund_nodes
$ l1-cli listfunds | jq .outputs
$ l1-cli listfunds | jq .channels
$ l2-cli listfunds | jq .outputs
$ l2-cli listfunds | jq .channels
$ cd /tmp/l1-regtest/regtest/
$ ls -1
$ cp lightningd.sqlite3 lightningd.sqlite3.bak
$ l2-cli invoice 20000sat inv "pizza" | jq .bolt11
$ l1-cli pay lnbcrt200u1p36kfdesp5qlsckdq0se4mw4shzxumx50ndwpu44lu7z007yntuhjjq5y0vn5qpp5pqwtdy4z47zs9pd3e062xvvpv0dn0m2dwj0pp38s3nfqzjrpgkdqdqgwp5h57npxqyjw5qcqp29qx3qysgqza5f5u5efj5a7qlswuxn2qh955696pxnfg43cj07xg529gnhyem5ymq58tujdz6st5g5ap7yrzgczfvnft0unhke7fhhdd0euyqfcpqppu7wua
$ l2-cli listfunds | jq .channels
$ l1-cli listfunds | jq .channels
$ l1-cli stop
$ cp lightningd.sqlite3.bak lightningd.sqlite3
$ /home/tony/lnroom/lightning/lightningd/lightningd --lightning-dir=/tmp/l1-regtest --offline --daemon
$ l1-cli listpeers -F | grep connected
$ l1-cli listfunds | jq .channels
$ l1-cli close 103x1x0 1
$ l2-cli listfunds | jq .channels
$ l2-cli listpeers | jq
$ bitcoin-cli -regtest -rpcwallet=default getnewaddress
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
$ l1-cli listpeers | jq
$ l2-cli listpeers | jq
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
$ l2-cli listpeers | jq
$ bitcoin-cli -regtest generatetoaddress 99 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
$ l2-cli listfunds | jq .outputs
$ l1-cli listfunds | jq .outputs
And below you can read the terminal session (command lines and outputs):
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ source contrib/startup_regtest.sh
lightning-cli is /home/tony/lnroom/lightning/cli/lightning-cli
lightningd is /home/tony/lnroom/lightning/lightningd/lightningd
Useful commands:
start_ln 3: start three nodes, l1, l2, l3
connect 1 2: connect l1 and l2
fund_nodes: connect all nodes with channels, in a row
stop_ln: shutdown
destroy_ln: remove ln directories
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ start_ln 2
Bitcoin Core starting
awaiting bitcoind...
Making "default" bitcoind wallet.
[1] 28341
[2] 28376
WARNING: eatmydata not found: instal it for faster testing
Commands:
l1-cli, l1-log,
l2-cli, l2-log,
bt-cli, stop_ln, fund_nodes
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ alias l1-cli
alias l1-cli='/home/tony/lnroom/lightning/cli/lightning-cli --lightning-dir=/tmp/l1-regtest'
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ fund_nodes
Mining into address bcrt1ql0337077n934he8l0hwn2js52294yp3fzjmwlc... done.
bitcoind balance: 50.00000000
Waiting for lightning node funds... found.
Funding channel from node 1 to node 2. Waiting for confirmation... done.
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l1-cli listfunds | jq .outputs
[
{
"txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"output": 1,
"amount_msat": 98999846000,
"scriptpubkey": "0014b3aa21fdb4a48726eaa4e1b0f886df042cce226f",
"address": "bcrt1qkw4zrld55jrjd64yuxc03pklqskvugn079prvj",
"status": "confirmed",
"blockheight": 103,
"reserved": false
}
]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 1000000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l2-cli listfunds | jq .outputs
[]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 0,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:~/lnroom/lightning:[git»(HEAD detached at v22.11.1)]
$ cd /tmp/l1-regtest/regtest/
◉ tony@tony:/tmp/l1-regtest/regtest:
$ ls -1
accounts.sqlite3
emergency.recover
gossip_store
hsm_secret
lightningd.sqlite3
lightning-rpc
◉ tony@tony:/tmp/l1-regtest/regtest:
$ cp lightningd.sqlite3 lightningd.sqlite3.bak
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli invoice 20000sat inv "pizza" | jq .bolt11
"lnbcrt200u1p36kfdesp5qlsckdq0se4mw4shzxumx50ndwpu44lu7z007yntuhjjq5y0vn5qpp5pqwtdy4z47zs9pd3e062xvvpv0dn0m2dwj0pp38s3nfqzjrpgkdqdqgwp5h57npxqyjw5qcqp29qx3qysgqza5f5u5efj5a7qlswuxn2qh955696pxnfg43cj07xg529gnhyem5ymq58tujdz6st5g5ap7yrzgczfvnft0unhke7fhhdd0euyqfcpqppu7wua"
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli pay lnbcrt200u1p36kfdesp5qlsckdq0se4mw4shzxumx50ndwpu44lu7z007yntuhjjq5y0vn5qpp5pqwtdy4z47zs9pd3e062xvvpv0dn0m2dwj0pp38s3nfqzjrpgkdqdqgwp5h57npxqyjw5qcqp29qx3qysgqza5f5u5efj5a7qlswuxn2qh955696pxnfg43cj07xg529gnhyem5ymq58tujdz6st5g5ap7yrzgczfvnft0unhke7fhhdd0euyqfcpqppu7wua
{
"destination": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"payment_hash": "081cb692a2af850285b1cbf4a3318163db37ed4d749e10c4f08cd2014861459a",
"created_at": 1672160710.331,
"parts": 1,
"amount_msat": 20000000,
"amount_sent_msat": 20000000,
"payment_preimage": "3fd9b15590a5a20ea55b2f8a58e02f0adac0778fda8a5983946906ed9455d022",
"status": "complete"
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 20000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": true,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 980000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli stop
"Shutdown complete"
[1]- Done test -f "/tmp/l$i-$network/lightningd-$network.pid" || $EATMYDATA "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" (wd: ~/lnroom/lightning)
(wd now: /tmp/l1-regtest/regtest)
◉ tony@tony:/tmp/l1-regtest/regtest:
$ cp lightningd.sqlite3.bak lightningd.sqlite3
◉ tony@tony:/tmp/l1-regtest/regtest:
$ /home/tony/lnroom/lightning/lightningd/lightningd \
► --lightning-dir=/tmp/l1-regtest \
► --offline \
► --daemon
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listpeers -F | grep connected
peers[0].connected=false
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .channels
[
{
"peer_id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": false,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 1000000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli close 103x1x0 1
# peer is offline, will negotiate once they reconnect (1 seconds before unilateral close).
# Timed out, forcing close.
{
"tx": "02000000017422ae17e0a21a7f8ca0945944ef6ed6d38db8779ad7643be58bf9b46bbce93e0000000000854d7580024a01000000000000220020d7e74f80605c585b4dd6d0304c626beed9f1d14cc7c738e5a890945a72947a08903e0f00000000002200209d8427536e0c80e41eceb6388627bfda1dcb9f114b03a59ce3f816fccbbbd2132c2eca20",
"txid": "589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1",
"type": "unilateral"
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .channels
[
{
"peer_id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"state": "CHANNELD_NORMAL",
"short_channel_id": "103x1x0",
"our_amount_msat": 20000000,
"amount_msat": 1000000000,
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_output": 0
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "CHANNELD_NORMAL",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
}
],
"status": [
"CHANNELD_NORMAL:Will attempt reconnect in 128 seconds"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest -rpcwallet=default getnewaddress
bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listpeers | jq
{
"peers": [
{
"id": "0311c4890bc6c2142bc21da4c773c40aafe52744774d359f034b137b01d660ce09",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1",
"last_tx_fee_msat": 614000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 0,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qde5hpdkqfnaxsxtg45xr6cypncdwn60sv6s9e0",
"close_to": "00146e6970b6c04cfa681968ad0c3d60819e1ae9e9f0",
"private": false,
"opener": "local",
"closer": "local",
"alias": {
"local": "5744536x13446725x44002"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "1000000000msat",
"remote_funds_msat": "0msat",
"pushed_msat": 0
},
"to_us_msat": 1000000000,
"min_to_us_msat": 1000000000,
"max_to_us_msat": 1000000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 988598000,
"receivable_msat": 0,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "user",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.230Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "user",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:09:42.006Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "CHANNELD_SHUTTING_DOWN",
"cause": "user",
"message": "User or plugin invoked close command"
},
{
"timestamp": "2022-12-27T17:09:43.009Z",
"old_state": "CHANNELD_SHUTTING_DOWN",
"new_state": "AWAITING_UNILATERAL",
"cause": "user",
"message": "Forcibly closed by `close` command timeout"
},
{
"timestamp": "2022-12-27T17:12:01.964Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "user",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:02.018Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "user",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking our own unilateral close",
"ONCHAIN:3 outputs unresolved: in 5 blocks will spend DELAYED_OUTPUT_TO_US (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_DELAYED_RETURN_TO_WALLET"
],
"in_payments_offered": 0,
"in_offered_msat": 0,
"in_payments_fulfilled": 0,
"in_fulfilled_msat": 0,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"closer": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:12:04.519Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "AWAITING_UNILATERAL",
"cause": "onchain",
"message": "Funding transaction spent"
},
{
"timestamp": "2022-12-27T17:12:04.520Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "onchain",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:04.746Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "onchain",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking their illegal close: taking all funds",
"ONCHAIN:3 outputs unresolved: waiting confirmation that we spent DELAYED_CHEAT_OUTPUT_TO_THEM (589b636e4b2952fc51033a7c4278cccbddb15bbe6e0c6927df55af43f7275bb1:1) using OUR_PENALTY_TX"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 1 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listpeers | jq
{
"peers": [
{
"id": "024b8ece67648d560ad5357153e577eb71ff157541731047bdb9ab91f708cba62e",
"connected": false,
"channels": [
{
"state": "ONCHAIN",
"scratch_txid": "5bc1062cf80e87a3f51892383ce4c2515c4ab31d38a1b9c1199091d9e0af0952",
"last_tx_fee_msat": 284000,
"feerate": {
"perkw": 253,
"perkb": 1012
},
"owner": "onchaind",
"short_channel_id": "103x1x0",
"direction": 1,
"channel_id": "ca66614515ca5c8139de87f748f556697682e53bc85de8aa64baf3a6d3ce8ccd",
"funding_txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"funding_outnum": 0,
"close_to_addr": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"close_to": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"private": false,
"opener": "remote",
"closer": "remote",
"alias": {
"local": "7721670x4646220x41974"
},
"features": [
"option_static_remotekey",
"option_anchor_outputs"
],
"funding": {
"local_funds_msat": "0msat",
"remote_funds_msat": "1000000000msat",
"pushed_msat": 0
},
"to_us_msat": 20000000,
"min_to_us_msat": 0,
"max_to_us_msat": 20000000,
"total_msat": 1000000000,
"fee_base_msat": 1,
"fee_proportional_millionths": 10,
"dust_limit_msat": 546000,
"max_total_htlc_in_msat": 18446744073709552000,
"their_reserve_msat": 10000000,
"our_reserve_msat": 10000000,
"spendable_msat": 10000000,
"receivable_msat": 968598000,
"minimum_htlc_in_msat": 0,
"minimum_htlc_out_msat": 0,
"maximum_htlc_out_msat": 990000000,
"their_to_self_delay": 6,
"our_to_self_delay": 6,
"max_accepted_htlcs": 483,
"state_changes": [
{
"timestamp": "2022-12-27T16:46:41.597Z",
"old_state": "DUALOPEND_OPEN_INIT",
"new_state": "DUALOPEND_AWAITING_LOCKIN",
"cause": "remote",
"message": "Sigs exchanged, waiting for lock-in"
},
{
"timestamp": "2022-12-27T16:46:46.322Z",
"old_state": "DUALOPEND_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
},
{
"timestamp": "2022-12-27T17:12:04.519Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "AWAITING_UNILATERAL",
"cause": "onchain",
"message": "Funding transaction spent"
},
{
"timestamp": "2022-12-27T17:12:04.520Z",
"old_state": "AWAITING_UNILATERAL",
"new_state": "FUNDING_SPEND_SEEN",
"cause": "onchain",
"message": "Onchain funding spend"
},
{
"timestamp": "2022-12-27T17:12:04.746Z",
"old_state": "FUNDING_SPEND_SEEN",
"new_state": "ONCHAIN",
"cause": "onchain",
"message": "Onchain init reply"
}
],
"status": [
"ONCHAIN:Tracking their illegal close: taking all funds",
"ONCHAIN:All outputs resolved: waiting 99 more blocks before forgetting channel"
],
"in_payments_offered": 1,
"in_offered_msat": 20000000,
"in_payments_fulfilled": 1,
"in_fulfilled_msat": 20000000,
"out_payments_offered": 0,
"out_offered_msat": 0,
"out_payments_fulfilled": 0,
"out_fulfilled_msat": 0,
"htlcs": []
}
]
}
]
}
◉ tony@tony:/tmp/l1-regtest/regtest:
$ bitcoin-cli -regtest generatetoaddress 99 bcrt1q3ljpd8zth5gn987jus2g94mlmpddheqgjktdek > /dev/null
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l2-cli listfunds | jq .outputs
[
{
"txid": "0c5a6189149e3900ffb8c4e4c050ba081ce3c2da55402e8d47272a87d0a544cc",
"output": 0,
"amount_msat": 998935000,
"scriptpubkey": "0014072f58aa14e90e8416b802caaf399fc1f050f3ad",
"address": "bcrt1qquh432s5ay8gg94cqt927wvlc8c9puadvuq95w",
"status": "confirmed",
"blockheight": 110,
"reserved": false
}
]
◉ tony@tony:/tmp/l1-regtest/regtest:
$ l1-cli listfunds | jq .outputs
[
{
"txid": "3ee9bc6bb4f98be53b64d79a77b88dd3d66eef445994a08c7f1aa2e017ae2274",
"output": 1,
"amount_msat": 98999846000,
"scriptpubkey": "0014b3aa21fdb4a48726eaa4e1b0f886df042cce226f",
"address": "bcrt1qkw4zrld55jrjd64yuxc03pklqskvugn079prvj",
"status": "confirmed",
"blockheight": 103,
"reserved": false
}
]