Playing with c-lightning on regtest I would like to see the last valid transaction for a channel. The closest related developer command I found to do that was dev-sign-last-tx. It seems to produce what I want, for example if I have 4 nodes connected like this:

l1 -- l2 -- l3 -- l4

At a certain state of the channel l1-l2 I can do

$ l1-cli dev-sign-last-tx `l2-cli getinfo | jq -r .id` | jq -r .tx | xargs bitcoin-cli -datadir=$PATH_TO_BITCOIN -regtest decoderawtransaction
  "txid": "df1e3ce314fe9b79c1a5f5152c09cb7deac8ade5e75ebf20db0da8d66276cf6d",
  "hash": "7fd1536fdb32e6353236fb9e535e6c7baf0e29c8f2f29fc08721f1248c0df929",
  "version": 2,
  "size": 345,
  "vsize": 180,
  "weight": 720,
  "locktime": 544766664,
  "vin": [
      "txid": "67ebfe5b79d7d92e199e80f93b9ca01ac287490749981a6f7649309b645f3eaf",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      "txinwitness": [
      "sequence": 2152692867
  "vout": [
      "value": 0.00001000,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 1a3be66bacb918f02df8b927fabd1569902d1360",
        "desc": "addr(bcrt1qrga7v6avhyv0qt0chynl40g4dxgz6ymq9dyffw)#85alm0m9",
        "hex": "00141a3be66bacb918f02df8b927fabd1569902d1360",
        "address": "bcrt1qrga7v6avhyv0qt0chynl40g4dxgz6ymq9dyffw",
        "type": "witness_v0_keyhash"
      "value": 0.00998816,
      "n": 1,
      "scriptPubKey": {
        "asm": "0 f0ddefa68335b3b84dd93cebcbb298fe88a9c7472f1ce1b2a25ae1106de776a0",
        "desc": "addr(bcrt1q7rw7lf5rxkemsnwe8n4uhv5cl6y2n3689uwwrv4ztts3qm08w6sqjchzpw)#c83sshvg",
        "hex": "0020f0ddefa68335b3b84dd93cebcbb298fe88a9c7472f1ce1b2a25ae1106de776a0",
        "address": "bcrt1q7rw7lf5rxkemsnwe8n4uhv5cl6y2n3689uwwrv4ztts3qm08w6sqjchzpw",
        "type": "witness_v0_scripthash"

and I can see the contents of some state transaction, with two outputs that must correspond to the balance of the channel: some of the sats go to l1 and some to l2. The commitment transaction on l2's side is different, which is expected. It would be interesting to verify which are the locking scripts for the outputs. I don't see how they can be extracted from the command line interface; maybe using l1-cli sql ... somehow?

However, I am not convinced the command dev-sign-last-tx is giving me the last valid state of the channel. If I try to make a payment through this channel and the payment succeeds I can see with dev-sign-last-tx a new distribution of liquidity in the outputs. But if I try to make a payment that fails, for instance by disconnecting the recipient and using a single trial with the primitive sendpay rpc instead of using the more complex pay plugin, I know there will have to a pending HTLC in the channel but it's not reflected with the dev-sign-last-tx; if I try that experiment I still see 2 outputs in the transaction, when there should be 3 or even more depending on how many HTLCs are pending.

To me this probably means that dev-sign-last-tx shows the last commitment transaction that didn't have HTLCs. How can I see the actual last valid transaction that gives me the state of the channel?

1 Answer 1


You are correct dev-sign-last-tx is indeed the command you want, but be careful, it is a dev command because it is used in integration tests to test a cheating party. Having an old fully signer commitment TX is very dangerous.

As you noticed dev-sign-last-tx is a bit racy, i.e., if there is a commmitment inbetween, you will end up with an old on. The other way of producing a commitment TX is to unilaterally close the channel, and in that case the commitment TX is guaranteed to be the latest agreed upon state.

You can likely compare the dev-sign-last-tx with the scratch_txid in listpeers and listpeerchannels to see if there have been changes inbetween.

Now for the lack of a 3rd output: the HTLC is only temporarily added, and then removed refunding the sender on failure and crediting the recipient on succcess. The HTLC output willl only be present for a brief period, and so you'd have to be rather fast to catch it with dev-sign-last-tx. If you want to cheat (in a test scenario) with an HTLC in the commitment you'll likely need to hold on to the incoming HTLC on the recipient side with the htlc_accepted hook, ensuring that you can call dev-sign-last-tx after adding, but before removing it again.

Also notice that for HTLCs that would be considered dust, they will not be materialized, since we'd spend more on creating and cleaning up the UTXO than the value that is actually being transferred.

  • I can see that the txid obtained from dev-sign-last-tx does not correspond to the scratch_txid I get with listpeerchannels. Why wasn't this command implemented to get me the actual latest transaction? Would it make sense to write a plugin to do so?
    – Lagrang3
    Commented Jun 15, 2023 at 6:50
  • So there is a refund mechanism for failed attempts? Then situations like this would no longer happen bitcoin.stackexchange.com/questions/86228/…
    – Lagrang3
    Commented Jun 15, 2023 at 6:54
  • So you're saying that if the amount is below the dust limit there won't be an HTLC output in the commitment transaction that represents the state of the channel? How would two nodes agree on the state of the channel if not through that commitment tx?
    – Lagrang3
    Commented Jun 15, 2023 at 7:32
  • Re: refund mechanism preventing stuck payment: refunding requires interaction by the endpoints. So if either endpoint is faulty and doesn't sign the refund it'll still get stuck.
    – cdecker
    Commented Jun 24, 2023 at 14:10
  • Even for dust HTLCs we have a state transition, and regole the old state (the sender might have dropped by a sat, or the recipient might have accumulated another sat (Dunst doesn't matter individually but it does in aggregate)
    – cdecker
    Commented Jun 24, 2023 at 14:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.