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

Add new EIP-2537 BLS test cases to EVM #3896

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

jochem-brouwer
Copy link
Member

Closes #3895

Adds test cases from: ethereum/EIPs#9425. We fail two:

   × Precompiles: pairing_check_bls.json > bls_pairing_e(0,-G2)!=e(-G1,G2) 13ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x00000000000000000000000000000000000…'
   × Precompiles: pairing_check_bls.json > bls_pairing_e(G1,0)!=e(-G1,G2) 8ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x00000000000000000000000000000000000…'

It seems like we also have the Nethermind bug (?) described here: https://ethereum-magicians.org/t/bls12-381-pairing-consensus-issue/23019

Copy link

codecov bot commented Mar 1, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 74.80%. Comparing base (0a419c3) to head (d4aa02d).

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
block 76.80% <ø> (ø)
blockchain 85.43% <ø> (ø)
client 66.23% <ø> (ø)
common 90.80% <ø> (ø)
devp2p 76.33% <ø> (ø)
ethash 81.04% <ø> (ø)
evm 68.60% <100.00%> (-0.02%) ⬇️
genesis 99.84% <ø> (ø)
mpt 59.89% <ø> (+0.23%) ⬆️
rlp 69.70% <ø> (ø)
statemanager 66.85% <ø> (ø)
tx 81.20% <ø> (ø)
util ?
vm 57.44% <ø> (ø)
wallet 83.78% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@jochem-brouwer
Copy link
Member Author

Hi @paulmillr, it seems that two tests fail, but only on Noble (pass on MCL), so the suspect is that something in evm/src/precompiles/bls2_381/noble.ts is wrong. These fail:

   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(0,-G2)!=e(-G1,G2) 16ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x00000000000000000000000000000000000…'
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(G1,0)!=e(-G1,G2) 9ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x00000000000000000000000000000000000…'

If I attempt to fix similar to the Nethermind way in 6871ec5, I now get other tests which fail. The EVM fails with an error on those tests;

 ❯ test/precompiles/eip-2537-bls.spec.ts (11 tests | 5 failed | 1 skipped) 248ms
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(0,G2) 14ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x'
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(G1,0) 4ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x'
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(0,-G2)!=e(-G1,G2) 10ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x'
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(G1,0)!=e(-G1,G2) 8ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x'
   × Precompiles: pairing_check_bls.json [Noble] > bls_pairing_e(G1,0)=e(0,G2) 8ms
     → return value should match testVectorResult: expected '0x00000000000000000000000000000000000…' to deeply equal '0x'

The relevant error here is:

  exceptionError: Error: pairing is not available for ZERO point
      at Object.pairingBatch (file:///home/jochem/Documents/ejs/hive/clients/ethereumjs/ethereumjs-monorepo/packages/evm/node_modules/@noble/curves/src/abstract/bls.ts:323:15)
      at NobleBLS.pairingCheck (/home/jochem/Documents/ejs/hive/clients/ethereumjs/ethereumjs-monorepo/packages/evm/src/precompiles/bls12_381/noble.ts:330:28)
      at precompile0f (/home/jochem/Documents/ejs/hive/clients/ethereumjs/ethereumjs-monorepo/packages/evm/src/precompiles/0f-bls12-pairing.ts:61:23)
      at /home/jochem/Documents/ejs/hive/clients/ethereumjs/ethereumjs-monorepo/packages/evm/test/precompiles/eip-2537-bls.spec.ts:83:38
      at processTicksAndRejections (node:internal/process/task_queues:95:5)
      at file:///home/jochem/Documents/ejs/hive/clients/ethereumjs/ethereumjs-monorepo/node_modules/@vitest/runner/dist/index.js:561:22

(Note: if there is an error it will output no bytes, so it will output 0x, I will harden the test runner such that it first check if there is an error in the case that no error is expected, and if there is an error raise before checking the return buffer to avoid confusion in the future)

I have absolutely no idea about any of this math, so I will not start guessing how I should fix this. Would be great if you could take a look :)

@jochem-brouwer
Copy link
Member Author

jochem-brouwer commented Mar 1, 2025

So here is the relevant code:

    // NOTE: check for point of infinity should happen only after all points parsed (in case they are malformed)
    for (const { g1, g2 } of pairs) {
      const _g2 = g2 as unknown as any
      // EIP: "If any input is the infinity point, pairing result will be 1"
      if (g1.equals(G1_ZERO) || (_g2.equals(G2_ZERO) as boolean)) {
         return BLS_ONE_BUFFER
      }
    }

    const FP12 = bls12_381.pairingBatch(pairs, true)

    if (bls12_381.fields.Fp12.eql(FP12, bls12_381.fields.Fp12.ONE)) {
      return BLS_ONE_BUFFER
    } else {
      return BLS_ZERO_BUFFER
    }

For the fix I have removed the for loop which checks the pairs, and if anything is G1_ZERO or G2_ZERO it previously returned the 0x00..01 buffer but in the fix it skips over this loop. However, it now runs into an error at the pairingBatch and 3 other tests now throw there too. We know that after the for loop (so when calling pairingBatch) at least one g1 or g2 of the pairs is either G1_ZERO or G2_ZERO.

Here is the code which seems somewhat similar in MCL (MCL passes these tests)

    GT = this._mcl.finalExp(GT)

    if (GT.isOne() === true) {
      return BLS_ONE_BUFFER
    } else {
      return BLS_ZERO_BUFFER
    }

@jochem-brouwer
Copy link
Member Author

Here is a thing which I want to investigate: if we run coverage on these tests (before any fix), then likely one of the lines will get flagged as yellow / partially tested. This could be used for the future to flag uncovered test cases which could then be used to find these kind of bugs / divergences.

@paulmillr
Copy link
Member

paulmillr commented Mar 2, 2025

The thread says

This explanation can be interpreted in two different ways:
If a pair {P, Q} where either P or Q is infinity on their respective curves, either
(1) the pair may be ignored, but the multi-pairing should be computed¹
(2) the precompile should return 1 and the multi-pairing should not be computed

We confirmed later that the original author intended (1).

So, you should filter-out infinity points from pairs and pass the result to pairingBatch:

const pairsf = pairs.filter(pair => isFinite(pair));
const FP12 = bls12_381.pairingBatch(pairsf, true)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BLS precompile: add more tests
2 participants