Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: zk shared key #586

Merged
merged 31 commits into from
Feb 5, 2025
Merged

feat: zk shared key #586

merged 31 commits into from
Feb 5, 2025

Conversation

Mikelle
Copy link
Member

@Mikelle Mikelle commented Jan 28, 2025

Describe your changes

Overview of the Changes in PR #586 - Zero-Knowledge Shared Key

Below is a high-level summary of what's going on in this pull request, focusing on the major changes and how they fit together. It’s grouped by core themes:

Doc for the reference: https://www.notion.so/primev/Preventing-Unopenable-Commitments-1606865efd6f8097927ed9c162ea7631


1. Zero-Knowledge (ZK) Context and Proof Flows

New BN128.sol Utility

  • A new file: contracts/contracts/utils/BN128.sol, which provides wrappers for two bn128 precompiles:
    • ecAdd (bn128 addition at 0x06)
    • ecMul (bn128 multiplication at 0x07)
  • If the precompile calls fail, it reverts with BN128AddFailed or BN128MulFailed.

ZK Proof in PreconfManager

  • PreconfManager.sol now:
    • Stores a zkContextHash (like "mev-commit opening <chainID>").
    • Expects an array param _zkProof in openCommitment(...).
    • If the caller is the commitment’s winner (the provider), it calls _verifyZKProof(zkProof).
    • _verifyZKProof(zkProof):
      1. Computes a = g^z * (providerPub)^c
      2. Computes a' = B^z * C^c
      3. Re-hashes these points with zkContextHash → a challenge
      4. Checks that it matches _zkProof’s challenge

ABI Changes

  • PreconfManager.abi & generated Go bindings changed:
    • Old sharedSecretKey in function parameters/events replaced by uint256[] zkProof.
    • New error types: BN128AddFailed, BN128MulFailed, ProviderZKProofInvalid.
  • No more storing “secret key bytes” on-chain; now it’s a ZK approach to prove ephemeral keys.

2. Struct / Signature Layout Changes

Commitment Data

  • Old param sharedSecretKey is removed; each side now supplies an 8-element zkProof array ([providerX, providerY, bidderX, bidderY, sharedX, sharedY, c, z]).

Bid Hash

  • EIP-712 struct for a bid now includes bidder’s BN254 public key:

    PreConfBid(
      string txnHash,
      string revertingTxHashes,
      uint256 bidAmt,
      uint64 blockNumber,
      uint64 decayStartTimeStamp,
      uint64 decayEndTimeStamp,
      uint256 bidderPKx,
      uint256 bidderPKy
    )
  • Go code (e.g. encryptor/encryptor.go) updated to include (bidderPKx, bidderPKy) in ABI-encoded hashing.

Pre-Confirmation Hash

  • Similarly updated from:

    OpenedCommitment(string txnHash, …, bytes sharedSecretKey)

    to

    OpenedCommitment(
      bytes32 bidHash,
      string signature,
      uint256 sharedKeyX,
      uint256 sharedKeyY
    )

3. Go Libraries and BN254 Key Handling

p2p/pkg/crypto/ecdh.go and zkproof.go

  • New ECDH for BN254:
    • GenerateKeyPairBN254()(sk, pk)
    • DeriveSharedKey(skA, pkB) → shared G1 point C.
  • ZK:
    • GenerateOptimizedProof(sk, A, B, C, context)(c, z)
    • VerifyOptimizedProof(proof, A, B, C, context)bool
  • Serialization:
    • BN254PublicKeyToBytes(...) / BN254PublicKeyFromBytes(...) (96 bytes uncompressed).
    • BN254PrivateKeyToBytes(...) / BN254PrivateKeyFromBytes(...) (32 bytes for fr.Element).
  • Key Store:
    • The old P256 ECDH references replaced by BN254 in keysstore.Store:
      • SetBN254PrivateKey(...), GetBN254PrivateKey(...)
      • SetBN254PublicKey(...), GetBN254PublicKey(...)

4. Effects on the P2P Side

  • The handshake in p2p/pkg/p2p/libp2p/internal/handshake/ now exchanges BN254 public keys instead of ECDH P256.
  • Keys struct in p2p/pkg/p2p/p2p.go changed:
    • NIKEPublicKey*bn254.G1Affine (was *ecdh.PublicKey).
    • Corresponding JSON marshalling logic updated for 96-byte BN254 format.

5. Contract and Storage Adjustments

  • PreconfManager:
    • openCommitment(...) → final param _zkProof.
    • If msg.sender == winner, calls _verifyZKProof(_zkProof).
    • Reverts with ProviderZKProofInvalid if verification fails.
    • Removes references to sharedSecretKey in events.
  • Less On-Chain Data: ephemeral key is no longer stored as raw bytes in events.

6. Typical Flow

  1. Bidder includes its BN254 public key (PKb) in EIP-712 bid signature.
  2. Provider sees (PKb), computes shared secret C = PKb^skProvider, and generates a ZK proof (c,z) for that ephemeral secret.
  3. Provider calls openCommitment(…, _zkProof).
  4. Contract verifies _verifyZKProof(...).
  5. If valid, on-chain logic accepts the provider’s ephemeral key.

7. Summary

  • Replaces a simple sharedSecretKey bytes approach with BN254 ECDH + zero-knowledge proof to ensure the ephemeral key is valid, without exposing it.
  • Affects:
    • On-chain: new ZK logic, updated ABIs, no more plain ephemeral secret in events.
    • Off-chain: new BN254-based ECDH library, updated key store, handshake, EIP-712 hashing.

Checklist before requesting a review

  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation

@Mikelle Mikelle self-assigned this Jan 28, 2025
@Mikelle Mikelle marked this pull request as ready for review January 30, 2025 14:49
@Mikelle Mikelle requested a review from chrmatt January 30, 2025 16:49
// 2) Retrieve the G1 generator (1,2) from the bn254 package
var g1Aff bn254.G1Affine
g1Aff.X.SetOne()
g1Aff.Y.SetUint64(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this hard-coded instead of using library function? See https://github.com/consensys/gnark-crypto/blob/v0.16.0/ecc/bn254/bn254.go#L151

Copy link
Member Author

@Mikelle Mikelle Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s hardcoded to use the same generators as on the Solidity side. Otherwise, I couldn’t verify the proof. (Or I need to pass them as an input too)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, makes sense. But then I think the comment "Retrieve the G1 generator (1,2) from the bn254 package" is misleading.

* (2) sharedSecret = bidPub^sk
* using the non-interactive challenge c = H(...).
*
* The proof is {c, z}, plus we have inputs {providerPub, bidPub, sharedSecret}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to recall here what zkProof[0], zkProof[1], etc., are.

Copy link
Collaborator

@aloknerurkar aloknerurkar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the minor comments LGTM!

if (msg.sender == winner) {
require(
_verifyZKProof(zkProof),
ProviderZKProofInvalid(msg.sender)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add commitment information here to be safe. The txn failures get the error and print it in the logs. So it would be helpful to know in logs as well the exact commitment that failed.

pk *bn254.G1Affine
err error
)
if bid.NikePublicKey == nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this check required? We will always generate a new one no?

Copy link
Collaborator

@mrekucci mrekucci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the comments, LGTM!

p2p/pkg/crypto/ecdh.go Outdated Show resolved Hide resolved
p2p/pkg/crypto/ecdh.go Outdated Show resolved Hide resolved
p2p/pkg/crypto/zkproof.go Outdated Show resolved Hide resolved
p2p/pkg/p2p/libp2p/providerkeys.go Show resolved Hide resolved
@Mikelle Mikelle requested a review from chrmatt February 4, 2025 17:02
@@ -200,7 +212,7 @@ contract PreconfManager is
* @param decayStartTimeStamp The start time of the decay
* @param decayEndTimeStamp The end time of the decay
* @param bidSignature The signature of the bid
* @param sharedSecretKey The shared secret key
* @param zkProof The zk proof
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @param zkProof The zk proof
* @param zkProof The zk proof is an array of c and z, representing the challenge and z = k - c*sk

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically I guess that isn't the case, since you're wrapping a bunch of data into that array.

Copy link
Member

@chrmatt chrmatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Mikelle Mikelle requested a review from ckartik February 4, 2025 20:00
@Mikelle Mikelle merged commit a821a0c into main Feb 5, 2025
5 checks passed
@Mikelle Mikelle deleted the feat/zk-sharedkey branch February 5, 2025 00:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants