From 8e2c2cc44e5962721610293947c9a18bc7fa050f Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Mon, 26 May 2025 17:28:10 +0300 Subject: [PATCH 01/11] docs: add mermaid diagrams for quickstart, RPC, client docs --- docs/nitrolite_client/index.mdx | 49 ++++++++++++- docs/nitrolite_client/types.md | 68 ++++++++++++++++--- docs/nitrolite_rpc/index.md | 36 +++++++++- .../quick_start/deposit_and_create_channel.md | 53 ++++++++++++--- docs/quick_start/index.mdx | 30 +++++++- 5 files changed, 213 insertions(+), 23 deletions(-) diff --git a/docs/nitrolite_client/index.mdx b/docs/nitrolite_client/index.mdx index d56e022..cb016cc 100644 --- a/docs/nitrolite_client/index.mdx +++ b/docs/nitrolite_client/index.mdx @@ -11,6 +11,53 @@ import { Card, CardGrid } from '@site/src/components/Card'; The `NitroliteClient` class is the main entry point for interacting with Nitrolite state channels. It provides a comprehensive set of methods for managing channels, deposits, and funds. +
+```mermaid +flowchart TB + User["User Application"] + + subgraph Client["Nitrolite Client"] + API["Client API"] + NService["Nitrolite Service"] + ERC20Service["ERC20 Service"] + Wallet["State Wallet"] + end + + subgraph Communication["Communication Layer"] + RPC["Nitrolite RPC"] + Messaging["Message Creation API"] + end + + subgraph External["External Components"] + ClearNode["ClearNode"] + Blockchain["Blockchain"] + NitroContract["Nitro Contract"] + Token["Token Contract"] + end + + User <--> API + API <--> NService + API <--> ERC20Service + NService <--> Wallet + ERC20Service <--> Wallet + Wallet <--> RPC + RPC <--> Messaging + Messaging <--> ClearNode + Messaging <--> Blockchain + ClearNode <--> Blockchain + Blockchain <--> NitroContract + Blockchain <--> Token + + classDef clientNode fill:#d0e8ff,stroke:#3080cf + classDef commNode fill:#ffe8d0,stroke:#cf8030 + classDef extNode fill:#d0ffe8,stroke:#30cf80 + + class API,NService,ERC20Service,Wallet clientNode + class RPC,Messaging commNode + class ClearNode,Blockchain,NitroContract,Token extNode +``` +
+ - */} \ No newline at end of file + */} diff --git a/docs/nitrolite_client/types.md b/docs/nitrolite_client/types.md index 3eb8533..d2b4b3e 100644 --- a/docs/nitrolite_client/types.md +++ b/docs/nitrolite_client/types.md @@ -9,6 +9,56 @@ keywords: [erc7824, statechannels, state channels, nitrolite, ethereum scaling, This page documents the core types used throughout the `@erc7824/nitrolite` SDK. Understanding these types is essential for effectively working with the `NitroliteClient`. +
+```mermaid +classDiagram + class ChannelState { + +turnNum: number + +isFinal: boolean + +appDefinition: address + +appData: bytes + +outcome: Outcome + } + + class Outcome { + +assetType: AssetType + +allocations: Allocation[] + } + + class Allocation { + +destination: address + +amount: uint256 + +metadata: bytes + } + + class AssetType { + <> + ETH + ERC20 + ERC721 + } + + class StateSignature { + +signer: address + +signature: bytes + } + + class Channel { + +participants: address[] + +channelNonce: uint256 + +chainId: uint256 + +states: ChannelState[] + +signatures: StateSignature[] + } + + Channel "1" *-- "many" ChannelState + Channel "1" *-- "many" StateSignature + ChannelState "1" *-- "1" Outcome + Outcome "1" *-- "many" Allocation + Outcome "1" *-- "1" AssetType +``` +
+ ## Core Types ### ChannelId @@ -99,16 +149,16 @@ Represents a complete state channel state, including allocations and signatures. interface NitroliteClientConfig { // Required: viem PublicClient for reading blockchain data publicClient: PublicClient; - + // Required: viem WalletClient for sending transactions and account context walletClient: WalletClient>; - + // Optional: Separate wallet client for signing states stateWalletClient?: WalletClient>; - + // Required: Contract addresses addresses: ContractAddresses; - + // Required: Challenge duration in seconds challengeDuration?: bigint; } @@ -230,10 +280,10 @@ const allowance: bigint = await client.getTokenAllowance() ```typescript // Create channel -const result: { - channelId: ChannelId; - initialState: State; - txHash: Hash +const result: { + channelId: ChannelId; + initialState: State; + txHash: Hash } = await client.createChannel({ initialAllocationAmounts: [bigint, bigint], stateData: Hex @@ -343,4 +393,4 @@ const finalState = { ], sigs: [userSig, guestSig] }; -``` \ No newline at end of file +``` diff --git a/docs/nitrolite_rpc/index.md b/docs/nitrolite_rpc/index.md index 75d0d1f..f29607d 100644 --- a/docs/nitrolite_rpc/index.md +++ b/docs/nitrolite_rpc/index.md @@ -12,6 +12,40 @@ import MethodDetails from '@site/src/components/MethodDetails'; The NitroliteRPC provides a secure, reliable real-time communication protocol for state channel applications. It enables off-chain message exchange, state updates, and channel management. This system is built around the `NitroliteRPC` class, which provides the foundational methods for message construction, signing, parsing, and verification. +
+```mermaid +sequenceDiagram + participant A as Alice + participant C as ClearNode + participant B as Bob + + Note over A,B: Channel Creation + A->>C: CreateChannel(channelId, participants, allocation) + C->>B: NotifyChannelCreation(channelId, participants, allocation) + B->>C: AcknowledgeChannel(channelId) + C->>A: ChannelReady(channelId) + + Note over A,B: Application Session + A->>C: ProposeAppSession(channelId, appDefinition) + C->>B: ForwardAppSession(channelId, appDefinition) + B->>C: AcceptAppSession(channelId, appDefinition) + C->>A: AppSessionAccepted(channelId) + + Note over A,B: State Updates + A->>C: UpdateState(channelId, state, signature) + C->>B: ForwardStateUpdate(channelId, state, signature) + B->>C: CountersignState(channelId, state, signature) + C->>A: StateFinalized(channelId, state, signatures) + + Note over A,B: Channel Closure + A->>C: ProposeClose(channelId, finalState) + C->>B: ForwardCloseProposal(channelId, finalState) + B->>C: AgreeToClose(channelId, finalState, signature) + C->>A: CloseApproved(channelId, signatures) + A->>C: SubmitClosure(channelId, finalState, signatures) +``` +
+ - \ No newline at end of file + diff --git a/docs/quick_start/deposit_and_create_channel.md b/docs/quick_start/deposit_and_create_channel.md index 15d5b0e..294b07e 100644 --- a/docs/quick_start/deposit_and_create_channel.md +++ b/docs/quick_start/deposit_and_create_channel.md @@ -12,6 +12,37 @@ import TabItem from '@theme/TabItem'; Creating a state channel involves depositing funds into a smart contract and establishing the channel parameters. This guide walks through the complete process of depositing USDC and creating a functional state channel. +
+```mermaid +flowchart TD + User["User Wallet"] + Chain["Blockchain"] + Contract["Nitro Contract"] + Channel["State Channel"] + Other["Other Participants"] + + User -->|1\. Deposit| Chain + Chain -->|2\. Lock in Contract| Contract + Contract -->|3\. Channel Creation| Channel + Channel -->|4\. Off-chain Transfers| Channel + Channel -->|5\. Off-chain Exchange| Other + Channel -->|6\. Final State| Contract + Contract -->|7\. Release Funds| Chain + Chain -->|8\. Withdraw| User + Chain -->|9\. Withdraw| Other + + classDef wallet fill:#ffcccc,stroke:#ff0000 + classDef blockchain fill:#ccffcc,stroke:#00cc00 + classDef contract fill:#ccccff,stroke:#0000ff + classDef channel fill:#ffffcc,stroke:#ffcc00 + + class User,Other wallet + class Chain blockchain + class Contract contract + class Channel channel +``` +
+ ## Understanding State Channels in Nitrolite A state channel is a secure off-chain communication pathway between two participants: @@ -65,7 +96,7 @@ const depositAmount = 100000000n; // 100 USDC (with 6 decimals) try { const depositTx = await client.deposit(depositAmount); console.log('Deposit transaction submitted:', depositTx); - + // The deposit function waits for transaction confirmation internally console.log('USDC tokens successfully deposited to custody contract'); } catch (error) { @@ -82,7 +113,7 @@ try { // 1. Check current allowance const currentAllowance = await client.getTokenAllowance(); console.log('Current USDC allowance:', currentAllowance); - + // 2. Approve USDC if needed if (currentAllowance < depositAmount) { const approvalTx = await client.approveTokens(depositAmount); @@ -90,7 +121,7 @@ try { // Wait for approval to be confirmed // (typically you would wait for the transaction to be mined) } - + // 3. Deposit USDC const depositTx = await client.deposit(depositAmount); console.log('Deposit transaction submitted:', depositTx); @@ -119,7 +150,7 @@ const channelParams = { try { // Create the channel const { channelId, initialState, txHash } = await client.createChannel(channelParams); - + console.log('Channel created with ID:', channelId); console.log('Initial state:', initialState); console.log('Transaction hash:', txHash); @@ -165,13 +196,13 @@ const channelParams = { }; try { - const { - channelId, - initialState, - depositTxHash, - createChannelTxHash + const { + channelId, + initialState, + depositTxHash, + createChannelTxHash } = await client.depositAndCreateChannel(depositAmount, channelParams); - + console.log('USDC deposited with transaction:', depositTxHash); console.log('Channel created with ID:', channelId); console.log('Channel creation transaction:', createChannelTxHash); @@ -208,4 +239,4 @@ After creating your channel, you can: 2. [View channel assets](balances) to monitor your funds 3. [Create an application session](application_session) to start transacting -For advanced channel operations, see the [Resize Channel](resize_channel) guide. \ No newline at end of file +For advanced channel operations, see the [Resize Channel](resize_channel) guide. diff --git a/docs/quick_start/index.mdx b/docs/quick_start/index.mdx index d7618b4..4001f90 100644 --- a/docs/quick_start/index.mdx +++ b/docs/quick_start/index.mdx @@ -19,7 +19,7 @@ Before you begin working with Nitrolite, ensure that you have: - **Node.js**: Version 16 or later - **Package Manager**: npm, yarn, or pnpm - **Web3 Development Knowledge**: Basic understanding of Ethereum and smart contracts -- **Development Environment**: +- **Development Environment**: - For frontend: React, Vue, or similar framework - For backend: Node.js environment - **Ethereum Wallet**: MetaMask or another web3 provider for testing @@ -72,6 +72,34 @@ We have generated a [llms-full.txt](https://erc7824.org/llms-full.txt) file that Channels follow a clear lifecycle that involves several key steps: +
+```mermaid +flowchart TB + subgraph OnChain["On-Chain Actions"] + A[Deposit Funds] --> B[Create Channel] + F[Challenge State] -.-> G[Respond to Challenge] + H[Close Channel] --> I[Withdraw Funds] + end + + subgraph OffChain["Off-Chain Actions"] + C[Initialize Application Session] --> D[Update Channel State] + D -->|New state| D + D --> E[Propose Channel Closure] + end + + B --> C + E --> H + E -.->|Dispute| F + G --> H + + classDef onchain fill:#ffcccc,stroke:#ff0000 + classDef offchain fill:#ccffcc,stroke:#00cc00 + + class A,B,F,G,H,I onchain + class C,D,E offchain +``` +
+ 1. **[Client Initialization](initializing_client)**: Set up the client with your desired configuration 2. **[Deposit and Channel Creation](deposit_and_create_channel)**: Fund your channel and establish it with participants 3. **[ClearNode Connection](connect_to_the_clearnode)**: Connect to a ClearNode for off-chain messaging From d106e5049cd298e32305d0ec7220bf659c86d1cb Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Mon, 26 May 2025 17:48:47 +0300 Subject: [PATCH 02/11] fix: broker link --- docs/quick_start/connect_to_the_clearnode.md | 2 +- package-lock.json | 21 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/quick_start/connect_to_the_clearnode.md b/docs/quick_start/connect_to_the_clearnode.md index efcc17b..7b3a18f 100644 --- a/docs/quick_start/connect_to_the_clearnode.md +++ b/docs/quick_start/connect_to_the_clearnode.md @@ -1640,5 +1640,5 @@ After successfully connecting to a ClearNode, you can: 1. [View and manage channel assets](balances) 2. [Create an application session](application_session) -3. [Start transacting off-chain](application_session#sending-transactions) +3. [Start transacting off-chain](application_session) 4. [Explore advanced channel operations](resize_channel) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 569fb82..cca5258 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15770,6 +15770,13 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/search-insights": { + "version": "2.17.3", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", + "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", + "license": "MIT", + "peer": true + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -16752,6 +16759,20 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", From b57ca39a47d05909945bb4f307415fabf7cbad81 Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 15:07:48 +0300 Subject: [PATCH 03/11] docs: add architecture overview and deployment guide --- docs/architecture/index.md | 240 +++++++++++++++++ docs/deployment/index.md | 231 +++++++++++++++++ docs/faq.md | 175 +++++++++++++ docs/security/index.md | 360 +++++++++++++++++++++++++ docs/troubleshooting.md | 519 +++++++++++++++++++++++++++++++++++++ 5 files changed, 1525 insertions(+) create mode 100644 docs/architecture/index.md create mode 100644 docs/deployment/index.md create mode 100644 docs/faq.md create mode 100644 docs/security/index.md create mode 100644 docs/troubleshooting.md diff --git a/docs/architecture/index.md b/docs/architecture/index.md new file mode 100644 index 0000000..d59003b --- /dev/null +++ b/docs/architecture/index.md @@ -0,0 +1,240 @@ +--- +title: Architecture Overview +description: Understanding the Nitrolite system architecture and design principles +keywords: [architecture, system design, nitrolite, state channels, erc7824] +--- + +# Architecture Overview + +Nitrolite's architecture represents a carefully balanced set of design decisions that prioritize performance, security, and usability while managing the inherent trade-offs in distributed systems. + +## Architectural Philosophy + +### Hybrid On-Chain/Off-Chain Design + +Nitrolite implements a **three-layer separation of concerns** where each layer is optimized for its specific role: + +- **Smart contracts** handle custody and dispute resolution +- **ClearNode** manages high-frequency off-chain operations +- **TypeScript SDK** provides developer-friendly abstractions + +**Design Rationale**: This separation allows the system to achieve high throughput (1000+ TPS per channel) while maintaining blockchain-level security guarantees. By keeping most operations off-chain, users pay gas fees only twice: when opening and closing channels. + +### Two-Party Channel Architecture + +Rather than supporting arbitrary n-party channels, Nitrolite implements a **fixed Creator/Broker model**: + +```solidity +uint256 constant PART_NUM = 2; +uint256 constant CLIENT_IDX = 0; // Channel creator +uint256 constant SERVER_IDX = 1; // Broker in clearnet context +``` + +**Design Rationale**: +- **Complexity reduction**: Two-party channels eliminate exponential complexity growth seen in n-party protocols +- **Hub efficiency**: Brokers act as liquidity hubs, enabling users to interact with all other users through a single channel +- **UX optimization**: Users maintain one channel instead of managing multiple peer connections +- **Capital efficiency**: Liquidity is consolidated rather than fragmented across many channels + +## Core Design Decisions + +### Magic Number Protocol + +Nitrolite uses explicit "magic numbers" to indicate state transition intentions: + +```solidity +uint32 constant CHANOPEN = 7877; // Funding state +uint32 constant CHANCLOSE = 7879; // Closing state +uint32 constant CHANRESIZE = 7883; // Resize state +``` + +**Design Rationale**: This prevents accidental state transitions and provides clear semantic meaning. Instead of inferring intent from state changes, the protocol requires explicit declaration, improving safety and debuggability. + +### Chain-Agnostic Signature Model + +The protocol deliberately avoids EIP-191 message prefixing: + +> "For signature verification, the stateHash is bare signed without EIP-191 since the protocol is intended to be chain-agnostic." + +**Design Rationale**: This enables identical protocol logic across all EVM-compatible chains without modification, supporting the multi-chain vision where users can choose their preferred blockchain based on fees and speed. + +### Double-Entry Ledger System + +ClearNode implements traditional double-entry accounting for off-chain balance tracking: + +```go +type Entry struct { + AccountID string + Credit decimal.Decimal + Debit decimal.Decimal + // Credits always equal debits across the system +} +``` + +**Design Rationale**: +- **Auditability**: Complete transaction history with immutable records +- **Consistency**: Mathematical guarantee that books balance +- **Compliance**: Follows established financial accounting practices +- **Precision**: Uses decimal arithmetic to avoid floating-point errors in financial calculations + +## Technology Choice Rationale + +### Go for ClearNode Backend + +**Why Go over Node.js/Python/Rust?** +- **Concurrency model**: Goroutines efficiently handle multiple blockchain event listeners simultaneously +- **Memory efficiency**: Better resource usage for long-running services compared to Node.js +- **Crypto libraries**: Excellent native support for ECDSA and Ethereum integration +- **Operational simplicity**: Single binary deployment with built-in monitoring +- **Network performance**: Superior WebSocket handling for real-time messaging requirements + +### TypeScript SDK Design + +**Why TypeScript over pure JavaScript?** +- **Type safety**: Prevents runtime errors in financial applications +- **Developer experience**: Excellent IDE support and autocomplete +- **Viem integration**: Built on modern, well-maintained Ethereum libraries +- **Multi-framework support**: Works with React, Vue, Angular without framework lock-in + +## Security Architecture + +### Challenge-Response Dispute Resolution + +Instead of requiring real-time monitoring, Nitrolite uses **optimistic execution** with challenge periods: + +```solidity +function challenge(bytes32 channelId, State calldata candidate, State[] calldata proofs) external { + // Economic security through time-locked disputes + meta.challengeExpire = block.timestamp + meta.chan.challenge; + meta.stage = ChannelStatus.DISPUTE; +} +``` + +**Design Rationale**: This provides economic security while allowing participants to be offline for extended periods. The system assumes good behavior but provides recourse through cryptographically verifiable challenges. + +### Interface Segregation Pattern + +The protocol separates concerns through focused interfaces: + +```solidity +interface IChannel { // Channel lifecycle +interface IDeposit { // Token custody +interface IAdjudicator { // Application logic +interface IComparable { // State ordering +``` + +**Design Rationale**: This modularity allows applications to implement only the interfaces they need, reducing attack surface and enabling compositability. + +## Virtual Application Architecture + +### Session Keys and Delegation + +Nitrolite supports "session keys" where channel creators can be different from fund providers: + +```solidity +// NOTE: it is allowed for depositor (and wallet) to be different from channel creator (participant) +// This enables logic of "session keys" where a user can create a channel on behalf of another account +``` + +**Design Rationale**: This separation improves UX by allowing high-frequency signing without exposing main wallet private keys. Applications can use hot wallets for state updates while keeping funds secured by cold storage. + +### Quorum-Based Virtual Ledger Channels + +Applications create isolated accounting contexts within channels: + +```go +// Virtual applications require quorum approval for state transitions +if totalWeight < int64(appSession.Quorum) { + return fmt.Errorf("quorum not met: %d / %d", totalWeight, appSession.Quorum) +} +``` + +**Design Rationale**: This enables complex multi-party applications (games, voting, auctions) while maintaining the simplicity of two-party channels. Virtual applications handle business logic while the underlying channel provides security guarantees. + +## Multi-Chain Strategy + +### Asset Management Philosophy + +Each token-chain combination is treated as a distinct asset: + +```go +type Asset struct { + Token string `gorm:"column:token;primaryKey"` + ChainID uint32 `gorm:"column:chain_id;primaryKey"` + Symbol string `gorm:"column:symbol;not null"` + Decimals uint8 `gorm:"column:decimals;not null"` +} +``` + +**Design Rationale**: This prevents cross-chain confusion and enables proper decimal handling. Users explicitly choose their blockchain based on preferences for fees, speed, and finality. + +### Independent Chain Operation + +Each blockchain maintains separate custody contracts and event listeners: + +```go +// Concurrent event listeners for multiple chains +for name, network := range config.networks { + go client.ListenEvents(context.Background()) +} +``` + +**Design Rationale**: Chain independence reduces systemic risk and allows the protocol to continue operating even if individual blockchains experience issues. + +## Trade-off Analysis + +### Centralization vs. User Experience + +**Trade-off**: Uses broker hub model instead of fully peer-to-peer architecture +- **Cost**: Introduces potential central points of failure +- **Benefit**: Dramatically improves UX (one channel vs. many), enables instant liquidity +- **Mitigation**: Users retain custody through on-chain contracts; multiple brokers can compete + +### Flexibility vs. Complexity + +**Trade-off**: Fixed 2-party channels instead of n-party support +- **Cost**: Cannot directly support complex multi-party protocols +- **Benefit**: Eliminates exponential complexity growth, easier security analysis +- **Mitigation**: Virtual ledger channels enable arbitrary multi-party applications + +### Performance vs. Decentralization + +**Trade-off**: Go backend service instead of pure smart contract execution +- **Cost**: Requires trust in ClearNode operators +- **Benefit**: Enables complex business logic, high throughput, better UX +- **Mitigation**: Challenge/response mechanism ensures trustless dispute resolution + +## Protocol Innovation + +### Array-Based RPC Messages + +Uses deterministic array serialization instead of object-based JSON: + +```json +{ + "req": [REQUEST_ID, METHOD, [PARAMETERS], TIMESTAMP], + "sig": ["SIGNATURE"] +} +``` + +**Design Rationale**: Arrays provide deterministic serialization order, ensuring consistent signature generation across different JSON implementations. This prevents signature verification failures due to property ordering differences. + +### Intent-Based State Validation + +Different state types follow different validation rules: + +```go +type Intent uint8 +const ( + IntentOPERATE Intent = 0 // Normal application states + IntentINITIALIZE Intent = 1 // Channel funding states + IntentRESIZE Intent = 2 // Capacity adjustment states + IntentFINALIZE Intent = 3 // Channel closing states +) +``` + +**Design Rationale**: This provides type safety and enables protocol evolution. New intent types can be added without breaking existing validation logic, supporting future protocol upgrades. + +--- + +This architecture represents a pragmatic approach to state channels that prioritizes practical usability while maintaining cryptographic security guarantees. Each design decision reflects careful consideration of the trade-offs inherent in distributed financial systems. diff --git a/docs/deployment/index.md b/docs/deployment/index.md new file mode 100644 index 0000000..edbb90b --- /dev/null +++ b/docs/deployment/index.md @@ -0,0 +1,231 @@ +--- +title: Production Deployment +description: Guide for deploying Nitrolite applications to production +keywords: [deployment, production, infrastructure, devops, scaling] +--- + +# Production Deployment + +Guide for deploying Nitrolite applications and ClearNode infrastructure to production environments. + +## Deployment Overview + +### Architecture Components + +
+```mermaid +graph TB + subgraph "Client Applications" + A[Web App] --> B[TypeScript SDK] + C[Mobile App] --> B + B --> D[WebSocket Connection] + end + + subgraph "ClearNode Infrastructure" + D --> E[ClearNode Server] + E --> F[PostgreSQL/SQLite] + E --> G[Prometheus Metrics] + end + + subgraph "Blockchain Infrastructure" + E --> H[Ethereum RPC] + H --> I[Custody Contracts] + E --> J[Multi-Chain Support] + end + + style A fill:#e3f2fd + style E fill:#f3e5f5 + style H fill:#fff3e0 +``` +
+ +## Prerequisites + +### ClearNode Requirements + +**Minimum Production Environment:** +- **Compute:** 2 vCPU, 4GB RAM for ClearNode +- **Storage:** 50GB SSD with automated backups +- **Network:** Stable internet connection with WebSocket support +- **Database:** PostgreSQL 13+ or SQLite for development + +**Recommended Production Environment:** +- **Compute:** 4 vCPU, 8GB RAM with monitoring +- **Storage:** 200GB SSD with daily backups +- **Network:** Load balancer with SSL termination +- **Monitoring:** Prometheus metrics collection + +### Software Dependencies + +**ClearNode Server:** +- **Go:** 1.21+ for building ClearNode +- **Database:** PostgreSQL 13+ (production) or SQLite (development) +- **RPC Access:** Infura, Alchemy, or self-hosted Ethereum nodes + +## Environment Configuration + +### ClearNode Environment Variables + +```bash +# Core Configuration +BROKER_PRIVATE_KEY=0x1234567890123456789012345678901234567890123456789012345678901234 +DATABASE_DRIVER=postgres +DATABASE_URL=postgresql://user:password@localhost:5432/clearnode +LOG_LEVEL=info +HTTP_PORT=8000 +METRICS_PORT=4242 + +# Multi-Chain Network Configuration +POLYGON_INFURA_URL=https://polygon-mainnet.infura.io/v3/YOUR_INFURA_KEY +POLYGON_CUSTODY_CONTRACT_ADDRESS=0xDB33fEC4e2994a675133320867a6439Da4A5acD8 + +CELO_INFURA_URL=https://celo-mainnet.infura.io/v3/YOUR_INFURA_KEY +CELO_CUSTODY_CONTRACT_ADDRESS=0xDB33fEC4e2994a675133320867a6439Da4A5acD8 + +BASE_INFURA_URL=https://base-mainnet.infura.io/v3/YOUR_INFURA_KEY +BASE_CUSTODY_CONTRACT_ADDRESS=0xYOUR_BASE_CONTRACT_ADDRESS + +ETH_SEPOLIA_INFURA_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY +ETH_SEPOLIA_CUSTODY_CONTRACT_ADDRESS=0xYOUR_SEPOLIA_CONTRACT_ADDRESS + +# Optional Configuration +MSG_EXPIRY_TIME=60 # Message timestamp expiry in seconds +``` + + +## ClearNode Deployment + +## 1. Database Setup + +### PostgreSQL + +Start PostgreSQL container: +```bash +docker run --rm -e POSTGRES_PASSWORD=postgres -e POSTGRES_HOST_AUTH_METHOD=trust -p 5432:5432 --name postgres postgres:16 +``` + +### SQLite + +```bash +# For development, ClearNode can use SQLite +DATABASE_DRIVER=sqlite +DATABASE_URL=clearnode.db +``` + +### Building from Source + +```bash +# Clone the repository +git clone https://github.com/erc7824/nitrolite.git +cd nitrolite/clearnode + +# Set environment variables +cp .env.example .env +# Edit .env with your configuration + +# Build the binary +go build -o clearnode ./... + +# Run ClearNode +./clearnode +``` + +### Docker Deployment + +```bash +# Build and run with Docker +docker build -t clearnode . + +# Start ClearNode +docker run -p 8000:8000 -p 4242:4242 --env-file .env clearnode +``` + +## Monitoring and Observability + +### Prometheus Metrics + +ClearNode provides built-in Prometheus metrics on port 4242: + +```bash +# Access metrics endpoint +curl http://localhost:4242/metrics +``` + +### Key Metrics + +```prometheus +# Channel and ledger metrics +clearnode_channels_total +clearnode_active_connections +clearnode_ledger_balance_total +clearnode_rpc_requests_total +clearnode_errors_total + +# System metrics +clearnode_memory_usage_bytes +clearnode_cpu_usage_percent +clearnode_database_connections +``` + +## Security and Maintenance + +### ClearNode Security + +**Private Key Management:** +- Store `BROKER_PRIVATE_KEY` securely using environment variables +- Use hardware security modules (HSMs) for production private keys + +**Network Security:** +- Use WSS for WebSocket connections in production +- Configure firewall to allow only ports 8000 (HTTP) and 4242 (metrics) + +### Database Backup and Recovery + +```bash +# PostgreSQL backup +pg_dump -h localhost -U clearnode_user clearnode > clearnode_backup.sql + +# Restore from backup +psql -h localhost -U clearnode_user clearnode < clearnode_backup.sql +``` + +### Health Checks + +**ClearNode Health Checks:** +```bash +# Check if ClearNode is responding +curl -f http://localhost:8000/health || exit 1 + +# Check WebSocket connectivity +# npm install -g wscat +wscat -c ws://localhost:8000/ws + +# Check metrics endpoint +curl http://localhost:4242/metrics +``` + +## Troubleshooting + +### Common Issues + +**ClearNode Port Conflicts:** +```bash +# Check what's using port 8000 +lsof -i :8000 + +# Change ClearNode HTTP port +HTTP_PORT=8080 +``` + +**ClearNode Database Issues:** +```bash +# Test PostgreSQL connectivity +psql $DATABASE_URL -c "SELECT 1;" + +# Check if ClearNode migrations ran +# ClearNode runs migrations automatically on startup +``` + +## Next Steps + +For ClearNode support, refer to the [ClearNode documentation](https://github.com/erc7824/nitrolite/tree/main/clearnode) or join [Discord](https://discord.gg/yellownetwork). diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 0000000..3521b0a --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,175 @@ +--- +title: Frequently Asked Questions +description: Common questions and answers about Nitrolite and state channels +keywords: [faq, questions, answers, help, support, state channels, nitrolite] +--- + +# Frequently Asked Questions + +Common questions and answers about Nitrolite, state channels, and ERC-7824. + +## Getting Started + +### What is Nitrolite? + +Nitrolite is a lightweight state channel framework that enables off-chain interactions between participants with on-chain security guarantees. It provides custody of ERC-20 tokens and native assets, mutual close capabilities, and challenge/response mechanisms for dispute resolution. + +### How do state channels work? + +State channels allow participants to conduct multiple transactions off-chain by: + +1. **Opening:** Locking funds in a smart contract +2. **Transacting:** Exchanging signed state updates off-chain +3. **Closing:** Settling the final state on-chain + +This reduces gas costs and enables instant transactions while maintaining security through cryptographic proofs and economic incentives. + +### What's the difference between Nitrolite and other scaling solutions? + +| Feature | Nitrolite | Optimistic Rollups | ZK Rollups | Sidechains | +|---------|-----------|-------------------|------------|------------| +| **Finality** | Instant | ~7 days | ~10 min | Variable | +| **Gas Costs** | Minimal | Reduced | Reduced | Low | +| **Security** | Ethereum-level | Ethereum-level | Ethereum-level | Sidechain-level | +| **Complexity** | Medium | Low | High | Low | +| **Use Cases** | P2P interactions | General purpose | General purpose | High throughput | + +## Technical Questions + +### What programming languages are supported? + +**Client SDK:** +- **TypeScript/JavaScript:** Full-featured SDK built on Viem for browser and Node.js +- **Framework Agnostic:** Works with React, Vue, Angular, and other modern frameworks + +**ClearNode Server:** +- **Go:** Complete server implementation with PostgreSQL/SQLite support +- **Multi-chain:** Supports Polygon, Celo, Base, Ethereum, and other EVM chains + +**Smart Contracts:** +- **Solidity:** Core contracts implementing IChannel, IDeposit, and IAdjudicator interfaces + +### What are the security guarantees? + +**Cryptographic Security:** +- All state transitions require valid ECDSA signatures +- State integrity protected by cryptographic hashes +- Replay protection through nonces + +**Economic Security:** +- Participants lock funds as commitment to honest behavior +- Challenge mechanisms provide recourse against disputes +- Penalty systems deter malicious actors + +**Operational Security:** +- Two-party channels with Creator/Broker roles +- Challenge periods for dispute resolution +- On-chain adjudication through smart contracts + +### How fast are transactions? + +**Performance Metrics:** +- **State Updates:** < 50ms average latency +- **Throughput:** 1000+ transactions per second per channel +- **Network Propagation:** < 100ms globally +- **Dispute Resolution:** Varied (challenge period is defined by the application) + +**Factors Affecting Speed:** +- Network connectivity and latency +- ClearNode performance and location +- WebSocket connection quality +- Database performance for ledger operations + +## Development + +### How do I get started? + +See the [Quick Start Guide](./quick_start/) for installation and setup instructions. + +### What types of applications can I build? + +**Gaming Applications:** +- Turn-based games (chess, poker, strategy games) +- Real-time multiplayer games with state synchronization +- Betting and prediction markets +- Virtual asset trading + +**Financial Applications:** +- Payment systems and micropayments +- Decentralized exchanges (order books) +- Lending and borrowing protocols +- Insurance and derivatives + +**Communication & Social:** +- Messaging apps with payment integration +- Social media with content monetization +- Collaborative editing and version control +- Digital content marketplace + +**Enterprise Solutions:** +- Supply chain tracking +- Multi-party business processes +- B2B payment networks +- IoT device coordination + + +### Can I integrate with existing smart contracts? + +Yes! Nitrolite supports custom adjudicator contracts: + +```solidity +contract MyGameApp is IAdjudicator { + function adjudicate( + Channel calldata chan, + State calldata candidate, + State[] calldata proofs + ) external view override returns (bool valid) { + // Implement your game-specific state validation + return validateGameMove(candidate, proofs); + } +} +``` + +**Integration Points:** +- **Custom Adjudicators:** Implement `IAdjudicator` interface for application logic +- **Asset Support:** ERC-20 tokens and native assets through Custody contract +- **Multi-Chain:** Deploy on any EVM-compatible blockchain + +## Network and Infrastructure + +### Do I need to run my own infrastructure? + +**Options Available:** + +**1. Self-Hosted (Recommended):** +- Run your own ClearNode instances using Go binary or Docker +- Full control over infrastructure and configuration +- Connect to your preferred blockchain RPC providers +- PostgreSQL or SQLite database options + +**2. Public ClearNodes:** +- Connect to community-operated ClearNode instances +- Suitable for development and testing +- No guaranteed uptime or support + +**3. Enterprise:** +- Deploy ClearNode clusters with load balancing +- Multi-region redundancy +- Custom monitoring and alerting + +## Where can I get help? + +**Documentation:** +- **[User Guides](./quick_start/):** Step-by-step tutorials +- **[API Reference](./nitrolite_client/):** Complete SDK documentation +- **[Troubleshooting](./troubleshooting):** Common issues and solutions + +**Links:** +- **[GitHub](https://github.com/erc7824/nitro):** Technical discussions and issues +- **[Discord](https://discord.gg/yellownetwork):** Community & real-time chat support + +--- + +## Still Have Questions? + +Check [GitHub](https://github.com/erc7824/nitro) or join [Discord](https://discord.gg/yellownetwork) for support. diff --git a/docs/security/index.md b/docs/security/index.md new file mode 100644 index 0000000..29c88f4 --- /dev/null +++ b/docs/security/index.md @@ -0,0 +1,360 @@ +--- +title: Security +description: Security considerations and best practices for Nitrolite applications +keywords: [security, best practices, vulnerabilities, smart contracts, state channels] +--- + +# Security + +Comprehensive security guidance for building secure applications with Nitrolite. + +## Security Model + +### Core Security Principles + +**1. Cryptographic Security** +- All state transitions require valid ECDSA signatures from participants +- State integrity protected by cryptographic hashing (keccak256) +- Version control prevents replay attacks through incremental state versioning + +**2. Economic Security** +- Participants lock funds in Custody contract as commitment +- Challenge mechanisms provide dispute resolution +- Configurable challenge periods for time-bounded operations + +**3. Two-Party Model** +- Simplified security with exactly two participants: Creator and Broker +- Clear role definitions reduce attack surface +- Deterministic state transitions based on participant signatures + +
+```mermaid +graph TB + subgraph "Security Layers" + A[Smart Contracts] --> B[ClearNode] + B --> C[TypeScript SDK] + C --> D[Application] + end + + subgraph "Threat Vectors" + E[Signature Forgery] --> A + F[State Manipulation] --> A + G[Network Attacks] --> B + H[Client Vulnerabilities] --> C + I[Application Logic] --> D + end + + style A fill:#ffebee + style B fill:#e8f5e8 + style C fill:#fff3e0 + style D fill:#e3f2fd +``` +
+ +## Threat Analysis + +### 1. **Signature-Based Attacks** + +**Threat:** Malicious actors attempting to forge or replay signatures. + +**Mitigations:** +```typescript +// Always verify signatures before processing +import { verifyMessage } from 'viem'; + +const isValid = await verifyMessage({ + address: expectedSigner, + message: stateHash, + signature: signature +}); + +if (!isValid) { + throw new Error('Invalid signature'); +} + +// Check version number to prevent replays +if (newState.version <= currentState.version) { + throw new Error('Invalid state version'); +} + +// Verify signer is expected participant (Creator or Broker) +const expectedSigner = state.version % 2 === 1 ? creatorAddress : brokerAddress; +if (recoveredSigner !== expectedSigner) { + throw new Error('Unauthorized signer'); +} +``` + +### 2. **State Manipulation** + +**Threat:** Attempting to submit invalid or malicious state updates. + +**Mitigations:** +```typescript +// Implement comprehensive state validation +function validateStateTransition( + channel: Channel, + prevState: State, + newState: State, + adjudicator: IAdjudicator +): boolean { + // Basic validation + if (newState.version !== prevState.version + 1) { + return false; + } + + // Adjudicator validation + return adjudicator.adjudicate(channel, newState, [prevState]); +} + +// Always validate locally before broadcasting +const isValid = validateStateTransition(channel, currentState, proposedState, adjudicator); +if (!isValid) { + throw new Error('Invalid state transition'); +} +``` + +### 3. **Network-Level Attacks** + +**Threat:** Eclipse attacks, message interception, or network partitioning. + +**Mitigations:** +```typescript +// Use multiple ClearNode connections for redundancy +const clearNodeUrls = [ + 'wss://clearnode1.example.com/ws', + 'wss://clearnode2.example.com/ws' +]; + +// Implement WebSocket reconnection logic +class SecureWebSocketClient { + private ws: WebSocket; + private reconnectAttempts = 0; + private maxReconnectAttempts = 5; + + connect(url: string) { + this.ws = new WebSocket(url); + + this.ws.onclose = () => { + if (this.reconnectAttempts < this.maxReconnectAttempts) { + setTimeout(() => { + this.reconnectAttempts++; + this.connect(url); + }, Math.pow(2, this.reconnectAttempts) * 1000); + } + }; + } + + // Verify message integrity + async sendMessage(message: RPCMessage) { + // Add timestamp for freshness + message.timestamp = Date.now(); + + // Sign message with private key + message.sig = await this.signMessage(message); + + this.ws.send(JSON.stringify(message)); + } +} +``` + +### 4. **Smart Contract Vulnerabilities** + +**Threat:** Bugs in smart contract logic enabling fund theft or manipulation. + +**Prevention Measures:** +- **Battle-tested patterns:** Built on established state channel designs +- **Interface segregation:** Clear separation between IChannel, IDeposit, and IAdjudicator +- **Access controls:** Role-based permissions for critical functions +- **Atomic operations:** State changes happen atomically + +```solidity +// Example: Custody contract security patterns +contract Custody is IChannel, IDeposit { + // Constants for participant roles + uint256 constant CREATOR = 0; + uint256 constant BROKER = 1; + + // Reentrancy protection + modifier nonReentrant() { + require(!_locked, "Reentrant call"); + _locked = true; + _; + _locked = false; + } + + // Access control for channel operations + modifier onlyParticipant(bytes32 channelId) { + require(isParticipant(channelId, msg.sender), "Not authorized"); + _; + } + + function close(bytes32 channelId, State calldata candidate, State[] calldata proofs) + external + onlyParticipant(channelId) + nonReentrant() + { + // Withdrawal logic with checks-effects-interactions pattern + } +} +``` + +## Best Practices + +### 1. **Private Key Management** + +**Secure Storage:** +```typescript +// NEVER store private keys in code or localStorage +// Use environment variables or secure key management + +// Good: Environment variable +const privateKey = process.env.PRIVATE_KEY; + +// Better: Hardware wallet integration +import { LedgerSigner } from '@ledgerhq/hw-app-eth'; + +// Best: Key management service +import { KMSClient } from '@aws-sdk/client-kms'; +``` + +**ClearNode Key Security:** +```bash +# Store broker private key securely +export BROKER_PRIVATE_KEY="0x..." + +# Use file permissions to protect .env +chmod 600 .env + +# Rotate keys regularly +# Generate new key and update ClearNode configuration +``` + +### 2. **Message Verification** + +**RPC Message Validation:** +```typescript +// Validate RPC message structure +function validateRPCMessage(message: RPCMessage): boolean { + // Check required fields + if (!message.req && !message.res) { + return false; + } + + // Verify timestamp freshness (prevent replay) + const maxAge = 60000; // 1 minute + const messageAge = Date.now() - message.timestamp; + if (messageAge > maxAge) { + return false; + } + + // Verify signatures + return message.sig.every(sig => verifySignature(message, sig)); +} +``` + +### 3. **Network Security** + +**ClearNode Configuration:** +```bash +# Use TLS/SSL for production +CLEARNODE_WS_URL=wss://your-domain.com/ws + +# Implement rate limiting +MSG_EXPIRY_TIME=60 + +# Database security +DATABASE_URL=postgresql://user:pass@localhost:5432/clearnode?sslmode=require +``` + +**Client-Side Security:** +```typescript +// Validate message timestamps +function isMessageFresh(timestamp: number): boolean { + const maxAge = 60000; // 1 minute + return Date.now() - timestamp < maxAge; +} + +// Verify WebSocket connection +const ws = new WebSocket('wss://clearnode.example.com/ws'); +ws.onopen = () => { + // Verify SSL certificate + if (location.protocol !== 'https:') { + console.warn('Insecure connection detected'); + } +}; +``` + +## Production Security + +### 1. **Infrastructure Hardening** + +**ClearNode Deployment:** +- Use reverse proxy (nginx) with SSL termination +- Implement rate limiting and request filtering +- Monitor for unusual connection patterns +- Keep database connections secure and encrypted + +**Key Management:** +- Store private keys in secure environment variables +- Use hardware security modules (HSMs) for high-value operations +- Implement key rotation procedures +- Never commit private keys to version control + +### 2. **Monitoring and Alerts** + +**Security Metrics:** +```bash +# Monitor ClearNode metrics +curl http://localhost:4242/metrics | grep clearnode_ + +# Key metrics to watch: +# - clearnode_errors_total +# - clearnode_active_connections +# - clearnode_rpc_requests_total +``` + +**Alert Conditions:** +- High error rates (>5% over 5 minutes) +- Unusual connection patterns +- Failed authentication attempts +- Database connection issues + +### 3. **Incident Response** + +**Emergency Procedures:** +1. **Identify the threat** - Monitor logs and metrics +2. **Isolate affected systems** - Disable compromised connections +3. **Assess impact** - Check channel states and fund security +4. **Restore operations** - Deploy fixed version or revert changes +5. **Post-incident review** - Analyze root cause and improve security + +## Security Checklist + +**Before Production:** +- [ ] Private keys stored securely +- [ ] SSL/TLS enabled for all connections +- [ ] Database connections encrypted +- [ ] Rate limiting implemented +- [ ] Monitoring and alerting configured +- [ ] Backup and recovery procedures tested +- [ ] Security team contact information documented + +**Regular Maintenance:** +- [ ] Monitor security metrics daily +- [ ] Review logs for suspicious activity +- [ ] Update dependencies regularly +- [ ] Rotate private keys quarterly +- [ ] Test backup and recovery procedures +- [ ] Review and update incident response procedures + +## Resources + +### Security Contacts +- **General Issues:** [GitHub Issues](https://github.com/erc7824/nitrolite/issues) +- **Security Vulnerabilities:** [Responsible Disclosure](https://github.com/erc7824/nitrolite/security) +- **Community Support:** [Discord](https://discord.gg/yellownetwork) + +### Documentation +- **[Architecture Overview](../architecture/)** - System design and security model +- **[Deployment Guide](../deployment/)** - Production deployment security +- **[Troubleshooting](../troubleshooting)** - Common security issues and solutions \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..752ead2 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,519 @@ +--- +title: Troubleshooting +description: Common issues and solutions when working with Nitrolite +keywords: [troubleshooting, debug, errors, common issues, nitrolite, support] +--- + +# Troubleshooting + +Common issues and solutions when working with the Nitrolite SDK. + +## Installation Issues + +### Node.js Version Compatibility + +**Problem:** Getting errors during installation or runtime related to Node.js version. + +**Solution:** +```bash +# Check your current Node.js version +node --version + +# Nitrolite requires Node.js 18+ +# Install using nvm (recommended) +nvm install 18 +nvm use 18 +npm install @erc7824/nitrolite +``` + +### Package Resolution Errors + +**Problem:** `Cannot resolve module` or `Module not found` errors. + +**Solution:** +```bash +# Clear npm cache +npm cache clean --force + +# Delete node_modules and reinstall +rm -rf node_modules package-lock.json +npm install + +# For yarn users +yarn cache clean +rm -rf node_modules yarn.lock +yarn install +``` + +### Webpack/Bundler Issues + +**Problem:** Build errors in React, Vue, or other frameworks. + +**Solution for Webpack 5:** +```javascript +// webpack.config.js or next.config.js +module.exports = { + resolve: { + fallback: { + "crypto": require.resolve("crypto-browserify"), + "stream": require.resolve("stream-browserify"), + "buffer": require.resolve("buffer"), + "process": require.resolve("process/browser"), + } + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'], + process: 'process/browser', + }), + ], +}; +``` + +## Connection Issues + +### WebSocket Connection Failed + +**Problem:** Cannot connect to ClearNode. + +**Solution:** +```javascript +// Check your WebSocket URL format +const ws = new WebSocket('wss://your-clearnode-domain.com/ws'); + +ws.onerror = (error) => { + console.error('WebSocket connection failed:', error); + + // Check if ClearNode is running + console.log('ClearNode should be running on port 8000'); + + // Implement retry logic + if (ws.readyState === WebSocket.CLOSED) { + setTimeout(() => { + console.log('Retrying connection...'); + // Retry connection with exponential backoff + }, Math.min(1000 * Math.pow(2, retryCount), 30000)); + } +}; + +// For local development +const localWs = new WebSocket('ws://localhost:8000/ws'); +``` + +### Provider Connection Issues + +**Problem:** Ethereum provider not connecting or throwing errors. + +**Solution:** +```javascript +// Check if MetaMask is installed +if (typeof window.ethereum === 'undefined') { + throw new Error('MetaMask not installed'); +} + +// Request account access +try { + await window.ethereum.request({ method: 'eth_requestAccounts' }); +} catch (error) { + if (error.code === 4001) { + console.error('User rejected the request'); + } else { + console.error('Unexpected error:', error); + } +} + +// Check network (example for Polygon) +const chainId = await window.ethereum.request({ method: 'eth_chainId' }); +if (chainId !== '0x89') { // Polygon Mainnet + // Request network switch + await window.ethereum.request({ + method: 'wallet_switchEthereumChain', + params: [{ chainId: '0x89' }], + }); +} +``` + +## Channel Operations + +### Channel Creation Failing + +**Problem:** Channel creation transactions fail or timeout. + +**Common Causes & Solutions:** + +1. **Insufficient Gas:** +```typescript +// Use Viem for gas estimation +import { estimateGas } from 'viem/actions'; + +const gasEstimate = await estimateGas(publicClient, { + account: account.address, + to: custodyContractAddress, + data: callData, +}); + +// Add 20% buffer +const gasLimit = (gasEstimate * 120n) / 100n; +``` + +2. **Insufficient Balance:** +```typescript +// Check balance before operations +import { getBalance } from 'viem/actions'; + +const balance = await getBalance(publicClient, { + address: account.address, +}); + +const requiredAmount = parseEther("0.1"); +if (balance < requiredAmount) { + throw new Error(`Insufficient balance. Required: ${formatEther(requiredAmount)} ETH`); +} +``` + +3. **Wrong Network:** +```typescript +// Verify you're on the correct network +const chainId = await publicClient.getChainId(); +const expectedChainId = 137; // Polygon + +if (chainId !== expectedChainId) { + throw new Error(`Wrong network. Expected ${expectedChainId}, got ${chainId}`); +} +``` + +### State Update Failures + +**Problem:** Off-chain state updates not being accepted. + +**Solution:** +```typescript +// Check state version progression +const currentState = await channel.getState(); +console.log('Current version:', currentState.version); + +// Ensure proper participant signing +const expectedSigner = currentState.version % 2 === 0 ? creatorAddress : brokerAddress; +console.log('Expected signer:', expectedSigner); + +// Verify signature with Viem +import { verifyMessage } from 'viem'; + +const isValid = await verifyMessage({ + address: expectedSigner, + message: stateHash, + signature: signature +}); + +if (!isValid) { + throw new Error('Invalid signature'); +} +``` + +## Common Error Codes + +### Error: `INVALID_SIGNATURE` + +**Cause:** Signature verification failed. + +**Solution:** +```typescript +// Ensure you're signing the correct state hash format +import { keccak256, encodePacked } from 'viem'; + +const stateHash = keccak256(encodePacked( + ['bytes32', 'bytes', 'uint256', 'tuple(address,address,uint256)[]'], + [channelId, state.data, state.version, state.allocations] +)); + +const signature = await walletClient.signMessage({ + account, + message: { raw: stateHash } +}); + +// Verify signature locally +const isValid = await verifyMessage({ + address: account.address, + message: { raw: stateHash }, + signature +}); +``` + +### Error: `CHANNEL_NOT_FOUND` + +**Cause:** Trying to operate on a non-existent channel. + +**Solution:** +```typescript +// Verify channel exists on-chain +import { readContract } from 'viem/actions'; + +const channelExists = await readContract(publicClient, { + address: custodyContractAddress, + abi: custodyAbi, + functionName: 'channelExists', + args: [channelId] +}); + +if (!channelExists) { + console.error('Channel does not exist:', channelId); + // Create channel first or verify channel ID calculation +} + +// Check channel status +const channelStatus = await readContract(publicClient, { + address: custodyContractAddress, + abi: custodyAbi, + functionName: 'getChannelStatus', + args: [channelId] +}); +``` + +### Error: `INSUFFICIENT_FUNDS` + +**Cause:** Not enough funds in channel for operation. + +**Solution:** +```javascript +// Check channel balance +const balance = await nitroliteClient.getChannelBalance(channelId); +console.log('Channel balance:', balance); + +// Check individual allocations +const allocations = await nitroliteClient.getAllocations(channelId); +console.log('Current allocations:', allocations); + +// Resize channel if needed +if (balance.lt(requiredAmount)) { + await nitroliteClient.resizeChannel({ + channelId, + additionalAmount: requiredAmount.sub(balance), + }); +} +``` + +## Performance Issues + +### Slow Transaction Processing + +**Problem:** Transactions taking too long to process. + +**Solutions:** + +1. **Optimize Gas Settings:** +```javascript +// Use EIP-1559 gas pricing +const tx = await nitroliteClient.deposit({ + // ... other params + maxFeePerGas: ethers.parseUnits('20', 'gwei'), + maxPriorityFeePerGas: ethers.parseUnits('2', 'gwei'), +}); +``` + +2. **Batch Operations:** +```javascript +// Instead of multiple single operations +const operations = [ + { type: 'transfer', to: addr1, amount: '100' }, + { type: 'transfer', to: addr2, amount: '200' }, +]; + +// Batch them in a single state update +await nitroliteClient.batchOperations(channelId, operations); +``` + +### Memory Leaks + +**Problem:** Memory usage growing over time. + +**Solution:** +```javascript +// Properly close WebSocket connections +const cleanup = () => { + if (ws) { + ws.close(); + ws = null; + } + + // Remove event listeners + window.removeEventListener('beforeunload', cleanup); +}; + +window.addEventListener('beforeunload', cleanup); + +// Clean up client resources +await nitroliteClient.disconnect(); +``` + +## Development Environment + +### TypeScript Errors + +**Problem:** Type errors when using TypeScript. + +**Solution:** +```bash +# Install type definitions +npm install --save-dev @types/node @types/ws + +# Add to tsconfig.json +{ + "compilerOptions": { + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "skipLibCheck": true + } +} +``` + +### Hot Reload Issues + +**Problem:** Changes not reflecting during development. + +**Solution:** +```javascript +// For webpack-dev-server +module.exports = { + devServer: { + hot: true, + // Add polling for file changes + watchOptions: { + poll: 1000, + }, + }, +}; +``` + +## ClearNode Issues + +### ClearNode Won't Start + +**Problem:** ClearNode binary fails to start. + +**Solution:** +```bash +# Check if required environment variables are set +echo $BROKER_PRIVATE_KEY +echo $DATABASE_URL + +# Check if ports are available +lsof -i :8000 # HTTP port +lsof -i :4242 # Metrics port + +# Check database connectivity +psql $DATABASE_URL -c "SELECT 1;" + +# Start with debug logging +LOG_LEVEL=debug ./clearnode +``` + +### Database Connection Errors + +**Problem:** ClearNode can't connect to database. + +**Solution:** +```bash +# For PostgreSQL +psql $DATABASE_URL -c "SELECT version();" + +# Check database permissions +psql $DATABASE_URL -c "CREATE TABLE test_table (id int);" +psql $DATABASE_URL -c "DROP TABLE test_table;" + +# For SQLite +DATABASE_DRIVER=sqlite DATABASE_URL=clearnode.db ./clearnode +``` + +### RPC Connection Issues + +**Problem:** Blockchain RPC calls failing. + +**Solution:** +```bash +# Test RPC connectivity +curl -X POST $POLYGON_INFURA_URL \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' + +# Check rate limits +echo "Check your Infura/Alchemy rate limits" + +# Use alternative RPC providers +POLYGON_INFURA_URL=https://polygon-rpc.com ./clearnode +``` + +## Getting Debug Information + +### Enable Debug Logging + +```bash +# For ClearNode +LOG_LEVEL=debug ./clearnode + +# For client applications +localStorage.setItem('debug', 'nitrolite:*'); +``` + +### Inspect WebSocket Messages + +```javascript +// Log all WebSocket messages +const originalSend = WebSocket.prototype.send; +WebSocket.prototype.send = function(data) { + console.log('WS Send:', data); + return originalSend.call(this, data); +}; + +ws.addEventListener('message', (event) => { + console.log('WS Receive:', event.data); +}); +``` + +### ClearNode Metrics + +```bash +# Check ClearNode health +curl http://localhost:4242/metrics + +# Monitor key metrics +curl http://localhost:4242/metrics | grep clearnode_errors_total +curl http://localhost:4242/metrics | grep clearnode_active_connections +``` + +### Export Debug Information + +```typescript +// Generate debug report for client issues +const debugInfo = { + sdkVersion: '@erc7824/nitrolite@1.0.0', + chainId: await publicClient.getChainId(), + blockNumber: await publicClient.getBlockNumber(), + userAddress: account.address, + balance: await getBalance(publicClient, { address: account.address }), + timestamp: new Date().toISOString(), +}; + +console.log('Debug Info:', JSON.stringify(debugInfo, null, 2)); +``` + +## Getting Help + +If you can't resolve your issue: + +1. **Check existing issues:** [GitHub Issues](https://github.com/erc7824/nitrolite/issues) +2. **Search documentation:** Use the search function on this site +3. **Join the community:** [Discord](https://discord.gg/yellownetwork) +4. **Report a bug:** Include debug information and steps to reproduce + +### When Reporting Issues + +Please include: +- Nitrolite SDK version (`@erc7824/nitrolite`) +- ClearNode version (if self-hosting) +- Node.js/browser version +- Operating system +- Network and contract addresses +- Code snippet that reproduces the issue +- Error messages and stack traces +- Debug information and metrics \ No newline at end of file From b838e159e87d42b1ac037660800e3b95aed8c6f6 Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 15:09:50 +0300 Subject: [PATCH 04/11] docs: add technical questions to FAQ --- docs/faq.md | 330 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 208 insertions(+), 122 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 3521b0a..f9bf4a2 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,175 +1,261 @@ --- -title: Frequently Asked Questions -description: Common questions and answers about Nitrolite and state channels -keywords: [faq, questions, answers, help, support, state channels, nitrolite] +title: Technical FAQ +description: Technical questions and implementation details for Nitrolite developers +keywords: [faq, technical, implementation, state channels, nitrolite, developers] --- -# Frequently Asked Questions +# Technical FAQ -Common questions and answers about Nitrolite, state channels, and ERC-7824. +Implementation details and technical questions for developers building with Nitrolite. -## Getting Started +## Protocol Implementation -### What is Nitrolite? +### How does the ClearNode RPC protocol work? -Nitrolite is a lightweight state channel framework that enables off-chain interactions between participants with on-chain security guarantees. It provides custody of ERC-20 tokens and native assets, mutual close capabilities, and challenge/response mechanisms for dispute resolution. +ClearNode uses JSON-RPC over WebSocket with deterministic array serialization: -### How do state channels work? +```json +{ + "req": [REQUEST_ID, METHOD, [PARAMETERS], TIMESTAMP], + "sig": ["ECDSA_SIGNATURE"] +} +``` -State channels allow participants to conduct multiple transactions off-chain by: +**Key Features:** +- **Array-based structure** ensures deterministic serialization across JSON implementations +- **Timestamp validation** with 60-second expiry prevents replay attacks +- **Session-based routing** via `AppSessionID` for virtual application isolation +- **Challenge-response authentication** using EIP-712 structured signing -1. **Opening:** Locking funds in a smart contract -2. **Transacting:** Exchanging signed state updates off-chain -3. **Closing:** Settling the final state on-chain +### How does signature verification work across different chains? -This reduces gas costs and enables instant transactions while maintaining security through cryptographic proofs and economic incentives. +Nitrolite uses **chain-agnostic signature verification** without EIP-191 prefixing: -### What's the difference between Nitrolite and other scaling solutions? +```go +// Raw ECDSA signing without chain-specific prefixes +messageHash := crypto.Keccak256Hash(stateBytes) +signature, _ := crypto.Sign(messageHash.Bytes(), privateKey) +``` -| Feature | Nitrolite | Optimistic Rollups | ZK Rollups | Sidechains | -|---------|-----------|-------------------|------------|------------| -| **Finality** | Instant | ~7 days | ~10 min | Variable | -| **Gas Costs** | Minimal | Reduced | Reduced | Low | -| **Security** | Ethereum-level | Ethereum-level | Ethereum-level | Sidechain-level | -| **Complexity** | Medium | Low | High | Low | -| **Use Cases** | P2P interactions | General purpose | General purpose | High throughput | +**Implementation Details:** +- **ECDSA with secp256k1** curve (Ethereum-compatible) +- **Keccak256 hashing** for message digests +- **65-byte signature format** (r,s,v) with v adjustment +- **Address recovery** using `crypto.SigToPub()` for authentication -## Technical Questions +### What is the channel state encoding format? -### What programming languages are supported? +States are ABI-encoded with a specific structure: -**Client SDK:** -- **TypeScript/JavaScript:** Full-featured SDK built on Viem for browser and Node.js -- **Framework Agnostic:** Works with React, Vue, Angular, and other modern frameworks +```go +// State encoding: (channelID, intent, version, stateData, allocations[]) +args := abi.Arguments{ + {Type: abi.Type{T: abi.FixedBytesTy, Size: 32}}, // channelID + {Type: intentType}, // intent (uint8) + {Type: versionType}, // version (uint256) + {Type: abi.Type{T: abi.BytesTy}}, // stateData + {Type: allocationType}, // allocations (tuple[]) +} +``` -**ClearNode Server:** -- **Go:** Complete server implementation with PostgreSQL/SQLite support -- **Multi-chain:** Supports Polygon, Celo, Base, Ethereum, and other EVM chains +**Intent Types:** +- `OPERATE(0)`: Normal application states +- `INITIALIZE(1)`: Channel funding states +- `RESIZE(2)`: Capacity adjustment states +- `FINALIZE(3)`: Channel closing states -**Smart Contracts:** -- **Solidity:** Core contracts implementing IChannel, IDeposit, and IAdjudicator interfaces +## Virtual Ledger System -### What are the security guarantees? +### How does the double-entry accounting work? -**Cryptographic Security:** -- All state transitions require valid ECDSA signatures -- State integrity protected by cryptographic hashes -- Replay protection through nonces +ClearNode implements traditional double-entry bookkeeping with DECIMAL(64,18) precision: -**Economic Security:** -- Participants lock funds as commitment to honest behavior -- Challenge mechanisms provide recourse against disputes -- Penalty systems deter malicious actors +```sql +CREATE TABLE ledger_entries ( + account_id VARCHAR NOT NULL, + wallet VARCHAR NOT NULL, + asset_symbol VARCHAR NOT NULL, + credit DECIMAL(64,18) NOT NULL, + debit DECIMAL(64,18) NOT NULL +); +``` -**Operational Security:** -- Two-party channels with Creator/Broker roles -- Challenge periods for dispute resolution -- On-chain adjudication through smart contracts +**Balance Calculation:** +```go +// Balance = SUM(credit) - SUM(debit) for each (wallet, asset) pair +balance := totalCredits.Sub(totalDebits) +``` -### How fast are transactions? +**Account Types:** +- **Participant accounts**: User wallet balances +- **Virtual app accounts**: Isolated application contexts +- **System accounts**: Protocol-level operations -**Performance Metrics:** -- **State Updates:** < 50ms average latency -- **Throughput:** 1000+ transactions per second per channel -- **Network Propagation:** < 100ms globally -- **Dispute Resolution:** Varied (challenge period is defined by the application) +### How do virtual applications achieve consensus? -**Factors Affecting Speed:** -- Network connectivity and latency -- ClearNode performance and location -- WebSocket connection quality -- Database performance for ledger operations +Virtual apps use **weighted quorum-based consensus** configured during channel creation: -## Development +```go +// Check if combined signature weights meet quorum threshold +if totalWeight < int64(appSession.Quorum) { + return fmt.Errorf("quorum not met: %d / %d", totalWeight, appSession.Quorum) +} +``` -### How do I get started? +**Weight Configuration:** +```go +type App struct { + Participants []address // Array of participants in the app + Weights []uint8 // Signers weights [50, 50, 80, 20, 20] + Quorum uint64 // Example: 100 would be the signature threshold +} +``` -See the [Quick Start Guide](./quick_start/) for installation and setup instructions. +**Consensus Flow:** +1. **State proposal** by any participant +2. **Signature collection** from participants until weight threshold met +3. **Validation** of weighted quorum achievement +4. **Ledger update** with atomic balance transfers -### What types of applications can I build? +**Example Scenarios:** +- **Simple majority**: Weights [50, 50], Quorum 51 +- **Supermajority**: Weights [25, 25, 25, 25], Quorum 75 +- **Dictator + veto**: Weights [80, 20], Quorum 100 -**Gaming Applications:** -- Turn-based games (chess, poker, strategy games) -- Real-time multiplayer games with state synchronization -- Betting and prediction markets -- Virtual asset trading +## Security Model -**Financial Applications:** -- Payment systems and micropayments -- Decentralized exchanges (order books) -- Lending and borrowing protocols -- Insurance and derivatives +### How does challenge/response dispute resolution work? -**Communication & Social:** -- Messaging apps with payment integration -- Social media with content monetization -- Collaborative editing and version control -- Digital content marketplace +The system uses **optimistic execution** with challenge periods: -**Enterprise Solutions:** -- Supply chain tracking -- Multi-party business processes -- B2B payment networks -- IoT device coordination +```solidity +function challenge(bytes32 channelId, State calldata candidate, State[] calldata proofs) external { + // Validate state via adjudicator + if (!IAdjudicator(meta.chan.adjudicator).adjudicate(meta.chan, candidate, proofs)) + revert InvalidState(); + + // Set challenge period + meta.challengeExpire = block.timestamp + meta.chan.challenge; + meta.stage = ChannelStatus.DISPUTE; +} +``` +**Security Guarantees:** +- **Economic security**: Funds locked in custody contracts +- **Temporal security**: Challenge periods prevent hasty closures +- **Cryptographic security**: All state transitions require valid signatures -### Can I integrate with existing smart contracts? +### How do session keys work? -Yes! Nitrolite supports custom adjudicator contracts: +Session keys enable **delegation without custody transfer**: ```solidity -contract MyGameApp is IAdjudicator { - function adjudicate( - Channel calldata chan, - State calldata candidate, - State[] calldata proofs - ) external view override returns (bool valid) { - // Implement your game-specific state validation - return validateGameMove(candidate, proofs); - } +// NOTE: it is allowed for depositor (and wallet) to be different from channel creator (participant) +// This enables logic of "session keys" where a user can create a channel on behalf of another account +``` + +**Implementation:** +- **Signer mapping**: `signers` table maps session keys to wallet addresses +- **Authentication flow**: EIP-712 structured signing for challenge-response +- **Session management**: 24-hour TTL with renewal capability +- **Scope limitation**: Session keys cannot withdraw funds, only sign state updates + +## Performance and Scaling + +### What are the performance bottlenecks? + +**Identified Constraints:** +- **Single WebSocket per wallet**: Limits concurrent connections +- **Synchronous signature verification**: CPU-bound operation for each message +- **Database balance queries**: Not cached, requires computation +- **Memory-based sessions**: Cannot distribute across multiple ClearNode instances + +### How does multi-chain asset handling work? + +Each token-chain combination is treated as a distinct asset: + +```go +type Asset struct { + Token string `gorm:"column:token;primaryKey"` + ChainID uint32 `gorm:"column:chain_id;primaryKey"` + Symbol string `gorm:"column:symbol;not null"` + Decimals uint8 `gorm:"column:decimals;not null"` } ``` -**Integration Points:** -- **Custom Adjudicators:** Implement `IAdjudicator` interface for application logic -- **Asset Support:** ERC-20 tokens and native assets through Custody contract -- **Multi-Chain:** Deploy on any EVM-compatible blockchain +**Precision Handling:** +- **Consistent DECIMAL(64,18)** across all monetary calculations +- **Chain-specific decimals** stored per asset +- **Token address normalization** per chain +- **Independent custody contracts** per supported chain + +### How does the system prevent replay attacks? -## Network and Infrastructure +**Multi-layer Protection:** +- **Timestamp validation**: Messages expire after 60 seconds +- **State version monotonicity**: Version numbers must strictly increase +- **Nonce progression**: Channel nonces prevent duplicate operations +- **Challenge periods**: Time-locked dispute resolution -### Do I need to run my own infrastructure? +## Integration Considerations -**Options Available:** +### How do you handle WebSocket connection management? -**1. Self-Hosted (Recommended):** -- Run your own ClearNode instances using Go binary or Docker -- Full control over infrastructure and configuration -- Connect to your preferred blockchain RPC providers -- PostgreSQL or SQLite database options +**Connection Lifecycle:** +```go +// Authentication-first connection establishment +func (h *UnifiedWSHandler) authenticateConnection(ws *websocket.Conn) error { + // Challenge-response authentication + // Session token generation + // Connection registration per wallet +} +``` -**2. Public ClearNodes:** -- Connect to community-operated ClearNode instances -- Suitable for development and testing -- No guaranteed uptime or support +**Features:** +- **One connection per wallet**: Latest connection replaces previous +- **Message forwarding**: Based on app session participants +- **Graceful error handling**: Structured error responses +- **Metrics tracking**: Connections, messages, auth events -**3. Enterprise:** -- Deploy ClearNode clusters with load balancing -- Multi-region redundancy -- Custom monitoring and alerting +### What database schema optimizations are recommended? -## Where can I get help? +**Critical Indexes:** +```sql +-- Balance calculation optimization +CREATE INDEX idx_ledger_wallet_asset ON ledger_entries(wallet, asset_symbol); -**Documentation:** -- **[User Guides](./quick_start/):** Step-by-step tutorials -- **[API Reference](./nitrolite_client/):** Complete SDK documentation -- **[Troubleshooting](./troubleshooting):** Common issues and solutions +-- Channel lookup optimization +CREATE INDEX idx_channels_participant ON channels(participant); -**Links:** -- **[GitHub](https://github.com/erc7824/nitro):** Technical discussions and issues -- **[Discord](https://discord.gg/yellownetwork):** Community & real-time chat support +-- Session routing optimization +CREATE INDEX idx_app_sessions_participants ON app_sessions USING gin(participants); +``` ---- +**Schema Considerations:** +- **DECIMAL precision**: Use DECIMAL(64,18) for all monetary values +- **UUID vs incremental IDs**: UUIDs for app sessions, incremental for performance-critical tables +- **Partitioning**: Consider partitioning ledger_entries by time for large deployments + +### How do you implement custom adjudicators? -## Still Have Questions? +Custom adjudicators must implement the `IAdjudicator` interface: + +```solidity +interface IAdjudicator { + function adjudicate( + Channel calldata chan, + State calldata candidate, + State[] calldata proofs + ) external view returns (bool valid); +} +``` + +**Implementation Patterns:** +- **Stateless validation**: Pure functions based on provided proofs +- **State transition rules**: Validate moves from previous to current state +- **Business logic**: Game rules, payment conditions, etc. +- **Proof requirements**: Define what constitutes valid state transitions + +--- -Check [GitHub](https://github.com/erc7824/nitro) or join [Discord](https://discord.gg/yellownetwork) for support. +For additional technical details, consult the [Architecture Overview](../architecture/) or examine the [ClearNode source code](https://github.com/erc7824/nitrolite/tree/main/clearnode). \ No newline at end of file From 1b092a2d6420d13417285d69f7f6213f6275c14a Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 15:23:01 +0300 Subject: [PATCH 05/11] docs: make security page a guideline for vApps --- docs/security/index.md | 614 +++++++++++++++++++++-------------------- 1 file changed, 317 insertions(+), 297 deletions(-) diff --git a/docs/security/index.md b/docs/security/index.md index 29c88f4..d7529f7 100644 --- a/docs/security/index.md +++ b/docs/security/index.md @@ -1,360 +1,380 @@ --- -title: Security -description: Security considerations and best practices for Nitrolite applications -keywords: [security, best practices, vulnerabilities, smart contracts, state channels] +title: Virtual Application Security +description: Security considerations for building secure virtual applications on Nitrolite +keywords: [security, virtual applications, adjudicators, consensus, quorum, state validation] --- -# Security +# Virtual Application Security -Comprehensive security guidance for building secure applications with Nitrolite. +Security considerations and implementation patterns for developers building virtual applications on Nitrolite's virtual ledger system. -## Security Model +## Adjudicator Security Patterns -### Core Security Principles +### Signature Validation Requirements -**1. Cryptographic Security** -- All state transitions require valid ECDSA signatures from participants -- State integrity protected by cryptographic hashing (keccak256) -- Version control prevents replay attacks through incremental state versioning +Virtual applications must implement robust signature validation in their adjudicators: -**2. Economic Security** -- Participants lock funds in Custody contract as commitment -- Challenge mechanisms provide dispute resolution -- Configurable challenge periods for time-bounded operations +```solidity +contract MyGameAdjudicator is IAdjudicator { + function adjudicate(Channel calldata chan, State calldata candidate, State[] calldata proofs) + external view override returns (bool valid) { + + // CRITICAL: Always validate unanimous signatures for state transitions + if (!candidate.validateUnanimousSignatures(chan)) { + return false; + } + + // Validate application-specific business logic + return validateGameMove(candidate, proofs); + } +} +``` -**3. Two-Party Model** -- Simplified security with exactly two participants: Creator and Broker -- Clear role definitions reduce attack surface -- Deterministic state transitions based on participant signatures +**Common Vulnerabilities:** +- **Missing signature validation**: Accepting states without proper signature verification +- **Insufficient signature checks**: Not validating all required participants have signed +- **Signature replay**: Not enforcing version progression in state validation -
-```mermaid -graph TB - subgraph "Security Layers" - A[Smart Contracts] --> B[ClearNode] - B --> C[TypeScript SDK] - C --> D[Application] - end +### State Transition Validation + +Implement strict state transition logic to prevent manipulation: + +```solidity +function _validateTransition(State memory previous, State memory current) internal pure returns (bool) { + // CRITICAL: Enforce version progression + if (current.version != previous.version + 1) { + return false; + } - subgraph "Threat Vectors" - E[Signature Forgery] --> A - F[State Manipulation] --> A - G[Network Attacks] --> B - H[Client Vulnerabilities] --> C - I[Application Logic] --> D - end + // CRITICAL: Validate allocation conservation + if (!_validateAllocationConservation(previous.allocations, current.allocations)) { + return false; + } - style A fill:#ffebee - style B fill:#e8f5e8 - style C fill:#fff3e0 - style D fill:#e3f2fd + // Application-specific transition rules + return _validateBusinessLogic(previous, current); +} ``` -
-## Threat Analysis +## Quorum-Based Consensus Vulnerabilities -### 1. **Signature-Based Attacks** +### Weight Manipulation Attacks -**Threat:** Malicious actors attempting to forge or replay signatures. +Virtual applications use weighted quorum consensus that can be vulnerable to manipulation: -**Mitigations:** -```typescript -// Always verify signatures before processing -import { verifyMessage } from 'viem'; +```go +// Vulnerable: Accepting weight configurations without validation +type VirtualApp struct { + Participants []string `json:"participants"` + Weights []uint64 `json:"weights"` // Potential manipulation vector + Quorum uint64 `json:"quorum"` // Must validate against total weights +} +``` -const isValid = await verifyMessage({ - address: expectedSigner, - message: stateHash, - signature: signature -}); +**Attack Vectors:** +- **Disproportionate weights**: Assigning excessive weights to compromised participants +- **Quorum threshold bypass**: Setting quorum lower than honest participant weights +- **Zero weight attacks**: Including participants with zero weight to dilute consensus -if (!isValid) { - throw new Error('Invalid signature'); +**Mitigation Patterns:** +```go +func validateQuorumConfiguration(participants []string, weights []uint64, quorum uint64) error { + if len(participants) != len(weights) { + return fmt.Errorf("participants and weights length mismatch") + } + + totalWeight := uint64(0) + for _, weight := range weights { + if weight == 0 { + return fmt.Errorf("zero weights not allowed") + } + totalWeight += weight + } + + // Require supermajority for security + minQuorum := (totalWeight * 2) / 3 + 1 + if quorum < minQuorum { + return fmt.Errorf("quorum too low: %d, minimum: %d", quorum, minQuorum) + } + + return nil } +``` -// Check version number to prevent replays -if (newState.version <= currentState.version) { - throw new Error('Invalid state version'); -} +### Consensus Bypassing + +ClearNode validates consensus but applications must implement additional checks: -// Verify signer is expected participant (Creator or Broker) -const expectedSigner = state.version % 2 === 1 ? creatorAddress : brokerAddress; -if (recoveredSigner !== expectedSigner) { - throw new Error('Unauthorized signer'); +```go +// In virtual application handler +if totalWeight < int64(appSession.Quorum) { + return fmt.Errorf("quorum not met: %d / %d", totalWeight, appSession.Quorum) } ``` -### 2. **State Manipulation** - -**Threat:** Attempting to submit invalid or malicious state updates. - -**Mitigations:** -```typescript -// Implement comprehensive state validation -function validateStateTransition( - channel: Channel, - prevState: State, - newState: State, - adjudicator: IAdjudicator -): boolean { - // Basic validation - if (newState.version !== prevState.version + 1) { - return false; - } - - // Adjudicator validation - return adjudicator.adjudicate(channel, newState, [prevState]); -} +**Security Requirements:** +- **Signature weight aggregation**: Correctly sum weights from valid signatures +- **Threshold enforcement**: Reject states that don't meet quorum requirements +- **Participant validation**: Ensure signers are actual participants with correct weights + +## Fund Isolation and Balance Security -// Always validate locally before broadcasting -const isValid = validateStateTransition(channel, currentState, proposedState, adjudicator); -if (!isValid) { - throw new Error('Invalid state transition'); +### Virtual Ledger Manipulation + +Virtual applications create isolated accounting contexts that can be vulnerable: + +```sql +-- Virtual app accounts use session IDs as account identifiers +INSERT INTO ledger_entries (account_id, wallet, asset_symbol, credit, debit) +VALUES ('session_abc123', 'participant_wallet', 'USDC', 0, 100.0); +``` + +**Attack Vectors:** +- **Double spending**: Spending the same funds across multiple virtual applications +- **Balance inflation**: Creating credits without corresponding debits +- **Cross-session leakage**: Accessing funds from other virtual applications + +**Security Implementation:** +```go +func (vl *VirtualLedger) TransferFunds(from, to, asset string, amount decimal.Decimal) error { + return vl.db.Transaction(func(tx *gorm.DB) error { + // CRITICAL: Atomic balance validation and transfer + fromBalance, err := vl.GetBalance(from, asset) + if err != nil { + return err + } + + if fromBalance.LessThan(amount) { + return fmt.Errorf("insufficient balance: %s < %s", fromBalance, amount) + } + + // CRITICAL: Atomic debit/credit operations + if err := vl.RecordTransaction(from, asset, amount.Neg(), tx); err != nil { + return err + } + + return vl.RecordTransaction(to, asset, amount, tx) + }) } ``` -### 3. **Network-Level Attacks** - -**Threat:** Eclipse attacks, message interception, or network partitioning. - -**Mitigations:** -```typescript -// Use multiple ClearNode connections for redundancy -const clearNodeUrls = [ - 'wss://clearnode1.example.com/ws', - 'wss://clearnode2.example.com/ws' -]; - -// Implement WebSocket reconnection logic -class SecureWebSocketClient { - private ws: WebSocket; - private reconnectAttempts = 0; - private maxReconnectAttempts = 5; - - connect(url: string) { - this.ws = new WebSocket(url); - - this.ws.onclose = () => { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - setTimeout(() => { - this.reconnectAttempts++; - this.connect(url); - }, Math.pow(2, this.reconnectAttempts) * 1000); - } - }; - } - - // Verify message integrity - async sendMessage(message: RPCMessage) { - // Add timestamp for freshness - message.timestamp = Date.now(); - - // Sign message with private key - message.sig = await this.signMessage(message); - - this.ws.send(JSON.stringify(message)); - } +### Session Isolation Enforcement + +Ensure virtual applications cannot access funds from other sessions: + +```go +func validateSessionAccess(sessionID string, participantWallet string, appSession *AppSession) error { + // CRITICAL: Verify participant is authorized for this session + for _, participant := range appSession.ParticipantWallets { + if participant == participantWallet { + return nil + } + } + return fmt.Errorf("unauthorized access to session %s", sessionID) } ``` -### 4. **Smart Contract Vulnerabilities** +## Application State Validation -**Threat:** Bugs in smart contract logic enabling fund theft or manipulation. +### Business Logic Security -**Prevention Measures:** -- **Battle-tested patterns:** Built on established state channel designs -- **Interface segregation:** Clear separation between IChannel, IDeposit, and IAdjudicator -- **Access controls:** Role-based permissions for critical functions -- **Atomic operations:** State changes happen atomically +Implement comprehensive validation for application-specific state transitions: ```solidity -// Example: Custody contract security patterns -contract Custody is IChannel, IDeposit { - // Constants for participant roles - uint256 constant CREATOR = 0; - uint256 constant BROKER = 1; - - // Reentrancy protection - modifier nonReentrant() { - require(!_locked, "Reentrant call"); - _locked = true; - _; - _locked = false; +contract TicTacToeAdjudicator is IAdjudicator { + struct GameState { + uint8[9] board; // 0=empty, 1=player1, 2=player2 + uint8 currentPlayer; // 1 or 2 + bool gameEnded; } - // Access control for channel operations - modifier onlyParticipant(bytes32 channelId) { - require(isParticipant(channelId, msg.sender), "Not authorized"); - _; - } - - function close(bytes32 channelId, State calldata candidate, State[] calldata proofs) - external - onlyParticipant(channelId) - nonReentrant() - { - // Withdrawal logic with checks-effects-interactions pattern + function adjudicate(Channel calldata chan, State calldata candidate, State[] calldata proofs) + external view override returns (bool valid) { + + if (proofs.length != 1) return false; + + GameState memory prevGame = abi.decode(proofs[0].data, (GameState)); + GameState memory currGame = abi.decode(candidate.data, (GameState)); + + // CRITICAL: Validate turn order + if (currGame.currentPlayer == prevGame.currentPlayer) { + return false; // Same player cannot move twice + } + + // CRITICAL: Validate single move + uint8 moveCount = 0; + for (uint i = 0; i < 9; i++) { + if (prevGame.board[i] != currGame.board[i]) { + if (prevGame.board[i] != 0) return false; // Cannot overwrite + if (currGame.board[i] != prevGame.currentPlayer) return false; // Wrong player + moveCount++; + } + } + + return moveCount == 1; // Exactly one move allowed } } ``` -## Best Practices - -### 1. **Private Key Management** - -**Secure Storage:** -```typescript -// NEVER store private keys in code or localStorage -// Use environment variables or secure key management +### State Encoding Security -// Good: Environment variable -const privateKey = process.env.PRIVATE_KEY; +Use proper ABI encoding to prevent state manipulation: -// Better: Hardware wallet integration -import { LedgerSigner } from '@ledgerhq/hw-app-eth'; +```solidity +// GOOD: Structured encoding +struct ApplicationData { + uint256 gameId; + bytes32 moveHash; + uint64 timestamp; +} -// Best: Key management service -import { KMSClient } from '@aws-sdk/client-kms'; +// BAD: Raw bytes that can be manipulated +// bytes applicationData; ``` -**ClearNode Key Security:** -```bash -# Store broker private key securely -export BROKER_PRIVATE_KEY="0x..." +## Session Management Security -# Use file permissions to protect .env -chmod 600 .env +### Authentication in Virtual Applications -# Rotate keys regularly -# Generate new key and update ClearNode configuration -``` +Virtual applications inherit ClearNode's authentication but should implement additional checks: -### 2. **Message Verification** - -**RPC Message Validation:** -```typescript -// Validate RPC message structure -function validateRPCMessage(message: RPCMessage): boolean { - // Check required fields - if (!message.req && !message.res) { - return false; - } - - // Verify timestamp freshness (prevent replay) - const maxAge = 60000; // 1 minute - const messageAge = Date.now() - message.timestamp; - if (messageAge > maxAge) { - return false; - } - - // Verify signatures - return message.sig.every(sig => verifySignature(message, sig)); +```go +func (h *VirtualAppHandler) validateParticipant(sessionID string, walletAddress string) error { + // CRITICAL: Verify participant is part of this virtual application + appSession, err := h.getAppSession(sessionID) + if err != nil { + return err + } + + for _, participant := range appSession.ParticipantWallets { + if strings.EqualFold(participant, walletAddress) { + return nil + } + } + + return fmt.Errorf("wallet %s not authorized for session %s", walletAddress, sessionID) } ``` -### 3. **Network Security** +### Session Lifecycle Security -**ClearNode Configuration:** -```bash -# Use TLS/SSL for production -CLEARNODE_WS_URL=wss://your-domain.com/ws +Implement proper session creation and termination: -# Implement rate limiting -MSG_EXPIRY_TIME=60 - -# Database security -DATABASE_URL=postgresql://user:pass@localhost:5432/clearnode?sslmode=require +```go +func createVirtualApplication(participants []string, weights []uint64, quorum uint64) error { + // CRITICAL: Validate all participants have active channels with broker + for _, participant := range participants { + if !h.hasActiveChannel(participant) { + return fmt.Errorf("participant %s has no active channel", participant) + } + } + + // CRITICAL: Validate quorum configuration + if err := validateQuorumConfiguration(participants, weights, quorum); err != nil { + return err + } + + // CRITICAL: Ensure sufficient funds for virtual application + return h.validateSufficientFunds(participants) +} ``` -**Client-Side Security:** -```typescript -// Validate message timestamps -function isMessageFresh(timestamp: number): boolean { - const maxAge = 60000; // 1 minute - return Date.now() - timestamp < maxAge; +## Race Conditions and Atomicity + +### Concurrent State Updates + +Virtual applications must handle concurrent updates safely: + +```go +func (vl *VirtualLedger) UpdateState(sessionID string, newState ApplicationState) error { + // CRITICAL: Use database transactions for atomicity + return vl.db.Transaction(func(tx *gorm.DB) error { + // Lock session for update + var session AppSession + if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}). + Where("session_id = ?", sessionID).First(&session).Error; err != nil { + return err + } + + // Validate state version progression + if newState.Version != session.Version + 1 { + return fmt.Errorf("invalid version progression") + } + + // Apply state update atomically + session.Version = newState.Version + return tx.Save(&session).Error + }) } - -// Verify WebSocket connection -const ws = new WebSocket('wss://clearnode.example.com/ws'); -ws.onopen = () => { - // Verify SSL certificate - if (location.protocol !== 'https:') { - console.warn('Insecure connection detected'); - } -}; ``` -## Production Security - -### 1. **Infrastructure Hardening** - -**ClearNode Deployment:** -- Use reverse proxy (nginx) with SSL termination -- Implement rate limiting and request filtering -- Monitor for unusual connection patterns -- Keep database connections secure and encrypted - -**Key Management:** -- Store private keys in secure environment variables -- Use hardware security modules (HSMs) for high-value operations -- Implement key rotation procedures -- Never commit private keys to version control - -### 2. **Monitoring and Alerts** +### Fund Transfer Atomicity + +Ensure fund transfers are atomic across multiple operations: + +```go +func (vl *VirtualLedger) ExecuteMultiPartyTransfer(transfers []Transfer) error { + return vl.db.Transaction(func(tx *gorm.DB) error { + // CRITICAL: Validate all transfers before executing any + for _, transfer := range transfers { + balance, err := vl.GetBalanceInTx(transfer.From, transfer.Asset, tx) + if err != nil { + return err + } + if balance.LessThan(transfer.Amount) { + return fmt.Errorf("insufficient balance for %s", transfer.From) + } + } + + // Execute all transfers atomically + for _, transfer := range transfers { + if err := vl.ExecuteTransferInTx(transfer, tx); err != nil { + return err // Automatic rollback + } + } + + return nil + }) +} +``` -**Security Metrics:** -```bash -# Monitor ClearNode metrics -curl http://localhost:4242/metrics | grep clearnode_ +## Security Checklist for Virtual Application Developers + +**Adjudicator Implementation:** +- [ ] Validate unanimous signatures for all state transitions +- [ ] Enforce strict version progression (current.version = previous.version + 1) +- [ ] Implement proper allocation conservation checks +- [ ] Validate application-specific business logic +- [ ] Use structured ABI encoding for state data + +**Quorum Configuration:** +- [ ] Validate participant count matches weight count +- [ ] Reject zero or negative weights +- [ ] Enforce minimum quorum thresholds (recommend ≥67% of total weight) +- [ ] Validate total weight calculations +- [ ] Implement supermajority requirements for critical operations + +**Fund Management:** +- [ ] Use atomic database transactions for all fund operations +- [ ] Validate sufficient balances before transfers +- [ ] Implement proper session isolation +- [ ] Audit virtual ledger balance calculations +- [ ] Prevent cross-session fund access + +**Session Security:** +- [ ] Validate participant authorization for virtual applications +- [ ] Implement proper session lifecycle management +- [ ] Use database locking for concurrent state updates +- [ ] Validate state version progression +- [ ] Implement session timeout mechanisms + +**Business Logic:** +- [ ] Validate all state transitions according to application rules +- [ ] Implement turn-based validation for sequential applications +- [ ] Prevent illegal moves or state manipulations +- [ ] Use deterministic state encoding +- [ ] Implement proper game/application termination conditions -# Key metrics to watch: -# - clearnode_errors_total -# - clearnode_active_connections -# - clearnode_rpc_requests_total -``` +--- -**Alert Conditions:** -- High error rates (>5% over 5 minutes) -- Unusual connection patterns -- Failed authentication attempts -- Database connection issues - -### 3. **Incident Response** - -**Emergency Procedures:** -1. **Identify the threat** - Monitor logs and metrics -2. **Isolate affected systems** - Disable compromised connections -3. **Assess impact** - Check channel states and fund security -4. **Restore operations** - Deploy fixed version or revert changes -5. **Post-incident review** - Analyze root cause and improve security - -## Security Checklist - -**Before Production:** -- [ ] Private keys stored securely -- [ ] SSL/TLS enabled for all connections -- [ ] Database connections encrypted -- [ ] Rate limiting implemented -- [ ] Monitoring and alerting configured -- [ ] Backup and recovery procedures tested -- [ ] Security team contact information documented - -**Regular Maintenance:** -- [ ] Monitor security metrics daily -- [ ] Review logs for suspicious activity -- [ ] Update dependencies regularly -- [ ] Rotate private keys quarterly -- [ ] Test backup and recovery procedures -- [ ] Review and update incident response procedures - -## Resources - -### Security Contacts -- **General Issues:** [GitHub Issues](https://github.com/erc7824/nitrolite/issues) -- **Security Vulnerabilities:** [Responsible Disclosure](https://github.com/erc7824/nitrolite/security) -- **Community Support:** [Discord](https://discord.gg/yellownetwork) - -### Documentation -- **[Architecture Overview](../architecture/)** - System design and security model -- **[Deployment Guide](../deployment/)** - Production deployment security -- **[Troubleshooting](../troubleshooting)** - Common security issues and solutions \ No newline at end of file +For implementation examples, see the [Consensus](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Consensus.sol) and [Remittance](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Remittance.sol) adjudicators. \ No newline at end of file From 5d3db7c255b8b878bc967c1cff8fe7fadc5d68c2 Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 15:30:25 +0300 Subject: [PATCH 06/11] docs: restructure and centralize technical docs under deep_dive --- .../index.md => deep_dive/architecture.md} | 0 .../index.md => deep_dive/deployment.md} | 0 docs/{ => deep_dive}/faq.md | 2 +- docs/deep_dive/index.mdx | 48 +++++++++++++++++++ .../index.md => deep_dive/security.md} | 0 docs/{ => deep_dive}/troubleshooting.md | 0 6 files changed, 49 insertions(+), 1 deletion(-) rename docs/{architecture/index.md => deep_dive/architecture.md} (100%) rename docs/{deployment/index.md => deep_dive/deployment.md} (100%) rename docs/{ => deep_dive}/faq.md (98%) create mode 100644 docs/deep_dive/index.mdx rename docs/{security/index.md => deep_dive/security.md} (100%) rename docs/{ => deep_dive}/troubleshooting.md (100%) diff --git a/docs/architecture/index.md b/docs/deep_dive/architecture.md similarity index 100% rename from docs/architecture/index.md rename to docs/deep_dive/architecture.md diff --git a/docs/deployment/index.md b/docs/deep_dive/deployment.md similarity index 100% rename from docs/deployment/index.md rename to docs/deep_dive/deployment.md diff --git a/docs/faq.md b/docs/deep_dive/faq.md similarity index 98% rename from docs/faq.md rename to docs/deep_dive/faq.md index f9bf4a2..ec94bd1 100644 --- a/docs/faq.md +++ b/docs/deep_dive/faq.md @@ -258,4 +258,4 @@ interface IAdjudicator { --- -For additional technical details, consult the [Architecture Overview](../architecture/) or examine the [ClearNode source code](https://github.com/erc7824/nitrolite/tree/main/clearnode). \ No newline at end of file +For additional technical details, consult the [Architecture Overview](architecture/) or examine the [ClearNode source code](https://github.com/erc7824/nitrolite/tree/main/clearnode). \ No newline at end of file diff --git a/docs/deep_dive/index.mdx b/docs/deep_dive/index.mdx new file mode 100644 index 0000000..e249a35 --- /dev/null +++ b/docs/deep_dive/index.mdx @@ -0,0 +1,48 @@ +--- +title: Deep Dive +description: Technical deep dive into Nitrolite implementation and architecture +keywords: [deep dive, technical, architecture, implementation, advanced] +--- + +import { Card, CardGrid } from '@site/src/components/Card'; + +# Deep Dive + +Technical deep dive documentation for developers building with Nitrolite, operating ClearNode infrastructure, and implementing virtual applications. + + + + + + + + + +--- + +These documents are intended for technical experts who need to understand implementation details, security considerations, and operational requirements when working with Nitrolite. \ No newline at end of file diff --git a/docs/security/index.md b/docs/deep_dive/security.md similarity index 100% rename from docs/security/index.md rename to docs/deep_dive/security.md diff --git a/docs/troubleshooting.md b/docs/deep_dive/troubleshooting.md similarity index 100% rename from docs/troubleshooting.md rename to docs/deep_dive/troubleshooting.md From 8aaacc7de8c3e6d35f37b8d9dbc38ede3195792c Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 16:35:55 +0300 Subject: [PATCH 07/11] docs: restructure technical Q&A into collapsible sections for readability --- docs/deep_dive/faq.md | 280 +++++++++++++++++++++++++++--------------- 1 file changed, 184 insertions(+), 96 deletions(-) diff --git a/docs/deep_dive/faq.md b/docs/deep_dive/faq.md index ec94bd1..2788e7c 100644 --- a/docs/deep_dive/faq.md +++ b/docs/deep_dive/faq.md @@ -10,9 +10,10 @@ Implementation details and technical questions for developers building with Nitr ## Protocol Implementation -### How does the ClearNode RPC protocol work? - -ClearNode uses JSON-RPC over WebSocket with deterministic array serialization: +
+ How does the ClearNode RPC protocol work? +
+

ClearNode uses JSON-RPC over WebSocket with deterministic array serialization:

```json { @@ -21,15 +22,21 @@ ClearNode uses JSON-RPC over WebSocket with deterministic array serialization: } ``` -**Key Features:** -- **Array-based structure** ensures deterministic serialization across JSON implementations -- **Timestamp validation** with 60-second expiry prevents replay attacks -- **Session-based routing** via `AppSessionID` for virtual application isolation -- **Challenge-response authentication** using EIP-712 structured signing +

Key Features:

+
    +
  • Array-based structure ensures deterministic serialization across JSON implementations
  • +
  • Timestamp validation with 60-second expiry prevents replay attacks
  • +
  • Session-based routing via AppSessionID for virtual application isolation
  • +
  • Challenge-response authentication using EIP-712 structured signing
  • +
-### How does signature verification work across different chains? +
+
-Nitrolite uses **chain-agnostic signature verification** without EIP-191 prefixing: +
+ How does signature verification work across different chains? +
+

Nitrolite uses chain-agnostic signature verification without EIP-191 prefixing:

```go // Raw ECDSA signing without chain-specific prefixes @@ -37,15 +44,21 @@ messageHash := crypto.Keccak256Hash(stateBytes) signature, _ := crypto.Sign(messageHash.Bytes(), privateKey) ``` -**Implementation Details:** -- **ECDSA with secp256k1** curve (Ethereum-compatible) -- **Keccak256 hashing** for message digests -- **65-byte signature format** (r,s,v) with v adjustment -- **Address recovery** using `crypto.SigToPub()` for authentication +

Implementation Details:

+
    +
  • ECDSA with secp256k1 curve (Ethereum-compatible)
  • +
  • Keccak256 hashing for message digests
  • +
  • 65-byte signature format (r,s,v) with v adjustment
  • +
  • Address recovery using crypto.SigToPub() for authentication
  • +
-### What is the channel state encoding format? +
+
-States are ABI-encoded with a specific structure: +
+ What is the channel state encoding format? +
+

States are ABI-encoded with a specific structure:

```go // State encoding: (channelID, intent, version, stateData, allocations[]) @@ -58,17 +71,23 @@ args := abi.Arguments{ } ``` -**Intent Types:** -- `OPERATE(0)`: Normal application states -- `INITIALIZE(1)`: Channel funding states -- `RESIZE(2)`: Capacity adjustment states -- `FINALIZE(3)`: Channel closing states +

Intent Types:

+
    +
  • OPERATE(0): Normal application states
  • +
  • INITIALIZE(1): Channel funding states
  • +
  • RESIZE(2): Capacity adjustment states
  • +
  • FINALIZE(3): Channel closing states
  • +
-## Virtual Ledger System +
+
-### How does the double-entry accounting work? +## Virtual Ledger System -ClearNode implements traditional double-entry bookkeeping with DECIMAL(64,18) precision: +
+ How does the double-entry accounting work? +
+

ClearNode implements traditional double-entry bookkeeping with DECIMAL(64,18) precision:

```sql CREATE TABLE ledger_entries ( @@ -80,20 +99,27 @@ CREATE TABLE ledger_entries ( ); ``` -**Balance Calculation:** +

Balance Calculation:

+ ```go // Balance = SUM(credit) - SUM(debit) for each (wallet, asset) pair balance := totalCredits.Sub(totalDebits) ``` -**Account Types:** -- **Participant accounts**: User wallet balances -- **Virtual app accounts**: Isolated application contexts -- **System accounts**: Protocol-level operations +

Account Types:

+
    +
  • Participant accounts: User wallet balances
  • +
  • Virtual app accounts: Isolated application contexts
  • +
  • System accounts: Protocol-level operations
  • +
-### How do virtual applications achieve consensus? +
+
-Virtual apps use **weighted quorum-based consensus** configured during channel creation: +
+ How do virtual applications achieve consensus? +
+

Virtual apps use weighted quorum-based consensus configured during channel creation:

```go // Check if combined signature weights meet quorum threshold @@ -102,7 +128,8 @@ if totalWeight < int64(appSession.Quorum) { } ``` -**Weight Configuration:** +

Weight Configuration:

+ ```go type App struct { Participants []address // Array of participants in the app @@ -111,22 +138,30 @@ type App struct { } ``` -**Consensus Flow:** -1. **State proposal** by any participant -2. **Signature collection** from participants until weight threshold met -3. **Validation** of weighted quorum achievement -4. **Ledger update** with atomic balance transfers +

Consensus Flow:

+
    +
  1. State proposal by any participant
  2. +
  3. Signature collection from participants until weight threshold met
  4. +
  5. Validation of weighted quorum achievement
  6. +
  7. Ledger update with atomic balance transfers
  8. +
-**Example Scenarios:** -- **Simple majority**: Weights [50, 50], Quorum 51 -- **Supermajority**: Weights [25, 25, 25, 25], Quorum 75 -- **Dictator + veto**: Weights [80, 20], Quorum 100 +

Example Scenarios:

+
    +
  • Simple majority: Weights [50, 50], Quorum 51
  • +
  • Supermajority: Weights [25, 25, 25, 25], Quorum 75
  • +
  • Dictator + veto: Weights [80, 20], Quorum 100
  • +
-## Security Model +
+
-### How does challenge/response dispute resolution work? +## Security Model -The system uses **optimistic execution** with challenge periods: +
+ How does challenge/response dispute resolution work? +
+

The system uses optimistic execution with challenge periods:

```solidity function challenge(bytes32 channelId, State calldata candidate, State[] calldata proofs) external { @@ -140,39 +175,58 @@ function challenge(bytes32 channelId, State calldata candidate, State[] calldata } ``` -**Security Guarantees:** -- **Economic security**: Funds locked in custody contracts -- **Temporal security**: Challenge periods prevent hasty closures -- **Cryptographic security**: All state transitions require valid signatures +

Security Guarantees:

+
    +
  • Economic security: Funds locked in custody contracts
  • +
  • Temporal security: Challenge periods prevent hasty closures
  • +
  • Cryptographic security: All state transitions require valid signatures
  • +
-### How do session keys work? +
+
-Session keys enable **delegation without custody transfer**: +
+ How do session keys work? +
+

Session keys enable delegation without custody transfer:

-```solidity +```go // NOTE: it is allowed for depositor (and wallet) to be different from channel creator (participant) // This enables logic of "session keys" where a user can create a channel on behalf of another account ``` -**Implementation:** -- **Signer mapping**: `signers` table maps session keys to wallet addresses -- **Authentication flow**: EIP-712 structured signing for challenge-response -- **Session management**: 24-hour TTL with renewal capability -- **Scope limitation**: Session keys cannot withdraw funds, only sign state updates +

Implementation:

+
    +
  • Signer mapping: signers table maps session keys to wallet addresses
  • +
  • Authentication flow: EIP-712 structured signing for challenge-response
  • +
  • Session management: 24-hour TTL with renewal capability
  • +
  • Scope limitation: Session keys cannot withdraw funds, only sign state updates
  • +
+ +
+
## Performance and Scaling -### What are the performance bottlenecks? +
+ What are the performance bottlenecks? +
-**Identified Constraints:** -- **Single WebSocket per wallet**: Limits concurrent connections -- **Synchronous signature verification**: CPU-bound operation for each message -- **Database balance queries**: Not cached, requires computation -- **Memory-based sessions**: Cannot distribute across multiple ClearNode instances +

Identified Constraints:

+
    +
  • Single WebSocket per wallet: Limits concurrent connections
  • +
  • Synchronous signature verification: CPU-bound operation for each message
  • +
  • Database balance queries: Not cached, requires computation
  • +
  • Memory-based sessions: Cannot distribute across multiple ClearNode instances
  • +
-### How does multi-chain asset handling work? +
+
-Each token-chain combination is treated as a distinct asset: +
+ How does multi-chain asset handling work? +
+

Each token-chain combination is treated as a distinct asset:

```go type Asset struct { @@ -183,25 +237,40 @@ type Asset struct { } ``` -**Precision Handling:** -- **Consistent DECIMAL(64,18)** across all monetary calculations -- **Chain-specific decimals** stored per asset -- **Token address normalization** per chain -- **Independent custody contracts** per supported chain +

Precision Handling:

+
    +
  • Consistent DECIMAL(64,18) across all monetary calculations
  • +
  • Chain-specific decimals stored per asset
  • +
  • Token address normalization per chain
  • +
  • Independent custody contracts per supported chain
  • +
+ +
+
+ +
+ How does the system prevent replay attacks? +
-### How does the system prevent replay attacks? +

Multi-layer Protection:

+
    +
  • Timestamp validation: Messages expire after 60 seconds
  • +
  • State version monotonicity: Version numbers must strictly increase
  • +
  • Nonce progression: Channel nonces prevent duplicate operations
  • +
  • Challenge periods: Time-locked dispute resolution
  • +
-**Multi-layer Protection:** -- **Timestamp validation**: Messages expire after 60 seconds -- **State version monotonicity**: Version numbers must strictly increase -- **Nonce progression**: Channel nonces prevent duplicate operations -- **Challenge periods**: Time-locked dispute resolution +
+
## Integration Considerations -### How do you handle WebSocket connection management? +
+ How do you handle WebSocket connection management? +
+ +

Connection Lifecycle:

-**Connection Lifecycle:** ```go // Authentication-first connection establishment func (h *UnifiedWSHandler) authenticateConnection(ws *websocket.Conn) error { @@ -211,15 +280,23 @@ func (h *UnifiedWSHandler) authenticateConnection(ws *websocket.Conn) error { } ``` -**Features:** -- **One connection per wallet**: Latest connection replaces previous -- **Message forwarding**: Based on app session participants -- **Graceful error handling**: Structured error responses -- **Metrics tracking**: Connections, messages, auth events +

Features:

+
    +
  • One connection per wallet: Latest connection replaces previous
  • +
  • Message forwarding: Based on app session participants
  • +
  • Graceful error handling: Structured error responses
  • +
  • Metrics tracking: Connections, messages, auth events
  • +
+ +
+
-### What database schema optimizations are recommended? +
+ What database schema optimizations are recommended? +
+ +

Critical Indexes:

-**Critical Indexes:** ```sql -- Balance calculation optimization CREATE INDEX idx_ledger_wallet_asset ON ledger_entries(wallet, asset_symbol); @@ -231,14 +308,20 @@ CREATE INDEX idx_channels_participant ON channels(participant); CREATE INDEX idx_app_sessions_participants ON app_sessions USING gin(participants); ``` -**Schema Considerations:** -- **DECIMAL precision**: Use DECIMAL(64,18) for all monetary values -- **UUID vs incremental IDs**: UUIDs for app sessions, incremental for performance-critical tables -- **Partitioning**: Consider partitioning ledger_entries by time for large deployments +

Schema Considerations:

+
    +
  • DECIMAL precision: Use DECIMAL(64,18) for all monetary values
  • +
  • UUID vs incremental IDs: UUIDs for app sessions, incremental for performance-critical tables
  • +
  • Partitioning: Consider partitioning ledger_entries by time for large deployments
  • +
-### How do you implement custom adjudicators? +
+
-Custom adjudicators must implement the `IAdjudicator` interface: +
+ How do you implement custom adjudicators? +
+

Custom adjudicators must implement the IAdjudicator interface:

```solidity interface IAdjudicator { @@ -250,11 +333,16 @@ interface IAdjudicator { } ``` -**Implementation Patterns:** -- **Stateless validation**: Pure functions based on provided proofs -- **State transition rules**: Validate moves from previous to current state -- **Business logic**: Game rules, payment conditions, etc. -- **Proof requirements**: Define what constitutes valid state transitions +

Implementation Patterns:

+
    +
  • Stateless validation: Pure functions based on provided proofs
  • +
  • State transition rules: Validate moves from previous to current state
  • +
  • Business logic: Game rules, payment conditions, etc.
  • +
  • Proof requirements: Define what constitutes valid state transitions
  • +
+ +
+
--- From 5f45b11a9940f9473fba031d2718395b6e006c4a Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 16:38:54 +0300 Subject: [PATCH 08/11] docs: hyperlink EIP references across documentation --- docs/deep_dive/architecture.md | 4 ++-- docs/deep_dive/faq.md | 6 +++--- docs/deep_dive/troubleshooting.md | 2 +- docs/nitrolite_client/advanced/abstract-accounts.md | 4 ++-- docs/nitrolite_client/advanced/index.md | 2 +- docs/nitrolite_client/methods.mdx | 8 ++++---- docs/nitrolite_client/types.md | 2 +- docs/nitrolite_rpc/message_creation_api.md | 2 +- docs/quick_start/connect_to_the_clearnode.md | 4 ++-- docs/quick_start/initializing_client.md | 8 ++++---- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/deep_dive/architecture.md b/docs/deep_dive/architecture.md index d59003b..0165b55 100644 --- a/docs/deep_dive/architecture.md +++ b/docs/deep_dive/architecture.md @@ -52,9 +52,9 @@ uint32 constant CHANRESIZE = 7883; // Resize state ### Chain-Agnostic Signature Model -The protocol deliberately avoids EIP-191 message prefixing: +The protocol deliberately avoids [EIP-191](https://eips.ethereum.org/EIPS/eip-191) message prefixing: -> "For signature verification, the stateHash is bare signed without EIP-191 since the protocol is intended to be chain-agnostic." +> "For signature verification, the stateHash is bare signed without [EIP-191](https://eips.ethereum.org/EIPS/eip-191) since the protocol is intended to be chain-agnostic." **Design Rationale**: This enables identical protocol logic across all EVM-compatible chains without modification, supporting the multi-chain vision where users can choose their preferred blockchain based on fees and speed. diff --git a/docs/deep_dive/faq.md b/docs/deep_dive/faq.md index 2788e7c..8e28d79 100644 --- a/docs/deep_dive/faq.md +++ b/docs/deep_dive/faq.md @@ -27,7 +27,7 @@ Implementation details and technical questions for developers building with Nitr
  • Array-based structure ensures deterministic serialization across JSON implementations
  • Timestamp validation with 60-second expiry prevents replay attacks
  • Session-based routing via AppSessionID for virtual application isolation
  • -
  • Challenge-response authentication using EIP-712 structured signing
  • +
  • Challenge-response authentication using [EIP-712](https://eips.ethereum.org/EIPS/eip-712) structured signing
  • @@ -36,7 +36,7 @@ Implementation details and technical questions for developers building with Nitr
    How does signature verification work across different chains?
    -

    Nitrolite uses chain-agnostic signature verification without EIP-191 prefixing:

    +

    Nitrolite uses chain-agnostic signature verification without [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefixing:

    ```go // Raw ECDSA signing without chain-specific prefixes @@ -198,7 +198,7 @@ function challenge(bytes32 channelId, State calldata candidate, State[] calldata

    Implementation:

    • Signer mapping: signers table maps session keys to wallet addresses
    • -
    • Authentication flow: EIP-712 structured signing for challenge-response
    • +
    • Authentication flow: [EIP-712](https://eips.ethereum.org/EIPS/eip-712) structured signing for challenge-response
    • Session management: 24-hour TTL with renewal capability
    • Scope limitation: Session keys cannot withdraw funds, only sign state updates
    diff --git a/docs/deep_dive/troubleshooting.md b/docs/deep_dive/troubleshooting.md index 752ead2..497802a 100644 --- a/docs/deep_dive/troubleshooting.md +++ b/docs/deep_dive/troubleshooting.md @@ -302,7 +302,7 @@ if (balance.lt(requiredAmount)) { 1. **Optimize Gas Settings:** ```javascript -// Use EIP-1559 gas pricing +// Use [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) gas pricing const tx = await nitroliteClient.deposit({ // ... other params maxFeePerGas: ethers.parseUnits('20', 'gwei'), diff --git a/docs/nitrolite_client/advanced/abstract-accounts.md b/docs/nitrolite_client/advanced/abstract-accounts.md index ce7b5e4..d717f08 100644 --- a/docs/nitrolite_client/advanced/abstract-accounts.md +++ b/docs/nitrolite_client/advanced/abstract-accounts.md @@ -33,7 +33,7 @@ These methods allow you to prepare transactions for the entire channel lifecycle `} example={`// Prepare deposit transaction(s) - may include ERC20 approval @@ -51,7 +51,7 @@ for (const tx of txs) { `} example={`// Prepare approval transaction diff --git a/docs/nitrolite_client/advanced/index.md b/docs/nitrolite_client/advanced/index.md index 01f03d6..6917d12 100644 --- a/docs/nitrolite_client/advanced/index.md +++ b/docs/nitrolite_client/advanced/index.md @@ -20,7 +20,7 @@ The NitroliteClient is built on top of specialized services that can be used dir ### Account Abstraction Integration -- [Abstract Accounts](./abstract-accounts.md) - Using NitroliteClient with ERC-4337 Account Abstraction for smart contract wallets +- [Abstract Accounts](./abstract-accounts.md) - Using NitroliteClient with [ERC-4337](https://eips.ethereum.org/EIPS/eip-4337) Account Abstraction for smart contract wallets ## When to Use Advanced Features diff --git a/docs/nitrolite_client/methods.mdx b/docs/nitrolite_client/methods.mdx index d533197..e2b28a7 100644 --- a/docs/nitrolite_client/methods.mdx +++ b/docs/nitrolite_client/methods.mdx @@ -22,7 +22,7 @@ The deposit phase includes methods for managing funds in the custody contract an diff --git a/docs/nitrolite_client/types.md b/docs/nitrolite_client/types.md index d2b4b3e..e4d7a1a 100644 --- a/docs/nitrolite_client/types.md +++ b/docs/nitrolite_client/types.md @@ -94,7 +94,7 @@ Represents a cryptographic signature used for signing state channel states. ```typescript interface Allocation { destination: Address; // Where funds are sent on channel closure - token: Address; // ERC-20 token address (zero address for ETH) + token: Address; // [ERC-20](https://eips.ethereum.org/EIPS/eip-20) token address (zero address for ETH) amount: bigint; // Token amount allocated } ``` diff --git a/docs/nitrolite_rpc/message_creation_api.md b/docs/nitrolite_rpc/message_creation_api.md index 052dd2f..a22dca6 100644 --- a/docs/nitrolite_rpc/message_creation_api.md +++ b/docs/nitrolite_rpc/message_creation_api.md @@ -389,6 +389,6 @@ async function setupSigner() { ``` **Important Considerations for `createEthersSigner`:** -* **Hashing Consistency:** The `sign` method within `createEthersSigner` must hash the payload in a way that is **identical** to how the `NitroliteRPC` class (specifically `NitroliteRPC.hashMessage`) expects messages to be hashed before signing. The example above uses `ethers.utils.id(JSON.stringify(payload))`. It's crucial to verify if the SDK's internal hashing uses a specific message prefix (e.g., EIP-191 personal_sign prefix) or a different serialization method. If the SDK does *not* use a standard EIP-191 prefix, or uses a custom one, your local signer's hashing logic must replicate this exactly for signatures to be valid. Using `NitroliteRPC.hashMessage(payload)` directly (if `payload` matches the `NitroliteRPCMessage` structure) is the safest way to ensure consistency. +* **Hashing Consistency:** The `sign` method within `createEthersSigner` must hash the payload in a way that is **identical** to how the `NitroliteRPC` class (specifically `NitroliteRPC.hashMessage`) expects messages to be hashed before signing. The example above uses `ethers.utils.id(JSON.stringify(payload))`. It's crucial to verify if the SDK's internal hashing uses a specific message prefix (e.g., [EIP-191](https://eips.ethereum.org/EIPS/eip-191) personal_sign prefix) or a different serialization method. If the SDK does *not* use a standard [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix, or uses a custom one, your local signer's hashing logic must replicate this exactly for signatures to be valid. Using `NitroliteRPC.hashMessage(payload)` directly (if `payload` matches the `NitroliteRPCMessage` structure) is the safest way to ensure consistency. * **Type Compatibility:** Ensure the `Address` type expected by functions like `createAuthRequestMessage` is compatible with `localSigner.address`. The example uses `localSigner.address as Address` assuming `Address` is `0x${string}`. * **Error Handling:** The provided examples include basic error logging. Robust applications should implement more sophisticated error handling. diff --git a/docs/quick_start/connect_to_the_clearnode.md b/docs/quick_start/connect_to_the_clearnode.md index 7b3a18f..95891cc 100644 --- a/docs/quick_start/connect_to_the_clearnode.md +++ b/docs/quick_start/connect_to_the_clearnode.md @@ -256,7 +256,7 @@ const messageSigner = async (payload) => { const messageBytes = ethers.getBytes(digestHex); // Sign the bytes with the wallet's signing key - // Note: This uses the raw signing method, not the EIP-191 prefixed signing! + // Note: This uses the raw signing method, not the [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefixed signing! const { serialized: signature } = client.stateWalletClient.wallet.signingKey.sign(messageBytes); return signature; @@ -1632,7 +1632,7 @@ When working with ClearNodes and state channels, keep these security best practi | Authentication failure | Invalid state wallet, incorrect signing | Verify your state wallet is properly initialized and signing correctly | | Frequent disconnections | Unstable network, server-side issues | Monitor connection events and implement automatic reconnection | | Message delivery failures | Connection issues, invalid message format | Add message queuing and confirmation mechanism | -| Invalid signature errors | EIP-191 prefix issues | Ensure you're signing raw message bytes without the EIP-191 prefix | +| Invalid signature errors | [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix issues | Ensure you're signing raw message bytes without the [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix | ## Next Steps diff --git a/docs/quick_start/initializing_client.md b/docs/quick_start/initializing_client.md index bb42c5d..d9db43c 100644 --- a/docs/quick_start/initializing_client.md +++ b/docs/quick_start/initializing_client.md @@ -29,11 +29,11 @@ Nitrolite uses separate wallet clients for on-chain and off-chain operations for - **User Experience**: Allows state channel operations without requiring wallet confirmation for every message - **Persistence**: The state wallet needs to be accessible across sessions without requiring frequent user approval -## The EIP-191 Prefix Issue +## The [EIP-191](https://eips.ethereum.org/EIPS/eip-191) Prefix Issue A critical detail when implementing the state wallet is handling message signing correctly: -- **Standard wallets** (like MetaMask) automatically add an EIP-191 prefix to messages (`"\x19Ethereum Signed Message:\n" + message.length + message`) +- **Standard wallets** (like MetaMask) automatically add an [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix to messages (`"\x19Ethereum Signed Message:\n" + message.length + message`) - **State channel protocols** often require signing raw messages WITHOUT this prefix for consensus compatibility - **Nitrolite requires** direct signing of raw message bytes for correct off-chain state validation @@ -98,13 +98,13 @@ async function initializeNitrolite() { const stateWalletAccount = privateKeyToAccount(stateWalletPrivateKey); // Create a state wallet client with the signing capabilities Nitrolite needs - // IMPORTANT: We need to sign raw messages WITHOUT the EIP-191 prefix + // IMPORTANT: We need to sign raw messages WITHOUT the [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix const stateWalletClient = { account: { address: stateWalletAccount.address, }, signMessage: async ({ message: { raw } }) => { - // Using ethers.js to sign the raw message without EIP-191 prefix + // Using ethers.js to sign the raw message without [EIP-191](https://eips.ethereum.org/EIPS/eip-191) prefix const wallet = new ethers.Wallet(stateWalletPrivateKey); const { serialized: signature } = wallet.signingKey.sign(raw); return signature; From dcb0d44b320ca5667f33909205df899ebaa56ef1 Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 16:41:19 +0300 Subject: [PATCH 09/11] docs: remove duplicate resize message example --- docs/nitrolite_rpc/message_creation_api.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/docs/nitrolite_rpc/message_creation_api.md b/docs/nitrolite_rpc/message_creation_api.md index a22dca6..05b9759 100644 --- a/docs/nitrolite_rpc/message_creation_api.md +++ b/docs/nitrolite_rpc/message_creation_api.md @@ -272,12 +272,6 @@ const resizeMsg = await createResizeChannelMessage(signer, resizeParams); // Send resizeMsg via WebSocket `} /> -// ...existing code... -// Assuming 'signer' and 'resizeParams' are defined -const resizeMsg = await createResizeChannelMessage(signer, resizeParams); -// Send resizeMsg via WebSocket -`} -/> ## Advanced: Creating a Local Signer for Development @@ -361,7 +355,7 @@ export const createEthersSigner = (privateKey: string): WalletSigner => { const messageToSign = JSON.stringify(payload); const messageHash = ethers.utils.id(messageToSign); // ethers.utils.id performs keccak256 const messageBytes = ethers.utils.arrayify(messageHash); - + const flatSignature = await wallet._signingKey().signDigest(messageBytes); const signature = ethers.utils.joinSignature(flatSignature); return signature as Hex; From afa90530242ba862ef0ae8c8ec213b98226e260d Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 16:47:35 +0300 Subject: [PATCH 10/11] docs: drop redundant section --- docs/deep_dive/architecture.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/docs/deep_dive/architecture.md b/docs/deep_dive/architecture.md index 0165b55..c9928be 100644 --- a/docs/deep_dive/architecture.md +++ b/docs/deep_dive/architecture.md @@ -77,25 +77,6 @@ type Entry struct { - **Compliance**: Follows established financial accounting practices - **Precision**: Uses decimal arithmetic to avoid floating-point errors in financial calculations -## Technology Choice Rationale - -### Go for ClearNode Backend - -**Why Go over Node.js/Python/Rust?** -- **Concurrency model**: Goroutines efficiently handle multiple blockchain event listeners simultaneously -- **Memory efficiency**: Better resource usage for long-running services compared to Node.js -- **Crypto libraries**: Excellent native support for ECDSA and Ethereum integration -- **Operational simplicity**: Single binary deployment with built-in monitoring -- **Network performance**: Superior WebSocket handling for real-time messaging requirements - -### TypeScript SDK Design - -**Why TypeScript over pure JavaScript?** -- **Type safety**: Prevents runtime errors in financial applications -- **Developer experience**: Excellent IDE support and autocomplete -- **Viem integration**: Built on modern, well-maintained Ethereum libraries -- **Multi-framework support**: Works with React, Vue, Angular without framework lock-in - ## Security Architecture ### Challenge-Response Dispute Resolution From d449f0790b578e0b5bf98d6563f2992b247d7812 Mon Sep 17 00:00:00 2001 From: Max Pushkarov Date: Tue, 27 May 2025 17:21:44 +0300 Subject: [PATCH 11/11] docs: enhance security page with narrative flow and visual diagrams --- docs/deep_dive/architecture.md | 21 +-- docs/deep_dive/security.md | 251 +++++++++++++++++++++++---------- 2 files changed, 185 insertions(+), 87 deletions(-) diff --git a/docs/deep_dive/architecture.md b/docs/deep_dive/architecture.md index c9928be..84bafa9 100644 --- a/docs/deep_dive/architecture.md +++ b/docs/deep_dive/architecture.md @@ -167,23 +167,26 @@ for name, network := range config.networks { ### Centralization vs. User Experience **Trade-off**: Uses broker hub model instead of fully peer-to-peer architecture -- **Cost**: Introduces potential central points of failure -- **Benefit**: Dramatically improves UX (one channel vs. many), enables instant liquidity -- **Mitigation**: Users retain custody through on-chain contracts; multiple brokers can compete + +Nitrolite's adoption of a broker hub model represents a fundamental architectural decision that prioritizes user experience over pure decentralization. While this approach introduces potential central points of failure where brokers could become unavailable or act maliciously, it delivers substantial user experience improvements by enabling users to maintain a single channel rather than managing multiple peer connections, while providing instant liquidity access to the entire network. + +The system mitigates centralization risks through economic incentives and technical safeguards. Users retain custody of their funds through on-chain smart contracts, ensuring that brokers cannot steal funds even if they act maliciously. Additionally, the protocol design allows multiple brokers to compete within the same ecosystem, preventing monopolization and providing users with choice in their infrastructure providers. ### Flexibility vs. Complexity **Trade-off**: Fixed 2-party channels instead of n-party support -- **Cost**: Cannot directly support complex multi-party protocols -- **Benefit**: Eliminates exponential complexity growth, easier security analysis -- **Mitigation**: Virtual ledger channels enable arbitrary multi-party applications + +The decision to implement fixed 2-party channels rather than supporting arbitrary n-party configurations represents a calculated trade-off between flexibility and complexity management. While this constraint prevents direct support for complex multi-party protocols at the channel level, it eliminates the exponential complexity growth that plagues n-party state channel implementations, enabling easier security analysis and more reliable implementations. + +Virtual ledger channels provide an elegant solution to this limitation, allowing arbitrary multi-party applications to operate within the security guarantees of 2-party channels. This approach combines the simplicity and security of bilateral channels with the functionality required for complex distributed applications. ### Performance vs. Decentralization **Trade-off**: Go backend service instead of pure smart contract execution -- **Cost**: Requires trust in ClearNode operators -- **Benefit**: Enables complex business logic, high throughput, better UX -- **Mitigation**: Challenge/response mechanism ensures trustless dispute resolution + +Utilizing a Go backend service rather than relying purely on smart contract execution introduces trust assumptions regarding ClearNode operators, as participants must trust that the service will process transactions honestly and remain available. However, this architectural choice enables the implementation of complex business logic that would be prohibitively expensive on-chain, achieves high transaction throughput measured in thousands of transactions per second, and delivers superior user experience through real-time interactions. + +The challenge/response mechanism provides critical protection against malicious operators by ensuring that all participants can validate and contest any state changes through cryptographically verifiable proofs. This trustless dispute resolution capability means that while participants must trust operators for liveness, they need not trust them for safety, as funds remain secure through on-chain enforcement. ## Protocol Innovation diff --git a/docs/deep_dive/security.md b/docs/deep_dive/security.md index d7529f7..be36621 100644 --- a/docs/deep_dive/security.md +++ b/docs/deep_dive/security.md @@ -18,22 +18,23 @@ Virtual applications must implement robust signature validation in their adjudic contract MyGameAdjudicator is IAdjudicator { function adjudicate(Channel calldata chan, State calldata candidate, State[] calldata proofs) external view override returns (bool valid) { - + // CRITICAL: Always validate unanimous signatures for state transitions if (!candidate.validateUnanimousSignatures(chan)) { return false; } - + // Validate application-specific business logic return validateGameMove(candidate, proofs); } } ``` -**Common Vulnerabilities:** -- **Missing signature validation**: Accepting states without proper signature verification -- **Insufficient signature checks**: Not validating all required participants have signed -- **Signature replay**: Not enforcing version progression in state validation +**Common Vulnerabilities** + +**The most critical vulnerability pattern in virtual application development involves inadequate signature validation. Applications that accept states without proper signature verification create immediate attack vectors for malicious actors.** This risk is compounded when developers fail to validate that all required participants have properly signed state transitions, potentially allowing unauthorized state changes. + +Equally dangerous is the failure to enforce version progression in state validation, which enables signature replay attacks where previously valid signatures are reused to manipulate application state. ### State Transition Validation @@ -45,12 +46,12 @@ function _validateTransition(State memory previous, State memory current) intern if (current.version != previous.version + 1) { return false; } - + // CRITICAL: Validate allocation conservation if (!_validateAllocationConservation(previous.allocations, current.allocations)) { return false; } - + // Application-specific transition rules return _validateBusinessLogic(previous, current); } @@ -71,10 +72,40 @@ type VirtualApp struct { } ``` -**Attack Vectors:** -- **Disproportionate weights**: Assigning excessive weights to compromised participants -- **Quorum threshold bypass**: Setting quorum lower than honest participant weights -- **Zero weight attacks**: Including participants with zero weight to dilute consensus +**Attack Vectors** + +**Weight manipulation represents one of the most sophisticated attack vectors against quorum-based consensus systems. Malicious actors may attempt to assign disproportionate weights to compromised participants, effectively granting themselves outsized control over consensus decisions.** This is often combined with quorum threshold bypass attacks, where the required consensus threshold is deliberately set lower than the combined weight of honest participants. + +```mermaid +graph TB + subgraph "Legitimate Configuration" + A1["Alice: 25%"] + B1["Bob: 25%"] + C1["Carol: 25%"] + D1["Dave: 25%"] + Q1["Quorum: 67% (3 participants)"] + end + + subgraph "Manipulated Configuration" + A2["Alice: 10%"] + B2["Bob: 10%"] + C2["Carol: 10%"] + M1["Mallory: 35%"] + M2["Mallory Alt: 35%"] + Q2["Quorum: 51% (Mallory controls)"] + end + + subgraph "Zero Weight Attack" + A3["Alice: 50%"] + B3["Bob: 50%"] + Z1["Zero1: 0%"] + Z2["Zero2: 0%"] + Z3["Zero3: 0%"] + Q3["Quorum: 51% (confusion in validation)"] + end +``` + +A particularly subtle attack involves including participants with zero weight to dilute consensus calculations. While these participants cannot directly influence voting outcomes, they can be used to manipulate quorum calculations and create confusion in consensus validation logic. **Mitigation Patterns:** ```go @@ -82,7 +113,7 @@ func validateQuorumConfiguration(participants []string, weights []uint64, quorum if len(participants) != len(weights) { return fmt.Errorf("participants and weights length mismatch") } - + totalWeight := uint64(0) for _, weight := range weights { if weight == 0 { @@ -90,13 +121,13 @@ func validateQuorumConfiguration(participants []string, weights []uint64, quorum } totalWeight += weight } - + // Require supermajority for security minQuorum := (totalWeight * 2) / 3 + 1 if quorum < minQuorum { return fmt.Errorf("quorum too low: %d, minimum: %d", quorum, minQuorum) } - + return nil } ``` @@ -112,10 +143,11 @@ if totalWeight < int64(appSession.Quorum) { } ``` -**Security Requirements:** -- **Signature weight aggregation**: Correctly sum weights from valid signatures -- **Threshold enforcement**: Reject states that don't meet quorum requirements -- **Participant validation**: Ensure signers are actual participants with correct weights +**Security Requirements** + +Robust quorum-based consensus requires careful attention to signature weight aggregation, ensuring that weights from valid signatures are correctly summed and verified against participant configurations. **The system must implement strict threshold enforcement, automatically rejecting any state transitions that fail to meet established quorum requirements.** + +Participant validation forms a critical component of security, requiring verification that all signers are legitimate participants with correctly assigned weights. This validation must occur before any consensus calculations to prevent unauthorized participants from influencing application state. ## Fund Isolation and Balance Security @@ -129,10 +161,34 @@ INSERT INTO ledger_entries (account_id, wallet, asset_symbol, credit, debit) VALUES ('session_abc123', 'participant_wallet', 'USDC', 0, 100.0); ``` -**Attack Vectors:** -- **Double spending**: Spending the same funds across multiple virtual applications -- **Balance inflation**: Creating credits without corresponding debits -- **Cross-session leakage**: Accessing funds from other virtual applications +**Attack Vectors** + +**Double spending attacks represent the most direct threat, where malicious participants attempt to spend the same funds simultaneously across multiple virtual applications.** This is particularly dangerous in systems where balance validation occurs independently within each application context. + +```mermaid +sequenceDiagram + participant U as User + participant A1 as Virtual App 1 + participant A2 as Virtual App 2 + participant VL as Virtual Ledger + + Note over U,VL: Double Spending Attack + U->>A1: Spend 100 USDC + U->>A2: Spend 100 USDC (same funds) + + A1->>VL: Validate balance: 100 USDC + VL-->>A1: ✓ Valid (independent check) + A2->>VL: Validate balance: 100 USDC + VL-->>A2: ✓ Valid (independent check) + + Note over A1,A2: Both apps accept transaction + A1->>VL: Debit 100 USDC + A2->>VL: Debit 100 USDC + + Note over VL: Balance: -100 USDC (compromised) +``` + +Balance inflation attacks exploit weaknesses in double-entry accounting implementations, where attackers create credits without corresponding debits, effectively creating funds from nothing. Cross-session leakage represents a more sophisticated attack where participants gain unauthorized access to funds from virtual applications they should not be able to access. **Security Implementation:** ```go @@ -143,16 +199,16 @@ func (vl *VirtualLedger) TransferFunds(from, to, asset string, amount decimal.De if err != nil { return err } - + if fromBalance.LessThan(amount) { return fmt.Errorf("insufficient balance: %s < %s", fromBalance, amount) } - + // CRITICAL: Atomic debit/credit operations if err := vl.RecordTransaction(from, asset, amount.Neg(), tx); err != nil { return err } - + return vl.RecordTransaction(to, asset, amount, tx) }) } @@ -187,20 +243,20 @@ contract TicTacToeAdjudicator is IAdjudicator { uint8 currentPlayer; // 1 or 2 bool gameEnded; } - + function adjudicate(Channel calldata chan, State calldata candidate, State[] calldata proofs) external view override returns (bool valid) { - + if (proofs.length != 1) return false; - + GameState memory prevGame = abi.decode(proofs[0].data, (GameState)); GameState memory currGame = abi.decode(candidate.data, (GameState)); - + // CRITICAL: Validate turn order if (currGame.currentPlayer == prevGame.currentPlayer) { return false; // Same player cannot move twice } - + // CRITICAL: Validate single move uint8 moveCount = 0; for (uint i = 0; i < 9; i++) { @@ -210,7 +266,7 @@ contract TicTacToeAdjudicator is IAdjudicator { moveCount++; } } - + return moveCount == 1; // Exactly one move allowed } } @@ -229,7 +285,7 @@ struct ApplicationData { } // BAD: Raw bytes that can be manipulated -// bytes applicationData; +// bytes applicationData; ``` ## Session Management Security @@ -245,13 +301,13 @@ func (h *VirtualAppHandler) validateParticipant(sessionID string, walletAddress if err != nil { return err } - + for _, participant := range appSession.ParticipantWallets { if strings.EqualFold(participant, walletAddress) { return nil } } - + return fmt.Errorf("wallet %s not authorized for session %s", walletAddress, sessionID) } ``` @@ -268,12 +324,12 @@ func createVirtualApplication(participants []string, weights []uint64, quorum ui return fmt.Errorf("participant %s has no active channel", participant) } } - + // CRITICAL: Validate quorum configuration if err := validateQuorumConfiguration(participants, weights, quorum); err != nil { return err } - + // CRITICAL: Ensure sufficient funds for virtual application return h.validateSufficientFunds(participants) } @@ -295,12 +351,12 @@ func (vl *VirtualLedger) UpdateState(sessionID string, newState ApplicationState Where("session_id = ?", sessionID).First(&session).Error; err != nil { return err } - + // Validate state version progression if newState.Version != session.Version + 1 { return fmt.Errorf("invalid version progression") } - + // Apply state update atomically session.Version = newState.Version return tx.Save(&session).Error @@ -325,56 +381,95 @@ func (vl *VirtualLedger) ExecuteMultiPartyTransfer(transfers []Transfer) error { return fmt.Errorf("insufficient balance for %s", transfer.From) } } - + // Execute all transfers atomically for _, transfer := range transfers { if err := vl.ExecuteTransferInTx(transfer, tx); err != nil { return err // Automatic rollback } } - + return nil }) } ``` -## Security Checklist for Virtual Application Developers - -**Adjudicator Implementation:** -- [ ] Validate unanimous signatures for all state transitions -- [ ] Enforce strict version progression (current.version = previous.version + 1) -- [ ] Implement proper allocation conservation checks -- [ ] Validate application-specific business logic -- [ ] Use structured ABI encoding for state data - -**Quorum Configuration:** -- [ ] Validate participant count matches weight count -- [ ] Reject zero or negative weights -- [ ] Enforce minimum quorum thresholds (recommend ≥67% of total weight) -- [ ] Validate total weight calculations -- [ ] Implement supermajority requirements for critical operations - -**Fund Management:** -- [ ] Use atomic database transactions for all fund operations -- [ ] Validate sufficient balances before transfers -- [ ] Implement proper session isolation -- [ ] Audit virtual ledger balance calculations -- [ ] Prevent cross-session fund access - -**Session Security:** -- [ ] Validate participant authorization for virtual applications -- [ ] Implement proper session lifecycle management -- [ ] Use database locking for concurrent state updates -- [ ] Validate state version progression -- [ ] Implement session timeout mechanisms - -**Business Logic:** -- [ ] Validate all state transitions according to application rules -- [ ] Implement turn-based validation for sequential applications -- [ ] Prevent illegal moves or state manipulations -- [ ] Use deterministic state encoding -- [ ] Implement proper game/application termination conditions +## Security Guidelines for Virtual Application Developers + +### Adjudicator Implementation Security + +**Secure adjudicator implementation forms the foundation of virtual application security. Every state transition must undergo unanimous signature validation to ensure all required participants have authorized the change.** This validation must be coupled with strict version progression enforcement, where each new state version must exactly equal the previous version plus one, preventing both replay attacks and state manipulation. + +```mermaid +flowchart TD + A["State Transition Request"] --> B["Check Signatures"] + B --> C["Check Version"] + C --> D["Check Allocation"] + D --> F["Check Business Logic"] + F --> G["Check ABI Encoding"] + G --> H["✅ Accept State"] + + B -.->|❌ Invalid| E["❌ Reject"] + C -.->|❌ Invalid| E + D -.->|❌ Invalid| E + F -.->|❌ Invalid| E + G -.->|❌ Invalid| E + + style A fill:#e3f2fd + style H fill:#c8e6c9 + style E fill:#ffcdd2 +``` + +Allocation conservation represents a critical financial security requirement, ensuring that the total value within the system remains constant across state transitions. Application-specific business logic validation must be implemented comprehensively to prevent manipulation of game rules or payment conditions. All state data should utilize structured ABI encoding to prevent ambiguity and ensure deterministic serialization across different implementations. + +### Quorum Configuration Security + +Proper quorum configuration requires meticulous validation of participant and weight configurations. The system must verify that participant counts exactly match weight counts to prevent configuration errors that could compromise consensus. **Zero or negative weights must be explicitly rejected, as they can be exploited to manipulate consensus calculations or create undefined behavior.** + +Minimum quorum thresholds should enforce supermajority requirements, typically requiring at least 67% of total weight for security-critical operations. Total weight calculations must be validated to prevent overflow conditions or mathematical errors that could compromise consensus integrity. Critical operations should implement enhanced supermajority requirements to provide additional security against coordinated attacks. + +### Fund Management Security + +**Financial security requires atomic database transactions for all fund operations to prevent race conditions and ensure consistency.** Every transfer operation must validate sufficient balances before execution, preventing overdraft conditions that could compromise system integrity. Session isolation must be properly implemented to prevent unauthorized access to funds from different virtual applications. + +Regular auditing of virtual ledger balance calculations helps detect discrepancies or computational errors that could indicate security breaches or implementation bugs. Cross-session fund access must be prevented through strict access controls and session validation to maintain the isolation guarantees that enable secure multi-tenancy. + +### Session Security Management + +Participant authorization validation ensures that only legitimate users can access virtual applications they are authorized to participate in. **Session lifecycle management must be implemented comprehensively, covering creation, operation, and termination phases with appropriate security controls at each stage.** + +```mermaid +stateDiagram-v2 + [*] --> Creation + Creation --> AuthValidation: Validate participants + AuthValidation --> QuorumCheck: Check weights & quorum + QuorumCheck --> Operation: Session active + + Operation --> StateUpdate: Process transactions + StateUpdate --> SignatureVerify: Validate signatures + SignatureVerify --> Operation: Continue session + + Operation --> Timeout: Session expires + Operation --> Termination: Manual close + + Timeout --> Cleanup: Force cleanup + Termination --> FinalSettlement: Settle balances + FinalSettlement --> Cleanup: Clean resources + Cleanup --> [*] + + AuthValidation --> [*]: Validation failed + QuorumCheck --> [*]: Quorum invalid + SignatureVerify --> [*]: Signature invalid +``` + +Database locking mechanisms are essential for handling concurrent state updates safely, preventing race conditions that could lead to inconsistent state or financial discrepancies. State version progression must be validated continuously to maintain the temporal ordering that underlies the security model. Session timeout mechanisms provide protection against abandoned sessions and help prevent resource exhaustion attacks. + +### Business Logic Security + +**Application-specific validation must encompass all possible state transitions according to the defined rules of the virtual application.** Turn-based applications require special consideration to prevent players from taking multiple consecutive actions or skipping opponents' turns. Comprehensive validation prevents illegal moves or state manipulations that could compromise fair play or financial integrity. + +Deterministic state encoding ensures that all participants interpret application state identically, preventing disputes arising from encoding ambiguities. Game and application termination conditions must be properly implemented to ensure clean resource cleanup and final settlement of participant balances. --- -For implementation examples, see the [Consensus](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Consensus.sol) and [Remittance](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Remittance.sol) adjudicators. \ No newline at end of file +These security guidelines provide a comprehensive framework for developing secure virtual applications on Nitrolite. For practical implementation examples demonstrating these principles, examine the [Consensus](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Consensus.sol) and [Remittance](https://github.com/erc7824/nitrolite/blob/main/contract/src/adjudicators/Remittance.sol) adjudicators in the Nitrolite repository.