← Back to Basis of Lightning Technology
BOLT 2specificationlightningtransactionsp2p

BOLT 2: Peer Protocol for Channel Management

The peer channel protocol has three phases: establishment, normal operation, and closing.

No reviews
Unknown·Updated Mar 28, 2026·0 reviews·0 attestations·View source
Collections:BOLTs — Merged

Specification

BOLT #2: Peer Protocol for Channel Management

The peer channel protocol has three phases: establishment, normal operation, and closing.

Table of Contents

Channel

Definition of channel_id

Some messages use a channel_id to identify the channel. It's derived from the funding transaction by combining the funding_txid and the funding_output_index, using big-endian exclusive-OR (i.e. funding_output_index alters the last 2 bytes).

Prior to channel establishment, a temporary_channel_id is used, which is a random nonce.

Note that as duplicate temporary_channel_ids may exist from different peers, APIs which reference channels by their channel id before the funding transaction is created are inherently unsafe. The only protocol-provided identifier for a channel before funding_created has been exchanged is the (source_node_id, destination_node_id, temporary_channel_id) tuple. Note that any such APIs which reference channels by their channel id before the funding transaction is confirmed are also not persistent - until you know the script pubkey corresponding to the funding output nothing prevents duplicative channel ids.

channel_id, v2

For channels established using the v2 protocol, the channel_id is the SHA256(lesser-revocation-basepoint || greater-revocation-basepoint), where the lesser and greater is based off the order of the basepoint.

When sending open_channel2, the peer's revocation basepoint is unknown. A temporary_channel_id must be computed by using a zeroed out basepoint for the non-initiator.

When sending accept_channel2, the temporary_channel_id from open_channel2 must be used, to allow the initiator to match the response to its request.

Rationale

The revocation basepoints must be remembered by both peers for correct operation anyway. They're known after the first exchange of messages, obviating the need for a temporary_channel_id in subsequent messages. By mixing information from both sides, they avoid channel_id collisions, and they remove the dependency on the funding txid.

Interactive Transaction Construction

Interactive transaction construction allows two peers to collaboratively build a transaction for broadcast. This protocol is the foundation for dual-funded channels establishment (v2).

Set-Up and Vocabulary

There are two parties to a transaction construction: an initiator and a non-initiator. The initiator is the peer which initiates the protocol, e.g. for channel establishment v2 the initiator would be the peer which sends open_channel2.

The protocol makes the following assumptions:

  • The feerate for the transaction is known.
  • The dust_limit for the transaction is known.
  • The nLocktime for the transaction is known.
  • The nVersion for the transaction is known.

Fee Responsibility

The initiator is responsible for paying the fees for the following fields, to be referred to as the common fields.

  • version
  • segwit marker + flag
  • input count
  • output count
  • locktime

The rest of the transaction bytes' fees are the responsibility of the peer who contributed that input or output via tx_add_input or tx_add_output, at the agreed upon feerate.

Overview

The initiator initiates the interactive transaction construction protocol with tx_add_input. The non-initiator responds with any of tx_add_input, tx_add_output, tx_remove_input, tx_remove_output, or tx_complete. The protocol continues with the synchronous exchange of interactive transaction protocol messages until both nodes have sent and received a consecutive tx_complete. This is a turn-based protocol.

Once peers have exchanged consecutive tx_completes, the interactive transaction construction protocol is considered concluded. Both peers should construct the transaction and fail the negotiation if an error is discovered.

This protocol is expressly designed to allow for parallel, multi-party sessions to collectively construct a single transaction. This preserves the ability to open multiple channels in a single transaction. While serial_ids are generally chosen randomly, to maintain consistent transaction ordering across all peer sessions, it is simplest to reuse received serial_ids when forwarding them to other peers, inverting the bottom bit as necessary to satisfy the parity requirement.

Here are a few example exchanges.

initiator only

A, the initiator, has two inputs and an output (the funding output). B, the non-initiator has nothing to contribute.

    +-------+                       +-------+
    |       |--(1)- tx_add_input -->|       |
    |       |<-(2)- tx_complete ----|       |
    |       |--(3)- tx_add_input -->|       |
    |   A   |<-(4)- tx_complete ----|   B   |
    |       |--(5)- tx_add_output ->|       |
    |       |<-(6)- tx_complete ----|       |
    |       |--(7)- tx_complete --->|       |
    +-------+                       +-------+

initiator and non-initiator

A, the initiator, contributes 2 inputs and an output that they then remove. B, the non-initiator, contributes 1 input and an output, but waits until A adds a second input before contributing.

Note that if A does not send a second input, the negotiation will end without B's contributions.

    +-------+                         +-------+
    |       |--(1)- tx_add_input ---->|       |
    |       |<-(2)- tx_complete ------|       |
    |       |--(3)- tx_add_output --->|       |
    |       |<-(4)- tx_complete ------|       |
    |       |--(5)- tx_add_input ---->|       |
    |   A   |<-(6)- tx_add_input -----|   B   |
    |       |--(7)- tx_remove_output >|       |
    |       |<-(8)- tx_add_output ----|       |
    |       |--(9)- tx_complete ----->|       |
    |       |<-(10) tx_complete ------|       |
    +-------+                         +-------+

The tx_add_input Message

This message contains a transaction input.

  1. type: 66 (tx_add_input)

  2. data:

    • [channel_id:channel_id]
    • [u64:serial_id]
    • [u16:prevtx_len]
    • [prevtx_len*byte:prevtx]
    • [u32:prevtx_vout]
    • [u32:sequence]
    • [tx_add_input_tlvs:tlvs]
  3. tlv_stream: tx_add_input_tlvs

  4. types:

    1. type: 0 (shared_input_txid)
    2. data:
    • [sha256:funding_txid]

Requirements

The sending node:

  • MUST add all sent inputs to the transaction
  • MUST use a unique serial_id for each input currently added to the transaction
  • MUST set sequence to be less than or equal to 4294967293 (0xFFFFFFFD)
  • MUST NOT re-transmit inputs it has received from the peer
  • if is the initiator:
    • MUST send even serial_ids
  • if is the non-initiator:
    • MUST send odd serial_ids

The receiving node:

  • MUST add all received inputs to the transaction
  • MUST fail the negotiation if:
    • sequence is set to 0xFFFFFFFE or 0xFFFFFFFF
    • if prevtx_len is 0:
      • shared_input_txid is not set
      • shared_input_txid and prevtx_vout don't match the previous funding output
      • a previously added (and not removed) input already exists with shared_input_txid set
    • if prevtx_len is not 0:
      • prevtx and prevtx_vout are identical to a previously added (and not removed) input
      • prevtx is not a valid transaction
      • prevtx_vout is greater or equal to the number of outputs on prevtx
      • the scriptPubKey of the prevtx_vout output of prevtx is not exactly a 1-byte push opcode (for the numeric values 0 to 16) followed by a data push between 2 and 40 bytes
    • the serial_id is already included in the transaction
    • the serial_id has the wrong parity
    • if has received 4096 tx_add_input messages during this negotiation

Rationale

Each node must know the set of the transaction inputs. The non-initiator MAY omit this message.

serial_id is a randomly chosen number which uniquely identifies this input. Inputs in the constructed transaction MUST be sorted by serial_id.

prevtx is the serialized transaction that contains the output this input spends, used to verify that the input is non-malleable. It can be ommitted (prevtx_len set to 0) when both peers already know that the input is non-malleable (e.g. when it is the previous funding output).

prevtx_vout is the index of the output being spent.

sequence is the sequence number of this input: it must signal replaceability, and the same value should be used across implementations to avoid on-chain fingerprinting.

Liquidity griefing

When sending tx_add_input, senders have no guarantee that the remote node will complete the protocol in a timely manner. Malicious remote nodes could delay messages or stop responding, which can result in a partially created transaction that cannot be broadcast by the honest node. If the honest node is locking the corresponding UTXO exclusively for this remote node, this can be exploited to lock up the honest node's liquidity.

It is thus recommended that implementations keep UTXOs unlocked and actively reuse them in concurrent sessions, which guarantees that transactions created with honest nodes double-spend pending transactions with malicious nodes at no additional cost for the honest node.

Unfortunately, this will also create conflicts between concurrent sessions with honest nodes. This is a reasonable trade-off though because:

  • on-chain funding attempts are relatively infrequent operations
  • honest nodes should complete the protocol quickly, reducing the risk of conflicts
  • failed attempts can simply be retried at no cost

The tx_add_output Message

This message adds a transaction output.

  1. type: 67 (tx_add_output)
  2. data:
    • [channel_id:channel_id]
    • [u64:serial_id]
    • [u64:sats]
    • [u16:scriptlen]
    • [scriptlen*byte:script]

Requirements

Either node:

  • MAY omit this message

The sending node:

  • MUST add all sent outputs to the transaction
  • if is the initiator:
    • MUST send even serial_ids
  • if is the non-initiator:
    • MUST send odd serial_ids

The receiving node:

  • MUST add all received outputs to the transaction
  • MUST accept P2WSH, P2WPKH, P2TR scripts
  • MAY fail the negotiation if script is non-standard
  • MUST fail the negotiation if:
    • the serial_id is already included in the transaction
    • the serial_id has the wrong parity
    • it has received 4096 tx_add_output messages during this negotiation
    • the sats amount is less than the dust_limit
    • the sats amount is greater than 2,100,000,000,000,000 (MAX_MONEY)

Rationale

Each node must know the set of the transaction outputs.

serial_id is a randomly chosen number which uniquely identifies this output. Outputs in the constructed transaction MUST be sorted by serial_id.

sats is the satoshi value of the output.

script is the scriptPubKey for the output (with its length omitted). scripts are not required to follow standardness rules: non-standard scripts such as OP_RETURN may be accepted, but the corresponding transaction may fail to relay across the network.

The tx_remove_input and tx_remove_output Messages

This message removes an input from the transaction.

  1. type: 68 (tx_remove_input)
  2. data:
    • [channel_id:channel_id]
    • [u64:serial_id]

This message removes an output from the transaction.

  1. type: 69 (tx_remove_output)
  2. data:
    • [channel_id:channel_id]

[Content truncatedview full spec at source]

Discussion (0 threads)

No discussion yet. Be the first to comment.