diff --git a/README.md b/README.md index d1d5698..aa82918 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,229 @@ -# Federated Signer Node +# federated-signer-node + +`federated-signer-node` is the **listener / federated-signer-node** service used by the current `bridge-utexo` federated signing stack. + +The code still uses some old naming such as `listener`, `tricorn`, and `FederatedSignerNode` in proto packages. Treat that as protocol compatibility and legacy naming, not as a different product model. + +## Current role in the stack + +One running instance of this repository represents **one federation participant**. + +It sits between: +- the **federated-signer orchestrator** in `bridge-utexo` +- the local **Parent Adapter** +- the local **Nitro Enclave** +- optionally an **EVM node** +- optionally the **rgb-multisig-bridge** hub + +```text +Bridge + | + v +Federated-signer orchestrator + | + v +Federated-signer-node / listener + | \ + | \ + v v +Parent Adapter EVM node + | + v +Nitro Enclave + +Optional on the same node: + federated-signer-node <-> rgb-multisig-bridge +``` -Listener / Signer Node for **Tricorn Federated Signing** (one instance per federation partner, typically on EC2). Accepts gRPC from the **Orchestrator** (`FederatedSignerNode`), validates bridge data, enriches `SignRequest.data`, and forwards to the **Enclave** via the **Parent Adapter** (`EnclaveService` on `listener-enclave.proto`). +## What the service actually does -## Proto layout (Tricorn communication map) +This repository now contains **three runtime capabilities** that can be enabled independently by config. -| Proto file | Channel | Go package (generated) | -|------------|---------|--------------------------| -| `proto/signer/signer.proto` | Shared messages | `signerpb` | -| `proto/bridge-orchestrator.proto` | Bridge → Orchestrator `FederatedSigner` | `pb_bridge_orchestrator` | -| `proto/orchestrator-listener.proto` | Orchestrator → Listener `FederatedSignerNode` | `pb_orchestrator_listener` | -| `proto/listener-enclave.proto` | Listener → Parent Adapter → Enclave `EnclaveService` | `pb_listener_enclave` | -| `proto/enriched/enriched-payload.proto` | Enriched / bootstrap `SignRequest.data` (no RPC) | `enrichedpb` | -| `proto/orchestrator-listener-health.proto` | Same port as Listener; ops health | `listenerhealthpb` | +### 1. EVM event indexing -Regenerate Go after editing: `bash scripts/gen-proto.sh`. +Code: +- [listener/evm_listener.go](listener/evm_listener.go) +- [listener/finality_tracker.go](listener/finality_tracker.go) -## First Task: Service Scaffolding + EVM Event Listener + SQLite +Role: +- polls the configured EVM bridge contract +- persists detected `FundsIn` events into SQLite +- tracks the last scanned block +- marks rows finalized using the chain finalized block -### Features +This is used by the signing path that must prove an EVM `FundsIn` really happened and is finalized before the enclave signs the outbound PSBT flow. -- **EVM event listener**: WebSocket subscription to Geth, parses `FundsIn`, `FundsInNative`, `FundsInBurn` from Bridge contract -- **SQLite**: `evm_events` table with migrations -- **Block finality tracker**: Polls `eth_getBlockByNumber("finalized")`, marks events as finalized -- **Graceful shutdown**: Context cancellation on SIGINT/SIGTERM +### 2. Orchestrator-facing gRPC node -### Project Structure +Code: +- [cmd/listener/main.go](cmd/listener/main.go) +- [internal/federatedsignergrpc/service.go](internal/federatedsignergrpc/service.go) +- [internal/federatedsignergrpc/grpc.go](internal/federatedsignergrpc/grpc.go) +- [internal/listenerenclaveclient](internal/listenerenclaveclient) -``` -cmd/listener/ # Main entrypoint -internal/ - config/ # Environment config - contracts/ # Bridge ABI (FundsIn events) - db/ # SQLite + migrations - listener/ # EVM listener, finality tracker +Role: +- exposes `FederatedSignerNode.Sign` +- exposes `FederatedSignerNode.PublicKey` +- exposes `ListenerHealth.Health` on the same port +- validates and enriches orchestrator bootstrap payloads +- forwards the enriched request to the enclave through the Parent Adapter + +This is the main runtime mode for the current bridge stack. + +### 3. RGB multisig hub cosigner + +Code: +- [listener/rgbbridge/listener.go](listener/rgbbridge/listener.go) +- [listener/microservice/attach_rgb.go](listener/microservice/attach_rgb.go) + +Role: +- connects to `rgb-multisig-bridge` +- syncs multisig operations from the hub +- responds as this cosigner +- stores RGB bridge audit state in SQLite +- can sign hub PSBTs either through `rgb-lib-go` or via the enclave + +This path is compiled only with `-tags rgb`. The Docker build uses that tag by default. + +## Signing flows + +The listener does not sign arbitrary opaque bytes. It validates bridge-specific context first, then sends an **enriched** payload to the enclave. + +### `TRANSACTION` flow: EVM -> RGB / PSBT signing + +Input bootstrap: +- [SignPsbtBootstrap](proto/enriched/enriched-payload.proto) + +Flow: +1. The orchestrator calls `FederatedSignerNode.Sign` with `data_type=TRANSACTION`. +2. The listener reads `tx_hash` and `operation_idx` from the bootstrap payload. +3. The listener checks that the EVM event exists in `evm_events` and is finalized. +4. The listener rejects `tx_hash` values already present in `processed_events`. +5. The listener fetches the PSBT from `rgb-multisig-bridge`. +6. The listener builds `EnrichedPsbtPayload`. +7. The listener calls `EnclaveService.Sign` through the Parent Adapter. +8. On success, the listener records the EVM tx hash in `processed_events`. + +### `SWAP` flow: RGB -> EVM / calldata signing + +Input bootstrap: +- [SignEvmBootstrap](proto/enriched/enriched-payload.proto) + +Flow: +1. The orchestrator calls `FederatedSignerNode.Sign` with `data_type=SWAP`. +2. The listener validates the consignment. +3. The listener parses `fundsOut` calldata and checks token, recipient, amount, and commission. +4. If `ORCHESTRATOR_RGB_EVM_TRANSFER_DB_CHECK=true`, the listener requires a matching `rgb_bridge_events` row by consignment hash and cross-checks the amount. +5. The listener injects chain ID, proxy contract, consignment hash, and validated amounts into `EnrichedEvmPayload`. +6. The listener calls `EnclaveService.Sign`. +7. The enclave returns the EVM signature. + +### `PublicKey` flow + +`FederatedSignerNode.PublicKey` is just proxied to the enclave through the Parent Adapter. + +### `Health` flow + +`ListenerHealth.Health` reports: +- geth syncing state +- current and highest block +- last finalized block +- whether the enclave is reachable + +## Persistence + +The service uses **SQLite**, not PostgreSQL. + +Code: +- [listener/database](listener/database) +- [listener/database/migrations](listener/database/migrations) + +Main tables: +- `evm_events`: detected EVM bridge events +- `listener_blocks`: last scanned EVM block +- `processed_events`: EVM tx hashes already consumed by successful sign flow +- `rgb_bridge_events`: RGB hub operation audit trail and transfer linkage + +## Proto map + +| Proto file | Purpose | +|------------|---------| +| [proto/signer/signer.proto](proto/signer/signer.proto) | shared signing messages | +| [proto/orchestrator-listener.proto](proto/orchestrator-listener.proto) | orchestrator -> listener RPC | +| [proto/orchestrator-listener-health.proto](proto/orchestrator-listener-health.proto) | operational health RPC | +| [proto/listener-enclave.proto](proto/listener-enclave.proto) | listener -> parent adapter -> enclave RPC | +| [proto/enriched/enriched-payload.proto](proto/enriched/enriched-payload.proto) | bootstrap and enriched payload messages | +| [proto/bridge-orchestrator.proto](proto/bridge-orchestrator.proto) | bridge -> orchestrator RPC contract reference | + +Regenerate Go code after proto edits: + +```bash +bash scripts/gen-proto.sh ``` -### Configuration +## Configuration -| Env | Default | Description | -|-----|---------|-------------| -| GETH_WS_ENDPOINT | ws://127.0.0.1:8545 | Geth WebSocket | -| BRIDGE_CONTRACT_ADDRESS | (required) | Bridge contract | -| PARENT_ADAPTER_GRPC | 127.0.0.1:5000 | Parent Adapter (`tricorn.EnclaveService`) | -| LISTEN_PORT | 5001 | Future gRPC port | -| LOG_LEVEL | info | debug, info, warn, error | -| DB_PATH | ./data/listener.db | SQLite path | -| FINALITY_POLL_INTERVAL_SEC | 12 | Finality poll interval | +Current example files: +- [configs/env_examples/.example.listener.env](configs/env_examples/.example.listener.env) +- [configs/env_examples/.example.listener.database.env](configs/env_examples/.example.listener.database.env) -Copy `.env.example` to `.env` and set `BRIDGE_CONTRACT_ADDRESS`. +Important current envs: +- `NODE_ADDRESS`: EVM RPC endpoint +- `CHAIN_ID`: EVM chain ID used in enriched EVM signing payload +- `BRIDGE_CONTRACT_ADDRESS`: EVM bridge contract watched by the listener +- `PROXY_CONTRACT_ADDRESS`: proxy contract used in RGB -> EVM signing checks +- `PARENT_ADAPTER_GRPC`: TCP address of the Parent Adapter +- `GRPC_LISTEN_ADDR`: listener gRPC address exposed to the orchestrator +- `EVM_LISTENER_ENABLED`: enables EVM indexing/finality tracking +- `ORCHESTRATOR_GRPC_ENABLED`: enables the gRPC listener node API +- `ORCHESTRATOR_RGB_EVM_TRANSFER_DB_CHECK`: requires RGB hub DB correlation for SWAP signing +- `RGB_MULTISIG_BRIDGE_ENABLED`: enables RGB hub cosigner mode -### Run +Important note: +- the committed examples are single-node examples +- the multi-node compose setup expects per-node files such as `.listener-node-1.env`, `.listener-node-2.env`, `.listener-node-3.env` +- those per-node files are environment-specific derivatives of the example listener config -From the module root: +## Running + +### Local manual run + +Without RGB-specific runtime: ```bash -go run ./cmd/listener +go run cmd/listener/main.go run -f ``` -Or build and run: +With RGB hub support: ```bash -go build -o listener ./cmd/listener -./listener +go run -tags rgb cmd/listener/main.go run -f ``` + +### Docker + +Primary Dockerfile: +- [deploy/listener.Dockerfile](deploy/listener.Dockerfile) + +Current multi-node stack reference: +- [deploy/docker-compose.yml](deploy/docker-compose.yml) + +That compose file models: +- 3 enclaves +- 3 parent adapters +- 3 listener nodes +- shared operational logging via Dozzle + +Practical note: +- [deploy/local/docker-compose.yml](deploy/local/docker-compose.yml) is an older single-listener setup and may need adjustments before use with the current runtime + +## Documentation stance + +When updating docs for this repository, assume: +- this repo is the **federated-signer-node / listener** only +- the orchestrator lives in `bridge-utexo` +- the enclave and parent adapter are downstream dependencies, not implemented here +- the important current paths are: + - orchestrator-facing gRPC signing + - EVM event validation/finality + - RGB multisig bridge synchronization diff --git a/configs/env_examples/.example.listener.env b/configs/env_examples/.example.listener.env index 26512ac..f363753 100644 --- a/configs/env_examples/.example.listener.env +++ b/configs/env_examples/.example.listener.env @@ -1,18 +1,43 @@ -GETH_WS_ENDPOINT=ws://127.0.0.1:8545 +NODE_ADDRESS=http://127.0.0.1:8545 +CHAIN_ID= BRIDGE_CONTRACT_ADDRESS= +PROXY_CONTRACT_ADDRESS= + PARENT_ADAPTER_GRPC=127.0.0.1:5000 # Background EnclaveService.PublicKey health probe interval (seconds); 0 disables. PARENT_ADAPTER_GRPC_HEALTH_PROBE_SEC=30 # Debug-log remaining deadline per Parent Adapter unary RPC (true/false). PARENT_ADAPTER_GRPC_LOG_DEADLINE=false -LISTEN_PORT=5001 + +GRPC_LISTEN_ADDR=:50051 LOG_LEVEL=info + +EVENT_POLL_INTERVAL_SEC=12 FINALITY_POLL_INTERVAL_SEC=12 EVENT_LISTENING_LIMIT=1000 -# SWAP (RGB→EVM): require a matching rgb_bridge_events row (hub consignment SHA-256 + amount). Set false if this listener has no RGB hub cosigner on the same DB. -# ORCHESTRATOR_RGB_EVM_TRANSFER_DB_CHECK=true +EVM_LISTENER_ENABLED=true +ORCHESTRATOR_GRPC_ENABLED=true +ORCHESTRATOR_RGB_EVM_TRANSFER_DB_CHECK=true # --- RGB multisig hub (-tags rgb) optional --- -# RGB_HUB_SIGN_VIA_ENCLAVE=true -# RGB_ENCLAVE_NETWORK_ID=0 +RGB_MULTISIG_BRIDGE_ENABLED=false +RGB_HUB_URL= +RGB_HUB_TOKEN= +RGB_MULTISIG_KEYS_JSON= +RGB_SIGNER_DATA_DIR=./data/rgb-signer +RGB_MULTISIG_POLL_INTERVAL_SEC=5 +RGB_INDEXER_URL= +RGB_WALLET_DATA_DIR=./data/rgb-wallet +RGB_WALLET_XPUB_VAN= +RGB_WALLET_XPUB_COL= +RGB_WALLET_MASTER_FINGERPRINT= +# RGB_WALLET_MNEMONIC= +# RGB_WALLET_VANILLA_KEYCHAIN=1 +RGB_WALLET_MAX_ALLOCATIONS_PER_UTXO=20 +RGB_WALLET_SKIP_CONSISTENCY_CHECK=false +RGB_BITCOIN_NETWORK=signet + +# Use the enclave instead of rgb-lib singlesig Wallet for hub PSBT signing. +RGB_HUB_SIGN_VIA_ENCLAVE=false +RGB_ENCLAVE_NETWORK_ID=0