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

Update rustbn-wasm usage #3304

Merged
merged 16 commits into from
Mar 8, 2024
Merged
16 changes: 9 additions & 7 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion packages/evm/examples/browser.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
const stateManager = new DefaultStateManager()
const blockchain = await Blockchain.create()

const evm = new EVM({
const evm = await EVM.create({
common,
stateManager,
blockchain,
Expand Down
10 changes: 7 additions & 3 deletions packages/evm/examples/eips.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Chain, Common } from '@ethereumjs/common'
import { EVM } from '@ethereumjs/evm'

const common = new Common({ chain: Chain.Mainnet, eips: [3074] })
const evm = new EVM({ common })
console.log(`EIP 3074 is active - ${evm.common.isActivatedEIP(3074)}`)
const main = async () => {
const common = new Common({ chain: Chain.Mainnet, eips: [3074] })
const evm = await EVM.create({ common })
console.log(`EIP 3074 is active - ${evm.common.isActivatedEIP(3074)}`)
}

main()
2 changes: 1 addition & 1 deletion packages/evm/examples/runCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const main = async () => {
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London })
const blockchain = await Blockchain.create()

const evm = new EVM({
const evm = await EVM.create({
common,
blockchain,
})
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/examples/simple.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { hexToBytes } from '@ethereumjs/util'
import { EVM } from '@ethereumjs/evm'

const evm = new EVM()
const main = async () => {
const evm = await EVM.create({})
const res = await evm.runCode({ code: hexToBytes('0x6001') }) // PUSH1 01 -- simple bytecode to push 1 onto the stack
console.log(res.executionGasUsed) // 3n
}
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/examples/withBlockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const main = async () => {
const stateManager = new DefaultStateManager()
const blockchain = await Blockchain.create()

const evm = new EVM({
const evm = await EVM.create({
common,
stateManager,
blockchain,
Expand Down
2 changes: 1 addition & 1 deletion packages/evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"@types/debug": "^4.1.9",
"debug": "^4.3.3",
"ethereum-cryptography": "^2.1.3",
"rustbn-wasm": "^0.2.0"
"rustbn-wasm": "^0.4.0"
},
"devDependencies": {
"@ethersproject/abi": "^5.0.12",
Expand Down
21 changes: 20 additions & 1 deletion packages/evm/src/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
zeros,
} from '@ethereumjs/util'
import debugDefault from 'debug'
import { initRustBN } from 'rustbn-wasm'

import { EOF, getEOFCode } from './eof.js'
import { ERROR, EvmError } from './exceptions.js'
Expand Down Expand Up @@ -137,7 +138,24 @@

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

constructor(opts: EVMOpts = {}) {
private bn128: {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does it make sense to create a EVM.create method similar to VM.create?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That thought definitely occurred to me while I was doing this. Let me take a look at our current usage of the EVM constructor and see if it makes sense. It would definitely simplify the API

ec_pairing: (input_str: string) => string
ec_add: (input_str: string) => string
ec_mul: (input_hex: string) => string
}

static async create(createOpts?: EVMOpts) {
const opts = createOpts ?? ({} as EVMOpts)
if (opts?.bn128 === undefined) {
opts.bn128 = await initRustBN()
Copy link
Member

Choose a reason for hiding this comment

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

Is it an idea to add something similar like customCrypto for these rustbn-wasm? Then we would treat them at the same foot as the crypto?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that but this isn't really "custom crypto". This is just our existing dependency but since it now requires an async constructor, I'm having to pass it in. In this case, I don't think it makes sense to instantiate it in Common or even attach it there. There isn't an alternative implementation of the crypto anywhere that I'm aware of aside from the one older ASM version we were previously using.

}
return new EVM(opts)
}
constructor(opts: EVMOpts) {
if (opts.bn128 === undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

This is breaking 🤔 Do we want this?

When we still had mcl-wasm I think we added an initPromise to the EVM. Whenever something was called, first this initPromise was awaited. In this case this initPromise would normally consist of the initRustBN in case the bn128 is not provided 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is breaking here? I guess it's not obvious to me why we would want to allow the EVM run without this dep? We've never allowed it before so doesn't make sense to allow it now to my way of thinking.

throw new Error('rustbn not provided')
}

Check warning on line 157 in packages/evm/src/evm.ts

View check run for this annotation

Codecov / codecov/patch

packages/evm/src/evm.ts#L156-L157

Added lines #L156 - L157 were not covered by tests
this.bn128 = opts.bn128 // Required to instantiate the EVM
this.events = new AsyncEventEmitter()

this._optsCached = opts
Expand Down Expand Up @@ -1079,6 +1097,7 @@
...this._optsCached,
common,
stateManager: this.stateManager.shallowCopy(),
bn128: this.bn128,
}
;(opts.stateManager as any).common = common
return new EVM(opts)
Expand Down
2 changes: 2 additions & 0 deletions packages/evm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import {
EVMRunCodeOpts,
ExecResult,
Log,
bn128,
} from './types.js'
export {
bn128,
EOF,
EVM,
EvmError,
Expand Down
4 changes: 2 additions & 2 deletions packages/evm/src/precompiles/06-ecadd.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { bytesToHex, bytesToUnprefixedHex, hexToBytes, short } from '@ethereumjs/util'
import { ec_add } from 'rustbn-wasm'

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

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

Expand All @@ -24,7 +24,7 @@ export function precompile06(opts: PrecompileInput): ExecResult {
return OOGResult(opts.gasLimit)
}

const returnData = hexToBytes(ec_add(inputData))
const returnData = hexToBytes((opts._EVM as EVM)['bn128'].ec_add(inputData))

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

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

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

Expand All @@ -24,7 +24,7 @@ export function precompile07(opts: PrecompileInput): ExecResult {
return OOGResult(opts.gasLimit)
}

const returnData = hexToBytes(ec_mul(inputData))
const returnData = hexToBytes((opts._EVM as EVM)['bn128'].ec_mul(inputData))

// check ecmul success or failure by comparing the output length
if (returnData.length !== 64) {
Expand Down
6 changes: 4 additions & 2 deletions packages/evm/src/precompiles/08-ecpairing.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { bytesToHex, bytesToUnprefixedHex, hexToBytes, short } from '@ethereumjs/util'
import { ec_pairing } from 'rustbn-wasm'

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

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

Expand All @@ -28,7 +28,9 @@ export function precompile08(opts: PrecompileInput): ExecResult {
return OOGResult(opts.gasLimit)
}

const returnData = hexToBytes(ec_pairing(bytesToUnprefixedHex(inputData)))
const returnData = hexToBytes(
(opts._EVM as EVM)['bn128'].ec_pairing(bytesToUnprefixedHex(inputData))
)

// check ecpairing success or failure by comparing the output length
if (returnData.length !== 32) {
Expand Down
17 changes: 17 additions & 0 deletions packages/evm/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ export type EVMProfilerOpts = {
* Options for instantiating a {@link EVM}.
*/
export interface EVMOpts {
/**
* The BN128 curve package (`rustbn-wasm`)
*/
bn128?: {
ec_pairing: (input_str: string) => string
ec_add: (input_str: string) => string
ec_mul: (input_hex: string) => string
}
/**
* Use a {@link Common} instance for EVM instantiation.
*
Expand Down Expand Up @@ -380,3 +388,12 @@ export class DefaultBlockchain implements Blockchain {
return this
}
}

/**
* The BN128 curve package (`rustbn-wasm`)
*/
export interface bn128 {
ec_pairing: (input_str: string) => string
ec_add: (input_str: string) => string
ec_mul: (input_hex: string) => string
}
2 changes: 1 addition & 1 deletion packages/evm/test/asyncEvents.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('async events', () => {
it('should work', async () => {
const caller = new Address(hexToBytes('0x00000000000000000000000000000000000000ee'))
const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Constantinople })
const evm = new EVM({
const evm = await EVM.create({
common,
})
evm.events.on('step', async (event, next) => {
Expand Down
6 changes: 3 additions & 3 deletions packages/evm/test/blobVersionedHashes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('BLOBHASH / access blobVersionedHashes in calldata', () => {
chain: 'custom',
hardfork: Hardfork.Cancun,
})
const evm = new EVM({
const evm = await EVM.create({
common,
})

Expand Down Expand Up @@ -44,7 +44,7 @@ describe(`BLOBHASH: access blobVersionedHashes within contract calls`, () => {
chain: 'custom',
hardfork: Hardfork.Cancun,
})
const evm = new EVM({
const evm = await EVM.create({
common,
})

Expand Down Expand Up @@ -95,7 +95,7 @@ describe(`BLOBHASH: access blobVersionedHashes in a CREATE/CREATE2 frame`, () =>
chain: 'custom',
hardfork: Hardfork.Cancun,
})
const evm = new EVM({
const evm = await EVM.create({
common,
})

Expand Down
4 changes: 2 additions & 2 deletions packages/evm/test/customCrypto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('custom crypto', () => {
}
const msg = Uint8Array.from([0, 1, 2, 3])
const common = new Common({ chain: Chain.Mainnet, customCrypto })
const evm = new EVM({ common })
const evm = await EVM.create({ common })
const addressStr = '0000000000000000000000000000000000000002'
const SHA256 = getActivePrecompiles(common).get(addressStr)!
const result = await SHA256({
Expand All @@ -46,7 +46,7 @@ describe('custom crypto', () => {
}
const msg = concatBytes(randomBytes(32), setLengthLeft(intToBytes(27), 32), randomBytes(32))
const common = new Common({ chain: Chain.Mainnet, customCrypto })
const evm = new EVM({ common })
const evm = await EVM.create({ common })
const addressStr = '0000000000000000000000000000000000000001'
const ECRECOVER = getActivePrecompiles(common).get(addressStr)!
const result = await ECRECOVER({
Expand Down
18 changes: 6 additions & 12 deletions packages/evm/test/customOpcodes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ describe('VM: custom opcodes', () => {
}

it('should add custom opcodes to the EVM', async () => {
const evm = new EVM({
customOpcodes: [testOpcode],
})
const evm = await EVM.create({ customOpcodes: [testOpcode] })
const gas = 123456
let correctOpcodeName = false
evm.events.on('step', (e: InterpreterStep) => {
Expand All @@ -45,7 +43,7 @@ describe('VM: custom opcodes', () => {
})

it('should delete opcodes from the EVM', async () => {
const evm = new EVM({
const evm = await EVM.create({
customOpcodes: [{ opcode: 0x20 }], // deletes KECCAK opcode
})
const gas = BigInt(123456)
Expand All @@ -59,7 +57,7 @@ describe('VM: custom opcodes', () => {
it('should not override default opcodes', async () => {
// This test ensures that always the original opcode map is used
// Thus, each time you recreate a EVM, it is in a clean state
const evm = new EVM({
const evm = await EVM.create({
customOpcodes: [{ opcode: 0x01 }], // deletes ADD opcode
})
const gas = BigInt(123456)
Expand All @@ -69,7 +67,7 @@ describe('VM: custom opcodes', () => {
})
assert.ok(res.executionGasUsed === gas, 'successfully deleted opcode')

const evmDefault = new EVM({})
const evmDefault = await EVM.create({})

// PUSH 04
// PUSH 01
Expand All @@ -88,9 +86,7 @@ describe('VM: custom opcodes', () => {

it('should override opcodes in the EVM', async () => {
testOpcode.opcode = 0x20 // Overrides KECCAK
const evm = new EVM({
customOpcodes: [testOpcode],
})
const evm = await EVM.create({ customOpcodes: [testOpcode] })
const gas = 123456
const res = await evm.runCode({
code: hexToBytes('0x20'),
Expand All @@ -113,9 +109,7 @@ describe('VM: custom opcodes', () => {
},
}

const evm = new EVM({
customOpcodes: [testOpcode],
})
const evm = await EVM.create({ customOpcodes: [testOpcode] })
evm.events.on('beforeMessage', () => {})
evm.events.on('beforeMessage', () => {})
const evmCopy = evm.shallowCopy()
Expand Down
Loading
Loading