mirror of
https://github.com/luxfi/precompile.git
synced 2026-09-24 00:26:21 +00:00
Forty-five of the fifty-six suite modules keyed off <name>Config and eleven did not, in three other spellings: Verify, Threshold, Encapsulate. The LP-4200 unified PQCrypto block carried four of those conventions across its eight addresses — two KEMs named differently (mlkemConfig beside hqcEncapsulate) and two threshold schemes named differently (pulsarVerify beside coronaThreshold) — so the block that exists to be one thing did not read as one thing. The deviations also named an OPERATION where a precompile belongs. HQC decapsulates as well as encapsulates and the Verify precompiles do more than verify, so hqcEncapsulate was the name of one entry point standing in for the whole module. A config key selects a precompile; it is not a method selector. All fifty-six are <name>Config now. There is no compatibility shim: an upgrade.json naming an old key is naming something that does not exist, which luxd already refuses loudly, and every chain config in the estate moves with this. Co-authored-by: Hanzo Dev <[email protected]>
CGGMP21 Threshold Signature Precompile
Overview
The CGGMP21 (Canetti-Gennaro-Goldfeder-Makriyannis-Peled 2021) precompile enables threshold ECDSA signature verification. CGGMP21 is the state-of-the-art threshold signature protocol with identifiable aborts and efficient key refresh.
Address: 0x020000000000000000000000000000000000000D
Features
- Threshold ECDSA: Any t out of n parties can sign
- Identifiable Aborts: Malicious parties can be detected
- Key Refresh: Update shares without changing public key
- Standard ECDSA: Compatible with Ethereum, Bitcoin, etc.
- MPC Custody: Enterprise-grade multi-party custody
Algorithm
CGGMP21 is a threshold signature scheme where:
- n total parties hold shares of an ECDSA private key
- Any t parties can collaborate to produce a valid signature
- The signature is standard ECDSA (indistinguishable from single-party)
- Malicious parties causing failures can be identified
Key Properties
- Identifiable Aborts: Unlike GG20, can identify malicious parties
- Efficient Refresh: Proactive security without downtime
- Non-Interactive DKG: Efficient distributed key generation
- Presignature Support: Precompute signatures for faster online phase
Specifications
Input Format
Total size: 170 bytes (minimum)
| Offset | Size | Field | Description |
|---|---|---|---|
| 0-3 | 4 bytes | threshold | Minimum signers required (t) |
| 4-7 | 4 bytes | totalSigners | Total number of parties (n) |
| 8-72 | 65 bytes | publicKey | Aggregated ECDSA public key (uncompressed) |
| 73-104 | 32 bytes | messageHash | Keccak256 hash of message |
| 105-169 | 65 bytes | signature | ECDSA signature (r || s || v) |
Output Format
32 bytes: Boolean result as uint256
0x0000...0001= Valid signature0x0000...0000= Invalid signature
Gas Costs
| Operation | Base Gas | Per-Signer Gas |
|---|---|---|
| CGGMP21 Verify | 75,000 | 10,000 |
Examples:
- 2-of-3 threshold: 75,000 + (3 × 10,000) = 105,000 gas
- 3-of-5 threshold: 75,000 + (5 × 10,000) = 125,000 gas
- 5-of-7 threshold: 75,000 + (7 × 10,000) = 145,000 gas
- 10-of-15 threshold: 75,000 + (15 × 10,000) = 225,000 gas
Usage Examples
Solidity - Threshold Wallet
import "./ICGGMP21.sol";
contract MultiSigWallet is CGGMP21Verifier {
bytes public thresholdPublicKey;
uint32 public threshold;
uint32 public totalSigners;
function executeWithThreshold(
address to,
uint256 value,
bytes calldata data,
bytes calldata signature
) external {
bytes32 txHash = keccak256(abi.encodePacked(to, value, data, nonce));
verifyCGGMP21Signature(
threshold,
totalSigners,
thresholdPublicKey,
txHash,
signature
);
// Execute transaction
(bool success, ) = to.call{value: value}(data);
require(success, "Execution failed");
}
}
TypeScript (ethers.js)
const CGGMP21 = new ethers.Contract(
'0x020000000000000000000000000000000000000D',
[
'function verify(uint32,uint32,bytes,bytes32,bytes) view returns(bool)'
],
provider
);
// Verify 3-of-5 threshold ECDSA signature
const isValid = await CGGMP21.verify(
3, // threshold
5, // totalSigners
publicKey, // 65 bytes uncompressed
messageHash, // 32 bytes
signature // 65 bytes ECDSA
);
Go - MPC Integration
import (
"github.com/luxfi/mpc/pkg/protocol/cggmp21"
)
// Initialize CGGMP21 protocol
protocol := cggmp21.NewProtocol()
// Distributed key generation (3-of-5)
keygenParty, err := protocol.KeyGen("party1", partyIDs, 3)
config := keygenParty.Result()
// Threshold signing
signParty, err := protocol.Sign(config, signers, messageHash)
signature := signParty.Result()
// Verify on-chain via precompile
Use Cases
1. Multi-Party Custody
contract InstitutionalCustody is CGGMP21Verifier {
// 5-of-7 threshold for institutional custody
uint32 constant THRESHOLD = 5;
uint32 constant TOTAL_SIGNERS = 7;
function transferAssets(
address token,
address to,
uint256 amount,
bytes calldata thresholdSig
) external {
bytes32 txHash = keccak256(abi.encodePacked(token, to, amount));
verifyCGGMP21Signature(
THRESHOLD,
TOTAL_SIGNERS,
custodyPublicKey,
txHash,
thresholdSig
);
IERC20(token).transfer(to, amount);
}
}
2. DAO Treasury Management
contract DAOTreasury is CGGMP21Verifier {
// 7-of-10 DAO council
function executeDAODecision(
bytes32 proposalId,
bytes calldata executionData,
bytes calldata councilSignature
) external {
verifyCGGMP21Signature(
DAO_THRESHOLD,
DAO_COUNCIL_SIZE,
daoPublicKey,
proposalId,
councilSignature
);
// Execute DAO decision
}
}
3. Cross-Chain Bridge
contract ThresholdBridge is CGGMP21Verifier {
// Validators sign cross-chain messages
function relayMessage(
uint256 sourceChain,
bytes calldata message,
bytes calldata validatorSig
) external {
bytes32 messageHash = keccak256(abi.encodePacked(
sourceChain,
block.chainid,
message
));
verifyCGGMP21Signature(
VALIDATOR_THRESHOLD,
TOTAL_VALIDATORS,
validatorPublicKey,
messageHash,
validatorSig
);
// Process cross-chain message
}
}
Security Considerations
Threshold Selection
- 2-of-3: Small teams, personal wallets
- 3-of-5: Standard multi-sig, small DAOs
- 5-of-7: Medium security, trading firms
- 7-of-10 or higher: High security, institutional custody
Key Management
- Key Refresh: Periodically refresh shares for proactive security
- Backup Shares: Securely backup threshold shares
- Identifiable Aborts: Monitor and remove malicious parties
- Share Distribution: Never store all shares in one location
Message Hashing
Always hash messages with domain separation:
bytes32 domainHash = keccak256(abi.encodePacked(
address(this),
block.chainid,
"CGGMP21-v1"
));
bytes32 messageHash = keccak256(abi.encodePacked(
domainHash,
nonce,
data
));
Performance
Benchmarks on Apple M1 Max:
| Configuration | Gas Cost | Verify Time | Memory |
|---|---|---|---|
| 2-of-3 | 105,000 | ~65 μs | 12 KB |
| 3-of-5 | 125,000 | ~80 μs | 14 KB |
| 5-of-7 | 145,000 | ~95 μs | 16 KB |
| 10-of-15 | 225,000 | ~140 μs | 22 KB |
Comparison with Other Schemes
| Algorithm | Type | Signature Size | Gas Cost | Identifiable Aborts |
|---|---|---|---|---|
| CGGMP21 (this) | ECDSA | 65 bytes | 75k-225k | ✅ |
| FROST | Schnorr | 64 bytes | 50k-125k | ❌ |
| GG20 | ECDSA | 65 bytes | 75k-225k | ❌ |
| BLS (Warp) | BLS12-381 | 96 bytes | 120k | ✅ |
Integration with Lux MPC
This precompile integrates with /Users/z/work/lux/mpc/pkg/protocol/cggmp21:
import "github.com/luxfi/mpc/pkg/protocol/cggmp21"
// The MPC library provides:
// - protocol.KeyGen() - Distributed key generation
// - protocol.Sign() - Threshold signing
// - protocol.Refresh() - Share refreshing
// - protocol.PreSign() - Presignature generation
Standards Compliance
- CGGMP21 Paper: ePrint 2021/060
- ECDSA: secp256k1 curve (Bitcoin/Ethereum)
- EIP-191: Signed data standard