Skip to content
16 changes: 15 additions & 1 deletion e2e/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,20 @@ func (s *E2ETestSuite) LoadConfig() error {
return err
}

return yaml.Unmarshal(data, &s.config)
if err := yaml.Unmarshal(data, &s.config); err != nil {
return err
}

return validateBadgerEncryptionKey(s.config.BadgerPassword)
}

func validateBadgerEncryptionKey(key string) error {
switch len([]byte(key)) {
case 16, 24, 32:
return nil
default:
return fmt.Errorf("badger_password must be 16, 24, or 32 bytes for Badger encryption, got %d bytes", len([]byte(key)))
}
}

func (s *E2ETestSuite) RunMakeClean() error {
Expand Down Expand Up @@ -284,6 +297,7 @@ func (s *E2ETestSuite) SeedPreParams(t *testing.T) {
DbPath string `yaml:"db_path"`
}
require.NoError(t, yaml.Unmarshal(configData, &nodeConfig), "Failed to parse node config")
require.NoError(t, validateBadgerEncryptionKey(nodeConfig.BadgerPassword), "Invalid badger_password in %s", configPath)

dbBasePath := nodeConfig.DbPath
if dbBasePath == "" {
Expand Down
24 changes: 20 additions & 4 deletions e2e/keygen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

func TestKeyGeneration(t *testing.T) {
suite := NewE2ETestSuite(".")
logger.Init("dev", true)

// Comprehensive cleanup before starting tests
t.Log("Performing pre-test cleanup...")
Expand Down Expand Up @@ -78,10 +79,23 @@
if suite.mpcClient == nil {
t.Fatal("MPC client is not initialized. Make sure Setup subtest runs first.")
}

walletID := uuid.New().String()

// Create a new wallet
createWalletAndVerify(t, suite, walletID)

// Create another new wallet with the same wallet ID
createWalletAndVerify(t, suite, walletID)

t.Log("Key generation test completed")
}

func createWalletAndVerify(t *testing.T, suite *E2ETestSuite, walletID string) {

Check failure on line 94 in e2e/keygen_test.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 18 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=fystack_mpcium&issues=AZ4LJdKUbgnSuG7_tuuu&open=AZ4LJdKUbgnSuG7_tuuu&pullRequest=160
// Generate 1 wallet ID for testing
walletIDs := make([]string, 0, 10)
for i := 0; i < 1; i++ {
walletIDs = append(walletIDs, uuid.New().String())
walletIDs = append(walletIDs, walletID)
suite.walletIDs = append(suite.walletIDs, walletIDs[i])
}

Expand All @@ -91,7 +105,11 @@
err := suite.mpcClient.OnWalletCreationResult(func(result event.KeygenResultEvent) {
logger.Info("On wallet creation result", "event", result)
t.Logf("Received keygen result for wallet %s: %s", result.WalletID, result.ResultType)
suite.keygenResults[result.WalletID] = &result

// For testing replay properly, don't overwrite existing result for the same wallet ID to preserve the first result
if _, exists := suite.keygenResults[result.WalletID]; !exists {
suite.keygenResults[result.WalletID] = &result
}

if result.ResultType == event.ResultTypeError {
t.Logf("Keygen failed for wallet %s: %s (%s)", result.WalletID, result.ErrorReason, result.ErrorCode)
Expand Down Expand Up @@ -164,8 +182,6 @@
assert.NotEmpty(t, result.EDDSAPubKey, "EdDSA public key should not be empty for wallet %s", walletID)
}
}

t.Log("Key generation test completed")
}

func verifyKeyConsistency(t *testing.T, suite *E2ETestSuite) {
Expand Down
2 changes: 2 additions & 0 deletions e2e/multi_client_routing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/fystack/mpcium/pkg/client"
"github.com/fystack/mpcium/pkg/event"
"github.com/fystack/mpcium/pkg/logger"
"github.com/fystack/mpcium/pkg/types"
"github.com/google/uuid"
"github.com/nats-io/nats.go"
Expand All @@ -36,6 +37,7 @@ type multiClientObserver struct {

func TestMultiClientResultRouting(t *testing.T) {
suite := NewE2ETestSuite(".")
logger.Init("dev", true)

t.Log("Performing pre-test cleanup...")
suite.CleanupTestEnvironment(t)
Expand Down
12 changes: 11 additions & 1 deletion e2e/setup_test_identities.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@

# Generate random password for badger encryption
echo "🔐 Generating random password for badger encryption..."
BADGER_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 32)
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
BADGER_PASSWORD=$(openssl rand -hex 16)
else
# Linux and others
BADGER_PASSWORD=$(< /dev/urandom tr -dc 'A-Za-z0-9' | head -c 32)
fi
if [ ${#BADGER_PASSWORD} -ne 32 ]; then

Check failure on line 35 in e2e/setup_test_identities.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=fystack_mpcium&issues=AZ4LJdKmbgnSuG7_tuuv&open=AZ4LJdKmbgnSuG7_tuuv&pullRequest=160
echo "❌ Generated Badger password must be exactly 32 bytes, got ${#BADGER_PASSWORD}"
exit 1
fi
echo "✅ Generated password: $BADGER_PASSWORD"

# Generate chain_code (32-byte hex value, 64 hex characters)
Expand Down
33 changes: 33 additions & 0 deletions pkg/eventconsumer/event_consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"sync"
"time"

"github.com/dgraph-io/badger/v4"
"github.com/fystack/mpcium/pkg/event"
"github.com/fystack/mpcium/pkg/identity"
"github.com/fystack/mpcium/pkg/logger"
Expand Down Expand Up @@ -132,7 +133,7 @@
logger.Info("MPC Event consumer started...!")
}

func (ec *eventConsumer) handleKeyGenEvent(natMsg *nats.Msg) {

Check failure on line 136 in pkg/eventconsumer/event_consumer.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=fystack_mpcium&issues=AZ4LJdHDbgnSuG7_tuut&open=AZ4LJdHDbgnSuG7_tuut&pullRequest=160
baseCtx, baseCancel := context.WithTimeout(context.Background(), KeyGenTimeOut)
defer baseCancel()

Expand All @@ -158,6 +159,31 @@

walletID := msg.WalletID

// Attempt to get previously stored wallet creation result (if any) by the wallet ID
storedWalletCreationResult, storedWalletCreationResultError := ec.node.GetWalletCreationResult(walletID)

// Error when retrieving wallet creation result for the wallet ID
if storedWalletCreationResultError != nil && !errors.Is(storedWalletCreationResultError, badger.ErrKeyNotFound) {
ec.handleKeygenSessionError(walletID, storedWalletCreationResultError, "Failed to check stored wallet creation result", natMsg)
return
}

// Replay the wallet creation result if it already exists for the wallet ID
// TODO: to move the following duplicate logic (line 308 and line 341) into a func

Check warning on line 172 in pkg/eventconsumer/event_consumer.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=fystack_mpcium&issues=AZ4LJdHDbgnSuG7_tuus&open=AZ4LJdHDbgnSuG7_tuus&pullRequest=160
if storedWalletCreationResult != nil {
key := event.KeygenResultSubject(natMsg.Header.Get(event.ClientIDHeader), walletID)
if err := ec.genKeyResultQueue.Enqueue(key, storedWalletCreationResult, &messaging.EnqueueOptions{
IdempotententKey: composeKeygenIdempotentKey(walletID, natMsg),
}); err != nil {
logger.Error("Failed to enqueue stored wallet creation result", err, "walletID", walletID)
ec.handleKeygenSessionError(walletID, err, "Failed to enqueue stored wallet creation result", natMsg)
return
}
ec.sendReplyToRemoveMsg(natMsg)
logger.Info("Returned stored wallet creation result for existing wallet", "walletID", walletID)
return
}

// Guard against duplicate keygen sessions for the same walletID.
// Under heavy load, the keygen consumer may NAK and JetStream redelivers,
// creating a second session on the same NATS topics which causes VSS verify failures.
Expand Down Expand Up @@ -272,6 +298,13 @@
return
}

// Store wallet creation result
if storeErr := ec.node.StoreWalletCreationResult(walletID, payload); storeErr != nil {
logger.Error("Failed to store wallet creation result", storeErr, "walletID", walletID)
ec.handleKeygenSessionError(walletID, storeErr, "Failed to store wallet creation result", natMsg)
return
}

key := event.KeygenResultSubject(natMsg.Header.Get(event.ClientIDHeader), walletID)
if err := ec.genKeyResultQueue.Enqueue(
key,
Expand Down
12 changes: 12 additions & 0 deletions pkg/mpc/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,18 @@ func (p *Node) CreateReshareSession(
}
}

const walletCreationResultPrefix = "wallet_creation_result_prefix"

func (p *Node) StoreWalletCreationResult(walletID string, result []byte) error {
key := fmt.Sprintf("%s:%s", walletCreationResultPrefix, walletID)
return p.kvstore.Put(key, result)
}

func (p *Node) GetWalletCreationResult(walletID string) ([]byte, error) {
key := fmt.Sprintf("%s:%s", walletCreationResultPrefix, walletID)
return p.kvstore.Get(key)
}

func ComposeReadyKey(nodeID string) string {
return fmt.Sprintf("ready/%s", nodeID)
}
Expand Down
2 changes: 2 additions & 0 deletions setup.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export PATH="$(go env GOPATH)/bin:$PATH"

NUM_NODES=3

make build
Expand Down
Loading