Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 144 additions & 0 deletions content/symbiotic/architecture.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
title: Architecture
---

System overview for the Symbiotic multi-provider template.

## Core Model

1. One active provider per running stack (`config/environments/<env>.json`).
2. Shared off-chain runtime:
- OZ Monitor for ingress
- 3 operator processes
- 3 Symbiotic relay sidecars for BLS signatures
- OZ Relayer for destination tx submission
- Redis queue
3. Provider-specific on-chain contracts and calldata format.

## Provider Matrix

| Provider | Source ingress event | Destination submit call | Local | Testnet | Mainnet |
| --- | --- | --- | --- | --- | --- |
| [`layerzero`](/symbiotic/layerzero) | `JobAssigned` | `SymbioticLayerZeroDVN.submitProof(...)` | Supported | Supported | Not yet |
| [`chainlink_ccv`](/symbiotic/chainlink-ccv) | `CCIPMessageSent` | `OffRamp.execute(...)` | Supported | Not yet | Not yet |

See per-provider pages for detailed message flows and code pointers.

## Shared Off-Chain Runtime

```mermaid
flowchart LR
subgraph source["Source Chain"]
Event["Provider Ingress Event"]
end

Event --> Monitor["OZ Monitor"]
Monitor --> Operators["Operators (x3)"]
Operators <--> Relays["Symbiotic Relays\n(BLS sidecars)"]
Operators --> Relayer["OZ Relayer"]

subgraph dest["Destination Chain"]
Submit["Provider Destination Call"]
end

Relayer --> Submit
```

All providers share the same off-chain pipeline. The provider abstraction determines:
- Which event the monitor watches for
- How operators encode the payload
- What calldata the relayer submits

## Merkle Tree Batching

Messages are batched into Merkle trees for gas efficiency:

1. Multiple messages are collected into a batch
2. Each message becomes a leaf in the Merkle tree
3. The Merkle root is signed by operators
4. Proofs allow verifying individual messages against the signed root

This means:
- One signature covers many messages
- On-chain verification cost is amortized
- Individual messages can be verified independently

## Symbiotic Integration

Symbiotic provides the shared security layer:

- **Operator Registration**: Operators stake and register their BLS public keys
- **Settlement Contract**: Verifies BLS signatures and checks quorum
- **Slashing**: Misbehaving operators can be penalized (production)

The Settlement contract:
1. Maintains the list of registered operators and their public keys
2. Defines the quorum threshold
3. Verifies aggregated signatures
4. Reports verification results to the provider contract

## BLS Signing Pipeline

1. Operators sign provider-defined payloads through Symbiotic relay sidecars.
2. Aggregation/quorum logic comes from settlement-backed Symbiotic attestation rules.
3. Provider-specific contracts decode and enforce those attestations on the destination execution path.

## Message Status Lifecycle

```mermaid
flowchart LR
Pending --> Processing --> Signed --> Submitted --> Confirmed
```

| Status | Description |
|--------|-------------|
| Pending | Received via webhook, awaiting batching |
| Processing | Batched into Merkle tree, awaiting BLS signatures |
| Signed | Quorum signatures collected, ready for submission |
| Submitted | Sent to OZ Relayer |
| Confirmed | On-chain TX confirmed |

## Operator Internals

| Module | Location | Purpose |
|--------|----------|---------|
| API Server | `operator/src/api/` | Axum HTTP server, webhook endpoint, debug routes |
| Provider | `operator/src/provider/` | Provider trait, event decoding, message storage |
| SignerJob | `operator/src/signer/` | Batches messages into Merkle trees, requests BLS signatures |
| RelaySubmitterJob | `operator/src/relay_submitter/` | Submits signed proofs via OZ Relayer |
| Storage | `operator/src/storage/` | redb key-value store (messages, Merkle trees, submissions) |
| Crypto | `operator/src/crypto/` | Merkle tree construction, leaf hashing, signing message encoding |

## Adding a New Provider

1. Create `operator/src/provider/yourprovider.rs` implementing the `Provider` trait:

```rust
#[async_trait]
pub trait Provider: Send + Sync + 'static {
fn name(&self) -> &'static str;
async fn handle_webhook_event(&self, event: &WebhookEvent) -> Result<(), ProviderError>;

// Optional overrides:
fn register_api_routes(&self, router: Router<AppState>) -> Router<AppState> { router }
async fn acceptance_hook(&self, _msg: &MessageData) -> Result<(), ProviderError> { Ok(()) }
}
```

2. Add configuration to `operator/src/config/mod.rs`.
3. Register in `create_provider()` in `operator/src/provider/mod.rs`.
4. Create provider-specific monitor template in `config/templates/oz-monitor/monitors/`.
5. Create `docs/<provider-name>.mdx` following the structure of existing provider docs (e.g., [LayerZero](/symbiotic/layerzero)).
6. Update this file's provider matrix, the [docs index](/symbiotic), and the project README.

## Environment Comparison

| Aspect | Local (Anvil) | Testnet | Production |
|--------|---------------|---------|------------|
| Source chain | Anvil 31337 | Base Sepolia 84532 | Mainnet |
| Dest/Settlement chain | Anvil 31338 | Sepolia 11155111 | Mainnet |
| Operators | 3 (local containers) | 3 (local containers) | 1+ (distributed) |
| Symbiotic Core | Deployed locally | Pre-deployed on Sepolia | Pre-deployed |
| BLS Keys | Deterministic | Deterministic | Hardware security |
| Quorum | 2-of-3 | 2-of-3 | Configurable |
| OZ Services | Local | Local | Hosted by OZ |
131 changes: 131 additions & 0 deletions content/symbiotic/chainlink-ccv.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
title: Chainlink CCV
---

Symbiotic-secured Cross-Chain Verifier (CCV) for Chainlink CCIP-compatible message verification.

## Overview

The CCV provider implements a Symbiotic-backed verifier compatible with Chainlink's CCIP CCV interface. When a message is sent through an OnRamp-compatible contract, a `CCIPMessageSent` event is emitted. Operators build a CCV payload, collect BLS attestations via Symbiotic relay sidecars, and the relayer submits the proof to `OffRamp.execute(...)` on the destination chain. The OffRamp calls `SymbioticCCV.verifyMessage(...)` for each message, and success is confirmed when `MessageExecuted(messageId)` is emitted.

<Callout>

This template supports the **Symbiotic CCV variant** only. The Chainlink auxiliary devenv stack (aggregator, indexer, verifier, executor) is not required.

</Callout>

## Message Flow

```mermaid
sequenceDiagram
participant App as Source App
participant OnRamp as OnRamp (Source)
participant Monitor as OZ Monitor
participant Operators as Operators (x3)
participant Relay as Symbiotic Relay (BLS)
participant Relayer as OZ Relayer
participant OffRamp as OffRamp (Dest)
participant CCV as SymbioticCCV (Dest)

App->>OnRamp: sendMessage()
OnRamp-->>Monitor: CCIPMessageSent event
Monitor->>Operators: HMAC webhook
Operators->>Operators: build CCV payload + Merkle tree
Operators->>Relay: sign Merkle root (BLS)
Relay-->>Operators: aggregated signature
Operators->>Relayer: OffRamp.execute calldata
Relayer->>OffRamp: execute(message, ccvs, verifierResults)
OffRamp->>CCV: verifyMessage(message, messageId, verifierResults)
CCV-->>OffRamp: verified
OffRamp-->>OffRamp: emit MessageExecuted(messageId)
```

## Code Pointers

### Contracts

- `contracts/src/ccv/SymbioticCCV.sol` -- CCV verifier implementation (`verifyMessage`, `forwardToVerifier`)
- `contracts/src/ccv/interfaces/` -- CCV interface definitions (`ICrossChainVerifierV1`, etc.)
- `contracts/src/ccv/libraries/` -- CCV encoding and helper libraries
- `contracts/src/symbiotic/Settlement.sol` -- BLS signature verification and quorum enforcement
- `contracts/src/symbiotic/KeyRegistry.sol` -- Operator BLS public key registry
- `contracts/src/symbiotic/Driver.sol` -- Epoch and genesis management

### Operator (Rust)

- `operator/src/provider/chainlink_ccv.rs` -- Decodes `CCIPMessageSent` events, builds CCV payloads
- `operator/src/provider/mod.rs` -- `Provider` trait and registration

### Config Templates

- `config/templates/oz-monitor/monitors/ccip_message_sent.json` -- Monitor job for `CCIPMessageSent` events

## Configuration

Select CCV as the active provider:

```json
// config/environments/<env>.json
{
"activeProvider": "chainlink_ccv"
}
```

Chain config is shared across providers — chain IDs from `chains.source.chainId` and `chains.destination.chainId` are used as CCIP chain selectors at runtime.

Address resolution for CCV scripts:

1. `CCV_*` environment variables (highest priority)
2. `deployments/<env>.json`

CCV settlement addresses in deployment state:

- Destination: `destination.chainlinkCcv.settlement` in `deployments/<env>.json`

Available `CCV_*` override variables:

| Variable | Description |
|----------|-------------|
| `CCV_SOURCE_ADDRESS` | SymbioticCCV on source chain |
| `CCV_DEST_ADDRESS` | SymbioticCCV on destination chain |
| `CCV_SOURCE_ONRAMP_ADDRESS` | Source OnRamp-compatible contract |
| `CCV_DEST_OFFRAMP_ADDRESS` | Destination OffRamp submit target |

## Usage

```bash
# Select chainlink_ccv provider in config/environments/local.json
# "activeProvider": "chainlink_ccv"

# Start the stack
make start

# Send a test message
make send MSG="hello"

# Watch until MessageExecuted on destination
make watch

# Or run both
make e2e
```

`make send` sends through the source mock `OnRamp.sendMessage(...)`, emitting `CCIPMessageSent`.

`make watch` succeeds only when `MessageExecuted(messageId)` is found on the destination chain (not just relayer submission).

See [CLI Reference](/symbiotic/cli) for full command options.

## Deployment Status

| Environment | Status |
|-------------|--------|
| Local | Supported (Symbiotic-only mock path) |
| Testnet | Not yet |
| Mainnet | Not yet |

## Common Issues

- **EpochTooStale revert (0xf5ab0d81)** -- Settlement epoch data is stale. Refresh genesis or tune epoch timing. See [Troubleshooting](/symbiotic/troubleshooting#epochtoostale-revert-0xf5ab0d81).
- **Watch does not reach success** -- CCV requires destination `MessageExecuted(messageId)`, not just relayer submission. See [Troubleshooting](/symbiotic/troubleshooting#watch-does-not-reach-success).
- **Submission fails at estimate-gas** -- Common causes: stale epoch, incorrect CCV addresses, settlement not initialized. See [Troubleshooting](/symbiotic/troubleshooting#submission-fails-at-estimate-gas).
Loading
Loading