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

EVM: Generic BLS Interface / Use JS Implementation (@noble/curves) as Default #3471

Merged
merged 60 commits into from
Jul 5, 2024

Conversation

holgerd77
Copy link
Member

Follow-up on #3350

Goal of this PR is to move over to use a JavaScript BLS implementation for the BLS precompiles (EIP-2537). For this the direct usage of the MCL library in the precompile files as well as the tight MCL library coupling (by adding as a property to the EVM) is replaced by using a generic EVMBLSInterface. It will likely be possible later on to fully allow switching the implementation, not sure if we want to go that far, we'll see.

This first push is for testing with the official test suite after setting up the base structures (as well as some temporary double structures like the doubled mcl code for gradual implementation which will go away at the end).

Copy link

codecov bot commented Jun 24, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 80.16%. Comparing base (d24ca11) to head (cbb8041).
Report is 42 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

Flag Coverage Δ
blockchain ?
common ?
statemanager ?
tx 80.16% <ø> (?)
util ?

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

@holgerd77
Copy link
Member Author

grafik

I've done something wrong with my fork builds of @noble/curves from here https://github.com/holgerd77/noble-curves/tree/build-5fcd71a, I battled with this already the first 1 1/2 hours of the morning and ended up manually copying over the code to my node_modules folder.

This is a known issue though and should go away once @noble/curves gets an official update with the changes.

@holgerd77
Copy link
Member Author

So if one is running

npx vitest run test/precompiles/eip-2537-bls.spec.ts

from the evm folder locally these are the more meaningful test runs (currently 8/131 tests failing).

@holgerd77 holgerd77 force-pushed the evm-js-default-for-bls-precompiles branch from a3f2a1e to 4190d5a Compare July 1, 2024 10:35
@jochem-brouwer
Copy link
Member

jochem-brouwer commented Jul 2, 2024

Instructions on how to run the ethereum-tests BLS tests (please note, we had a discussion on that PR in April, see ethereum/tests#1366)

Ensure ethereum-tests is on v14:

cd ./packages/ethereum-tests && git checkout tags/v14.0

To run the state tests:

cd ./packages/vm && npm run test:state -- --fork=Prague --dir=../EIPTests/StateTests/stEIP2537

Note: blockchain tests cannot be ran, it throws some error about a header field missing, so it runs on partial Prague.

State tests output:

1..1403
# tests 1403
# pass  1217
# fail  186

The tests all pass on our current master (so with MCL)!

@jochem-brouwer
Copy link
Member

Oh wait, nvm, I can of course test using mcl-wasm. If I re-run the tests with MCL, then all pass. So we definitely have bugs!

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

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

Some comments, but this looks extremely good in general, especially how MCL / Noble is now decoupled, I love it!

packages/evm/src/evm.ts Show resolved Hide resolved
packages/evm/src/index.ts Show resolved Hide resolved

export const zeroByteCheck = (
opts: PrecompileInput,
zeroByteRanges: number[][],
Copy link
Member

Choose a reason for hiding this comment

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

Note: this will thus slice the array exclusive of zeroByteRanges[x][1]. So for instance the array [[0,1]] will only check the 0th byte, not the byte at position 1. Is this correct? (And can docs be added for this method?)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I think this should be all correct, actually this only copies your former code into a new function 😂, see this commit 62f15f2.

So this is a BLS specific zero byte check, applying this part of the EIP:

grafik

And specificall this "Due to the size of p, the top 16 bytes are always zeroes." part, see that the input subarrays always have a length of 16 bytes.

I'll give this and the other methods some documentation.

zeroByteRanges[index][0] + pairStart,
zeroByteRanges[index][1] + pairStart
)
if (!(equalsBytes(slicedBuffer, ZERO_BYTES_16) === true)) {
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 constant ZERO_BYTES_16 here and not zeros(x) where x is the expected length of the zero byte array?

Copy link
Member Author

Choose a reason for hiding this comment

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

See answer above.

* An interface for the MCL WASM implementation https://github.com/herumi/mcl-wasm
* is shipped with this library, see the following test file for an example on
* how to use:
* https://github.com/ethereumjs/ethereumjs-monorepo/blob/master/packages/evm/test/precompiles/eip-2537-bls.spec.ts
Copy link
Member

Choose a reason for hiding this comment

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

This test file does not have tests which specifically use MCL. Did you intend to use a permalink here?

Copy link
Member

Choose a reason for hiding this comment

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

I think you intend to permalink to the test which specifically runs on this PR (and later thus likely on master). Could you also point to the specific lines in this permalink? Especially the mcl init and the mcl curve settings should be initialized before being passed into the EVM.

Copy link
Member Author

Choose a reason for hiding this comment

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

I initially wanted to just add a code example here (which I think is the better solution) and then I tried and the linter complained and I was just lazy and therfore added the test link. 😂 Will go back to the code version, this now worked this time anyhow (no idea why).

mcl.setMapToMode(mcl.IRTF) // set the right map mode; otherwise mapToG2 will return wrong values.
mcl.verifyOrderG1(true) // subgroup checks for G1
mcl.verifyOrderG2(true) // subgroup checks for G2
const mclbls = new MCLBLS(mcl)
Copy link
Member

Choose a reason for hiding this comment

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

I think we can maybe improve here (I also now see why we export MCLBLS / NobleBLS). Can we add a static method to the interfaces which inits these BLSs? Then we can init the MCL using MCLBLS.init(mcl) and write those settings there

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a great idea, will directly add! 👍 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

I now went for a non-static init() version, I find it a bit nicer if the out asny initialization (so await mcl.init(mcl.BLS12_381)) is not also wrapped and can be done separately by the user, guess both ways would work and be fine though, for this one I find the typing also a bit nicer. The optional init() method from the interface is the automatically called from within the EVM constructor if present.

import * as mcl from 'mcl-wasm'

await mcl.init(mcl.BLS12_381)
const evm = await EVM.create({ mcl: new MCLBLS(mcl) })

@holgerd77 holgerd77 force-pushed the evm-js-default-for-bls-precompiles branch 2 times, most recently from 38563a2 to 3eab6a4 Compare July 2, 2024 12:03
@holgerd77 holgerd77 force-pushed the evm-js-default-for-bls-precompiles branch from 1c997ed to cbb8041 Compare July 3, 2024 11:02
Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

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

This review is on top of my previous review, got 2 small comments, LGTM in general 😄

* @param pairStart
* @returns
*/
export const zeroByteCheck = (
Copy link
Member

Choose a reason for hiding this comment

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

Nit: this does zero byte checks, but only if the range of these zero bytes is 16 zero bytes, so the name is a bit misleading.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, will rename to leading16ZeroBytesCheck

@@ -45,6 +45,7 @@ import type { bn128 } from '@ethereumjs/evm'
* --expected-test-amount: number. If passed, check after tests are ran if at least this amount of tests have passed (inclusive)
* --verify-test-amount-alltests: number. If passed, get the expected amount from tests and verify afterwards if this is the count of tests (expects tests are ran with default settings)
* --reps: number. If passed, each test case will be run the number of times indicated
* --bls: string. Use MCL as a bls library if passed (default: Noble) (state tests only)
Copy link
Member

Choose a reason for hiding this comment

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

Could we also add this for blockchain tests?

Copy link
Contributor

Choose a reason for hiding this comment

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

Following on this, is there a reason we wouldn't want to use MCL by default in the test runner? I don't know how many BLS related tests there are but anything that slows down the tests seems undesirable to me since these are the main bottleneck in our CI right now, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

Was a bit lazy and I feel that every new code is this messy test runner is a bit of "lost code" 😂, so I decided to skip. But I can very well add, makes sense for practical reasons and shouldn't be so much extra work.

Copy link
Member Author

Choose a reason for hiding this comment

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

@acolytec3 Yes, guess we can do that for the VM as long as we keep running tests for both implementations in the EVM.

Copy link
Contributor

@acolytec3 acolytec3 left a comment

Choose a reason for hiding this comment

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

Looks great overall. One item regarding comments in the noble.ts that should be clarified.

return G1
}

// input: a mcl G1 point
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need all these mcl references? I'm assuming these are obsolete in this file, right? Can they be replaced with something like noble/curves

Copy link
Member Author

Choose a reason for hiding this comment

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

Don't think that a noble/curves reference is needed here, will just completely remove

@@ -45,6 +45,7 @@ import type { bn128 } from '@ethereumjs/evm'
* --expected-test-amount: number. If passed, check after tests are ran if at least this amount of tests have passed (inclusive)
* --verify-test-amount-alltests: number. If passed, get the expected amount from tests and verify afterwards if this is the count of tests (expects tests are ran with default settings)
* --reps: number. If passed, each test case will be run the number of times indicated
* --bls: string. Use MCL as a bls library if passed (default: Noble) (state tests only)
Copy link
Contributor

Choose a reason for hiding this comment

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

Following on this, is there a reason we wouldn't want to use MCL by default in the test runner? I don't know how many BLS related tests there are but anything that slows down the tests seems undesirable to me since these are the main bottleneck in our CI right now, right?

jochem-brouwer
jochem-brouwer previously approved these changes Jul 5, 2024
Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

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

LGTM!

Copy link
Member

@jochem-brouwer jochem-brouwer left a comment

Choose a reason for hiding this comment

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

EVM lint failed, should pass now, still LGTM 😄 👍

@holgerd77 holgerd77 dismissed acolytec3’s stale review July 5, 2024 13:46

All addressed.

@holgerd77 holgerd77 merged commit e08c229 into master Jul 5, 2024
34 checks passed
@holgerd77 holgerd77 deleted the evm-js-default-for-bls-precompiles branch July 5, 2024 13:46
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.

3 participants