Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
1 change: 1 addition & 0 deletions Dockerfile.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
!/s3
!/fallback
!/metrics
!/signer
!/go.mod
!/go.sum
158 changes: 55 additions & 103 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,49 @@ This repository implements a Celestia `da-server` for Alt-DA mode using generic
┌─────────────────────────────────────────────────────────────────────┐
│ PUT Request → Create blob → Submit via CoreGRPC → Return BlobID │
│ ↑ │
clientTX (signs with local keyring) │
Signer (local keyring OR POPSigner) │
│ │
│ GET Request → Parse BlobID (height + commitment) → blob.Get() │
│ ↑ │
│ Bridge node JSON-RPC │
├─────────────────────────────────────────────────────────────────────┤
│ Requirements: Local keyring, Bridge node (read), CoreGRPC (write) │
│ Requirements: Signer, Bridge node (read), CoreGRPC (write)
└─────────────────────────────────────────────────────────────────────┘
```

## Prerequisites

Before running the DA server, you need:

1. **A Celestia keyring with a funded account** - Required for signing transactions
1. **A signer for Celestia transactions** - Either local keyring OR POPSigner
2. **Access to a Celestia bridge/light node** - For reading blobs (JSON-RPC)
3. **Access to a Celestia consensus node** - For submitting blobs (CoreGRPC)
4. **Go 1.21+** - For building from source

### ⚠️ Important: Keyring Requirement
### Signer Options

The DA server signs transactions using a Celestia keyring. You have two options:
The DA server supports two signing modes:

**Option 1: Local Keyring** (Traditional)
- You must have a keyring directory (e.g., `~/.celestia-light-mocha-4/keys`)
- The keyring must contain a funded key for paying transaction fees
- The key name must match your configuration (`default_key_name`)
| Mode | Description | Best For |
|------|-------------|----------|
| **Local Keyring** | Filesystem-based keyring | Development, self-hosted |
| **POPSigner** | Remote signing service | Production, key security |

**Option 2: AWS KMS Keyring** (Recommended for production)
- Uses a single pre-configured KMS key
- Minimal permissions: only `kms:GetPublicKey` and `kms:Sign` required
- Key name `default_key_name` in the configuration corresponds to the KMS key alias or key ID
- Works with LocalStack for local development
## Quick Start

**The server will not work without a properly configured keyring.**
### 1. Build the Server

## Quick Start
```bash
make da-server
# or
go build -o bin/da-server ./cmd/daserver
```

### 2. Set Up Signing

### 1. Set Up Celestia Keyring
Choose **one** of the following:

First, create and fund a Celestia key:
#### Option A: Local Keyring

```bash
# Initialize a Celestia light node (creates keyring directory)
Expand All @@ -67,94 +69,43 @@ celestia-appd keys show my_celes_key --keyring-backend test \
# - Arabica faucet: https://faucet.celestia-arabica-11.com/
```

#### Alternative: AWS KMS Keyring Backend

Instead of a local keyring, you can use AWS KMS for signing Celestia transactions. This provides:
- **Hardware security** - Keys are stored in AWS KMS HSMs
- **Remote signing** - No local key material needed
- **Minimal permissions** - Only `kms:GetPublicKey` and `kms:Sign` required
- **LocalStack support** - Test locally before deploying to AWS

##### Step 1: Create a KMS Key
#### Option B: POPSigner (Remote Signing)

```bash
# For LocalStack (development)
docker run -d -p 4566:4566 localstack/localstack

# Create a secp256k1 key
KEY_ID=$(aws --endpoint-url=http://localhost:4566 kms create-key \
--key-spec ECC_SECG_P256K1 \
--key-usage SIGN_VERIFY \
--query 'KeyMetadata.KeyId' \
--output text)

# Create an alias for the key
aws --endpoint-url=http://localhost:4566 kms create-alias \
--alias-name alias/my_celes_key \
--target-key-id $KEY_ID
```

For production AWS, omit `--endpoint-url`.

##### Step 2: Configure the Server
# 1. Create account at https://popsigner.com
# 2. Generate an API key (psk_live_xxxxx)
# 3. Create a Celestia key and note the key_id (UUID)
# 4. Fund the key's Celestia address with TIA

```toml
[celestia]
keyring_backend = "awskms"
default_key_name = "alias/my_celes_key" # Use full alias name
# ... other celestia settings ...

[celestia.awskms]
region = "us-east-1"
endpoint = "http://localhost:4566" # Leave empty for AWS production
export POPSIGNER_API_KEY="psk_live_xxxxx"
```

##### Step 3: Run the Server
### 3. Run the Server

**With Local Keyring:**

```bash
./bin/da-server --config=config.toml
```

**IAM Permissions Required:**

```json
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["kms:GetPublicKey", "kms:Sign"],
"Resource": "arn:aws:kms:REGION:ACCOUNT:key/KEY_ID"
}]
}
```

### 2. Build the Server
**With POPSigner:**

```bash
make da-server
# or
go build -o bin/da-server ./cmd/daserver
POPSIGNER_API_KEY="psk_live_xxxxx" ./bin/da-server --config=config.toml
```

### 3. Run the Server
Or using CLI flags:

```bash
./bin/da-server \
--celestia.namespace="00000000000000000000000000000000000000000000000000000000acfe" \
Comment thread
tuxcanfly marked this conversation as resolved.
--celestia.server="http://localhost:26658" \
--celestia.auth-token="your-bridge-auth-token" \
--celestia.tx-client.core-grpc.addr="consensus-full-mocha-4.celestia-mocha.com:9090" \
--celestia.tx-client.keyring-path="$HOME/.celestia-light-mocha-4/keys" \
--celestia.tx-client.key-name="my_celes_key" \
--celestia.tx-client.p2p-network="mocha-4"
```

Or using a config file:

```bash
./bin/da-server --config=config.toml
```

## Configuration

### CLI Flags
Expand All @@ -173,13 +124,22 @@ Or using a config file:

| Flag | Environment Variable | Default | Description |
| -------------------------------------------- | --------------------------------------------------- | -------------- | -------------------------------------- |
| `--celestia.tx-client.key-name` | `OP_ALTDA_CELESTIA_TX_CLIENT_KEY_NAME` | `my_celes_key` | Key name in keyring |
| `--celestia.tx-client.keyring-path` | `OP_ALTDA_CELESTIA_TX_CLIENT_KEYRING_PATH` | **(required)** | Path to keyring directory (omit when using KMS) |
| `--celestia.tx-client.core-grpc.addr` | `OP_ALTDA_CELESTIA_TX_CLIENT_CORE_GRPC_ADDR` | **(required)** | CoreGRPC endpoint |
| `--celestia.tx-client.core-grpc.tls-enabled` | `OP_ALTDA_CELESTIA_TX_CLIENT_CORE_GRPC_TLS_ENABLED` | `true` | Enable TLS for CoreGRPC |
| `--celestia.tx-client.core-grpc.auth-token` | `OP_ALTDA_CELESTIA_TX_CLIENT_CORE_GRPC_AUTH_TOKEN` | | CoreGRPC auth token |
| `--celestia.tx-client.p2p-network` | `OP_ALTDA_CELESTIA_TX_CLIENT_P2P_NETWORK` | `mocha-4` | Network: mocha-4, arabica-11, celestia |

#### Signer Configuration

| Flag | Environment Variable | Default | Description |
| ------------------------------------- | -------------------------------------------- | -------------- | ------------------------------------- |
| `--celestia.signer-mode` | `OP_ALTDA_CELESTIA_SIGNER_MODE` | `local` | Signer mode: local, popsigner |
| `--celestia.tx-client.keyring-path` | `OP_ALTDA_CELESTIA_TX_CLIENT_KEYRING_PATH` | | Path to keyring (local mode) |
| `--celestia.tx-client.key-name` | `OP_ALTDA_CELESTIA_TX_CLIENT_KEY_NAME` | `my_celes_key` | Key name in keyring (local mode) |
| `--celestia.remote-signer.key-id` | `OP_ALTDA_CELESTIA_REMOTE_SIGNER_KEY_ID` | | POPSigner key UUID |
| `--celestia.remote-signer.api-key` | `POPSIGNER_API_KEY` | | POPSigner API key (env var preferred) |
| `--celestia.remote-signer.base-url` | `OP_ALTDA_CELESTIA_REMOTE_SIGNER_BASE_URL` | | Custom POPSigner endpoint |

#### Fallback Storage (Optional)

| Flag | Environment Variable | Default | Description |
Expand Down Expand Up @@ -217,39 +177,31 @@ See `config.toml.example` for a complete example:
# Server settings
addr = "127.0.0.1"
port = 3100
log_level = "info"
log_format = "text"

[celestia]
namespace = "00000000000000000000000000000000000000000000000000000000acfe"
Comment thread
tuxcanfly marked this conversation as resolved.
blobid_compact = true

# Bridge node (for reading blobs via JSON-RPC)
bridge_addr = "http://localhost:26658"
bridge_auth_token = "your-bridge-auth-token"

# Core gRPC (for submitting blobs to consensus)
core_grpc_addr = "consensus-full-mocha-4.celestia-mocha.com:9090"
core_grpc_tls_enabled = true
p2p_network = "mocha-4"

# Signer configuration - choose ONE mode
[celestia.signer]
mode = "local" # or "popsigner"

# Keyring
keyring_backend = "test" # or "awskms" for AWS KMS
# Local keyring settings
[celestia.signer.local]
keyring_path = "~/.celestia-light-mocha-4/keys"
default_key_name = "my_celes_key" # For awskms, use full alias: "alias/my_celes_key"
p2p_network = "mocha-4"
key_name = "my_celes_key"

# AWS KMS keyring (when keyring_backend = "awskms")
[celestia.awskms]
region = "us-east-1"
endpoint = "" # Set to http://localhost:4566 for localstack
# POPSigner settings (when mode = "popsigner")
# [celestia.signer.popsigner]
# key_id = "your-key-uuid"
# api_key via POPSIGNER_API_KEY env var (recommended)

[submission]
timeout = "60s"
tx_priority = 2 # 1=low, 2=medium, 3=high

[read]
timeout = "30s"

[metrics]
enabled = true
port = 6060
Expand Down
47 changes: 7 additions & 40 deletions celestia_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@ import (
"strings"
"time"

awskeyring "github.com/celestiaorg/aws-kms-keyring"
txClient "github.com/celestiaorg/celestia-node/api/client"
"github.com/celestiaorg/celestia-node/api/rpc/client"
"github.com/celestiaorg/celestia-node/blob"
blobAPI "github.com/celestiaorg/celestia-node/nodebuilder/blob"
"github.com/celestiaorg/celestia-node/nodebuilder/p2p"
"github.com/celestiaorg/celestia-node/state"
libshare "github.com/celestiaorg/go-square/v3/share"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/celestiaorg/op-alt-da/signer"
altda "github.com/ethereum-optimism/optimism/op-alt-da"
"github.com/ethereum/go-ethereum/log"
)
Expand Down Expand Up @@ -133,17 +132,14 @@ func (c *CelestiaBlobID) UnmarshalBinary(data []byte) error {
const VersionByte = 0x0c

type TxClientConfig struct {
DefaultKeyName string
KeyringBackend string // Backend type: remote - "awskms", local - "test", "file", "os", "kwallet", "pass", "keychain", "memory"
KeyringPath string
CoreGRPCAddr string
CoreGRPCTLSEnabled bool
CoreGRPCAuthToken string
P2PNetwork string
TxWorkerAccounts int // 0=immediate, 1=queued single, >1=parallel workers

// Keyring backend-specific configuration
AWSKMSConfig *awskeyring.Config
// Signer configuration (local keyring, POPSigner, AWS KMS, etc.)
Signer signer.Config
}

type RPCClientConfig struct {
Expand Down Expand Up @@ -193,42 +189,13 @@ func NewCelestiaStore(ctx context.Context, cfg RPCClientConfig) (*CelestiaStore,
}, nil
}

func initKeyring(ctx context.Context, cfg *RPCClientConfig) (keyring.Keyring, error) {
keyname := cfg.TxClientConfig.DefaultKeyName
if keyname == "" {
keyname = "my_celes_key"
}

backend := cfg.TxClientConfig.KeyringBackend
if backend == "" {
backend = keyring.BackendTest
}

var kr keyring.Keyring
var err error
switch backend {
case "awskms":
if cfg.TxClientConfig.AWSKMSConfig == nil {
return nil, fmt.Errorf("AWS KMS config is required when using awskms backend")
}
kmsCfg := *cfg.TxClientConfig.AWSKMSConfig
kmsCfg.KeyName = keyname
kr, err = awskeyring.NewKMSKeyring(ctx, kmsCfg)
default:
kr, err = txClient.KeyringWithNewKey(txClient.KeyringConfig{
KeyName: keyname,
BackendName: backend,
}, cfg.TxClientConfig.KeyringPath)
}
return kr, err
}

// initTxClient initializes a transaction client for Celestia.
// The provided context is used for client initialization and allows cancellation during startup.
func initTxClient(ctx context.Context, cfg RPCClientConfig) (blobAPI.Module, error) {
kr, err := initKeyring(ctx, &cfg)
// Create keyring using the signer package
kr, keyName, err := signer.NewKeyring(cfg.TxClientConfig.Signer)
if err != nil {
return nil, fmt.Errorf("failed to initialize keyring: %w", err)
return nil, fmt.Errorf("failed to create keyring: %w", err)
}

// Configure client
Expand All @@ -243,7 +210,7 @@ func initTxClient(ctx context.Context, cfg RPCClientConfig) (blobAPI.Module, err
EnableDATLS: cfg.TLSEnabled,
},
SubmitConfig: txClient.SubmitConfig{
DefaultKeyName: cfg.TxClientConfig.DefaultKeyName,
DefaultKeyName: keyName,
Network: p2p.Network(cfg.TxClientConfig.P2PNetwork),
TxWorkerAccounts: cfg.TxClientConfig.TxWorkerAccounts,
CoreGRPCConfig: txClient.CoreGRPCConfig{
Expand Down
Loading