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 BN254 (alt_BN128) Interface for Precompiles / Use @noble/curves By Default #3564

Merged
merged 32 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
47db32f
Rename BN254 (alt_BN128) interface to match the more generic naming s…
holgerd77 Aug 6, 2024
2cb20a3
Add bn254 EVM constructor option, use option for existing rustbn.js p…
holgerd77 Aug 6, 2024
59766c9
Add a thin wrapper interface around pure rustbn, use interface within…
holgerd77 Aug 6, 2024
fb2f4a7
Naming adjustments
holgerd77 Aug 6, 2024
ef4e8dc
Switch over to use Uint8Array as input and output values for the inte…
holgerd77 Aug 6, 2024
cfe2f32
One-time WASM initialization fix
holgerd77 Aug 6, 2024
1743872
Add dummy Noble interface
holgerd77 Aug 6, 2024
978335f
Add custom (temporary) @noble/curves build to EVM package.json
holgerd77 Aug 6, 2024
ec29aaf
Rebuild package-lock.json
holgerd77 Aug 6, 2024
b7fdd49
Temporary fix for conflicting @noble/curves versions
holgerd77 Aug 6, 2024
4e0be50
Integrate Noble usage for multiplication
holgerd77 Aug 6, 2024
d61aa47
Add generic equalityLengthCheck, moduloLengthCheck methods from BLS u…
holgerd77 Aug 6, 2024
610852b
Fix test
holgerd77 Aug 6, 2024
69bca4c
Minor
holgerd77 Aug 6, 2024
d324623
Fixes
holgerd77 Aug 6, 2024
ef7ab69
Add validity assertion for G1 point
holgerd77 Aug 7, 2024
484a859
Replace equality length check with byte length correction (chop off o…
holgerd77 Aug 7, 2024
69f387e
Fixes
holgerd77 Aug 7, 2024
09bbab2
Add BN254 add implementation
holgerd77 Aug 7, 2024
29faa84
Add additional pairing modulo length check since not safe to rely on …
holgerd77 Aug 7, 2024
ea1780d
Some basic alignment
holgerd77 Aug 7, 2024
ce4eca5
First try on pairing (not working yet)
holgerd77 Aug 7, 2024
50919fc
First pairing tests passing
holgerd77 Aug 7, 2024
8e41321
Fixes
holgerd77 Aug 7, 2024
042765c
Merge branch 'master' into evm-bn254-precompile-native-js
holgerd77 Aug 7, 2024
d978474
Update @noble/curves to final v1.5.0 release
holgerd77 Aug 8, 2024
cfe48cf
Rebuild package-lock.json
holgerd77 Aug 8, 2024
def936d
Merge branch 'master' into evm-bn254-precompile-native-js
holgerd77 Aug 8, 2024
c187705
Use plain Noble BN254 by default, clean-up, interface type exports, n…
holgerd77 Aug 8, 2024
f35850c
Move rustbn-wasm dependency over to EVM dev dependencies, add to clie…
holgerd77 Aug 8, 2024
33cc3e9
Rebuild package-lock.json
holgerd77 Aug 8, 2024
3b3e324
Integrate in client
holgerd77 Aug 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"mcl-wasm": "^1.5.0",
"memory-level": "^1.0.0",
"prom-client": "^15.1.0",
"rustbn-wasm": "^0.4.0",
"verkle-cryptography-wasm": "^0.4.5",
"winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.5",
Expand Down
7 changes: 6 additions & 1 deletion packages/client/src/execution/vmexecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
DBSetTD,
} from '@ethereumjs/blockchain'
import { CacheType, ConsensusType, Hardfork } from '@ethereumjs/common'
import { MCLBLS } from '@ethereumjs/evm'
import { MCLBLS, RustBN254 } from '@ethereumjs/evm'
import { getGenesis } from '@ethereumjs/genesis'
import { DefaultStateManager, StatelessVerkleStateManager } from '@ethereumjs/statemanager'
import { createTrie } from '@ethereumjs/trie'
Expand All @@ -21,6 +21,7 @@ import {
import { VM, runBlock, runTx } from '@ethereumjs/vm'
import { writeFileSync } from 'fs'
import * as mcl from 'mcl-wasm'
import { initRustBN } from 'rustbn-wasm'
import { loadVerkleCrypto } from 'verkle-cryptography-wasm'

import { Event } from '../types.js'
Expand Down Expand Up @@ -178,12 +179,14 @@ export class VMExecution extends Execution {
})

await mcl.init(mcl.BLS12_381)
const rustBN = await initRustBN()
this.merkleVM = await VM.create({
common: this.config.execCommon,
blockchain: this.chain.blockchain,
stateManager,
evmOpts: {
bls: new MCLBLS(mcl),
bn254: new RustBN254(rustBN),
},
profilerOpts: this.config.vmProfilerOpts,
})
Expand All @@ -201,12 +204,14 @@ export class VMExecution extends Execution {
verkleCrypto,
})
await mcl.init(mcl.BLS12_381)
const rustBN = await initRustBN()
this.verkleVM = await VM.create({
common: this.config.execCommon,
blockchain: this.chain.blockchain,
stateManager,
evmOpts: {
bls: new MCLBLS(mcl),
bn254: new RustBN254(rustBN),
},
profilerOpts: this.config.vmProfilerOpts,
})
Expand Down
6 changes: 3 additions & 3 deletions packages/evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,10 @@
"@ethereumjs/statemanager": "^2.3.0",
"@ethereumjs/tx": "^5.3.0",
"@ethereumjs/util": "^9.0.3",
"@noble/curves": "^1.4.2",
"@noble/curves": "^1.5.0",
"@types/debug": "^4.1.9",
"debug": "^4.3.3",
"ethereum-cryptography": "^2.2.1",
"rustbn-wasm": "^0.4.0"
"ethereum-cryptography": "^2.2.1"
},
"devDependencies": {
"@ethersproject/abi": "^5.0.12",
Expand All @@ -81,6 +80,7 @@
"minimist": "^1.2.5",
"node-dir": "^0.1.17",
"rollup-plugin-visualizer": "^5.12.0",
"rustbn-wasm": "^0.4.0",
"solc": "^0.8.1",
"split": "^1.0.1"
},
Expand Down
12 changes: 5 additions & 7 deletions packages/evm/src/constructors.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { Common, Mainnet } from '@ethereumjs/common'
import { SimpleStateManager } from '@ethereumjs/statemanager'
import { initRustBN } from 'rustbn-wasm'

import { NobleBN254 } from './precompiles/index.js'
import { DefaultBlockchain } from './types.js'

import { EVM } from './index.js'

import type { EVMOpts, bn128 } from './index.js'

let initializedRustBN: bn128 | undefined = undefined
import type { EVMOpts } from './index.js'

/**
* Use this async static constructor for the initialization
Expand All @@ -19,8 +17,8 @@ let initializedRustBN: bn128 | undefined = undefined
*/
export async function createEVM(createOpts?: EVMOpts) {
const opts = createOpts ?? ({} as EVMOpts)
const bn128 = initializedRustBN ?? ((await initRustBN()) as bn128)
initializedRustBN = bn128

opts.bn254 = new NobleBN254()

if (opts.common === undefined) {
opts.common = new Common({ chain: Mainnet })
Expand All @@ -34,5 +32,5 @@ export async function createEVM(createOpts?: EVMOpts) {
opts.stateManager = new SimpleStateManager()
}

return new EVM(opts, bn128)
return new EVM(opts)
}
11 changes: 6 additions & 5 deletions packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ import type {
Blockchain,
CustomOpcode,
EVMBLSInterface,
EVMBN254Interface,
EVMEvents,
EVMInterface,
EVMOpts,
EVMResult,
EVMRunCallOpts,
EVMRunCodeOpts,
ExecResult,
bn128,
} from './types.js'
import type { Common, StateManagerInterface } from '@ethereumjs/common'

Expand Down Expand Up @@ -143,7 +143,7 @@ export class EVM implements EVMInterface {

protected readonly _emit: (topic: string, data: any) => Promise<void>

private _bn128: bn128
private _bn254: EVMBN254Interface

/**
*
Expand All @@ -156,7 +156,7 @@ export class EVM implements EVMInterface {
* @param opts The EVM options
* @param bn128 Initialized bn128 WASM object for precompile usage (internal)
*/
constructor(opts: EVMOpts, bn128: bn128) {
constructor(opts: EVMOpts) {
this.common = opts.common!
this.blockchain = opts.blockchain!
this.stateManager = opts.stateManager!
Expand All @@ -172,7 +172,6 @@ export class EVM implements EVMInterface {
}
}

this._bn128 = bn128
this.events = new AsyncEventEmitter()
this._optsCached = opts

Expand Down Expand Up @@ -215,10 +214,12 @@ export class EVM implements EVMInterface {
this.getActiveOpcodes()
this._precompiles = getActivePrecompiles(this.common, this._customPrecompiles)

// Precompile crypto libraries
if (this.common.isActivatedEIP(2537)) {
this._bls = opts.bls ?? new NobleBLS()
this._bls.init?.()
}
this._bn254 = opts.bn254!

this._emit = async (topic: string, data: any): Promise<void> => {
return new Promise((resolve) => this.events.emit(topic as keyof EVMEvents, data, resolve))
Expand Down Expand Up @@ -1085,7 +1086,7 @@ export class EVM implements EVMInterface {
stateManager: this.stateManager.shallowCopy(),
}
;(opts.stateManager as any).common = common
return new EVM(opts, this._bn128)
return new EVM(opts)
}

public getPerformanceLogs() {
Expand Down
3 changes: 3 additions & 0 deletions packages/evm/src/exceptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export enum ERROR {
BLS_12_381_INPUT_EMPTY = 'input is empty',
BLS_12_381_FP_NOT_IN_FIELD = 'fp point not in field',

// BN254 errors
BN254_FP_NOT_IN_FIELD = 'fp point not in field',

// Point Evaluation Errors
INVALID_COMMITMENT = 'kzg commitment does not match versioned hash',
INVALID_INPUTS = 'kzg inputs invalid',
Expand Down
10 changes: 8 additions & 2 deletions packages/evm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@ import { getOpcodesForHF } from './opcodes/index.js'
import {
MCLBLS,
NobleBLS,
NobleBN254,
type PrecompileInput,
RustBN254,
getActivePrecompiles,
} from './precompiles/index.js'

import type { InterpreterStep } from './interpreter.js'
import type {
EVMBLSInterface,
EVMBN254Interface,
EVMInterface,
EVMOpts,
EVMResult,
EVMRunCallOpts,
EVMRunCodeOpts,
ExecResult,
Log,
bn128,
} from './types.js'
export * from './logger.js'

export type {
bn128,
EVMBLSInterface,
EVMBN254Interface,
EVMInterface,
EVMOpts,
EVMResult,
Expand All @@ -46,6 +50,8 @@ export {
MCLBLS,
Message,
NobleBLS,
NobleBN254,
RustBN254,
validateEOF,
}

Expand Down
20 changes: 15 additions & 5 deletions packages/evm/src/precompiles/06-ecadd.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { bytesToHex, bytesToUnprefixedHex, hexToBytes, short } from '@ethereumjs/util'
import { bytesToHex, setLengthRight, short } from '@ethereumjs/util'

import { OOGResult } from '../evm.js'
import { EvmErrorResult, OOGResult } from '../evm.js'

import type { EVM } from '../evm.js'
import type { ExecResult } from '../types.js'
import type { PrecompileInput } from './types.js'

export function precompile06(opts: PrecompileInput): ExecResult {
const inputData = bytesToUnprefixedHex(opts.data.subarray(0, 128))

const gasUsed = opts.common.param('ecAddGas')
if (opts._debug !== undefined) {
opts._debug(
Expand All @@ -24,7 +22,19 @@ export function precompile06(opts: PrecompileInput): ExecResult {
return OOGResult(opts.gasLimit)
}

const returnData = hexToBytes((opts._EVM as EVM)['_bn128'].ec_add(inputData))
// > 128 bytes: chop off extra bytes
// < 128 bytes: right-pad with 0-s
const input = setLengthRight(opts.data.subarray(0, 128), 128)

let returnData
try {
returnData = (opts._EVM as EVM)['_bn254'].add(input)
} catch (e: any) {
if (opts._debug !== undefined) {
opts._debug(`ECADD (0x06) failed: ${e.message}`)
}
return EvmErrorResult(e, opts.gasLimit)
}

// check ecadd success or failure by comparing the output length
if (returnData.length !== 64) {
Expand Down
19 changes: 15 additions & 4 deletions packages/evm/src/precompiles/07-ecmul.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { bytesToHex, bytesToUnprefixedHex, hexToBytes, short } from '@ethereumjs/util'
import { bytesToHex, setLengthRight, short } from '@ethereumjs/util'

import { OOGResult } from '../evm.js'
import { EvmErrorResult, OOGResult } from '../evm.js'

import type { EVM } from '../evm.js'
import type { ExecResult } from '../types.js'
import type { PrecompileInput } from './types.js'

export function precompile07(opts: PrecompileInput): ExecResult {
const inputData = bytesToUnprefixedHex(opts.data.subarray(0, 128))
const gasUsed = opts.common.param('ecMulGas')
if (opts._debug !== undefined) {
opts._debug(
Expand All @@ -24,7 +23,19 @@ export function precompile07(opts: PrecompileInput): ExecResult {
return OOGResult(opts.gasLimit)
}

const returnData = hexToBytes((opts._EVM as EVM)['_bn128'].ec_mul(inputData))
// > 128 bytes: chop off extra bytes
// < 128 bytes: right-pad with 0-s
const input = setLengthRight(opts.data.subarray(0, 128), 128)

let returnData
try {
returnData = (opts._EVM as EVM)['_bn254'].mul(input)
} catch (e: any) {
if (opts._debug !== undefined) {
opts._debug(`ECMUL (0x07) failed: ${e.message}`)
}
return EvmErrorResult(e, opts.gasLimit)
}

// check ecmul success or failure by comparing the output length
if (returnData.length !== 64) {
Expand Down
Loading
Loading