docs: 📚 Add Snowbridge relay documentation

Add comprehensive documentation for the 4 Snowbridge relays:

- Beacon Relay: Syncs Ethereum beacon chain finality to DataHaven
- BEEFY Relay: Submits DataHaven BEEFY proofs to Ethereum
- Execution Relay: Delivers Ethereum messages to DataHaven
- Solochain Relay: Sends DataHaven messages to Ethereum

Each document includes:
- CLI flags and configuration file structure
- Key requirements (Ethereum/Substrate private keys)
- Docker and Kubernetes deployment examples
- Multi-instance deployment for high availability
- Monitoring and troubleshooting guides

Also updates README.md with Snowbridge section and relay overview table.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Degosserie 2025-11-29 10:05:49 +01:00
parent 948a57832e
commit 67bc549b1b
No known key found for this signature in database
GPG key ID: 9455A9F1AD80CE80
5 changed files with 1490 additions and 0 deletions

View file

@ -15,6 +15,12 @@ This directory contains comprehensive documentation for setting up and operating
- [Indexer Setup](./storagehub-indexer.md) - Indexer node configuration and operations
- [Fisherman Setup](./storagehub-fisherman.md) - Fisherman node configuration and operations
### Snowbridge Relays
- [Beacon Relay](./snowbridge-beacon-relay.md) - Ethereum beacon chain → DataHaven
- [BEEFY Relay](./snowbridge-beefy-relay.md) - DataHaven BEEFY finality → Ethereum
- [Execution Relay](./snowbridge-execution-relay.md) - Ethereum messages → DataHaven
- [Solochain Relay](./snowbridge-solochain-relay.md) - DataHaven messages → Ethereum
## Quick Reference
### Node Types Overview
@ -29,6 +35,15 @@ This directory contains comprehensive documentation for setting up and operating
| **Indexer** | Blockchain data indexer | None | No | Yes (PostgreSQL) |
| **Fisherman** | Storage provider monitor | 1 (BCSV ECDSA) | No | Yes (PostgreSQL) |
### Snowbridge Relays Overview
| Relay | Direction | Keys Required | Persistent Storage |
|-------|-----------|---------------|-------------------|
| **Beacon Relay** | Ethereum → DataHaven | Substrate | Yes (datastore) |
| **BEEFY Relay** | DataHaven → Ethereum | Ethereum | No |
| **Execution Relay** | Ethereum → DataHaven | Substrate | Yes (datastore) |
| **Solochain Relay** | DataHaven → Ethereum | Ethereum + Substrate | Yes (datastore) |
### Common CLI Flags
All node types support standard Substrate flags:
@ -71,6 +86,8 @@ All node types support standard Substrate flags:
- [Main Repository](https://github.com/Moonsong-Labs/datahaven)
- [StorageHub Repository](https://github.com/Moonsong-Labs/storage-hub)
- [Snowbridge Repository](https://github.com/datahaven-xyz/snowbridge) (solochain branch)
- [Snowbridge Documentation](https://docs.snowbridge.network)
- [E2E Testing Guide](../test/README.md)
- [Docker Compose Guide](../operator/DOCKER-COMPOSE.md)
- [Kubernetes Deployment](../deploy/charts/node/README.md)

View file

@ -0,0 +1,335 @@
# Snowbridge Beacon Relay
## Overview
The Beacon Relay syncs Ethereum beacon chain (consensus layer) finality to the DataHaven blockchain. It monitors the Ethereum beacon chain and submits sync committee updates and finality proofs to the `EthereumBeaconClient` pallet on DataHaven.
## Purpose
- Relay Ethereum beacon chain finality to DataHaven
- Submit sync committee updates for light client verification
- Enable trustless verification of Ethereum state on DataHaven
- Support cross-chain message verification from Ethereum
## Direction
```
Ethereum Beacon Chain → DataHaven
```
## Prerequisites
- Docker with `linux/amd64` platform support
- Access to Ethereum consensus layer (beacon) endpoint
- Access to DataHaven node WebSocket endpoint
- Substrate account with balance for transaction fees
- Persistent storage for relay datastore
## Key Requirements
### Substrate Private Key
The Beacon Relay requires a **Substrate private key** to sign and submit extrinsics to the DataHaven chain.
| Key Type | Purpose |
|----------|---------|
| Substrate (sr25519/ecdsa) | Sign beacon update extrinsics on DataHaven |
### Account Funding
The relay account must be funded with HAVE tokens to pay for transaction fees when submitting beacon updates.
**Recommended Balance**: 100+ HAVE for continuous operations
## CLI Flags
### Required Flags
| Flag | Description |
|------|-------------|
| `--config <PATH>` | Path to the JSON configuration file |
### Private Key Flags (One Required)
| Flag | Description |
|------|-------------|
| `--substrate.private-key <KEY>` | Substrate private key URI directly |
| `--substrate.private-key-file <PATH>` | Path to file containing the private key |
| `--substrate.private-key-id <ID>` | AWS Secrets Manager secret ID for the private key |
## Configuration File
### Structure
```json
{
"source": {
"beacon": {
"endpoint": "http://beacon-node:4000",
"stateEndpoint": "http://beacon-node:4000",
"spec": {
"syncCommitteeSize": 512,
"slotsInEpoch": 32,
"epochsPerSyncCommitteePeriod": 256,
"forkVersions": {
"deneb": 0,
"electra": 0
}
},
"datastore": {
"location": "/relay-data",
"maxEntries": 100
}
}
},
"sink": {
"parachain": {
"endpoint": "ws://datahaven-node:9944",
"maxWatchedExtrinsics": 8,
"headerRedundancy": 20
},
"updateSlotInterval": 30
}
}
```
### Configuration Parameters
#### Source (Beacon Chain)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `source.beacon.endpoint` | Beacon chain HTTP API endpoint | `http://beacon-node:4000` |
| `source.beacon.stateEndpoint` | Beacon chain state endpoint (usually same as above) | `http://beacon-node:4000` |
| `source.beacon.spec.syncCommitteeSize` | Size of sync committee | `512` |
| `source.beacon.spec.slotsInEpoch` | Slots per epoch | `32` |
| `source.beacon.spec.epochsPerSyncCommitteePeriod` | Epochs per sync committee period | `256` |
| `source.beacon.spec.forkVersions` | Fork version configuration | `{"deneb": 0, "electra": 0}` |
| `source.beacon.datastore.location` | Path to persistent datastore | `/relay-data` |
| `source.beacon.datastore.maxEntries` | Maximum datastore entries | `100` |
#### Sink (DataHaven)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `sink.parachain.endpoint` | DataHaven WebSocket endpoint | `ws://datahaven-node:9944` |
| `sink.parachain.maxWatchedExtrinsics` | Max concurrent watched extrinsics | `8` |
| `sink.parachain.headerRedundancy` | Header redundancy factor | `20` |
| `sink.updateSlotInterval` | Slot interval for updates | `30` |
## Initialization: Beacon Client Pallet
Before starting the Beacon Relay, the `EthereumBeaconClient` pallet must be initialized with a checkpoint.
### Generate Initial Checkpoint
```bash
docker run --rm \
-v $(pwd)/beacon-relay.json:/app/beacon-relay.json:ro \
-v $(pwd)/checkpoint.json:/app/dump-initial-checkpoint.json \
-v $(pwd)/datastore:/data \
--platform linux/amd64 \
datahavenxyz/snowbridge-relay:latest \
generate-beacon-checkpoint --config beacon-relay.json --export-json
```
### Submit Checkpoint to DataHaven
The checkpoint must be submitted via a sudo call to `EthereumBeaconClient.force_checkpoint`:
```typescript
import { createClient } from 'polkadot-api';
import { datahaven } from '@polkadot-api/descriptors';
const client = createClient(wsProvider);
const api = client.getTypedApi(datahaven);
const forceCheckpointCall = api.tx.EthereumBeaconClient.force_checkpoint({
update: checkpoint // Parsed from dump-initial-checkpoint.json
});
const tx = api.tx.Sudo.sudo({
call: forceCheckpointCall.decodedCall
});
await tx.signAndSubmit(sudoSigner);
```
## Running the Relay
### Docker Run
```bash
docker run -d \
--name snowbridge-beacon-relay \
--platform linux/amd64 \
--add-host host.docker.internal:host-gateway \
--network datahaven-network \
-v $(pwd)/beacon-relay.json:/configs/beacon-relay.json:ro \
-v $(pwd)/relay-data:/relay-data \
--pull always \
datahavenxyz/snowbridge-relay:latest \
run beacon \
--config /configs/beacon-relay.json \
--substrate.private-key "0x..."
```
### Docker Compose
```yaml
version: '3.8'
services:
beacon-relay:
image: datahavenxyz/snowbridge-relay:latest
container_name: snowbridge-beacon-relay
platform: linux/amd64
restart: unless-stopped
volumes:
- ./configs/beacon-relay.json:/configs/beacon-relay.json:ro
- beacon-relay-data:/relay-data
command:
- "run"
- "beacon"
- "--config"
- "/configs/beacon-relay.json"
- "--substrate.private-key-file"
- "/secrets/substrate-key"
secrets:
- substrate-key
volumes:
beacon-relay-data:
secrets:
substrate-key:
file: ./secrets/beacon-relay-substrate-key
```
## Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dh-beacon-relay
spec:
serviceName: dh-beacon-relay
replicas: 1
selector:
matchLabels:
app: dh-beacon-relay
template:
metadata:
labels:
app: dh-beacon-relay
spec:
containers:
- name: beacon-relay
image: datahavenxyz/snowbridge-relay:latest
imagePullPolicy: Always
args:
- "run"
- "beacon"
- "--config"
- "/configs/beacon-relay.json"
- "--substrate.private-key-file"
- "/secrets/dh-beacon-relay-substrate-key"
volumeMounts:
- name: config
mountPath: /configs
readOnly: true
- name: secrets
mountPath: /secrets
readOnly: true
- name: relay-data
mountPath: /relay-data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: config
configMap:
name: beacon-relay-config
- name: secrets
secret:
secretName: dh-beacon-relay-substrate-key
volumeClaimTemplates:
- metadata:
name: relay-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
```
## Monitoring
### Health Checks
The relay logs sync committee updates and beacon chain state:
```bash
# View relay logs
docker logs -f snowbridge-beacon-relay
# Check for successful updates
docker logs snowbridge-beacon-relay 2>&1 | grep -i "submitted\|update"
```
### Key Metrics to Monitor
- Beacon chain slot lag
- Sync committee update frequency
- Transaction success rate
- Account balance (for fees)
## Troubleshooting
### Issue: Beacon Chain Not Ready
**Symptoms**: Relay fails to start or continuously retries
**Check**:
1. Beacon chain endpoint is accessible
2. Beacon chain has finalized blocks
3. Network connectivity between relay and beacon node
```bash
# Test beacon chain connectivity
curl http://beacon-node:4000/eth/v1/beacon/states/head/finality_checkpoints
```
### Issue: Checkpoint Submission Failed
**Check**:
1. Sudo account has sufficient balance
2. Checkpoint data is valid
3. DataHaven node is synced
### Issue: Transaction Failures
**Check**:
1. Relay account has sufficient HAVE balance
2. DataHaven node is accessible
3. No duplicate relayers submitting same updates
## Security Considerations
1. **Private Key Protection**: Store private keys securely (AWS Secrets Manager, Kubernetes secrets, or encrypted files)
2. **Network Security**: Restrict access to relay endpoints
3. **Access Control**: Use dedicated accounts with minimal required permissions
4. **Monitoring**: Set up alerts for relay failures
## Related Documentation
- [BEEFY Relay](./snowbridge-beefy-relay.md)
- [Execution Relay](./snowbridge-execution-relay.md)
- [Solochain Relay](./snowbridge-solochain-relay.md)
- [Snowbridge Documentation](https://docs.snowbridge.network)
- [DataHaven Snowbridge Repository](https://github.com/datahaven-xyz/snowbridge)

View file

@ -0,0 +1,353 @@
# Snowbridge BEEFY Relay
## Overview
The BEEFY Relay submits DataHaven BEEFY (Bridge Efficiency Enabling Finality Yielder) finality proofs to the `BeefyClient` smart contract on Ethereum. This enables trustless verification of DataHaven state on Ethereum.
## Purpose
- Relay DataHaven BEEFY finality proofs to Ethereum
- Submit validator set commitments to BeefyClient contract
- Enable trustless verification of DataHaven state on Ethereum
- Support cross-chain message verification to Ethereum
## Direction
```
DataHaven → Ethereum
```
## Prerequisites
- Docker with `linux/amd64` platform support
- Access to DataHaven node WebSocket endpoint
- Access to Ethereum execution layer WebSocket endpoint
- Ethereum account with ETH for gas fees
- Deployed BeefyClient and Gateway contracts on Ethereum
## Key Requirements
### Ethereum Private Key
The BEEFY Relay requires an **Ethereum private key** to sign and submit transactions to the BeefyClient contract.
| Key Type | Purpose |
|----------|---------|
| Ethereum (secp256k1) | Sign Ethereum transactions to BeefyClient contract |
### Account Funding
The relay account must be funded with ETH to pay for gas when submitting BEEFY proofs.
**Recommended Balance**: 0.5+ ETH for continuous operations (gas costs vary with network conditions)
## CLI Flags
### Required Flags
| Flag | Description |
|------|-------------|
| `--config <PATH>` | Path to the JSON configuration file |
### Private Key Flags (One Required)
| Flag | Description |
|------|-------------|
| `--ethereum.private-key <KEY>` | Ethereum private key directly |
| `--ethereum.private-key-file <PATH>` | Path to file containing the private key |
| `--ethereum.private-key-id <ID>` | AWS Secrets Manager secret ID for the private key |
### Optional Flags
| Flag | Description | Default |
|------|-------------|---------|
| `--on-demand` | Synchronize commitments on demand | `false` |
## Configuration File
### Structure
```json
{
"source": {
"polkadot": {
"endpoint": "ws://datahaven-node:9944"
}
},
"sink": {
"ethereum": {
"endpoint": "ws://ethereum-node:8546",
"gas-limit": ""
},
"descendants-until-final": 3,
"contracts": {
"BeefyClient": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528",
"Gateway": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf"
}
},
"on-demand-sync": {
"max-tokens": 5,
"refill-amount": 1,
"refill-period": 3600
}
}
```
### Configuration Parameters
#### Source (DataHaven)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `source.polkadot.endpoint` | DataHaven WebSocket endpoint | `ws://datahaven-node:9944` |
#### Sink (Ethereum)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `sink.ethereum.endpoint` | Ethereum WebSocket endpoint | `ws://ethereum-node:8546` |
| `sink.ethereum.gas-limit` | Optional gas limit override | `""` (empty for auto) |
| `sink.descendants-until-final` | Blocks to wait for finality | `3` |
| `sink.contracts.BeefyClient` | BeefyClient contract address | `0x...` |
| `sink.contracts.Gateway` | Gateway contract address | `0x...` |
#### On-Demand Sync (Rate Limiting)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `on-demand-sync.max-tokens` | Maximum tokens for rate limiting | `5` |
| `on-demand-sync.refill-amount` | Tokens to refill per period | `1` |
| `on-demand-sync.refill-period` | Refill period in seconds | `3600` |
## Prerequisites: BEEFY Protocol Ready
Before starting the BEEFY Relay, the BEEFY protocol must be active on DataHaven.
### Check BEEFY Status
```bash
# Using curl with JSON-RPC
curl -s -H "Content-Type: application/json" \
-d '{"id":1, "jsonrpc":"2.0", "method": "beefy_getFinalizedHead"}' \
http://localhost:9944
# Should return a non-zero block hash when ready
```
### Automated Wait (Test Environment)
The test framework automatically waits for BEEFY with a 60-second timeout:
```typescript
const waitBeefyReady = async (pollIntervalMs: number, timeoutMs: number) => {
// Poll beefy_getFinalizedHead until it returns a non-zero hash
};
```
## Running the Relay
### Docker Run
```bash
docker run -d \
--name snowbridge-beefy-relay \
--platform linux/amd64 \
--add-host host.docker.internal:host-gateway \
--network datahaven-network \
-v $(pwd)/beefy-relay.json:/configs/beefy-relay.json:ro \
--pull always \
datahavenxyz/snowbridge-relay:latest \
run beefy \
--config /configs/beefy-relay.json \
--ethereum.private-key "0x..."
```
### Docker Compose
```yaml
version: '3.8'
services:
beefy-relay:
image: datahavenxyz/snowbridge-relay:latest
container_name: snowbridge-beefy-relay
platform: linux/amd64
restart: unless-stopped
volumes:
- ./configs/beefy-relay.json:/configs/beefy-relay.json:ro
command:
- "run"
- "beefy"
- "--config"
- "/configs/beefy-relay.json"
- "--ethereum.private-key-file"
- "/secrets/ethereum-key"
secrets:
- ethereum-key
secrets:
ethereum-key:
file: ./secrets/beefy-relay-ethereum-key
```
## Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dh-beefy-relay
spec:
replicas: 1
selector:
matchLabels:
app: dh-beefy-relay
template:
metadata:
labels:
app: dh-beefy-relay
spec:
containers:
- name: beefy-relay
image: datahavenxyz/snowbridge-relay:latest
imagePullPolicy: Always
args:
- "run"
- "beefy"
- "--config"
- "/configs/beefy-relay.json"
- "--ethereum.private-key-file"
- "/secrets/dh-beefy-relay-ethereum-key"
volumeMounts:
- name: config
mountPath: /configs
readOnly: true
- name: secrets
mountPath: /secrets
readOnly: true
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "250m"
volumes:
- name: config
configMap:
name: beefy-relay-config
- name: secrets
secret:
secretName: dh-beefy-relay-ethereum-key
```
Note: BEEFY Relay does **not** require persistent storage (no `volumeClaimTemplates`).
## Contract Requirements
### BeefyClient Contract
The BeefyClient contract must be deployed on Ethereum and initialized with:
- Initial validator set
- Initial BEEFY authority set ID
### Gateway Contract
The Gateway contract coordinates cross-chain message passing and interacts with BeefyClient for verification.
### Get Contract Addresses
Contract addresses are typically stored in a deployments file after contract deployment:
```typescript
const deployments = await parseDeploymentsFile();
const beefyClientAddress = deployments.BeefyClient;
const gatewayAddress = deployments.Gateway;
```
## Monitoring
### Health Checks
```bash
# View relay logs
docker logs -f snowbridge-beefy-relay
# Check for BEEFY proof submissions
docker logs snowbridge-beefy-relay 2>&1 | grep -i "submit\|proof\|commitment"
```
### Key Metrics to Monitor
- BEEFY finality lag (DataHaven blocks behind)
- Ethereum transaction success rate
- Gas costs and account balance
- BeefyClient contract state
### Ethereum Contract State
```bash
# Check BeefyClient latest commitment (using cast from Foundry)
cast call $BEEFY_CLIENT "latestBeefyBlock()" --rpc-url $ETH_RPC_URL
```
## Troubleshooting
### Issue: BEEFY Not Ready
**Symptoms**: Relay fails to start with "BEEFY protocol not ready"
**Check**:
1. DataHaven network has active validators
2. BEEFY pallet is enabled in runtime
3. Sufficient blocks have been produced for BEEFY finality
```bash
# Check BEEFY finalized head
curl -s -H "Content-Type: application/json" \
-d '{"id":1, "jsonrpc":"2.0", "method": "beefy_getFinalizedHead"}' \
http://localhost:9944
```
### Issue: Ethereum Transaction Failures
**Check**:
1. Relay account has sufficient ETH for gas
2. BeefyClient contract is deployed and initialized
3. Gas price is appropriate for network conditions
4. No competing relayers submitting same proofs
### Issue: Rate Limiting
**Symptoms**: Relay slows down or stops submitting proofs
**Check**:
1. `on-demand-sync` configuration is appropriate
2. Increase `max-tokens` if needed for higher throughput
3. Ensure `refill-period` matches expected submission frequency
## Security Considerations
1. **Private Key Protection**: Store Ethereum private keys securely
2. **Gas Management**: Monitor gas costs and set appropriate limits
3. **Access Control**: Use dedicated accounts with minimal ETH
4. **Monitoring**: Set up alerts for transaction failures and low balance
## Economics
### Gas Costs
- BEEFY proof submission: ~0.0003 ETH per message (varies with gas price)
- Validator set updates: Higher gas cost (less frequent)
### Incentives
Relayers can earn incentives for successful proof submissions. See Snowbridge documentation for incentive structure.
## Related Documentation
- [Beacon Relay](./snowbridge-beacon-relay.md)
- [Execution Relay](./snowbridge-execution-relay.md)
- [Solochain Relay](./snowbridge-solochain-relay.md)
- [Snowbridge Documentation](https://docs.snowbridge.network)
- [DataHaven Snowbridge Repository](https://github.com/datahaven-xyz/snowbridge)

View file

@ -0,0 +1,367 @@
# Snowbridge Execution Relay
## Overview
The Execution Relay processes Ethereum execution layer events and delivers cross-chain messages to DataHaven. It monitors the Gateway contract on Ethereum and relays messages to the corresponding pallets on DataHaven.
## Purpose
- Relay Ethereum execution layer messages to DataHaven
- Monitor Gateway contract for outbound messages
- Submit message proofs to DataHaven for verification
- Enable cross-chain token transfers and message passing from Ethereum
## Direction
```
Ethereum Execution Layer → DataHaven
```
## Prerequisites
- Docker with `linux/amd64` platform support
- Access to Ethereum execution layer WebSocket endpoint
- Access to Ethereum consensus layer (beacon) HTTP endpoint
- Access to DataHaven node WebSocket endpoint
- Substrate account with balance for transaction fees
- Deployed Gateway contract on Ethereum
- Persistent storage for relay datastore
## Key Requirements
### Substrate Private Key
The Execution Relay requires a **Substrate private key** to sign and submit extrinsics to DataHaven.
| Key Type | Purpose |
|----------|---------|
| Substrate (sr25519/ecdsa) | Sign message delivery extrinsics on DataHaven |
### Account Funding
The relay account must be funded with HAVE tokens to pay for transaction fees.
**Recommended Balance**: 100+ HAVE for continuous operations
## CLI Flags
### Required Flags
| Flag | Description |
|------|-------------|
| `--config <PATH>` | Path to the JSON configuration file |
### Private Key Flags (One Required)
| Flag | Description |
|------|-------------|
| `--substrate.private-key <KEY>` | Substrate private key URI directly |
| `--substrate.private-key-file <PATH>` | Path to file containing the private key |
| `--substrate.private-key-id <ID>` | AWS Secrets Manager secret ID for the private key |
## Configuration File
### Structure
```json
{
"source": {
"ethereum": {
"endpoint": "ws://ethereum-node:8546"
},
"contracts": {
"Gateway": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf"
},
"beacon": {
"endpoint": "http://beacon-node:4000",
"stateEndpoint": "http://beacon-node:4000",
"spec": {
"syncCommitteeSize": 512,
"slotsInEpoch": 32,
"epochsPerSyncCommitteePeriod": 256,
"forkVersions": {
"deneb": 0,
"electra": 0
}
},
"datastore": {
"location": "/relay-data",
"maxEntries": 100
}
}
},
"sink": {
"parachain": {
"endpoint": "ws://datahaven-node:9944",
"maxWatchedExtrinsics": 8,
"headerRedundancy": 20
}
},
"instantVerification": false,
"schedule": {
"id": null,
"totalRelayerCount": 1,
"sleepInterval": 1
}
}
```
### Configuration Parameters
#### Source (Ethereum)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `source.ethereum.endpoint` | Ethereum execution layer WebSocket | `ws://ethereum-node:8546` |
| `source.contracts.Gateway` | Gateway contract address | `0x...` |
#### Source (Beacon Chain)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `source.beacon.endpoint` | Beacon chain HTTP API endpoint | `http://beacon-node:4000` |
| `source.beacon.stateEndpoint` | Beacon chain state endpoint | `http://beacon-node:4000` |
| `source.beacon.spec.*` | Beacon chain specification | See beacon spec parameters |
| `source.beacon.datastore.location` | Path to persistent datastore | `/relay-data` |
| `source.beacon.datastore.maxEntries` | Maximum datastore entries | `100` |
#### Sink (DataHaven)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `sink.parachain.endpoint` | DataHaven WebSocket endpoint | `ws://datahaven-node:9944` |
| `sink.parachain.maxWatchedExtrinsics` | Max concurrent watched extrinsics | `8` |
| `sink.parachain.headerRedundancy` | Header redundancy factor | `20` |
#### Relay Settings
| Parameter | Description | Example |
|-----------|-------------|---------|
| `instantVerification` | Enable instant verification mode | `false` |
| `schedule.id` | Relayer instance ID (for multi-instance) | `null` or `0` |
| `schedule.totalRelayerCount` | Total number of relayer instances | `1` |
| `schedule.sleepInterval` | Seconds between message checks | `1` |
## Multi-Instance Deployment
For high-availability or load distribution, multiple Execution Relayers can be deployed:
```json
{
"schedule": {
"id": 0,
"totalRelayerCount": 3,
"sleepInterval": 1
}
}
```
Each instance should have a unique `id` (0, 1, 2, ...) and the same `totalRelayerCount`.
## Running the Relay
### Docker Run
```bash
docker run -d \
--name snowbridge-execution-relay \
--platform linux/amd64 \
--add-host host.docker.internal:host-gateway \
--network datahaven-network \
-v $(pwd)/execution-relay.json:/configs/execution-relay.json:ro \
-v $(pwd)/relay-data:/relay-data \
--pull always \
datahavenxyz/snowbridge-relay:latest \
run execution \
--config /configs/execution-relay.json \
--substrate.private-key "0x..."
```
### Docker Compose
```yaml
version: '3.8'
services:
execution-relay:
image: datahavenxyz/snowbridge-relay:latest
container_name: snowbridge-execution-relay
platform: linux/amd64
restart: unless-stopped
volumes:
- ./configs/execution-relay.json:/configs/execution-relay.json:ro
- execution-relay-data:/relay-data
command:
- "run"
- "execution"
- "--config"
- "/configs/execution-relay.json"
- "--substrate.private-key-file"
- "/secrets/substrate-key"
secrets:
- substrate-key
volumes:
execution-relay-data:
secrets:
substrate-key:
file: ./secrets/execution-relay-substrate-key
```
## Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dh-execution-relay
spec:
serviceName: dh-execution-relay
replicas: 1
selector:
matchLabels:
app: dh-execution-relay
template:
metadata:
labels:
app: dh-execution-relay
spec:
containers:
- name: execution-relay
image: datahavenxyz/snowbridge-relay:latest
imagePullPolicy: Always
args:
- "run"
- "execution"
- "--config"
- "/configs/execution-relay.json"
- "--substrate.private-key-file"
- "/secrets/dh-execution-relay-substrate-key"
volumeMounts:
- name: config
mountPath: /configs
readOnly: true
- name: secrets
mountPath: /secrets
readOnly: true
- name: relay-data
mountPath: /relay-data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: config
configMap:
name: execution-relay-config
- name: secrets
secret:
secretName: dh-execution-relay-substrate-key
volumeClaimTemplates:
- metadata:
name: relay-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
```
## Message Flow
### Ethereum → DataHaven Message Flow
1. User calls Gateway contract on Ethereum
2. Gateway emits `OutboundMessageAccepted` event
3. Execution Relay monitors for Gateway events
4. Relay constructs message proof using beacon chain state
5. Relay submits proof to DataHaven via `EthereumInboundQueue` pallet
6. DataHaven verifies proof against beacon client state
7. Message is dispatched to target pallet
### Supported Message Types
- Token transfers (ERC-20 tokens to DataHaven)
- Arbitrary cross-chain messages
- Smart contract calls
## Monitoring
### Health Checks
```bash
# View relay logs
docker logs -f snowbridge-execution-relay
# Check for message processing
docker logs snowbridge-execution-relay 2>&1 | grep -i "message\|submit\|proof"
```
### Key Metrics to Monitor
- Message queue depth
- Message delivery success rate
- Ethereum event processing lag
- Account balance (for fees)
- Beacon chain sync status
## Troubleshooting
### Issue: Messages Not Being Delivered
**Check**:
1. Gateway contract address is correct
2. Ethereum endpoint is accessible
3. Beacon chain is synced
4. DataHaven node is accessible
```bash
# Check Ethereum connectivity
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://ethereum-node:8545
```
### Issue: Proof Verification Failures
**Check**:
1. Beacon Relay is running and synced
2. `EthereumBeaconClient` pallet has recent updates
3. Beacon chain spec matches configuration
### Issue: High Latency
**Solutions**:
1. Reduce `sleepInterval` for faster message detection
2. Deploy multiple relay instances
3. Ensure low-latency connections to endpoints
## Security Considerations
1. **Private Key Protection**: Store Substrate private keys securely
2. **Network Security**: Use secure connections (WSS) when possible
3. **Access Control**: Use dedicated accounts with minimal permissions
4. **Monitoring**: Set up alerts for message delivery failures
## Economics
### Transaction Costs
- Message delivery: ~0.012 DOT equivalent per message (varies with message size)
- Relayers earn incentives for successful deliveries
### Incentive Structure
Relayers can claim incentives from the protocol for successful message deliveries. See Snowbridge documentation for details.
## Related Documentation
- [Beacon Relay](./snowbridge-beacon-relay.md)
- [BEEFY Relay](./snowbridge-beefy-relay.md)
- [Solochain Relay](./snowbridge-solochain-relay.md)
- [Snowbridge Documentation](https://docs.snowbridge.network)
- [DataHaven Snowbridge Repository](https://github.com/datahaven-xyz/snowbridge)

View file

@ -0,0 +1,418 @@
# Snowbridge Solochain Relay
## Overview
The Solochain Relay handles DataHaven-specific operations, including relaying outbound messages from DataHaven to Ethereum and managing validator reward distributions. This relay is specific to the DataHaven solochain implementation of Snowbridge.
## Purpose
- Relay DataHaven outbound messages to Ethereum
- Submit messages to the Gateway contract on Ethereum
- Handle validator reward synchronization
- Enable cross-chain token transfers from DataHaven to Ethereum
## Direction
```
DataHaven → Ethereum (with bidirectional monitoring)
```
## Prerequisites
- Docker with `linux/amd64` platform support
- Access to DataHaven node WebSocket endpoint
- Access to Ethereum execution layer WebSocket endpoint
- Access to Ethereum consensus layer (beacon) HTTP endpoint
- Ethereum account with ETH for gas fees
- Substrate account for DataHaven operations
- Deployed BeefyClient, Gateway, and RewardsRegistry contracts on Ethereum
- Persistent storage for relay datastore
## Key Requirements
### Both Ethereum and Substrate Private Keys
The Solochain Relay requires **both** an Ethereum private key and a Substrate private key.
| Key Type | Purpose |
|----------|---------|
| Ethereum (secp256k1) | Sign Ethereum transactions to Gateway contract |
| Substrate (sr25519/ecdsa) | Sign DataHaven operations |
### Account Funding
| Account | Funding Required |
|---------|-----------------|
| Ethereum | 0.5+ ETH for gas fees |
| Substrate | 100+ HAVE for transaction fees |
## CLI Flags
### Required Flags
| Flag | Description |
|------|-------------|
| `--config <PATH>` | Path to the JSON configuration file |
### Ethereum Private Key Flags (One Required)
| Flag | Description |
|------|-------------|
| `--ethereum.private-key <KEY>` | Ethereum private key directly |
| `--ethereum.private-key-file <PATH>` | Path to file containing the private key |
| `--ethereum.private-key-id <ID>` | AWS Secrets Manager secret ID for the private key |
### Substrate Private Key Flag
| Flag | Description |
|------|-------------|
| `--substrate.private-key <KEY>` | Substrate private key URI |
## Configuration File
### Structure
```json
{
"source": {
"ethereum": {
"endpoint": "ws://ethereum-node:8546"
},
"solochain": {
"endpoint": "ws://datahaven-node:9944"
},
"contracts": {
"BeefyClient": "0x4826533B4897376654Bb4d4AD88B7faFD0C98528",
"Gateway": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf"
},
"beacon": {
"endpoint": "http://beacon-node:4000",
"stateEndpoint": "http://beacon-node:4000",
"spec": {
"syncCommitteeSize": 512,
"slotsInEpoch": 32,
"epochsPerSyncCommitteePeriod": 256,
"forkVersions": {
"deneb": 0,
"electra": 0
}
},
"datastore": {
"location": "/relay-data",
"maxEntries": 100
}
}
},
"sink": {
"ethereum": {
"endpoint": "ws://ethereum-node:8546"
},
"contracts": {
"Gateway": "0x8f86403A4DE0BB5791fa46B8e795C547942fE4Cf"
}
},
"schedule": {
"id": 0,
"totalRelayerCount": 1,
"sleepInterval": 10
},
"reward-address": "0x4c5859f0F772848b2D91F1D83E2Fe57935348029",
"ofac": {
"enabled": false,
"apiKey": ""
}
}
```
### Configuration Parameters
#### Source Configuration
| Parameter | Description | Example |
|-----------|-------------|---------|
| `source.ethereum.endpoint` | Ethereum WebSocket endpoint | `ws://ethereum-node:8546` |
| `source.solochain.endpoint` | DataHaven WebSocket endpoint | `ws://datahaven-node:9944` |
| `source.contracts.BeefyClient` | BeefyClient contract address | `0x...` |
| `source.contracts.Gateway` | Gateway contract address | `0x...` |
| `source.beacon.*` | Beacon chain configuration | See beacon spec |
#### Sink Configuration
| Parameter | Description | Example |
|-----------|-------------|---------|
| `sink.ethereum.endpoint` | Ethereum WebSocket endpoint | `ws://ethereum-node:8546` |
| `sink.contracts.Gateway` | Gateway contract address | `0x...` |
#### Schedule Configuration
| Parameter | Description | Example |
|-----------|-------------|---------|
| `schedule.id` | Relayer instance ID (for multi-instance) | `0` |
| `schedule.totalRelayerCount` | Total number of relayer instances | `1` |
| `schedule.sleepInterval` | Seconds between message checks | `10` |
#### Rewards Configuration
| Parameter | Description | Example |
|-----------|-------------|---------|
| `reward-address` | RewardsRegistry contract address | `0x...` |
#### OFAC Compliance (Optional)
| Parameter | Description | Example |
|-----------|-------------|---------|
| `ofac.enabled` | Enable OFAC sanctions screening | `false` |
| `ofac.apiKey` | API key for OFAC screening service | `""` |
## Contract Requirements
### Required Contracts
1. **BeefyClient**: Verifies BEEFY finality proofs on Ethereum
2. **Gateway**: Handles cross-chain message passing
3. **RewardsRegistry**: Manages validator reward distribution
### Get Contract Addresses
```typescript
const deployments = await parseDeploymentsFile();
const beefyClientAddress = deployments.BeefyClient;
const gatewayAddress = deployments.Gateway;
const rewardsRegistryAddress = deployments.RewardsRegistry;
```
## Running the Relay
### Docker Run
```bash
docker run -d \
--name snowbridge-solochain-relay \
--platform linux/amd64 \
--add-host host.docker.internal:host-gateway \
--network datahaven-network \
-v $(pwd)/solochain-relay.json:/configs/solochain-relay.json:ro \
-v $(pwd)/relay-data:/relay-data \
--pull always \
datahavenxyz/snowbridge-relay:latest \
run solochain \
--config /configs/solochain-relay.json \
--ethereum.private-key "0x..." \
--substrate.private-key "0x..."
```
### Docker Compose
```yaml
version: '3.8'
services:
solochain-relay:
image: datahavenxyz/snowbridge-relay:latest
container_name: snowbridge-solochain-relay
platform: linux/amd64
restart: unless-stopped
volumes:
- ./configs/solochain-relay.json:/configs/solochain-relay.json:ro
- solochain-relay-data:/relay-data
command:
- "run"
- "solochain"
- "--config"
- "/configs/solochain-relay.json"
- "--ethereum.private-key-file"
- "/secrets/ethereum-key"
- "--substrate.private-key"
- "${SUBSTRATE_PRIVATE_KEY}"
secrets:
- ethereum-key
environment:
- SUBSTRATE_PRIVATE_KEY=${SUBSTRATE_PRIVATE_KEY}
volumes:
solochain-relay-data:
secrets:
ethereum-key:
file: ./secrets/solochain-relay-ethereum-key
```
## Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: dh-solochain-relay
spec:
serviceName: dh-solochain-relay
replicas: 1
selector:
matchLabels:
app: dh-solochain-relay
template:
metadata:
labels:
app: dh-solochain-relay
spec:
containers:
- name: solochain-relay
image: datahavenxyz/snowbridge-relay:latest
imagePullPolicy: Always
args:
- "run"
- "solochain"
- "--config"
- "/configs/solochain-relay.json"
- "--ethereum.private-key-file"
- "/secrets/dh-solochain-relay-ethereum-key"
- "--substrate.private-key"
- "$(SUBSTRATE_PRIVATE_KEY)"
env:
- name: SUBSTRATE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: dh-solochain-relay-substrate-key
key: private-key
volumeMounts:
- name: config
mountPath: /configs
readOnly: true
- name: secrets
mountPath: /secrets
readOnly: true
- name: relay-data
mountPath: /relay-data
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
volumes:
- name: config
configMap:
name: solochain-relay-config
- name: secrets
secret:
secretName: dh-solochain-relay-ethereum-key
volumeClaimTemplates:
- metadata:
name: relay-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
```
## Message Flow
### DataHaven → Ethereum Message Flow
1. User submits outbound message on DataHaven
2. `EthereumOutboundQueue` pallet queues the message
3. Solochain Relay monitors for outbound messages
4. Relay constructs message proof using BEEFY finality
5. Relay submits proof to Gateway contract on Ethereum
6. Gateway verifies proof against BeefyClient
7. Message is executed on Ethereum
### Reward Distribution Flow
1. Validators earn rewards on DataHaven
2. Reward data is synchronized to RewardsRegistry contract
3. Operators can claim rewards on Ethereum
## Multi-Instance Deployment
For high-availability, deploy multiple Solochain Relayers:
```json
{
"schedule": {
"id": 0,
"totalRelayerCount": 2,
"sleepInterval": 10
}
}
```
**Instance 0**:
```json
{ "schedule": { "id": 0, "totalRelayerCount": 2, "sleepInterval": 10 } }
```
**Instance 1**:
```json
{ "schedule": { "id": 1, "totalRelayerCount": 2, "sleepInterval": 10 } }
```
## Monitoring
### Health Checks
```bash
# View relay logs
docker logs -f snowbridge-solochain-relay
# Check for message processing
docker logs snowbridge-solochain-relay 2>&1 | grep -i "message\|submit\|reward"
```
### Key Metrics to Monitor
- Outbound message queue depth
- Message delivery success rate
- Reward synchronization status
- Ethereum and Substrate account balances
- BEEFY finality status
## Troubleshooting
### Issue: Messages Not Being Delivered
**Check**:
1. BeefyClient has recent commitments (BEEFY Relay running)
2. Gateway contract address is correct
3. Both Ethereum and Substrate endpoints are accessible
4. Account balances are sufficient
### Issue: Reward Synchronization Failures
**Check**:
1. RewardsRegistry contract address is correct
2. Ethereum account has sufficient gas
3. Reward data is available on DataHaven
### Issue: OFAC Screening Failures
**Check**:
1. API key is valid (if OFAC enabled)
2. Network connectivity to OFAC service
3. Consider disabling OFAC for testing environments
## Security Considerations
1. **Private Key Protection**: Secure both Ethereum and Substrate keys
2. **Dual Account Management**: Monitor balances on both chains
3. **Network Security**: Use secure connections (WSS) when possible
4. **Access Control**: Use dedicated accounts with minimal permissions
5. **OFAC Compliance**: Enable OFAC screening for production if required
## Dependencies
The Solochain Relay depends on:
- **BEEFY Relay**: Must be running to provide finality proofs
- **Beacon Relay**: Must be running for Ethereum light client state
Ensure both relays are operational before starting the Solochain Relay.
## Related Documentation
- [Beacon Relay](./snowbridge-beacon-relay.md)
- [BEEFY Relay](./snowbridge-beefy-relay.md)
- [Execution Relay](./snowbridge-execution-relay.md)
- [Snowbridge Documentation](https://docs.snowbridge.network)
- [DataHaven Snowbridge Repository](https://github.com/datahaven-xyz/snowbridge)