Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
needs: build
strategy:
matrix:
testcase: [TestKeyGeneration, TestSigning, TestResharing]
testcase: [TestKeyGeneration, TestSigning, TestResharing, TestCKDSigning]
steps:
- uses: actions/checkout@v4

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ event_initiator.key
event_initiator.key.age
coverage.out
coverage.html
node*/
peers.json

# E2E test artifacts
Expand All @@ -23,3 +24,4 @@ node2
config.yaml
.vscode
.vagrant
.chain_code
23 changes: 23 additions & 0 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,29 @@ Detailed steps can be found in [SETUP.md](SETUP.md).

---

## chain_code setup (required)

Generate one 32-byte hex chain code and set it in all configs:

```bash
cd /home/carmy/Documents/works/mpcium
CC=$(openssl rand -hex 32) && echo "$CC" > .chain_code
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" config.yaml
for n in node0 node1 node2; do
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" "$n/config.yaml"
done
```

Start nodes normally (no env export needed):

```bash
cd node0 && mpcium start -n node0
```

Repeat for `node1` and `node2`. The value must be exactly 64 hex chars (32 bytes).

---

## Production Deployment (High Security)

1. Use production-grade **NATS** and **Consul** clusters.
Expand Down
66 changes: 42 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
> _"Setting up MPC wallets has always been painful, complex, and confusing. With MPCIUM, you can launch a secure MPC node cluster and generate wallets in minutes."_

<p><img title="fystack logo" src="https://avatars.githubusercontent.com/u/149689344?s=400&u=13bed818667eefccd78ca4b4207d088eeb4f6110&v=4" width="320" height="320"></p>
<p><a href="https://t.me/+IsRhPyWuOFxmNmM9">Join our Telegram community to discuss MPCIUM and Web3 cyber security!</a></p>
<p><a href="https://t.me/+9AtC0z8sS79iZjFl">Join our Telegram community to discuss MPCIUM and Web3 cyber security!</a></p>

[![Go Version](https://img.shields.io/badge/Go-v1.23+-00ADD8?logo=go&style=for-the-badge)](https://go.dev/)
[![License](https://img.shields.io/github/license/fystack/mpcium?style=for-the-badge)](./LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/fystack/mpcium?style=for-the-badge)](https://goreportcard.com/report/github.com/fystack/mpcium)
[![Version](https://img.shields.io/github/v/release/fystack/mpcium?label=version&logo=semantic-release&style=for-the-badge)](https://github.com/fystack/mpcium/releases)
[![Telegram](https://img.shields.io/badge/Telegram-Community%20-26A5E4?logo=telegram&style=for-the-badge)](https://t.me/+IsRhPyWuOFxmNmM9)
[![Telegram](https://img.shields.io/badge/Telegram-Community%20-26A5E4?logo=telegram&style=for-the-badge)](https://t.me/+9AtC0z8sS79iZjFl)
[![Made by Fystack](https://img.shields.io/badge/Made%20by-Fystack-7D3DF4?style=for-the-badge)](https://fystack.io)

</div>
Expand Down Expand Up @@ -133,6 +133,18 @@ The application uses a YAML configuration file (`config.yaml`) with the followin
- `event_initiator_pubkey`: Public key of the event initiator
- `max_concurrent_keygen`: Maximum concurrent key generation operations

#### chain_code (required)
- Mpcium derives child keys using a master chain code.
- Provide a single 32-byte hex value in `config.yaml` under `chain_code`, and use the same value for all nodes.
- Example to generate once and set:
```bash
CC=$(openssl rand -hex 32)
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" config.yaml
for n in node0 node1 node2; do
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" "$n/config.yaml"
done
```

## Installation

- **Local Development**: For quick setup and testing, see [INSTALLATION.md](./INSTALLATION.md)
Expand Down Expand Up @@ -287,7 +299,7 @@ configs := []client.KMSSignerOptions{
},
// Full ARN
{
Region: "ap-southeast-1",
Region: "ap-southeast-1",
KeyID: "arn:aws:kms:ap-southeast-1:123456789012:key/12345678-1234-1234-1234-123456789012",
},
// Key alias
Expand All @@ -307,13 +319,14 @@ For local development and testing with AWS KMS functionality, you can use LocalS
### Setup LocalStack

1. **Install and start LocalStack:**

```bash
# Using Docker
docker run -d \
-p 4566:4566 \
-p 4510-4559:4510-4559 \
localstack/localstack

# Or using LocalStack CLI
pip install localstack
localstack start
Expand All @@ -329,6 +342,7 @@ For local development and testing with AWS KMS functionality, you can use LocalS
### Create P256 Key in LocalStack

1. **Create a P256 keypair in AWS KMS:**

```bash
aws kms create-key \
--endpoint-url=http://localhost:4566 \
Expand All @@ -338,33 +352,33 @@ For local development and testing with AWS KMS functionality, you can use LocalS
```

Expected response:

```json
{
"KeyMetadata": {
"AWSAccountId": "000000000000",
"KeyId": "330a9df7-4fd9-4e86-bfc5-f360b4c4be39",
"Arn": "arn:aws:kms:us-east-1:000000000000:key/330a9df7-4fd9-4e86-bfc5-f360b4c4be39",
"CreationDate": "2025-08-28T16:42:18.487655+07:00",
"Enabled": true,
"Description": "Test P-256 keypair for Mpcium",
"KeyUsage": "SIGN_VERIFY",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "ECC_NIST_P256",
"KeySpec": "ECC_NIST_P256",
"SigningAlgorithms": [
"ECDSA_SHA_256"
],
"MultiRegion": false
}
"KeyMetadata": {
"AWSAccountId": "000000000000",
"KeyId": "330a9df7-4fd9-4e86-bfc5-f360b4c4be39",
"Arn": "arn:aws:kms:us-east-1:000000000000:key/330a9df7-4fd9-4e86-bfc5-f360b4c4be39",
"CreationDate": "2025-08-28T16:42:18.487655+07:00",
"Enabled": true,
"Description": "Test P-256 keypair for Mpcium",
"KeyUsage": "SIGN_VERIFY",
"KeyState": "Enabled",
"Origin": "AWS_KMS",
"KeyManager": "CUSTOMER",
"CustomerMasterKeySpec": "ECC_NIST_P256",
"KeySpec": "ECC_NIST_P256",
"SigningAlgorithms": ["ECDSA_SHA_256"],
"MultiRegion": false
}
}
```

2. **Get the public key (save the KeyId from step 1):**

```bash
export KMS_KEY_ID="330a9df7-4fd9-4e86-bfc5-f360b4c4be39" # Replace with your KeyId

aws kms get-public-key \
--endpoint-url=http://localhost:4566 \
--key-id $KMS_KEY_ID \
Expand All @@ -373,6 +387,7 @@ For local development and testing with AWS KMS functionality, you can use LocalS
```

Expected response (hex-encoded public key):

```
3059301306072a8648ce3d020106082a8648ce3d030107034200042b7539fc51123c3ba53c71e244be71d2d3138cbed4909fa259b924b56c92148cadd410cf98b789269d7f672c3ba978e99fc1f01c87daee97292d3666357738fd
```
Expand All @@ -382,7 +397,7 @@ For local development and testing with AWS KMS functionality, you can use LocalS
Update your `config.yaml` file with the KMS public key and algorithm:

```yaml
# MPC Configuration
# MPC Configuration
mpc_threshold: 2
event_initiator_pubkey: "3059301306072a8648ce3d020106082a8648ce3d030107034200042b7539fc51123c3ba53c71e244be71d2d3138cbed4909fa259b924b56c92148cadd410cf98b789269d7f672c3ba978e99fc1f01c87daee97292d3666357738fd"
event_initiator_algorithm: "p256"
Expand All @@ -404,6 +419,7 @@ go run examples/generate/kms/main.go -n 1
```

The example will:

1. Connect to LocalStack KMS endpoint
2. Load the P256 public key from KMS
3. Use KMS for signing wallet creation events
Expand Down Expand Up @@ -469,6 +485,7 @@ make test
Test MPC performance with the integrated benchmark tool:

### Keygen Benchmark

```bash
# Test wallet creation
mpcium-cli benchmark keygen 10
Expand All @@ -478,6 +495,7 @@ mpcium-cli benchmark --config config.yaml --output results.txt keygen 50
```

### Signing Benchmark

```bash
# Test ECDSA signing
mpcium-cli benchmark sign-ecdsa 100 wallet-id
Expand Down
15 changes: 15 additions & 0 deletions cmd/mpcium/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ func runNode(ctx context.Context, c *cli.Command) error {
peerNodeIDs := GetPeerIDs(peers)
peerRegistry := mpc.NewRegistry(nodeID, peerNodeIDs, consulClient.KV(), directMessaging, pubsub, identityStore)

chainCodeHex := viper.GetString("chain_code")
ckd, err := mpc.NewCKDFromHex(chainCodeHex)
if err != nil {
logger.Fatal("Failed to create ckd store", err)
}

mpcNode := mpc.NewNode(
nodeID,
peerNodeIDs,
Expand All @@ -212,6 +218,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
keyinfoStore,
peerRegistry,
identityStore,
ckd,
)
defer mpcNode.Close()

Expand Down Expand Up @@ -443,6 +450,14 @@ func checkRequiredConfigValues(appConfig *config.AppConfig) {
if viper.GetString("event_initiator_pubkey") == "" {
logger.Fatal("Event initiator public key is required", nil)
}

chainCode := strings.TrimSpace(viper.GetString("chain_code"))
if chainCode == "" {
logger.Fatal("chain_code is required in config.yaml", nil)
}
if len(chainCode) != 64 { // 32 bytes hex
logger.Fatal("chain_code must be 32-byte hex (64 chars)", nil)
}
}

func NewConsulClient(addr string) *api.Client {
Expand Down
1 change: 1 addition & 0 deletions e2e/config.test.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ nats:
max_concurrent_keygen: 1
max_concurrent_signing: 10
session_warm_up_delay_ms: 500
chain_code: "{{.CKDChainCode}}"
2 changes: 2 additions & 0 deletions e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,5 @@ require (
replace github.com/fystack/mpcium => ../

replace github.com/agl/ed25519 => github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43

replace github.com/bnb-chain/tss-lib/v2 => github.com/fystack/tss-lib/v2 v2.0.1
4 changes: 2 additions & 2 deletions e2e/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43 h1:Vkf7rtHx8uHx8gDfkQaCdVfc+gfrF9v6sR6xJy7RXNg=
github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43/go.mod h1:TnVqVdGEK8b6erOMkcyYGWzCQMw7HEMCOw3BgFYCFWs=
github.com/bnb-chain/tss-lib/v2 v2.0.2 h1:dL2GJFCSYsYQ0bHkGll+hNM2JWsC1rxDmJJJQEmUy9g=
github.com/bnb-chain/tss-lib/v2 v2.0.2/go.mod h1:s4LRfEqj89DhfNb+oraW0dURt5LtOHWXb9Gtkghn0L8=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
Expand Down Expand Up @@ -118,6 +116,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fystack/tss-lib/v2 v2.0.1 h1:xnC2+DYShoVWco1geliW0km9IvGD7T2FqFOeXM3/7K0=
github.com/fystack/tss-lib/v2 v2.0.1/go.mod h1:s4LRfEqj89DhfNb+oraW0dURt5LtOHWXb9Gtkghn0L8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
Expand Down
23 changes: 22 additions & 1 deletion e2e/setup_test_identities.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ echo "🔐 Generating random password for badger encryption..."
BADGER_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 32)
echo "✅ Generated password: $BADGER_PASSWORD"

# Generate chain_code (32-byte hex value, 64 hex characters)
echo "🔐 Generating chain_code (32-byte hex)..."
CHAIN_CODE=$(openssl rand -hex 32)
echo "✅ Generated chain_code: $CHAIN_CODE"

# Generate config.test.yaml from template
echo "📝 Generating config.test.yaml from template..."
if [ ! -f "config.test.yaml.template" ]; then
Expand All @@ -43,6 +48,7 @@ ESCAPED_PASSWORD=$(printf '%s\n' "$BADGER_PASSWORD" | sed 's/[[\.*^$()+?{|]/\\&/

sed -e "s/{{\.BadgerPassword}}/$ESCAPED_PASSWORD/g" \
-e "s/{{\.EventInitiatorPubkey}}/$TEMP_PUBKEY/g" \
-e "s/{{\.CKDChainCode}}/$CHAIN_CODE/g" \
config.test.yaml.template > config.test.yaml

echo "✅ Generated config.test.yaml from template"
Expand Down Expand Up @@ -106,20 +112,35 @@ if [ -f "test_event_initiator.identity.json" ]; then
PUBKEY=$(cat test_event_initiator.identity.json | jq -r '.public_key')
echo "📝 Updating config files with event initiator public key and password..."

# Update all test node config files with the actual public key and password
# Update all test node config files with the actual public key, password, and chain_code
for i in $(seq 0 $((NUM_NODES-1))); do
# Update public key using sed with | as delimiter (safer than /)
sed_inplace "s|event_initiator_pubkey:.*|event_initiator_pubkey: $PUBKEY|g" "$BASE_DIR/test_node$i/config.yaml"
# Update password using sed with | as delimiter and escaped password
sed_inplace "s|badger_password:.*|badger_password: $ESCAPED_PASSWORD|g" "$BASE_DIR/test_node$i/config.yaml"
# Update chain_code
if grep -q '^\s*chain_code:' "$BASE_DIR/test_node$i/config.yaml"; then
sed_inplace "s|chain_code:.*|chain_code: \"$CHAIN_CODE\"|g" "$BASE_DIR/test_node$i/config.yaml"
else
printf '\nchain_code: "%s"\n' "$CHAIN_CODE" >> "$BASE_DIR/test_node$i/config.yaml"
fi
done

# Also update the main config.test.yaml
sed_inplace "s|event_initiator_pubkey:.*|event_initiator_pubkey: $PUBKEY|g" "$BASE_DIR/config.test.yaml"
sed_inplace "s|badger_password:.*|badger_password: $ESCAPED_PASSWORD|g" "$BASE_DIR/config.test.yaml"
# Update chain_code in config.test.yaml if it was replaced with placeholder
if grep -q '{{\.CKDChainCode}}' "$BASE_DIR/config.test.yaml" 2>/dev/null; then
sed_inplace "s|{{\.CKDChainCode}}|$CHAIN_CODE|g" "$BASE_DIR/config.test.yaml"
elif grep -q '^\s*chain_code:' "$BASE_DIR/config.test.yaml"; then
sed_inplace "s|chain_code:.*|chain_code: \"$CHAIN_CODE\"|g" "$BASE_DIR/config.test.yaml"
else
printf '\nchain_code: "%s"\n' "$CHAIN_CODE" >> "$BASE_DIR/config.test.yaml"
fi

echo "✅ Event initiator public key updated: $PUBKEY"
echo "✅ Badger password updated: $BADGER_PASSWORD"
echo "✅ Chain code updated: $CHAIN_CODE"
else
echo "❌ Failed to generate event initiator identity"
exit 1
Expand Down
Loading
Loading