Skip to content

Commit 15ca306

Browse files
anhthiivietddude
andauthored
Support child key derivation (#113)
* Merge pull request #122 from fystack/update-readme Update readme * WIP: Add CKD implementation and integrate derivation path in signing sessions use consulKV for store ckd seed * Update .gitignore to exclude node modules and modify CKD example * chore: fix lint error * feat: support eddsa ckd * CKD struct refactor: rename chain code field to masterChainCode and adjust constructor/accessor * Enable wallet-scoped derivation: add walletID to Derive and wire through HMAC-SHA512 * Add chain_code setup instructions and integrate chain_code into configuration * Enhance setup scripts: distribute event initiator public key to node configs and add build step in setup.sh * Update dependencies: replace bnb-chain/tss-lib with fystack/tss-lib and add btcec v2.3.2 * Fix reshare event handling: ensure successEvent.PubKey is non-empty before send result * Add chain_code config and integration CKD sign into e2e * Update e2e test matrix to include TestCKDSigning * Remove unused functions * Update derivatoin logic * Add hd wallet examples for both ecdsa and eddsa * Fix lint issue --------- Co-authored-by: vietddude <imrbdnv@gmail.com>
1 parent 717f2e8 commit 15ca306

26 files changed

Lines changed: 1878 additions & 23 deletions

.github/workflows/e2e-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
needs: build
5555
strategy:
5656
matrix:
57-
testcase: [TestKeyGeneration, TestSigning, TestResharing]
57+
testcase: [TestKeyGeneration, TestSigning, TestResharing, TestCKDSigning]
5858
steps:
5959
- uses: actions/checkout@v4
6060

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ event_initiator.key
66
event_initiator.key.age
77
coverage.out
88
coverage.html
9+
node*/
910
peers.json
1011

1112
# E2E test artifacts
@@ -23,3 +24,4 @@ node2
2324
config.yaml
2425
.vscode
2526
.vagrant
27+
.chain_code

INSTALLATION.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,29 @@ Detailed steps can be found in [SETUP.md](SETUP.md).
5656

5757
---
5858

59+
## chain_code setup (required)
60+
61+
Generate one 32-byte hex chain code and set it in all configs:
62+
63+
```bash
64+
cd /home/carmy/Documents/works/mpcium
65+
CC=$(openssl rand -hex 32) && echo "$CC" > .chain_code
66+
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" config.yaml
67+
for n in node0 node1 node2; do
68+
sed -i -E "s|^([[:space:]]*chain_code:).*|\1 \"$CC\"|" "$n/config.yaml"
69+
done
70+
```
71+
72+
Start nodes normally (no env export needed):
73+
74+
```bash
75+
cd node0 && mpcium start -n node0
76+
```
77+
78+
Repeat for `node1` and `node2`. The value must be exactly 64 hex chars (32 bytes).
79+
80+
---
81+
5982
## Production Deployment (High Security)
6083

6184
1. Use production-grade **NATS** and **Consul** clusters.

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ The application uses a YAML configuration file (`config.yaml`) with the followin
133133
- `event_initiator_pubkey`: Public key of the event initiator
134134
- `max_concurrent_keygen`: Maximum concurrent key generation operations
135135

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

138150
- **Local Development**: For quick setup and testing, see [INSTALLATION.md](./INSTALLATION.md)

cmd/mpcium/main.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ func runNode(ctx context.Context, c *cli.Command) error {
203203
peerNodeIDs := GetPeerIDs(peers)
204204
peerRegistry := mpc.NewRegistry(nodeID, peerNodeIDs, consulClient.KV(), directMessaging, pubsub, identityStore)
205205

206+
chainCodeHex := viper.GetString("chain_code")
207+
ckd, err := mpc.NewCKDFromHex(chainCodeHex)
208+
if err != nil {
209+
logger.Fatal("Failed to create ckd store", err)
210+
}
211+
206212
mpcNode := mpc.NewNode(
207213
nodeID,
208214
peerNodeIDs,
@@ -212,6 +218,7 @@ func runNode(ctx context.Context, c *cli.Command) error {
212218
keyinfoStore,
213219
peerRegistry,
214220
identityStore,
221+
ckd,
215222
)
216223
defer mpcNode.Close()
217224

@@ -443,6 +450,14 @@ func checkRequiredConfigValues(appConfig *config.AppConfig) {
443450
if viper.GetString("event_initiator_pubkey") == "" {
444451
logger.Fatal("Event initiator public key is required", nil)
445452
}
453+
454+
chainCode := strings.TrimSpace(viper.GetString("chain_code"))
455+
if chainCode == "" {
456+
logger.Fatal("chain_code is required in config.yaml", nil)
457+
}
458+
if len(chainCode) != 64 { // 32 bytes hex
459+
logger.Fatal("chain_code must be 32-byte hex (64 chars)", nil)
460+
}
446461
}
447462

448463
func NewConsulClient(addr string) *api.Client {

e2e/config.test.yaml.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ nats:
1111
max_concurrent_keygen: 1
1212
max_concurrent_signing: 10
1313
session_warm_up_delay_ms: 500
14+
chain_code: "{{.CKDChainCode}}"

e2e/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,5 @@ require (
106106
replace github.com/fystack/mpcium => ../
107107

108108
replace github.com/agl/ed25519 => github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43
109+
110+
replace github.com/bnb-chain/tss-lib/v2 => github.com/fystack/tss-lib/v2 v2.0.1

e2e/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
5353
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
5454
github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43 h1:Vkf7rtHx8uHx8gDfkQaCdVfc+gfrF9v6sR6xJy7RXNg=
5555
github.com/binance-chain/edwards25519 v0.0.0-20200305024217-f36fc4b53d43/go.mod h1:TnVqVdGEK8b6erOMkcyYGWzCQMw7HEMCOw3BgFYCFWs=
56-
github.com/bnb-chain/tss-lib/v2 v2.0.2 h1:dL2GJFCSYsYQ0bHkGll+hNM2JWsC1rxDmJJJQEmUy9g=
57-
github.com/bnb-chain/tss-lib/v2 v2.0.2/go.mod h1:s4LRfEqj89DhfNb+oraW0dURt5LtOHWXb9Gtkghn0L8=
5856
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
5957
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
6058
github.com/btcsuite/btcd v0.23.4/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
@@ -118,6 +116,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
118116
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
119117
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
120118
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
119+
github.com/fystack/tss-lib/v2 v2.0.1 h1:xnC2+DYShoVWco1geliW0km9IvGD7T2FqFOeXM3/7K0=
120+
github.com/fystack/tss-lib/v2 v2.0.1/go.mod h1:s4LRfEqj89DhfNb+oraW0dURt5LtOHWXb9Gtkghn0L8=
121121
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
122122
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
123123
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=

e2e/setup_test_identities.sh

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ echo "🔐 Generating random password for badger encryption..."
2828
BADGER_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 32)
2929
echo "✅ Generated password: $BADGER_PASSWORD"
3030

31+
# Generate chain_code (32-byte hex value, 64 hex characters)
32+
echo "🔐 Generating chain_code (32-byte hex)..."
33+
CHAIN_CODE=$(openssl rand -hex 32)
34+
echo "✅ Generated chain_code: $CHAIN_CODE"
35+
3136
# Generate config.test.yaml from template
3237
echo "📝 Generating config.test.yaml from template..."
3338
if [ ! -f "config.test.yaml.template" ]; then
@@ -43,6 +48,7 @@ ESCAPED_PASSWORD=$(printf '%s\n' "$BADGER_PASSWORD" | sed 's/[[\.*^$()+?{|]/\\&/
4348

4449
sed -e "s/{{\.BadgerPassword}}/$ESCAPED_PASSWORD/g" \
4550
-e "s/{{\.EventInitiatorPubkey}}/$TEMP_PUBKEY/g" \
51+
-e "s/{{\.CKDChainCode}}/$CHAIN_CODE/g" \
4652
config.test.yaml.template > config.test.yaml
4753

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

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

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

121141
echo "✅ Event initiator public key updated: $PUBKEY"
122142
echo "✅ Badger password updated: $BADGER_PASSWORD"
143+
echo "✅ Chain code updated: $CHAIN_CODE"
123144
else
124145
echo "❌ Failed to generate event initiator identity"
125146
exit 1

0 commit comments

Comments
 (0)