Skip to content

Commit 55c41bc

Browse files
committed
Add examples for generate with authorizers
1 parent a6a2d61 commit 55c41bc

4 files changed

Lines changed: 415 additions & 9 deletions

File tree

e2e/go.mod

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
module github.com/fystack/mpcium/e2e
22

3-
go 1.23.0
3+
go 1.23.8
4+
5+
toolchain go1.24.9
46

57
require (
68
github.com/dgraph-io/badger/v4 v4.7.0
79
github.com/fystack/mpcium v0.0.0-00010101000000-000000000000
810
github.com/google/uuid v1.6.0
9-
github.com/hashicorp/consul/api v1.26.1
11+
github.com/hashicorp/consul/api v1.32.1
1012
github.com/nats-io/nats.go v1.31.0
1113
github.com/stretchr/testify v1.10.0
1214
gopkg.in/yaml.v2 v2.4.0
@@ -91,7 +93,7 @@ require (
9193
go.uber.org/multierr v1.9.0 // indirect
9294
go.uber.org/zap v1.21.0 // indirect
9395
golang.org/x/crypto v0.37.0 // indirect
94-
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
96+
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
9597
golang.org/x/net v0.39.0 // indirect
9698
golang.org/x/sys v0.33.0 // indirect
9799
golang.org/x/term v0.31.0 // indirect

e2e/go.sum

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,10 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
158158
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
159159
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
160160
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
161-
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
162-
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
163-
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
164-
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
161+
github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE=
162+
github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4=
163+
github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg=
164+
github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s=
165165
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
166166
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
167167
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -402,8 +402,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
402402
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
403403
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
404404
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
405-
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
406-
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
405+
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
406+
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
407407
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
408408
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
409409
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
package main
2+
3+
import (
4+
"encoding/hex"
5+
"encoding/json"
6+
"flag"
7+
"fmt"
8+
"os"
9+
"os/signal"
10+
"slices"
11+
"sync"
12+
"sync/atomic"
13+
"syscall"
14+
"time"
15+
16+
"github.com/fystack/mpcium/pkg/client"
17+
"github.com/fystack/mpcium/pkg/config"
18+
"github.com/fystack/mpcium/pkg/event"
19+
"github.com/fystack/mpcium/pkg/logger"
20+
"github.com/fystack/mpcium/pkg/types"
21+
"github.com/google/uuid"
22+
"github.com/nats-io/nats.go"
23+
"github.com/spf13/viper"
24+
)
25+
26+
// Required authorizer names (hardcoded as requested)
27+
var requiredAuthorizers = []string{"authorizer1", "authorizer2"}
28+
29+
func main() {
30+
const environment = "development"
31+
numWallets := flag.Int("n", 1, "Number of wallets to generate")
32+
33+
flag.Parse()
34+
35+
config.InitViperConfig("")
36+
logger.Init(environment, false)
37+
38+
algorithm := viper.GetString("event_initiator_algorithm")
39+
if algorithm == "" {
40+
algorithm = string(types.EventInitiatorKeyTypeEd25519)
41+
}
42+
43+
if !slices.Contains(
44+
[]string{
45+
string(types.EventInitiatorKeyTypeEd25519),
46+
string(types.EventInitiatorKeyTypeP256),
47+
},
48+
algorithm,
49+
) {
50+
logger.Fatal(
51+
fmt.Sprintf(
52+
"invalid algorithm: %s. Must be %s or %s",
53+
algorithm,
54+
types.EventInitiatorKeyTypeEd25519,
55+
types.EventInitiatorKeyTypeP256,
56+
),
57+
nil,
58+
)
59+
}
60+
61+
natsURL := viper.GetString("nats.url")
62+
natsConn, err := nats.Connect(natsURL)
63+
if err != nil {
64+
logger.Fatal("Failed to connect to NATS", err)
65+
}
66+
defer natsConn.Drain()
67+
defer natsConn.Close()
68+
69+
localSigner, err := client.NewLocalSigner(types.EventInitiatorKeyType(algorithm), client.LocalSignerOptions{
70+
KeyPath: "./event_initiator.key",
71+
})
72+
if err != nil {
73+
logger.Fatal("Failed to create local signer", err)
74+
}
75+
76+
// Load authorizer signers
77+
authorizerSigners := make(map[string]client.Signer)
78+
for _, authorizerID := range requiredAuthorizers {
79+
keyPath := fmt.Sprintf("./%s.authorizer.key", authorizerID)
80+
signer, err := client.NewLocalSigner(types.EventInitiatorKeyTypeEd25519, client.LocalSignerOptions{
81+
KeyPath: keyPath,
82+
})
83+
if err != nil {
84+
logger.Fatal(fmt.Sprintf("Failed to create authorizer signer for %s", authorizerID), err)
85+
}
86+
authorizerSigners[authorizerID] = signer
87+
}
88+
89+
mpcClient := client.NewMPCClient(client.Options{
90+
NatsConn: natsConn,
91+
Signer: localSigner,
92+
})
93+
94+
var walletStartTimes sync.Map
95+
var walletIDs []string
96+
var walletIDsMu sync.Mutex
97+
var wg sync.WaitGroup
98+
var completedCount int32
99+
100+
startAll := time.Now()
101+
102+
// STEP 1: Pre-generate wallet IDs and store start times
103+
for i := 0; i < *numWallets; i++ {
104+
walletID := uuid.New().String()
105+
walletStartTimes.Store(walletID, time.Now())
106+
107+
walletIDsMu.Lock()
108+
walletIDs = append(walletIDs, walletID)
109+
walletIDsMu.Unlock()
110+
}
111+
112+
// STEP 2: Register the result handler AFTER all walletIDs are stored
113+
err = mpcClient.OnWalletCreationResult(func(event event.KeygenResultEvent) {
114+
logger.Info("Received wallet creation result", "event", event)
115+
now := time.Now()
116+
startTimeAny, ok := walletStartTimes.Load(event.WalletID)
117+
if ok {
118+
startTime := startTimeAny.(time.Time)
119+
duration := now.Sub(startTime).Seconds()
120+
accumulated := now.Sub(startAll).Seconds()
121+
countSoFar := atomic.AddInt32(&completedCount, 1)
122+
123+
logger.Info("Wallet created",
124+
"walletID", event.WalletID,
125+
"duration_seconds", fmt.Sprintf("%.3f", duration),
126+
"accumulated_time_seconds", fmt.Sprintf("%.3f", accumulated),
127+
"count_so_far", countSoFar,
128+
)
129+
130+
walletStartTimes.Delete(event.WalletID)
131+
} else {
132+
logger.Warn("Received wallet result but no start time found", "walletID", event.WalletID)
133+
}
134+
wg.Done()
135+
})
136+
if err != nil {
137+
logger.Fatal("Failed to subscribe to wallet-creation results", err)
138+
}
139+
140+
// STEP 3: Create wallets with authorizer signatures
141+
for _, walletID := range walletIDs {
142+
wg.Add(1) // Add to WaitGroup BEFORE attempting to create wallet
143+
144+
// Create a temporary message to get the initiator signature
145+
tempMsg := &types.GenerateKeyMessage{
146+
WalletID: walletID,
147+
}
148+
149+
// Sign with initiator
150+
raw, err := tempMsg.Raw()
151+
if err != nil {
152+
logger.Error("Failed to get raw message", err)
153+
walletStartTimes.Delete(walletID)
154+
wg.Done()
155+
continue
156+
}
157+
158+
signature, err := localSigner.Sign(raw)
159+
if err != nil {
160+
logger.Error("Failed to sign message", err)
161+
walletStartTimes.Delete(walletID)
162+
wg.Done()
163+
continue
164+
}
165+
tempMsg.Signature = signature
166+
167+
// Collect authorizer signatures
168+
authorizerRaw, err := types.ComposeAuthorizerRaw(tempMsg)
169+
if err != nil {
170+
logger.Error("Failed to compose authorizer raw data", err)
171+
walletStartTimes.Delete(walletID)
172+
wg.Done()
173+
continue
174+
}
175+
176+
var authorizerSignatures []types.AuthorizerSignature
177+
for _, authorizerID := range requiredAuthorizers {
178+
signer := authorizerSigners[authorizerID]
179+
authSig, err := signer.Sign(authorizerRaw)
180+
if err != nil {
181+
logger.Error(fmt.Sprintf("Failed to sign with authorizer %s", authorizerID), err)
182+
continue
183+
}
184+
185+
authorizerSignatures = append(authorizerSignatures, types.AuthorizerSignature{
186+
AuthorizerID: authorizerID,
187+
Signature: authSig,
188+
})
189+
190+
logger.Info("Added authorizer signature",
191+
"authorizer", authorizerID,
192+
"signature", hex.EncodeToString(authSig),
193+
)
194+
}
195+
196+
// Use the new CreateWalletWithAuthorizers method
197+
if err := mpcClient.CreateWalletWithAuthorizers(walletID, authorizerSignatures); err != nil {
198+
logger.Error("CreateWallet failed", err)
199+
walletStartTimes.Delete(walletID)
200+
wg.Done()
201+
continue
202+
}
203+
204+
logger.Info("CreateWallet sent with authorizers, awaiting result...",
205+
"walletID", walletID,
206+
"authorizers", requiredAuthorizers,
207+
)
208+
}
209+
210+
// Wait until all wallet creations complete
211+
go func() {
212+
wg.Wait()
213+
totalDuration := time.Since(startAll).Seconds()
214+
logger.Info(
215+
"All wallets generated",
216+
"count",
217+
completedCount,
218+
"total_duration_seconds",
219+
fmt.Sprintf("%.3f", totalDuration),
220+
)
221+
222+
// Save wallet IDs to wallets.json
223+
walletIDsMu.Lock()
224+
data, err := json.MarshalIndent(walletIDs, "", " ")
225+
walletIDsMu.Unlock()
226+
if err != nil {
227+
logger.Error("Failed to marshal wallet IDs", err)
228+
} else {
229+
err = os.WriteFile("wallets.json", data, 0600)
230+
if err != nil {
231+
logger.Error("Failed to write wallets.json", err)
232+
} else {
233+
logger.Info("wallets.json written", "count", len(walletIDs))
234+
}
235+
}
236+
os.Exit(0)
237+
}()
238+
239+
// Block on SIGINT/SIGTERM (Ctrl+C etc.)
240+
stop := make(chan os.Signal, 1)
241+
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
242+
<-stop
243+
244+
fmt.Println("Shutting down.")
245+
}

0 commit comments

Comments
 (0)