laconic-nitro-docs/nitro-integration.md

9.8 KiB

Laconic Nitro integration

Laconic has experimental support for making state channel payments using the Nitro protocol, allowing low-latency, minimal-cost transfer of Ethereum-native cryptoassets.

Laconic's Nitro functionality is currently a limited subset of the full Nitro protocol, focused on basic channel operations with significant constraints.

Nitro: State channel framework

The Nitro protocol is a comprehensive state channel framework that enables secure off-chain token transfers with on-chain settlement guarantees. It creates "state channels" between participants that can update balances and execute application logic without requiring every transaction to be recorded on the blockchain, while maintaining the ability to dispute and resolve conflicts on-chain when necessary.

Components

  • Uses go-nitro library as the underlying protocol implementation
  • Wraps go-nitro within Cosmos SDK server architecture
  • Routes P2P messaging through CometBFT instead of libp2p
  • Integrates with Ethereum L1 for on-chain adjudication

What works

Basic channel operations

  • Create channels: Can create ledger (on-chain) and virtual (off-chain) channels between two parties
  • Close chanels: Can close channels cooperatively
  • Send payments: Send payments over virtual channels

Current use cases

  • Simple two-party payment channels with a validator as counterparty
  • Basic state channel experimentation and testing
  • Foundation for future development of full Nitro protocol support

Current limitations

  • Requires persistent counterparty connection: Both parties must maintain active connections during operations, limiting usage to scenarios with reliable, long-lived connections.
    • In practice, this means channels currently only work with validator nodes as the counterparty.
  • Nitro traffic can become heavy and in extreme cases disrupt consensus. This is mitigated effectively with rate limiting, and there is much room for optimization at the design and implementation level.

What doesn't work yet

  • Complex outcomes: Currently only single-asset outcomes are supported
  • Challenge mechanisms: Dispute resolution is not yet implemented
  • ERC721, ERC1155, and qualified assets and custom applications aren't yet supported

Architecture

graph LR
    %% Subgraph for the whole pipeline
    subgraph Pipeline [ ]
        direction LR
        User[User]

        subgraph Laconicd [laconicd]
            NitroSM["Nitro<br/>state machine"]
        end

        subgraph L1 ["ETH L1"]
            NitroContracts[Nitro contracts]
        end
    end

    %% Connections
    User -->|Transactions| Laconicd
    Laconicd -->|Nitro<br/>events| L1
    L1 -->|Nitro<br/>updates| Laconicd

    %% Styling
    classDef userNode fill:#e8f4fd,stroke:#1976d2,stroke-width:2px;
    classDef l1Node fill:#e8f5e8,stroke:#388e3c,stroke-width:2px;
    classDef stateNode fill:#fce4ec,stroke:#c2185b,stroke-width:2px;
    classDef pipelineBox fill:#ffffff,stroke:#9e9e9e,stroke-width:2px,stroke-dasharray: 3 3;

    class User userNode;
    class L1,NitroContracts l1Node;
    class Laconicd,NitroSM stateNode;
    class Pipeline pipelineBox;

Core components

  • Nitro server (server/nitro/): Main integration point that instantiates go-nitro Node
    • Ethereum integration: Connects to Ethereum L1 for contract interactions
    • Message service: Routes protocol messages via CometBFT P2P
  • Nitrobank module (x/nitrobank/): Cosmos SDK module for state management
    • Currently just tracks state of opened channels
    • In future, will act as main data store for Nitro state machine, leveraging state syncing and WAL

Message flow

Currently, operations proceed by first executing a Nitro operation, and then publishing chain lifecycle events. For example, opening a Nitro ledger channel involves the following steps:

  1. laconicd parses and performs basic validation of the arguments.
  2. A P2P connection is opened between our client and the target node (over CometBFT).
  3. The request is passed to the go-nitro engine, which creates an objective object to initiate the state transition.
    1. The two nodes exchange messages to negotiate the initial channel state.
    2. If both sides approve and sign the initial channel state, the channel is created.
  4. A MsgOpenChannel transaction is published to the chain to reflect the update.

Plans are to further integrate the Nitro engine and chain state, such that Nitro operations will execute as part of a chain transaction to provide atomicity and better leverage the Cosmos-SDK architecture.

Roadmap

While incomplete, this gives a foundation for expanding to full Nitro protocol capabilities:

  • Distributed signature infrastructure exists but is not integrated
  • Integrating Nitro state in consensus-managed storage will improve consistency and reliability
  • Performance improvements in P2P messaging and state management will resolve consensus issues

This integration is early-stage, but under active development.

Configuration

Required configuration parameters:

# in <home-dir>/config/app.toml
[nitro]
enable = true
eth-url = "https://ethereum-rpc-endpoint"

eth-key = "ethereum-key-name-in-keyring"
eth-na-address = "0x..." # Nitro Adjudicator contract
eth-vpa-address = "0x..." # Virtual Payment App contract
eth-ca-address = "0x..." # Consensus App contract

Examples

Open a ledger channel

With a known Ethereum address 0xd4Fa489Eacc52BA59438993f37Be9fcC20090E39 for the counterparty, belonging to a target validator node:

laconicd nitro open-channel 0xd4Fa489Eacc52BA59438993f37Be9fcC20090E39 10eth --output json
# -> {"height":"0","txhash":"6BF863D28E9334B7CC74F283791FBB29C636970E96B48E36DA0ECE4AF1B8DF77", ...}

laconicd q tx 6BF863D28E9334B7CC74F283791FBB29C636970E96B48E36DA0ECE4AF1B8DF77 --output json
# -> {..."body":{"messages":[{"@type":"/cerc.nitrobank.v1.MsgOpenChannel","from_address":"laconic128czkp8j8azu3vvzxy0y2wg706jjdkrnlec6ek","funds":[{"denom":"eth","amount":"10"}],"nitro_address":"0xD9995BAE12FEe327256FFec1e3184d492bD94C31","counterparty":"0xd4Fa489Eacc52BA59438993f37Be9fcC20090E39","channel_id":"0x6267f99810630ea2237a48be87e1892045d0cc2d114034935f94eea2327ffef1"}], ...}}

Open a payment channel

After opening a ledger channel as above:

laconicd nitro open-payment-channel 0xd4Fa489Eacc52BA59438993f37Be9fcC20090E39 10eth --output json
# -> {"height":"0","txhash":"FE3CF32E49C2206EF730DB8D0804BFFD9B80A3CA458A728632B71AC1D36BC780", ...}

laconicd q tx FE3CF32E49C2206EF730DB8D0804BFFD9B80A3CA458A728632B71AC1D36BC780 --output json
# -> {..."body":{"messages":[{"@type":"/cerc.nitrobank.v1.MsgCreatePaymentChannel","from_address":"laconic128czkp8j8azu3vvzxy0y2wg706jjdkrnlec6ek","intermediaries":[],"counterparty":"0xd4Fa489Eacc52BA59438993f37Be9fcC20090E39","challenge_duration":0,"funds":[{"denom":"eth","amount":"10"}],"channel_id":"0x9172ce4c708a33ed13113c209f8d68d3d6974a780f1957dda4ca7d183cb1fa1a"}], ...}}

Make a payment

Send a payment over an existing virtual channel:

# Make a payment of 5 ETH over virtual channel
laconicd nitro pay 0x9172ce4c708a33ed13113c209f8d68d3d6974a780f1957dda4ca7d183cb1fa1a 5eth
# -> Payment of 5eth sent successfully

Query channel information

The Nitro integration provides several query commands to inspect channel states and node information:

Get specific channel information

Query details about a specific ledger or payment channel:

# Get ledger channel information
laconicd nitro get-channel 0x6267f99810630ea2237a48be87e1892045d0cc2d114034935f94eea2327ffef1 --ledger
# -> {
#      "ID": "0x6267f99810630ea2237a48be87e1892045d0cc2d114034935f94eea2327ffef1",
#      "Status": "Open",
#      "Balance": {
#        "AssetAddress": "0x0000000000000000000000000000000000000000",
#        "Me": "10000000000000000000",
#        "Them": "0"
#      },
#      "ChannelMode": "Open"
#    }

# Get payment channel information
laconicd nitro get-channel 0x9172ce4c708a33ed13113c209f8d68d3d6974a780f1957dda4ca7d183cb1fa1a --payment
# -> {
#      "ID": "0x9172ce4c708a33ed13113c209f8d68d3d6974a780f1957dda4ca7d183cb1fa1a",
#      "Status": "Open",
#      "Balance": {...},
#      "Route": [...]
#    }

List all channels

List all ledger channels:

laconicd nitro list-channels
# -> [
#      {
#        "ID": "0x6267f99810630ea2237a48be87e1892045d0cc2d114034935f94eea2327ffef1",
#        "Status": "Open",
#        "Balance": {...}
#      },
#      ...
#    ]

List payment channels by ledger

Get all payment channels associated with a specific ledger channel:

laconicd nitro list-payment-channels 0x6267f99810630ea2237a48be87e1892045d0cc2d114034935f94eea2327ffef1
# -> [
#      {
#        "ID": "0x9172ce4c708a33ed13113c209f8d68d3d6974a780f1957dda4ca7d183cb1fa1a",
#        "Status": "Open",
#        "Route": [...],
#        "Balance": {...}
#      }
#    ]

Local-only queries

All channel query commands support a --local flag for offline queries that don't require connecting to a remote node:

# Query local data only (no P2P connection setup)
laconicd nitro get-channel 0x6267... --ledger --local
laconicd nitro list-channels --local
laconicd nitro list-payment-channels 0x6267... --local

This is useful when:

  • P2P connectivity is unavailable
  • You only need locally cached channel data
  • You want faster query performance without network overhead

Get node identity

Retrieve the node's identity information:

# Get principal ID (default)
laconicd nitro identity
# -> 0xD9995BAE12FEe327256FFec1e3184d492bD94C31

# Get participant address explicitly
laconicd nitro identity --participant
# -> 0xD9995BAE12FEe327256FFec1e3184d492bD94C31

# Get network address
laconicd nitro identity --network
# -> 0x1234567890abcdef1234567890abcdef12345678