diff --git a/config/eslint.cjs b/config/eslint.cjs index 1b5f05b615..3d20388403 100644 --- a/config/eslint.cjs +++ b/config/eslint.cjs @@ -132,7 +132,7 @@ module.exports = { }, }, { - files: ['**/examples/**/*.ts', '**/benchmarks/*.ts', ], + files: ['**/examples/**/*.ts', '**/examples/**/*.js','**/benchmarks/*.ts'], rules: { 'implicit-dependencies/no-implicit': 'off', 'import/no-extraneous-dependencies': 'off', diff --git a/packages/client/test/sim/snapsync.spec.ts b/packages/client/test/sim/snapsync.spec.ts index ba480b905b..65ca364e04 100644 --- a/packages/client/test/sim/snapsync.spec.ts +++ b/packages/client/test/sim/snapsync.spec.ts @@ -49,6 +49,49 @@ let stateManager: MerkleStateManager | undefined = undefined const EOATransferToAccount = '0x3dA33B9A0894b908DdBb00d96399e506515A1009' let EOATransferToBalance = BigInt(0) +async function createSnapClient( + common: any, + customGenesisState: any, + bootnodes: any, + peerBeaconUrl: any, + datadir: any, +) { + // Turn on `debug` logs, defaults to all client logging + debug.enable(process.env.DEBUG_SNAP ?? '') + const logger = getLogger({ logLevel: 'debug' }) + const config = new Config({ + common, + bootnodes, + multiaddrs: [], + logger, + accountCache: 10000, + storageCache: 1000, + discDns: false, + discV4: false, + port: 30304, + enableSnapSync: true, + // syncmode: 'none', + // Keep the single job sync range high as the state is not big + maxAccountRange: (BigInt(2) ** BigInt(256) - BigInt(1)) / BigInt(10), + maxFetcherJobs: 10, + }) + const peerConnectedPromise = new Promise((resolve) => { + config.events.once(Event.PEER_CONNECTED, (peer: any) => resolve(peer)) + }) + const snapSyncCompletedPromise = new Promise((resolve) => { + config.events.once( + Event.SYNC_SNAPSYNC_COMPLETE, + (stateRoot: Uint8Array, stateManager: MerkleStateManager) => + resolve([stateRoot, stateManager]), + ) + }) + + const ejsInlineClient = await createInlineClient(config, common, customGenesisState, datadir) + const beaconSyncRelayer = await setupEngineUpdateRelay(ejsInlineClient, peerBeaconUrl) + + return { ejsInlineClient, peerConnectedPromise, snapSyncCompletedPromise, beaconSyncRelayer } +} + export async function runTx(data: PrefixedHexString | '', to?: PrefixedHexString, value?: bigint) { return runTxHelper({ client, common, sender, pkey }, data, to, value) } @@ -238,49 +281,6 @@ describe('simple mainnet test run', async () => { }, 60_000) }) -async function createSnapClient( - common: any, - customGenesisState: any, - bootnodes: any, - peerBeaconUrl: any, - datadir: any, -) { - // Turn on `debug` logs, defaults to all client logging - debug.enable(process.env.DEBUG_SNAP ?? '') - const logger = getLogger({ logLevel: 'debug' }) - const config = new Config({ - common, - bootnodes, - multiaddrs: [], - logger, - accountCache: 10000, - storageCache: 1000, - discDns: false, - discV4: false, - port: 30304, - enableSnapSync: true, - // syncmode: 'none', - // Keep the single job sync range high as the state is not big - maxAccountRange: (BigInt(2) ** BigInt(256) - BigInt(1)) / BigInt(10), - maxFetcherJobs: 10, - }) - const peerConnectedPromise = new Promise((resolve) => { - config.events.once(Event.PEER_CONNECTED, (peer: any) => resolve(peer)) - }) - const snapSyncCompletedPromise = new Promise((resolve) => { - config.events.once( - Event.SYNC_SNAPSYNC_COMPLETE, - (stateRoot: Uint8Array, stateManager: MerkleStateManager) => - resolve([stateRoot, stateManager]), - ) - }) - - const ejsInlineClient = await createInlineClient(config, common, customGenesisState, datadir) - const beaconSyncRelayer = await setupEngineUpdateRelay(ejsInlineClient, peerBeaconUrl) - - return { ejsInlineClient, peerConnectedPromise, snapSyncCompletedPromise, beaconSyncRelayer } -} - process.on('uncaughtException', (err: any, origin: any) => { console.log({ err, origin }) process.exit() diff --git a/packages/evm/examples/decode-opcodes.ts b/packages/evm/examples/decode-opcodes.ts index 429dd9560a..0fefe6349d 100644 --- a/packages/evm/examples/decode-opcodes.ts +++ b/packages/evm/examples/decode-opcodes.ts @@ -11,8 +11,19 @@ const opcodes = getOpcodesForHF(common).opcodes const data = '0x6107608061000e6000396000f30060003560e060020a90048063141961bc1461006e57806319ac74bd' -nameOpCodes(hexToBytes(data)) +function pad(num: number, size: number) { + let s = num + '' + while (s.length < size) s = '0' + s + return s +} + +function log(num: number, base: number) { + return Math.log(num) / Math.log(base) +} +function roundLog(num: number, base: number) { + return Math.ceil(log(num, base)) +} function nameOpCodes(raw: Uint8Array) { let pushData = new Uint8Array() @@ -39,16 +50,4 @@ function nameOpCodes(raw: Uint8Array) { } } -function pad(num: number, size: number) { - let s = num + '' - while (s.length < size) s = '0' + s - return s -} - -function log(num: number, base: number) { - return Math.log(num) / Math.log(base) -} - -function roundLog(num: number, base: number) { - return Math.ceil(log(num, base)) -} +nameOpCodes(hexToBytes(data)) diff --git a/packages/evm/src/eof/verify.ts b/packages/evm/src/eof/verify.ts index 0d46057658..ac0e632050 100644 --- a/packages/evm/src/eof/verify.ts +++ b/packages/evm/src/eof/verify.ts @@ -43,6 +43,7 @@ export function verifyCode( evm: EVM, mode: ContainerSectionType = ContainerSectionType.RuntimeCode, ) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define return validateOpcodes(container, evm, mode) } diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index f7bc9f5cdb..1983bca46a 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ import { Hardfork } from '@ethereumjs/common' import { Account, @@ -1191,3 +1192,4 @@ export function defaultBlock(): Block { }, } } +/* eslint-enable @typescript-eslint/no-use-before-define */ diff --git a/packages/evm/src/opcodes/util.ts b/packages/evm/src/opcodes/util.ts index 1dec494e1e..a4206f32f8 100644 --- a/packages/evm/src/opcodes/util.ts +++ b/packages/evm/src/opcodes/util.ts @@ -24,6 +24,42 @@ import type { Address } from '@ethereumjs/util' const MASK_160 = (BIGINT_1 << BIGINT_160) - BIGINT_1 +export function mod(a: bigint, b: bigint) { + let r = a % b + if (r < BIGINT_0) { + r = b + r + } + return r +} + +export function fromTwos(a: bigint) { + return BigInt.asIntN(256, a) +} + +export function toTwos(a: bigint) { + return BigInt.asUintN(256, a) +} + +export function abs(a: bigint) { + if (a > 0) { + return a + } + return a * BIGINT_NEG1 +} + +const N = BigInt(115792089237316195423570985008687907853269984665640564039457584007913129639936) +export function exponentiation(bas: bigint, exp: bigint) { + let t = BIGINT_1 + while (exp > BIGINT_0) { + if (exp % BIGINT_2 !== BIGINT_0) { + t = (t * bas) % N + } + bas = (bas * bas) % N + exp = exp / BIGINT_2 + } + return t +} + /** * Create an address from a stack item (256 bit integer). * This wrapper ensures that the value is masked to 160 bits. @@ -235,39 +271,3 @@ export function updateSstoreGas( return common.param('sstoreSetGas') } } - -export function mod(a: bigint, b: bigint) { - let r = a % b - if (r < BIGINT_0) { - r = b + r - } - return r -} - -export function fromTwos(a: bigint) { - return BigInt.asIntN(256, a) -} - -export function toTwos(a: bigint) { - return BigInt.asUintN(256, a) -} - -export function abs(a: bigint) { - if (a > 0) { - return a - } - return a * BIGINT_NEG1 -} - -const N = BigInt(115792089237316195423570985008687907853269984665640564039457584007913129639936) -export function exponentiation(bas: bigint, exp: bigint) { - let t = BIGINT_1 - while (exp > BIGINT_0) { - if (exp % BIGINT_2 !== BIGINT_0) { - t = (t * bas) % N - } - bas = (bas * bas) % N - exp = exp / BIGINT_2 - } - return t -} diff --git a/packages/evm/src/precompiles/bls12_381/mcl.ts b/packages/evm/src/precompiles/bls12_381/mcl.ts index 133ef18cbe..5fe748f9e3 100644 --- a/packages/evm/src/precompiles/bls12_381/mcl.ts +++ b/packages/evm/src/precompiles/bls12_381/mcl.ts @@ -81,6 +81,31 @@ function BLS12_381_FromG1Point(input: any): Uint8Array { return concatBytes(xBytes, yBytes) } +// input: two 64-byte buffers +// output: a mcl Fp2 point + +function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array, mcl: any): any { + // check if the coordinates are in the field + if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { + throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + } + if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { + throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + } + + const fp_x = new mcl.Fp() + const fp_y = new mcl.Fp() + + const fp2 = new mcl.Fp2() + fp_x.setStr(bytesToUnprefixedHex(fpXCoordinate.subarray(16)), 16) + fp_y.setStr(bytesToUnprefixedHex(fpYCoordinate.subarray(16)), 16) + + fp2.set_a(fp_x) + fp2.set_b(fp_y) + + return fp2 +} + /** * Converts an Uint8Array to a MCL G2 point. Raises errors if the point is not on the curve * and (if activated) if the point is in the subgroup / order check. @@ -175,31 +200,6 @@ function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array, mcl: any): any { return fp } -// input: two 64-byte buffers -// output: a mcl Fp2 point - -function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array, mcl: any): any { - // check if the coordinates are in the field - if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) - } - if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) - } - - const fp_x = new mcl.Fp() - const fp_y = new mcl.Fp() - - const fp2 = new mcl.Fp2() - fp_x.setStr(bytesToUnprefixedHex(fpXCoordinate.subarray(16)), 16) - fp_y.setStr(bytesToUnprefixedHex(fpYCoordinate.subarray(16)), 16) - - fp2.set_a(fp_x) - fp2.set_b(fp_y) - - return fp2 -} - /** * Implementation of the `EVMBLSInterface` using the `mcl-wasm` WASM `mcl` wrapper library, * see https://github.com/herumi/mcl-wasm. diff --git a/packages/evm/src/precompiles/bls12_381/noble.ts b/packages/evm/src/precompiles/bls12_381/noble.ts index ff6576ceb3..211f16afd1 100644 --- a/packages/evm/src/precompiles/bls12_381/noble.ts +++ b/packages/evm/src/precompiles/bls12_381/noble.ts @@ -36,6 +36,21 @@ type AffinePoint = { const G1_ZERO = bls12_381.G1.ProjectivePoint.ZERO const G2_ZERO = bls12_381.G2.ProjectivePoint.ZERO +function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { + // check if the coordinates are in the field + if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { + throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + } + if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { + throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) + } + + const fpBytes = concatBytes(fpXCoordinate.subarray(16), fpYCoordinate.subarray(16)) + + const FP = bls12_381.fields.Fp2.fromBytes(fpBytes) + return FP +} + /** * Converts an Uint8Array to a Noble G1 point. Raises errors if the point is not on the curve * and (if activated) if the point is in the subgroup / order check. @@ -159,21 +174,6 @@ function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array) { return FP } -function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { - // check if the coordinates are in the field - if (bytesToBigInt(fpXCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) - } - if (bytesToBigInt(fpYCoordinate) >= BLS_FIELD_MODULUS) { - throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) - } - - const fpBytes = concatBytes(fpXCoordinate.subarray(16), fpYCoordinate.subarray(16)) - - const FP = bls12_381.fields.Fp2.fromBytes(fpBytes) - return FP -} - /** * Implementation of the `EVMBLSInterface` using the `ethereum-cryptography (`@noble/curves`) * JS library, see https://github.com/ethereum/js-ethereum-cryptography. diff --git a/packages/evm/src/precompiles/bn254/noble.ts b/packages/evm/src/precompiles/bn254/noble.ts index d82745d802..7afa2999ec 100644 --- a/packages/evm/src/precompiles/bn254/noble.ts +++ b/packages/evm/src/precompiles/bn254/noble.ts @@ -67,6 +67,20 @@ function toFrPoint(input: Uint8Array): bigint { return Fr } +function toFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { + if (bytesToBigInt(fpXCoordinate) >= bn254.fields.Fp2.ORDER) { + throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) + } + if (bytesToBigInt(fpYCoordinate) >= bn254.fields.Fp2.ORDER) { + throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) + } + + const fpBytes = concatBytes(fpXCoordinate, fpYCoordinate) + + const FP = bn254.fields.Fp2.fromBytes(fpBytes) + return FP +} + /** * Converts an Uint8Array to a Noble G2 point. Raises errors if the point is not on the curve * and (if activated) if the point is in the subgroup / order check. @@ -104,20 +118,6 @@ function toG2Point(input: Uint8Array) { return pG2 } -function toFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array) { - if (bytesToBigInt(fpXCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) - } - if (bytesToBigInt(fpYCoordinate) >= bn254.fields.Fp2.ORDER) { - throw new EvmError(ERROR.BN254_FP_NOT_IN_FIELD) - } - - const fpBytes = concatBytes(fpXCoordinate, fpYCoordinate) - - const FP = bn254.fields.Fp2.fromBytes(fpBytes) - return FP -} - /** * Implementation of the `EVMBN254Interface` using the `ethereum-cryptography (`@noble/curves`) * JS library, see https://github.com/ethereum/js-ethereum-cryptography. diff --git a/packages/evm/src/verkleAccessWitness.ts b/packages/evm/src/verkleAccessWitness.ts index be83cbaba2..65a9f77ad7 100644 --- a/packages/evm/src/verkleAccessWitness.ts +++ b/packages/evm/src/verkleAccessWitness.ts @@ -44,6 +44,41 @@ type ChunkAccessEvent = StemAccessEvent & { fill?: boolean } // Since stem is pedersen hashed, it is useful to maintain the reverse relationship type StemMeta = { address: Address; treeIndex: number | bigint } +export function decodeAccessedState( + treeIndex: number | bigint, + chunkIndex: number, +): VerkleAccessedState { + const position = BigInt(treeIndex) * BigInt(VERKLE_NODE_WIDTH) + BigInt(chunkIndex) + switch (position) { + case BigInt(0): + return { type: VerkleAccessedStateType.BasicData } + case BigInt(1): + return { type: VerkleAccessedStateType.CodeHash } + default: + if (position < VERKLE_HEADER_STORAGE_OFFSET) { + throw Error(`No attribute yet stored >=2 and <${VERKLE_HEADER_STORAGE_OFFSET}`) + } + + if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) { + const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET) + return { type: VerkleAccessedStateType.Storage, slot } + } else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) { + const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET + return { + type: VerkleAccessedStateType.Code, + codeOffset: codeChunkIdx * 31, + } + } else if (position >= VERKLE_MAIN_STORAGE_OFFSET) { + const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET) + return { type: VerkleAccessedStateType.Storage, slot } + } else { + throw Error( + `Invalid treeIndex=${treeIndex} chunkIndex=${chunkIndex} for verkle tree access`, + ) + } + } +} + export class VerkleAccessWitness implements VerkleAccessWitnessInterface { stems: Map chunks: Map @@ -310,38 +345,3 @@ export class VerkleAccessWitness implements VerkleAccessWitnessInterface { } } } - -export function decodeAccessedState( - treeIndex: number | bigint, - chunkIndex: number, -): VerkleAccessedState { - const position = BigInt(treeIndex) * BigInt(VERKLE_NODE_WIDTH) + BigInt(chunkIndex) - switch (position) { - case BigInt(0): - return { type: VerkleAccessedStateType.BasicData } - case BigInt(1): - return { type: VerkleAccessedStateType.CodeHash } - default: - if (position < VERKLE_HEADER_STORAGE_OFFSET) { - throw Error(`No attribute yet stored >=2 and <${VERKLE_HEADER_STORAGE_OFFSET}`) - } - - if (position >= VERKLE_HEADER_STORAGE_OFFSET && position < VERKLE_CODE_OFFSET) { - const slot = position - BigInt(VERKLE_HEADER_STORAGE_OFFSET) - return { type: VerkleAccessedStateType.Storage, slot } - } else if (position >= VERKLE_CODE_OFFSET && position < VERKLE_MAIN_STORAGE_OFFSET) { - const codeChunkIdx = Number(position) - VERKLE_CODE_OFFSET - return { - type: VerkleAccessedStateType.Code, - codeOffset: codeChunkIdx * 31, - } - } else if (position >= VERKLE_MAIN_STORAGE_OFFSET) { - const slot = BigInt(position - VERKLE_MAIN_STORAGE_OFFSET) - return { type: VerkleAccessedStateType.Storage, slot } - } else { - throw Error( - `Invalid treeIndex=${treeIndex} chunkIndex=${chunkIndex} for verkle tree access`, - ) - } - } -} diff --git a/packages/rlp/src/index.ts b/packages/rlp/src/index.ts index b7d9546425..89e731b385 100644 --- a/packages/rlp/src/index.ts +++ b/packages/rlp/src/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ export type Input = string | number | bigint | Uint8Array | Array | null | undefined export type NestedUint8Array = Array @@ -7,45 +8,6 @@ export interface Decoded { remainder: Uint8Array } -/** - * RLP Encoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ - * This function takes in data, converts it to Uint8Array if not, - * and adds a length for recursion. - * @param input Will be converted to Uint8Array - * @returns Uint8Array of encoded data - **/ -export function encode(input: Input): Uint8Array { - if (Array.isArray(input)) { - const output: Uint8Array[] = [] - let outputLength = 0 - for (let i = 0; i < input.length; i++) { - const encoded = encode(input[i]) - output.push(encoded) - outputLength += encoded.length - } - return concatBytes(encodeLength(outputLength, 192), ...output) - } - const inputBuf = toBytes(input) - if (inputBuf.length === 1 && inputBuf[0] < 128) { - return inputBuf - } - return concatBytes(encodeLength(inputBuf.length, 128), inputBuf) -} - -/** - * Slices a Uint8Array, throws if the slice goes out-of-bounds of the Uint8Array. - * E.g. `safeSlice(hexToBytes('aa'), 1, 2)` will throw. - * @param input - * @param start - * @param end - */ -function safeSlice(input: Uint8Array, start: number, end: number) { - if (end > input.length) { - throw new Error('invalid RLP (safeSlice): end slice of Uint8Array out-of-bounds') - } - return input.slice(start, end) -} - /** * Parse integers. Check if there is no leading zeros * @param v The value to parse @@ -68,32 +30,17 @@ function encodeLength(len: number, offset: number): Uint8Array { } /** - * RLP Decoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ - * @param input Will be converted to Uint8Array - * @param stream Is the input a stream (false by default) - * @returns decoded Array of Uint8Arrays containing the original message - **/ -export function decode(input: Input, stream?: false): Uint8Array | NestedUint8Array -export function decode(input: Input, stream?: true): Decoded -export function decode(input: Input, stream = false): Uint8Array | NestedUint8Array | Decoded { - if (typeof input === 'undefined' || input === null || (input as any).length === 0) { - return Uint8Array.from([]) - } - - const inputBytes = toBytes(input) - const decoded = _decode(inputBytes) - - if (stream) { - return { - data: decoded.data, - remainder: decoded.remainder.slice(), - } - } - if (decoded.remainder.length !== 0) { - throw new Error('invalid RLP: remainder must be zero') + * Slices a Uint8Array, throws if the slice goes out-of-bounds of the Uint8Array. + * E.g. `safeSlice(hexToBytes('aa'), 1, 2)` will throw. + * @param input + * @param start + * @param end + */ +function safeSlice(input: Uint8Array, start: number, end: number) { + if (end > input.length) { + throw new Error('invalid RLP (safeSlice): end slice of Uint8Array out-of-bounds') } - - return decoded.data + return input.slice(start, end) } /** Decode an input with RLP */ @@ -305,6 +252,60 @@ function toBytes(v: Input): Uint8Array { throw new Error('toBytes: received unsupported type ' + typeof v) } +/** + * RLP Encoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ + * This function takes in data, converts it to Uint8Array if not, + * and adds a length for recursion. + * @param input Will be converted to Uint8Array + * @returns Uint8Array of encoded data + **/ +export function encode(input: Input): Uint8Array { + if (Array.isArray(input)) { + const output: Uint8Array[] = [] + let outputLength = 0 + for (let i = 0; i < input.length; i++) { + const encoded = encode(input[i]) + output.push(encoded) + outputLength += encoded.length + } + return concatBytes(encodeLength(outputLength, 192), ...output) + } + const inputBuf = toBytes(input) + if (inputBuf.length === 1 && inputBuf[0] < 128) { + return inputBuf + } + return concatBytes(encodeLength(inputBuf.length, 128), inputBuf) +} + +/** + * RLP Decoding based on https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/ + * @param input Will be converted to Uint8Array + * @param stream Is the input a stream (false by default) + * @returns decoded Array of Uint8Arrays containing the original message + **/ +export function decode(input: Input, stream?: false): Uint8Array | NestedUint8Array +export function decode(input: Input, stream?: true): Decoded +export function decode(input: Input, stream = false): Uint8Array | NestedUint8Array | Decoded { + if (typeof input === 'undefined' || input === null || (input as any).length === 0) { + return Uint8Array.from([]) + } + + const inputBytes = toBytes(input) + const decoded = _decode(inputBytes) + + if (stream) { + return { + data: decoded.data, + remainder: decoded.remainder.slice(), + } + } + if (decoded.remainder.length !== 0) { + throw new Error('invalid RLP: remainder must be zero') + } + + return decoded.data +} + export const utils = { bytesToHex, concatBytes, @@ -313,3 +314,5 @@ export const utils = { } export const RLP = { encode, decode } + +/* eslint-enable @typescript-eslint/no-use-before-define */ diff --git a/packages/statemanager/test/testdata/providerData/mockProvider.ts b/packages/statemanager/test/testdata/providerData/mockProvider.ts index 708bdb8050..6b52798ac6 100644 --- a/packages/statemanager/test/testdata/providerData/mockProvider.ts +++ b/packages/statemanager/test/testdata/providerData/mockProvider.ts @@ -14,6 +14,41 @@ export type JSONReturnType = { eth_getBlockByNumber: { id: number; result: JSONBlock } eth_getTransactionByHash: { id: number; result: any } } + +const getProofValues = async (params: [address: string, _: [], blockTag: bigint | string]) => { + const [address, _slot, blockTag] = params + try { + const { account } = await import(`./accounts/${address}.ts`) + return account[blockTag.toString() ?? 'latest'] + } catch { + return { + address, + balance: '0x0', + codeHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + nonce: '0x0', + storageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + storageProof: [], + } + } +} + +const getBlockValues = async (params: [blockTag: string, _: boolean]) => { + const [blockTag, _] = params + if (blockTag.slice(0, 2) !== '0x') + return { + number: 'latest', + stateRoot: '0x2ffb7ec5bbe8616c24a222737f0817f389d00ab9268f9574e0b7dfe251fbfa05', + } + const { block } = await import(`./blocks/block${blockTag}.ts`) + return block +} + +const getTransactionData = async (params: [txHash: string]) => { + const [txHash] = params + const { tx } = await import(`./transactions/${txHash}.ts`) + return tx +} + export const getValues = async ( method: Method, id: number, @@ -58,37 +93,3 @@ export const getValues = async ( throw new Error(`${method} not supported in tests`) } } - -const getProofValues = async (params: [address: string, _: [], blockTag: bigint | string]) => { - const [address, _slot, blockTag] = params - try { - const { account } = await import(`./accounts/${address}.ts`) - return account[blockTag.toString() ?? 'latest'] - } catch { - return { - address, - balance: '0x0', - codeHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - nonce: '0x0', - storageHash: '0x0000000000000000000000000000000000000000000000000000000000000000', - storageProof: [], - } - } -} - -const getBlockValues = async (params: [blockTag: string, _: boolean]) => { - const [blockTag, _] = params - if (blockTag.slice(0, 2) !== '0x') - return { - number: 'latest', - stateRoot: '0x2ffb7ec5bbe8616c24a222737f0817f389d00ab9268f9574e0b7dfe251fbfa05', - } - const { block } = await import(`./blocks/block${blockTag}.ts`) - return block -} - -const getTransactionData = async (params: [txHash: string]) => { - const [txHash] = params - const { tx } = await import(`./transactions/${txHash}.ts`) - return tx -} diff --git a/packages/vm/examples/run-blockchain.ts b/packages/vm/examples/run-blockchain.ts index 50a92d051b..a5350df5c0 100644 --- a/packages/vm/examples/run-blockchain.ts +++ b/packages/vm/examples/run-blockchain.ts @@ -26,6 +26,37 @@ import type { Block } from '@ethereumjs/block' import type { Blockchain, ConsensusDict } from '@ethereumjs/blockchain' import type { VM } from '@ethereumjs/vm' +async function setupPreConditions(vm: VM, data: any) { + await vm.stateManager.checkpoint() + + for (const [addr, acct] of Object.entries(data.pre)) { + const { nonce, balance, storage, code } = acct as any + + const address = new Address(hexToBytes(addr)) + const account = createAccount({ nonce, balance }) + await vm.stateManager.putAccount(address, account) + + for (const [key, val] of Object.entries(storage)) { + const storageKey = setLengthLeft(hexToBytes(key), 32) + const storageVal = hexToBytes(val as string) + await vm.stateManager.putStorage(address, storageKey, storageVal) + } + + const codeBuf = hexToBytes('0x' + code) + await vm.stateManager.putCode(address, codeBuf) + } + + await vm.stateManager.commit() +} + +async function putBlocks(blockchain: Blockchain, common: Common, data: typeof testData) { + for (const blockData of data.blocks) { + const blockRlp = toBytes(blockData.rlp) + const block = createBlockFromRLP(blockRlp, { common }) + await blockchain.putBlock(block) + } +} + async function main() { const common = new Common({ chain: Mainnet, hardfork: testData.network.toLowerCase() }) const validatePow = common.consensusType() === ConsensusType.ProofOfWork @@ -63,35 +94,4 @@ async function main() { console.log('Expected:', testData.lastblockhash) } -async function setupPreConditions(vm: VM, data: any) { - await vm.stateManager.checkpoint() - - for (const [addr, acct] of Object.entries(data.pre)) { - const { nonce, balance, storage, code } = acct as any - - const address = new Address(hexToBytes(addr)) - const account = createAccount({ nonce, balance }) - await vm.stateManager.putAccount(address, account) - - for (const [key, val] of Object.entries(storage)) { - const storageKey = setLengthLeft(hexToBytes(key), 32) - const storageVal = hexToBytes(val as string) - await vm.stateManager.putStorage(address, storageKey, storageVal) - } - - const codeBuf = hexToBytes('0x' + code) - await vm.stateManager.putCode(address, codeBuf) - } - - await vm.stateManager.commit() -} - -async function putBlocks(blockchain: Blockchain, common: Common, data: typeof testData) { - for (const blockData of data.blocks) { - const blockRlp = toBytes(blockData.rlp) - const block = createBlockFromRLP(blockRlp, { common }) - await blockchain.putBlock(block) - } -} - void main() diff --git a/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts b/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts index 1913fffd4c..6ad293eba1 100644 --- a/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4788-beaconroot.spec.ts @@ -37,6 +37,14 @@ const common = new Common({ const pkey = hexToBytes(`0x${'20'.repeat(32)}`) const contractAddress = createAddressFromString('0x' + 'c0de'.repeat(10)) +/** + * Get call status saved in the contract + */ +async function getCallStatus(vm: VM) { + const stat = await vm.stateManager.getStorage(contractAddress, new Uint8Array(32)) + return bytesToBigInt(stat) +} + function beaconrootBlock( blockroot: bigint, timestamp: BigIntLike, @@ -118,14 +126,6 @@ async function runBlock(block: Block) { } } -/** - * Get call status saved in the contract - */ -async function getCallStatus(vm: VM) { - const stat = await vm.stateManager.getStorage(contractAddress, new Uint8Array(32)) - return bytesToBigInt(stat) -} - /** * Run block test * @param input diff --git a/packages/vm/test/api/EIPs/eip-6110.spec.ts b/packages/vm/test/api/EIPs/eip-6110.spec.ts index 1ceb939bab..28fda2b593 100644 --- a/packages/vm/test/api/EIPs/eip-6110.spec.ts +++ b/packages/vm/test/api/EIPs/eip-6110.spec.ts @@ -19,6 +19,22 @@ import { setupVM } from '../utils.js' import type { PrefixedHexString } from '@ethereumjs/util' +function parseDepositRequest(requestData: Uint8Array) { + const pubkey = requestData.subarray(0, 48) + const withdrawalCredentials = requestData.subarray(48, 48 + 32) + const amount = requestData.subarray(48 + 32, 48 + 32 + 8) + const signature = requestData.subarray(48 + 32 + 8, 48 + 32 + 8 + 96) + const index = requestData.subarray(48 + 32 + 8 + 96, 48 + 32 + 8 + 96 + 8) + + return { + pubkey, + withdrawalCredentials, + amount, + signature, + index, + } +} + const depositContractByteCode = hexToBytes( '0x60806040526004361061003f5760003560e01c806301ffc9a71461004457806322895118146100a4578063621fd130146101ba578063c5f2892f14610244575b600080fd5b34801561005057600080fd5b506100906004803603602081101561006757600080fd5b50357fffffffff000000000000000000000000000000000000000000000000000000001661026b565b604080519115158252519081900360200190f35b6101b8600480360360808110156100ba57600080fd5b8101906020810181356401000000008111156100d557600080fd5b8201836020820111156100e757600080fd5b8035906020019184600183028401116401000000008311171561010957600080fd5b91939092909160208101903564010000000081111561012757600080fd5b82018360208201111561013957600080fd5b8035906020019184600183028401116401000000008311171561015b57600080fd5b91939092909160208101903564010000000081111561017957600080fd5b82018360208201111561018b57600080fd5b803590602001918460018302840111640100000000831117156101ad57600080fd5b919350915035610304565b005b3480156101c657600080fd5b506101cf6110b5565b6040805160208082528351818301528351919283929083019185019080838360005b838110156102095781810151838201526020016101f1565b50505050905090810190601f1680156102365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561025057600080fd5b506102596110c7565b60408051918252519081900360200190f35b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a70000000000000000000000000000000000000000000000000000000014806102fe57507fffffffff0000000000000000000000000000000000000000000000000000000082167f8564090700000000000000000000000000000000000000000000000000000000145b92915050565b6030861461035d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118056026913960400191505060405180910390fd5b602084146103b6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603681526020018061179c6036913960400191505060405180910390fd5b6060821461040f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260298152602001806118786029913960400191505060405180910390fd5b670de0b6b3a7640000341015610470576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260268152602001806118526026913960400191505060405180910390fd5b633b9aca003406156104cd576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260338152602001806117d26033913960400191505060405180910390fd5b633b9aca00340467ffffffffffffffff811115610535576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061182b6027913960400191505060405180910390fd5b6060610540826114ba565b90507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c589898989858a8a6105756020546114ba565b6040805160a0808252810189905290819060208201908201606083016080840160c085018e8e80828437600083820152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690910187810386528c815260200190508c8c808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01690920188810386528c5181528c51602091820193918e019250908190849084905b83811015610648578181015183820152602001610630565b50505050905090810190601f1680156106755780820380516001836020036101000a031916815260200191505b5086810383528881526020018989808284376000838201819052601f9091017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169092018881038452895181528951602091820193918b019250908190849084905b838110156106ef5781810151838201526020016106d7565b50505050905090810190601f16801561071c5780820380516001836020036101000a031916815260200191505b509d505050505050505050505050505060405180910390a1600060028a8a600060801b604051602001808484808284377fffffffffffffffffffffffffffffffff0000000000000000000000000000000090941691909301908152604080517ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0818403018152601090920190819052815191955093508392506020850191508083835b602083106107fc57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016107bf565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610859573d6000803e3d6000fd5b5050506040513d602081101561086e57600080fd5b5051905060006002806108846040848a8c6116fe565b6040516020018083838082843780830192505050925050506040516020818303038152906040526040518082805190602001908083835b602083106108f857805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016108bb565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610955573d6000803e3d6000fd5b5050506040513d602081101561096a57600080fd5b5051600261097b896040818d6116fe565b60405160009060200180848480828437919091019283525050604080518083038152602092830191829052805190945090925082918401908083835b602083106109f457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe090920191602091820191016109b7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610a51573d6000803e3d6000fd5b5050506040513d6020811015610a6657600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610ada57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610a9d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610b37573d6000803e3d6000fd5b5050506040513d6020811015610b4c57600080fd5b50516040805160208101858152929350600092600292839287928f928f92018383808284378083019250505093505050506040516020818303038152906040526040518082805190602001908083835b60208310610bd957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610b9c565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610c36573d6000803e3d6000fd5b5050506040513d6020811015610c4b57600080fd5b50516040518651600291889160009188916020918201918291908601908083835b60208310610ca957805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610c6c565b6001836020036101000a0380198251168184511680821785525050505050509050018367ffffffffffffffff191667ffffffffffffffff1916815260180182815260200193505050506040516020818303038152906040526040518082805190602001908083835b60208310610d4e57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610d11565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610dab573d6000803e3d6000fd5b5050506040513d6020811015610dc057600080fd5b5051604080516020818101949094528082019290925280518083038201815260609092019081905281519192909182918401908083835b60208310610e3457805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610df7565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015610e91573d6000803e3d6000fd5b5050506040513d6020811015610ea657600080fd5b50519050858114610f02576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260548152602001806117486054913960600191505060405180910390fd5b60205463ffffffff11610f60576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260218152602001806117276021913960400191505060405180910390fd5b602080546001019081905560005b60208110156110a9578160011660011415610fa0578260008260208110610f9157fe5b0155506110ac95505050505050565b600260008260208110610faf57fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061102557805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101610fe8565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa158015611082573d6000803e3d6000fd5b5050506040513d602081101561109757600080fd5b50519250600282049150600101610f6e565b50fe5b50505050505050565b60606110c26020546114ba565b905090565b6020546000908190815b60208110156112f05781600116600114156111e6576002600082602081106110f557fe5b01548460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061116b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161112e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156111c8573d6000803e3d6000fd5b5050506040513d60208110156111dd57600080fd5b505192506112e2565b600283602183602081106111f657fe5b015460405160200180838152602001828152602001925050506040516020818303038152906040526040518082805190602001908083835b6020831061126b57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161122e565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa1580156112c8573d6000803e3d6000fd5b5050506040513d60208110156112dd57600080fd5b505192505b6002820491506001016110d1565b506002826112ff6020546114ba565b600060401b6040516020018084815260200183805190602001908083835b6020831061135a57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0909201916020918201910161131d565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790527fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000095909516920191825250604080518083037ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8018152601890920190819052815191955093508392850191508083835b6020831061143f57805182527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09092019160209182019101611402565b51815160209384036101000a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01801990921691161790526040519190930194509192505080830381855afa15801561149c573d6000803e3d6000fd5b5050506040513d60208110156114b157600080fd5b50519250505090565b60408051600880825281830190925260609160208201818036833701905050905060c082901b8060071a60f81b826000815181106114f457fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060061a60f81b8260018151811061153757fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060051a60f81b8260028151811061157a57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060041a60f81b826003815181106115bd57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060031a60f81b8260048151811061160057fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060021a60f81b8260058151811061164357fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060011a60f81b8260068151811061168657fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508060001a60f81b826007815181106116c957fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535050919050565b6000808585111561170d578182fd5b83861115611719578182fd5b505082019391909203915056fe4465706f736974436f6e74726163743a206d65726b6c6520747265652066756c6c4465706f736974436f6e74726163743a207265636f6e7374727563746564204465706f7369744461746120646f6573206e6f74206d6174636820737570706c696564206465706f7369745f646174615f726f6f744465706f736974436f6e74726163743a20696e76616c6964207769746864726177616c5f63726564656e7469616c73206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c7565206e6f74206d756c7469706c65206f6620677765694465706f736974436f6e74726163743a20696e76616c6964207075626b6579206c656e6774684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f20686967684465706f736974436f6e74726163743a206465706f7369742076616c756520746f6f206c6f774465706f736974436f6e74726163743a20696e76616c6964207369676e6174757265206c656e677468a2646970667358221220dceca8706b29e917dacf25fceef95acac8d90d765ac926663ce4096195952b6164736f6c634300060b0033', ) @@ -105,19 +121,3 @@ describe('EIP-7685 buildBlock tests', () => { assert.equal(bytesToHex(parsedRequest.pubkey), pubkey) }) }) - -function parseDepositRequest(requestData: Uint8Array) { - const pubkey = requestData.subarray(0, 48) - const withdrawalCredentials = requestData.subarray(48, 48 + 32) - const amount = requestData.subarray(48 + 32, 48 + 32 + 8) - const signature = requestData.subarray(48 + 32 + 8, 48 + 32 + 8 + 96) - const index = requestData.subarray(48 + 32 + 8 + 96, 48 + 32 + 8 + 96 + 8) - - return { - pubkey, - withdrawalCredentials, - amount, - signature, - index, - } -} diff --git a/packages/vm/test/api/runTx.spec.ts b/packages/vm/test/api/runTx.spec.ts index 17a9b963eb..4d50d93e13 100644 --- a/packages/vm/test/api/runTx.spec.ts +++ b/packages/vm/test/api/runTx.spec.ts @@ -878,6 +878,7 @@ describe('EIP 4844 transaction tests', () => { { excessBlobGas: 0n, number: 1, + // eslint-disable-next-line @typescript-eslint/no-use-before-define parentHash: blockchain.genesisBlock.hash(), }, { diff --git a/packages/vm/test/t8n/t8ntool.ts b/packages/vm/test/t8n/t8ntool.ts index 068aacf97b..19605a8fe1 100644 --- a/packages/vm/test/t8n/t8ntool.ts +++ b/packages/vm/test/t8n/t8ntool.ts @@ -28,6 +28,78 @@ import type { TypedTxData } from '@ethereumjs/tx' import type { CLRequest, CLRequestType, PrefixedHexString } from '@ethereumjs/util' const kzg = new microEthKZG(trustedSetup) +// Helper methods + +/** + * Returns a blockchain with an overridden "getBlock" method to return the correct block hash + * @param inputEnv the T8NEnv input, which contains a `blockHashes` list containing the respective block hashes + * @returns + */ +function getBlockchain(inputEnv: T8NEnv) { + const blockchain = new EVMMockBlockchain() + + blockchain.getBlock = async function (number?: Number) { + for (const key in inputEnv.blockHashes) { + if (Number(key) === number) { + return { + hash() { + return hexToBytes(inputEnv.blockHashes[key]) + }, + } + } + } + return { + hash() { + return new Uint8Array(32) + }, + } + } + return blockchain +} + +/** + * Normalizes txData to use with EthereumJS keywords. For instance, 1559-txs have `v` fields on the inputs, where EthereumJS expects `yParity` + * @param txData Array of txData + * @returns Normalized array of txData + */ +function normalizeTxData(txData: TypedTxData[]) { + return txData.map((data: any) => { + if (data.v !== undefined) { + data.yParity = data.v + } + if (data.gas !== undefined) { + data.gasLimit = data.gas + } + + if (data.authorizationList !== undefined) { + data.authorizationList.map((e: any) => { + if (e.yParity === undefined) { + e.yParity = e.v + } + if (e.yParity === '0x0') { + e.yParity = '0x' + } + if (e.nonce === '0x0') { + e.nonce = '0x' + } + if (e.chainId === '0x0') { + e.chainId = '0x' + } + if (e.r === '0x0') { + e.r = '0x' + } + if (e.s === '0x0') { + e.s = '0x' + } + }) + } + if (data.input !== undefined) { + data.data = data.input + } + return data as TypedTxData + }) +} + /** * This is the TransitionTool class to run transitions. The entire class is marked `private` since * it is only intended to be used **once**. To use it, use the single public entrypoint TransitionTool.run(args) @@ -249,75 +321,3 @@ export class TransitionTool { writeFileSync(outputAllocFilePath, JSON.stringify(outputAlloc)) } } - -// Helper methods - -/** - * Returns a blockchain with an overridden "getBlock" method to return the correct block hash - * @param inputEnv the T8NEnv input, which contains a `blockHashes` list containing the respective block hashes - * @returns - */ -function getBlockchain(inputEnv: T8NEnv) { - const blockchain = new EVMMockBlockchain() - - blockchain.getBlock = async function (number?: Number) { - for (const key in inputEnv.blockHashes) { - if (Number(key) === number) { - return { - hash() { - return hexToBytes(inputEnv.blockHashes[key]) - }, - } - } - } - return { - hash() { - return new Uint8Array(32) - }, - } - } - return blockchain -} - -/** - * Normalizes txData to use with EthereumJS keywords. For instance, 1559-txs have `v` fields on the inputs, where EthereumJS expects `yParity` - * @param txData Array of txData - * @returns Normalized array of txData - */ -function normalizeTxData(txData: TypedTxData[]) { - return txData.map((data: any) => { - if (data.v !== undefined) { - data.yParity = data.v - } - if (data.gas !== undefined) { - data.gasLimit = data.gas - } - - if (data.authorizationList !== undefined) { - data.authorizationList.map((e: any) => { - if (e.yParity === undefined) { - e.yParity = e.v - } - if (e.yParity === '0x0') { - e.yParity = '0x' - } - if (e.nonce === '0x0') { - e.nonce = '0x' - } - if (e.chainId === '0x0') { - e.chainId = '0x' - } - if (e.r === '0x0') { - e.r = '0x' - } - if (e.s === '0x0') { - e.s = '0x' - } - }) - } - if (data.input !== undefined) { - data.data = data.input - } - return data as TypedTxData - }) -} diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index 0e014203f6..4a4a0f4877 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -39,6 +39,107 @@ import type { } from '@ethereumjs/tx' import type * as tape from 'tape' +export function format(a: any, toZero: boolean = false, isHex: boolean = false): Uint8Array { + if (a === '') { + return new Uint8Array() + } + + if (typeof a === 'string' && isHexString(a)) { + a = a.slice(2) + if (a.length % 2) a = '0' + a + a = hexToBytes(`0x${a}`) + } else if (!isHex) { + try { + a = bigIntToBytes(BigInt(a)) + } catch { + // pass + } + } else { + if (a.length % 2) a = '0' + a + a = hexToBytes(`0x${a}`) + } + + if (toZero && bytesToHex(a) === '0x') { + a = Uint8Array.from([0]) + } + + return a +} + +/** + * verifyAccountPostConditions using JSON from tests repo + * @param state DB/trie + * @param address Account Address + * @param account to verify + * @param acctData postconditions JSON from tests repo + */ +export function verifyAccountPostConditions( + state: any, + address: string, + account: Account, + acctData: any, + t: tape.Test, +) { + return new Promise((resolve) => { + t.comment('Account: ' + address) + if (!equalsBytes(format(account.balance, true), format(acctData.balance, true))) { + t.comment( + `Expected balance of ${bytesToBigInt(format(acctData.balance, true))}, but got ${ + account.balance + }`, + ) + } + if (!equalsBytes(format(account.nonce, true), format(acctData.nonce, true))) { + t.comment( + `Expected nonce of ${bytesToBigInt(format(acctData.nonce, true))}, but got ${account.nonce}`, + ) + } + + // validate storage + const origRoot = state.root() + + const hashedStorage: any = {} + for (const key in acctData.storage) { + hashedStorage[ + bytesToHex(keccak256(setLengthLeft(hexToBytes(isHexString(key) ? key : `0x${key}`), 32))) + ] = acctData.storage[key] + } + + state.root(account.storageRoot) + const rs = state.createReadStream() + rs.on('data', function (data: any) { + let key = bytesToHex(data.key) + const val = bytesToHex(RLP.decode(data.value) as Uint8Array) + + if (key === '0x') { + key = '0x00' + acctData.storage['0x00'] = acctData.storage['0x00'] ?? acctData.storage['0x'] + delete acctData.storage['0x'] + } + + if (val !== hashedStorage[key]) { + t.comment( + `Expected storage key ${bytesToHex(data.key)} at address ${address} to have value ${ + hashedStorage[key] ?? '0x' + }, but got ${val}}`, + ) + } + delete hashedStorage[key] + }) + + rs.on('end', function () { + for (const key in hashedStorage) { + if (hashedStorage[key] !== '0x00') { + t.comment(`key: ${key} not found in storage at address ${address}`) + } + } + + state.root(origRoot) + resolve() + }) + }) +} + export function dumpState(state: any, cb: Function) { function readAccounts(state: any) { return new Promise((resolve) => { @@ -92,33 +193,6 @@ export function dumpState(state: any, cb: Function) { }) } -export function format(a: any, toZero: boolean = false, isHex: boolean = false): Uint8Array { - if (a === '') { - return new Uint8Array() - } - - if (typeof a === 'string' && isHexString(a)) { - a = a.slice(2) - if (a.length % 2) a = '0' + a - a = hexToBytes(`0x${a}`) - } else if (!isHex) { - try { - a = bigIntToBytes(BigInt(a)) - } catch { - // pass - } - } else { - if (a.length % 2) a = '0' + a - a = hexToBytes(`0x${a}`) - } - - if (toZero && bytesToHex(a) === '0x') { - a = Uint8Array.from([0]) - } - - return a -} - /** * Make a tx using JSON from tests repo * @param {Object} txData The tx object from tests repo @@ -209,80 +283,6 @@ export async function verifyPostConditions(state: any, testData: any, t: tape.Te }) } -/** - * verifyAccountPostConditions using JSON from tests repo - * @param state DB/trie - * @param address Account Address - * @param account to verify - * @param acctData postconditions JSON from tests repo - */ -export function verifyAccountPostConditions( - state: any, - address: string, - account: Account, - acctData: any, - t: tape.Test, -) { - return new Promise((resolve) => { - t.comment('Account: ' + address) - if (!equalsBytes(format(account.balance, true), format(acctData.balance, true))) { - t.comment( - `Expected balance of ${bytesToBigInt(format(acctData.balance, true))}, but got ${ - account.balance - }`, - ) - } - if (!equalsBytes(format(account.nonce, true), format(acctData.nonce, true))) { - t.comment( - `Expected nonce of ${bytesToBigInt(format(acctData.nonce, true))}, but got ${account.nonce}`, - ) - } - - // validate storage - const origRoot = state.root() - - const hashedStorage: any = {} - for (const key in acctData.storage) { - hashedStorage[ - bytesToHex(keccak256(setLengthLeft(hexToBytes(isHexString(key) ? key : `0x${key}`), 32))) - ] = acctData.storage[key] - } - - state.root(account.storageRoot) - const rs = state.createReadStream() - rs.on('data', function (data: any) { - let key = bytesToHex(data.key) - const val = bytesToHex(RLP.decode(data.value) as Uint8Array) - - if (key === '0x') { - key = '0x00' - acctData.storage['0x00'] = acctData.storage['0x00'] ?? acctData.storage['0x'] - delete acctData.storage['0x'] - } - - if (val !== hashedStorage[key]) { - t.comment( - `Expected storage key ${bytesToHex(data.key)} at address ${address} to have value ${ - hashedStorage[key] ?? '0x' - }, but got ${val}}`, - ) - } - delete hashedStorage[key] - }) - - rs.on('end', function () { - for (const key in hashedStorage) { - if (hashedStorage[key] !== '0x00') { - t.comment(`key: ${key} not found in storage at address ${address}`) - } - } - - state.root(origRoot) - resolve() - }) - }) -} - /** * verifyGas by computing the difference of coinbase account balance * @param {Object} results to verify diff --git a/packages/wallet/test/index.spec.ts b/packages/wallet/test/index.spec.ts index b7c918d2bb..b99a2825a5 100644 --- a/packages/wallet/test/index.spec.ts +++ b/packages/wallet/test/index.spec.ts @@ -210,9 +210,9 @@ describe('Wallet tests', () => { const permus = [] const keys = Array.from( objs.reduce((acc: any, curr: object) => { - Object.keys(curr).forEach((key) => { + for (const key of Object.keys(curr)) { acc.add(key) - }) + } return acc }, new Set()), ) @@ -225,11 +225,11 @@ describe('Wallet tests', () => { .split('') .map((v) => parseInt(v, 10)) const obj: any = {} - ;(zip(selectors, keys) as [number, string][]).forEach(([sel, k]: [number, string]) => { - if ((objs as any)[sel].hasOwnProperty(k) === true) { - obj[k] = (objs as any)[sel][k] + for (const [sel, k] of zip(selectors, keys)) { + if (Object.prototype.hasOwnProperty.call(objs[sel!], k as string)) { + obj[k as string] = (objs as any)[sel!][k as string] } - }) + } permus.push(obj) } return permus