207 lines
7.8 KiB
Go
207 lines
7.8 KiB
Go
package integration_test
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/rwadurian/mpc-system/pkg/tss"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestFull2of3MPCFlow tests the complete 2-of-3 MPC flow:
|
|
// 1. Key generation with 3 parties
|
|
// 2. Signing with 2 parties (threshold)
|
|
// 3. Signature verification
|
|
func TestFull2of3MPCFlow(t *testing.T) {
|
|
fmt.Println("========================================")
|
|
fmt.Println(" MPC 2-of-3 Full Flow Integration Test")
|
|
fmt.Println("========================================")
|
|
|
|
// ============================================
|
|
// Step 1: Key Generation (2-of-3)
|
|
// ============================================
|
|
fmt.Println("\n[Step 1] Running 2-of-3 Distributed Key Generation...")
|
|
fmt.Println(" - Threshold (t): 1 (meaning t+1=2 signers required)")
|
|
fmt.Println(" - Total Parties (n): 3")
|
|
|
|
// In tss-lib, threshold=1 means 2 signers are required (t+1)
|
|
threshold := 1
|
|
totalParties := 3
|
|
|
|
keygenResults, err := tss.RunLocalKeygen(threshold, totalParties)
|
|
require.NoError(t, err, "Keygen should succeed")
|
|
require.Len(t, keygenResults, 3, "Should have 3 key shares")
|
|
|
|
// Extract the shared public key
|
|
publicKey := keygenResults[0].PublicKey
|
|
require.NotNil(t, publicKey, "Public key should not be nil")
|
|
|
|
fmt.Printf(" [OK] Key generation completed!\n")
|
|
fmt.Printf(" Public Key X: %s...\n", publicKey.X.Text(16)[:32])
|
|
fmt.Printf(" Public Key Y: %s...\n", publicKey.Y.Text(16)[:32])
|
|
|
|
// Verify all parties have the same public key
|
|
for i, result := range keygenResults {
|
|
assert.Equal(t, publicKey.X, result.PublicKey.X, "Party %d should have same X", i)
|
|
assert.Equal(t, publicKey.Y, result.PublicKey.Y, "Party %d should have same Y", i)
|
|
}
|
|
fmt.Println(" All parties have consistent public key")
|
|
|
|
// ============================================
|
|
// Step 2: Signing with 2 Parties (Threshold)
|
|
// ============================================
|
|
fmt.Println("\n[Step 2] Running Threshold Signing (2-of-3)...")
|
|
|
|
// Create a message to sign
|
|
message := []byte("Hello MPC World! This is a test transaction.")
|
|
messageHash := sha256.Sum256(message)
|
|
|
|
fmt.Printf(" Message: \"%s\"\n", string(message))
|
|
fmt.Printf(" Message Hash: %s\n", hex.EncodeToString(messageHash[:]))
|
|
|
|
// Test all 3 combinations of 2 parties
|
|
combinations := []struct {
|
|
name string
|
|
parties []*tss.LocalKeygenResult
|
|
}{
|
|
{"Party 0 + Party 1", []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}},
|
|
{"Party 0 + Party 2", []*tss.LocalKeygenResult{keygenResults[0], keygenResults[2]}},
|
|
{"Party 1 + Party 2", []*tss.LocalKeygenResult{keygenResults[1], keygenResults[2]}},
|
|
}
|
|
|
|
for i, combo := range combinations {
|
|
fmt.Printf("\n [Signing %d] %s\n", i+1, combo.name)
|
|
|
|
signResult, err := tss.RunLocalSigning(threshold, combo.parties, messageHash[:])
|
|
require.NoError(t, err, "Signing with %s should succeed", combo.name)
|
|
|
|
// Verify signature components
|
|
require.NotNil(t, signResult.R, "R should not be nil")
|
|
require.NotNil(t, signResult.S, "S should not be nil")
|
|
require.Len(t, signResult.Signature, 64, "Signature should be 64 bytes")
|
|
|
|
fmt.Printf(" R: %s...\n", signResult.R.Text(16)[:32])
|
|
fmt.Printf(" S: %s...\n", signResult.S.Text(16)[:32])
|
|
fmt.Printf(" Recovery ID: %d\n", signResult.RecoveryID)
|
|
|
|
// ============================================
|
|
// Step 3: Verify Signature
|
|
// ============================================
|
|
valid := ecdsa.Verify(publicKey, messageHash[:], signResult.R, signResult.S)
|
|
require.True(t, valid, "Signature verification should pass for %s", combo.name)
|
|
|
|
fmt.Printf(" [OK] Signature verified successfully!\n")
|
|
}
|
|
|
|
// ============================================
|
|
// Step 4: Test Different Messages
|
|
// ============================================
|
|
fmt.Println("\n[Step 3] Testing with Different Messages...")
|
|
|
|
messages := []string{
|
|
"Transaction: Send 1.5 ETH to 0x1234...",
|
|
"Contract call: approve(spender, amount)",
|
|
"NFT transfer: tokenId=42",
|
|
}
|
|
|
|
signers := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}
|
|
|
|
for _, msg := range messages {
|
|
msgHash := sha256.Sum256([]byte(msg))
|
|
signResult, err := tss.RunLocalSigning(threshold, signers, msgHash[:])
|
|
require.NoError(t, err)
|
|
|
|
valid := ecdsa.Verify(publicKey, msgHash[:], signResult.R, signResult.S)
|
|
require.True(t, valid)
|
|
|
|
fmt.Printf(" [OK] Message: \"%s...\"\n", msg[:min(30, len(msg))])
|
|
}
|
|
|
|
// ============================================
|
|
// Summary
|
|
// ============================================
|
|
fmt.Println("\n========================================")
|
|
fmt.Println(" Test Summary")
|
|
fmt.Println("========================================")
|
|
fmt.Println(" [OK] 2-of-3 Key Generation: PASSED")
|
|
fmt.Println(" [OK] Threshold Signing (3 combinations): PASSED")
|
|
fmt.Println(" [OK] Signature Verification: PASSED")
|
|
fmt.Println(" [OK] Multi-message Signing: PASSED")
|
|
fmt.Println("========================================")
|
|
fmt.Println(" All MPC operations completed successfully!")
|
|
fmt.Println("========================================")
|
|
}
|
|
|
|
// TestSecurityProperties tests security properties of the MPC system
|
|
func TestSecurityProperties(t *testing.T) {
|
|
fmt.Println("\n========================================")
|
|
fmt.Println(" Security Properties Test")
|
|
fmt.Println("========================================")
|
|
|
|
threshold := 1
|
|
totalParties := 3
|
|
|
|
// Generate keys
|
|
keygenResults, err := tss.RunLocalKeygen(threshold, totalParties)
|
|
require.NoError(t, err)
|
|
|
|
publicKey := keygenResults[0].PublicKey
|
|
message := []byte("Security test message")
|
|
messageHash := sha256.Sum256(message)
|
|
|
|
// Test 1: Single party cannot sign
|
|
fmt.Println("\n[Test 1] Verifying single party cannot sign alone...")
|
|
// Note: With threshold=1, minimum 2 parties are required
|
|
// Attempting to sign with 1 party should fail
|
|
singleParty := []*tss.LocalKeygenResult{keygenResults[0]}
|
|
_, err = tss.RunLocalSigning(threshold, singleParty, messageHash[:])
|
|
// This should fail because we need at least t+1=2 parties
|
|
if err != nil {
|
|
fmt.Println(" [OK] Single party signing correctly rejected")
|
|
} else {
|
|
t.Error("Single party should not be able to sign")
|
|
}
|
|
|
|
// Test 2: Different key shares produce same public key
|
|
fmt.Println("\n[Test 2] Verifying key share consistency...")
|
|
for i := 0; i < totalParties; i++ {
|
|
assert.Equal(t, publicKey.X.Cmp(keygenResults[i].PublicKey.X), 0)
|
|
assert.Equal(t, publicKey.Y.Cmp(keygenResults[i].PublicKey.Y), 0)
|
|
}
|
|
fmt.Println(" [OK] All parties have consistent public key")
|
|
|
|
// Test 3: Signatures from different party combinations verify with same public key
|
|
fmt.Println("\n[Test 3] Verifying signature consistency across party combinations...")
|
|
combo1 := []*tss.LocalKeygenResult{keygenResults[0], keygenResults[1]}
|
|
combo2 := []*tss.LocalKeygenResult{keygenResults[1], keygenResults[2]}
|
|
|
|
sig1, err := tss.RunLocalSigning(threshold, combo1, messageHash[:])
|
|
require.NoError(t, err)
|
|
|
|
sig2, err := tss.RunLocalSigning(threshold, combo2, messageHash[:])
|
|
require.NoError(t, err)
|
|
|
|
// Both signatures should verify with the same public key
|
|
valid1 := ecdsa.Verify(publicKey, messageHash[:], sig1.R, sig1.S)
|
|
valid2 := ecdsa.Verify(publicKey, messageHash[:], sig2.R, sig2.S)
|
|
|
|
assert.True(t, valid1, "Signature from combo1 should verify")
|
|
assert.True(t, valid2, "Signature from combo2 should verify")
|
|
fmt.Println(" [OK] All party combinations produce valid signatures")
|
|
|
|
fmt.Println("\n========================================")
|
|
fmt.Println(" Security tests passed!")
|
|
fmt.Println("========================================")
|
|
}
|
|
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|