Mpcium MPC Engine

repository·master·Indexed 41 days ago

https://github.com/fystack/mpcium

A high-performance Multi-Party Computation (MPC) engine for generating and managing cryptographic wallets across distributed nodes. It supports secure threshold signing for ECDSA (secp256k1) and EdDSA (Ed25519) without reconstructing full private keys on a single machine, utilizing a t-of-n threshold scheme. The system integrates NATS for messaging, Consul for service discovery, and Badger KV for encrypted key share storage.

Tokens
28K
Snippets
72
Records
108
Agent score
49%

What's inside Mpcium

  1. Overview of Mpcium MPC Engine

    master

    Mpcium is a high-performance, open-source Multi-Party Computation (MPC) engine designed for securely generating and managing cryptographic wallets across distributed nodes. It ensures that the full private key is never exposed by using a threshold signature scheme.

    Supported cryptographic schemes include:

    • ECDSA (secp256k1): For Bitcoin, Ethereum, BNB, Polygon, and EVM-compatible L2 chains.
    • EdDSA (Ed25519): For Solana, Polkadot, Cardano, and other modern blockchains.
  2. Mpcium Production Directory Structure

    master

    Understanding the file layout is critical for backups and manual configuration.

    • /opt/mpcium/: Application home (owned by mpcium:mpcium).
      • certs/: TLS certificates (client-cert.pem, client-key.pem, rootCA.pem).
      • db/: BadgerDB storage.
      • backups/: Encrypted backups.
      • identity/: Node identity files (e.g., nodeX_identity.json, nodeX_private.key or nodeX_private.key.age).
      • peers.json: Peer registry.
      • .env: Environment variables.
    • /etc/mpcium/: Configuration (owned by root:mpcium).
      • config.yaml: Main configuration file.
  3. Mpcium Node Architecture and Message Flow

    master

    Each Mpcium node operates with the following components and lifecycle:

    Node Components

    • Key Share Storage: Holds a key share in local AES-256 encrypted storage using Badger KV.
    • Cryptographic Engine: Uses tss-lib for threshold key generation and signing.
    • Messaging: Communicates via NATS for real-time coordination.
    • Service Discovery: Registers with Consul for health checks and cluster integrity.
    • Authentication: Verifies all incoming messages using Ed25519-based mutual authentication.

    Signing Request Flow

    1. A signing request is broadcast to the cluster via NATS as an authenticated event.
    2. Each node verifies the Ed25519 signature of the sender and ensures the message is unique (non-replayable).
    3. If the node is healthy and part of the quorum (t), it computes a partial signature using its local share.
    4. The partial signature is published back via NATS.
    5. Once t partial signatures are collected, they are aggregated into a full signature.
  4. How Mpcium nodes work in an ECS Fargate architecture

    master

    Mpcium uses a t-of-n threshold signature scheme where n is the total number of MPC nodes (minimum 3 recommended) and t is the minimum nodes required for operations ($t \ge \text{floor}(n/2) + 1$). Private keys are never fully reconstructed; only $t$ nodes are needed for signing operations.

    In an AWS ECS Fargate deployment, each task runs a single node with a unique identity (Ed25519 keypair), encrypted BadgerDB storage on EFS, and connections to NATS (message broker) and Consul (service discovery).

  5. Understand the Secrets and Configuration Flow in ECS Fargate

    master

    In the ECS Fargate deployment, the mpcium main container is distroless (no shell), so all secrets and configuration files must be pre-written to specific paths by specialized init containers.

    Data Flow Summary

    • Secrets Manager: Provides db-password and identity-password, which are written to /secrets/ by the init-secrets container.
    • S3: Provides config.yaml, peers.json, and identity files (*.age, *.json), which are written to /config/ and /identity/ by the init-config container.
    • EFS: The init-data container copies identity files and peers.json from the ephemeral volumes to the persistent EFS volume at /app/data/.

    Runtime Environment Variables

    To allow the same config.yaml to be used across different environments, use environment variables to override connection strings. The application reads these at runtime:

    • NATS_URL: Overrides the NATS field in config.yaml.
    • CONSUL_ADDRESS: Overrides the Consul field in config.yaml.

    Leave these fields empty in your config.yaml to ensure the environment variables take precedence.

    # Example of how NATS and Consul are injected via env vars
    # rather than hardcoded in config.yaml
    NATS_URL=nats://nlb.internal:4222
    CONSUL_ADDRESS=nlb.internal:8500
  6. How threshold signing and nodes work in Mpcium

    master

    Mpcium utilizes a t-of-n threshold scheme to manage security and fault tolerance.

    • n represents the total number of MPC nodes (key shares).
    • t represents the minimum number of nodes required to successfully sign a transaction.

    To prevent a single point of compromise, Mpcium enforces the security property: t ≥ ⌊n / 2⌋ + 1

    Example (2-of-3 Threshold): In a cluster where n=3 and t=2:

    • node0 + node1 can sign successfully.
    • node1 + node2 can sign successfully.
    • node0 alone cannot sign.

    This configuration ensures that even if some nodes go offline, the system remains functional, and even if a node is compromised, the full key remains secure.

  7. Configure ClientID for result routing

    master

    When running multiple client instances against the same MPC cluster, you must set a unique ClientID in the client.Options.

    Without a unique ClientID, concurrent requests from different clients may cause a race condition where one client receives the result intended for another.

    • Scope: ClientID scopes the NATS consumer and result subject.
    • Allowed characters: Alphanumeric, hyphens (-), and underscores (_).
    • Default: If only one client instance is running, ClientID can be an empty string.
  8. Understand the Mpcium Threshold Signature Model

    master

    Mpcium operates on a $t$-of-$n$ threshold signature scheme. This means that in a cluster of $n$ total MPC nodes, a minimum of $t$ nodes must collaborate to perform cryptographic operations (like signing or key generation).

    Key properties:

    • $n$ (Total Nodes): The total number of nodes in the cluster. A minimum of 3 nodes is recommended for production.
    • $t$ (Threshold): The minimum number of nodes required for operations. It is calculated as $t \ge \lfloor n/2 \rfloor + 1$.
    • Security: Private keys are never fully reconstructed on any single node; only $t$ nodes are needed to complete a signing operation.

    Each node requires a unique Ed25519 identity, encrypted BadgerDB storage, and connections to NATS (for messaging) and Consul (for service discovery).

  9. Configure the required chain_code

    master

    The chain_code is a mandatory 32-byte cryptographic parameter (represented as a 64-character hexadecimal string) used for Hierarchical Deterministic (HD) wallet functionality.

    Critical Requirement: All nodes in your MPC cluster MUST use the identical chain_code value. If the values differ or are missing, nodes will fail to start.

    To generate a new code and apply it to the main configuration and all node directories, use the following pattern:

    # Generate a random 32-byte chain code and save it
    CC=$(openssl rand -hex 32) && echo "$CC" > .chain_code
    
    # Apply to main config
    sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" config.yaml
    
    # Apply to all node configs (example for 3 nodes)
    for n in node0 node1 node2; do
      sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" "$n/config.yaml"
    done
  10. Understand the E2E test flow and configuration

    master

    The E2E tests follow a specific lifecycle to ensure the MPC system is resilient and consistent:

    Test Lifecycle

    1. Infrastructure Setup: Starts NATS (port 4223) and Consul (port 8501) via Docker.
    2. Node Setup: Creates 3 nodes (test_node0, test_node1, test_node2) with unique identities, separate database paths (./test_db/), and peer registration in Consul.
    3. Start MPC Nodes: Launches 3 mpcium processes in parallel.
    4. Key Generation: Triggers simultaneous key generation for multiple random wallet IDs.
    5. Consistency Verification: Stops nodes and verifies that both ECDSA and EdDSA keys exist and are identical across all node databases.
    6. Cleanup: Removes all processes, containers, and temporary files.

    Test Configuration Constants

    • NATS Port: 4223
    • Consul Port: 8501
    • Database Path: ./test_db/
    • Badger Password: test_password_123
    • Node Names: test_node0, test_node1, test_node2
  11. Understand the ECS Task Structure for Mpcium

    master

    Each ECS task is composed of four containers to manage the lifecycle of secrets, configuration, and data:

    1. init-secrets: Pulls passwords from AWS Secrets Manager and writes them to the /secrets/ volume.
    2. init-config: Downloads config.yaml, peers.json, and identity files from S3.
    3. init-data: Copies identity files and peers.json from ephemeral volumes to EFS (/app/data/) to ensure persistence across restarts.
    4. mpcium: The main application container (distroless, no shell).
  12. Pre-Deployment Setup: Generate Chain Code and BadgerDB Passwords

    master

    Complete the security setup by generating a random chain code and unique BadgerDB passwords for each node.

    Generate Chain Code

    CHAIN_CODE=$(openssl rand -hex 32)
    echo "Chain code: $CHAIN_CODE"

    Generate BadgerDB Passwords

    Generate a 16-character alphanumeric password for each node's database:

    for node in node0 node1 node2; do
      PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!@#$' | head -c 16; echo)
      echo "$node BadgerDB password: $PASSWORD"
    done
    # Generate Chain Code
    CHAIN_CODE=$(openssl rand -hex 32)
    
    # Generate BadgerDB Passwords
    for node in node0 node1 node2; do
      PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9!@#$' | head -c 16; echo)
      echo "$node BadgerDB password: $PASSWORD"
    done