# EXODUS - EXchange Of Digital Uniformed Signatures ## Overview This module optimizes a hot spot in the verification formula used for missing‑signer recovery. The target expression is: `k*P + l*Q + d*M` which requires three scalar multiplications and two point additions. Naive elliptic‑curve routines make this very gas‑expensive; this work reduces cost while maintaining correctness. For full background and protocol details see the [project wiki](https://git.ghostchain.io/ghostchain/ghost-exodus-draft/wiki/Description). ## Goals * Reduce gas for the targeted combination of scalar multiplications and additions. * Keep implementation compact and auditable for on‑chain use. * Maintain correctness and safety for cryptographic operations. ## Design choices * Use Projective coordinates (not Jacobian) to cut down on the number of `mulmod`/`addmod` operations where possible while retaining simple formulas for point addition and doubling. * Perform the final conversion to affine coordinates with an optimized `Extended Euclidean Algorithm` implemented in inline assembly to reduce gas compared with high‑level inversion routines. * Benchmark against the Jacobian implementation from the [witnet elliptic‑curve‑solidity project](https://github.com/witnet/elliptic-curve-solidity) as a reference. ## Rationale * Projective coordinates: fewer modular multiplications in the common path, making point operations cheaper on average. * Assembly `Extended Euclidean Algorithm` for inversion: this algorithm in optimized inline assembly typically yields lower gas for single inversions compared with repeated `mulmod` exponentiation or other higher‑level approaches. * Comparing to a well‑maintained Jacobian implementation gives a meaningful baseline for gas and correctness. ## Gas Usage `Jacobian` is the original implementation used as a reference implementation, while `Projective` is optimized one. ```bash ╭----------------------------------------+-----------------+---------+---------+---------+---------╮ | src/MathTester.sol:MathTester Contract | | | | | | +==================================================================================================+ | Deployment Cost | Deployment Size | | | | | |----------------------------------------+-----------------+---------+---------+---------+---------| | 3269040 | 14905 | | | | | |----------------------------------------+-----------------+---------+---------+---------+---------| | | | | | | | |----------------------------------------+-----------------+---------+---------+---------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |----------------------------------------+-----------------+---------+---------+---------+---------| | addGhost | 1450 | 1450 | 1450 | 1450 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | addJacobian | 1976 | 1976 | 1976 | 1976 | 88 | |----------------------------------------+-----------------+---------+---------+---------+---------| | addMixedGhost | 1084 | 1084 | 1084 | 1084 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | addMixedProjective | 1098 | 1098 | 1098 | 1098 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | addProjective | 1252 | 1252 | 1252 | 1252 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | doubleGhost | 699 | 699 | 699 | 699 | 45 | |----------------------------------------+-----------------+---------+---------+---------+---------| | doubleJacobian | 1101 | 1101 | 1101 | 1101 | 45 | |----------------------------------------+-----------------+---------+---------+---------+---------| | doubleProjective | 677 | 677 | 677 | 677 | 45 | |----------------------------------------+-----------------+---------+---------+---------+---------| | isOnCurve | 296 | 296 | 296 | 296 | 306 | |----------------------------------------+-----------------+---------+---------+---------+---------| | isOnCurveGhost | 824 | 824 | 824 | 824 | 306 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulPairEc | 103705 | 731500 | 1058710 | 1270392 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulPairGhost | 4185 | 174517 | 303073 | 330416 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulPairProjective | 5275 | 182401 | 327577 | 361377 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulQuartetEc | 308036 | 1561245 | 1982575 | 2672715 | 42 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulQuartetProjective | 12468 | 206689 | 346026 | 396570 | 42 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulSingleEc | 2093 | 327159 | 408874 | 600125 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulSingleGhost | 1775 | 109646 | 143531 | 212019 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulSingleProjective | 1916 | 114289 | 149838 | 220860 | 44 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulTripletEc | 205416 | 1140277 | 1546137 | 1958683 | 43 | |----------------------------------------+-----------------+---------+---------+---------+---------| | mulTripletProjective | 6636 | 199868 | 346618 | 397506 | 43 | |----------------------------------------+-----------------+---------+---------+---------+---------| | toAffineGhost | 11853 | 13845 | 13925 | 15527 | 176 | |----------------------------------------+-----------------+---------+---------+---------+---------| | toAffineJacobian | 40395 | 47860 | 47979 | 53983 | 133 | |----------------------------------------+-----------------+---------+---------+---------+---------| | toAffineProjective | 11165 | 13918 | 13900 | 16214 | 351 | ╰----------------------------------------+-----------------+---------+---------+---------+---------╯ ╭-----------------------------------------------------+-----------------+--------+--------+--------+---------╮ | test/GhostVerifier.t.sol:GhostVerifierImpl Contract | | | | | | +============================================================================================================+ | Deployment Cost | Deployment Size | | | | | |-----------------------------------------------------+-----------------+--------+--------+--------+---------| | 1687628 | 7589 | | | | | |-----------------------------------------------------+-----------------+--------+--------+--------+---------| | | | | | | | |-----------------------------------------------------+-----------------+--------+--------+--------+---------| | Function Name | Min | Avg | Median | Max | # Calls | |-----------------------------------------------------+-----------------+--------+--------+--------+---------| | setPubkey | 111516 | 111529 | 111528 | 111540 | 9 | |-----------------------------------------------------+-----------------+--------+--------+--------+---------| | verify | 538818 | 609541 | 578174 | 884038 | 9 | ╰-----------------------------------------------------+-----------------+--------+--------+--------+---------╯ ``` The `verify` function consumes a maximum of ~~955,747~~ 884,038 gas units when processing 1,000 signers with 300 missing signatures. Assuming an ETH price of $4,200 and gas cost of 2 gwei: * ~~test_verify_5_of_5_works() (gas: 735686 = $6.18) - Baseline (100%)~~ * ~~test_verify_67_of_100() (gas: 812318 = $6.82) - 10% increase~~ * ~~test_verify_700_of_1000() (gas: 1118143 = $9.39) - 51% increase~~ * test_verify_5_of_5_works() (gas: 663441 = $5.57) - Baseline (100%) * test_verify_67_of_100() (gas: 740823 = $6.22) - 11% increase * test_verify_700_of_1000() (gas: 1046398 = $8.78) - 57% increase The gas cost scales reasonably across different numbers of signers, making the solution practical for real-world use cases. ### Compiler Inconsistency Note We observed unexpected behavior in the Solidity compiler. Despite identical code in `ECMathProjective` and `GhostEllipticCurves` libraries, gas costs vary for critical elliptic curve operations: * `doubleProjective` and `addProjective` show inconsistent gas costs between implementations * `mulSingleGhost` unexpectedly consumes less gas than `mulSingleProjective` These variations occur even with clean compilation runs, suggesting compiler-level optimizations that behave unpredictably with identical code in different library contexts. The root cause remains unclear. ## Implementation Details Final implementation formula is: ``` s*G + sum_i^n (e*ai*(Pi) + b*R1m + d*R2m) = R + e*Hagg s*G + sum_i^n (e*ai*(Pi)) + sum_i^n (b*R1m) + sum_i^n (b*R2m) = b*R1 + d*R2 + e*Hagg s*G + e*(Hagg - sum_i^n Hi) = b*(R1 - sum_i^n R1m) + d*(R2 - sum_i^n R2m) s*G + e*(Hagg - sum_i^n Hi) = b*R1e + d*R2e where R1 = R1e + R1m (existing nonce + missing nonce) R2 = R2e + R2m Hi = ai*Pi ``` ### Nonce Abuse Our approach enables any participant to submit pairs of (`R1e`, `R1m`) and (`R2e`, `R2m`), which we use to reconstruct the original nonces `R1` and `R2`. These reconstructed values are essential for computing `b` and `d` during the verification process, ultimately deriving the final `R` required for commitment `e`. This design leverages circular dependencies and cryptographic hash functions to ensure verification integrity. ``` arbitrary data: s, msg, R1e, R1m, R2e, R2m s*G + e*H` = b*R1e + d*R2e s*G = b*R1e + d*R2e - e*H` s*G = target_value (Discrete Log Problem) ``` However, the hash dependencies create a circular constraint similar to the "chicken and egg" problem: ``` b = sha256(R1e + R1m, R2e + R2m, H, msg) d = sha256(msg, H, R2e + R2m, R1e + R1m) e = sha256(b*(R1e+R1m) + d*(R2e+R2m), H, msg) b*R1e + d*R2e - s*G = e*H` ``` For now it looks safe if `sha256` or `keccak256` hashing algorithm is used. ### Missing Aggregation Abuse We are proposing the to store not only final `Hagg` point but the hash of all signers (both coordinates for each) in the sequence that they were aggregated. Thus each `verify` call will have all list of `Xi=ai*Pi` with indexes of missing signers. Here we are relying on fact that `sha256` and `keccak256` do not have any known ability to find collisions and resistant to preimage attack. We propose storing not only the final aggregated point `Hagg` but also the cryptographic hash of all signers' coordinates in their aggregation sequence. This approach ensures each verify call has access to the complete list of `Xi = ai * Pi` along with indexes of missing signers. This design relies on the cryptographic security of `sha256` and `keccak256`, both of which: * Have no known practical collision attacks * Maintain strong resistance to preimage attacks * Provide robust security guarantees for our verification process ### Precompile Abuse The step by step hack is described [here](https://git.ghostchain.io/ghostchain/ghost-exodus-draft/wiki/Description#abuse-of-ecrecover-precompile). ## Contributing All contributions are welcome — whether it's code, documentation, tests, performance benchmarks, or review. Please submit commits, issues, or pull requests; any help to improve correctness, security, or gas efficiency is greatly appreciated.