From 6ea9dbad4cba83c1d7490802eaee22a64466d310 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 27 Mar 2023 13:41:41 -0400 Subject: [PATCH 01/32] `Buffer` to `Uint8Array` conversion (#2566) * V7 update to master 1 (#2593) * Added v7 release reference in main README table (#2562) * common: Schedule shanghai on goerli (#2563) * common: Schedule shanghai on goerli * update timestamp * util/tx: Shift ssz back to case dependency free ES2019 compatible version (#2564) * util/tx: Shift ssz back to case dependency free ES2019 compatible version * update package lock * update karma ecma version * VM: some optimization on the bnadd/bnmul precompiles to only copy over the necessary 128 bytes as input for the WASM call (#2568) * EVM: Memory Fix & Other Optimizations (#2570) * EVM: Rename evm debug logger to evm:evm (one for package, one for class), consistency, also, logger will otherwise be left out when run with evm:* * VM: Rename message checkpoint to state checkpoint in debug message (there is a dedicated message checkpoint msg along msg logging) * EVM: CALL/CREATE debug exit msg differentiation * EVM: avoid buffer copy in memory read (performance) * EVM: Rewrite runCall() checkpoint/revert conditional for readability/simplification * EVM: Added EIP check for transient storage checkpointing * EVM: Precompile Debug Logger Improvements (#2572) * EVM: Added general precompile debug logger setup, first ECRECOVER exemplary debug logging * EVM: Added remaining precompile debug loggers * EVM: added error cases to BLS precompile debug log * EVM: Added missing precompile return value debug logs * Small fixes * tx: ensure eip3860 txs can have more than max_initcode_size data if to field is non-empty (#2575) * EVM: Avoid memory.read() Memory Copy (#2573) * EVM: added avoidCopy parameter to memory.read() function, first test on CREATE opcode * EVM: Add direct memory read to all calling opcodes * EVM: Copy over memory on IDENTITY precompile * EVM: remove length checks and return buffer 0-filling in Memory.read() (memory is uncoditionally being extended properly anyhow) * Some optimizations * blockchain: fix merge->clique transition (#2571) * Client: ensure safe/finalized blocks are part of the canonical chain on forkchoiceUpdated (#2577) * client/engine: ensure finalized/safe blocks are in canonical chain * client: engine-api: fix finalized block check * client/tests: fix forkchoice updated test * client: add fcu tests to check if blocks are part of canonical chain * client/engine: ensure payload has a valid timestamp forkchoiceUpdated (#2579) * client/engine: ensure invalid blockhash response matches spec (#2583) * client/engine: delete invalid skeleton blocks (#2584) Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> * Setup to dev/test snapsync with sim architecture (#2574) * Setup to dev/test snapsync with sim architecture * modfiy single-run to setup a lodestar<>geth node to snapsync from * setup an ethereumjs inline client and get it to peer with geth * cleanup setup a bit * snapsync run spec * get the snap testdev sim working * finalize the test infra and update usage doc * enhance coverage * Use geth RPC to connect to ethJS * refac wait for snap sync completion --------- Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> * client: Add safe and finalized blockoptions to the chain (#2585) * client: Add safe and finalized blockoptions to the chain * fix tests * fix more tests * fix remaining * cleanup * enhance coverage * unset scheduled goerli timestamp based hfs colliding with test * Client: Small Debug Helpers and CLI Improvements (#2586) * Client: new constant MAX_TOLERATED_BLOCK_TIME for execution, added warning for slowly executed blocks * Client -> Execution: NumBlocksPerIteration (default: 50) as an option * Client: only restart RLPx server or log peer stats if max peers is set to be greater than 0 * Apply suggestions from code review Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> * Apply suggestions from code review --------- Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> * common: Schedule Shanghai on mainnet! (#2591) * common: Schedule Shanghai on mainnet! * clear hf timestamp for test * VM: Diff-based Touched Accounts Checkpointing (#2581) * VM: Switched to a more efficient diff-based way of touched account checkpointing * VM: move accessed storage inefficient checkpointing problem to berlin, haha * EVM: avoid memory copy in MLOAD opcode function * Remove console.log() in EVM * vmState: ensure touched accounts delete stack gets properly updated on commit * vm/eei: save touched height * vm/vmState: new possible fix for touched accounts * vm/vmState: another attempt to fix touched accounts journaling * vm: add journaling * Check correct journal height on revert --------- Co-authored-by: Jochem Brouwer Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com> --------- Co-authored-by: Holger Drewes Co-authored-by: g11tech Co-authored-by: Jochem Brouwer * First pass - most util changes * Fix most account stuff * Fix account test * Many byte fixes * util: fix constants tests * remaining fixes * Turn off ci jobs * monorepo: bigIntToUnpaddedBuffer -> bigIntToUnpaddedBytes * util: update description of bytes exporT * util: remove unused import * common: use bytesToHex helper instead of toString('hex') * trie: refactor non-test files to Uint8Array * util: add binary string utils * util: remove extra Uint8Array.from * util: remove arrToBufArray util * trie: adjust tests and fix outstanding issues * util: remove binarystring utils and add compareBytes and randomBytes util * common: refactor common with Uint8Array * util: accept 0x-prefixed and non-prefixed hex strings in toBytes * tx: refactor Buffer -> Uint8Array * tx: remove unused import * util: revert toBytes update * block: refactor Buffer -> uint8array * block: adjust import * trie: refactor remaining buffer instances * move devp2p to uint8Array * statemanager: refactor buffer -> uint8array * util: simplify zeros * util: add concatBytesUnsafe * ethash: partial migration * ethash: update examples * ethash: wip fixes * ethash: more WIP * ethash: ensure fnv input is read from mix, not mixView * blockchain: migrate to uint8array * ethash: renable all tests * ethash: fix bytesReverse * Fix miner tests * many hexToBytes moves * most of evm/vm moves * evm: Fix all tests * vm: more fixes * More fixes * vm: fix receipts encoding * vm: fix tester * client: refactor buffer to uint8array * client: additional uint8 adjustments * client: fix most tests * fix remaining client unit tests * reactivate most CI * client: fix les test/protocol * turn client CI on * util: fix name typo * lint * Fix withdrawals * remove buffarraytoarr * Remove bufArrtoArr references * Lint * fix examples * Fix difficulty test * lint * block: update randomBytes import * replace randombytes import * client: fix sim test util * vm: fix example * devp2p: update snappy typing and fix tests * Fix tests * Remove additional buffer references * rustbn fixes * add 0x prefix to precompile address * Remove `node-ip` dependency and buffer references in devp2p * Switch slice to subarray * evm: fix blake2f * Merge fixes * more merge commit fixes * more test fixes * Address all the feedback * fix dns test * Update packages/util/src/bytes.ts Co-authored-by: Jochem Brouwer * Fix return type for baToJSON * util: instantiate hexByByte array * Remove baToJson * rebase fixes * Fix event typing * Revert outdated initcode changes * lint --------- Co-authored-by: Holger Drewes Co-authored-by: g11tech Co-authored-by: Jochem Brouwer Co-authored-by: Gabriel Rocheleau --- package-lock.json | 48 +-- packages/block/src/block.ts | 59 ++-- packages/block/src/from-rpc.ts | 4 +- packages/block/src/header.ts | 201 +++++------ packages/block/src/helpers.ts | 4 +- packages/block/src/types.ts | 44 +-- packages/block/test/block.spec.ts | 58 ++-- packages/block/test/clique.spec.ts | 39 +-- packages/block/test/difficulty.spec.ts | 3 +- packages/block/test/eip1559block.spec.ts | 13 +- packages/block/test/eip4844block.spec.ts | 12 +- packages/block/test/eip4895block.spec.ts | 32 +- packages/block/test/from-rpc.spec.ts | 24 +- packages/block/test/header.spec.ts | 104 +++--- packages/block/test/mergeBlock.spec.ts | 35 +- packages/block/test/util.ts | 6 +- packages/blockchain/src/blockchain.ts | 107 +++--- packages/blockchain/src/consensus/clique.ts | 89 +++-- packages/blockchain/src/db/cache.ts | 21 +- packages/blockchain/src/db/constants.ts | 33 +- packages/blockchain/src/db/helpers.ts | 17 +- packages/blockchain/src/db/manager.ts | 56 +-- packages/blockchain/src/db/operation.ts | 18 +- .../blockchain/src/genesisStates/index.ts | 17 +- packages/blockchain/src/types.ts | 8 +- .../blockchain/test/blockValidation.spec.ts | 13 +- packages/blockchain/test/clique.spec.ts | 117 +++---- .../blockchain/test/customConsensus.spec.ts | 3 +- packages/blockchain/test/index.spec.ts | 105 +++--- packages/blockchain/test/iterator.spec.ts | 22 +- packages/blockchain/test/pos.spec.ts | 3 +- packages/blockchain/test/reorg.spec.ts | 15 +- packages/blockchain/test/util.ts | 64 ++-- packages/blockchain/test/utils.spec.ts | 5 +- packages/client/bin/cli.ts | 39 ++- packages/client/bin/startRpc.ts | 14 +- packages/client/browser/index.ts | 4 +- packages/client/browser/util/index.ts | 13 +- .../devnets/4844-interop/tools/txGenerator.ts | 8 +- packages/client/lib/blockchain/chain.ts | 20 +- packages/client/lib/client.ts | 6 +- packages/client/lib/config.ts | 19 +- packages/client/lib/execution/execution.ts | 8 +- packages/client/lib/execution/level.ts | 12 +- packages/client/lib/execution/receipt.ts | 98 +++--- packages/client/lib/execution/vmexecution.ts | 28 +- packages/client/lib/miner/miner.ts | 18 +- packages/client/lib/miner/pendingBlock.ts | 74 ++-- packages/client/lib/net/peer/rlpxpeer.ts | 4 +- .../client/lib/net/protocol/ethprotocol.ts | 126 +++---- .../client/lib/net/protocol/lesprotocol.ts | 86 ++--- .../client/lib/net/protocol/libp2psender.ts | 23 +- packages/client/lib/net/protocol/sender.ts | 2 +- .../client/lib/net/protocol/snapprotocol.ts | 96 +++--- packages/client/lib/net/server/rlpxserver.ts | 15 +- packages/client/lib/net/server/server.ts | 2 +- packages/client/lib/rpc/helpers.ts | 6 +- packages/client/lib/rpc/modules/admin.ts | 6 +- packages/client/lib/rpc/modules/debug.ts | 11 +- packages/client/lib/rpc/modules/engine.ts | 91 ++--- packages/client/lib/rpc/modules/eth.ts | 81 ++--- packages/client/lib/rpc/modules/web3.ts | 4 +- .../client/lib/service/ethereumservice.ts | 6 +- .../client/lib/service/fullethereumservice.ts | 7 +- packages/client/lib/service/txpool.ts | 44 +-- packages/client/lib/sync/beaconsync.ts | 6 +- .../client/lib/sync/fetcher/accountfetcher.ts | 63 ++-- .../client/lib/sync/fetcher/blockfetcher.ts | 12 +- .../client/lib/sync/fetcher/storagefetcher.ts | 85 ++--- packages/client/lib/sync/fullsync.ts | 19 +- packages/client/lib/sync/skeleton.ts | 112 +++--- packages/client/lib/sync/snapsync.ts | 5 +- packages/client/lib/types.ts | 2 +- packages/client/lib/util/debug.ts | 12 +- packages/client/lib/util/index.ts | 13 +- packages/client/lib/util/metaDBManager.ts | 16 +- packages/client/lib/util/parse.ts | 12 +- packages/client/lib/util/rpc.ts | 4 +- packages/client/test/blockchain/chain.spec.ts | 6 +- .../integration/fullethereumservice.spec.ts | 19 +- .../client/test/integration/merge.spec.ts | 8 +- .../client/test/integration/miner.spec.ts | 8 +- .../client/test/integration/mocks/mockpeer.ts | 2 +- packages/client/test/miner/miner.spec.ts | 28 +- .../client/test/miner/pendingBlock.spec.ts | 38 +-- .../test/net/protocol/ethprotocol.spec.ts | 57 ++-- .../test/net/protocol/lesprotocol.spec.ts | 29 +- .../test/net/protocol/libp2psender.spec.ts | 11 +- .../test/net/protocol/snapprotocol.spec.ts | 121 +++---- .../test/net/server/libp2pserver.spec.ts | 7 +- .../client/test/net/server/rlpxserver.spec.ts | 20 +- .../test/rpc/debug/traceTransaction.spec.ts | 10 +- .../rpc/engine/forkchoiceUpdatedV1.spec.ts | 10 +- .../test/rpc/engine/getBlobsBundleV1.spec.ts | 7 +- .../engine/getPayloadBodiesByHashV1.spec.ts | 29 +- .../engine/getPayloadBodiesByRangeV1.spec.ts | 14 +- .../test/rpc/engine/newPayloadV1.spec.ts | 13 +- .../test/rpc/engine/newPayloadv2.spec.ts | 13 +- .../test/rpc/engine/newPayloadv3.spec.ts | 13 +- .../test/rpc/engine/withdrawals.spec.ts | 6 +- packages/client/test/rpc/eth/call.spec.ts | 14 +- .../test/rpc/eth/getBlockByNumber.spec.ts | 11 +- packages/client/test/rpc/eth/getLogs.spec.ts | 14 +- .../client/test/rpc/eth/getStorageAt.spec.ts | 15 +- .../test/rpc/eth/getTransactionByHash.spec.ts | 14 +- .../rpc/eth/getTransactionReceipt.spec.ts | 10 +- .../eth/getUncleCountByBlockNumber.spec.ts | 2 +- .../test/rpc/eth/sendRawTransaction.spec.ts | 26 +- packages/client/test/rpc/helpers.ts | 10 +- packages/client/test/rpc/mockBlockchain.ts | 6 +- packages/client/test/rpc/rpc.spec.ts | 3 +- .../client/test/rpc/txpool/content.spec.ts | 2 +- packages/client/test/rpc/validation.spec.ts | 35 +- packages/client/test/rpc/websocket.spec.ts | 3 +- .../test/service/fullethereumservice.spec.ts | 27 +- packages/client/test/sim/eof.spec.ts | 6 +- packages/client/test/sim/mainnet.spec.ts | 6 +- packages/client/test/sim/sharding.spec.ts | 15 +- packages/client/test/sim/simutils.ts | 47 +-- packages/client/test/sim/snapsync.spec.ts | 5 +- packages/client/test/sim/txGenerator.ts | 27 +- packages/client/test/sync/beaconsync.spec.ts | 4 +- .../test/sync/fetcher/accountfetcher.spec.ts | 62 ++-- packages/client/test/sync/fullsync.spec.ts | 2 +- packages/client/test/sync/lightsync.spec.ts | 6 +- packages/client/test/sync/skeleton.spec.ts | 9 +- packages/client/test/sync/sync.spec.ts | 2 +- packages/client/test/sync/txpool.spec.ts | 56 +-- packages/client/test/util/rpc.spec.ts | 10 +- packages/common/src/common.ts | 25 +- packages/common/src/types.ts | 2 +- packages/common/test/hardforks.spec.ts | 18 +- packages/common/test/timestamp.spec.ts | 11 +- packages/common/test/utils.spec.ts | 6 +- .../devp2p/examples/peer-communication-les.ts | 31 +- .../devp2p/examples/peer-communication.ts | 46 ++- packages/devp2p/examples/simple.ts | 9 +- packages/devp2p/package.json | 3 - packages/devp2p/scripts/singlePeerRun.ts | 2 +- .../devp2p/src/@types/snappyjs/index.d.ts | 4 +- packages/devp2p/src/dns/enr.ts | 28 +- packages/devp2p/src/dpt/ban-list.ts | 4 +- packages/devp2p/src/dpt/dpt.ts | 24 +- packages/devp2p/src/dpt/kbucket.ts | 19 +- packages/devp2p/src/dpt/message.ts | 106 +++--- packages/devp2p/src/dpt/server.ts | 27 +- packages/devp2p/src/protocol/eth.ts | 87 +++-- packages/devp2p/src/protocol/les.ts | 83 ++--- packages/devp2p/src/protocol/protocol.ts | 2 +- packages/devp2p/src/protocol/snap.ts | 3 +- packages/devp2p/src/rlpx/ecies.ts | 258 +++++++------- packages/devp2p/src/rlpx/mac.ts | 12 +- packages/devp2p/src/rlpx/peer.ts | 125 +++---- packages/devp2p/src/rlpx/rlpx.ts | 48 +-- packages/devp2p/src/util.ts | 165 ++++++--- packages/devp2p/test/dpt-message.spec.ts | 43 +-- packages/devp2p/test/enr.spec.ts | 6 +- .../test/integration/dpt-simulator.spec.ts | 13 +- .../test/integration/eth-simulator.spec.ts | 15 +- .../test/integration/les-simulator.spec.ts | 13 +- .../test/integration/rlpx-simulator.spec.ts | 3 +- packages/devp2p/test/integration/util.ts | 2 +- packages/devp2p/test/rlpx-ecies.spec.ts | 53 +-- packages/devp2p/test/rlpx-mac.spec.ts | 16 +- packages/ethash/examples/example.ts | 8 +- packages/ethash/examples/rawExample.ts | 21 +- packages/ethash/package.json | 1 + packages/ethash/src/index.ts | 123 ++++--- packages/ethash/src/util.ts | 21 +- packages/ethash/test/block.spec.ts | 15 +- packages/ethash/test/ethash.spec.ts | 16 +- packages/ethash/test/miner.spec.ts | 13 +- packages/evm/examples/decode-opcodes.ts | 17 +- packages/evm/examples/runCode.ts | 5 +- packages/evm/package.json | 3 +- packages/evm/src/eof.ts | 10 +- packages/evm/src/evm.ts | 54 +-- packages/evm/src/interpreter.ts | 120 ++++--- packages/evm/src/memory.ts | 27 +- packages/evm/src/message.ts | 26 +- packages/evm/src/opcodes/EIP1283.ts | 20 +- packages/evm/src/opcodes/EIP2200.ts | 22 +- packages/evm/src/opcodes/EIP2929.ts | 14 +- packages/evm/src/opcodes/functions.ts | 117 +++---- packages/evm/src/opcodes/gas.ts | 42 +-- packages/evm/src/opcodes/util.ts | 34 +- packages/evm/src/precompiles/01-ecrecover.ts | 40 +-- packages/evm/src/precompiles/02-sha256.ts | 14 +- packages/evm/src/precompiles/03-ripemd160.ts | 14 +- packages/evm/src/precompiles/04-identity.ts | 8 +- packages/evm/src/precompiles/05-modexp.ts | 47 +-- packages/evm/src/precompiles/06-ecadd.ts | 15 +- packages/evm/src/precompiles/07-ecmul.ts | 16 +- packages/evm/src/precompiles/08-ecpairing.ts | 13 +- packages/evm/src/precompiles/09-blake2f.ts | 39 +-- .../evm/src/precompiles/0a-bls12-g1add.ts | 23 +- .../evm/src/precompiles/0b-bls12-g1mul.ts | 23 +- .../src/precompiles/0c-bls12-g1multiexp.ts | 25 +- .../evm/src/precompiles/0d-bls12-g2add.ts | 21 +- .../evm/src/precompiles/0e-bls12-g2mul.ts | 23 +- .../src/precompiles/0f-bls12-g2multiexp.ts | 25 +- .../evm/src/precompiles/10-bls12-pairing.ts | 35 +- .../src/precompiles/11-bls12-map-fp-to-g1.ts | 21 +- .../src/precompiles/12-bls12-map-fp2-to-g2.ts | 23 +- .../precompiles/14-kzg-point-evaluation.ts | 55 +-- packages/evm/src/precompiles/index.ts | 8 +- packages/evm/src/precompiles/types.ts | 2 +- .../evm/src/precompiles/util/bls12_381.ts | 73 ++-- packages/evm/src/transientStorage.ts | 22 +- packages/evm/src/types.ts | 52 +-- packages/evm/test/asyncEvents.spec.ts | 5 +- packages/evm/test/customOpcodes.spec.ts | 13 +- packages/evm/test/customPrecompiles.spec.ts | 48 +-- packages/evm/test/eips/eip-3860.spec.ts | 42 +-- packages/evm/test/eof.spec.ts | 5 +- packages/evm/test/memory.spec.ts | 12 +- .../evm/test/precompiles/06-ecadd.spec.ts | 2 +- .../evm/test/precompiles/07-ecmul.spec.ts | 2 +- .../evm/test/precompiles/08-ecpairing.spec.ts | 7 +- .../precompiles/14-pointevaluation.spec.ts | 47 ++- .../evm/test/precompiles/eip-2537-BLS.spec.ts | 15 +- .../evm/test/precompiles/hardfork.spec.ts | 3 +- packages/evm/test/runCall.spec.ts | 142 ++++---- packages/evm/test/runCode.spec.ts | 7 +- packages/evm/test/stack.spec.ts | 13 +- packages/evm/test/transientStorage.spec.ts | 60 ++-- packages/statemanager/src/baseStateManager.ts | 6 +- packages/statemanager/src/cache.ts | 14 +- .../statemanager/src/ethersStateManager.ts | 81 ++--- packages/statemanager/src/interface.ts | 16 +- packages/statemanager/src/stateManager.ts | 129 +++---- packages/statemanager/test/cache.spec.ts | 24 +- .../test/ethersStateManager.spec.ts | 32 +- .../test/proofStateManager.spec.ts | 66 ++-- .../statemanager/test/stateManager.spec.ts | 151 ++++----- packages/trie/benchmarks/engines/level.ts | 14 +- packages/trie/benchmarks/keys.ts | 4 +- packages/trie/benchmarks/suite.ts | 6 +- packages/trie/examples/level.js | 2 +- packages/trie/recipes/level-legacy.ts | 8 +- packages/trie/recipes/level.ts | 20 +- packages/trie/recipes/lmdb.ts | 6 +- packages/trie/src/db/checkpoint.ts | 24 +- packages/trie/src/db/map.ts | 18 +- packages/trie/src/proof/range.ts | 42 +-- packages/trie/src/trie/node/branch.ts | 11 +- packages/trie/src/trie/node/extension.ts | 2 +- packages/trie/src/trie/node/leaf.ts | 2 +- packages/trie/src/trie/node/node.ts | 17 +- packages/trie/src/trie/node/util.ts | 13 +- packages/trie/src/trie/trie.ts | 120 +++---- packages/trie/src/types.ts | 36 +- packages/trie/src/util/nibbles.ts | 16 +- packages/trie/src/util/readStream.ts | 4 +- packages/trie/src/util/walkController.ts | 14 +- packages/trie/test/db/checkpoint.spec.ts | 22 +- packages/trie/test/db/db.spec.ts | 13 +- packages/trie/test/encoding.spec.ts | 8 +- packages/trie/test/index.spec.ts | 196 +++++------ packages/trie/test/official.spec.ts | 27 +- packages/trie/test/proof.spec.ts | 117 +++---- packages/trie/test/proof/range.spec.ts | 84 +++-- packages/trie/test/stream.spec.ts | 91 ++--- packages/trie/test/trie/checkpoint.spec.ts | 91 ++--- packages/trie/test/trie/prune.spec.ts | 108 +++--- packages/trie/test/trie/secure.spec.ts | 120 +++---- packages/trie/test/trie/trie.spec.ts | 26 +- packages/tx/examples/custom-chain-tx.ts | 6 +- packages/tx/examples/ropsten-tx.ts | 4 +- packages/tx/examples/transactions.ts | 12 +- packages/tx/src/baseTransaction.ts | 64 ++-- packages/tx/src/eip1559Transaction.ts | 103 +++--- packages/tx/src/eip2930Transaction.ts | 106 +++--- packages/tx/src/eip4844Transaction.ts | 129 ++++--- packages/tx/src/fromRpc.ts | 4 +- packages/tx/src/legacyTransaction.ts | 94 +++--- packages/tx/src/transactionFactory.ts | 18 +- packages/tx/src/types.ts | 76 ++--- packages/tx/src/util.ts | 38 +-- packages/tx/src/utils/blobHelpers.ts | 40 +-- packages/tx/test/base.spec.ts | 49 +-- packages/tx/test/eip1559.spec.ts | 44 +-- packages/tx/test/eip3860.spec.ts | 12 +- packages/tx/test/eip4844.spec.ts | 48 +-- packages/tx/test/fromRpc.spec.ts | 5 +- packages/tx/test/inputValue.spec.ts | 22 +- packages/tx/test/legacy.spec.ts | 134 ++++---- packages/tx/test/testLoader.ts | 5 +- packages/tx/test/transactionFactory.spec.ts | 9 +- packages/tx/test/transactionRunner.ts | 6 +- packages/tx/test/typedTxsAndEIP2930.spec.ts | 126 +++---- packages/util/src/account.ts | 152 +++++---- packages/util/src/address.ts | 60 ++-- packages/util/src/bytes.ts | 319 ++++++++++-------- packages/util/src/constants.ts | 10 +- packages/util/src/helpers.ts | 6 +- packages/util/src/index.ts | 2 +- packages/util/src/internal.ts | 8 +- packages/util/src/signature.ts | 101 +++--- packages/util/src/types.ts | 49 ++- packages/util/src/withdrawal.ts | 45 ++- packages/util/test/account.spec.ts | 270 +++++++-------- packages/util/test/address.spec.ts | 31 +- packages/util/test/bytes.spec.ts | 299 +++++++--------- packages/util/test/constants.spec.ts | 7 +- packages/util/test/internal.spec.ts | 5 +- packages/util/test/signature.spec.ts | 191 +++++------ packages/util/test/ssz.spec.ts | 31 +- packages/util/test/types.spec.ts | 52 +-- packages/util/test/withdrawal.spec.ts | 27 +- packages/vm/benchmarks/mockchain.ts | 2 +- packages/vm/benchmarks/util.ts | 20 +- packages/vm/examples/helpers/account-utils.ts | 2 +- packages/vm/examples/run-blockchain.ts | 21 +- packages/vm/examples/run-solidity-contract.ts | 25 +- packages/vm/src/bloom/index.ts | 20 +- packages/vm/src/buildBlock.ts | 14 +- packages/vm/src/eei/eei.ts | 12 +- packages/vm/src/eei/vmState.ts | 71 ++-- packages/vm/src/runBlock.ts | 71 ++-- packages/vm/src/runTx.ts | 36 +- packages/vm/src/types.ts | 16 +- packages/vm/src/vm.ts | 13 +- packages/vm/test/api/EIPs/eip-1153.spec.ts | 26 +- .../EIPs/eip-1283-net-gas-metering.spec.ts | 13 +- .../test/api/EIPs/eip-1559-FeeMarket.spec.ts | 15 +- packages/vm/test/api/EIPs/eip-2315.spec.ts | 3 +- .../api/EIPs/eip-2565-modexp-gas-cost.spec.ts | 11 +- packages/vm/test/api/EIPs/eip-2929.spec.ts | 21 +- .../api/EIPs/eip-2930-accesslists.spec.ts | 18 +- .../test/api/EIPs/eip-3074-authcall.spec.ts | 301 +++++++++-------- .../vm/test/api/EIPs/eip-3198-BaseFee.spec.ts | 5 +- packages/vm/test/api/EIPs/eip-3529.spec.ts | 21 +- .../EIPs/eip-3540-evm-object-format.spec.ts | 30 +- packages/vm/test/api/EIPs/eip-3541.spec.ts | 3 +- packages/vm/test/api/EIPs/eip-3607.spec.ts | 4 +- .../api/EIPs/eip-3651-warm-coinbase.spec.ts | 9 +- .../EIPs/eip-3670-eof-code-validation.spec.ts | 41 ++- packages/vm/test/api/EIPs/eip-3855.spec.ts | 9 +- packages/vm/test/api/EIPs/eip-3860.spec.ts | 7 +- ...t-difficulty-opcode-with-prevrando.spec.ts | 7 +- .../api/EIPs/eip-4895-withdrawals.spec.ts | 57 ++-- packages/vm/test/api/bloom.spec.ts | 44 +-- packages/vm/test/api/buildBlock.spec.ts | 54 +-- packages/vm/test/api/customChain.spec.ts | 10 +- packages/vm/test/api/eei.spec.ts | 3 +- packages/vm/test/api/events.spec.ts | 14 +- packages/vm/test/api/index.spec.ts | 12 +- .../vm/test/api/istanbul/eip-1108.spec.ts | 10 +- .../vm/test/api/istanbul/eip-1344.spec.ts | 9 +- packages/vm/test/api/istanbul/eip-152.spec.ts | 7 +- .../vm/test/api/istanbul/eip-1884.spec.ts | 11 +- .../vm/test/api/istanbul/eip-2200.spec.ts | 13 +- packages/vm/test/api/runBlock.spec.ts | 81 ++--- packages/vm/test/api/runTx.spec.ts | 59 ++-- .../vm/test/api/state/accountExists.spec.ts | 41 +-- packages/vm/test/api/utils.ts | 8 +- packages/vm/test/api/vmState.spec.ts | 57 ++-- .../vm/test/retesteth/transition-child.ts | 34 +- .../tester/runners/BlockchainTestsRunner.ts | 21 +- .../tester/runners/GeneralStateTestsRunner.ts | 8 +- packages/vm/test/tester/testLoader.ts | 4 +- packages/vm/test/util.ts | 56 ++- 363 files changed, 6526 insertions(+), 6324 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3f3595f681d..01a1d98d679 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4459,14 +4459,6 @@ "file-uri-to-path": "1.0.0" } }, - "node_modules/bl": { - "version": "1.2.3", - "license": "MIT", - "dependencies": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "node_modules/blakejs": { "version": "1.2.1", "license": "MIT" @@ -5453,6 +5445,7 @@ }, "node_modules/core-util-is": { "version": "1.0.3", + "dev": true, "license": "MIT" }, "node_modules/cors": { @@ -8566,10 +8559,6 @@ "node": ">= 0.10" } }, - "node_modules/ip": { - "version": "1.1.8", - "license": "MIT" - }, "node_modules/ip-address": { "version": "6.4.0", "license": "MIT", @@ -9068,6 +9057,7 @@ }, "node_modules/isarray": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/isbinaryfile": { @@ -13983,6 +13973,7 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", + "dev": true, "license": "MIT" }, "node_modules/process-on-spawn": { @@ -14248,6 +14239,7 @@ }, "node_modules/readable-stream": { "version": "2.3.7", + "dev": true, "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -14261,10 +14253,12 @@ }, "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", + "dev": true, "license": "MIT" }, "node_modules/readable-stream/node_modules/string_decoder": { "version": "1.1.1", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" @@ -17998,13 +17992,10 @@ "@ethereumjs/rlp": "^4.0.1", "@ethereumjs/util": "^8.0.5", "@scure/base": "1.1.1", - "@types/bl": "^2.1.0", "@types/k-bucket": "^5.0.0", "@types/lru-cache": "^5.1.0", - "bl": "^1.1.2", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", - "ip": "^1.1.3", "k-bucket": "^5.0.0", "lru-cache": "^5.1.1", "ms": "^0.7.1", @@ -19665,18 +19656,15 @@ "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@scure/base": "1.1.1", - "@types/bl": "^2.1.0", "@types/chalk": "^2.2.0", "@types/debug": "^4.1.4", "@types/ip": "^1.1.0", "@types/k-bucket": "^5.0.0", "@types/lru-cache": "^5.1.0", "@types/ms": "^0.7.30", - "bl": "^1.1.2", "chalk": "^2.4.2", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", - "ip": "^1.1.3", "k-bucket": "^5.0.0", "lru-cache": "^5.1.1", "ms": "^0.7.1", @@ -21554,13 +21542,6 @@ "file-uri-to-path": "1.0.0" } }, - "bl": { - "version": "1.2.3", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" - } - }, "blakejs": { "version": "1.2.1" }, @@ -22264,7 +22245,8 @@ "dev": true }, "core-util-is": { - "version": "1.0.3" + "version": "1.0.3", + "dev": true }, "cors": { "version": "2.8.5", @@ -24386,9 +24368,6 @@ "version": "2.2.0", "dev": true }, - "ip": { - "version": "1.1.8" - }, "ip-address": { "version": "6.4.0", "requires": { @@ -24663,7 +24642,8 @@ } }, "isarray": { - "version": "1.0.0" + "version": "1.0.0", + "dev": true }, "isbinaryfile": { "version": "4.0.10", @@ -28100,7 +28080,8 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.1" + "version": "2.0.1", + "dev": true }, "process-on-spawn": { "version": "1.0.0", @@ -28289,6 +28270,7 @@ }, "readable-stream": { "version": "2.3.7", + "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -28300,10 +28282,12 @@ }, "dependencies": { "safe-buffer": { - "version": "5.1.2" + "version": "5.1.2", + "dev": true }, "string_decoder": { "version": "1.1.1", + "dev": true, "requires": { "safe-buffer": "~5.1.0" } diff --git a/packages/block/src/block.ts b/packages/block/src/block.ts index 6f7753042ed..915901b95ae 100644 --- a/packages/block/src/block.ts +++ b/packages/block/src/block.ts @@ -5,10 +5,9 @@ import { BlobEIP4844Transaction, Capability, TransactionFactory } from '@ethereu import { KECCAK256_RLP, Withdrawal, - arrToBufArr, bigIntToHex, - bufArrToArr, - bufferToHex, + bytesToHex, + equalsBytes, intToHex, isHexPrefixed, ssz, @@ -20,7 +19,7 @@ import { blockFromRpc } from './from-rpc' import { BlockHeader } from './header' import { getDataGasPrice } from './helpers' -import type { BlockBuffer, BlockData, BlockOptions, JsonBlock, JsonRpcBlock } from './types' +import type { BlockBytes, BlockData, BlockOptions, JsonBlock, JsonRpcBlock } from './types' import type { Common } from '@ethereumjs/common' import type { FeeMarketEIP1559Transaction, @@ -28,7 +27,7 @@ import type { TxOptions, TypedTransaction, } from '@ethereumjs/tx' -import type { WithdrawalBuffer } from '@ethereumjs/util' +import type { WithdrawalBytes } from '@ethereumjs/util' /** * An object that represents the block. @@ -49,7 +48,7 @@ export class Block { public static async genWithdrawalsTrieRoot(wts: Withdrawal[], emptyTrie?: Trie) { const trie = emptyTrie ?? new Trie() for (const [i, wt] of wts.entries()) { - await trie.put(Buffer.from(RLP.encode(i)), arrToBufArr(RLP.encode(wt.raw()))) + await trie.put(RLP.encode(i), RLP.encode(wt.raw())) } return trie.root() } @@ -70,7 +69,7 @@ export class Block { public static async genTransactionsTrieRoot(txs: TypedTransaction[], emptyTrie?: Trie) { const trie = emptyTrie ?? new Trie() for (const [i, tx] of txs.entries()) { - await trie.put(Buffer.from(RLP.encode(i)), tx.serialize()) + await trie.put(RLP.encode(i), tx.serialize()) } return trie.root() } @@ -133,8 +132,8 @@ export class Block { * @param serialized * @param opts */ - public static fromRLPSerializedBlock(serialized: Buffer, opts?: BlockOptions) { - const values = arrToBufArr(RLP.decode(Uint8Array.from(serialized))) as BlockBuffer + public static fromRLPSerializedBlock(serialized: Uint8Array, opts?: BlockOptions) { + const values = RLP.decode(Uint8Array.from(serialized)) as BlockBytes if (!Array.isArray(values)) { throw new Error('Invalid serialized block input. Must be array') @@ -144,16 +143,16 @@ export class Block { } /** - * Static constructor to create a block from an array of Buffer values + * Static constructor to create a block from an array of Bytes values * * @param values * @param opts */ - public static fromValuesArray(values: BlockBuffer, opts?: BlockOptions) { + public static fromValuesArray(values: BlockBytes, opts?: BlockOptions) { if (values.length > 4) { throw new Error('invalid block. More values than expected were received') } - const [headerData, txsData, uhsData, withdrawalsBuffer] = values + const [headerData, txsData, uhsData, withdrawalBytes] = values const header = BlockHeader.fromValuesArray(headerData, opts) @@ -189,7 +188,7 @@ export class Block { uncleHeaders.push(BlockHeader.fromValuesArray(uncleHeaderData, uncleOpts)) } - const withdrawals = (withdrawalsBuffer as WithdrawalBuffer[]) + const withdrawals = (withdrawalBytes as WithdrawalBytes[]) ?.map(([index, validatorIndex, address, amount]) => ({ index, validatorIndex, @@ -302,27 +301,27 @@ export class Block { } /** - * Returns a Buffer Array of the raw Buffers of this block, in order. + * Returns a Array of the raw Bytes Arays of this block, in order. */ - raw(): BlockBuffer { - const bufferArray = [ + raw(): BlockBytes { + const bytesArray = [ this.header.raw(), this.transactions.map((tx) => tx.supports(Capability.EIP2718TypedTransaction) ? tx.serialize() : tx.raw() - ) as Buffer[], + ) as Uint8Array[], this.uncleHeaders.map((uh) => uh.raw()), ] const withdrawalsRaw = this.withdrawals?.map((wt) => wt.raw()) if (withdrawalsRaw) { - bufferArray.push(withdrawalsRaw) + bytesArray.push(withdrawalsRaw) } - return bufferArray + return bytesArray } /** * Returns the hash of the block. */ - hash(): Buffer { + hash(): Uint8Array { return this.header.hash() } @@ -336,8 +335,8 @@ export class Block { /** * Returns the rlp encoding of the block. */ - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw()))) + serialize(): Uint8Array { + return RLP.encode(this.raw()) } /** @@ -355,14 +354,14 @@ export class Block { async validateTransactionsTrie(): Promise { let result if (this.transactions.length === 0) { - result = this.header.transactionsTrie.equals(KECCAK256_RLP) + result = equalsBytes(this.header.transactionsTrie, KECCAK256_RLP) return result } - if (this.txTrie.root().equals(KECCAK256_RLP)) { + if (equalsBytes(this.txTrie.root(), KECCAK256_RLP)) { await this.genTxTrie() } - result = this.txTrie.root().equals(this.header.transactionsTrie) + result = equalsBytes(this.txTrie.root(), this.header.transactionsTrie) return result } @@ -477,8 +476,8 @@ export class Block { */ validateUnclesHash(): boolean { const uncles = this.uncleHeaders.map((uh) => uh.raw()) - const raw = RLP.encode(bufArrToArr(uncles)) - return Buffer.from(keccak256(raw)).equals(this.header.uncleHash) + const raw = RLP.encode(uncles) + return equalsBytes(keccak256(raw), this.header.uncleHash) } /** @@ -489,7 +488,7 @@ export class Block { throw new Error('EIP 4895 is not activated') } const withdrawalsRoot = await Block.genWithdrawalsTrieRoot(this.withdrawals!) - return withdrawalsRoot.equals(this.header.withdrawalsRoot!) + return equalsBytes(withdrawalsRoot, this.header.withdrawalsRoot!) } /** @@ -513,7 +512,7 @@ export class Block { } // Header does not count an uncle twice. - const uncleHashes = this.uncleHeaders.map((header) => header.hash().toString('hex')) + const uncleHashes = this.uncleHeaders.map((header) => bytesToHex(header.hash())) if (!(new Set(uncleHashes).size === uncleHashes.length)) { const msg = this._errorMsg('duplicate uncles') throw new Error(msg) @@ -562,7 +561,7 @@ export class Block { public errorStr() { let hash = '' try { - hash = bufferToHex(this.hash()) + hash = bytesToHex(this.hash()) } catch (e: any) { hash = 'error' } diff --git a/packages/block/src/from-rpc.ts b/packages/block/src/from-rpc.ts index 3169df04974..6891aa04922 100644 --- a/packages/block/src/from-rpc.ts +++ b/packages/block/src/from-rpc.ts @@ -1,5 +1,5 @@ import { TransactionFactory } from '@ethereumjs/tx' -import { TypeOutput, setLengthLeft, toBuffer, toType } from '@ethereumjs/util' +import { TypeOutput, setLengthLeft, toBytes, toType } from '@ethereumjs/util' import { blockHeaderFromRpc } from './header-from-rpc' @@ -21,7 +21,7 @@ function normalizeTxParams(_txParams: any) { // strict byte length checking txParams.to = txParams.to !== null && txParams.to !== undefined - ? setLengthLeft(toBuffer(txParams.to), 20) + ? setLengthLeft(toBytes(txParams.to), 20) : null txParams.v = toType(txParams.v, TypeOutput.BigInt) diff --git a/packages/block/src/header.ts b/packages/block/src/header.ts index 7f99a9200d0..35905e0960e 100644 --- a/packages/block/src/header.ts +++ b/packages/block/src/header.ts @@ -5,28 +5,31 @@ import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, TypeOutput, - arrToBufArr, - bigIntToBuffer, + bigIntToBytes, bigIntToHex, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, - bufferToHex, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToHex, + bytesToPrefixedHexString, + concatBytes, + concatBytesNoTypeCheck, ecrecover, ecsign, + equalsBytes, toType, zeros, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { hexToBytes } from 'ethereum-cryptography/utils' import { CLIQUE_EXTRA_SEAL, CLIQUE_EXTRA_VANITY } from './clique' import { valuesArrayToHeaderData } from './helpers' -import type { BlockHeaderBuffer, BlockOptions, HeaderData, JsonHeader } from './types' +import type { BlockHeaderBytes, BlockOptions, HeaderData, JsonHeader } from './types' import type { CliqueConfig } from '@ethereumjs/common' interface HeaderCache { - hash: Buffer | undefined + hash: Uint8Array | undefined } const DEFAULT_GAS_LIMIT = BigInt('0xffffffffffffff') @@ -35,23 +38,23 @@ const DEFAULT_GAS_LIMIT = BigInt('0xffffffffffffff') * An object that represents the block header. */ export class BlockHeader { - public readonly parentHash: Buffer - public readonly uncleHash: Buffer + public readonly parentHash: Uint8Array + public readonly uncleHash: Uint8Array public readonly coinbase: Address - public readonly stateRoot: Buffer - public readonly transactionsTrie: Buffer - public readonly receiptTrie: Buffer - public readonly logsBloom: Buffer + public readonly stateRoot: Uint8Array + public readonly transactionsTrie: Uint8Array + public readonly receiptTrie: Uint8Array + public readonly logsBloom: Uint8Array public readonly difficulty: bigint public readonly number: bigint public readonly gasLimit: bigint public readonly gasUsed: bigint public readonly timestamp: bigint - public readonly extraData: Buffer - public readonly mixHash: Buffer - public readonly nonce: Buffer + public readonly extraData: Uint8Array + public readonly mixHash: Uint8Array + public readonly nonce: Uint8Array public readonly baseFeePerGas?: bigint - public readonly withdrawalsRoot?: Buffer + public readonly withdrawalsRoot?: Uint8Array public readonly excessDataGas?: bigint public readonly _common: Common @@ -89,28 +92,28 @@ export class BlockHeader { * @param serializedHeaderData * @param opts */ - public static fromRLPSerializedHeader(serializedHeaderData: Buffer, opts: BlockOptions = {}) { - const values = arrToBufArr(RLP.decode(Uint8Array.from(serializedHeaderData))) + public static fromRLPSerializedHeader(serializedHeaderData: Uint8Array, opts: BlockOptions = {}) { + const values = RLP.decode(serializedHeaderData) if (!Array.isArray(values)) { throw new Error('Invalid serialized header input. Must be array') } - return BlockHeader.fromValuesArray(values as Buffer[], opts) + return BlockHeader.fromValuesArray(values as Uint8Array[], opts) } /** - * Static constructor to create a block header from an array of Buffer values + * Static constructor to create a block header from an array of Bytes values * * @param values * @param opts */ - public static fromValuesArray(values: BlockHeaderBuffer, opts: BlockOptions = {}) { + public static fromValuesArray(values: BlockHeaderBytes, opts: BlockOptions = {}) { const headerData = valuesArrayToHeaderData(values) const { number, baseFeePerGas } = headerData // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (opts.common?.isActivatedEIP(1559) && baseFeePerGas === undefined) { - const eip1559ActivationBlock = bigIntToBuffer(opts.common?.eipBlock(1559)!) + const eip1559ActivationBlock = bigIntToBytes(opts.common?.eipBlock(1559)!) // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions - if (eip1559ActivationBlock && eip1559ActivationBlock.equals(number! as Buffer)) { + if (eip1559ActivationBlock && equalsBytes(eip1559ActivationBlock, number as Uint8Array)) { throw new Error('invalid header. baseFeePerGas should be provided') } } @@ -153,29 +156,30 @@ export class BlockHeader { gasLimit: DEFAULT_GAS_LIMIT, gasUsed: BigInt(0), timestamp: BigInt(0), - extraData: Buffer.from([]), + extraData: new Uint8Array(0), mixHash: zeros(32), nonce: zeros(8), } - const parentHash = toType(headerData.parentHash, TypeOutput.Buffer) ?? defaults.parentHash - const uncleHash = toType(headerData.uncleHash, TypeOutput.Buffer) ?? defaults.uncleHash + const parentHash = toType(headerData.parentHash, TypeOutput.Uint8Array) ?? defaults.parentHash + const uncleHash = toType(headerData.uncleHash, TypeOutput.Uint8Array) ?? defaults.uncleHash const coinbase = new Address( - toType(headerData.coinbase ?? defaults.coinbase, TypeOutput.Buffer) + toType(headerData.coinbase ?? defaults.coinbase, TypeOutput.Uint8Array) ) - const stateRoot = toType(headerData.stateRoot, TypeOutput.Buffer) ?? defaults.stateRoot + const stateRoot = toType(headerData.stateRoot, TypeOutput.Uint8Array) ?? defaults.stateRoot const transactionsTrie = - toType(headerData.transactionsTrie, TypeOutput.Buffer) ?? defaults.transactionsTrie - const receiptTrie = toType(headerData.receiptTrie, TypeOutput.Buffer) ?? defaults.receiptTrie - const logsBloom = toType(headerData.logsBloom, TypeOutput.Buffer) ?? defaults.logsBloom + toType(headerData.transactionsTrie, TypeOutput.Uint8Array) ?? defaults.transactionsTrie + const receiptTrie = + toType(headerData.receiptTrie, TypeOutput.Uint8Array) ?? defaults.receiptTrie + const logsBloom = toType(headerData.logsBloom, TypeOutput.Uint8Array) ?? defaults.logsBloom const difficulty = toType(headerData.difficulty, TypeOutput.BigInt) ?? defaults.difficulty const number = toType(headerData.number, TypeOutput.BigInt) ?? defaults.number const gasLimit = toType(headerData.gasLimit, TypeOutput.BigInt) ?? defaults.gasLimit const gasUsed = toType(headerData.gasUsed, TypeOutput.BigInt) ?? defaults.gasUsed const timestamp = toType(headerData.timestamp, TypeOutput.BigInt) ?? defaults.timestamp - const extraData = toType(headerData.extraData, TypeOutput.Buffer) ?? defaults.extraData - const mixHash = toType(headerData.mixHash, TypeOutput.Buffer) ?? defaults.mixHash - const nonce = toType(headerData.nonce, TypeOutput.Buffer) ?? defaults.nonce + const extraData = toType(headerData.extraData, TypeOutput.Uint8Array) ?? defaults.extraData + const mixHash = toType(headerData.mixHash, TypeOutput.Uint8Array) ?? defaults.mixHash + const nonce = toType(headerData.nonce, TypeOutput.Uint8Array) ?? defaults.nonce const hardforkByBlockNumber = options.hardforkByBlockNumber ?? false if (hardforkByBlockNumber || options.hardforkByTTD !== undefined) { @@ -196,7 +200,7 @@ export class BlockHeader { const baseFeePerGas = toType(headerData.baseFeePerGas, TypeOutput.BigInt) ?? hardforkDefaults.baseFeePerGas const withdrawalsRoot = - toType(headerData.withdrawalsRoot, TypeOutput.Buffer) ?? hardforkDefaults.withdrawalsRoot + toType(headerData.withdrawalsRoot, TypeOutput.Uint8Array) ?? hardforkDefaults.withdrawalsRoot const excessDataGas = toType(headerData.excessDataGas, TypeOutput.BigInt) ?? hardforkDefaults.excessDataGas @@ -251,7 +255,7 @@ export class BlockHeader { const minExtraDataLength = CLIQUE_EXTRA_VANITY + CLIQUE_EXTRA_SEAL if (this.extraData.length < minExtraDataLength) { const remainingLength = minExtraDataLength - this.extraData.length - this.extraData = Buffer.concat([this.extraData, Buffer.alloc(remainingLength)]) + this.extraData = concatBytes(this.extraData, new Uint8Array(remainingLength)) } this.extraData = this.cliqueSealBlock(options.cliqueSigner) @@ -390,7 +394,7 @@ export class BlockHeader { } } // MixHash format - if (!this.mixHash.equals(Buffer.alloc(32))) { + if (!equalsBytes(this.mixHash, new Uint8Array(32))) { const msg = this._errorMsg(`mixHash must be filled with zeros, received ${this.mixHash}`) throw new Error(msg) } @@ -400,10 +404,10 @@ export class BlockHeader { let error = false let errorMsg = '' - if (!uncleHash.equals(KECCAK256_RLP_ARRAY)) { - errorMsg += `, uncleHash: ${uncleHash.toString( - 'hex' - )} (expected: ${KECCAK256_RLP_ARRAY.toString('hex')})` + if (!equalsBytes(uncleHash, KECCAK256_RLP_ARRAY)) { + errorMsg += `, uncleHash: ${bytesToHex(uncleHash)} (expected: ${bytesToHex( + KECCAK256_RLP_ARRAY + )})` error = true } if (number !== BigInt(0)) { @@ -413,18 +417,18 @@ export class BlockHeader { error = true } if (extraData.length > 32) { - errorMsg += `, extraData: ${extraData.toString( - 'hex' + errorMsg += `, extraData: ${bytesToHex( + extraData )} (cannot exceed 32 bytes length, received ${extraData.length} bytes)` error = true } - if (!nonce.equals(zeros(8))) { - errorMsg += `, nonce: ${nonce.toString('hex')} (expected: ${zeros(8).toString('hex')})` + if (!equalsBytes(nonce, zeros(8))) { + errorMsg += `, nonce: ${bytesToHex(nonce)} (expected: ${bytesToHex(zeros(8))})` error = true } } if (error) { - const msg = this._errorMsg(`Invalid PoS block${errorMsg}`) + const msg = this._errorMsg(`Invalid PoS block: ${errorMsg}`) throw new Error(msg) } } @@ -520,36 +524,36 @@ export class BlockHeader { } /** - * Returns a Buffer Array of the raw Buffers in this header, in order. + * Returns a Uint8Array Array of the raw Bytes in this header, in order. */ - raw(): BlockHeaderBuffer { + raw(): BlockHeaderBytes { const rawItems = [ this.parentHash, this.uncleHash, - this.coinbase.buf, + this.coinbase.bytes, this.stateRoot, this.transactionsTrie, this.receiptTrie, this.logsBloom, - bigIntToUnpaddedBuffer(this.difficulty), - bigIntToUnpaddedBuffer(this.number), - bigIntToUnpaddedBuffer(this.gasLimit), - bigIntToUnpaddedBuffer(this.gasUsed), - bigIntToUnpaddedBuffer(this.timestamp ?? BigInt(0)), + bigIntToUnpaddedBytes(this.difficulty), + bigIntToUnpaddedBytes(this.number), + bigIntToUnpaddedBytes(this.gasLimit), + bigIntToUnpaddedBytes(this.gasUsed), + bigIntToUnpaddedBytes(this.timestamp ?? BigInt(0)), this.extraData, this.mixHash, this.nonce, ] if (this._common.isActivatedEIP(1559) === true) { - rawItems.push(bigIntToUnpaddedBuffer(this.baseFeePerGas!)) + rawItems.push(bigIntToUnpaddedBytes(this.baseFeePerGas!)) } if (this._common.isActivatedEIP(4895) === true) { rawItems.push(this.withdrawalsRoot!) } if (this._common.isActivatedEIP(4844) === true) { - rawItems.push(bigIntToUnpaddedBuffer(this.excessDataGas!)) + rawItems.push(bigIntToUnpaddedBytes(this.excessDataGas!)) } return rawItems @@ -558,15 +562,15 @@ export class BlockHeader { /** * Returns the hash of the block header. */ - hash(): Buffer { + hash(): Uint8Array { if (Object.isFrozen(this)) { if (!this.cache.hash) { - this.cache.hash = Buffer.from(keccak256(RLP.encode(bufArrToArr(this.raw())))) + this.cache.hash = keccak256(RLP.encode(this.raw())) } return this.cache.hash } - return Buffer.from(keccak256(RLP.encode(bufArrToArr(this.raw())))) + return keccak256(RLP.encode(this.raw())) } /** @@ -614,7 +618,7 @@ export class BlockHeader { if (this._common.hardforkGteHardfork(hardfork, Hardfork.Byzantium) === true) { // max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99) (EIP100) - const uncleAddend = parentBlockHeader.uncleHash.equals(KECCAK256_RLP_ARRAY) ? 1 : 2 + const uncleAddend = equalsBytes(parentBlockHeader.uncleHash, KECCAK256_RLP_ARRAY) ? 1 : 2 let a = BigInt(uncleAddend) - (blockTs - parentTs) / BigInt(9) const cutoff = BigInt(-99) // MAX(cutoff, a) @@ -666,8 +670,8 @@ export class BlockHeader { cliqueSigHash() { this._requireClique('cliqueSigHash') const raw = this.raw() - raw[12] = this.extraData.slice(0, this.extraData.length - CLIQUE_EXTRA_SEAL) - return Buffer.from(keccak256(RLP.encode(bufArrToArr(raw)))) + raw[12] = this.extraData.subarray(0, this.extraData.length - CLIQUE_EXTRA_SEAL) + return keccak256(RLP.encode(raw)) } /** @@ -686,18 +690,18 @@ export class BlockHeader { * Returns extra vanity data * (only clique PoA, throws otherwise) */ - cliqueExtraVanity(): Buffer { + cliqueExtraVanity(): Uint8Array { this._requireClique('cliqueExtraVanity') - return this.extraData.slice(0, CLIQUE_EXTRA_VANITY) + return this.extraData.subarray(0, CLIQUE_EXTRA_VANITY) } /** * Returns extra seal data * (only clique PoA, throws otherwise) */ - cliqueExtraSeal(): Buffer { + cliqueExtraSeal(): Uint8Array { this._requireClique('cliqueExtraSeal') - return this.extraData.slice(-CLIQUE_EXTRA_SEAL) + return this.extraData.subarray(-CLIQUE_EXTRA_SEAL) } /** @@ -705,18 +709,21 @@ export class BlockHeader { * Returns the final extraData field to be assigned to `this.extraData`. * @hidden */ - private cliqueSealBlock(privateKey: Buffer) { + private cliqueSealBlock(privateKey: Uint8Array) { this._requireClique('cliqueSealBlock') const signature = ecsign(this.cliqueSigHash(), privateKey) - const signatureB = Buffer.concat([ + const signatureB = concatBytesNoTypeCheck( signature.r, signature.s, - bigIntToBuffer(signature.v - BigInt(27)), - ]) + bigIntToBytes(signature.v - BigInt(27)) + ) - const extraDataWithoutSeal = this.extraData.slice(0, this.extraData.length - CLIQUE_EXTRA_SEAL) - const extraData = Buffer.concat([extraDataWithoutSeal, signatureB]) + const extraDataWithoutSeal = this.extraData.subarray( + 0, + this.extraData.length - CLIQUE_EXTRA_SEAL + ) + const extraData = concatBytesNoTypeCheck(extraDataWithoutSeal, signatureB) return extraData } @@ -737,12 +744,12 @@ export class BlockHeader { const start = CLIQUE_EXTRA_VANITY const end = this.extraData.length - CLIQUE_EXTRA_SEAL - const signerBuffer = this.extraData.slice(start, end) + const signerBytes = this.extraData.subarray(start, end) - const signerList: Buffer[] = [] + const signerList: Uint8Array[] = [] const signerLength = 20 - for (let start = 0; start <= signerBuffer.length - signerLength; start += signerLength) { - signerList.push(signerBuffer.slice(start, start + signerLength)) + for (let start = 0; start <= signerBytes.length - signerLength; start += signerLength) { + signerList.push(signerBytes.subarray(start, start + signerLength)) } return signerList.map((buf) => new Address(buf)) } @@ -769,12 +776,12 @@ export class BlockHeader { this._requireClique('cliqueSigner') const extraSeal = this.cliqueExtraSeal() // Reasonable default for default blocks - if (extraSeal.length === 0 || extraSeal.equals(Buffer.alloc(65).fill(0))) { + if (extraSeal.length === 0 || equalsBytes(extraSeal, new Uint8Array(65))) { return Address.zero() } - const r = extraSeal.slice(0, 32) - const s = extraSeal.slice(32, 64) - const v = bufferToBigInt(extraSeal.slice(64, 65)) + BigInt(27) + const r = extraSeal.subarray(0, 32) + const s = extraSeal.subarray(32, 64) + const v = bytesToBigInt(extraSeal.subarray(64, 65)) + BigInt(27) const pubKey = ecrecover(this.cliqueSigHash(), v, r, s) return Address.fromPublicKey(pubKey) } @@ -782,8 +789,8 @@ export class BlockHeader { /** * Returns the rlp encoding of the block header. */ - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw()))) + serialize(): Uint8Array { + return RLP.encode(this.raw()) } /** @@ -791,25 +798,25 @@ export class BlockHeader { */ toJSON(): JsonHeader { const withdrawalAttr = this.withdrawalsRoot - ? { withdrawalsRoot: '0x' + this.withdrawalsRoot.toString('hex') } + ? { withdrawalsRoot: bytesToPrefixedHexString(this.withdrawalsRoot) } : {} const jsonDict: JsonHeader = { - parentHash: '0x' + this.parentHash.toString('hex'), - uncleHash: '0x' + this.uncleHash.toString('hex'), + parentHash: bytesToPrefixedHexString(this.parentHash), + uncleHash: bytesToPrefixedHexString(this.uncleHash), coinbase: this.coinbase.toString(), - stateRoot: '0x' + this.stateRoot.toString('hex'), - transactionsTrie: '0x' + this.transactionsTrie.toString('hex'), + stateRoot: bytesToPrefixedHexString(this.stateRoot), + transactionsTrie: bytesToPrefixedHexString(this.transactionsTrie), ...withdrawalAttr, - receiptTrie: '0x' + this.receiptTrie.toString('hex'), - logsBloom: '0x' + this.logsBloom.toString('hex'), + receiptTrie: bytesToPrefixedHexString(this.receiptTrie), + logsBloom: bytesToPrefixedHexString(this.logsBloom), difficulty: bigIntToHex(this.difficulty), number: bigIntToHex(this.number), gasLimit: bigIntToHex(this.gasLimit), gasUsed: bigIntToHex(this.gasUsed), timestamp: bigIntToHex(this.timestamp), - extraData: '0x' + this.extraData.toString('hex'), - mixHash: '0x' + this.mixHash.toString('hex'), - nonce: '0x' + this.nonce.toString('hex'), + extraData: bytesToPrefixedHexString(this.extraData), + mixHash: bytesToPrefixedHexString(this.mixHash), + nonce: bytesToPrefixedHexString(this.nonce), } if (this._common.isActivatedEIP(1559) === true) { jsonDict.baseFeePerGas = bigIntToHex(this.baseFeePerGas!) @@ -832,10 +839,10 @@ export class BlockHeader { if (DAOActivationBlock === null || this.number < DAOActivationBlock) { return } - const DAO_ExtraData = Buffer.from('64616f2d686172642d666f726b', 'hex') + const DAO_ExtraData = hexToBytes('64616f2d686172642d666f726b') const DAO_ForceExtraDataRange = BigInt(9) const drift = this.number - DAOActivationBlock - if (drift <= DAO_ForceExtraDataRange && !this.extraData.equals(DAO_ExtraData)) { + if (drift <= DAO_ForceExtraDataRange && !equalsBytes(this.extraData, DAO_ExtraData)) { const msg = this._errorMsg("extraData should be 'dao-hard-fork'") throw new Error(msg) } @@ -847,7 +854,7 @@ export class BlockHeader { public errorStr() { let hash = '' try { - hash = bufferToHex(this.hash()) + hash = bytesToHex(this.hash()) } catch (e: any) { hash = 'error' } diff --git a/packages/block/src/helpers.ts b/packages/block/src/helpers.ts index a89a35cc5dc..8c3fcd4dab7 100644 --- a/packages/block/src/helpers.ts +++ b/packages/block/src/helpers.ts @@ -1,7 +1,7 @@ import { TypeOutput, isHexString, toType } from '@ethereumjs/util' import type { BlockHeader } from './header' -import type { BlockHeaderBuffer, HeaderData } from './types' +import type { BlockHeaderBytes, HeaderData } from './types' /** * Returns a 0x-prefixed hex number string from a hex string or string integer. @@ -20,7 +20,7 @@ export const numberToHex = function (input?: string) { return input } -export function valuesArrayToHeaderData(values: BlockHeaderBuffer): HeaderData { +export function valuesArrayToHeaderData(values: BlockHeaderBytes): HeaderData { const [ parentHash, uncleHash, diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 1fa494f2bf7..3fe11f549c2 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -10,9 +10,9 @@ import type { import type { AddressLike, BigIntLike, - BufferLike, + BytesLike, JsonRpcWithdrawal, - WithdrawalBuffer, + WithdrawalBytes, WithdrawalData, } from '@ethereumjs/util' @@ -76,7 +76,7 @@ export interface BlockOptions { * Provide a clique signer's privateKey to seal this block. * Will throw if provided on a non-PoA chain. */ - cliqueSigner?: Buffer + cliqueSigner?: Uint8Array /** * Skip consensus format validation checks on header if set. Defaults to false. */ @@ -87,23 +87,23 @@ export interface BlockOptions { * A block header's data. */ export interface HeaderData { - parentHash?: BufferLike - uncleHash?: BufferLike + parentHash?: BytesLike + uncleHash?: BytesLike coinbase?: AddressLike - stateRoot?: BufferLike - transactionsTrie?: BufferLike - receiptTrie?: BufferLike - logsBloom?: BufferLike + stateRoot?: BytesLike + transactionsTrie?: BytesLike + receiptTrie?: BytesLike + logsBloom?: BytesLike difficulty?: BigIntLike number?: BigIntLike gasLimit?: BigIntLike gasUsed?: BigIntLike timestamp?: BigIntLike - extraData?: BufferLike - mixHash?: BufferLike - nonce?: BufferLike + extraData?: BytesLike + mixHash?: BytesLike + nonce?: BytesLike baseFeePerGas?: BigIntLike - withdrawalsRoot?: BufferLike + withdrawalsRoot?: BytesLike excessDataGas?: BigIntLike } @@ -120,18 +120,18 @@ export interface BlockData { withdrawals?: Array } -export type WithdrawalsBuffer = WithdrawalBuffer[] +export type WithdrawalsBytes = WithdrawalBytes[] -export type BlockBuffer = - | [BlockHeaderBuffer, TransactionsBuffer, UncleHeadersBuffer] - | [BlockHeaderBuffer, TransactionsBuffer, UncleHeadersBuffer, WithdrawalsBuffer] -export type BlockHeaderBuffer = Buffer[] -export type BlockBodyBuffer = [TransactionsBuffer, UncleHeadersBuffer, WithdrawalsBuffer?] +export type BlockBytes = + | [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes] + | [BlockHeaderBytes, TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes] +export type BlockHeaderBytes = Uint8Array[] +export type BlockBodyBytes = [TransactionsBytes, UncleHeadersBytes, WithdrawalsBytes?] /** - * TransactionsBuffer can be an array of serialized txs for Typed Transactions or an array of Buffer Arrays for legacy transactions. + * TransactionsBytes can be an array of serialized txs for Typed Transactions or an array of Uint8Array Arrays for legacy transactions. */ -export type TransactionsBuffer = Buffer[][] | Buffer[] -export type UncleHeadersBuffer = Buffer[][] +export type TransactionsBytes = Uint8Array[][] | Uint8Array[] +export type UncleHeadersBytes = Uint8Array[][] /** * An object with the block's data represented as strings. diff --git a/packages/block/test/block.spec.ts b/packages/block/test/block.spec.ts index e7e6a3a73ec..9857da82725 100644 --- a/packages/block/test/block.spec.ts +++ b/packages/block/test/block.spec.ts @@ -1,6 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { toBuffer, zeros } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexStringToBytes, toBytes, zeros } from '@ethereumjs/util' import * as tape from 'tape' // explicitly import util, needed for karma-typescript bundling // eslint-disable-next-line @typescript-eslint/no-unused-vars, simple-import-sort/imports @@ -15,14 +15,14 @@ import * as testDataPreLondon2 from './testdata/testdata_pre-london-2.json' import * as testDataPreLondon from './testdata/testdata_pre-london.json' import * as testnetMerge from './testdata/testnetMerge.json' -import type { BlockBuffer } from '../src' +import type { BlockBytes } from '../src' import type { NestedUint8Array } from '@ethereumjs/util' tape('[Block]: block functions', function (t) { t.test('should test block initialization', function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const genesis = Block.fromBlockData({}, { common }) - st.ok(genesis.hash().toString('hex'), 'block should initialize') + st.ok(bytesToHex(genesis.hash()), 'block should initialize') // test default freeze values // also test if the options are carried over to the constructor @@ -39,22 +39,22 @@ tape('[Block]: block functions', function (t) { block = Block.fromRLPSerializedBlock(rlpBlock, { freeze: false }) st.ok(!Object.isFrozen(block), 'block should not be frozen when freeze deactivated in options') - const zero = Buffer.alloc(0) + const zero = new Uint8Array(0) const headerArray = [] for (let item = 0; item < 15; item++) { headerArray.push(zero) } // mock header data (if set to zeros(0) header throws) - headerArray[0] = zeros(32) //parentHash - headerArray[2] = zeros(20) //coinbase - headerArray[3] = zeros(32) //stateRoot - headerArray[4] = zeros(32) //transactionsTrie - headerArray[5] = zeros(32) //receiptTrie + headerArray[0] = zeros(32) // parentHash + headerArray[2] = zeros(20) // coinbase + headerArray[3] = zeros(32) // stateRoot + headerArray[4] = zeros(32) // transactionsTrie + headerArray[5] = zeros(32) // receiptTrie headerArray[13] = zeros(32) // mixHash headerArray[14] = zeros(8) // nonce - const valuesArray = [headerArray, [], []] + const valuesArray = [headerArray, [], []] block = Block.fromValuesArray(valuesArray, { common }) st.ok(Object.isFrozen(block), 'block should be frozen by default') @@ -77,7 +77,7 @@ tape('[Block]: block functions', function (t) { { header: { number: 12, // Berlin block - extraData: Buffer.alloc(97), + extraData: new Uint8Array(97), }, }, { common, hardforkByBlockNumber: true } @@ -102,7 +102,7 @@ tape('[Block]: block functions', function (t) { { header: { number: 12, // Berlin block, - extraData: Buffer.alloc(97), + extraData: new Uint8Array(97), }, }, { common, hardforkByTTD: 3000 } @@ -151,7 +151,7 @@ tape('[Block]: block functions', function (t) { function (st) { const common = new Common({ chain: Chain.Rinkeby }) const uncleBlock = Block.fromBlockData( - { header: { extraData: Buffer.alloc(117) } }, + { header: { extraData: new Uint8Array(117) } }, { common } ) st.throws(function () { @@ -163,7 +163,7 @@ tape('[Block]: block functions', function (t) { t.test('should test block validation on pow chain', async function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const blockRlp = toBuffer(testDataPreLondon.blocks[0].rlp) + const blockRlp = toBytes(testDataPreLondon.blocks[0].rlp) try { Block.fromRLPSerializedBlock(blockRlp, { common }) st.pass('should pass') @@ -189,11 +189,11 @@ tape('[Block]: block functions', function (t) { } t.test('should test transaction validation', async function (st) { - const blockRlp = toBuffer(testDataPreLondon.blocks[0].rlp) + const blockRlp = toBytes(testDataPreLondon.blocks[0].rlp) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) await testTransactionValidation(st, block) - ;(block.header as any).transactionsTrie = Buffer.alloc(32) + ;(block.header as any).transactionsTrie = new Uint8Array(32) try { await block.validateData() st.fail('should throw') @@ -209,7 +209,7 @@ tape('[Block]: block functions', function (t) { t.test('should test transaction validation with legacy tx in london', async function (st) { const common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.London }) - const blockRlp = toBuffer(testDataPreLondon.blocks[0].rlp) + const blockRlp = toBytes(testDataPreLondon.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) await testTransactionValidation(st, block) ;(block.transactions[0] as any).gasPrice = BigInt(0) @@ -222,10 +222,10 @@ tape('[Block]: block functions', function (t) { t.test('should test uncles hash validation', async function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const blockRlp = toBuffer(testDataPreLondon2.blocks[2].rlp) + const blockRlp = toBytes(testDataPreLondon2.blocks[2].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) st.equal(block.validateUnclesHash(), true) - ;(block.header as any).uncleHash = Buffer.alloc(32) + ;(block.header as any).uncleHash = new Uint8Array(32) try { await block.validateData() st.fail('should throw') @@ -253,10 +253,10 @@ tape('[Block]: block functions', function (t) { t.test('should test genesis hashes (mainnet default)', function (st) { const common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.Chainstart }) - const rlp = Buffer.from(testDataGenesis.test.genesis_rlp_hex, 'hex') - const hash = Buffer.from(testDataGenesis.test.genesis_hash, 'hex') + const rlp = hexStringToBytes(testDataGenesis.test.genesis_rlp_hex) + const hash = hexStringToBytes(testDataGenesis.test.genesis_hash) const block = Block.fromRLPSerializedBlock(rlp, { common }) - st.ok(block.hash().equals(hash), 'genesis hash match') + st.ok(equalsBytes(block.hash(), hash), 'genesis hash match') st.end() }) @@ -272,17 +272,17 @@ tape('[Block]: block functions', function (t) { t.test('should return the same block data from raw()', function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const block = Block.fromRLPSerializedBlock(toBuffer(testDataPreLondon2.blocks[2].rlp), { + const block = Block.fromRLPSerializedBlock(toBytes(testDataPreLondon2.blocks[2].rlp), { common, }) const blockFromRaw = Block.fromValuesArray(block.raw(), { common }) - st.ok(block.hash().equals(blockFromRaw.hash())) + st.ok(equalsBytes(block.hash(), blockFromRaw.hash())) st.end() }) t.test('should test toJSON', function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const block = Block.fromRLPSerializedBlock(toBuffer(testDataPreLondon2.blocks[2].rlp), { + const block = Block.fromRLPSerializedBlock(toBytes(testDataPreLondon2.blocks[2].rlp), { common, }) st.equal(typeof block.toJSON(), 'object') @@ -292,22 +292,22 @@ tape('[Block]: block functions', function (t) { t.test('DAO hardfork', function (st) { const blockData = RLP.decode(testDataPreLondon2.blocks[0].rlp) as NestedUint8Array // Set block number from test block to mainnet DAO fork block 1920000 - blockData[0][8] = Buffer.from('1D4C00', 'hex') + blockData[0][8] = hexStringToBytes('1D4C00') const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Dao }) st.throws( function () { - Block.fromValuesArray(blockData as BlockBuffer, { common }) + Block.fromValuesArray(blockData as BlockBytes, { common }) }, /Error: extraData should be 'dao-hard-fork'/, 'should throw on DAO HF block with wrong extra data' ) // eslint-disable-line // Set extraData to dao-hard-fork - blockData[0][12] = Buffer.from('64616f2d686172642d666f726b', 'hex') + blockData[0][12] = hexStringToBytes('64616f2d686172642d666f726b') st.doesNotThrow(function () { - Block.fromValuesArray(blockData as BlockBuffer, { common }) + Block.fromValuesArray(blockData as BlockBytes, { common }) }, 'should not throw on DAO HF block with correct extra data') st.end() }) diff --git a/packages/block/test/clique.spec.ts b/packages/block/test/clique.spec.ts index 947a2c8c214..8ca4bdb09b2 100644 --- a/packages/block/test/clique.spec.ts +++ b/packages/block/test/clique.spec.ts @@ -1,5 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address } from '@ethereumjs/util' +import { Address, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { BlockHeader } from '../src/header' @@ -13,44 +13,47 @@ tape('[Header]: Clique PoA Functionality', function (t) { header.cliqueIsEpochTransition() }, 'cliqueIsEpochTransition() -> should throw on PoW networks') - header = BlockHeader.fromHeaderData({ extraData: Buffer.alloc(97) }, { common }) + header = BlockHeader.fromHeaderData({ extraData: new Uint8Array(97) }, { common }) st.ok( header.cliqueIsEpochTransition(), 'cliqueIsEpochTransition() -> should indicate an epoch transition for the genesis block' ) - header = BlockHeader.fromHeaderData({ number: 1, extraData: Buffer.alloc(97) }, { common }) + header = BlockHeader.fromHeaderData({ number: 1, extraData: new Uint8Array(97) }, { common }) st.notOk( header.cliqueIsEpochTransition(), 'cliqueIsEpochTransition() -> should correctly identify a non-epoch block' ) st.deepEqual( header.cliqueExtraVanity(), - Buffer.alloc(32), + new Uint8Array(32), 'cliqueExtraVanity() -> should return correct extra vanity value' ) st.deepEqual( header.cliqueExtraSeal(), - Buffer.alloc(65), + new Uint8Array(65), 'cliqueExtraSeal() -> should return correct extra seal value' ) st.throws(() => { header.cliqueEpochTransitionSigners() }, 'cliqueEpochTransitionSigners() -> should throw on non-epch block') - header = BlockHeader.fromHeaderData({ number: 60000, extraData: Buffer.alloc(137) }, { common }) + header = BlockHeader.fromHeaderData( + { number: 60000, extraData: new Uint8Array(137) }, + { common } + ) st.ok( header.cliqueIsEpochTransition(), 'cliqueIsEpochTransition() -> should correctly identify an epoch block' ) st.deepEqual( header.cliqueExtraVanity(), - Buffer.alloc(32), + new Uint8Array(32), 'cliqueExtraVanity() -> should return correct extra vanity value' ) st.deepEqual( header.cliqueExtraSeal(), - Buffer.alloc(65), + new Uint8Array(65), 'cliqueExtraSeal() -> should return correct extra seal value' ) const msg = @@ -62,19 +65,17 @@ tape('[Header]: Clique PoA Functionality', function (t) { type Signer = { address: Address - privateKey: Buffer - publicKey: Buffer + privateKey: Uint8Array + publicKey: Uint8Array } const A: Signer = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' + address: new Address(hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexStringToBytes( + '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993' ), - publicKey: Buffer.from( - '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195', - 'hex' + publicKey: hexStringToBytes( + '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195' ), } @@ -82,7 +83,7 @@ tape('[Header]: Clique PoA Functionality', function (t) { const cliqueSigner = A.privateKey let header = BlockHeader.fromHeaderData( - { number: 1, extraData: Buffer.alloc(97) }, + { number: 1, extraData: new Uint8Array(97) }, { common, freeze: false, cliqueSigner } ) @@ -90,7 +91,7 @@ tape('[Header]: Clique PoA Functionality', function (t) { st.ok(header.cliqueVerifySignature([A.address]), 'should verify signature') st.ok(header.cliqueSigner().equals(A.address), 'should recover the correct signer address') - header = BlockHeader.fromHeaderData({ extraData: Buffer.alloc(97) }, { common }) + header = BlockHeader.fromHeaderData({ extraData: new Uint8Array(97) }, { common }) st.ok( header.cliqueSigner().equals(Address.zero()), 'should return zero address on default block' diff --git a/packages/block/test/difficulty.spec.ts b/packages/block/test/difficulty.spec.ts index 05cf8afdfac..c3b4fab6ac1 100644 --- a/packages/block/test/difficulty.spec.ts +++ b/packages/block/test/difficulty.spec.ts @@ -1,5 +1,4 @@ import { Chain, Common } from '@ethereumjs/common' -import { bufferToInt } from '@ethereumjs/util' import * as tape from 'tape' import { Block } from '../src' @@ -112,7 +111,7 @@ tape('[Header]: difficulty tests', (t) => { header: { timestamp: test.parentTimestamp, difficulty: test.parentDifficulty, - number: bufferToInt(test.currentBlockNumber) - 1, + number: BigInt(test.currentBlockNumber) - BigInt(1), uncleHash, }, }, diff --git a/packages/block/test/eip1559block.spec.ts b/packages/block/test/eip1559block.spec.ts index 6942bc0a754..2384eb74d25 100644 --- a/packages/block/test/eip1559block.spec.ts +++ b/packages/block/test/eip1559block.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Block } from '../src/block' @@ -168,7 +169,7 @@ tape('EIP1559 tests', function (t) { parentHash: block1.hash(), timestamp: BigInt(2), gasLimit: genesis.header.gasLimit * BigInt(2), // Special case on EIP-1559 transition block - baseFeePerGas: Buffer.from('342770c0', 'hex'), + baseFeePerGas: hexStringToBytes('342770c0'), }, }, { @@ -283,7 +284,7 @@ tape('EIP1559 tests', function (t) { parentHash: block1.hash(), timestamp: BigInt(2), gasLimit: parentGasLimit + parentGasLimit / BigInt(1024) - BigInt(1), - baseFeePerGas: Buffer.from('342770c0', 'hex'), + baseFeePerGas: hexStringToBytes('342770c0'), }, { calcDifficultyFromHeader: block1.header, @@ -299,7 +300,7 @@ tape('EIP1559 tests', function (t) { parentHash: block1.hash(), timestamp: BigInt(2), gasLimit: parentGasLimit - parentGasLimit / BigInt(1024) + BigInt(1), - baseFeePerGas: Buffer.from('342770c0', 'hex'), + baseFeePerGas: hexStringToBytes('342770c0'), }, { calcDifficultyFromHeader: block1.header, @@ -343,7 +344,7 @@ tape('EIP1559 tests', function (t) { parentHash: block1.hash(), timestamp: BigInt(2), gasLimit: parentGasLimit + parentGasLimit / BigInt(1024), - baseFeePerGas: Buffer.from('342770c0', 'hex'), + baseFeePerGas: hexStringToBytes('342770c0'), }, { calcDifficultyFromHeader: block1.header, @@ -394,7 +395,7 @@ tape('EIP1559 tests', function (t) { parentHash: block1.hash(), timestamp: BigInt(2), gasLimit: parentGasLimit - parentGasLimit / BigInt(1024), - baseFeePerGas: Buffer.from('342770c0', 'hex'), + baseFeePerGas: hexStringToBytes('342770c0'), }, { calcDifficultyFromHeader: block1.header, @@ -420,7 +421,7 @@ tape('EIP1559 tests', function (t) { maxPriorityFeePerGas: BigInt(0), }, { common } - ).sign(Buffer.from('46'.repeat(32), 'hex')) + ).sign(hexStringToBytes('46'.repeat(32))) const block = Block.fromBlockData( { header: { diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index 2d048de2251..c38fe887380 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -5,8 +5,8 @@ import { commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' +import { randomBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import * as tape from 'tape' import { Block, calcExcessDataGas, getDataGasPrice } from '../src' @@ -129,11 +129,9 @@ tape('data gas tests', async (t) => { const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const bufferedHashes = versionedHashes.map((el) => Buffer.from(el)) - const unsignedTx = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, maxFeePerDataGas: 100000000n, @@ -161,11 +159,9 @@ tape('transaction validation tests', async (t) => { const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const bufferedHashes = versionedHashes.map((el) => Buffer.from(el)) - const tx1 = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, maxFeePerDataGas: 100000000n, @@ -176,7 +172,7 @@ tape('transaction validation tests', async (t) => { ).sign(randomBytes(32)) const tx2 = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, maxFeePerDataGas: 1n, diff --git a/packages/block/test/eip4895block.spec.ts b/packages/block/test/eip4895block.spec.ts index bbda3f6f55c..1289c1b0c7f 100644 --- a/packages/block/test/eip4895block.spec.ts +++ b/packages/block/test/eip4895block.spec.ts @@ -1,12 +1,12 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { decode } from '@ethereumjs/rlp' -import { Address, KECCAK256_RLP, Withdrawal } from '@ethereumjs/util' +import { Address, KECCAK256_RLP, Withdrawal, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Block } from '../src/block' import { BlockHeader } from '../src/header' -import type { WithdrawalBuffer, WithdrawalData } from '@ethereumjs/util' +import type { WithdrawalBytes, WithdrawalData } from '@ethereumjs/util' const gethWithdrawals8BlockRlp = 'f903e1f90213a0fe950635b1bd2a416ff6283b0bbd30176e1b1125ad06fa729da9f3f4c1c61710a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794aa00000000000000000000000000000000000000a07f7510a0cb6203f456e34ec3e2ce30d6c5590ded42c10a9cf3f24784119c5afba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080018401c9c380802f80a0ff0000000000000000000000000000000000000000000000000000000000000088000000000000000007a0b695b29ec7ee934ef6a68838b13729f2d49fffe26718de16a1a9ed94a4d7d06dc0c0f901c6da8082ffff94000000000000000000000000000000000000000080f83b0183010000940100000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f83b0283010001940200000000000000000000000000000000000000a00200000000000000000000000000000000000000000000000000000000000000f83b0383010002940300000000000000000000000000000000000000a00300000000000000000000000000000000000000000000000000000000000000f83b0483010003940400000000000000000000000000000000000000a00400000000000000000000000000000000000000000000000000000000000000f83b0583010004940500000000000000000000000000000000000000a00500000000000000000000000000000000000000000000000000000000000000f83b0683010005940600000000000000000000000000000000000000a00600000000000000000000000000000000000000000000000000000000000000f83b0783010006940700000000000000000000000000000000000000a00700000000000000000000000000000000000000000000000000000000000000' @@ -32,12 +32,12 @@ common.hardforkBlock = function (hardfork: string | undefined) { tape('EIP4895 tests', function (t) { t.test('should correctly generate withdrawalsRoot', async function (st) { // get withdwalsArray - const gethBlockBufferArray = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex')) - const withdrawals = (gethBlockBufferArray[3] as WithdrawalBuffer[]).map((wa) => + const gethBlockBytesArray = decode(hexStringToBytes(gethWithdrawals8BlockRlp)) + const withdrawals = (gethBlockBytesArray[3] as WithdrawalBytes[]).map((wa) => Withdrawal.fromValuesArray(wa) ) st.equal(withdrawals.length, 8, '8 withdrawals should have been found') - const gethWitdrawalsRoot = (gethBlockBufferArray[0] as Buffer[])[16] as Buffer + const gethWitdrawalsRoot = (gethBlockBytesArray[0] as Uint8Array[])[16] as Uint8Array st.deepEqual( await Block.genWithdrawalsTrieRoot(withdrawals), gethWitdrawalsRoot, @@ -51,7 +51,7 @@ tape('EIP4895 tests', function (t) { st.throws(() => { BlockHeader.fromHeaderData( { - withdrawalsRoot: Buffer.from('00'.repeat(32), 'hex'), + withdrawalsRoot: hexStringToBytes('00'.repeat(32)), }, { common: earlyCommon, @@ -69,7 +69,7 @@ tape('EIP4895 tests', function (t) { st.doesNotThrow(() => { BlockHeader.fromHeaderData( { - withdrawalsRoot: Buffer.from('00'.repeat(32), 'hex'), + withdrawalsRoot: hexStringToBytes('00'.repeat(32)), }, { common, @@ -102,7 +102,7 @@ tape('EIP4895 tests', function (t) { Block.fromBlockData( { header: { - withdrawalsRoot: Buffer.from('00'.repeat(32), 'hex'), + withdrawalsRoot: hexStringToBytes('00'.repeat(32)), }, withdrawals: [], }, @@ -114,7 +114,7 @@ tape('EIP4895 tests', function (t) { const block = Block.fromBlockData( { header: { - withdrawalsRoot: Buffer.from('00'.repeat(32), 'hex'), + withdrawalsRoot: hexStringToBytes('00'.repeat(32)), }, withdrawals: [], }, @@ -143,16 +143,15 @@ tape('EIP4895 tests', function (t) { const withdrawal = { index: BigInt(0), validatorIndex: BigInt(0), - address: new Address(Buffer.from('20'.repeat(20), 'hex')), + address: new Address(hexStringToBytes('20'.repeat(20))), amount: BigInt(1000), } const validBlockWithWithdrawal = Block.fromBlockData( { header: { - withdrawalsRoot: Buffer.from( - '897ca49edcb278aecab2688bcc2b7b7ee43524cc489672534fee332a172f1718', - 'hex' + withdrawalsRoot: hexStringToBytes( + '897ca49edcb278aecab2688bcc2b7b7ee43524cc489672534fee332a172f1718' ), }, withdrawals: [withdrawal], @@ -169,16 +168,15 @@ tape('EIP4895 tests', function (t) { const withdrawal2 = { index: BigInt(1), validatorIndex: BigInt(11), - address: new Address(Buffer.from('30'.repeat(20), 'hex')), + address: new Address(hexStringToBytes('30'.repeat(20))), amount: BigInt(2000), } const validBlockWithWithdrawal2 = Block.fromBlockData( { header: { - withdrawalsRoot: Buffer.from( - '3b514862c42008079d461392e29d5b6775dd5ed370a6c4441ccb8ab742bf2436', - 'hex' + withdrawalsRoot: hexStringToBytes( + '3b514862c42008079d461392e29d5b6775dd5ed370a6c4441ccb8ab742bf2436' ), }, withdrawals: [withdrawal, withdrawal2], diff --git a/packages/block/test/from-rpc.spec.ts b/packages/block/test/from-rpc.spec.ts index e17d1beb7ff..e5d17cbde8f 100644 --- a/packages/block/test/from-rpc.spec.ts +++ b/packages/block/test/from-rpc.spec.ts @@ -1,4 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { blockFromRpc } from '../src/from-rpc' @@ -32,8 +34,8 @@ tape('[fromRPC]: block #2924874', function (t) { t.test('should create a block header with the correct hash', function (st) { const block = blockHeaderFromRpc(blockData, { common }) - const hash = Buffer.from(blockData.hash.slice(2), 'hex') - st.ok(block.hash().equals(hash)) + const hash = hexStringToBytes(blockData.hash) + st.ok(equalsBytes(block.hash(), hash)) st.end() }) }) @@ -103,7 +105,7 @@ tape('[fromRPC]:', function (t) { `0x${block.header.baseFeePerGas?.toString(16)}`, testDataFromRpcGoerliLondon.baseFeePerGas ) - st.equal(`0x${block.hash().toString('hex')}`, testDataFromRpcGoerliLondon.hash) + st.equal(bytesToPrefixedHexString(block.hash()), testDataFromRpcGoerliLondon.hash) st.end() }) @@ -126,8 +128,8 @@ tape('[fromRPC]:', function (t) { function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Shanghai }) const block = blockHeaderFromRpc(blockDataWithWithdrawals, { common }) - const hash = Buffer.from(blockDataWithWithdrawals.hash.slice(2), 'hex') - st.ok(block.hash().equals(hash)) + const hash = blockDataWithWithdrawals.hash.slice(2) + st.equal(bytesToHex(block.hash()), hash) st.end() } ) @@ -137,7 +139,7 @@ tape('[fromRPC] - Alchemy/Infura API block responses', (t) => { t.test('should create pre merge block from Alchemy API response to eth_getBlockByHash', (st) => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const block = blockFromRpc(alchemy14151203, [], { common }) - st.equal(`0x${block.hash().toString('hex')}`, alchemy14151203.hash) + st.equal(bytesToPrefixedHexString(block.hash()), alchemy14151203.hash) st.end() }) @@ -147,13 +149,13 @@ tape('[fromRPC] - Alchemy/Infura API block responses', (t) => { const common = new Common({ chain: Chain.Mainnet }) let block = blockFromRpc(infura2000004woTxs, [], { common, hardforkByBlockNumber: true }) st.equal( - `0x${block.hash().toString('hex')}`, + bytesToPrefixedHexString(block.hash()), infura2000004woTxs.hash, 'created premerge block w/o txns' ) block = blockFromRpc(infura2000004wTxs, [], { common, hardforkByBlockNumber: true }) st.equal( - `0x${block.hash().toString('hex')}`, + bytesToPrefixedHexString(block.hash()), infura2000004wTxs.hash, 'created premerge block with txns' ) @@ -162,7 +164,7 @@ tape('[fromRPC] - Alchemy/Infura API block responses', (t) => { hardforkByTTD: 58750000000000000000000n, }) st.equal( - `0x${block.hash().toString('hex')}`, + bytesToPrefixedHexString(block.hash()), infura15571241woTxs.hash, 'created post merge block without txns' ) @@ -172,7 +174,7 @@ tape('[fromRPC] - Alchemy/Infura API block responses', (t) => { hardforkByTTD: 58750000000000000000000n, }) st.equal( - `0x${block.hash().toString('hex')}`, + bytesToPrefixedHexString(block.hash()), infura15571241wTxs.hash, 'created post merge block with txns' ) @@ -189,7 +191,7 @@ tape('[fromEthersProvider]', async (t) => { const blockHash = '0x1850b014065b23d804ecf71a8a4691d076ca87c2e6fb8fe81ee20a4d8e884c24' const block = await Block.fromEthersProvider(provider, blockHash, { common }) t.equal( - '0x' + block.hash().toString('hex'), + bytesToPrefixedHexString(block.hash()), blockHash, 'assembled a block from blockdata from a provider' ) diff --git a/packages/block/test/header.spec.ts b/packages/block/test/header.spec.ts index b966b2a0820..c15e653f694 100644 --- a/packages/block/test/header.spec.ts +++ b/packages/block/test/header.spec.ts @@ -1,6 +1,16 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { Address, KECCAK256_RLP, KECCAK256_RLP_ARRAY, toBuffer, zeros } from '@ethereumjs/util' +import { + Address, + KECCAK256_RLP, + KECCAK256_RLP_ARRAY, + bytesToHex, + concatBytes, + equalsBytes, + hexStringToBytes, + toBytes, + zeros, +} from '@ethereumjs/util' import * as tape from 'tape' import { Block } from '../src' @@ -14,21 +24,21 @@ const blocksMainnet = require('./testdata/blocks_mainnet.json') tape('[Block]: Header functions', function (t) { t.test('should create with default constructor', function (st) { function compareDefaultHeader(st: tape.Test, header: BlockHeader) { - st.ok(header.parentHash.equals(zeros(32))) - st.ok(header.uncleHash.equals(KECCAK256_RLP_ARRAY)) + st.ok(equalsBytes(header.parentHash, zeros(32))) + st.ok(equalsBytes(header.uncleHash, KECCAK256_RLP_ARRAY)) st.ok(header.coinbase.equals(Address.zero())) - st.ok(header.stateRoot.equals(zeros(32))) - st.ok(header.transactionsTrie.equals(KECCAK256_RLP)) - st.ok(header.receiptTrie.equals(KECCAK256_RLP)) - st.ok(header.logsBloom.equals(zeros(256))) + st.ok(equalsBytes(header.stateRoot, zeros(32))) + st.ok(equalsBytes(header.transactionsTrie, KECCAK256_RLP)) + st.ok(equalsBytes(header.receiptTrie, KECCAK256_RLP)) + st.ok(equalsBytes(header.logsBloom, zeros(256))) st.equal(header.difficulty, BigInt(0)) st.equal(header.number, BigInt(0)) st.equal(header.gasLimit, BigInt('0xffffffffffffff')) st.equal(header.gasUsed, BigInt(0)) st.equal(header.timestamp, BigInt(0)) - st.ok(header.extraData.equals(Buffer.from([]))) - st.ok(header.mixHash.equals(zeros(32))) - st.ok(header.nonce.equals(zeros(8))) + st.ok(equalsBytes(header.extraData, new Uint8Array(0))) + st.ok(equalsBytes(header.mixHash, zeros(32))) + st.ok(equalsBytes(header.nonce, zeros(8))) } const header = BlockHeader.fromHeaderData() @@ -43,7 +53,7 @@ tape('[Block]: Header functions', function (t) { t.test('Initialization -> fromHeaderData()', function (st) { const common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.Chainstart }) let header = BlockHeader.fromHeaderData(undefined, { common }) - st.ok(header.hash().toString('hex'), 'genesis block should initialize') + st.ok(bytesToHex(header.hash()), 'genesis block should initialize') st.equal(header._common.hardfork(), 'chainstart', 'should initialize with correct HF provided') common.setHardfork(Hardfork.Byzantium) @@ -54,7 +64,7 @@ tape('[Block]: Header functions', function (t) { ) header = BlockHeader.fromHeaderData({}, { common }) - st.ok(header.hash().toString('hex'), 'default block should initialize') + st.ok(bytesToHex(header.hash()), 'default block should initialize') // test default freeze values // also test if the options are carried over to the constructor @@ -94,14 +104,13 @@ tape('[Block]: Header functions', function (t) { ) header = BlockHeader.fromRLPSerializedHeader( - Buffer.from( - 'f90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000850400000000808213888080a011bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82faa00000000000000000000000000000000000000000000000000000000000000000880000000000000042', - 'hex' + hexStringToBytes( + 'f90214a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000850400000000808213888080a011bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82faa00000000000000000000000000000000000000000000000000000000000000000880000000000000042' ), { common, hardforkByBlockNumber: false } ) st.equal( - header.hash().toString('hex'), + bytesToHex(header.hash()), 'f0f936910ebf101b7b168bbe08e3f166ce1e75e16f513dd5a97af02fbe7de7c0', 'genesis block should produce incorrect hash since default hardfork is london' ) @@ -110,7 +119,7 @@ tape('[Block]: Header functions', function (t) { t.test('Initialization -> fromRLPSerializedHeader() -> error cases', function (st) { try { - BlockHeader.fromRLPSerializedHeader(Buffer.from(RLP.encode('a'))) + BlockHeader.fromRLPSerializedHeader(RLP.encode('a')) } catch (e: any) { const expectedError = 'Invalid serialized header input. Must be array' st.ok(e.message.includes(expectedError), 'should throw with header as rlp encoded string') @@ -120,7 +129,7 @@ tape('[Block]: Header functions', function (t) { t.test('Initialization -> fromValuesArray()', function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const zero = Buffer.alloc(0) + const zero = new Uint8Array(0) const headerArray = [] for (let item = 0; item < 15; item++) { headerArray.push(zero) @@ -144,7 +153,7 @@ tape('[Block]: Header functions', function (t) { }) t.test('Initialization -> fromValuesArray() -> error cases', function (st) { - const headerArray = Array(19).fill(Buffer.alloc(0)) + const headerArray = Array(19).fill(new Uint8Array(0)) // mock header data (if set to zeros(0) header throws) headerArray[0] = zeros(32) //parentHash @@ -173,8 +182,8 @@ tape('[Block]: Header functions', function (t) { t.test('Initialization -> Clique Blocks', function (st) { const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Chainstart }) - const header = BlockHeader.fromHeaderData({ extraData: Buffer.alloc(97) }, { common }) - st.ok(header.hash().toString('hex'), 'default block should initialize') + const header = BlockHeader.fromHeaderData({ extraData: new Uint8Array(97) }, { common }) + st.ok(bytesToHex(header.hash()), 'default block should initialize') st.end() }) @@ -193,7 +202,7 @@ tape('[Block]: Header functions', function (t) { // valid extraData: at limit let testCase = 'pow block should validate with 32 bytes of extraData' - let extraData = Buffer.alloc(32) + let extraData = new Uint8Array(32) try { BlockHeader.fromHeaderData({ ...data, extraData }, opts) @@ -204,7 +213,7 @@ tape('[Block]: Header functions', function (t) { // valid extraData: fewer than limit testCase = 'pow block should validate with 12 bytes of extraData' - extraData = Buffer.alloc(12) + extraData = new Uint8Array(12) try { BlockHeader.fromHeaderData({ ...data, extraData }, opts) @@ -215,7 +224,7 @@ tape('[Block]: Header functions', function (t) { // extraData beyond limit testCase = 'pow block should throw with excess amount of extraData' - extraData = Buffer.alloc(42) + extraData = new Uint8Array(42) try { BlockHeader.fromHeaderData({ ...data, extraData }, opts) @@ -226,7 +235,7 @@ tape('[Block]: Header functions', function (t) { // PoA common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Chainstart }) - genesis = Block.fromBlockData({ header: { extraData: Buffer.alloc(97) } }, { common }) + genesis = Block.fromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) parentHash = genesis.hash() gasLimit = genesis.header.gasLimit @@ -236,7 +245,7 @@ tape('[Block]: Header functions', function (t) { // valid extraData (32 byte vanity + 65 byte seal) testCase = 'clique block should validate with valid number of bytes in extraData: 32 byte vanity + 65 byte seal' - extraData = Buffer.concat([Buffer.alloc(32), Buffer.alloc(65)]) + extraData = concatBytes(new Uint8Array(32), new Uint8Array(65)) try { BlockHeader.fromHeaderData({ ...data, extraData }, opts) t.pass(testCase) @@ -246,7 +255,7 @@ tape('[Block]: Header functions', function (t) { // invalid extraData length testCase = 'clique block should throw on invalid extraData length' - extraData = Buffer.alloc(32) + extraData = new Uint8Array(32) try { BlockHeader.fromHeaderData({ ...data, extraData }, opts) t.fail(testCase) @@ -261,12 +270,12 @@ tape('[Block]: Header functions', function (t) { // signer list indivisible by 20 testCase = 'clique blocks should throw on invalid extraData length: indivisible by 20' - extraData = Buffer.concat([ - Buffer.alloc(32), - Buffer.alloc(65), - Buffer.alloc(20), - Buffer.alloc(21), - ]) + extraData = concatBytes( + new Uint8Array(32), + new Uint8Array(65), + new Uint8Array(20), + new Uint8Array(21) + ) const epoch = BigInt((common.consensusConfig() as CliqueConfig).epoch) try { BlockHeader.fromHeaderData({ ...data, number: epoch, extraData }, opts) @@ -285,7 +294,7 @@ tape('[Block]: Header functions', function (t) { t.test('should skip consensusFormatValidation if flag is set to false', (st) => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) - const extraData = Buffer.concat([Buffer.alloc(1)]) + const extraData = concatBytes(new Uint8Array(1)) try { BlockHeader.fromHeaderData({ extraData }, { common, skipConsensusFormatValidation: true }) @@ -300,7 +309,7 @@ tape('[Block]: Header functions', function (t) { }) t.test('_genericFormatValidation checks', (st) => { - const badHash = Buffer.alloc(31) + const badHash = new Uint8Array(31) st.throws( () => BlockHeader.fromHeaderData({ parentHash: badHash }), @@ -319,7 +328,7 @@ tape('[Block]: Header functions', function (t) { ) st.throws( - () => BlockHeader.fromHeaderData({ nonce: Buffer.alloc(5) }), + () => BlockHeader.fromHeaderData({ nonce: new Uint8Array(5) }), (err: any) => err.message.includes('nonce must be 8 bytes'), 'contains nonce length error message' ) @@ -333,14 +342,14 @@ tape('[Block]: Header functions', function (t) { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Istanbul }) const blockchain = new Mockchain() - const genesisRlp = toBuffer(testDataPreLondon.genesisRLP) + const genesisRlp = toBytes(testDataPreLondon.genesisRLP) const block = Block.fromRLPSerializedBlock(genesisRlp, { common }) await blockchain.putBlock(block) headerData.number = 1 headerData.timestamp = BigInt(1422494850) - headerData.extraData = Buffer.alloc(97) - headerData.mixHash = Buffer.alloc(32) + headerData.extraData = new Uint8Array(97) + headerData.mixHash = new Uint8Array(32) headerData.difficulty = BigInt(2) let testCase = 'should throw on lower than period timestamp diffs' @@ -380,7 +389,7 @@ tape('[Block]: Header functions', function (t) { headerData.coinbase = Address.zero() testCase = 'should throw on non-zero mixHash' - headerData.mixHash = Buffer.alloc(32).fill(1) + headerData.mixHash = new Uint8Array(32).fill(1) header = BlockHeader.fromHeaderData(headerData, { common }) try { await header.validate(blockchain) @@ -392,7 +401,7 @@ tape('[Block]: Header functions', function (t) { st.fail('should throw with appropriate error') } } - headerData.mixHash = Buffer.alloc(32) + headerData.mixHash = new Uint8Array(32) testCase = 'should throw on invalid clique difficulty' headerData.difficulty = BigInt(3) @@ -411,9 +420,8 @@ tape('[Block]: Header functions', function (t) { testCase = 'validateCliqueDifficulty() should return true with NOTURN difficulty and one signer' headerData.difficulty = BigInt(2) const poaBlockchain = new PoaMockchain() - const cliqueSigner = Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' + const cliqueSigner = hexToBytes( + '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993' ) const poaBlock = Block.fromRLPSerializedBlock(genesisRlp, { common, cliqueSigner }) await poaBlockchain.putBlock(poaBlock) @@ -445,9 +453,9 @@ tape('[Block]: Header functions', function (t) { const bcBlockGasLimitTestData = testData.BlockGasLimit2p63m1 for (const key of Object.keys(bcBlockGasLimitTestData)) { - const genesisRlp = toBuffer(bcBlockGasLimitTestData[key].genesisRLP) + const genesisRlp = toBytes(bcBlockGasLimitTestData[key].genesisRLP) const parentBlock = Block.fromRLPSerializedBlock(genesisRlp, { common }) - const blockRlp = toBuffer(bcBlockGasLimitTestData[key].blocks[0].rlp) + const blockRlp = toBytes(bcBlockGasLimitTestData[key].blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) st.doesNotThrow(() => block.validateGasLimit(parentBlock)) } @@ -468,7 +476,7 @@ tape('[Block]: Header functions', function (t) { let common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) let header = BlockHeader.fromHeaderData(blocksMainnet[0]['header'], { common }) st.equal( - header.hash().toString('hex'), + bytesToHex(header.hash()), '88e96d4537bea4d9c05d12549907b32561d3bf31f45aae734cdc119f13406cb6', 'correct PoW hash (mainnet block 1)' ) @@ -476,7 +484,7 @@ tape('[Block]: Header functions', function (t) { common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) header = BlockHeader.fromHeaderData(blocksGoerli[0]['header'], { common }) st.equal( - header.hash().toString('hex'), + bytesToHex(header.hash()), '8f5bab218b6bb34476f51ca588e9f4553a3a7ce5e13a66c660a5283e97e9a85a', 'correct PoA clique hash (goerli block 1)' ) diff --git a/packages/block/test/mergeBlock.spec.ts b/packages/block/test/mergeBlock.spec.ts index e9bd5620a97..3761501ef62 100644 --- a/packages/block/test/mergeBlock.spec.ts +++ b/packages/block/test/mergeBlock.spec.ts @@ -1,5 +1,12 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, KECCAK256_RLP, KECCAK256_RLP_ARRAY, zeros } from '@ethereumjs/util' +import { + Address, + KECCAK256_RLP, + KECCAK256_RLP_ARRAY, + equalsBytes, + hexStringToBytes, + zeros, +} from '@ethereumjs/util' import * as tape from 'tape' import { Block } from '../src/block' @@ -11,13 +18,13 @@ const common = new Common({ }) function validateMergeHeader(st: tape.Test, header: BlockHeader) { - st.ok(header.parentHash.equals(zeros(32)), 'parentHash') - st.ok(header.uncleHash.equals(KECCAK256_RLP_ARRAY), 'uncleHash') + st.ok(equalsBytes(header.parentHash, zeros(32)), 'parentHash') + st.ok(equalsBytes(header.uncleHash, KECCAK256_RLP_ARRAY), 'uncleHash') st.ok(header.coinbase.equals(Address.zero()), 'coinbase') - st.ok(header.stateRoot.equals(zeros(32)), 'stateRoot') - st.ok(header.transactionsTrie.equals(KECCAK256_RLP), 'transactionsTrie') - st.ok(header.receiptTrie.equals(KECCAK256_RLP), 'receiptTrie') - st.ok(header.logsBloom.equals(zeros(256)), 'logsBloom') + st.ok(equalsBytes(header.stateRoot, zeros(32)), 'stateRoot') + st.ok(equalsBytes(header.transactionsTrie, KECCAK256_RLP), 'transactionsTrie') + st.ok(equalsBytes(header.receiptTrie, KECCAK256_RLP), 'receiptTrie') + st.ok(equalsBytes(header.logsBloom, zeros(256)), 'logsBloom') st.equal(header.difficulty, BigInt(0), 'difficulty') st.equal(header.number, BigInt(0), 'number') st.equal(header.gasLimit, BigInt('0xffffffffffffff'), 'gasLimit') @@ -25,7 +32,7 @@ function validateMergeHeader(st: tape.Test, header: BlockHeader) { st.equal(header.timestamp, BigInt(0), 'timestamp') st.ok(header.extraData.length <= 32, 'extraData') st.equal(header.mixHash.length, 32, 'mixHash') - st.ok(header.nonce.equals(zeros(8)), 'nonce') + st.ok(equalsBytes(header.nonce, zeros(8)), 'nonce') } tape('[Header]: Casper PoS / The Merge Functionality', function (t) { @@ -43,7 +50,7 @@ tape('[Header]: Casper PoS / The Merge Functionality', function (t) { // Building a header with random values for constants try { const headerData = { - uncleHash: Buffer.from('123abc', 'hex'), + uncleHash: hexStringToBytes('123abc'), } BlockHeader.fromHeaderData(headerData, { common }) st.fail('should throw') @@ -64,7 +71,7 @@ tape('[Header]: Casper PoS / The Merge Functionality', function (t) { try { const headerData = { - extraData: Buffer.alloc(33).fill(1), + extraData: new Uint8Array(33).fill(1), number: 1n, } BlockHeader.fromHeaderData(headerData, { common }) @@ -75,7 +82,7 @@ tape('[Header]: Casper PoS / The Merge Functionality', function (t) { try { const headerData = { - mixHash: Buffer.alloc(30).fill(1), + mixHash: new Uint8Array(30).fill(1), } BlockHeader.fromHeaderData(headerData, { common }) st.fail('should throw') @@ -85,7 +92,7 @@ tape('[Header]: Casper PoS / The Merge Functionality', function (t) { try { const headerData = { - nonce: Buffer.alloc(8).fill(1), + nonce: new Uint8Array(8).fill(1), number: 1n, } BlockHeader.fromHeaderData(headerData, { common }) @@ -110,9 +117,9 @@ tape('[Header]: Casper PoS / The Merge Functionality', function (t) { }) t.test('EIP-4399: prevRando should return mixHash value', function (st) { - const mixHash = Buffer.alloc(32, 3) + const mixHash = new Uint8Array(32).fill(3) let block = Block.fromBlockData({ header: { mixHash } }, { common }) - st.ok(block.header.prevRandao.equals(mixHash), 'prevRandao should return mixHash value') + st.ok(equalsBytes(block.header.prevRandao, mixHash), 'prevRandao should return mixHash value') const commonLondon = common.copy() commonLondon.setHardfork(Hardfork.London) diff --git a/packages/block/test/util.ts b/packages/block/test/util.ts index 7f4f0c9b918..bf6c6f6aef4 100644 --- a/packages/block/test/util.ts +++ b/packages/block/test/util.ts @@ -1,6 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' +import { utf8ToBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { Block } from '../src' @@ -29,7 +29,7 @@ function createBlock( const number = parentBlock.header.number + BigInt(1) const timestamp = parentBlock.header.timestamp + BigInt(1) - const uncleHash = keccak256(RLP.encode(bufArrToArr(uncles.map((uh) => uh.raw())))) + const uncleHash = keccak256(RLP.encode(uncles.map((uh) => uh.raw()))) const londonHfBlock = common.hardforkBlock(Hardfork.London) const baseFeePerGas = @@ -44,7 +44,7 @@ function createBlock( parentHash: parentBlock.hash(), timestamp, gasLimit: parentBlock.header.gasLimit, - extraData: Buffer.from(extraData), + extraData: utf8ToBytes(extraData), uncleHash, baseFeePerGas, }, diff --git a/packages/blockchain/src/blockchain.ts b/packages/blockchain/src/blockchain.ts index 7704a7bafd2..c3c366f59ff 100644 --- a/packages/blockchain/src/blockchain.ts +++ b/packages/blockchain/src/blockchain.ts @@ -1,6 +1,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '@ethereumjs/common' -import { KECCAK256_RLP, Lock } from '@ethereumjs/util' +import { KECCAK256_RLP, Lock, concatBytesNoTypeCheck } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import { CasperConsensus, CliqueConsensus, EthashConsensus } from './consensus' @@ -23,7 +24,7 @@ import type { AbstractLevel } from 'abstract-level' */ export class Blockchain implements BlockchainInterface { consensus: Consensus - db: AbstractLevel + db: AbstractLevel dbManager: DBManager private _genesisBlock?: Block /** The genesis block of this blockchain */ @@ -36,16 +37,16 @@ export class Blockchain implements BlockchainInterface { * the hash with the highest total difficulty. */ /** The hash of the current head block */ - private _headBlockHash?: Buffer + private _headBlockHash?: Uint8Array /** The hash of the current head header */ - private _headHeaderHash?: Buffer + private _headHeaderHash?: Uint8Array /** * A Map which stores the head of each key (for instance the "vm" key) which is * updated along a {@link Blockchain.iterator} method run and can be used to (re)run * non-verified blocks (for instance in the VM). */ - private _heads: { [key: string]: Buffer } + private _heads: { [key: string]: Uint8Array } protected _isInitialized = false private _lock: Lock @@ -204,10 +205,7 @@ export class Blockchain implements BlockchainInterface { let stateRoot if (this._common.chainId() === BigInt(1) && this._customGenesisState === undefined) { // For mainnet use the known genesis stateRoot to quicken setup - stateRoot = Buffer.from( - 'd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544', - 'hex' - ) + stateRoot = hexToBytes('d7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544') } else { stateRoot = await genesisStateRoot(this.genesisState()) } @@ -216,7 +214,7 @@ export class Blockchain implements BlockchainInterface { // If the DB has a genesis block, then verify that the genesis block in the // DB is indeed the Genesis block generated or assigned. - if (dbGenesisBlock && !genesisBlock.hash().equals(dbGenesisBlock.hash())) { + if (dbGenesisBlock && !equalsBytes(genesisBlock.hash(), dbGenesisBlock.hash())) { throw new Error( 'The genesis block in the DB has a different hash than the provided genesis block.' ) @@ -691,11 +689,11 @@ export class Blockchain implements BlockchainInterface { canonicalBlockMap.push(parentBlock) // mark block hash as part of the canonical chain - canonicalChainHashes[parentBlock.hash().toString('hex')] = true + canonicalChainHashes[bytesToHex(parentBlock.hash())] = true // for each of the uncles, mark the uncle as included parentBlock.uncleHeaders.map((uh) => { - includedUncles[uh.hash().toString('hex')] = true + includedUncles[bytesToHex(uh.hash())] = true }) parentHash = parentBlock.header.parentHash @@ -707,8 +705,8 @@ export class Blockchain implements BlockchainInterface { // Uncle Header has a parentHash which points to the canonical chain. uncleHeaders.map((uh) => { - const uncleHash = uh.hash().toString('hex') - const parentHash = uh.parentHash.toString('hex') + const uncleHash = bytesToHex(uh.hash()) + const parentHash = bytesToHex(uh.parentHash) if (!canonicalChainHashes[parentHash]) { throw new Error( @@ -734,7 +732,7 @@ export class Blockchain implements BlockchainInterface { * this will be immediately looked up, otherwise it will wait until we have * unlocked the DB */ - async getBlock(blockId: Buffer | number | bigint): Promise { + async getBlock(blockId: Uint8Array | number | bigint): Promise { // cannot wait for a lock here: it is used both in `validate` of `Block` // (calls `getBlock` to get `parentHash`) it is also called from `runBlock` // in the `VM` if we encounter a `BLOCKHASH` opcode: then a bigint is used we @@ -745,7 +743,7 @@ export class Blockchain implements BlockchainInterface { } catch (error: any) { if (error.code === 'LEVEL_NOT_FOUND') { if (typeof blockId === 'object') { - error.message = `Block with hash ${blockId.toString('hex')} not found in DB (NotFound)` + error.message = `Block with hash ${bytesToHex(blockId)} not found in DB (NotFound)` } else { error.message = `Block number ${blockId} not found in DB (NotFound)` } @@ -757,7 +755,7 @@ export class Blockchain implements BlockchainInterface { /** * Gets total difficulty for a block specified by hash and number */ - public async getTotalDifficulty(hash: Buffer, number?: bigint): Promise { + public async getTotalDifficulty(hash: Uint8Array, number?: bigint): Promise { if (number === undefined) { number = await this.dbManager.hashToNumber(hash) } @@ -783,7 +781,7 @@ export class Blockchain implements BlockchainInterface { * @param reverse - Fetch blocks in reverse */ async getBlocks( - blockId: Buffer | bigint | number, + blockId: Uint8Array | bigint | number, maxBlocks: number, skip: number, reverse: boolean @@ -792,7 +790,7 @@ export class Blockchain implements BlockchainInterface { const blocks: Block[] = [] let i = -1 - const nextBlock = async (blockId: Buffer | bigint | number): Promise => { + const nextBlock = async (blockId: Uint8Array | bigint | number): Promise => { let block try { block = await this.getBlock(blockId) @@ -824,8 +822,8 @@ export class Blockchain implements BlockchainInterface { * Therefore, the array needs to be ordered upon number. * @param hashes - Ordered array of hashes (ordered on `number`). */ - async selectNeededHashes(hashes: Array): Promise { - return this.runWithLock(async () => { + async selectNeededHashes(hashes: Array): Promise { + return this.runWithLock(async () => { let max: number let mid: number let min: number @@ -864,7 +862,7 @@ export class Blockchain implements BlockchainInterface { * we can be sure it is correct). * @param blockHash - The hash of the block to be deleted */ - async delBlock(blockHash: Buffer) { + async delBlock(blockHash: Uint8Array) { // Q: is it safe to make this not wait for a lock? this is called from // `BlockchainTestsRunner` in case `runBlock` throws (i.e. the block is invalid). // But is this the way to go? If we know this is called from the @@ -876,7 +874,7 @@ export class Blockchain implements BlockchainInterface { /** * @hidden */ - private async _delBlock(blockHash: Buffer) { + private async _delBlock(blockHash: Uint8Array) { const dbOps: DBOp[] = [] // get header @@ -888,7 +886,7 @@ export class Blockchain implements BlockchainInterface { // check if block is in the canonical chain const canonicalHash = await this.safeNumberToHash(blockNumber) - const inCanonical = canonicalHash !== false && canonicalHash.equals(blockHash) + const inCanonical = canonicalHash !== false && equalsBytes(canonicalHash, blockHash) // delete the block, and if block is in the canonical chain, delete all // children as well @@ -918,9 +916,9 @@ export class Blockchain implements BlockchainInterface { * @hidden */ private async _delChild( - blockHash: Buffer, + blockHash: Uint8Array, blockNumber: bigint, - headHash: Buffer | null, + headHash: Uint8Array | null, ops: DBOp[] ) { // delete header, body, hash to number mapping and td @@ -933,11 +931,14 @@ export class Blockchain implements BlockchainInterface { return } - if (this._headHeaderHash?.equals(blockHash) === true) { + if ( + this._headHeaderHash !== undefined && + equalsBytes(this._headHeaderHash, blockHash) === true + ) { this._headHeaderHash = headHash } - if (this._headBlockHash?.equals(blockHash) === true) { + if (this._headBlockHash !== undefined && equalsBytes(this._headBlockHash, blockHash)) { this._headBlockHash = headHash } @@ -983,7 +984,9 @@ export class Blockchain implements BlockchainInterface { while (maxBlocks !== blocksRanCounter) { try { let nextBlock = await this.getBlock(nextBlockNumber) - const reorg = lastBlock ? !lastBlock.hash().equals(nextBlock.header.parentHash) : false + const reorg = lastBlock + ? !equalsBytes(lastBlock.hash(), nextBlock.header.parentHash) + : false if (reorg) { // If reorg has happened, the _heads must have been updated so lets reload the counters headHash = this._heads[name] ?? this.genesisBlock.hash() @@ -1025,7 +1028,7 @@ export class Blockchain implements BlockchainInterface { * @param tag - The tag to save the headHash to * @param headHash - The head hash to save */ - async setIteratorHead(tag: string, headHash: Buffer) { + async setIteratorHead(tag: string, headHash: Uint8Array) { await this.runWithLock(async () => { this._heads[tag] = headHash await this._saveHeads() @@ -1055,13 +1058,13 @@ export class Blockchain implements BlockchainInterface { if (header.number !== newHeader.number) { throw new Error('Failed to find ancient header') } - while (!header.hash().equals(newHeader.hash()) && header.number > BigInt(0)) { + while (!equalsBytes(header.hash(), newHeader.hash()) && header.number > BigInt(0)) { header = await this.getCanonicalHeader(header.number - BigInt(1)) ancestorHeaders.add(header) newHeader = await this._getHeader(newHeader.parentHash, newHeader.number - BigInt(1)) ancestorHeaders.add(newHeader) } - if (!header.hash().equals(newHeader.hash())) { + if (!equalsBytes(header.hash(), newHeader.hash())) { throw new Error('Failed to find ancient header') } return { @@ -1085,10 +1088,10 @@ export class Blockchain implements BlockchainInterface { */ private async _deleteCanonicalChainReferences( blockNumber: bigint, - headHash: Buffer, + headHash: Uint8Array, ops: DBOp[] ) { - let hash: Buffer | false + let hash: Uint8Array | false hash = await this.safeNumberToHash(blockNumber) while (hash !== false) { @@ -1099,18 +1102,18 @@ export class Blockchain implements BlockchainInterface { // executed block) blocks to verify the chain up to the current, actual, // head. for (const name of Object.keys(this._heads)) { - if (this._heads[name].equals(hash)) { + if (equalsBytes(this._heads[name], hash)) { this._heads[name] = headHash } } - // reset stale headBlock to current canonical - if (this._headBlockHash?.equals(hash) === true) { - this._headBlockHash = headHash + // reset stale headHeader to current canonical + if (this._headHeaderHash !== undefined && equalsBytes(this._headHeaderHash, hash) === true) { + this._headHeaderHash = headHash } // reset stale headBlock to current canonical - if (this._headHeaderHash?.equals(hash) === true) { - this._headHeaderHash = headHash + if (this._headBlockHash !== undefined && equalsBytes(this._headBlockHash, hash) === true) { + this._headBlockHash = headHash } blockNumber++ @@ -1136,18 +1139,18 @@ export class Blockchain implements BlockchainInterface { */ private async _rebuildCanonical(header: BlockHeader, ops: DBOp[]) { let currentNumber = header.number - let currentCanonicalHash: Buffer = header.hash() + let currentCanonicalHash: Uint8Array = header.hash() // track the staleHash: this is the hash currently in the DB which matches // the block number of the provided header. - let staleHash: Buffer | false = false + let staleHash: Uint8Array | false = false let staleHeads: string[] = [] let staleHeadBlock = false const loopCondition = async () => { staleHash = await this.safeNumberToHash(currentNumber) currentCanonicalHash = header.hash() - return staleHash === false || !currentCanonicalHash.equals(staleHash) + return staleHash === false || !equalsBytes(currentCanonicalHash, staleHash) } while (await loopCondition()) { @@ -1166,12 +1169,16 @@ export class Blockchain implements BlockchainInterface { // mark each key `_heads` which is currently set to the hash in the DB as // stale to overwrite later in `_deleteCanonicalChainReferences`. for (const name of Object.keys(this._heads)) { - if (staleHash && this._heads[name].equals(staleHash)) { + if (staleHash && equalsBytes(this._heads[name], staleHash)) { staleHeads.push(name) } } // flag stale headBlock for reset - if (staleHash && this._headBlockHash?.equals(staleHash) === true) { + if ( + staleHash && + this._headBlockHash !== undefined && + equalsBytes(this._headBlockHash, staleHash) === true + ) { staleHeadBlock = true } @@ -1226,7 +1233,7 @@ export class Blockchain implements BlockchainInterface { * * @hidden */ - private async _getHeader(hash: Buffer, number?: bigint) { + private async _getHeader(hash: Uint8Array, number?: bigint) { if (number === undefined) { number = await this.dbManager.hashToNumber(hash) } @@ -1276,12 +1283,12 @@ export class Blockchain implements BlockchainInterface { } /** - * This method either returns a Buffer if there exists one in the DB or if it + * This method either returns a Uint8Array if there exists one in the DB or if it * does not exist (DB throws a `NotFoundError`) then return false If DB throws * any other error, this function throws. * @param number */ - async safeNumberToHash(number: bigint): Promise { + async safeNumberToHash(number: bigint): Promise { try { const hash = await this.dbManager.numberToHash(number) return hash @@ -1305,7 +1312,7 @@ export class Blockchain implements BlockchainInterface { * Creates a genesis {@link Block} for the blockchain with params from {@link Common.genesis} * @param stateRoot The genesis stateRoot */ - createGenesisBlock(stateRoot: Buffer): Block { + createGenesisBlock(stateRoot: Uint8Array): Block { const common = this._common.copy() common.setHardforkByBlockNumber( 0, @@ -1326,7 +1333,7 @@ export class Blockchain implements BlockchainInterface { header.extraData = common.genesis().extraData } else { // Add required extraData (32 bytes vanity + 65 bytes filled with zeroes - header.extraData = Buffer.concat([Buffer.alloc(32), Buffer.alloc(65).fill(0)]) + header.extraData = concatBytesNoTypeCheck(new Uint8Array(32), new Uint8Array(65)) } } return Block.fromBlockData( diff --git a/packages/blockchain/src/consensus/clique.ts b/packages/blockchain/src/consensus/clique.ts index e7ec9b503e4..b28760f788e 100644 --- a/packages/blockchain/src/consensus/clique.ts +++ b/packages/blockchain/src/consensus/clique.ts @@ -1,7 +1,8 @@ import { ConsensusAlgorithm } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { Address, arrToBufArr, bigIntToBuffer, bufArrToArr, bufferToBigInt } from '@ethereumjs/util' +import { Address, TypeOutput, bigIntToBytes, bytesToBigInt, toType } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import type { Blockchain } from '..' import type { Consensus, ConsensusOptions } from './interface' @@ -11,9 +12,9 @@ import type { CliqueConfig } from '@ethereumjs/common' const debug = createDebugLogger('blockchain:clique') // Magic nonce number to vote on adding a new signer -export const CLIQUE_NONCE_AUTH = Buffer.from('ffffffffffffffff', 'hex') +export const CLIQUE_NONCE_AUTH = hexToBytes('ffffffffffffffff') // Magic nonce number to vote on removing a signer. -export const CLIQUE_NONCE_DROP = Buffer.alloc(8) +export const CLIQUE_NONCE_DROP = new Uint8Array(8) const CLIQUE_SIGNERS_KEY = 'CliqueSigners' const CLIQUE_VOTES_KEY = 'CliqueVotes' @@ -25,8 +26,8 @@ export const CLIQUE_DIFF_INTURN = BigInt(2) export const CLIQUE_DIFF_NOTURN = BigInt(1) const DB_OPTS = { - keyEncoding: 'buffer', - valueEncoding: 'buffer', + keyEncoding: 'view', + valueEncoding: 'view', } // Clique Signer State @@ -36,7 +37,7 @@ type CliqueLatestSignerStates = CliqueSignerState[] // Clique Vote type CliqueVote = [ blockNumber: bigint, - vote: [signer: Address, beneficiary: Address, cliqueNonce: Buffer] + vote: [signer: Address, beneficiary: Address, cliqueNonce: Uint8Array] ] type CliqueLatestVotes = CliqueVote[] @@ -234,14 +235,10 @@ export class CliqueConsensus implements Consensus { // save to db const formatted = this._cliqueLatestSignerStates.map((state) => [ - bigIntToBuffer(state[0]), - state[1].map((a) => a.toBuffer()), + bigIntToBytes(state[0]), + state[1].map((a) => a.toBytes()), ]) - await this.blockchain!.db.put( - CLIQUE_SIGNERS_KEY, - Buffer.from(RLP.encode(bufArrToArr(formatted))), - DB_OPTS - ) + await this.blockchain!.db.put(CLIQUE_SIGNERS_KEY, RLP.encode(formatted), DB_OPTS) // Output active signers for debugging purposes let i = 0 for (const signer of this.cliqueActiveSigners()) { @@ -281,7 +278,7 @@ export class CliqueConsensus implements Consensus { vote[0] >= BigInt(lastEpochBlockNumber) && !vote[1][0].equals(signer) && vote[1][1].equals(beneficiary) && - vote[1][2].equals(CLIQUE_NONCE_AUTH) + equalsBytes(vote[1][2], CLIQUE_NONCE_AUTH) ) }) const beneficiaryVotesAUTH: Address[] = [] @@ -294,7 +291,7 @@ export class CliqueConsensus implements Consensus { } } let numBeneficiaryVotesAUTH = beneficiaryVotesAUTH.length - if (round === 2 && nonce.equals(CLIQUE_NONCE_AUTH)) { + if (round === 2 && equalsBytes(nonce, CLIQUE_NONCE_AUTH)) { numBeneficiaryVotesAUTH += 1 } // Majority consensus @@ -303,8 +300,14 @@ export class CliqueConsensus implements Consensus { // Authorize new signer activeSigners.push(beneficiary) activeSigners.sort((a, b) => { - // Sort by buffer size - return a.toBuffer().compare(b.toBuffer()) + // Sort by array size + const result = + toType(a.toString(), TypeOutput.BigInt) < toType(b.toString(), TypeOutput.BigInt) + if (result) { + return -1 + } else { + return 1 + } }) // Discard votes for added signer this._cliqueLatestVotes = this._cliqueLatestVotes.filter( @@ -318,7 +321,7 @@ export class CliqueConsensus implements Consensus { vote[0] >= BigInt(lastEpochBlockNumber) && !vote[1][0].equals(signer) && vote[1][1].equals(beneficiary) && - vote[1][2].equals(CLIQUE_NONCE_DROP) + equalsBytes(vote[1][2], CLIQUE_NONCE_DROP) ) }) const beneficiaryVotesDROP: Address[] = [] @@ -332,7 +335,7 @@ export class CliqueConsensus implements Consensus { } let numBeneficiaryVotesDROP = beneficiaryVotesDROP.length - if (round === 2 && nonce.equals(CLIQUE_NONCE_DROP)) { + if (round === 2 && equalsBytes(nonce, CLIQUE_NONCE_DROP)) { numBeneficiaryVotesDROP += 1 } // Majority consensus @@ -352,7 +355,7 @@ export class CliqueConsensus implements Consensus { this._cliqueLatestVotes.push(latestVote) debug( `[Block ${header.number}] New clique vote: ${signer} -> ${beneficiary} ${ - nonce.equals(CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP' + equalsBytes(nonce, CLIQUE_NONCE_AUTH) ? 'AUTH' : 'DROP' }` ) } @@ -388,14 +391,10 @@ export class CliqueConsensus implements Consensus { // save votes to db const formatted = this._cliqueLatestVotes.map((v) => [ - bigIntToBuffer(v[0]), - [v[1][0].toBuffer(), v[1][1].toBuffer(), v[1][2]], + bigIntToBytes(v[0]), + [v[1][0].toBytes(), v[1][1].toBytes(), v[1][2]], ]) - await this.blockchain!.db.put( - CLIQUE_VOTES_KEY, - Buffer.from(RLP.encode(bufArrToArr(formatted))), - DB_OPTS - ) + await this.blockchain!.db.put(CLIQUE_VOTES_KEY, RLP.encode(formatted), DB_OPTS) } /** @@ -496,14 +495,10 @@ export class CliqueConsensus implements Consensus { // save to db const formatted = this._cliqueLatestBlockSigners.map((b) => [ - bigIntToBuffer(b[0]), - b[1].toBuffer(), + bigIntToBytes(b[0]), + b[1].toBytes(), ]) - await this.blockchain!.db.put( - CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY, - Buffer.from(RLP.encode(bufArrToArr(formatted))), - DB_OPTS - ) + await this.blockchain!.db.put(CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY, RLP.encode(formatted), DB_OPTS) } /** @@ -512,14 +507,14 @@ export class CliqueConsensus implements Consensus { */ private async getCliqueLatestSignerStates(): Promise { try { - const signerStates = await this.blockchain!.db.get( + const signerStates = await this.blockchain!.db.get( CLIQUE_SIGNERS_KEY, DB_OPTS ) - const states = arrToBufArr(RLP.decode(Uint8Array.from(signerStates))) as [Buffer, Buffer[]] + const states = RLP.decode(signerStates) as [Uint8Array, Uint8Array[]] return states.map((state) => { - const blockNum = bufferToBigInt(state[0] as Buffer) - const addrs = (state[1]).map((buf: Buffer) => new Address(buf)) + const blockNum = bytesToBigInt(state[0] as Uint8Array) + const addrs = (state[1]).map((bytes: Uint8Array) => new Address(bytes)) return [blockNum, addrs] }) as CliqueLatestSignerStates } catch (error: any) { @@ -536,13 +531,13 @@ export class CliqueConsensus implements Consensus { */ private async getCliqueLatestVotes(): Promise { try { - const signerVotes = await this.blockchain!.db.get(CLIQUE_VOTES_KEY, DB_OPTS) - const votes = arrToBufArr(RLP.decode(Uint8Array.from(signerVotes))) as [ - Buffer, - [Buffer, Buffer, Buffer] - ] + const signerVotes = await this.blockchain!.db.get( + CLIQUE_VOTES_KEY, + DB_OPTS + ) + const votes = RLP.decode(signerVotes) as [Uint8Array, [Uint8Array, Uint8Array, Uint8Array]] return votes.map((vote) => { - const blockNum = bufferToBigInt(vote[0] as Buffer) + const blockNum = bytesToBigInt(vote[0] as Uint8Array) const signer = new Address((vote[1] as any)[0]) const beneficiary = new Address((vote[1] as any)[1]) const nonce = (vote[1] as any)[2] @@ -562,13 +557,13 @@ export class CliqueConsensus implements Consensus { */ private async getCliqueLatestBlockSigners(): Promise { try { - const blockSigners = await this.blockchain!.db.get( + const blockSigners = await this.blockchain!.db.get( CLIQUE_BLOCK_SIGNERS_SNAPSHOT_KEY, DB_OPTS ) - const signers = arrToBufArr(RLP.decode(Uint8Array.from(blockSigners))) as [Buffer, Buffer][] + const signers = RLP.decode(blockSigners) as [Uint8Array, Uint8Array][] return signers.map((s) => { - const blockNum = bufferToBigInt(s[0] as Buffer) + const blockNum = bytesToBigInt(s[0] as Uint8Array) const signer = new Address(s[1] as any) return [blockNum, signer] }) as CliqueLatestBlockSigners diff --git a/packages/blockchain/src/db/cache.ts b/packages/blockchain/src/db/cache.ts index d84a3459c8c..9df9511420b 100644 --- a/packages/blockchain/src/db/cache.ts +++ b/packages/blockchain/src/db/cache.ts @@ -1,7 +1,8 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' import * as LRUCache from 'lru-cache' /** - * Simple LRU Cache that allows for keys of type Buffer + * Simple LRU Cache that allows for keys of type Uint8Array * @hidden */ export class Cache { @@ -11,23 +12,23 @@ export class Cache { this._cache = new LRUCache(opts) } - set(key: string | Buffer, value: V): void { - if (key instanceof Buffer) { - key = key.toString('hex') + set(key: string | Uint8Array, value: V): void { + if (key instanceof Uint8Array) { + key = bytesToHex(key) } this._cache.set(key, value) } - get(key: string | Buffer): V | undefined { - if (key instanceof Buffer) { - key = key.toString('hex') + get(key: string | Uint8Array): V | undefined { + if (key instanceof Uint8Array) { + key = bytesToHex(key) } return this._cache.get(key) } - del(key: string | Buffer): void { - if (key instanceof Buffer) { - key = key.toString('hex') + del(key: string | Uint8Array): void { + if (key instanceof Uint8Array) { + key = bytesToHex(key) } this._cache.del(key) } diff --git a/packages/blockchain/src/db/constants.ts b/packages/blockchain/src/db/constants.ts index 341b338baeb..7bb6aa3bdf3 100644 --- a/packages/blockchain/src/db/constants.ts +++ b/packages/blockchain/src/db/constants.ts @@ -1,4 +1,4 @@ -import { bigIntToBuffer } from '@ethereumjs/util' +import { bigIntToBytes, concatBytesNoTypeCheck, utf8ToBytes } from '@ethereumjs/util' // Geth compatible DB keys @@ -17,52 +17,55 @@ const HEAD_BLOCK_KEY = 'LastBlock' /** * headerPrefix + number + hash -> header */ -const HEADER_PREFIX = Buffer.from('h') +const HEADER_PREFIX = utf8ToBytes('h') /** * headerPrefix + number + hash + tdSuffix -> td */ -const TD_SUFFIX = Buffer.from('t') +const TD_SUFFIX = utf8ToBytes('t') /** * headerPrefix + number + numSuffix -> hash */ -const NUM_SUFFIX = Buffer.from('n') +const NUM_SUFFIX = utf8ToBytes('n') /** * blockHashPrefix + hash -> number */ -const BLOCK_HASH_PEFIX = Buffer.from('H') +const BLOCK_HASH_PEFIX = utf8ToBytes('H') /** * bodyPrefix + number + hash -> block body */ -const BODY_PREFIX = Buffer.from('b') +const BODY_PREFIX = utf8ToBytes('b') // Utility functions /** - * Convert bigint to big endian Buffer + * Convert bigint to big endian Uint8Array */ -const bufBE8 = (n: bigint) => bigIntToBuffer(BigInt.asUintN(64, n)) +const bytesBE8 = (n: bigint) => bigIntToBytes(BigInt.asUintN(64, n)) -const tdKey = (n: bigint, hash: Buffer) => - Buffer.concat([HEADER_PREFIX, bufBE8(n), hash, TD_SUFFIX]) +const tdKey = (n: bigint, hash: Uint8Array) => + concatBytesNoTypeCheck(HEADER_PREFIX, bytesBE8(n), hash, TD_SUFFIX) -const headerKey = (n: bigint, hash: Buffer) => Buffer.concat([HEADER_PREFIX, bufBE8(n), hash]) +const headerKey = (n: bigint, hash: Uint8Array) => + concatBytesNoTypeCheck(HEADER_PREFIX, bytesBE8(n), hash) -const bodyKey = (n: bigint, hash: Buffer) => Buffer.concat([BODY_PREFIX, bufBE8(n), hash]) +const bodyKey = (n: bigint, hash: Uint8Array) => + concatBytesNoTypeCheck(BODY_PREFIX, bytesBE8(n), hash) -const numberToHashKey = (n: bigint) => Buffer.concat([HEADER_PREFIX, bufBE8(n), NUM_SUFFIX]) +const numberToHashKey = (n: bigint) => + concatBytesNoTypeCheck(HEADER_PREFIX, bytesBE8(n), NUM_SUFFIX) -const hashToNumberKey = (hash: Buffer) => Buffer.concat([BLOCK_HASH_PEFIX, hash]) +const hashToNumberKey = (hash: Uint8Array) => concatBytesNoTypeCheck(BLOCK_HASH_PEFIX, hash) /** * @hidden */ export { bodyKey, - bufBE8, + bytesBE8, hashToNumberKey, HEAD_BLOCK_KEY, HEAD_HEADER_KEY, diff --git a/packages/blockchain/src/db/helpers.ts b/packages/blockchain/src/db/helpers.ts index c7146b050f3..c55a19716c5 100644 --- a/packages/blockchain/src/db/helpers.ts +++ b/packages/blockchain/src/db/helpers.ts @@ -1,8 +1,7 @@ import { Block } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' -import { bufBE8 } from './constants' +import { bytesBE8 } from './constants' import { DBOp, DBTarget } from './operation' import type { BlockHeader } from '@ethereumjs/block' @@ -12,8 +11,8 @@ import type { BlockHeader } from '@ethereumjs/block' * and the DB operations from `db/operation.ts` and also handles the right encoding of the keys */ -function DBSetTD(TD: bigint, blockNumber: bigint, blockHash: Buffer): DBOp { - return DBOp.set(DBTarget.TotalDifficulty, Buffer.from(RLP.encode(TD)), { +function DBSetTD(TD: bigint, blockNumber: bigint, blockHash: Uint8Array): DBOp { + return DBOp.set(DBTarget.TotalDifficulty, RLP.encode(TD), { blockNumber, blockHash, }) @@ -50,7 +49,7 @@ function DBSetBlockOrHeader(blockBody: Block | BlockHeader): DBOp[] { (blockBody.withdrawals?.length ?? 0) || blockBody.uncleHeaders.length)) ) { - const bodyValue = Buffer.from(RLP.encode(bufArrToArr(blockBody.raw()).slice(1))) + const bodyValue = RLP.encode(blockBody.raw().slice(1)) dbOps.push( DBOp.set(DBTarget.Body, bodyValue, { blockNumber, @@ -62,20 +61,20 @@ function DBSetBlockOrHeader(blockBody: Block | BlockHeader): DBOp[] { return dbOps } -function DBSetHashToNumber(blockHash: Buffer, blockNumber: bigint): DBOp { - const blockNumber8Byte = bufBE8(blockNumber) +function DBSetHashToNumber(blockHash: Uint8Array, blockNumber: bigint): DBOp { + const blockNumber8Byte = bytesBE8(blockNumber) return DBOp.set(DBTarget.HashToNumber, blockNumber8Byte, { blockHash, }) } -function DBSaveLookups(blockHash: Buffer, blockNumber: bigint, skipNumIndex?: boolean): DBOp[] { +function DBSaveLookups(blockHash: Uint8Array, blockNumber: bigint, skipNumIndex?: boolean): DBOp[] { const ops = [] if (skipNumIndex !== true) { ops.push(DBOp.set(DBTarget.NumberToHash, blockHash, { blockNumber })) } - const blockNumber8Bytes = bufBE8(blockNumber) + const blockNumber8Bytes = bytesBE8(blockNumber) ops.push( DBOp.set(DBTarget.HashToNumber, blockNumber8Bytes, { blockHash, diff --git a/packages/blockchain/src/db/manager.ts b/packages/blockchain/src/db/manager.ts index 98a806c1fcc..d84911091af 100644 --- a/packages/blockchain/src/db/manager.ts +++ b/packages/blockchain/src/db/manager.ts @@ -1,12 +1,12 @@ import { Block, BlockHeader, valuesArrayToHeaderData } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' -import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, arrToBufArr, bufferToBigInt } from '@ethereumjs/util' +import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, bytesToBigInt, equalsBytes } from '@ethereumjs/util' import { Cache } from './cache' import { DBOp, DBTarget } from './operation' import type { DBOpData, DatabaseKey } from './operation' -import type { BlockBodyBuffer, BlockBuffer, BlockOptions } from '@ethereumjs/block' +import type { BlockBodyBytes, BlockBytes, BlockOptions } from '@ethereumjs/block' import type { Common } from '@ethereumjs/common' import type { AbstractLevel } from 'abstract-level' @@ -32,7 +32,7 @@ export interface GetOpts { cache?: string } -export type CacheMap = { [key: string]: Cache } +export type CacheMap = { [key: string]: Cache } /** * Abstraction over a DB to facilitate storing/fetching blockchain-related @@ -42,10 +42,10 @@ export type CacheMap = { [key: string]: Cache } export class DBManager { private _cache: CacheMap private _common: Common - private _db: AbstractLevel + private _db: AbstractLevel constructor( - db: AbstractLevel, + db: AbstractLevel, common: Common ) { this._db = db @@ -62,10 +62,10 @@ export class DBManager { /** * Fetches iterator heads from the db. */ - async getHeads(): Promise<{ [key: string]: Buffer }> { + async getHeads(): Promise<{ [key: string]: Uint8Array }> { const heads = await this.get(DBTarget.Heads) for (const key of Object.keys(heads)) { - heads[key] = Buffer.from(heads[key]) + heads[key] = Uint8Array.from(heads[key]) } return heads } @@ -73,14 +73,14 @@ export class DBManager { /** * Fetches header of the head block. */ - async getHeadHeader(): Promise { + async getHeadHeader(): Promise { return this.get(DBTarget.HeadHeader) } /** * Fetches head block. */ - async getHeadBlock(): Promise { + async getHeadBlock(): Promise { return this.get(DBTarget.HeadBlock) } @@ -88,14 +88,14 @@ export class DBManager { * Fetches a block (header and body) given a block id, * which can be either its hash or its number. */ - async getBlock(blockId: Buffer | bigint | number): Promise { + async getBlock(blockId: Uint8Array | bigint | number): Promise { if (typeof blockId === 'number' && Number.isInteger(blockId)) { blockId = BigInt(blockId) } let number let hash - if (Buffer.isBuffer(blockId)) { + if (blockId instanceof Uint8Array) { hash = blockId number = await this.hashToNumber(blockId) } else if (typeof blockId === 'bigint') { @@ -106,7 +106,7 @@ export class DBManager { } const header = await this.getHeader(hash, number) - let body: BlockBodyBuffer + let body: BlockBodyBytes try { body = await this.getBody(hash, number) } catch (error: any) { @@ -116,8 +116,8 @@ export class DBManager { // Do extra validations on the header since we are assuming empty transactions and uncles if ( - !header.transactionsTrie.equals(KECCAK256_RLP) || - !header.uncleHash.equals(KECCAK256_RLP_ARRAY) + !equalsBytes(header.transactionsTrie, KECCAK256_RLP) || + !equalsBytes(header.uncleHash, KECCAK256_RLP_ARRAY) ) { throw error } @@ -125,14 +125,14 @@ export class DBManager { // If this block had empty withdrawals push an empty array in body if (header.withdrawalsRoot !== undefined) { // Do extra validations for withdrawal before assuming empty withdrawals - if (!header.withdrawalsRoot.equals(KECCAK256_RLP)) { + if (!equalsBytes(header.withdrawalsRoot, KECCAK256_RLP)) { throw error } body.push([]) } } - const blockData = [header.raw(), ...body] as BlockBuffer + const blockData = [header.raw(), ...body] as BlockBytes const opts: BlockOptions = { common: this._common } if (number === BigInt(0)) { opts.hardforkByTTD = await this.getTotalDifficulty(hash, BigInt(0)) @@ -145,17 +145,17 @@ export class DBManager { /** * Fetches body of a block given its hash and number. */ - async getBody(blockHash: Buffer, blockNumber: bigint): Promise { + async getBody(blockHash: Uint8Array, blockNumber: bigint): Promise { const body = await this.get(DBTarget.Body, { blockHash, blockNumber }) - return arrToBufArr(RLP.decode(Uint8Array.from(body))) as BlockBodyBuffer + return RLP.decode(body) as BlockBodyBytes } /** * Fetches header of a block given its hash and number. */ - async getHeader(blockHash: Buffer, blockNumber: bigint) { + async getHeader(blockHash: Uint8Array, blockNumber: bigint) { const encodedHeader = await this.get(DBTarget.Header, { blockHash, blockNumber }) - const headerValues = arrToBufArr(RLP.decode(Uint8Array.from(encodedHeader))) + const headerValues = RLP.decode(encodedHeader) const opts: BlockOptions = { common: this._common } if (blockNumber === BigInt(0)) { @@ -163,33 +163,33 @@ export class DBManager { } else { // Lets fetch the parent hash but not by number since this block might not // be in canonical chain - const headerData = valuesArrayToHeaderData(headerValues as Buffer[]) - const parentHash = headerData.parentHash as Buffer + const headerData = valuesArrayToHeaderData(headerValues as Uint8Array[]) + const parentHash = headerData.parentHash as Uint8Array opts.hardforkByTTD = await this.getTotalDifficulty(parentHash, blockNumber - BigInt(1)) } - return BlockHeader.fromValuesArray(headerValues as Buffer[], opts) + return BlockHeader.fromValuesArray(headerValues as Uint8Array[], opts) } /** * Fetches total difficulty for a block given its hash and number. */ - async getTotalDifficulty(blockHash: Buffer, blockNumber: bigint): Promise { + async getTotalDifficulty(blockHash: Uint8Array, blockNumber: bigint): Promise { const td = await this.get(DBTarget.TotalDifficulty, { blockHash, blockNumber }) - return bufferToBigInt(Buffer.from(RLP.decode(Uint8Array.from(td)) as Uint8Array)) + return bytesToBigInt(RLP.decode(td) as Uint8Array) } /** * Performs a block hash to block number lookup. */ - async hashToNumber(blockHash: Buffer): Promise { + async hashToNumber(blockHash: Uint8Array): Promise { const value = await this.get(DBTarget.HashToNumber, { blockHash }) - return bufferToBigInt(value) + return bytesToBigInt(value) } /** * Performs a block number to block hash lookup. */ - async numberToHash(blockNumber: bigint): Promise { + async numberToHash(blockNumber: bigint): Promise { if (blockNumber < BigInt(0)) { throw new NotFoundError(blockNumber) } diff --git a/packages/blockchain/src/db/operation.ts b/packages/blockchain/src/db/operation.ts index 53c70fef208..4a6cfa9c1ae 100644 --- a/packages/blockchain/src/db/operation.ts +++ b/packages/blockchain/src/db/operation.ts @@ -31,16 +31,16 @@ export enum DBTarget { */ export interface DBOpData { type?: string - key: Buffer | string + key: Uint8Array | string keyEncoding: string valueEncoding?: string - value?: Buffer | object + value?: Uint8Array | object } // a Database Key is identified by a block hash, a block number, or both export type DatabaseKey = { blockNumber?: bigint - blockHash?: Buffer + blockHash?: Uint8Array } /** @@ -56,8 +56,8 @@ export class DBOp { this.baseDBOp = { key: '', - keyEncoding: 'buffer', - valueEncoding: 'buffer', + keyEncoding: 'view', + valueEncoding: 'view', } switch (operationTarget) { @@ -107,7 +107,11 @@ export class DBOp { } // set operation: note: value/key is not in default order - public static set(operationTarget: DBTarget, value: Buffer | object, key?: DatabaseKey): DBOp { + public static set( + operationTarget: DBTarget, + value: Uint8Array | object, + key?: DatabaseKey + ): DBOp { const dbOperation = new DBOp(operationTarget, key) dbOperation.baseDBOp.value = value dbOperation.baseDBOp.type = 'put' @@ -130,7 +134,7 @@ export class DBOp { public updateCache(cacheMap: CacheMap) { if (this.cacheString !== undefined && cacheMap[this.cacheString] !== undefined) { if (this.baseDBOp.type === 'put') { - Buffer.isBuffer(this.baseDBOp.value) && + this.baseDBOp.value instanceof Uint8Array && cacheMap[this.cacheString].set(this.baseDBOp.key, this.baseDBOp.value) } else if (this.baseDBOp.type === 'del') { cacheMap[this.cacheString].del(this.baseDBOp.key) diff --git a/packages/blockchain/src/genesisStates/index.ts b/packages/blockchain/src/genesisStates/index.ts index 9a00414fa85..838daf9fd08 100644 --- a/packages/blockchain/src/genesisStates/index.ts +++ b/packages/blockchain/src/genesisStates/index.ts @@ -1,7 +1,8 @@ import { RLP } from '@ethereumjs/rlp' import { Trie } from '@ethereumjs/trie' -import { Account, isHexPrefixed, toBuffer, unpadBuffer } from '@ethereumjs/util' +import { Account, isHexPrefixed, toBytes, unpadBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { hexToBytes } from 'ethereum-cryptography/utils' import type { PrefixedHexString } from '@ethereumjs/util' @@ -23,7 +24,7 @@ export interface GenesisState { export async function genesisStateRoot(genesisState: GenesisState) { const trie = new Trie({ useKeyHashing: true }) for (const [key, value] of Object.entries(genesisState)) { - const address = isHexPrefixed(key) ? toBuffer(key) : Buffer.from(key, 'hex') + const address = isHexPrefixed(key) ? toBytes(key) : hexToBytes(key) const account = new Account() if (typeof value === 'string') { account.balance = BigInt(value) @@ -33,18 +34,14 @@ export async function genesisStateRoot(genesisState: GenesisState) { account.balance = BigInt(balance) } if (code !== undefined) { - account.codeHash = Buffer.from(keccak256(toBuffer(code))) + account.codeHash = keccak256(toBytes(code)) } if (storage !== undefined) { const storageTrie = new Trie({ useKeyHashing: true }) for (const [k, val] of storage) { - const storageKey = isHexPrefixed(k) ? toBuffer(k) : Buffer.from(k, 'hex') - const storageVal = Buffer.from( - RLP.encode( - Uint8Array.from( - unpadBuffer(isHexPrefixed(val) ? toBuffer(val) : Buffer.from(val, 'hex')) - ) - ) + const storageKey = isHexPrefixed(k) ? toBytes(k) : hexToBytes(k) + const storageVal = RLP.encode( + unpadBytes(isHexPrefixed(val) ? toBytes(val) : hexToBytes(val)) ) await storageTrie.put(storageKey, storageVal) } diff --git a/packages/blockchain/src/types.ts b/packages/blockchain/src/types.ts index cdbfb138599..a1396722af4 100644 --- a/packages/blockchain/src/types.ts +++ b/packages/blockchain/src/types.ts @@ -21,12 +21,12 @@ export interface BlockchainInterface { * * @param blockHash - The hash of the block to be deleted */ - delBlock(blockHash: Buffer): Promise + delBlock(blockHash: Uint8Array): Promise /** * Returns a block by its hash or number. */ - getBlock(blockId: Buffer | number | bigint): Promise + getBlock(blockId: Uint8Array | number | bigint): Promise /** * Iterates through blocks starting at the specified iterator head and calls @@ -66,7 +66,7 @@ export interface BlockchainInterface { /** * Gets total difficulty for a block specified by hash and number */ - getTotalDifficulty?(hash: Buffer, number?: bigint): Promise + getTotalDifficulty?(hash: Uint8Array, number?: bigint): Promise /** * Returns the genesis state of the blockchain. @@ -112,7 +112,7 @@ export interface BlockchainOptions { * or use the `level` convenience package: * `new MemoryLevel('./db1')` */ - db?: AbstractLevel + db?: AbstractLevel /** * This flags indicates if a block should be validated along the consensus algorithm diff --git a/packages/blockchain/test/blockValidation.spec.ts b/packages/blockchain/test/blockValidation.spec.ts index 014e8e790a0..af2d5bca641 100644 --- a/packages/blockchain/test/blockValidation.spec.ts +++ b/packages/blockchain/test/blockValidation.spec.ts @@ -1,9 +1,8 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' +import { bytesToPrefixedHexString } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src' @@ -335,8 +334,9 @@ tape('[Blockchain]: Block validation tests', (t) => { common: new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }), }) - forkBlockHeaderData.uncleHash = - '0x' + bytesToHex(keccak256(RLP.encode(bufArrToArr([uncleHeader.raw()])))) + forkBlockHeaderData.uncleHash = bytesToPrefixedHexString( + keccak256(RLP.encode([uncleHeader.raw()])) + ) const forkBlock_ValidCommon = Block.fromBlockData( { @@ -348,8 +348,9 @@ tape('[Blockchain]: Block validation tests', (t) => { } ) - st.ok( - forkBlock_ValidCommon.uncleHeaders[0].hash().equals(uncleHeader.hash()), + st.deepEquals( + forkBlock_ValidCommon.uncleHeaders[0].hash(), + uncleHeader.hash(), 'successfully validated a pre-london uncle on a london block' ) st.equal(common.hardfork(), Hardfork.London, 'validation did not change common hardfork') diff --git a/packages/blockchain/test/clique.spec.ts b/packages/blockchain/test/clique.spec.ts index 23a673fdda3..4cd8801f937 100644 --- a/packages/blockchain/test/clique.spec.ts +++ b/packages/blockchain/test/clique.spec.ts @@ -1,6 +1,7 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { concatBytes, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src' @@ -15,7 +16,7 @@ tape('Clique: Initialization', (t) => { const blockchain = await Blockchain.create({ common }) const head = await blockchain.getIteratorHead() - st.ok(head.hash().equals(blockchain.genesisBlock.hash()), 'correct genesis hash') + st.ok(equalsBytes(head.hash(), blockchain.genesisBlock.hash()), 'correct genesis hash') st.deepEquals( (blockchain.consensus as CliqueConsensus).cliqueActiveSigners(), @@ -26,84 +27,60 @@ tape('Clique: Initialization', (t) => { }) const COMMON = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Chainstart }) - const EXTRA_DATA = Buffer.alloc(97) + const EXTRA_DATA = new Uint8Array(97) const GAS_LIMIT = BigInt(8000000) type Signer = { address: Address - privateKey: Buffer - publicKey: Buffer + privateKey: Uint8Array + publicKey: Uint8Array } const A: Signer = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' - ), - publicKey: Buffer.from( - '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195', - 'hex' + address: new Address(hexToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), + publicKey: hexToBytes( + '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195' ), } const B: Signer = { - address: new Address(Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex')), - privateKey: Buffer.from( - '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', - 'hex' - ), - publicKey: Buffer.from( - 'ca0a55f6e81cb897aee6a1c390aa83435c41048faa0564b226cfc9f3df48b73e846377fb0fd606df073addc7bd851f22547afbbdd5c3b028c91399df802083a2', - 'hex' + address: new Address(hexToBytes('6f62d8382bf2587361db73ceca28be91b2acb6df')), + privateKey: hexToBytes('2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6'), + publicKey: hexToBytes( + 'ca0a55f6e81cb897aee6a1c390aa83435c41048faa0564b226cfc9f3df48b73e846377fb0fd606df073addc7bd851f22547afbbdd5c3b028c91399df802083a2' ), } const C: Signer = { - address: new Address(Buffer.from('83c30730d1972baa09765a1ac72a43db27fedce5', 'hex')), - privateKey: Buffer.from( - 'f216ddcf276079043c52b5dd144aa073e6b272ad4bfeaf4fbbc044aa478d1927', - 'hex' - ), - publicKey: Buffer.from( - '555b19a5cbe6dd082a4a1e1e0520dd52a82ba24fd5598ea31f0f31666c40905ed319314c5fb06d887b760229e1c0e616294e7b1cb5dfefb71507c9112132ce56', - 'hex' + address: new Address(hexToBytes('83c30730d1972baa09765a1ac72a43db27fedce5')), + privateKey: hexToBytes('f216ddcf276079043c52b5dd144aa073e6b272ad4bfeaf4fbbc044aa478d1927'), + publicKey: hexToBytes( + '555b19a5cbe6dd082a4a1e1e0520dd52a82ba24fd5598ea31f0f31666c40905ed319314c5fb06d887b760229e1c0e616294e7b1cb5dfefb71507c9112132ce56' ), } const D: Signer = { - address: new Address(Buffer.from('8458f408106c4875c96679f3f556a511beabe138', 'hex')), - privateKey: Buffer.from( - '159e95d07a6c64ddbafa6036cdb7b8114e6e8cdc449ca4b0468a6d0c955f991b', - 'hex' - ), - publicKey: Buffer.from( - 'f02724341e2df54cf53515f079b1354fa8d437e79c5b091b8d8cc7cbcca00fd8ad854cb3b3a85b06c44ecb7269404a67be88b561f2224c94d133e5fc21be915c', - 'hex' + address: new Address(hexToBytes('8458f408106c4875c96679f3f556a511beabe138')), + privateKey: hexToBytes('159e95d07a6c64ddbafa6036cdb7b8114e6e8cdc449ca4b0468a6d0c955f991b'), + publicKey: hexToBytes( + 'f02724341e2df54cf53515f079b1354fa8d437e79c5b091b8d8cc7cbcca00fd8ad854cb3b3a85b06c44ecb7269404a67be88b561f2224c94d133e5fc21be915c' ), } const E: Signer = { - address: new Address(Buffer.from('ab80a948c661aa32d09952d2a6c4ad77a4c947be', 'hex')), - privateKey: Buffer.from( - '48ec5a6c4a7fc67b10a9d4c8a8f594a81ae42e41ed061fa5218d96abb6012344', - 'hex' - ), - publicKey: Buffer.from( - 'adefb82b9f54e80aa3532263e4478739de16fcca6828f4ae842f8a07941c347fa59d2da1300569237009f0f122dc1fd6abb0db8fcb534280aa94948a5cc95f94', - 'hex' + address: new Address(hexToBytes('ab80a948c661aa32d09952d2a6c4ad77a4c947be')), + privateKey: hexToBytes('48ec5a6c4a7fc67b10a9d4c8a8f594a81ae42e41ed061fa5218d96abb6012344'), + publicKey: hexToBytes( + 'adefb82b9f54e80aa3532263e4478739de16fcca6828f4ae842f8a07941c347fa59d2da1300569237009f0f122dc1fd6abb0db8fcb534280aa94948a5cc95f94' ), } const F: Signer = { - address: new Address(Buffer.from('dc7bc81ddf67d037d7439f8e6ff12f3d2a100f71', 'hex')), - privateKey: Buffer.from( - '86b0ff7b6cf70786f29f297c57562905ab0b6c32d69e177a46491e56da9e486e', - 'hex' - ), - publicKey: Buffer.from( - 'd3e3d2b722e325bfc085ff5638a112b4e7e88ff13f92fc7f6cfc14b5a25e8d1545a2f27d8537b96e8919949d5f8c139ae7fc81aea7cf7fe5d43d7faaa038e35b', - 'hex' + address: new Address(hexToBytes('dc7bc81ddf67d037d7439f8e6ff12f3d2a100f71')), + privateKey: hexToBytes('86b0ff7b6cf70786f29f297c57562905ab0b6c32d69e177a46491e56da9e486e'), + publicKey: hexToBytes( + 'd3e3d2b722e325bfc085ff5638a112b4e7e88ff13f92fc7f6cfc14b5a25e8d1545a2f27d8537b96e8919949d5f8c139ae7fc81aea7cf7fe5d43d7faaa038e35b' ), } @@ -111,11 +88,11 @@ tape('Clique: Initialization', (t) => { common = common ?? COMMON const blocks: Block[] = [] - const extraData = Buffer.concat([ - Buffer.alloc(32), - ...signers.map((s) => s.address.toBuffer()), - Buffer.alloc(65), - ]) + const extraData = concatBytes( + new Uint8Array(32), + ...signers.map((s) => s.address.toBytes()), + new Uint8Array(65) + ) const genesisBlock = Block.fromBlockData( { header: { gasLimit: GAS_LIMIT, extraData } }, { common } @@ -152,11 +129,11 @@ tape('Clique: Initialization', (t) => { nonce = CLIQUE_NONCE_AUTH } } else if (checkpointSigners) { - extraData = Buffer.concat([ - Buffer.alloc(32), - ...checkpointSigners.map((s) => s.address.toBuffer()), - Buffer.alloc(65), - ]) + extraData = concatBytes( + new Uint8Array(32), + ...checkpointSigners.map((s) => s.address.toBytes()), + new Uint8Array(65) + ) } const blockData = { @@ -195,12 +172,12 @@ tape('Clique: Initialization', (t) => { ;(blockchain as any)._validateConsensus = true const number = (COMMON.consensusConfig() as CliqueConfig).epoch const unauthorizedSigner = Address.fromString('0x00a839de7922491683f547a67795204763ff8237') - const extraData = Buffer.concat([ - Buffer.alloc(32), - A.address.toBuffer(), - unauthorizedSigner.toBuffer(), - Buffer.alloc(65), - ]) + const extraData = concatBytes( + new Uint8Array(32), + A.address.toBytes(), + unauthorizedSigner.toBytes(), + new Uint8Array(65) + ) const block = Block.fromBlockData( { header: { number, extraData } }, { common: COMMON, cliqueSigner: A.privateKey } @@ -225,7 +202,7 @@ tape('Clique: Initialization', (t) => { await addNextBlock(blockchain, blocks, A) const parentHeader = await blockchain.getCanonicalHeadHeader() const number = BigInt(2) - const extraData = Buffer.alloc(97) + const extraData = new Uint8Array(97) let difficulty = BigInt(5) let block = Block.fromBlockData( { @@ -292,7 +269,7 @@ tape('Clique: Initialization', (t) => { // noturn block await addNextBlock(blockchain, blocks, A) const block = await blockchain.getBlock(1) - if (inturnBlock.hash().equals(block.hash())) { + if (equalsBytes(inturnBlock.hash(), block.hash())) { st.pass('correct canonical block') } else { st.fail('invalid canonical block') diff --git a/packages/blockchain/test/customConsensus.spec.ts b/packages/blockchain/test/customConsensus.spec.ts index 479c1797200..d385eee7f63 100644 --- a/packages/blockchain/test/customConsensus.spec.ts +++ b/packages/blockchain/test/customConsensus.spec.ts @@ -1,5 +1,6 @@ import { Block } from '@ethereumjs/block' import { Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain, EthashConsensus } from '../src' @@ -19,7 +20,7 @@ class fibonacciConsensus implements Consensus { return new Promise((resolve) => resolve()) } validateConsensus(_block: Block): Promise { - if (_block.header.extraData.toString('hex') !== '12358d') { + if (bytesToHex(_block.header.extraData) !== '12358d') { throw new Error( 'header contains invalid extradata - must match first 6 elements of fibonacci sequence' ) diff --git a/packages/blockchain/test/index.spec.ts b/packages/blockchain/test/index.spec.ts index a1fb958af7e..7238639bc9d 100644 --- a/packages/blockchain/test/index.spec.ts +++ b/packages/blockchain/test/index.spec.ts @@ -1,5 +1,6 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import * as tape from 'tape' @@ -27,8 +28,9 @@ tape('blockchain test', (t) => { const iteratorHead = await blockchain.getIteratorHead() - st.ok( - iteratorHead.hash().equals(blockchain.genesisBlock.hash()), + st.deepEquals( + iteratorHead.hash(), + blockchain.genesisBlock.hash(), 'correct genesis hash (getIteratorHead())' ) @@ -76,8 +78,9 @@ tape('blockchain test', (t) => { validateConsensus: false, genesisBlock, }) - st.ok( - genesisBlock.hash().equals((await blockchain.getCanonicalHeadHeader()).hash()), + st.deepEquals( + genesisBlock.hash(), + (await blockchain.getCanonicalHeadHeader()).hash(), 'genesis block hash should be correct' ) st.end() @@ -185,7 +188,7 @@ tape('blockchain test', (t) => { const returnedBlock = await blockchain.getBlock(1) if (typeof returnedBlock !== 'undefined') { - st.ok(returnedBlock.hash().equals(blocks[1].hash())) + st.deepEquals(returnedBlock.hash(), blocks[1].hash()) } else { st.fail('block is not defined!') } @@ -204,7 +207,7 @@ tape('blockchain test', (t) => { genesisBlock, }) const block = await blockchain.getBlock(genesisBlock.hash()) - st.ok(block.hash().equals(genesisBlock.hash())) + st.deepEquals(block.hash(), genesisBlock.hash()) try { await blockchain.getBlock(5) @@ -214,7 +217,7 @@ tape('blockchain test', (t) => { } try { - await blockchain.getBlock(Buffer.from('1234', 'hex')) + await blockchain.getBlock(hexToBytes('1234')) st.fail('should throw an exception') } catch (e: any) { st.ok(e.message.includes('NotFound'), `should throw for non-existing block-by-hash request`) @@ -262,8 +265,8 @@ tape('blockchain test', (t) => { const newblock22 = await blockchain.getBlock(22) st.equal(newblock22.header.number, BigInt(22), 'canonical references should be restored') st.equal( - newblock22.hash().toString('hex'), - newblock22.hash().toString('hex'), + bytesToHex(newblock22.hash()), + bytesToHex(newblock22.hash()), 'fetched block should match' ) const newheader22 = await blockchain.getCanonicalHeader(BigInt(22)) @@ -406,13 +409,13 @@ tape('blockchain test', (t) => { t.test('should find needed hashes', async (st) => { const { blockchain, blocks, error } = await generateBlockchain(25) st.error(error, 'no error') - const neededHash = Buffer.from('abcdef', 'hex') + const neededHash = hexToBytes('abcdef') const hashes = await blockchain.selectNeededHashes([ blocks[0].hash(), blocks[9].hash(), neededHash, ]) - st.ok(hashes[0].equals(neededHash)) + st.deepEquals(hashes[0], neededHash) st.end() }) @@ -438,8 +441,8 @@ tape('blockchain test', (t) => { await blockchain.putHeader(forkHeader) - st.ok(blockchain._heads['staletest'].equals(blocks[14].hash()), 'should update stale head') - st.ok(blockchain._headBlockHash.equals(blocks[14].hash()), 'should update stale headBlock') + st.deepEquals(blockchain._heads['staletest'], blocks[14].hash(), 'should update stale head') + st.deepEquals(blockchain._headBlockHash, blocks[14].hash(), 'should update stale headBlock') st.end() }) @@ -464,13 +467,13 @@ tape('blockchain test', (t) => { await blockchain.putHeader(forkHeader) - st.ok(blockchain._heads['staletest'].equals(blocks[14].hash()), 'should update stale head') - st.ok(blockchain._headBlockHash.equals(blocks[14].hash()), 'should update stale headBlock') + st.deepEquals(blockchain._heads['staletest'], blocks[14].hash(), 'should update stale head') + st.deepEquals(blockchain._headBlockHash, blocks[14].hash(), 'should update stale headBlock') await blockchain.delBlock(forkHeader.hash()) - st.ok(blockchain._headHeaderHash.equals(blocks[14].hash()), 'should reset headHeader') - st.ok(blockchain._headBlockHash.equals(blocks[14].hash()), 'should not change headBlock') + st.deepEquals(blockchain._headHeaderHash, blocks[14].hash(), 'should reset headHeader') + st.deepEquals(blockchain._headBlockHash, blocks[14].hash(), 'should not change headBlock') st.end() }) @@ -487,7 +490,7 @@ tape('blockchain test', (t) => { } await delNextBlock(9) - st.ok(blockchain._headHeaderHash.equals(blocks[5].hash()), 'should have block 5 as head') + st.deepEquals(blockchain._headHeaderHash, blocks[5].hash(), 'should have block 5 as head') st.end() }) @@ -495,7 +498,7 @@ tape('blockchain test', (t) => { const { blockchain, blocks, error } = await generateBlockchain(25) st.error(error, 'no error') await blockchain.delBlock(blocks[1].hash()) - st.ok(blockchain._headHeaderHash.equals(blocks[0].hash()), 'should have genesis as head') + st.deepEquals(blockchain._headHeaderHash, blocks[0].hash(), 'should have genesis as head') st.end() }) @@ -524,7 +527,7 @@ tape('blockchain test', (t) => { await blockchain.getBlock(BigInt(1)) const block2HeaderValuesArray = blocks[2].header.raw() - block2HeaderValuesArray[1] = Buffer.alloc(32) + block2HeaderValuesArray[1] = new Uint8Array(32) const block2Header = BlockHeader.fromValuesArray(block2HeaderValuesArray, { common: blocks[2]._common, }) @@ -572,7 +575,7 @@ tape('blockchain test', (t) => { t.test('should add block with body', async (st) => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) - const genesisRlp = Buffer.from(testDataPreLondon.genesisRLP.slice(2), 'hex') + const genesisRlp = hexToBytes(testDataPreLondon.genesisRLP.slice(2)) const genesisBlock = Block.fromRLPSerializedBlock(genesisRlp, { common }) const blockchain = await Blockchain.create({ validateBlocks: true, @@ -580,7 +583,7 @@ tape('blockchain test', (t) => { genesisBlock, }) - const blockRlp = Buffer.from(testDataPreLondon.blocks[0].rlp.slice(2), 'hex') + const blockRlp = hexToBytes(testDataPreLondon.blocks[0].rlp.slice(2)) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) await blockchain.putBlock(block) st.end() @@ -597,7 +600,7 @@ tape('blockchain test', (t) => { st.equal(number, BigInt(0), 'should perform _hashToNumber correctly') const hash = await blockchain.dbManager.numberToHash(BigInt(0)) - st.ok(genesis.hash().equals(hash), 'should perform _numberToHash correctly') + st.deepEquals(genesis.hash(), hash, 'should perform _numberToHash correctly') // cast the blockchain as in order to get access to the private getTotalDifficulty const td = await (blockchain).getTotalDifficulty(genesis.hash(), BigInt(0)) @@ -606,7 +609,7 @@ tape('blockchain test', (t) => { }) t.test('should save headers', async (st) => { - const db = new MemoryLevel() + const db = new MemoryLevel() const gasLimit = 8000000 const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) @@ -638,10 +641,10 @@ tape('blockchain test', (t) => { }) const latestHeader = await blockchain.getCanonicalHeadHeader() - st.ok(latestHeader.hash().equals(header.hash()), 'should save headHeader') + st.deepEquals(latestHeader.hash(), header.hash(), 'should save headHeader') const latestBlock = await blockchain.getCanonicalHeadBlock() - st.ok(latestBlock.hash().equals(genesisBlock.hash()), 'should save headBlock') + st.deepEquals(latestBlock.hash(), genesisBlock.hash(), 'should save headBlock') st.end() }) @@ -691,18 +694,18 @@ tape('blockchain test', (t) => { await blockchain.putHeaders(headers) const latestHeader = await blockchain.getCanonicalHeadHeader() - st.ok(latestHeader.hash().equals(headers[1].hash()), 'should update latest header') + st.deepEquals(latestHeader.hash(), headers[1].hash(), 'should update latest header') const latestBlock = await blockchain.getCanonicalHeadBlock() - st.ok(latestBlock.hash().equals(genesisBlock.hash()), 'should not change latest block') + st.deepEquals(latestBlock.hash(), genesisBlock.hash(), 'should not change latest block') await blockchain.putBlock(block) const latestHeader2 = await blockchain.getCanonicalHeadHeader() - st.ok(latestHeader2.hash().equals(headers[1].hash()), 'should not change latest header') + st.deepEquals(latestHeader2.hash(), headers[1].hash(), 'should not change latest header') const getBlock = await blockchain.getCanonicalHeadBlock() - st.ok(getBlock!.hash().equals(block.hash()), 'should update latest block') + st.deepEquals(getBlock!.hash(), block.hash(), 'should update latest block') st.end() }) @@ -768,8 +771,9 @@ tape('initialization tests', (t) => { const blockchain = await Blockchain.create({ common }) const genesisHash = blockchain.genesisBlock.hash() - st.ok( - (await blockchain.getIteratorHead()).hash().equals(genesisHash), + st.deepEquals( + (await blockchain.getIteratorHead()).hash(), + genesisHash, 'head hash should equal expected ropsten genesis hash' ) @@ -777,8 +781,9 @@ tape('initialization tests', (t) => { const newBlockchain = await Blockchain.create({ db, common }) - st.ok( - (await newBlockchain.getIteratorHead()).hash().equals(genesisHash), + st.deepEquals( + (await newBlockchain.getIteratorHead()).hash(), + genesisHash, 'head hash should be read from the provided db' ) st.end() @@ -789,7 +794,7 @@ tape('initialization tests', (t) => { const genesisBlock = Block.fromBlockData( { header: { - extraData: Buffer.from('custom extra data'), + extraData: utf8ToBytes('custom extra data'), }, }, { common } @@ -798,14 +803,16 @@ tape('initialization tests', (t) => { const blockchain = await Blockchain.create({ common, genesisBlock }) const db = blockchain.db - st.ok( - (await blockchain.getIteratorHead()).hash().equals(hash), + st.deepEquals( + (await blockchain.getIteratorHead()).hash(), + hash, 'blockchain should put custom genesis block' ) const newBlockchain = await Blockchain.create({ db, genesisBlock }) - st.ok( - (await newBlockchain.getIteratorHead()).hash().equals(hash), + st.deepEquals( + (await newBlockchain.getIteratorHead()).hash(), + hash, 'head hash should be read from the provided db' ) st.end() @@ -816,7 +823,7 @@ tape('initialization tests', (t) => { const genesisBlock = Block.fromBlockData( { header: { - extraData: Buffer.from('custom extra data'), + extraData: utf8ToBytes('custom extra data'), }, }, { common } @@ -828,14 +835,14 @@ tape('initialization tests', (t) => { const otherGenesisBlock = Block.fromBlockData( { header: { - extraData: Buffer.from('other extra data'), + extraData: utf8ToBytes('other extra data'), }, }, { common } ) // assert that this is a block with a new hash - if (otherGenesisBlock.hash().equals(hash)) { + if (equalsBytes(otherGenesisBlock.hash(), hash)) { st.fail('other genesis block should have a different hash than the genesis block') } @@ -861,16 +868,14 @@ tape('initialization tests', (t) => { t.test('should correctly derive ropsten genesis block hash and stateRoot', async (st) => { const common = new Common({ chain: Chain.Ropsten }) const blockchain = await Blockchain.create({ common }) - const ropstenGenesisBlockHash = Buffer.from( - '41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d', - 'hex' + const ropstenGenesisBlockHash = hexToBytes( + '41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d' ) - const ropstenGenesisStateRoot = Buffer.from( - '217b0bbcfb72e2d57e28f33cb361b9983513177755dc3f33ce3e7022ed62b77b', - 'hex' + const ropstenGenesisStateRoot = hexToBytes( + '217b0bbcfb72e2d57e28f33cb361b9983513177755dc3f33ce3e7022ed62b77b' ) - st.ok(blockchain.genesisBlock.hash().equals(ropstenGenesisBlockHash)) - st.ok(blockchain.genesisBlock.header.stateRoot.equals(ropstenGenesisStateRoot)) + st.deepEquals(blockchain.genesisBlock.hash(), ropstenGenesisBlockHash) + st.deepEquals(blockchain.genesisBlock.header.stateRoot, ropstenGenesisStateRoot) st.end() }) }) diff --git a/packages/blockchain/test/iterator.spec.ts b/packages/blockchain/test/iterator.spec.ts index 58bb2fbf44f..b13f81bf08c 100644 --- a/packages/blockchain/test/iterator.spec.ts +++ b/packages/blockchain/test/iterator.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src' @@ -14,7 +15,7 @@ tape('blockchain test', (t) => { let reorged = 0 const iterated = await blockchain.iterator('test', (block: Block, reorg: boolean) => { if (reorg) reorged++ - if (block.hash().equals(blocks[i + 1].hash())) { + if (equalsBytes(block.hash(), blocks[i + 1].hash()) === true) { i++ } }) @@ -51,7 +52,7 @@ tape('blockchain test', (t) => { await blockchain.putBlocks(reorgedBlocks) } } else { - if (block.hash().equals(reorgedBlocks[Number(block.header.number) - 5].hash())) { + if (equalsBytes(block.hash(), reorgedBlocks[Number(block.header.number) - 5].hash())) { servedReorged++ } } @@ -59,7 +60,7 @@ tape('blockchain test', (t) => { undefined, true ) - st.equal(reorged, 1, ' should have reorged once') + st.equal(reorged, 1, 'should have reorged once') st.equal( servedReorged, reorgedBlocks.length, @@ -78,7 +79,7 @@ tape('blockchain test', (t) => { const iterated = await blockchain.iterator( 'test', (block: Block) => { - if (block.hash().equals(blocks[i + 1].hash())) { + if (equalsBytes(block.hash(), blocks[i + 1].hash())) { i++ } }, @@ -100,7 +101,7 @@ tape('blockchain test', (t) => { .iterator( 'test', (block: Block) => { - if (block.hash().equals(blocks[i + 1].hash())) { + if (equalsBytes(block.hash(), blocks[i + 1].hash())) { i++ } }, @@ -123,7 +124,7 @@ tape('blockchain test', (t) => { .iterator( 'test', (block: Block) => { - if (block.hash().equals(blocks[i + 1].hash())) { + if (equalsBytes(block.hash(), blocks[i + 1].hash())) { i++ } }, @@ -145,14 +146,14 @@ tape('blockchain test', (t) => { await blockchain.setIteratorHead('myHead', headHash) const currentHeadBlock = await blockchain.getIteratorHead('myHead') - st.ok(headHash.equals(currentHeadBlock.hash()), 'head hash equals the provided head hash') + st.deepEquals(headHash, currentHeadBlock.hash(), 'head hash equals the provided head hash') let i = 0 // check that iterator starts from this head block await blockchain.iterator( 'myHead', (block: Block) => { - if (block.hash().equals(blocks[headBlockIndex + 1].hash())) { + if (equalsBytes(block.hash(), blocks[headBlockIndex + 1].hash())) { i++ } }, @@ -196,10 +197,11 @@ tape('blockchain test', (t) => { const [db, genesis] = await createTestDB() const blockchain = await Blockchain.create({ db, genesisBlock: genesis }) const head = await blockchain.getIteratorHead() + if (typeof genesis !== 'undefined') { - st.ok(head.hash().equals(genesis.hash()), 'should get head') + st.deepEquals(head.hash(), genesis.hash(), 'should get head') st.equal( - (blockchain as any)._heads['head0'].toString('hex'), + bytesToHex((blockchain as any)._heads['head0']), 'abcd', 'should get state root heads' ) diff --git a/packages/blockchain/test/pos.spec.ts b/packages/blockchain/test/pos.spec.ts index 9c3daa75dbb..af4581efe97 100644 --- a/packages/blockchain/test/pos.spec.ts +++ b/packages/blockchain/test/pos.spec.ts @@ -1,5 +1,6 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src' @@ -64,7 +65,7 @@ tape('Proof of Stake - inserting blocks into blockchain', async (t) => { }) const genesisHeader = await blockchain.getCanonicalHeadHeader() t.equal( - genesisHeader.hash().toString('hex'), + bytesToHex(genesisHeader.hash()), '1119dc5ff680bf7b4c3d9cd41168334dee127d46b3626482076025cdd498ed0b', 'genesis hash matches' ) diff --git a/packages/blockchain/test/reorg.spec.ts b/packages/blockchain/test/reorg.spec.ts index fdc642c702d..3b141c9c0f8 100644 --- a/packages/blockchain/test/reorg.spec.ts +++ b/packages/blockchain/test/reorg.spec.ts @@ -1,6 +1,7 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src' @@ -71,7 +72,7 @@ tape('reorg tests', (t) => { async (st) => { const common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Chainstart }) const genesisBlock = Block.fromBlockData( - { header: { extraData: Buffer.alloc(97) } }, + { header: { extraData: new Uint8Array(97) } }, { common } ) const blockchain = await Blockchain.create({ @@ -81,16 +82,15 @@ tape('reorg tests', (t) => { genesisBlock, }) - const extraData = Buffer.from( - '506172697479205465636820417574686f7269747900000000000000000000002bbf886181970654ed46e3fae0ded41ee53fec702c47431988a7ae80e6576f3552684f069af80ba11d36327aaf846d470526e4a1c461601b2fd4ebdcdc2b734a01', - 'hex' + const extraData = hexToBytes( + '506172697479205465636820417574686f7269747900000000000000000000002bbf886181970654ed46e3fae0ded41ee53fec702c47431988a7ae80e6576f3552684f069af80ba11d36327aaf846d470526e4a1c461601b2fd4ebdcdc2b734a01' ) // from goerli block 1 const { gasLimit } = genesisBlock.header const base = { extraData, gasLimit, difficulty: 1 } const nonce = CLIQUE_NONCE_AUTH - const beneficiary1 = new Address(Buffer.alloc(20).fill(1)) - const beneficiary2 = new Address(Buffer.alloc(20).fill(2)) + const beneficiary1 = new Address(new Uint8Array(20).fill(1)) + const beneficiary2 = new Address(new Uint8Array(20).fill(2)) const block1_low = Block.fromBlockData( { @@ -158,6 +158,7 @@ tape('reorg tests', (t) => { await blockchain.putBlocks([block1_high, block2_high, block3_high]) let signerStates = (blockchain.consensus as CliqueConsensus)._cliqueLatestSignerStates + t.ok( !signerStates.find( (s: any) => s[0] === BigInt(2) && s[1].find((a: Address) => a.equals(beneficiary1)) @@ -172,7 +173,7 @@ tape('reorg tests', (t) => { v[0] === BigInt(2) && v[1][0].equals(block1_low.header.cliqueSigner()) && v[1][1].equals(beneficiary1) && - v[1][2].equals(CLIQUE_NONCE_AUTH) + equalsBytes(v[1][2], CLIQUE_NONCE_AUTH) ), 'should not find reorged clique vote' ) diff --git a/packages/blockchain/test/util.ts b/packages/blockchain/test/util.ts index b8d7286c80a..3c66a11a771 100644 --- a/packages/blockchain/test/util.ts +++ b/packages/blockchain/test/util.ts @@ -1,8 +1,9 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr, toBuffer } from '@ethereumjs/util' +import { toBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import { Blockchain } from '../src' @@ -110,7 +111,7 @@ export const isConsecutive = (blocks: Block[]) => { } const { parentHash } = block.header const lastBlockHash = blocks[index - 1].hash() - return !parentHash.equals(lastBlockHash) + return !equalsBytes(parentHash, lastBlockHash) }) } @@ -121,67 +122,64 @@ export const createTestDB = async (): Promise<[Level, Block]> => { await db.batch([ { type: 'put', - key: Buffer.from('6800000000000000006e', 'hex'), - keyEncoding: 'buffer', - valueEncoding: 'buffer', + key: hexToBytes('6800000000000000006e'), + keyEncoding: 'view', + valueEncoding: 'view', value: genesis.hash(), }, { type: 'put', - key: Buffer.from('48d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', 'hex'), - keyEncoding: 'buffer', - valueEncoding: 'buffer', - value: Buffer.from('00', 'hex'), + key: hexToBytes('48d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'), + keyEncoding: 'view', + valueEncoding: 'view', + value: hexToBytes('00'), }, { type: 'put', key: 'LastHeader', - keyEncoding: 'buffer', - valueEncoding: 'buffer', + keyEncoding: 'view', + valueEncoding: 'view', value: genesis.hash(), }, { type: 'put', key: 'LastBlock', - keyEncoding: 'buffer', - valueEncoding: 'buffer', + keyEncoding: 'view', + valueEncoding: 'view', value: genesis.hash(), }, { type: 'put', - key: Buffer.from( - '680000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' + key: hexToBytes( + '680000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ), - keyEncoding: 'buffer', - valueEncoding: 'buffer', + keyEncoding: 'view', + valueEncoding: 'view', value: genesis.header.serialize(), }, { type: 'put', - key: Buffer.from( - '680000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa374', - 'hex' + key: hexToBytes( + '680000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa374' ), - keyEncoding: 'buffer', - valueEncoding: 'buffer', - value: Buffer.from(RLP.encode(Uint8Array.from(toBuffer(17179869184)))), + keyEncoding: 'view', + valueEncoding: 'view', + value: RLP.encode(toBytes(17179869184)), }, { type: 'put', - key: Buffer.from( - '620000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' + key: hexToBytes( + '620000000000000000d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ), - keyEncoding: 'buffer', - valueEncoding: 'buffer', - value: Buffer.from(RLP.encode(bufArrToArr(genesis.raw()).slice(1))), + keyEncoding: 'view', + valueEncoding: 'view', + value: RLP.encode(genesis.raw().slice(1)), }, { type: 'put', key: 'heads', valueEncoding: 'json', - value: { head0: { type: 'Buffer', data: [171, 205] } }, + value: { head0: [171, 205] }, }, ]) return [db as any, genesis] @@ -209,7 +207,7 @@ function createBlock( const number = parentBlock.header.number + BigInt(1) const timestamp = parentBlock.header.timestamp + BigInt(1) - const uncleHash = keccak256(RLP.encode(bufArrToArr(uncles.map((uh) => uh.raw())))) + const uncleHash = keccak256(RLP.encode(uncles.map((uh) => uh.raw()))) const londonHfBlock = common.hardforkBlock(Hardfork.London) const baseFeePerGas = @@ -224,7 +222,7 @@ function createBlock( parentHash: parentBlock.hash(), timestamp, gasLimit: parentBlock.header.gasLimit, - extraData: Buffer.from(extraData), + extraData: utf8ToBytes(extraData), uncleHash, baseFeePerGas, }, diff --git a/packages/blockchain/test/utils.spec.ts b/packages/blockchain/test/utils.spec.ts index 364df4cc8c4..47a9f859380 100644 --- a/packages/blockchain/test/utils.spec.ts +++ b/packages/blockchain/test/utils.spec.ts @@ -1,4 +1,5 @@ import { Common } from '@ethereumjs/common' +import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Blockchain } from '../src/blockchain' @@ -22,7 +23,7 @@ tape('[Utils/Parse]', (t) => { const genesisState = parseGethGenesisState(json) const stateRoot = await genesisStateRoot(genesisState) t.equal( - stateRoot.toString('hex'), + bytesToHex(stateRoot), '52e628c7f35996ba5a0402d02b34535993c89ff7fc4c430b2763ada8554bee62', 'kiln stateRoot matches' ) @@ -35,7 +36,7 @@ tape('[Utils/Parse]', (t) => { const genesisHash = blockchain.genesisBlock.hash() t.equal( - genesisHash.toString('hex'), + bytesToHex(genesisHash), '51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8', 'kiln genesis hash matches' ) diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index 55d20260285..54ba7087959 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -5,9 +5,16 @@ import { Blockchain, parseGethGenesisState } from '@ethereumjs/blockchain' import { Chain, Common, ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { initKZG } from '@ethereumjs/tx' -import { Address, arrToBufArr, short, toBuffer } from '@ethereumjs/util' +import { + Address, + bytesToHex, + bytesToPrefixedHexString, + hexStringToBytes, + randomBytes, + short, + toBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import { existsSync, writeFileSync } from 'fs' import { ensureDirSync, readFileSync, removeSync } from 'fs-extra' import { Level } from 'level' @@ -27,14 +34,14 @@ import type { Logger } from '../lib/logging' import type { FullEthereumService } from '../lib/service' import type { ClientOpts } from '../lib/types' import type { RPCArgs } from './startRpc' -import type { BlockBuffer } from '@ethereumjs/block' +import type { BlockBytes } from '@ethereumjs/block' import type { GenesisState } from '@ethereumjs/blockchain/dist/genesisStates' import type { AbstractLevel } from 'abstract-level' const { hideBin } = require('yargs/helpers') const yargs = require('yargs/yargs') -type Account = [address: Address, privateKey: Buffer] +type Account = [address: Address, privateKey: Uint8Array] const networks = Object.entries(Common._getInitializedChains().names) @@ -311,24 +318,24 @@ const args: ClientOpts = yargs(hideBin(process.argv)) * Initializes and returns the databases needed for the client */ function initDBs(config: Config): { - chainDB: AbstractLevel - stateDB: AbstractLevel - metaDB: AbstractLevel + chainDB: AbstractLevel + stateDB: AbstractLevel + metaDB: AbstractLevel } { // Chain DB const chainDataDir = config.getDataDirectory(DataDirectory.Chain) ensureDirSync(chainDataDir) - const chainDB = new Level(chainDataDir) + const chainDB = new Level(chainDataDir) // State DB const stateDataDir = config.getDataDirectory(DataDirectory.State) ensureDirSync(stateDataDir) - const stateDB = new Level(stateDataDir) + const stateDB = new Level(stateDataDir) // Meta DB (receipts, logs, indexes, skeleton chain) const metaDataDir = config.getDataDirectory(DataDirectory.Meta) ensureDirSync(metaDataDir) - const metaDB = new Level(metaDataDir) + const metaDB = new Level(metaDataDir) return { chainDB, stateDB, metaDB } } @@ -447,14 +454,14 @@ async function startClient(config: Config, customGenesisState?: GenesisState) { let buf = RLP.decode(blockRlp, true) while (buf.data?.length > 0 || buf.remainder?.length > 0) { try { - const block = Block.fromValuesArray(arrToBufArr(buf.data) as unknown as BlockBuffer, { + const block = Block.fromValuesArray(buf.data as BlockBytes, { common: config.chainCommon, hardforkByBlockNumber: true, }) blocks.push(block) buf = RLP.decode(buf.remainder, true) config.logger.info( - `Preloading block hash=0x${short(block.header.hash().toString('hex'))} number=${ + `Preloading block hash=0x${short(bytesToHex(block.header.hash()))} number=${ block.header.number }` ) @@ -574,7 +581,7 @@ async function inputAccounts() { `Please enter the 0x-prefixed private key to unlock ${address}:\n` ) ;(rl as any).history = (rl as any).history.slice(1) - const privKey = toBuffer(inputKey) + const privKey = toBytes(inputKey) const derivedAddress = Address.fromPrivateKey(privKey) if (address.equals(derivedAddress)) { accounts.push([address, privKey]) @@ -587,7 +594,7 @@ async function inputAccounts() { } } else { const acc = readFileSync(path.resolve(args.unlock!), 'utf-8') - const privKey = Buffer.from(acc, 'hex') + const privKey = hexStringToBytes(acc) const derivedAddress = Address.fromPrivateKey(privKey) accounts.push([derivedAddress, privKey]) } @@ -608,7 +615,7 @@ function generateAccount(): Account { console.log('='.repeat(50)) console.log('Account generated for mining blocks:') console.log(`Address: ${address}`) - console.log(`Private key: 0x${privKey.toString('hex')}`) + console.log(`Private key: ${bytesToPrefixedHexString(privKey)}`) console.log('WARNING: Do not use this account for mainnet funds') console.log('='.repeat(50)) return [address, privKey] @@ -746,7 +753,7 @@ async function run() { config.logger.error(`Error writing listener details to disk: ${(e as Error).message}`) } }) - if (customGenesisState) { + if (customGenesisState !== undefined) { const numAccounts = Object.keys(customGenesisState).length config.logger.info(`Reading custom genesis state accounts=${numAccounts}`) } diff --git a/packages/client/bin/startRpc.ts b/packages/client/bin/startRpc.ts index fa6bab0f80c..2f6ba9c77f6 100644 --- a/packages/client/bin/startRpc.ts +++ b/packages/client/bin/startRpc.ts @@ -1,3 +1,5 @@ +import { hexStringToBytes, randomBytes } from '@ethereumjs/util' +import { bytesToHex } from 'ethereum-cryptography/utils' import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs' import { RPCManager, saveReceiptsMethods } from '../lib/rpc' @@ -35,8 +37,8 @@ export type RPCArgs = { /** * Returns a jwt secret from a provided file path, otherwise saves a randomly generated one to datadir if none already exists */ -function parseJwtSecret(config: Config, jwtFilePath?: string): Buffer { - let jwtSecret: Buffer +function parseJwtSecret(config: Config, jwtFilePath?: string): Uint8Array { + let jwtSecret: Uint8Array const defaultJwtPath = `${config.datadir}/jwtsecret` const usedJwtPath = jwtFilePath !== undefined ? jwtFilePath : defaultJwtPath @@ -52,15 +54,15 @@ function parseJwtSecret(config: Config, jwtFilePath?: string): Buffer { if (jwtSecretHex === undefined || jwtSecretHex.length !== 64) { throw Error('Need a valid 256 bit hex encoded secret') } - jwtSecret = Buffer.from(jwtSecretHex, 'hex') + jwtSecret = hexStringToBytes(jwtSecretHex) } else { const folderExists = existsSync(config.datadir) if (!folderExists) { mkdirSync(config.datadir, { recursive: true }) } - jwtSecret = Buffer.from(Array.from({ length: 32 }, () => Math.round(Math.random() * 255))) - writeFileSync(defaultJwtPath, jwtSecret.toString('hex'), {}) + jwtSecret = randomBytes(32) + writeFileSync(defaultJwtPath, bytesToHex(jwtSecret), {}) config.logger.info(`New Engine API JWT token created path=${defaultJwtPath}`) } config.logger.info(`Using Engine API with JWT token authentication path=${usedJwtPath}`) @@ -93,7 +95,7 @@ export function startRPCServers(client: EthereumClient, args: RPCArgs) { const manager = new RPCManager(client, config) const { logger } = config const jwtSecret = - rpcEngine && rpcEngineAuth ? parseJwtSecret(config, jwtSecretPath) : Buffer.from([]) + rpcEngine && rpcEngineAuth ? parseJwtSecret(config, jwtSecretPath) : new Uint8Array(0) let withEngineMethods = false if ((rpc || rpcEngine) && !config.saveReceipts) { diff --git a/packages/client/browser/index.ts b/packages/client/browser/index.ts index 1b218ebac70..71744f2191c 100644 --- a/packages/client/browser/index.ts +++ b/packages/client/browser/index.ts @@ -70,7 +70,9 @@ export async function createClient(args: any) { discDns: false, }) config.events.setMaxListeners(50) - const chainDB = new Level(`${datadir}/${common.chainName()}`) + const chainDB = new Level( + `${datadir}/${common.chainName()}` + ) const blockchain = await Blockchain.create({ db: chainDB, diff --git a/packages/client/browser/util/index.ts b/packages/client/browser/util/index.ts index dac3e915d1a..3a28e37796e 100644 --- a/packages/client/browser/util/index.ts +++ b/packages/client/browser/util/index.ts @@ -1,18 +1,19 @@ /** * @module util */ +import { bytesToPrefixedHexString } from '@ethereumjs/util' import { platform } from 'os' import { version as packageVersion } from '../../package.json' export * from '../../lib/util/parse' -export function short(buf: Buffer | string): string { - if (buf === null || buf === undefined || buf === '') return '' - const bufStr = Buffer.isBuffer(buf) ? `0x${buf.toString('hex')}` : buf - let str = bufStr.substring(0, 6) + '…' - if (bufStr.length === 66) { - str += bufStr.substring(62) +export function short(bytes: Uint8Array | string): string { + if (bytes === null || bytes === undefined || bytes === '') return '' + const bytesString = bytes instanceof Uint8Array ? bytesToPrefixedHexString(bytes) : bytes + let str = bytesString.substring(0, 6) + '…' + if (bytesString.length === 66) { + str += bytesString.substring(62) } return str } diff --git a/packages/client/devnets/4844-interop/tools/txGenerator.ts b/packages/client/devnets/4844-interop/tools/txGenerator.ts index d92da6c03f5..16dd356693d 100644 --- a/packages/client/devnets/4844-interop/tools/txGenerator.ts +++ b/packages/client/devnets/4844-interop/tools/txGenerator.ts @@ -6,16 +6,16 @@ import { commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' -import { Address } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' +import { randomBytes } from '@ethereumjs/util' import { Client } from 'jayson/promise' // CLI Args const clientPort = parseInt(process.argv[2]) // EL client port number const input = process.argv[3] // text to generate blob from const genesisJson = require(process.argv[4]) // Genesis parameters -const pkey = Buffer.from(process.argv[5], 'hex') // private key of tx sender as unprefixed hex string +const pkey = hexStringToBytes(process.argv[5]) // private key of tx sender as unprefixed hex string initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') @@ -65,7 +65,7 @@ async function run(data: any) { const res = await client.request( 'eth_sendRawTransaction', - ['0x' + serializedWrapper.toString('hex')], + [bytesToPrefixedHexString(serializedWrapper)], 2.0 ) diff --git a/packages/client/lib/blockchain/chain.ts b/packages/client/lib/blockchain/chain.ts index a5fbe9388b9..134293ea873 100644 --- a/packages/client/lib/blockchain/chain.ts +++ b/packages/client/lib/blockchain/chain.ts @@ -1,6 +1,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' +import { equalsBytes } from 'ethereum-cryptography/utils' import { Event } from '../types' @@ -19,7 +20,7 @@ export interface ChainOptions { /** * Database to store blocks and metadata. Should be an abstract-leveldown compliant store. */ - chainDB?: AbstractLevel + chainDB?: AbstractLevel /** * Specify a blockchain which implements the Chain interface @@ -97,7 +98,7 @@ export interface ChainHeaders { */ export class Chain { public config: Config - public chainDB: AbstractLevel + public chainDB: AbstractLevel public blockchain: Blockchain public opened: boolean @@ -338,7 +339,12 @@ export class Chain { * @param reverse get blocks in reverse * @returns an array of the blocks */ - async getBlocks(block: Buffer | bigint, max = 1, skip = 0, reverse = false): Promise { + async getBlocks( + block: Uint8Array | bigint, + max = 1, + skip = 0, + reverse = false + ): Promise { if (!this.opened) throw new Error('Chain closed') return this.blockchain.getBlocks(block, max, skip, reverse) } @@ -348,7 +354,7 @@ export class Chain { * @param block block hash or number * @throws if block is not found */ - async getBlock(block: Buffer | bigint): Promise { + async getBlock(block: Uint8Array | bigint): Promise { if (!this.opened) throw new Error('Chain closed') return this.blockchain.getBlock(block) } @@ -369,7 +375,7 @@ export class Chain { for (const block of blocks) { if (this.headers.finalized !== null && block.header.number <= this.headers.finalized.number) { const canonicalBlock = await this.getBlock(block.header.number) - if (!canonicalBlock.hash().equals(block.hash())) { + if (!equalsBytes(canonicalBlock.hash(), block.hash())) { throw Error( `Invalid putBlock for block=${block.header.number} before finalized=${this.headers.finalized.number}` ) @@ -416,7 +422,7 @@ export class Chain { * @returns list of block headers */ async getHeaders( - block: Buffer | bigint, + block: Uint8Array | bigint, max: number, skip: number, reverse: boolean @@ -494,7 +500,7 @@ export class Chain { * @param num the block number * @returns the td */ - async getTd(hash: Buffer, num: bigint): Promise { + async getTd(hash: Uint8Array, num: bigint): Promise { if (!this.opened) throw new Error('Chain closed') return this.blockchain.getTotalDifficulty(hash, num) } diff --git a/packages/client/lib/client.ts b/packages/client/lib/client.ts index d114c88bff6..817da522318 100644 --- a/packages/client/lib/client.ts +++ b/packages/client/lib/client.ts @@ -23,7 +23,7 @@ export interface EthereumClientOptions { * * Default: Database created by the Blockchain class */ - chainDB?: AbstractLevel + chainDB?: AbstractLevel /** * Database to store the state. @@ -31,7 +31,7 @@ export interface EthereumClientOptions { * * Default: Database created by the Trie class */ - stateDB?: AbstractLevel + stateDB?: AbstractLevel /** * Database to store tx receipts, logs, and indexes. @@ -39,7 +39,7 @@ export interface EthereumClientOptions { * * Default: Database created in datadir folder */ - metaDB?: AbstractLevel + metaDB?: AbstractLevel /* List of bootnodes to use for discovery */ bootnodes?: MultiaddrLike[] diff --git a/packages/client/lib/config.ts b/packages/client/lib/config.ts index a1485804a10..571f64667f8 100644 --- a/packages/client/lib/config.ts +++ b/packages/client/lib/config.ts @@ -83,7 +83,7 @@ export interface ConfigOptions { * Use return value of {@link Config.getClientKey}. * If left blank, a random key will be generated and used. */ - key?: Buffer + key?: Uint8Array /** * Network transports ('rlpx' and/or 'libp2p') @@ -236,7 +236,7 @@ export interface ConfigOptions { * * Default: [] */ - accounts?: [address: Address, privKey: Buffer][] + accounts?: [address: Address, privKey: Uint8Array][] /** * Address for mining rewards (etherbase) @@ -312,7 +312,7 @@ export class Config { public readonly vm?: VM public readonly lightserv: boolean public readonly datadir: string - public readonly key: Buffer + public readonly key: Uint8Array public readonly transports: string[] public readonly bootnodes?: Multiaddr[] public readonly port?: number @@ -332,7 +332,7 @@ export class Config { public readonly discV4: boolean public readonly mine: boolean public readonly isSingleNode: boolean - public readonly accounts: [address: Address, privKey: Buffer][] + public readonly accounts: [address: Address, privKey: Uint8Array][] public readonly minerCoinbase?: Address public readonly safeReorgDistance: number @@ -453,11 +453,14 @@ export class Config { return } - if (latest) { + if (latest !== null && latest !== undefined) { const height = latest.number if (height >= (this.syncTargetHeight ?? BigInt(0))) { this.syncTargetHeight = height - this.lastSyncDate = latest.timestamp ? Number(latest.timestamp) * 1000 : Date.now() + this.lastSyncDate = + typeof latest.timestamp === 'bigint' && latest.timestamp > 0n + ? Number(latest.timestamp) * 1000 + : Date.now() const diff = Date.now() - this.lastSyncDate // update synchronized @@ -491,7 +494,7 @@ export class Config { this.logger.debug( `Client synchronized=${this.synchronized}${ - latest ? ' height=' + latest.number : '' + latest !== null && latest !== undefined ? ' height=' + latest.number : '' } syncTargetHeight=${this.syncTargetHeight} lastSyncDate=${ (Date.now() - this.lastSyncDate) / 1000 } secs ago` @@ -527,7 +530,7 @@ export class Config { * Returns the config level db. */ static getConfigDB(networkDir: string) { - return new Level(`${networkDir}/config` as any) + return new Level(`${networkDir}/config` as any) } /** diff --git a/packages/client/lib/execution/execution.ts b/packages/client/lib/execution/execution.ts index 7045782a0fd..c0c43c1dddb 100644 --- a/packages/client/lib/execution/execution.ts +++ b/packages/client/lib/execution/execution.ts @@ -7,10 +7,10 @@ export interface ExecutionOptions { config: Config /* State database */ - stateDB?: AbstractLevel + stateDB?: AbstractLevel /* Meta database (receipts, logs, indexes) */ - metaDB?: AbstractLevel + metaDB?: AbstractLevel /** Chain */ chain: Chain @@ -19,8 +19,8 @@ export interface ExecutionOptions { export abstract class Execution { public config: Config - protected stateDB?: AbstractLevel - protected metaDB?: AbstractLevel + protected stateDB?: AbstractLevel + protected metaDB?: AbstractLevel protected chain: Chain public running: boolean = false diff --git a/packages/client/lib/execution/level.ts b/packages/client/lib/execution/level.ts index a336f4513e9..08398f739d5 100644 --- a/packages/client/lib/execution/level.ts +++ b/packages/client/lib/execution/level.ts @@ -10,7 +10,7 @@ export const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } * which validates inputs and sets encoding type. */ export class LevelDB implements DB { - _leveldb: AbstractLevel + _leveldb: AbstractLevel /** * Initialize a DB instance. If `leveldb` is not provided, DB @@ -18,7 +18,7 @@ export class LevelDB implements DB { * @param leveldb - An abstract-leveldown compliant store */ constructor( - leveldb?: AbstractLevel + leveldb?: AbstractLevel ) { this._leveldb = leveldb ?? new MemoryLevel(ENCODING_OPTS) } @@ -26,7 +26,7 @@ export class LevelDB implements DB { /** * @inheritDoc */ - async get(key: Buffer): Promise { + async get(key: Uint8Array): Promise { let value = null try { value = await this._leveldb.get(key, ENCODING_OPTS) @@ -38,20 +38,20 @@ export class LevelDB implements DB { throw error } } - return value as Buffer | null + return value as Uint8Array | null } /** * @inheritDoc */ - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { await this._leveldb.put(key, val, ENCODING_OPTS) } /** * @inheritDoc */ - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._leveldb.del(key, ENCODING_OPTS) } diff --git a/packages/client/lib/execution/receipt.ts b/packages/client/lib/execution/receipt.ts index c5fb45b503b..583f9568b1b 100644 --- a/packages/client/lib/execution/receipt.ts +++ b/packages/client/lib/execution/receipt.ts @@ -1,11 +1,11 @@ import { RLP } from '@ethereumjs/rlp' import { - arrToBufArr, - bigIntToBuffer, - bufArrToArr, - bufferToBigInt, - bufferToInt, - intToBuffer, + bigIntToBytes, + bytesToBigInt, + bytesToInt, + equalsBytes, + intToBytes, + utf8ToBytes, } from '@ethereumjs/util' import { Bloom } from '@ethereumjs/vm' @@ -35,7 +35,7 @@ interface PostByzantiumTxReceiptWithType extends PostByzantiumTxReceipt { */ type GetReceiptByTxHashReturn = [ receipt: TxReceipt, - blockHash: Buffer, + blockHash: Uint8Array, txIndex: number, logIndex: number ] @@ -50,7 +50,7 @@ type GetLogsReturn = { /** * Indexes */ -type TxHashIndex = [blockHash: Buffer, txIndex: number] +type TxHashIndex = [blockHash: Uint8Array, txIndex: number] enum IndexType { TxHash, @@ -64,8 +64,8 @@ enum IndexOperation { * Storage encodings */ type rlpLog = Log -type rlpReceipt = [postStateOrStatus: Buffer, cumulativeGasUsed: Buffer, logs: rlpLog[]] -type rlpTxHash = [blockHash: Buffer, txIndex: Buffer] +type rlpReceipt = [postStateOrStatus: Uint8Array, cumulativeGasUsed: Uint8Array, logs: rlpLog[]] +type rlpTxHash = [blockHash: Uint8Array, txIndex: Uint8Array] enum RlpConvert { Encode, @@ -113,17 +113,17 @@ export class ReceiptsManager extends MetaDBManager { * @param includeTxType whether to include the tx type for each receipt (default: false) */ async getReceipts( - blockHash: Buffer, + blockHash: Uint8Array, calcBloom?: boolean, includeTxType?: true ): Promise async getReceipts( - blockHash: Buffer, + blockHash: Uint8Array, calcBloom?: boolean, includeTxType?: false ): Promise async getReceipts( - blockHash: Buffer, + blockHash: Uint8Array, calcBloom = false, includeTxType = false ): Promise { @@ -150,7 +150,7 @@ export class ReceiptsManager extends MetaDBManager { * Returns receipt by tx hash with additional metadata for the JSON RPC response, or null if not found * @param txHash the tx hash */ - async getReceiptByTxHash(txHash: Buffer): Promise { + async getReceiptByTxHash(txHash: Uint8Array): Promise { const txHashIndex = await this.getIndex(IndexType.TxHash, txHash) if (!txHashIndex) return null const [blockHash, txIndex] = txHashIndex @@ -169,8 +169,8 @@ export class ReceiptsManager extends MetaDBManager { async getLogs( from: Block, to: Block, - addresses?: Buffer[], - topics: (Buffer | Buffer[] | null)[] = [] + addresses?: Uint8Array[], + topics: (Uint8Array | Uint8Array[] | null)[] = [] ): Promise { const returnedLogs: GetLogsReturn = [] let returnedLogsSize = 0 @@ -192,7 +192,7 @@ export class ReceiptsManager extends MetaDBManager { ) } if (addresses && addresses.length > 0) { - logs = logs.filter((l) => addresses.some((a) => a.equals(l.log[0]))) + logs = logs.filter((l) => addresses.some((a) => equalsBytes(a, l.log[0]))) } if (topics.length > 0) { // From https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newfilter/: @@ -207,19 +207,19 @@ export class ReceiptsManager extends MetaDBManager { for (const [i, topic] of topics.entries()) { if (Array.isArray(topic)) { // Can match any items in this array - if (!topic.find((t) => t.equals(l.log[1][i]))) return false + if (!topic.find((t) => equalsBytes(t, l.log[1][i]))) return false } else if (!topic) { // If null then can match any } else { // If a value is specified then it must match - if (!topic.equals(l.log[1][i])) return false + if (!equalsBytes(topic, l.log[1][i])) return false } return true } }) } returnedLogs.push(...logs) - returnedLogsSize += Buffer.byteLength(JSON.stringify(logs)) + returnedLogsSize += utf8ToBytes(JSON.stringify(logs)).byteLength if ( returnedLogs.length >= this.GET_LOGS_LIMIT || returnedLogsSize >= this.GET_LOGS_LIMIT_MEGABYTES * 1048576 @@ -280,8 +280,8 @@ export class ReceiptsManager extends MetaDBManager { * @param type the {@link IndexType} * @param value for {@link IndexType.TxHash}, the txHash to get */ - private async getIndex(type: IndexType.TxHash, value: Buffer): Promise - private async getIndex(type: IndexType, value: Buffer): Promise { + private async getIndex(type: IndexType.TxHash, value: Uint8Array): Promise + private async getIndex(type: IndexType, value: Uint8Array): Promise { switch (type) { case IndexType.TxHash: { const encoded = await this.get(DBKey.TxHash, value) @@ -299,29 +299,33 @@ export class ReceiptsManager extends MetaDBManager { * @param type one of {@link RlpType} * @param value the value to encode or decode */ - private rlp(conversion: RlpConvert.Encode, type: RlpType, value: rlpOut): Buffer - private rlp(conversion: RlpConvert.Decode, type: RlpType.Receipts, values: Buffer): TxReceipt[] + private rlp(conversion: RlpConvert.Encode, type: RlpType, value: rlpOut): Uint8Array + private rlp( + conversion: RlpConvert.Decode, + type: RlpType.Receipts, + values: Uint8Array + ): TxReceipt[] private rlp(conversion: RlpConvert.Decode, type: RlpType.Logs, value: rlpLog[]): Log[] - private rlp(conversion: RlpConvert.Decode, type: RlpType.TxHash, value: Buffer): TxHashIndex - private rlp(conversion: RlpConvert, type: RlpType, value: Buffer | rlpOut): Buffer | rlpOut { + private rlp(conversion: RlpConvert.Decode, type: RlpType.TxHash, value: Uint8Array): TxHashIndex + private rlp( + conversion: RlpConvert, + type: RlpType, + value: Uint8Array | rlpOut + ): Uint8Array | rlpOut { switch (type) { case RlpType.Receipts: if (conversion === RlpConvert.Encode) { value = value as TxReceipt[] - return Buffer.from( - RLP.encode( - bufArrToArr( - value.map((r) => [ - (r as PreByzantiumTxReceipt).stateRoot ?? - intToBuffer((r as PostByzantiumTxReceipt).status), - bigIntToBuffer(r.cumulativeBlockGasUsed), - this.rlp(RlpConvert.Encode, RlpType.Logs, r.logs), - ]) - ) - ) + return RLP.encode( + value.map((r) => [ + (r as PreByzantiumTxReceipt).stateRoot ?? + intToBytes((r as PostByzantiumTxReceipt).status), + bigIntToBytes(r.cumulativeBlockGasUsed), + this.rlp(RlpConvert.Encode, RlpType.Logs, r.logs), + ]) ) } else { - const decoded = arrToBufArr(RLP.decode(Uint8Array.from(value as Buffer))) as rlpReceipt[] + const decoded = RLP.decode(value as Uint8Array) as unknown as rlpReceipt[] return decoded.map((r) => { const gasUsed = r[1] const logs = this.rlp(RlpConvert.Decode, RlpType.Logs, r[2]) @@ -329,14 +333,14 @@ export class ReceiptsManager extends MetaDBManager { // Pre-Byzantium Receipt return { stateRoot: r[0], - cumulativeBlockGasUsed: bufferToBigInt(gasUsed), + cumulativeBlockGasUsed: bytesToBigInt(gasUsed), logs, } as PreByzantiumTxReceipt } else { // Post-Byzantium Receipt return { - status: bufferToInt(r[0]), - cumulativeBlockGasUsed: bufferToBigInt(gasUsed), + status: bytesToInt(r[0]), + cumulativeBlockGasUsed: bytesToBigInt(gasUsed), logs, } as PostByzantiumTxReceipt } @@ -344,19 +348,17 @@ export class ReceiptsManager extends MetaDBManager { } case RlpType.Logs: if (conversion === RlpConvert.Encode) { - return Buffer.from(RLP.encode(bufArrToArr(value as Log[]))) + return RLP.encode(value as Log[]) } else { - return arrToBufArr(RLP.decode(Uint8Array.from(value as Buffer))) as Log[] + return RLP.decode(value as Uint8Array) as Log[] } case RlpType.TxHash: if (conversion === RlpConvert.Encode) { const [blockHash, txIndex] = value as TxHashIndex - return Buffer.from(RLP.encode(bufArrToArr([blockHash, intToBuffer(txIndex)]))) + return RLP.encode([blockHash, intToBytes(txIndex)]) } else { - const [blockHash, txIndex] = arrToBufArr( - RLP.decode(Uint8Array.from(value as Buffer)) - ) as rlpTxHash - return [blockHash, bufferToInt(txIndex)] as TxHashIndex + const [blockHash, txIndex] = RLP.decode(value as Uint8Array) as unknown as rlpTxHash + return [blockHash, bytesToInt(txIndex)] as TxHashIndex } default: throw new Error('Unknown rlp conversion') diff --git a/packages/client/lib/execution/vmexecution.ts b/packages/client/lib/execution/vmexecution.ts index 40bc65305dd..45114c38f00 100644 --- a/packages/client/lib/execution/vmexecution.ts +++ b/packages/client/lib/execution/vmexecution.ts @@ -7,7 +7,7 @@ import { import { ConsensusType, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' -import { Lock, bufferToHex } from '@ethereumjs/util' +import { Lock, bytesToHex, bytesToPrefixedHexString, equalsBytes } from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' import { Event } from '../types' @@ -138,7 +138,7 @@ export class VMExecution extends Execution { } if (receipts !== undefined) { // Save receipts - this.pendingReceipts?.set(block.hash().toString('hex'), receipts) + this.pendingReceipts?.set(bytesToHex(block.hash()), receipts) } // Bypass updating head by using blockchain db directly const [hash, num] = [block.hash(), block.header.number] @@ -202,10 +202,10 @@ export class VMExecution extends Execution { // skip emitting the chain update event as we will manually do it await this.chain.putBlocks(blocks, true, true) for (const block of blocks) { - const receipts = this.pendingReceipts?.get(block.hash().toString('hex')) + const receipts = this.pendingReceipts?.get(bytesToHex(block.hash())) if (receipts) { void this.receiptsManager?.saveReceipts(block, receipts) - this.pendingReceipts?.delete(block.hash().toString('hex')) + this.pendingReceipts?.delete(bytesToHex(block.hash())) } } @@ -215,7 +215,7 @@ export class VMExecution extends Execution { continue } const blockByNumber = await this.chain.getBlock(block.header.number) - if (!blockByNumber.hash().equals(block.hash())) { + if (!equalsBytes(blockByNumber.hash(), block.hash())) { throw Error(`${blockName} not in canonical chain`) } } @@ -255,7 +255,7 @@ export class VMExecution extends Execution { ) let headBlock: Block | undefined - let parentState: Buffer | undefined + let parentState: Uint8Array | undefined let errorBlock: Block | undefined while ( @@ -265,7 +265,7 @@ export class VMExecution extends Execution { canonicalHead.header.number - startHeadBlock.header.number >= BigInt(this.config.numBlocksPerIteration))) && (numExecuted === undefined || (loop && numExecuted === this.config.numBlocksPerIteration)) && - startHeadBlock.hash().equals(canonicalHead.hash()) === false + equalsBytes(startHeadBlock.hash(), canonicalHead.hash()) === false ) { let txCounter = 0 headBlock = undefined @@ -274,10 +274,10 @@ export class VMExecution extends Execution { this.vmPromise = blockchain.iterator( 'vm', async (block: Block, reorg: boolean) => { - if (errorBlock) return + if (errorBlock !== undefined) return // determine starting state for block run // if we are just starting or if a chain reorg has happened - if (!headBlock || reorg) { + if (headBlock === undefined || reorg) { const headBlock = await blockchain.getBlock(block.header.parentHash) parentState = headBlock.header.stateRoot } @@ -326,7 +326,7 @@ export class VMExecution extends Execution { if (diffSec > this.MAX_TOLERATED_BLOCK_TIME) { const msg = `Slow block execution for block num=${ block.header.number - } hash=0x${block.hash().toString('hex')} txs=${block.transactions.length} gasUsed=${ + } hash=0x${bytesToHex(block.hash())} txs=${block.transactions.length} gasUsed=${ result.gasUsed } time=${diffSec}secs` this.config.logger.warn(msg) @@ -496,9 +496,9 @@ export class VMExecution extends Execution { const res = await vm.runBlock({ block, skipHeaderValidation: true }) const afterTS = Date.now() const diffSec = Math.round((afterTS - beforeTS) / 1000) - const msg = `Executed block num=${blockNumber} hash=0x${block.hash().toString('hex')} txs=${ - block.transactions.length - } gasUsed=${res.gasUsed} time=${diffSec}secs` + const msg = `Executed block num=${blockNumber} hash=${bytesToPrefixedHexString( + block.hash() + )} txs=${block.transactions.length} gasUsed=${res.gasUsed} time=${diffSec}secs` if (diffSec <= this.MAX_TOLERATED_BLOCK_TIME) { this.config.logger.info(msg) } else { @@ -510,7 +510,7 @@ export class VMExecution extends Execution { // Useful e.g. to trace slow txs const allTxs = txHashes.length === 1 && txHashes[0] === '*' ? true : false for (const tx of block.transactions) { - const txHash = bufferToHex(tx.hash()) + const txHash = bytesToHex(tx.hash()) if (allTxs || txHashes.includes(txHash)) { const res = await vm.runTx({ block, tx }) this.config.logger.info( diff --git a/packages/client/lib/miner/miner.ts b/packages/client/lib/miner/miner.ts index 24e87381e29..ad4ee936378 100644 --- a/packages/client/lib/miner/miner.ts +++ b/packages/client/lib/miner/miner.ts @@ -1,6 +1,8 @@ import { BlockHeader } from '@ethereumjs/block' import { ConsensusType, Hardfork } from '@ethereumjs/common' import { Ethash } from '@ethereumjs/ethash' +import { bytesToPrefixedHexString } from '@ethereumjs/util' +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import { Event } from '../types' @@ -132,7 +134,7 @@ export class Miner { const header = this.latestBlockHeader() this.ethashMiner = this.ethash.getMiner(header) const solution = await this.ethashMiner.iterate(-1) - if (!header.hash().equals(this.latestBlockHeader().hash())) { + if (!equalsBytes(header.hash(), this.latestBlockHeader().hash())) { // New block was inserted while iterating so we will discard solution return } @@ -319,17 +321,17 @@ export class Miner { // We can here decide to keep a tx in pool if it belongs to future hf // but for simplicity just remove the tx as the sender can always retransmit // the tx - this.service.txPool.removeByHash(txs[index].hash().toString('hex')) + this.service.txPool.removeByHash(bytesToHex(txs[index].hash())) this.config.logger.error( - `Pending: Removed from txPool tx 0x${txs[index] - .hash() - .toString('hex')} having different hf=${txs[ - index - ].common.hardfork()} than block vm hf=${blockBuilder['vm']._common.hardfork()}` + `Pending: Removed from txPool tx ${bytesToPrefixedHexString( + txs[index].hash() + )} having different hf=${txs[index].common.hardfork()} than block vm hf=${blockBuilder[ + 'vm' + ]._common.hardfork()}` ) } else { // If there is an error adding a tx, it will be skipped - const hash = '0x' + txs[index].hash().toString('hex') + const hash = bytesToPrefixedHexString(txs[index].hash()) this.config.logger.debug( `Skipping tx ${hash}, error encountered when trying to add tx:\n${error}` ) diff --git a/packages/client/lib/miner/pendingBlock.ts b/packages/client/lib/miner/pendingBlock.ts index 542477f46e7..802d354ec6d 100644 --- a/packages/client/lib/miner/pendingBlock.ts +++ b/packages/client/lib/miner/pendingBlock.ts @@ -2,9 +2,12 @@ import { BlockHeader } from '@ethereumjs/block' import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { TypeOutput, - bigIntToUnpaddedBuffer, - bufferToHex, - toBuffer, + bigIntToUnpaddedBytes, + bytesToHex, + bytesToPrefixedHexString, + concatBytes, + equalsBytes, + toBytes, toType, zeros, } from '@ethereumjs/util' @@ -32,8 +35,8 @@ interface PendingBlockOpts { interface BlobBundle { blockHash: string - blobs: Buffer[] - kzgCommitments: Buffer[] + blobs: Uint8Array[] + kzgCommitments: Uint8Array[] } /** * In the future this class should build a pending block by keeping the @@ -94,20 +97,20 @@ export class PendingBlock { // payload is uniquely defined by timestamp, parent and mixHash, gasLimit can also be // potentially included in the fcU in future and can be safely added in uniqueness calc - const timestampBuf = bigIntToUnpaddedBuffer(toType(timestamp ?? 0, TypeOutput.BigInt)) - const gasLimitBuf = bigIntToUnpaddedBuffer(gasLimit) - const mixHashBuf = toType(mixHash!, TypeOutput.Buffer) ?? zeros(32) - const payloadIdBuffer = toBuffer( - keccak256(Buffer.concat([parentBlock.hash(), mixHashBuf, timestampBuf, gasLimitBuf])).slice( + const timestampBuf = bigIntToUnpaddedBytes(toType(timestamp ?? 0, TypeOutput.BigInt)) + const gasLimitBuf = bigIntToUnpaddedBytes(gasLimit) + const mixHashBuf = toType(mixHash!, TypeOutput.Uint8Array) ?? zeros(32) + const payloadIdBytes = toBytes( + keccak256(concatBytes(parentBlock.hash(), mixHashBuf, timestampBuf, gasLimitBuf)).subarray( 0, 8 ) ) - const payloadId = bufferToHex(payloadIdBuffer) + const payloadId = bytesToPrefixedHexString(payloadIdBytes) // If payload has already been triggered, then return the payloadid if (this.pendingPayloads.get(payloadId)) { - return payloadIdBuffer + return payloadIdBytes } // Prune the builders and blobbundles @@ -176,9 +179,9 @@ export class PendingBlock { } else { // If there is an error adding a tx, it will be skipped this.config.logger.debug( - `Pending: Skipping tx 0x${txs[index] - .hash() - .toString('hex')}, error encountered when trying to add tx:\n${error}` + `Pending: Skipping tx ${bytesToPrefixedHexString( + txs[index].hash() + )}, error encountered when trying to add tx:\n${error}` ) } } @@ -202,15 +205,15 @@ export class PendingBlock { ) this.constructBlobsBundle(payloadId, blobTxs, header.hash()) } - return payloadIdBuffer + return payloadIdBytes } /** * Stops a pending payload */ - stop(payloadIdBuffer: Buffer | string) { + stop(payloadIdBytes: Uint8Array | string) { const payloadId = - typeof payloadIdBuffer !== 'string' ? bufferToHex(payloadIdBuffer) : payloadIdBuffer + typeof payloadIdBytes !== 'string' ? bytesToPrefixedHexString(payloadIdBytes) : payloadIdBytes const builder = this.pendingPayloads.get(payloadId) if (builder === undefined) return // Revert blockBuilder @@ -224,10 +227,10 @@ export class PendingBlock { * Returns the completed block */ async build( - payloadIdBuffer: Buffer | string + payloadIdBytes: Uint8Array | string ): Promise { const payloadId = - typeof payloadIdBuffer !== 'string' ? bufferToHex(payloadIdBuffer) : payloadIdBuffer + typeof payloadIdBytes !== 'string' ? bytesToPrefixedHexString(payloadIdBytes) : payloadIdBytes const builder = this.pendingPayloads.get(payloadId) if (!builder) { return @@ -241,8 +244,9 @@ export class PendingBlock { // Add new txs that the pool received const txs = (await this.txPool.txsByPriceAndNonce(vm, headerData.baseFeePerGas)).filter( (tx) => - (builder as any).transactions.some((t: TypedTransaction) => t.hash().equals(tx.hash())) === - false + (builder as any).transactions.some((t: TypedTransaction) => + equalsBytes(t.hash(), tx.hash()) + ) === false ) this.config.logger.info(`Pending: Adding ${txs.length} additional eligible txs`) let index = 0 @@ -269,11 +273,11 @@ export class PendingBlock { // We can here decide to keep a tx in pool if it belongs to future hf // but for simplicity just remove the tx as the sender can always retransmit // the tx - this.txPool.removeByHash(txs[index].hash().toString('hex')) + this.txPool.removeByHash(bytesToHex(txs[index].hash())) this.config.logger.error( - `Pending: Removed from txPool tx 0x${txs[index] - .hash() - .toString('hex')} having different hf=${txs[ + `Pending: Removed from txPool tx ${bytesToPrefixedHexString( + txs[index].hash() + )} having different hf=${txs[ index ].common.hardfork()} than block vm hf=${vm._common.hardfork()}` ) @@ -281,9 +285,9 @@ export class PendingBlock { skippedByAddErrors++ // If there is an error adding a tx, it will be skipped this.config.logger.debug( - `Pending: Skipping tx 0x${txs[index] - .hash() - .toString('hex')}, error encountered when trying to add tx:\n${error}` + `Pending: Skipping tx ${bytesToPrefixedHexString( + txs[index].hash() + )}, error encountered when trying to add tx:\n${error}` ) } } @@ -295,9 +299,7 @@ export class PendingBlock { this.config.logger.info( `Pending: Built block number=${block.header.number} txs=${ block.transactions.length - }${withdrawalsStr} skippedByAddErrors=${skippedByAddErrors} hash=${block - .hash() - .toString('hex')}` + }${withdrawalsStr} skippedByAddErrors=${skippedByAddErrors} hash=${bytesToHex(block.hash())}` ) // Construct blobs bundle @@ -317,10 +319,10 @@ export class PendingBlock { private constructBlobsBundle = ( payloadId: string, txs: BlobEIP4844Transaction[], - blockHash: Buffer + blockHash: Uint8Array ) => { - let blobs: Buffer[] = [] - let kzgCommitments: Buffer[] = [] + let blobs: Uint8Array[] = [] + let kzgCommitments: Uint8Array[] = [] const bundle = this.blobBundles.get(payloadId) if (bundle !== undefined) { blobs = bundle.blobs @@ -335,7 +337,7 @@ export class PendingBlock { } } this.blobBundles.set(payloadId, { - blockHash: '0x' + blockHash.toString('hex'), + blockHash: bytesToPrefixedHexString(blockHash), blobs, kzgCommitments, }) diff --git a/packages/client/lib/net/peer/rlpxpeer.ts b/packages/client/lib/net/peer/rlpxpeer.ts index b37f7d24072..014a30255a3 100644 --- a/packages/client/lib/net/peer/rlpxpeer.ts +++ b/packages/client/lib/net/peer/rlpxpeer.ts @@ -4,7 +4,7 @@ import { RLPx as Devp2pRLPx, SNAP as Devp2pSNAP, } from '@ethereumjs/devp2p' -import { randomBytes } from 'crypto' +import { hexStringToBytes, randomBytes } from '@ethereumjs/util' import { Event } from '../../types' import { RlpxSender } from '../protocol' @@ -111,7 +111,7 @@ export class RlpxPeer extends Peer { common: this.config.chainCommon, }) await this.rlpx.connect({ - id: Buffer.from(this.id, 'hex'), + id: hexStringToBytes(this.id), address: this.host, tcpPort: this.port, }) diff --git a/packages/client/lib/net/protocol/ethprotocol.ts b/packages/client/lib/net/protocol/ethprotocol.ts index dc9b2463dac..1a63af33d98 100644 --- a/packages/client/lib/net/protocol/ethprotocol.ts +++ b/packages/client/lib/net/protocol/ethprotocol.ts @@ -3,12 +3,10 @@ import { Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { BlobEIP4844Transaction, TransactionFactory } from '@ethereumjs/tx' import { - arrToBufArr, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, - bufferToInt, - intToUnpaddedBuffer, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToInt, + intToUnpaddedBytes, } from '@ethereumjs/util' import { encodeReceipt } from '@ethereumjs/vm/dist/runBlock' @@ -17,9 +15,10 @@ import { Protocol } from './protocol' import type { Chain } from '../../blockchain' import type { TxReceiptWithType } from '../../execution/receipt' import type { Message, ProtocolOptions } from './protocol' -import type { BlockBodyBuffer, BlockBuffer, BlockHeaderBuffer } from '@ethereumjs/block' +import type { BlockBodyBytes, BlockBytes, BlockHeaderBytes } from '@ethereumjs/block' +import type { Log } from '@ethereumjs/evm' import type { TypedTransaction } from '@ethereumjs/tx' -import type { BigIntLike } from '@ethereumjs/util' +import type { BigIntLike, NestedUint8Array } from '@ethereumjs/util' import type { PostByzantiumTxReceipt, PreByzantiumTxReceipt, TxReceipt } from '@ethereumjs/vm' interface EthProtocolOptions extends ProtocolOptions { @@ -31,7 +30,7 @@ type GetBlockHeadersOpts = { /* Request id (default: next internal id) */ reqId?: bigint /* The block's number or hash */ - block: bigint | Buffer + block: bigint | Uint8Array /* Max number of blocks to return */ max: number /* Number of blocks to skip apart (default: 0) */ @@ -44,21 +43,21 @@ type GetBlockBodiesOpts = { /* Request id (default: next internal id) */ reqId?: bigint /* The block hashes */ - hashes: Buffer[] + hashes: Uint8Array[] } type GetPooledTransactionsOpts = { /* Request id (default: next internal id) */ reqId?: bigint /* The tx hashes */ - hashes: Buffer[] + hashes: Uint8Array[] } type GetReceiptsOpts = { /* Request id (default: next internal id) */ reqId?: bigint /* The block hashes to request receipts for */ - hashes: Buffer[] + hashes: Uint8Array[] } /* @@ -67,7 +66,7 @@ type GetReceiptsOpts = { */ export interface EthProtocolMethods { getBlockHeaders: (opts: GetBlockHeadersOpts) => Promise<[bigint, BlockHeader[]]> - getBlockBodies: (opts: GetBlockBodiesOpts) => Promise<[bigint, BlockBodyBuffer[]]> + getBlockBodies: (opts: GetBlockBodiesOpts) => Promise<[bigint, BlockBodyBytes[]]> getPooledTransactions: (opts: GetPooledTransactionsOpts) => Promise<[bigint, TypedTransaction[]]> getReceipts: (opts: GetReceiptsOpts) => Promise<[bigint, TxReceipt[]]> } @@ -86,8 +85,8 @@ export class EthProtocol extends Protocol { { name: 'NewBlockHashes', code: 0x01, - encode: (hashes: any[]) => hashes.map((hn) => [hn[0], bigIntToUnpaddedBuffer(hn[1])]), - decode: (hashes: any[]) => hashes.map((hn) => [hn[0], bufferToBigInt(hn[1])]), + encode: (hashes: any[]) => hashes.map((hn) => [hn[0], bigIntToUnpaddedBytes(hn[1])]), + decode: (hashes: any[]) => hashes.map((hn) => [hn[0], bytesToBigInt(hn[1])]), }, { name: 'Transactions', @@ -101,7 +100,7 @@ export class EthProtocol extends Protocol { } return serializedTxs }, - decode: (txs: Buffer[]) => { + decode: (txs: Uint8Array[]) => { if (!this.config.synchronized) return const common = this.config.chainCommon.copy() common.setHardforkByBlockNumber( @@ -120,31 +119,31 @@ export class EthProtocol extends Protocol { code: 0x03, response: 0x04, encode: ({ reqId, block, max, skip = 0, reverse = false }: GetBlockHeadersOpts) => [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), [ - typeof block === 'bigint' ? bigIntToUnpaddedBuffer(block) : block, - intToUnpaddedBuffer(max), - intToUnpaddedBuffer(skip), - intToUnpaddedBuffer(!reverse ? 0 : 1), + typeof block === 'bigint' ? bigIntToUnpaddedBytes(block) : block, + intToUnpaddedBytes(max), + intToUnpaddedBytes(skip), + intToUnpaddedBytes(!reverse ? 0 : 1), ], ], decode: ([reqId, [block, max, skip, reverse]]: any) => ({ - reqId: bufferToBigInt(reqId), - block: block.length === 32 ? block : bufferToBigInt(block), - max: bufferToInt(max), - skip: bufferToInt(skip), - reverse: bufferToInt(reverse) === 0 ? false : true, + reqId: bytesToBigInt(reqId), + block: block.length === 32 ? block : bytesToBigInt(block), + max: bytesToInt(max), + skip: bytesToInt(skip), + reverse: bytesToInt(reverse) === 0 ? false : true, }), }, { name: 'BlockHeaders', code: 0x04, encode: ({ reqId, headers }: { reqId: bigint; headers: BlockHeader[] }) => [ - bigIntToUnpaddedBuffer(reqId), + bigIntToUnpaddedBytes(reqId), headers.map((h) => h.raw()), ], - decode: ([reqId, headers]: [Buffer, BlockHeaderBuffer[]]) => [ - bufferToBigInt(reqId), + decode: ([reqId, headers]: [Uint8Array, BlockHeaderBytes[]]) => [ + bytesToBigInt(reqId), headers.map((h) => { const headerData = valuesArrayToHeaderData(h) const difficulty = getDifficulty(headerData)! @@ -166,28 +165,28 @@ export class EthProtocol extends Protocol { code: 0x05, response: 0x06, encode: ({ reqId, hashes }: GetBlockBodiesOpts) => [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), hashes, ], - decode: ([reqId, hashes]: [Buffer, Buffer[]]) => ({ - reqId: bufferToBigInt(reqId), + decode: ([reqId, hashes]: [Uint8Array, Uint8Array[]]) => ({ + reqId: bytesToBigInt(reqId), hashes, }), }, { name: 'BlockBodies', code: 0x06, - encode: ({ reqId, bodies }: { reqId: bigint; bodies: BlockBodyBuffer[] }) => [ - bigIntToUnpaddedBuffer(reqId), + encode: ({ reqId, bodies }: { reqId: bigint; bodies: BlockBodyBytes[] }) => [ + bigIntToUnpaddedBytes(reqId), bodies, ], - decode: ([reqId, bodies]: [Buffer, BlockBodyBuffer[]]) => [bufferToBigInt(reqId), bodies], + decode: ([reqId, bodies]: [Uint8Array, BlockBodyBytes[]]) => [bytesToBigInt(reqId), bodies], }, { name: 'NewBlock', code: 0x07, - encode: ([block, td]: [Block, bigint]) => [block.raw(), bigIntToUnpaddedBuffer(td)], - decode: ([block, td]: [BlockBuffer, Buffer]) => [ + encode: ([block, td]: [Block, bigint]) => [block.raw(), bigIntToUnpaddedBytes(td)], + decode: ([block, td]: [BlockBytes, Uint8Array]) => [ Block.fromValuesArray(block, { common: this.config.chainCommon, hardforkByBlockNumber: true, @@ -198,19 +197,19 @@ export class EthProtocol extends Protocol { { name: 'NewPooledTransactionHashes', code: 0x08, - encode: (hashes: Buffer[]) => hashes, - decode: (hashes: Buffer[]) => hashes, + encode: (hashes: Uint8Array[]) => hashes, + decode: (hashes: Uint8Array[]) => hashes, }, { name: 'GetPooledTransactions', code: 0x09, response: 0x0a, encode: ({ reqId, hashes }: GetPooledTransactionsOpts) => [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), hashes, ], - decode: ([reqId, hashes]: [Buffer, Buffer[]]) => ({ - reqId: bufferToBigInt(reqId), + decode: ([reqId, hashes]: [Uint8Array, Uint8Array[]]) => ({ + reqId: bytesToBigInt(reqId), hashes, }), }, @@ -232,9 +231,9 @@ export class EthProtocol extends Protocol { break } } - return [bigIntToUnpaddedBuffer(reqId), serializedTxs] + return [bigIntToUnpaddedBytes(reqId), serializedTxs] }, - decode: ([reqId, txs]: [Buffer, any[]]) => { + decode: ([reqId, txs]: [Uint8Array, any[]]) => { const common = this.config.chainCommon.copy() common.setHardforkByBlockNumber( this.chain.headers.latest?.number ?? // Use latest header number if available OR @@ -245,7 +244,7 @@ export class EthProtocol extends Protocol { this.chain.headers.latest?.timestamp ?? Math.floor(Date.now() / 1000) ) return [ - bufferToBigInt(reqId), + bytesToBigInt(reqId), txs.map((txData) => { if (txData[0] === 5) { // Blob transactions are deserialized with network wrapper @@ -260,12 +259,12 @@ export class EthProtocol extends Protocol { name: 'GetReceipts', code: 0x0f, response: 0x10, - encode: ({ reqId, hashes }: { reqId: bigint; hashes: Buffer[] }) => [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + encode: ({ reqId, hashes }: { reqId: bigint; hashes: Uint8Array[] }) => [ + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), hashes, ], - decode: ([reqId, hashes]: [Buffer, Buffer[]]) => ({ - reqId: bufferToBigInt(reqId), + decode: ([reqId, hashes]: [Uint8Array, Uint8Array[]]) => ({ + reqId: bytesToBigInt(reqId), hashes, }), }, @@ -278,23 +277,28 @@ export class EthProtocol extends Protocol { const encodedReceipt = encodeReceipt(receipt, receipt.txType) serializedReceipts.push(encodedReceipt) } - return [bigIntToUnpaddedBuffer(reqId), serializedReceipts] + return [bigIntToUnpaddedBytes(reqId), serializedReceipts] }, - decode: ([reqId, receipts]: [Buffer, Buffer[]]) => [ - bufferToBigInt(reqId), + decode: ([reqId, receipts]: [Uint8Array, Uint8Array[]]) => [ + bytesToBigInt(reqId), receipts.map((r) => { // Legacy receipt if r[0] >= 0xc0, otherwise typed receipt with first byte as TransactionType - const decoded = arrToBufArr(RLP.decode(bufArrToArr(r[0] >= 0xc0 ? r : r.slice(1)))) as any - const [stateRootOrStatus, cumulativeGasUsed, logsBloom, logs] = decoded + const decoded = RLP.decode(r[0] >= 0xc0 ? r : r.subarray(1)) as NestedUint8Array + const [stateRootOrStatus, cumulativeGasUsed, logsBloom, logs] = decoded as [ + Uint8Array, + Uint8Array, + Uint8Array, + Log[] + ] const receipt = { - cumulativeBlockGasUsed: bufferToBigInt(cumulativeGasUsed), + cumulativeBlockGasUsed: bytesToBigInt(cumulativeGasUsed), bitvector: logsBloom, logs, } as TxReceipt if (stateRootOrStatus.length === 32) { ;(receipt as PreByzantiumTxReceipt).stateRoot = stateRootOrStatus } else { - ;(receipt as PostByzantiumTxReceipt).status = bufferToInt(stateRootOrStatus) as 0 | 1 + ;(receipt as PostByzantiumTxReceipt).status = bytesToInt(stateRootOrStatus) as 0 | 1 } return receipt }), @@ -352,11 +356,11 @@ export class EthProtocol extends Protocol { */ encodeStatus(): any { return { - networkId: bigIntToUnpaddedBuffer(this.chain.networkId), - td: bigIntToUnpaddedBuffer(this.chain.blocks.td), + networkId: bigIntToUnpaddedBytes(this.chain.networkId), + td: bigIntToUnpaddedBytes(this.chain.blocks.td), bestHash: this.chain.blocks.latest!.hash(), genesisHash: this.chain.genesis.hash(), - latestBlock: bigIntToUnpaddedBuffer(this.chain.blocks.latest!.header.number), + latestBlock: bigIntToUnpaddedBytes(this.chain.blocks.latest!.header.number), } } @@ -366,8 +370,8 @@ export class EthProtocol extends Protocol { */ decodeStatus(status: any): any { return { - networkId: bufferToBigInt(status.networkId), - td: bufferToBigInt(status.td), + networkId: bytesToBigInt(status.networkId), + td: bytesToBigInt(status.td), bestHash: status.bestHash, genesisHash: status.genesisHash, } diff --git a/packages/client/lib/net/protocol/lesprotocol.ts b/packages/client/lib/net/protocol/lesprotocol.ts index 50acc2de53c..62e3eb27c7f 100644 --- a/packages/client/lib/net/protocol/lesprotocol.ts +++ b/packages/client/lib/net/protocol/lesprotocol.ts @@ -1,12 +1,18 @@ import { BlockHeader } from '@ethereumjs/block' -import { bigIntToUnpaddedBuffer, bufferToBigInt, bufferToInt, intToBuffer } from '@ethereumjs/util' +import { + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToInt, + hexStringToBytes, + intToBytes, +} from '@ethereumjs/util' import { Protocol } from './protocol' import type { Chain } from '../../blockchain' import type { FlowControl } from './flowcontrol' import type { Message, ProtocolOptions } from './protocol' -import type { BlockHeaderBuffer } from '@ethereumjs/block' +import type { BlockHeaderBytes } from '@ethereumjs/block' export interface LesProtocolOptions extends ProtocolOptions { /* Blockchain */ @@ -20,7 +26,7 @@ type GetBlockHeadersOpts = { /* Request id (default: next internal id) */ reqId?: bigint /* The block's number or hash */ - block: bigint | Buffer + block: bigint | Uint8Array /* Max number of blocks to return */ max: number /* Number of blocks to skip apart (default: 0) */ @@ -56,16 +62,16 @@ export class LesProtocol extends Protocol { encode: ({ headHash, headNumber, headTd, reorgDepth }: any) => [ // TO DO: handle state changes headHash, - bigIntToUnpaddedBuffer(headNumber), - bigIntToUnpaddedBuffer(headTd), - intToBuffer(reorgDepth), + bigIntToUnpaddedBytes(headNumber), + bigIntToUnpaddedBytes(headTd), + intToBytes(reorgDepth), ], decode: ([headHash, headNumber, headTd, reorgDepth]: any) => ({ // TO DO: handle state changes headHash, - headNumber: bufferToBigInt(headNumber), - headTd: bufferToBigInt(headTd), - reorgDepth: bufferToInt(reorgDepth), + headNumber: bytesToBigInt(headNumber), + headTd: bytesToBigInt(headTd), + reorgDepth: bytesToInt(reorgDepth), }), }, { @@ -73,34 +79,35 @@ export class LesProtocol extends Protocol { code: 0x02, response: 0x03, encode: ({ reqId, block, max, skip = 0, reverse = false }: GetBlockHeadersOpts) => [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), [ - typeof block === 'bigint' ? bigIntToUnpaddedBuffer(block) : block, - max, - skip, - !reverse ? 0 : 1, + typeof block === 'bigint' ? bigIntToUnpaddedBytes(block) : block, + intToBytes(max), + intToBytes(skip), + intToBytes(!reverse ? 0 : 1), ], ], decode: ([reqId, [block, max, skip, reverse]]: any) => ({ - reqId: bufferToBigInt(reqId), - block: block.length === 32 ? block : bufferToBigInt(block), - max: bufferToInt(max), - skip: bufferToInt(skip), - reverse: bufferToInt(reverse) === 0 ? false : true, + reqId: bytesToBigInt(reqId), + block: block.length === 32 ? block : bytesToBigInt(block), + max: bytesToInt(max), + skip: bytesToInt(skip), + reverse: bytesToInt(reverse) === 0 ? false : true, }), }, + { name: 'BlockHeaders', code: 0x03, encode: ({ reqId, bv, headers }: any) => [ - bigIntToUnpaddedBuffer(reqId), - bigIntToUnpaddedBuffer(bv), + bigIntToUnpaddedBytes(reqId), + bigIntToUnpaddedBytes(bv), headers.map((h: BlockHeader) => h.raw()), ], decode: ([reqId, bv, headers]: any) => ({ - reqId: bufferToBigInt(reqId), - bv: bufferToBigInt(bv), - headers: headers.map((h: BlockHeaderBuffer) => + reqId: bytesToBigInt(reqId), + bv: bytesToBigInt(bv), + headers: headers.map((h: BlockHeaderBytes) => BlockHeader.fromValuesArray(h, { hardforkByBlockNumber: true, common: this.config.chainCommon, // eslint-disable-line no-invalid-this @@ -167,11 +174,11 @@ export class LesProtocol extends Protocol { serveChainSince: 0, serveStateSince: 0, // txRelay: 1, TODO: uncomment with client tx pool functionality - 'flowControl/BL': intToBuffer(this.flow.bl), - 'flowControl/MRR': intToBuffer(this.flow.mrr), + 'flowControl/BL': intToBytes(this.flow.bl), + 'flowControl/MRR': intToBytes(this.flow.mrr), 'flowControl/MRC': Object.entries(this.flow.mrc).map(([name, { base, req }]) => { const { code } = this.messages.find((m) => m.name === name)! - return [intToBuffer(code), intToBuffer(base), intToBuffer(req)] + return [intToBytes(code), intToBytes(base), intToBytes(req)] }), } } @@ -183,16 +190,16 @@ export class LesProtocol extends Protocol { const nextFork = this.config.chainCommon.nextHardforkBlockOrTimestamp( this.config.chainCommon.hardfork() ) - const forkID = [Buffer.from(forkHash.slice(2), 'hex'), bigIntToUnpaddedBuffer(nextFork ?? 0n)] + const forkID = [hexStringToBytes(forkHash.slice(2)), bigIntToUnpaddedBytes(nextFork ?? 0n)] return { - networkId: bigIntToUnpaddedBuffer(this.chain.networkId), - headTd: bigIntToUnpaddedBuffer(this.chain.headers.td), + networkId: bigIntToUnpaddedBytes(this.chain.networkId), + headTd: bigIntToUnpaddedBytes(this.chain.headers.td), headHash: this.chain.headers.latest?.hash(), - headNum: bigIntToUnpaddedBuffer(this.chain.headers.height), + headNum: bigIntToUnpaddedBytes(this.chain.headers.height), genesisHash: this.chain.genesis.hash(), forkID, - recentTxLookup: intToBuffer(1), + recentTxLookup: intToBytes(1), ...serveOptions, } } @@ -206,7 +213,7 @@ export class LesProtocol extends Protocol { const mrc: any = {} if (status['flowControl/MRC'] !== undefined) { for (let entry of status['flowControl/MRC']) { - entry = entry.map((e: any) => bufferToInt(e)) + entry = entry.map((e: any) => bytesToInt(e)) mrc[entry[0]] = { base: entry[1], req: entry[2] } const message = this.messages.find((m) => m.code === entry[0]) if (message) { @@ -215,10 +222,10 @@ export class LesProtocol extends Protocol { } } return { - networkId: bufferToBigInt(status.networkId), - headTd: bufferToBigInt(status.headTd), + networkId: bytesToBigInt(status.networkId), + headTd: bytesToBigInt(status.headTd), headHash: status.headHash, - headNum: bufferToBigInt(status.headNum), + headNum: bytesToBigInt(status.headNum), genesisHash: status.genesisHash, forkID: status.forkID, recentTxLookup: status.recentTxLookup, @@ -226,12 +233,9 @@ export class LesProtocol extends Protocol { serveChainSince: status.serveChainSince ?? 0, serveStateSince: status.serveStateSince ?? 0, txRelay: status.txRelay === true, - bl: - status['flowControl/BL'] !== undefined ? bufferToInt(status['flowControl/BL']) : undefined, + bl: status['flowControl/BL'] !== undefined ? bytesToInt(status['flowControl/BL']) : undefined, mrr: - status['flowControl/MRR'] !== undefined - ? bufferToInt(status['flowControl/MRR']) - : undefined, + status['flowControl/MRR'] !== undefined ? bytesToInt(status['flowControl/MRR']) : undefined, mrc, } } diff --git a/packages/client/lib/net/protocol/libp2psender.ts b/packages/client/lib/net/protocol/libp2psender.ts index 5df71836a5f..b1da0b97532 100644 --- a/packages/client/lib/net/protocol/libp2psender.ts +++ b/packages/client/lib/net/protocol/libp2psender.ts @@ -1,15 +1,16 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr, bufArrToArr, bufferToInt, intToBuffer } from '@ethereumjs/util' +import { bytesToInt, bytesToUtf8, intToBytes, utf8ToBytes } from '@ethereumjs/util' import * as pipe from 'it-pipe' import * as pushable from 'it-pushable' import { Sender } from './sender' import type { Libp2pMuxedStream as MuxedStream } from '../../types' +import type { NestedUint8Array } from '@ethereumjs/util' // TypeScript doesn't have support yet for ReturnType // with generic types, so this wrapper is used as a helper. -const wrapperPushable = () => pushable() +const wrapperPushable = () => pushable() type Pushable = ReturnType /** @@ -41,15 +42,15 @@ export class Libp2pSender extends Sender { // incoming stream void pipe.pipe(this.stream, async (source: any) => { for await (const bl of source) { - // convert BufferList to Buffer - const data: Buffer = bl.slice() + // convert BytesList to Uint8Array + const data: Uint8Array = bl.slice() try { - const [codeBuf, payload]: any = arrToBufArr(RLP.decode(Uint8Array.from(data))) - const code = bufferToInt(codeBuf) + const [codeBuf, payload] = RLP.decode(Uint8Array.from(data)) + const code = bytesToInt(codeBuf as Uint8Array) if (code === 0) { const status: any = {} - for (const [k, v] of payload.values()) { - status[k.toString()] = v + for (const [k, v] of (payload as NestedUint8Array).values()) { + status[bytesToUtf8(k as Uint8Array)] = v } this.status = status } else { @@ -67,8 +68,8 @@ export class Libp2pSender extends Sender { * @param status */ sendStatus(status: any) { - const payload: any = Object.entries(status).map(([k, v]) => [Buffer.from(k), v]) - this.pushable.push(Buffer.from(RLP.encode(bufArrToArr([intToBuffer(0), payload])))) + const payload: any = Object.entries(status).map(([k, v]) => [utf8ToBytes(k), v]) + this.pushable.push(RLP.encode([intToBytes(0), payload])) } /** @@ -77,6 +78,6 @@ export class Libp2pSender extends Sender { * @param data message payload */ sendMessage(code: number, data: any) { - this.pushable.push(Buffer.from(RLP.encode(bufArrToArr([intToBuffer(code), data])))) + this.pushable.push(RLP.encode([intToBytes(code), data])) } } diff --git a/packages/client/lib/net/protocol/sender.ts b/packages/client/lib/net/protocol/sender.ts index 2f8d8b86410..6a172f31e01 100644 --- a/packages/client/lib/net/protocol/sender.ts +++ b/packages/client/lib/net/protocol/sender.ts @@ -38,7 +38,7 @@ export class Sender extends EventEmitter { * @param code message code * @param rlpEncodedData rlp encoded message payload */ - sendMessage(_code: number, _rlpEncodedData: any[] | Buffer) { + sendMessage(_code: number, _rlpEncodedData: any[] | Uint8Array) { throw new Error('Unimplemented') } } diff --git a/packages/client/lib/net/protocol/snapprotocol.ts b/packages/client/lib/net/protocol/snapprotocol.ts index 59917a953be..aa3ec103ac1 100644 --- a/packages/client/lib/net/protocol/snapprotocol.ts +++ b/packages/client/lib/net/protocol/snapprotocol.ts @@ -1,8 +1,8 @@ import { accountBodyFromSlim, accountBodyToSlim, - bigIntToUnpaddedBuffer, - bufferToBigInt, + bigIntToUnpaddedBytes, + bytesToBigInt, setLengthLeft, } from '@ethereumjs/util' @@ -10,7 +10,7 @@ import { Protocol } from './protocol' import type { Chain } from '../../blockchain' import type { Message, ProtocolOptions } from './protocol' -import type { AccountBodyBuffer } from '@ethereumjs/util' +import type { AccountBodyBytes } from '@ethereumjs/util' interface SnapProtocolOptions extends ProtocolOptions { /* Blockchain */ @@ -23,22 +23,22 @@ interface SnapProtocolOptions extends ProtocolOptions { } export type AccountData = { - hash: Buffer - body: AccountBodyBuffer + hash: Uint8Array + body: AccountBodyBytes } type GetAccountRangeOpts = { /* Request id (default: next internal id) */ reqId?: bigint - root: Buffer - origin: Buffer - limit: Buffer + root: Uint8Array + origin: Uint8Array + limit: Uint8Array bytes: bigint } type GetStorageRangesOpts = { reqId?: bigint - root: Buffer + root: Uint8Array // If multiple accounts' storage is requested, serving nodes // should reply with the entire storage ranges (thus no Merkle @@ -51,27 +51,27 @@ type GetStorageRangesOpts = { // starting hash, up to the last one or until the packet fills // up. It the entire storage range is not being returned, a // Merkle proof must be attached. - accounts: Buffer[] - origin: Buffer - limit: Buffer + accounts: Uint8Array[] + origin: Uint8Array + limit: Uint8Array bytes: bigint } export type StorageData = { - hash: Buffer - body: Buffer + hash: Uint8Array + body: Uint8Array } type GetByteCodesOpts = { reqId?: bigint - hashes: Buffer[] + hashes: Uint8Array[] bytes: bigint } type GetTrieNodesOpts = { reqId?: bigint - root: Buffer - paths: Buffer[][] + root: Uint8Array + paths: Uint8Array[][] bytes: bigint } /* @@ -81,13 +81,13 @@ type GetTrieNodesOpts = { export interface SnapProtocolMethods { getAccountRange: ( opts: GetAccountRangeOpts - ) => Promise<{ reqId: bigint; accounts: AccountData[]; proof: Buffer[] }> + ) => Promise<{ reqId: bigint; accounts: AccountData[]; proof: Uint8Array[] }> getStorageRanges: (opts: GetStorageRangesOpts) => Promise<{ reqId: bigint slots: StorageData[][] - proof: Buffer[] + proof: Uint8Array[] }> - getByteCodes: (opts: GetByteCodesOpts) => Promise<{ reqId: bigint; codes: Buffer[] }> + getByteCodes: (opts: GetByteCodesOpts) => Promise<{ reqId: bigint; codes: Uint8Array[] }> } /** @@ -109,20 +109,20 @@ export class SnapProtocol extends Protocol { // [reqID: P, rootHash: B_32, startingHash: B_32, limitHash: B_32, responseBytes: P] encode: ({ reqId, root, origin, limit, bytes }: GetAccountRangeOpts) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), setLengthLeft(root, 32), setLengthLeft(origin, 32), setLengthLeft(limit, 32), - bigIntToUnpaddedBuffer(bytes), + bigIntToUnpaddedBytes(bytes), ] }, decode: ([reqId, root, origin, limit, bytes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), root, origin, limit, - bytes: bufferToBigInt(bytes), + bytes: bytesToBigInt(bytes), } }, }, @@ -137,10 +137,10 @@ export class SnapProtocol extends Protocol { }: { reqId: bigint accounts: AccountData[] - proof: Buffer[] + proof: Uint8Array[] }) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), accounts.map((account) => [ setLengthLeft(account.hash, 32), accountBodyToSlim(account.body), @@ -150,7 +150,7 @@ export class SnapProtocol extends Protocol { }, decode: ([reqId, accounts, proof]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), accounts: accounts.map( ([hash, body]: any) => ({ @@ -169,22 +169,22 @@ export class SnapProtocol extends Protocol { // [reqID: P, rootHash: B_32, accountHashes: [B_32], startingHash: B, limitHash: B, responseBytes: P] encode: ({ reqId, root, accounts, origin, limit, bytes }: GetStorageRangesOpts) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), setLengthLeft(root, 32), accounts.map((acc) => setLengthLeft(acc, 32)), origin, limit, - bigIntToUnpaddedBuffer(bytes), + bigIntToUnpaddedBytes(bytes), ] }, decode: ([reqId, root, accounts, origin, limit, bytes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), root, accounts, origin, limit, - bytes: bufferToBigInt(bytes), + bytes: bytesToBigInt(bytes), } }, }, @@ -199,10 +199,10 @@ export class SnapProtocol extends Protocol { }: { reqId: bigint slots: StorageData[][] - proof: Buffer[] + proof: Uint8Array[] }) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), slots.map((accSlots) => accSlots.map((slotData) => [setLengthLeft(slotData.hash, 32), slotData.body]) ), @@ -211,7 +211,7 @@ export class SnapProtocol extends Protocol { }, decode: ([reqId, slots, proof]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), slots: slots.map((accSlots: any) => accSlots.map(([hash, body]: any) => ({ hash, body } as StorageData)) ), @@ -226,16 +226,16 @@ export class SnapProtocol extends Protocol { // [reqID: P, hashes: [hash1: B_32, hash2: B_32, ...], bytes: P] encode: ({ reqId, hashes, bytes }: GetByteCodesOpts) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), hashes.map((hash) => setLengthLeft(hash, 32)), - bigIntToUnpaddedBuffer(bytes), + bigIntToUnpaddedBytes(bytes), ] }, decode: ([reqId, hashes, bytes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), hashes, - bytes: bufferToBigInt(bytes), + bytes: bytesToBigInt(bytes), } }, }, @@ -243,12 +243,12 @@ export class SnapProtocol extends Protocol { name: 'ByteCodes', code: 0x05, // [reqID: P, codes: [code1: B, code2: B, ...]] - encode: ({ reqId, codes }: { reqId: bigint; codes: Buffer[] }) => { - return [bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), codes] + encode: ({ reqId, codes }: { reqId: bigint; codes: Uint8Array[] }) => { + return [bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), codes] }, decode: ([reqId, codes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), codes, } }, @@ -260,18 +260,18 @@ export class SnapProtocol extends Protocol { // [reqID: P, rootHash: B_32, paths: [[accPath: B, slotPath1: B, slotPath2: B, ...]...], bytes: P] encode: ({ reqId, root, paths, bytes }: GetTrieNodesOpts) => { return [ - bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), + bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), setLengthLeft(root, 32), paths, - bigIntToUnpaddedBuffer(bytes), + bigIntToUnpaddedBytes(bytes), ] }, decode: ([reqId, root, paths, bytes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), root, paths, - bytes: bufferToBigInt(bytes), + bytes: bytesToBigInt(bytes), } }, }, @@ -279,12 +279,12 @@ export class SnapProtocol extends Protocol { name: 'TrieNodes', code: 0x07, // [reqID: P, nodes: [node1: B, node2: B, ...]] - encode: ({ reqId, nodes }: { reqId: bigint; nodes: Buffer[] }) => { - return [bigIntToUnpaddedBuffer(reqId ?? ++this.nextReqId), nodes] + encode: ({ reqId, nodes }: { reqId: bigint; nodes: Uint8Array[] }) => { + return [bigIntToUnpaddedBytes(reqId ?? ++this.nextReqId), nodes] }, decode: ([reqId, nodes]: any) => { return { - reqId: bufferToBigInt(reqId), + reqId: bytesToBigInt(reqId), nodes, } }, diff --git a/packages/client/lib/net/server/rlpxserver.ts b/packages/client/lib/net/server/rlpxserver.ts index 89e813317ad..9d5f90eb6fb 100644 --- a/packages/client/lib/net/server/rlpxserver.ts +++ b/packages/client/lib/net/server/rlpxserver.ts @@ -1,4 +1,5 @@ import { DPT as Devp2pDPT, RLPx as Devp2pRLPx } from '@ethereumjs/devp2p' +import { bytesToHex, utf8ToBytes } from 'ethereum-cryptography/utils' import { Event } from '../../types' import { getClientVersion } from '../../util' @@ -28,8 +29,8 @@ const ignoredErrors = new RegExp( // DPT message decoding 'Hash verification failed', - 'Invalid address buffer', - 'Invalid timestamp buffer', + 'Invalid address bytes', + 'Invalid timestamp bytes', 'Invalid type', 'Timeout error: ping', // connection 'Peer is banned', // connection @@ -104,7 +105,7 @@ export class RlpxServer extends Server { ports: { discovery: this.config.port, listener: this.config.port }, } } - const id = this.rlpx._id.toString('hex') + const id = bytesToHex(this.rlpx._id) return { enode: `enode://${id}@${listenAddr}`, id, @@ -239,7 +240,7 @@ export class RlpxServer extends Server { private async initRlpx() { return new Promise((resolve) => { this.rlpx = new Devp2pRLPx(this.key, { - clientId: Buffer.from(getClientVersion()), + clientId: utf8ToBytes(getClientVersion()), dpt: this.dpt!, maxPeers: this.config.maxPeers, capabilities: RlpxPeer.capabilities(Array.from(this.protocols)), @@ -251,7 +252,7 @@ export class RlpxServer extends Server { this.rlpx.on('peer:added', async (rlpxPeer: Devp2pRLPxPeer) => { const peer = new RlpxPeer({ config: this.config, - id: rlpxPeer.getId()!.toString('hex'), + id: bytesToHex(rlpxPeer.getId()!), host: rlpxPeer._socket.remoteAddress!, port: rlpxPeer._socket.remotePort!, protocols: Array.from(this.protocols), @@ -270,7 +271,7 @@ export class RlpxServer extends Server { }) this.rlpx.on('peer:removed', (rlpxPeer: Devp2pRLPxPeer, reason: any) => { - const id = (rlpxPeer.getId() as Buffer).toString('hex') + const id = bytesToHex(rlpxPeer.getId() as Uint8Array) const peer = this.peers.get(id) if (peer) { this.peers.delete(peer.id) @@ -282,7 +283,7 @@ export class RlpxServer extends Server { }) this.rlpx.on('peer:error', (rlpxPeer: Devp2pRLPxPeer, error: Error) => { - const peerId = rlpxPeer.getId() + const peerId = bytesToHex(rlpxPeer.getId() as Uint8Array) if (peerId === null) { return this.error(error) } diff --git a/packages/client/lib/net/server/server.ts b/packages/client/lib/net/server/server.ts index a745674ab76..b6ce4b942e5 100644 --- a/packages/client/lib/net/server/server.ts +++ b/packages/client/lib/net/server/server.ts @@ -28,7 +28,7 @@ export interface ServerOptions { */ export class Server { public config: Config - public key: Buffer + public key: Uint8Array public bootnodes: Multiaddr[] = [] public dnsNetworks: DnsNetwork[] diff --git a/packages/client/lib/rpc/helpers.ts b/packages/client/lib/rpc/helpers.ts index 563377cbb36..6f01670724e 100644 --- a/packages/client/lib/rpc/helpers.ts +++ b/packages/client/lib/rpc/helpers.ts @@ -1,4 +1,4 @@ -import { bigIntToHex, bufferToHex, intToHex } from '@ethereumjs/util' +import { bigIntToHex, bytesToPrefixedHexString, intToHex } from '@ethereumjs/util' import type { Block } from '@ethereumjs/block' import type { JsonRpcTx, TypedTransaction } from '@ethereumjs/tx' @@ -9,7 +9,7 @@ import type { JsonRpcTx, TypedTransaction } from '@ethereumjs/tx' export const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number): JsonRpcTx => { const txJSON = tx.toJSON() return { - blockHash: block ? bufferToHex(block.hash()) : null, + blockHash: block ? bytesToPrefixedHexString(block.hash()) : null, blockNumber: block ? bigIntToHex(block.header.number) : null, from: tx.getSenderAddress().toString(), gas: txJSON.gasLimit!, @@ -19,7 +19,7 @@ export const jsonRpcTx = (tx: TypedTransaction, block?: Block, txIndex?: number) type: intToHex(tx.type), accessList: txJSON.accessList, chainId: txJSON.chainId, - hash: bufferToHex(tx.hash()), + hash: bytesToPrefixedHexString(tx.hash()), input: txJSON.data!, nonce: txJSON.nonce!, to: tx.to?.toString() ?? null, diff --git a/packages/client/lib/rpc/modules/admin.ts b/packages/client/lib/rpc/modules/admin.ts index 37263f9cccc..443114c6c4d 100644 --- a/packages/client/lib/rpc/modules/admin.ts +++ b/packages/client/lib/rpc/modules/admin.ts @@ -1,4 +1,4 @@ -import { bufferToHex } from '@ethereumjs/util' +import { bytesToHex } from '@ethereumjs/util' import { getClientVersion } from '../../util' import { middleware } from '../validation' @@ -41,8 +41,8 @@ export class Admin { const latestHeader = this._chain.headers.latest! const difficulty = latestHeader.difficulty.toString() - const genesis = bufferToHex(this._chain.genesis.hash()) - const head = bufferToHex(latestHeader.mixHash) + const genesis = bytesToHex(this._chain.genesis.hash()) + const head = bytesToHex(latestHeader.mixHash) const network = this._chain.networkId.toString() const nodeInfo = { diff --git a/packages/client/lib/rpc/modules/debug.ts b/packages/client/lib/rpc/modules/debug.ts index c36e2c3d901..680b5306171 100644 --- a/packages/client/lib/rpc/modules/debug.ts +++ b/packages/client/lib/rpc/modules/debug.ts @@ -1,4 +1,4 @@ -import { bigIntToHex, bufferToHex, toBuffer } from '@ethereumjs/util' +import { bigIntToHex, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' import { INTERNAL_ERROR, INVALID_PARAMS } from '../error-code' import { middleware, validators } from '../validation' @@ -101,7 +101,7 @@ export class Debug { try { const result = await this.service.execution.receiptsManager.getReceiptByTxHash( - toBuffer(txHash) + hexStringToBytes(txHash) ) if (!result) return null const [_, blockHash, txIndex] = result @@ -131,7 +131,7 @@ export class Debug { } if (opts.enableMemory === true) { for (let x = 0; x < step.memoryWordCount; x++) { - const word = bufferToHex(step.memory.slice(x * 32, 32)) + const word = bytesToPrefixedHexString(step.memory.slice(x * 32, 32)) memory.push(word) } } @@ -142,8 +142,7 @@ export class Debug { gas: Number(step.gasLeft), depth: step.depth, error: null, - stack: - opts.disableStack !== true ? step.stack.map((entry) => bigIntToHex(entry)) : undefined, + stack: opts.disableStack !== true ? step.stack.map(bigIntToHex) : undefined, storage, memory, returnData: undefined, @@ -162,7 +161,7 @@ export class Debug { const res = await vmCopy.runTx({ tx, block }) trace.gas = bigIntToHex(res.totalGasSpent) trace.failed = res.execResult.exceptionError !== undefined - trace.returnValue = bufferToHex(res.execResult.returnValue) + trace.returnValue = bytesToPrefixedHexString(res.execResult.returnValue) return trace } catch (err: any) { throw { diff --git a/packages/client/lib/rpc/modules/engine.ts b/packages/client/lib/rpc/modules/engine.ts index 41f12fe9090..a9810b8e382 100644 --- a/packages/client/lib/rpc/modules/engine.ts +++ b/packages/client/lib/rpc/modules/engine.ts @@ -1,7 +1,16 @@ import { Block } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' -import { Withdrawal, bigIntToHex, bufferToHex, toBuffer, zeros } from '@ethereumjs/util' +import { + Withdrawal, + bigIntToHex, + bytesToHex, + bytesToPrefixedHexString, + equalsBytes, + hexStringToBytes, + toBytes, + zeros, +} from '@ethereumjs/util' import { PendingBlock } from '../../miner' import { short } from '../../util' @@ -163,7 +172,8 @@ const payloadAttributesFieldValidatorsV2 = { export const blockToExecutionPayload = (block: Block, value: bigint) => { const blockJson = block.toJSON() const header = blockJson.header! - const transactions = block.transactions.map((tx) => bufferToHex(tx.serialize())) ?? [] + const transactions = + block.transactions.map((tx) => bytesToPrefixedHexString(tx.serialize())) ?? [] const withdrawalsArr = blockJson.withdrawals ? { withdrawals: blockJson.withdrawals } : {} const executionPayload: ExecutionPayload = { @@ -179,7 +189,7 @@ export const blockToExecutionPayload = (block: Block, value: bigint) => { extraData: header.extraData!, baseFeePerGas: header.baseFeePerGas!, excessDataGas: header.excessDataGas, - blockHash: bufferToHex(block.hash()), + blockHash: bytesToPrefixedHexString(block.hash()), prevRandao: header.mixHash!, transactions, ...withdrawalsArr, @@ -190,14 +200,19 @@ export const blockToExecutionPayload = (block: Block, value: bigint) => { /** * Recursively finds parent blocks starting from the parentHash. */ -const recursivelyFindParents = async (vmHeadHash: Buffer, parentHash: Buffer, chain: Chain) => { - if (parentHash.equals(vmHeadHash) || parentHash.equals(Buffer.alloc(32))) { +const recursivelyFindParents = async ( + vmHeadHash: Uint8Array, + parentHash: Uint8Array, + chain: Chain +) => { + if (equalsBytes(parentHash, vmHeadHash) || equalsBytes(parentHash, new Uint8Array(32))) { return [] } + const parentBlocks = [] const block = await chain.getBlock(parentHash) parentBlocks.push(block) - while (!parentBlocks[parentBlocks.length - 1].hash().equals(parentHash)) { + while (!equalsBytes(parentBlocks[parentBlocks.length - 1].hash(), parentHash)) { const block: Block = await chain.getBlock( parentBlocks[parentBlocks.length - 1].header.parentHash ) @@ -209,19 +224,19 @@ const recursivelyFindParents = async (vmHeadHash: Buffer, parentHash: Buffer, ch /** * Returns the block hash as a 0x-prefixed hex string if found valid in the blockchain, otherwise returns null. */ -const validHash = async (hash: Buffer, chain: Chain): Promise => { +const validHash = async (hash: Uint8Array, chain: Chain): Promise => { try { await chain.getBlock(hash) } catch (error: any) { return null } - return bufferToHex(hash) + return bytesToPrefixedHexString(hash) } /** * Returns the block hash as a 0x-prefixed hex string if found valid in the blockchain, otherwise returns null. */ -const validBlock = async (hash: Buffer, chain: Chain): Promise => { +const validBlock = async (hash: Uint8Array, chain: Chain): Promise => { try { return await chain.getBlock(hash) } catch (error: any) { @@ -272,12 +287,12 @@ const assembleBlock = async ( const txs = [] for (const [index, serializedTx] of transactions.entries()) { try { - const tx = TransactionFactory.fromSerializedData(toBuffer(serializedTx), { common }) + const tx = TransactionFactory.fromSerializedData(hexStringToBytes(serializedTx), { common }) txs.push(tx) } catch (error) { const validationError = `Invalid tx at index ${index}: ${error}` config.logger.error(validationError) - const latestValidHash = await validHash(toBuffer(payload.parentHash), chain) + const latestValidHash = await validHash(hexStringToBytes(payload.parentHash), chain) const response = { status: Status.INVALID, latestValidHash, validationError } return { error: response } } @@ -302,19 +317,19 @@ const assembleBlock = async ( // correctly set to the correct hf block = Block.fromBlockData({ header, transactions: txs, withdrawals }, { common }) // Verify blockHash matches payload - if (!block.hash().equals(toBuffer(payload.blockHash))) { + if (!equalsBytes(block.hash(), hexStringToBytes(payload.blockHash))) { const validationError = `Invalid blockHash, expected: ${ payload.blockHash - }, received: ${bufferToHex(block.hash())}` + }, received: ${bytesToPrefixedHexString(block.hash())}` config.logger.debug(validationError) - const latestValidHash = null + const latestValidHash = await validHash(toBytes(header.parentHash), chain) const response = { status: Status.INVALID_BLOCK_HASH, latestValidHash, validationError } return { error: response } } } catch (error) { const validationError = `Error verifying block during init: ${error}` config.logger.debug(validationError) - const latestValidHash = await validHash(toBuffer(header.parentHash), chain) + const latestValidHash = await validHash(toBytes(header.parentHash), chain) const response = { status: Status.INVALID, latestValidHash, validationError } return { error: response } } @@ -323,7 +338,7 @@ const assembleBlock = async ( } const getPayloadBody = (block: Block): ExecutionPayloadBodyV1 => { - const transactions = block.transactions.map((tx) => bufferToHex(tx.serialize())) + const transactions = block.transactions.map((tx) => bytesToPrefixedHexString(tx.serialize())) const withdrawals = block.withdrawals?.map((wt) => wt.toJSON()) ?? null return { @@ -514,7 +529,7 @@ export class Engine { if (!response) { const validationError = `Error assembling block during init` this.config.logger.debug(validationError) - const latestValidHash = await validHash(toBuffer(payload.parentHash), this.chain) + const latestValidHash = await validHash(hexStringToBytes(payload.parentHash), this.chain) response = { status: Status.INVALID, latestValidHash, validationError } } return response @@ -538,7 +553,7 @@ export class Engine { // is pow block which this client would like to mint and attempt proposing it const optimisticLookup = await this.service.beaconSync?.extendChain(block) - const blockExists = await validBlock(toBuffer(blockHash), this.chain) + const blockExists = await validBlock(hexStringToBytes(blockHash), this.chain) if (blockExists) { const isBlockExecuted = await this.vm.stateManager.hasStateRoot(blockExists.header.stateRoot) if (isBlockExecuted) { @@ -552,14 +567,14 @@ export class Engine { } try { - const parent = await this.chain.getBlock(toBuffer(parentHash)) + const parent = await this.chain.getBlock(hexStringToBytes(parentHash)) if (!parent._common.gteHardfork(Hardfork.Merge)) { const validTerminalBlock = await validateTerminalBlock(parent, this.chain) if (!validTerminalBlock) { const response = { status: Status.INVALID, validationError: null, - latestValidHash: bufferToHex(zeros(32)), + latestValidHash: bytesToPrefixedHexString(zeros(32)), } return response } @@ -575,7 +590,7 @@ export class Engine { optimisticLookup === true ? Status.SYNCING : Status.ACCEPTED if (status === Status.ACCEPTED) { // Stash the block for a potential forced forkchoice update to it later. - this.remoteBlocks.set(block.hash().toString('hex'), block) + this.remoteBlocks.set(bytesToHex(block.hash()), block) } const response = { status, validationError: null, latestValidHash: null } return response @@ -620,11 +635,11 @@ export class Engine { return response } - this.remoteBlocks.set(block.hash().toString('hex'), block) + this.remoteBlocks.set(bytesToHex(block.hash()), block) const response = { status: Status.VALID, - latestValidHash: bufferToHex(block.hash()), + latestValidHash: bytesToPrefixedHexString(block.hash()), validationError: null, } return response @@ -723,10 +738,10 @@ export class Engine { const { headBlockHash, finalizedBlockHash, safeBlockHash } = params[0] const payloadAttributes = params[1] - const safe = toBuffer(safeBlockHash) - const finalized = toBuffer(finalizedBlockHash) + const safe = toBytes(safeBlockHash) + const finalized = toBytes(finalizedBlockHash) - if (!finalized.equals(zeroBlockHash) && safe.equals(zeroBlockHash)) { + if (!equalsBytes(finalized, zeroBlockHash) && equalsBytes(safe, zeroBlockHash)) { throw { code: INVALID_PARAMS, message: 'safe block can not be zero if finalized is not zero', @@ -749,10 +764,10 @@ export class Engine { */ let headBlock: Block | undefined try { - headBlock = await this.chain.getBlock(toBuffer(headBlockHash)) + headBlock = await this.chain.getBlock(toBytes(headBlockHash)) } catch (error) { headBlock = - (await this.service.beaconSync?.skeleton.getBlockByHash(toBuffer(headBlockHash))) ?? + (await this.service.beaconSync?.skeleton.getBlockByHash(toBytes(headBlockHash))) ?? this.remoteBlocks.get(headBlockHash.slice(2)) if (headBlock === undefined) { this.config.logger.debug(`Forkchoice requested unknown head hash=${short(headBlockHash)}`) @@ -796,7 +811,7 @@ export class Engine { payloadStatus: { status: Status.INVALID, validationError: null, - latestValidHash: bufferToHex(zeros(32)), + latestValidHash: bytesToHex(zeros(32)), }, payloadId: null, } @@ -822,8 +837,8 @@ export class Engine { */ let safeBlock, finalizedBlock - if (!safe.equals(zeroBlockHash)) { - if (safe.equals(headBlock.hash())) { + if (!equalsBytes(safe, zeroBlockHash)) { + if (equalsBytes(safe, headBlock.hash())) { safeBlock = headBlock } else { try { @@ -841,7 +856,7 @@ export class Engine { safeBlock = undefined } - if (!finalized.equals(zeroBlockHash)) { + if (!equalsBytes(finalized, zeroBlockHash)) { try { // Right now only check if the block is available, canonicality check is done // in setHead after chain.putBlocks so as to reflect latest canonical chain @@ -857,7 +872,7 @@ export class Engine { } const vmHeadHash = this.chain.headers.latest!.hash() - if (!vmHeadHash.equals(headBlock.hash())) { + if (!equalsBytes(vmHeadHash, headBlock.hash())) { let parentBlocks: Block[] = [] if (this.chain.headers.latest && this.chain.headers.latest.number < headBlock.header.number) { try { @@ -923,7 +938,7 @@ export class Engine { ) const latestValidHash = await validHash(headBlock.hash(), this.chain) const payloadStatus = { status: Status.VALID, latestValidHash, validationError: null } - const response = { payloadStatus, payloadId: bufferToHex(payloadId), headBlock } + const response = { payloadStatus, payloadId: bytesToPrefixedHexString(payloadId), headBlock } return response } @@ -979,7 +994,7 @@ export class Engine { * @returns Instance of {@link ExecutionPayloadV1} or an error */ private async getPayload(params: [Bytes8]) { - const payloadId = toBuffer(params[0]) + const payloadId = hexStringToBytes(params[0]) try { const built = await this.pendingBlock.build(payloadId) if (!built) { @@ -1057,8 +1072,8 @@ export class Engine { return { blockHash: bundle.blockHash, - kzgs: bundle.kzgCommitments.map((commitment) => '0x' + commitment.toString('hex')), - blobs: bundle.blobs.map((blob) => '0x' + blob.toString('hex')), + kzgs: bundle.kzgCommitments.map(bytesToPrefixedHexString), + blobs: bundle.blobs.map(bytesToPrefixedHexString), } } @@ -1085,7 +1100,7 @@ export class Engine { message: 'More than 32 execution payload bodies requested', } } - const hashes = params[0].map((hash) => toBuffer(hash)) + const hashes = params[0].map(hexStringToBytes) const blocks: (ExecutionPayloadBodyV1 | null)[] = [] for (const hash of hashes) { try { diff --git a/packages/client/lib/rpc/modules/eth.ts b/packages/client/lib/rpc/modules/eth.ts index 0ad46d3f0a4..a8b16d7cde9 100644 --- a/packages/client/lib/rpc/modules/eth.ts +++ b/packages/client/lib/rpc/modules/eth.ts @@ -4,11 +4,12 @@ import { Address, TypeOutput, bigIntToHex, - bufferToHex, + bytesToPrefixedHexString, + hexStringToBytes, intToHex, setLengthLeft, - toBuffer, toType, + utf8ToBytes, } from '@ethereumjs/util' import { INTERNAL_ERROR, INVALID_PARAMS, PARSE_ERROR } from '../error-code' @@ -83,7 +84,7 @@ const jsonRpcBlock = async ( const json = block.toJSON() const header = json!.header! const transactions = block.transactions.map((tx, txIndex) => - includeTransactions ? jsonRpcTx(tx, block, txIndex) : bufferToHex(tx.hash()) + includeTransactions ? jsonRpcTx(tx, block, txIndex) : bytesToPrefixedHexString(tx.hash()) ) const withdrawalsAttr = header.withdrawalsRoot !== undefined @@ -95,7 +96,7 @@ const jsonRpcBlock = async ( const td = await chain.getTd(block.hash(), block.header.number) return { number: header.number!, - hash: bufferToHex(block.hash()), + hash: bytesToPrefixedHexString(block.hash()), parentHash: header.parentHash!, mixHash: header.mixHash, nonce: header.nonce!, @@ -108,12 +109,12 @@ const jsonRpcBlock = async ( difficulty: header.difficulty!, totalDifficulty: bigIntToHex(td), extraData: header.extraData!, - size: intToHex(Buffer.byteLength(JSON.stringify(json))), + size: intToHex(utf8ToBytes(JSON.stringify(json)).byteLength), gasLimit: header.gasLimit!, gasUsed: header.gasUsed!, timestamp: header.timestamp!, transactions, - uncles: block.uncleHeaders.map((uh) => bufferToHex(uh.hash())), + uncles: block.uncleHeaders.map((uh) => bytesToPrefixedHexString(uh.hash())), baseFeePerGas: header.baseFeePerGas, ...withdrawalsAttr, excessDataGas: header.excessDataGas, @@ -133,12 +134,12 @@ const jsonRpcLog = async ( removed: false, // TODO implement logIndex: logIndex !== undefined ? intToHex(logIndex) : null, transactionIndex: txIndex !== undefined ? intToHex(txIndex) : null, - transactionHash: tx ? bufferToHex(tx.hash()) : null, - blockHash: block ? bufferToHex(block.hash()) : null, + transactionHash: tx ? bytesToPrefixedHexString(tx.hash()) : null, + blockHash: block ? bytesToPrefixedHexString(block.hash()) : null, blockNumber: block ? bigIntToHex(block.header.number) : null, - address: bufferToHex(log[0]), - topics: log[1].map((t) => bufferToHex(t as Buffer)), - data: bufferToHex(log[2]), + address: bytesToPrefixedHexString(log[0]), + topics: log[1].map(bytesToPrefixedHexString), + data: bytesToPrefixedHexString(log[2]), }) /** @@ -154,9 +155,9 @@ const jsonRpcReceipt = async ( logIndex: number, contractAddress?: Address ): Promise => ({ - transactionHash: bufferToHex(tx.hash()), + transactionHash: bytesToPrefixedHexString(tx.hash()), transactionIndex: intToHex(txIndex), - blockHash: bufferToHex(block.hash()), + blockHash: bytesToPrefixedHexString(block.hash()), blockNumber: bigIntToHex(block.header.number), from: tx.getSenderAddress().toString(), to: tx.to?.toString() ?? null, @@ -167,13 +168,15 @@ const jsonRpcReceipt = async ( logs: await Promise.all( receipt.logs.map((l, i) => jsonRpcLog(l, block, tx, txIndex, logIndex + i)) ), - logsBloom: bufferToHex(receipt.bitvector), - root: Buffer.isBuffer((receipt as PreByzantiumTxReceipt).stateRoot) - ? bufferToHex((receipt as PreByzantiumTxReceipt).stateRoot) - : undefined, - status: Buffer.isBuffer((receipt as PostByzantiumTxReceipt).status) - ? intToHex((receipt as PostByzantiumTxReceipt).status) - : undefined, + logsBloom: bytesToPrefixedHexString(receipt.bitvector), + root: + (receipt as PreByzantiumTxReceipt).stateRoot instanceof Uint8Array + ? bytesToPrefixedHexString((receipt as PreByzantiumTxReceipt).stateRoot) + : undefined, + status: + ((receipt as PostByzantiumTxReceipt).status as unknown) instanceof Uint8Array + ? intToHex((receipt as PostByzantiumTxReceipt).status) + : undefined, }) /** @@ -401,10 +404,10 @@ export class Eth { gasLimit: toType(gasLimit, TypeOutput.BigInt), gasPrice: toType(gasPrice, TypeOutput.BigInt), value: toType(value, TypeOutput.BigInt), - data: data !== undefined ? toBuffer(data) : undefined, + data: data !== undefined ? hexStringToBytes(data) : undefined, } const { execResult } = await vm.evm.runCall(runCallOpts) - return bufferToHex(execResult.returnValue) + return bytesToPrefixedHexString(execResult.returnValue) } catch (error: any) { throw { code: INTERNAL_ERROR, @@ -527,7 +530,7 @@ export class Eth { const [blockHash, includeTransactions] = params try { - const block = await this._chain.getBlock(toBuffer(blockHash)) + const block = await this._chain.getBlock(hexStringToBytes(blockHash)) return await jsonRpcBlock(block, this._chain, includeTransactions) } catch (error) { throw { @@ -556,7 +559,7 @@ export class Eth { async getBlockTransactionCountByHash(params: [string]) { const [blockHash] = params try { - const block = await this._chain.getBlock(toBuffer(blockHash)) + const block = await this._chain.getBlock(hexStringToBytes(blockHash)) return intToHex(block.transactions.length) } catch (error) { throw { @@ -585,7 +588,7 @@ export class Eth { const address = Address.fromString(addressHex) const code = await vm.stateManager.getContractCode(address) - return bufferToHex(code) + return bytesToPrefixedHexString(code) } /** @@ -608,11 +611,11 @@ export class Eth { const address = Address.fromString(addressHex) const storageTrie = await (vm.stateManager as any)._getStorageTrie(address) - const position = setLengthLeft(toBuffer(positionHex), 32) + const position = setLengthLeft(hexStringToBytes(positionHex), 32) const storage = await storageTrie.get(position) return storage !== null && storage !== undefined - ? bufferToHex( - setLengthLeft(Buffer.from(RLP.decode(Uint8Array.from(storage)) as Uint8Array), 32) + ? bytesToPrefixedHexString( + setLengthLeft(RLP.decode(Uint8Array.from(storage)) as Uint8Array, 32) ) : '0x' } @@ -627,7 +630,7 @@ export class Eth { try { const [blockHash, txIndexHex] = params const txIndex = parseInt(txIndexHex, 16) - const block = await this._chain.getBlock(toBuffer(blockHash)) + const block = await this._chain.getBlock(hexStringToBytes(blockHash)) if (block.transactions.length <= txIndex) { return null } @@ -652,7 +655,7 @@ export class Eth { try { if (!this.receiptsManager) throw new Error('missing receiptsManager') - const result = await this.receiptsManager.getReceiptByTxHash(toBuffer(txHash)) + const result = await this.receiptsManager.getReceiptByTxHash(hexStringToBytes(txHash)) if (!result) return null const [_receipt, blockHash, txIndex] = result const block = await this._chain.getBlock(blockHash) @@ -732,7 +735,7 @@ export class Eth { try { if (!this.receiptsManager) throw new Error('missing receiptsManager') - const result = await this.receiptsManager.getReceiptByTxHash(toBuffer(txHash)) + const result = await this.receiptsManager.getReceiptByTxHash(hexStringToBytes(txHash)) if (!result) return null const [receipt, blockHash, txIndex, logIndex] = result const block = await this._chain.getBlock(blockHash) @@ -791,7 +794,7 @@ export class Eth { let from: Block, to: Block if (blockHash !== undefined) { try { - from = to = await this._chain.getBlock(toBuffer(blockHash)) + from = to = await this._chain.getBlock(hexStringToBytes(blockHash)) } catch (error: any) { throw { code: INVALID_PARAMS, @@ -842,17 +845,17 @@ export class Eth { if (t === null) { return null } else if (Array.isArray(t)) { - return t.map((x) => toBuffer(x)) + return t.map((x) => hexStringToBytes(x)) } else { - return toBuffer(t) + return hexStringToBytes(t) } }) let addrs if (address !== undefined) { if (Array.isArray(address)) { - addrs = address.map((a) => toBuffer(a)) + addrs = address.map((a) => hexStringToBytes(a)) } else { - addrs = [toBuffer(address)] + addrs = [hexStringToBytes(address)] } } const logs = await this.receiptsManager.getLogs(from, to, addrs, formattedTopics) @@ -896,7 +899,7 @@ export class Eth { let tx try { - const txBuf = toBuffer(serializedTx) + const txBuf = hexStringToBytes(serializedTx) if (txBuf[0] === 0x05) { // Blob Transactions sent over RPC are expected to be in Network Wrapper format tx = BlobEIP4844Transaction.fromSerializedBlobTxNetworkWrapper(txBuf, { common }) @@ -943,7 +946,7 @@ export class Eth { } txPool.sendTransactions([tx], peerPool.peers) - return bufferToHex(tx.hash()) + return bytesToPrefixedHexString(tx.hash()) } /** @@ -970,7 +973,7 @@ export class Eth { await vm.stateManager.setStateRoot(block.header.stateRoot) const address = Address.fromString(addressHex) - const slots = slotsHex.map((slotHex) => setLengthLeft(toBuffer(slotHex), 32)) + const slots = slotsHex.map((slotHex) => setLengthLeft(hexStringToBytes(slotHex), 32)) const proof = await vm.stateManager.getProof!(address, slots) return proof } diff --git a/packages/client/lib/rpc/modules/web3.ts b/packages/client/lib/rpc/modules/web3.ts index 6212413d860..31f5a031317 100644 --- a/packages/client/lib/rpc/modules/web3.ts +++ b/packages/client/lib/rpc/modules/web3.ts @@ -1,4 +1,4 @@ -import { addHexPrefix, toBuffer } from '@ethereumjs/util' +import { addHexPrefix, toBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { bytesToHex } from 'ethereum-cryptography/utils' @@ -42,7 +42,7 @@ export class Web3 { * @param params The data to convert into a SHA3 hash */ sha3(params: string[]) { - const hexEncodedDigest = addHexPrefix(bytesToHex(keccak256(toBuffer(params[0])))) + const hexEncodedDigest = addHexPrefix(bytesToHex(keccak256(toBytes(params[0])))) return hexEncodedDigest } } diff --git a/packages/client/lib/service/ethereumservice.ts b/packages/client/lib/service/ethereumservice.ts index a1c39ea525c..f7831564419 100644 --- a/packages/client/lib/service/ethereumservice.ts +++ b/packages/client/lib/service/ethereumservice.ts @@ -12,13 +12,13 @@ export interface EthereumServiceOptions extends ServiceOptions { chain: Chain /* Blockchain database */ - chainDB?: AbstractLevel + chainDB?: AbstractLevel /* State database */ - stateDB?: AbstractLevel + stateDB?: AbstractLevel /* Meta database (receipts, logs, indexes) */ - metaDB?: AbstractLevel + metaDB?: AbstractLevel /* Sync retry interval in ms (default: 8000) */ interval?: number diff --git a/packages/client/lib/service/fullethereumservice.ts b/packages/client/lib/service/fullethereumservice.ts index 838cce0d81c..0c48f37aeca 100644 --- a/packages/client/lib/service/fullethereumservice.ts +++ b/packages/client/lib/service/fullethereumservice.ts @@ -1,5 +1,6 @@ import { Hardfork } from '@ethereumjs/common' import { encodeReceipt } from '@ethereumjs/vm/dist/runBlock' +import { concatBytes } from 'ethereum-cryptography/utils' import { VMExecution } from '../execution' import { Miner } from '../miner' @@ -256,7 +257,7 @@ export class FullEthereumService extends EthereumService { case 'GetBlockBodies': { const { reqId, hashes } = message.data const blocks: Block[] = await Promise.all( - hashes.map((hash: Buffer) => this.chain.getBlock(hash)) + hashes.map((hash: Uint8Array) => this.chain.getBlock(hash)) ) const bodies = blocks.map((block) => block.raw().slice(1)) peer.eth!.send('BlockBodies', { reqId, bodies }) @@ -309,8 +310,8 @@ export class FullEthereumService extends EthereumService { const blockReceipts = await receiptsManager.getReceipts(hash, true, true) if (blockReceipts === undefined) continue receipts.push(...blockReceipts) - const receiptsBuffer = Buffer.concat(receipts.map((r) => encodeReceipt(r, r.txType))) - receiptsSize += Buffer.byteLength(receiptsBuffer) + const receiptsBytes = concatBytes(...receipts.map((r) => encodeReceipt(r, r.txType))) + receiptsSize += receiptsBytes.byteLength // From spec: The recommended soft limit for Receipts responses is 2 MiB. if (receiptsSize >= 2097152) { break diff --git a/packages/client/lib/service/txpool.ts b/packages/client/lib/service/txpool.ts index 48fa899c9bd..475f779efbb 100644 --- a/packages/client/lib/service/txpool.ts +++ b/packages/client/lib/service/txpool.ts @@ -1,5 +1,5 @@ import { BlobEIP4844Transaction, Capability } from '@ethereumjs/tx' -import { Address, bufferToHex } from '@ethereumjs/util' +import { Address, bytesToHex, equalsBytes, hexStringToBytes } from '@ethereumjs/util' import Heap = require('qheap') import type { Config } from '../config' @@ -279,8 +279,8 @@ export class TxPool { // Replace pooled txs with the same nonce const existingTxn = inPool.find((poolObj) => poolObj.tx.nonce === tx.nonce) if (existingTxn) { - if (existingTxn.tx.hash().equals(tx.hash())) { - throw new Error(`${bufferToHex(tx.hash())}: this transaction is already in the TxPool`) + if (equalsBytes(existingTxn.tx.hash(), tx.hash())) { + throw new Error(`${bytesToHex(tx.hash())}: this transaction is already in the TxPool`) } this.validateTxGasBump(existingTxn.tx, tx) } @@ -327,7 +327,7 @@ export class TxPool { * @param isLocalTransaction if this is a local transaction (loosens some constraints) (default: false) */ async add(tx: TypedTransaction, isLocalTransaction: boolean = false) { - const hash: UnprefixedHash = tx.hash().toString('hex') + const hash: UnprefixedHash = bytesToHex(tx.hash()) const added = Date.now() const address: UnprefixedAddress = tx.getSenderAddress().toString().slice(2) try { @@ -353,10 +353,10 @@ export class TxPool { * @param txHashes * @returns Array with tx objects */ - getByHash(txHashes: Buffer[]): TypedTransaction[] { + getByHash(txHashes: Uint8Array[]): TypedTransaction[] { const found = [] for (const txHash of txHashes) { - const txHashStr = txHash.toString('hex') + const txHashStr = bytesToHex(txHash) const handled = this.handled.get(txHashStr) if (!handled) continue const inPool = this.pool.get(handled.address)?.filter((poolObj) => poolObj.hash === txHashStr) @@ -395,21 +395,21 @@ export class TxPool { * @param peer * @returns Array with txs which are new to the list */ - addToKnownByPeer(txHashes: Buffer[], peer: Peer): Buffer[] { + addToKnownByPeer(txHashes: Uint8Array[], peer: Peer): Uint8Array[] { // Make sure data structure is initialized if (!this.knownByPeer.has(peer.id)) { this.knownByPeer.set(peer.id, []) } - const newHashes: Buffer[] = [] + const newHashes: Uint8Array[] = [] for (const hash of txHashes) { const inSent = this.knownByPeer .get(peer.id)! - .filter((sentObject) => sentObject.hash === hash.toString('hex')).length + .filter((sentObject) => sentObject.hash === bytesToHex(hash)).length if (inSent === 0) { const added = Date.now() const add = { - hash: hash.toString('hex'), + hash: bytesToHex(hash), added, } this.knownByPeer.get(peer.id)!.push(add) @@ -428,7 +428,7 @@ export class TxPool { * @param txHashes Array with transactions to send * @param peers */ - async sendNewTxHashes(txHashes: Buffer[], peers: Peer[]) { + async sendNewTxHashes(txHashes: Uint8Array[], peers: Peer[]) { for (const peer of peers) { // Make sure data structure is initialized if (!this.knownByPeer.has(peer.id)) { @@ -464,8 +464,8 @@ export class TxPool { // This is used to avoid re-sending along pooledTxHashes // announcements/re-broadcasts const newHashes = this.addToKnownByPeer(hashes, peer) - const newHashesHex = newHashes.map((txHash) => txHash.toString('hex')) - const newTxs = txs.filter((tx) => newHashesHex.includes(tx.hash().toString('hex'))) + const newHashesHex = newHashes.map((txHash) => bytesToHex(txHash)) + const newTxs = txs.filter((tx) => newHashesHex.includes(bytesToHex(tx.hash()))) peer.eth?.request('Transactions', newTxs).catch((e) => { this.markFailedSends(peer, newHashes, e as Error) }) @@ -473,11 +473,11 @@ export class TxPool { } } - private markFailedSends(peer: Peer, failedHashes: Buffer[], e: Error): void { + private markFailedSends(peer: Peer, failedHashes: Uint8Array[], e: Error): void { for (const txHash of failedHashes) { const sendobject = this.knownByPeer .get(peer.id) - ?.filter((sendObject) => sendObject.hash === txHash.toString('hex'))[0] + ?.filter((sendObject) => sendObject.hash === bytesToHex(txHash))[0] if (sendobject) { sendobject.error = e } @@ -506,7 +506,7 @@ export class TxPool { newTxHashes.push(tx.hash()) } catch (error: any) { this.config.logger.debug( - `Error adding tx to TxPool: ${error.message} (tx hash: ${bufferToHex(tx.hash())})` + `Error adding tx to TxPool: ${error.message} (tx hash: ${bytesToHex(tx.hash())})` ) } } @@ -524,13 +524,13 @@ export class TxPool { * @param peer Announcing peer * @param peerPool Reference to the peer pool */ - async handleAnnouncedTxHashes(txHashes: Buffer[], peer: Peer, peerPool: PeerPool) { + async handleAnnouncedTxHashes(txHashes: Uint8Array[], peer: Peer, peerPool: PeerPool) { if (!this.running || txHashes.length === 0) return this.addToKnownByPeer(txHashes, peer) const reqHashes = [] for (const txHash of txHashes) { - const txHashStr: UnprefixedHash = txHash.toString('hex') + const txHashStr: UnprefixedHash = bytesToHex(txHash) if (this.pending.includes(txHashStr) || this.handled.has(txHashStr)) { continue } @@ -541,7 +541,7 @@ export class TxPool { this.config.logger.debug(`TxPool: received new tx hashes number=${reqHashes.length}`) - const reqHashesStr: UnprefixedHash[] = reqHashes.map((hash) => hash.toString('hex')) + const reqHashesStr: UnprefixedHash[] = reqHashes.map(bytesToHex) this.pending = this.pending.concat(reqHashesStr) this.config.logger.debug( `TxPool: requesting txs number=${reqHashes.length} pending=${this.pending.length}` @@ -565,7 +565,7 @@ export class TxPool { await this.add(tx) } catch (error: any) { this.config.logger.debug( - `Error adding tx to TxPool: ${error.message} (tx hash: ${bufferToHex(tx.hash())})` + `Error adding tx to TxPool: ${error.message} (tx hash: ${bytesToHex(tx.hash())})` ) } newTxHashes.push(tx.hash()) @@ -580,7 +580,7 @@ export class TxPool { if (!this.running) return for (const block of newBlocks) { for (const tx of block.transactions) { - const txHash: UnprefixedHash = tx.hash().toString('hex') + const txHash: UnprefixedHash = bytesToHex(tx.hash()) this.removeByHash(txHash) } } @@ -694,7 +694,7 @@ export class TxPool { .map((obj) => obj.tx) .sort((a, b) => Number(a.nonce - b.nonce)) // Check if the account nonce matches the lowest known tx nonce - const { nonce } = await vm.eei.getAccount(new Address(Buffer.from(address, 'hex'))) + const { nonce } = await vm.eei.getAccount(new Address(hexStringToBytes(address))) if (txsSortedByNonce[0].nonce !== nonce) { // Account nonce does not match the lowest known tx nonce, // therefore no txs from this address are currently executable diff --git a/packages/client/lib/sync/beaconsync.ts b/packages/client/lib/sync/beaconsync.ts index a1c727e32b2..c67705c06af 100644 --- a/packages/client/lib/sync/beaconsync.ts +++ b/packages/client/lib/sync/beaconsync.ts @@ -1,3 +1,5 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' + import { Event } from '../types' import { short } from '../util' @@ -76,8 +78,8 @@ export class BeaconSynchronizer extends Synchronizer { this.config.chainCommon.setHardforkByBlockNumber(number, td, timestamp) this.config.logger.info( - `Latest local block number=${Number(number)} td=${td} hash=${hash.toString( - 'hex' + `Latest local block number=${Number(number)} td=${td} hash=${bytesToHex( + hash )} hardfork=${this.config.chainCommon.hardfork()}` ) diff --git a/packages/client/lib/sync/fetcher/accountfetcher.ts b/packages/client/lib/sync/fetcher/accountfetcher.ts index 70995be5524..73d284ba0f2 100644 --- a/packages/client/lib/sync/fetcher/accountfetcher.ts +++ b/packages/client/lib/sync/fetcher/accountfetcher.ts @@ -2,10 +2,10 @@ import { Trie } from '@ethereumjs/trie' import { KECCAK256_RLP, accountBodyToRLP, - bigIntToBuffer, - bufArrToArr, - bufferToBigInt, - bufferToHex, + bigIntToBytes, + bytesToBigInt, + bytesToHex, + equalsBytes, setLengthLeft, } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' @@ -33,7 +33,7 @@ type AccountDataResponse = AccountData[] & { completed?: boolean } */ export interface AccountFetcherOptions extends FetcherOptions { /** Root hash of the account trie to serve */ - root: Buffer + root: Uint8Array /** The origin to start account fetcher from */ first: bigint @@ -57,13 +57,13 @@ export type FetcherDoneFlags = { storageFetcherDone: boolean accountFetcherDone: boolean eventBus?: EventBusType | undefined - stateRoot?: Buffer | undefined + stateRoot?: Uint8Array | undefined } export function snapFetchersCompleted( fetcherDoneFlags: FetcherDoneFlags, fetcherType: Object, - root?: Buffer, + root?: Uint8Array, eventBus?: EventBusType ) { switch (fetcherType) { @@ -78,10 +78,7 @@ export function snapFetchersCompleted( break } if (fetcherDoneFlags.accountFetcherDone && fetcherDoneFlags.storageFetcherDone) { - fetcherDoneFlags.eventBus!.emit( - Event.SYNC_SNAPSYNC_COMPLETE, - bufArrToArr(fetcherDoneFlags.stateRoot as Buffer) - ) + fetcherDoneFlags.eventBus!.emit(Event.SYNC_SNAPSYNC_COMPLETE, fetcherDoneFlags.stateRoot!) } } @@ -92,7 +89,7 @@ export class AccountFetcher extends Fetcher * The stateRoot for the fetcher which sorts of pin it to a snapshot. * This might eventually be removed as the snapshots are moving and not static */ - root: Buffer + root: Uint8Array /** The origin to start account fetcher from (including), by default starts from 0 (0x0000...) */ first: bigint @@ -154,19 +151,19 @@ export class AccountFetcher extends Fetcher } private async verifyRangeProof( - stateRoot: Buffer, - origin: Buffer, - { accounts, proof }: { accounts: AccountData[]; proof: Buffer[] } + stateRoot: Uint8Array, + origin: Uint8Array, + { accounts, proof }: { accounts: AccountData[]; proof: Uint8Array[] } ): Promise { this.debug( - `verifyRangeProof accounts:${accounts.length} first=${bufferToHex( + `verifyRangeProof accounts:${accounts.length} first=${bytesToHex( accounts[0].hash )} last=${short(accounts[accounts.length - 1].hash)}` ) for (let i = 0; i < accounts.length - 1; i++) { // ensure the range is monotonically increasing - if (accounts[i].hash.compare(accounts[i + 1].hash) === 1) { + if (bytesToBigInt(accounts[i].hash) > bytesToBigInt(accounts[i + 1].hash)) { throw Error( `Account hashes not monotonically increasing: ${i} ${accounts[i].hash} vs ${i + 1} ${ accounts[i + 1].hash @@ -182,28 +179,32 @@ export class AccountFetcher extends Fetcher return trie.verifyRangeProof(stateRoot, origin, keys[keys.length - 1], keys, values, proof) } - private getOrigin(job: Job): Buffer { + private getOrigin(job: Job): Uint8Array { const { task, partialResult } = job const { first } = task // Snap protocol will automatically pad it with 32 bytes left, so we don't need to worry const origin = partialResult - ? bigIntToBuffer(bufferToBigInt(partialResult[partialResult.length - 1].hash) + BigInt(1)) - : bigIntToBuffer(first) + ? bigIntToBytes(bytesToBigInt(partialResult[partialResult.length - 1].hash) + BigInt(1)) + : bigIntToBytes(first) return setLengthLeft(origin, 32) } - private getLimit(job: Job): Buffer { + private getLimit(job: Job): Uint8Array { const { task } = job const { first, count } = task - const limit = bigIntToBuffer(first + BigInt(count) - BigInt(1)) + const limit = bigIntToBytes(first + BigInt(count) - BigInt(1)) return setLengthLeft(limit, 32) } private isMissingRightRange( - limit: Buffer, - { accounts, proof: _proof }: { accounts: AccountData[]; proof: Buffer[] } + limit: Uint8Array, + { accounts, proof: _proof }: { accounts: AccountData[]; proof: Uint8Array[] } ): boolean { - if (accounts.length > 0 && accounts[accounts.length - 1]?.hash.compare(limit) >= 0) { + if ( + accounts.length > 0 && + accounts[accounts.length - 1] !== undefined && + bytesToBigInt(accounts[accounts.length - 1].hash) >= bytesToBigInt(limit) + ) { return false } else { // TODO: Check if there is a proof of missing limit in state @@ -237,7 +238,7 @@ export class AccountFetcher extends Fetcher if ( rangeResult.accounts.length === 0 || - limit.compare(bigIntToBuffer(BigInt(2) ** BigInt(256))) === 0 + equalsBytes(limit, bigIntToBytes(BigInt(2) ** BigInt(256))) === true ) { // TODO have to check proof of nonexistence -- as a shortcut for now, we can mark as completed if a proof is present if (rangeResult.proof.length > 0) { @@ -258,9 +259,9 @@ export class AccountFetcher extends Fetcher let completed: boolean if (isMissingRightRange && this.isMissingRightRange(limit, rangeResult)) { this.debug( - `Peer ${peerInfo} returned missing right range account=${rangeResult.accounts[ - rangeResult.accounts.length - 1 - ].hash.toString('hex')} limit=${limit.toString('hex')}` + `Peer ${peerInfo} returned missing right range account=${bytesToHex( + rangeResult.accounts[rangeResult.accounts.length - 1].hash + )} limit=${bytesToHex(limit)}` ) completed = false } else { @@ -345,7 +346,7 @@ export class AccountFetcher extends Fetcher tasks(first = this.first, count = this.count, maxTasks = this.config.maxFetcherJobs): JobTask[] { const max = this.config.maxAccountRange const tasks: JobTask[] = [] - let debugStr = `origin=${short(setLengthLeft(bigIntToBuffer(first), 32))}` + let debugStr = `origin=${short(setLengthLeft(bigIntToBytes(first), 32))}` let pushedCount = BigInt(0) const startedWith = first @@ -370,7 +371,7 @@ export class AccountFetcher extends Fetcher } debugStr += ` limit=${short( - setLengthLeft(bigIntToBuffer(startedWith + pushedCount - BigInt(1)), 32) + setLengthLeft(bigIntToBytes(startedWith + pushedCount - BigInt(1)), 32) )}` this.debug(`Created new tasks num=${tasks.length} ${debugStr}`) return tasks diff --git a/packages/client/lib/sync/fetcher/blockfetcher.ts b/packages/client/lib/sync/fetcher/blockfetcher.ts index 14141eadcde..5b7cdba2bc2 100644 --- a/packages/client/lib/sync/fetcher/blockfetcher.ts +++ b/packages/client/lib/sync/fetcher/blockfetcher.ts @@ -1,5 +1,5 @@ import { Block } from '@ethereumjs/block' -import { KECCAK256_RLP, KECCAK256_RLP_ARRAY } from '@ethereumjs/util' +import { KECCAK256_RLP, KECCAK256_RLP_ARRAY, equalsBytes } from '@ethereumjs/util' import { Event } from '../../types' @@ -8,7 +8,7 @@ import { BlockFetcherBase } from './blockfetcherbase' import type { Peer } from '../../net/peer' import type { BlockFetcherOptions, JobTask } from './blockfetcherbase' import type { Job } from './types' -import type { BlockBuffer } from '@ethereumjs/block' +import type { BlockBytes } from '@ethereumjs/block' /** * Implements an eth/66 based block fetcher @@ -69,10 +69,10 @@ export class BlockFetcher extends BlockFetcherBase { for (const [i, [txsData, unclesData, withdrawalsData]] of bodies.entries()) { const header = headers[i] if ( - (!header.transactionsTrie.equals(KECCAK256_RLP) && txsData.length === 0) || - (!header.uncleHash.equals(KECCAK256_RLP_ARRAY) && unclesData.length === 0) || + (!equalsBytes(header.transactionsTrie, KECCAK256_RLP) && txsData.length === 0) || + (!equalsBytes(header.uncleHash, KECCAK256_RLP_ARRAY) && unclesData.length === 0) || (header.withdrawalsRoot !== undefined && - !header.withdrawalsRoot.equals(KECCAK256_RLP) && + !equalsBytes(header.withdrawalsRoot, KECCAK256_RLP) && (withdrawalsData?.length ?? 0) === 0) ) { this.debug( @@ -80,7 +80,7 @@ export class BlockFetcher extends BlockFetcherBase { ) return [] } - const values: BlockBuffer = [headers[i].raw(), txsData, unclesData] + const values: BlockBytes = [headers[i].raw(), txsData, unclesData] if (withdrawalsData !== undefined) { values.push(withdrawalsData) } diff --git a/packages/client/lib/sync/fetcher/storagefetcher.ts b/packages/client/lib/sync/fetcher/storagefetcher.ts index 512ed0fd54a..672c1251449 100644 --- a/packages/client/lib/sync/fetcher/storagefetcher.ts +++ b/packages/client/lib/sync/fetcher/storagefetcher.ts @@ -1,9 +1,10 @@ import { Trie } from '@ethereumjs/trie' import { - bigIntToBuffer, + bigIntToBytes, bigIntToHex, - bufferToBigInt, - bufferToHex, + bytesToBigInt, + bytesToHex, + equalsBytes, setLengthLeft, } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' @@ -24,8 +25,8 @@ const TOTAL_RANGE_END = BigInt(2) ** BigInt(256) - BigInt(1) type StorageDataResponse = StorageData[][] & { completed?: boolean } export type StorageRequest = { - accountHash: Buffer - storageRoot: Buffer + accountHash: Uint8Array + storageRoot: Uint8Array first: bigint count: bigint } @@ -36,7 +37,7 @@ export type StorageRequest = { */ export interface StorageFetcherOptions extends FetcherOptions { /** Root hash of the account trie to serve */ - root: Buffer + root: Uint8Array /** Storage requests to fetch */ storageRequests?: StorageRequest[] @@ -64,7 +65,7 @@ export class StorageFetcher extends Fetcher { try { this.debug( @@ -115,7 +116,7 @@ export class StorageFetcher extends Fetcher bytesToBigInt(slots[i + 1].hash)) { throw Error( `Account hashes not monotonically increasing: ${i} ${slots[i].hash} vs ${i + 1} ${ slots[i + 1].hash @@ -141,12 +142,12 @@ export class StorageFetcher extends Fetcher { + private async verifySlots(slots: StorageData[], root: Uint8Array): Promise { try { this.debug(`verify ${slots.length} slots`) for (let i = 0; i < slots.length - 1; i++) { // ensure the range is monotonically increasing - if (slots[i].hash.compare(slots[i + 1].hash) === 1) { + if (bytesToBigInt(slots[i].hash) > bytesToBigInt(slots[i + 1].hash)) { throw Error( `Account hashes not monotonically increasing: ${i} ${slots[i].hash} vs ${i + 1} ${ slots[i + 1].hash @@ -164,7 +165,7 @@ export class StorageFetcher extends Fetcher): Buffer { + private getOrigin(job: Job): Uint8Array { const { task, partialResult } = job if (task.storageRequests.length > 1 || task.storageRequests[0].first === BigInt(0)) { // peer does not respect origin or limit for multi-account storage fetch - return setLengthLeft(bigIntToBuffer(BigInt(0)), 32) + return setLengthLeft(bigIntToBytes(BigInt(0)), 32) } const { first } = task.storageRequests[0]! let origin = undefined if (partialResult) { const lastSlotArray = partialResult[partialResult.length - 1] const lastSlot = lastSlotArray[lastSlotArray.length - 1] - origin = bigIntToBuffer(bufferToBigInt(lastSlot.hash) + BigInt(1)) + origin = bigIntToBytes(bytesToBigInt(lastSlot.hash) + BigInt(1)) } else { - origin = bigIntToBuffer(first + BigInt(1)) + origin = bigIntToBytes(first + BigInt(1)) } return setLengthLeft(origin, 32) } - private getLimit(job: Job): Buffer { + private getLimit(job: Job): Uint8Array { const { task } = job if (task.storageRequests.length > 1) { // peer does not respect origin or limit for multi-account storage fetch - return setLengthLeft(bigIntToBuffer(TOTAL_RANGE_END), 32) + return setLengthLeft(bigIntToBytes(TOTAL_RANGE_END), 32) } const { first, count } = task.storageRequests[0] - const limit = bigIntToBuffer((first + (BigInt(count as any) as any)) as any) + const limit = bigIntToBytes((first + (BigInt(count as any) as any)) as any) return setLengthLeft(limit, 32) } private isMissingRightRange( - limit: Buffer, - { slots, proof: _proof }: { slots: StorageData[][]; proof: Buffer[] } + limit: Uint8Array, + { slots, proof: _proof }: { slots: StorageData[][]; proof: Uint8Array[] } ): boolean { - if (slots.length > 0 && slots[0][slots[0].length - 1]?.hash.compare(limit) >= 0) { + if ( + slots.length > 0 && + slots[0][slots[0].length - 1] !== undefined && + bytesToBigInt(slots[0][slots[0].length - 1].hash) >= bytesToBigInt(limit) + ) { return false } else { return true @@ -230,11 +235,11 @@ export class StorageFetcher extends Fetcher bufferToHex(req.accountHash))}` + `requested account hashes: ${task.storageRequests.map((req) => bytesToHex(req.accountHash))}` ) const rangeResult = await peer!.snap!.getStorageRanges({ @@ -308,9 +313,9 @@ export class StorageFetcher extends Fetcher { const accountHash = result.requests[i].accountHash const storageTrie = - this.accountToStorageTrie.get(bufferToHex(accountHash)) ?? + this.accountToStorageTrie.get(bytesToHex(accountHash)) ?? new Trie({ useKeyHashing: false }) for (const slot of slotArray as any) { slotCount++ void storageTrie.put(slot.hash, slot.body) } - this.accountToStorageTrie.set(bufferToHex(accountHash), storageTrie) + this.accountToStorageTrie.set(bytesToHex(accountHash), storageTrie) }) this.debug(`Stored ${slotCount} slot(s)`) } catch (err) { @@ -462,7 +467,7 @@ export class StorageFetcher extends Fetcher= BigInt(max) && tasks.length < maxTasks) { const task = { @@ -512,7 +517,7 @@ export class StorageFetcher extends Fetcher knownBlock.hash.equals(blockHash))) { + if (knownBlocks.find((knownBlock) => equalsBytes(knownBlock.hash, blockHash))) { return true } knownBlocks.push({ hash: blockHash, added: Date.now() }) @@ -321,7 +322,11 @@ export class FullSynchronizer extends Synchronizer { // https://github.com/ethereum/devp2p/blob/master/caps/eth.md#block-propagation const numPeersToShareWith = Math.floor(Math.sqrt(this.pool.peers.length)) await this.sendNewBlock(block, this.pool.peers.slice(0, numPeersToShareWith)) - if (this.chain.blocks.latest?.hash().equals(block.header.parentHash) === true) { + const latestBlockHash = this.chain.blocks.latest?.hash() + if ( + latestBlockHash !== undefined && + equalsBytes(latestBlockHash, block.header.parentHash) === true + ) { // If new block is child of current chain tip, insert new block into chain await this.chain.putBlocks([block]) // Check if new sync target height can be set @@ -350,10 +355,10 @@ export class FullSynchronizer extends Synchronizer { * Chain was updated, new block hashes received * @param data new block hash announcements */ - handleNewBlockHashes(data: [Buffer, bigint][]) { + handleNewBlockHashes(data: [Uint8Array, bigint][]) { if (!data.length || !this.fetcher || this.fetcher.syncErrored) return let min = BigInt(-1) - let newSyncHeight: [Buffer, bigint] | undefined + let newSyncHeight: [Uint8Array, bigint] | undefined const blockNumberList: bigint[] = [] for (const value of data) { const blockNumber = value[1] @@ -392,7 +397,7 @@ export class FullSynchronizer extends Synchronizer { this.config.syncTargetHeight !== BigInt(0) && this.chain.blocks.height <= this.config.syncTargetHeight - BigInt(50) this.execution.run(true, shouldRunOnlyBatched).catch((e) => { - this.config.logger.error(`Full sync execution trigger erored`, {}, e) + this.config.logger.error(`Full sync execution trigger errored`, {}, e) }) } diff --git a/packages/client/lib/sync/skeleton.ts b/packages/client/lib/sync/skeleton.ts index 34589ca4f40..94a7060d0fb 100644 --- a/packages/client/lib/sync/skeleton.ts +++ b/packages/client/lib/sync/skeleton.ts @@ -3,11 +3,12 @@ import { Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Lock, - arrToBufArr, - bigIntToBuffer, - bufferToBigInt, - bufferToInt, - intToBuffer, + bigIntToBytes, + bytesToBigInt, + bytesToInt, + equalsBytes, + intToBytes, + utf8ToBytes, zeros, } from '@ethereumjs/util' @@ -44,9 +45,9 @@ type SkeletonProgress = { type SkeletonSubchain = { head: bigint /** Block number of the newest header in the subchain */ tail: bigint /** Block number of the oldest header in the subchain */ - next: Buffer /** Block hash of the next oldest header in the subchain */ + next: Uint8Array /** Block hash of the next oldest header in the subchain */ } -type SkeletonSubchainRLP = [head: Buffer, tail: Buffer, next: Buffer] +type SkeletonSubchainRLP = [head: Uint8Array, tail: Uint8Array, next: Uint8Array] /** * errSyncReorged is an internal helper error to signal that the head chain of @@ -134,7 +135,7 @@ export class Skeleton extends MetaDBManager { if (tail === BigInt(0)) return true if (tail <= this.chain.blocks.height + BigInt(1)) { const nextBlock = await this.chain.getBlock(tail - BigInt(1)) - const linked = next.equals(nextBlock.hash()) + const linked = equalsBytes(next, nextBlock.hash()) if (linked && this.status.progress.subchains.length > 1) { // Remove all other subchains as no more relevant const junkedSubChains = this.status.progress.subchains.splice(1) @@ -170,7 +171,7 @@ export class Skeleton extends MetaDBManager { for (let newHead = head + BigInt(1); newHead <= target; newHead += BigInt(1)) { const newBlock = await this.getBlock(newHead, true) - if (newBlock === undefined || !newBlock.header.parentHash.equals(headBlock.hash())) { + if (newBlock === undefined || !equalsBytes(newBlock.header.parentHash, headBlock.hash())) { // Head can't be updated forward break } @@ -195,7 +196,7 @@ export class Skeleton extends MetaDBManager { // the outer loop to tear down the skeleton sync and restart it const { number } = head.header if (number === BigInt(0)) { - if (!this.chain.genesis.hash().equals(head.hash())) { + if (!equalsBytes(this.chain.genesis.hash(), head.hash())) { throw Error( `Invalid genesis setHead announcement number=${number} hash=${short( head.hash() @@ -234,7 +235,7 @@ export class Skeleton extends MetaDBManager { // Check if its duplicate announcement, if not trim the head and let the match run // post this if block const mayBeDupBlock = await this.getBlock(number) - if (mayBeDupBlock !== undefined && mayBeDupBlock.header.hash().equals(head.hash())) { + if (mayBeDupBlock !== undefined && equalsBytes(mayBeDupBlock.header.hash(), head.hash())) { this.config.logger.debug( `Skeleton duplicate announcement tail=${lastchain.tail} head=${ lastchain.head @@ -277,7 +278,7 @@ export class Skeleton extends MetaDBManager { } } const parent = await this.getBlock(number - BigInt(1)) - if (!parent || !parent.hash().equals(head.header.parentHash)) { + if (parent === undefined || !equalsBytes(parent.hash(), head.header.parentHash)) { if (force) { this.config.logger.warn( `Beacon chain forked ancestor=${parent?.header.number} hash=${short( @@ -329,7 +330,7 @@ export class Skeleton extends MetaDBManager { if ( subchain === undefined || parent === undefined || - !parent.hash().equals(head.header.parentHash) + !equalsBytes(parent.hash(), head.header.parentHash) ) { const s = { head: head.header.number, @@ -425,10 +426,11 @@ export class Skeleton extends MetaDBManager { } // If the old subchain is an extension of the new one, merge the two // and let the skeleton syncer restart (to clean internal state) + + const subChain1Head = await this.getBlock(this.status.progress.subchains[1].head) if ( - (await this.getBlock(this.status.progress.subchains[1].head)) - ?.hash() - .equals(this.status.progress.subchains[0].next) === true + subChain1Head !== undefined && + equalsBytes(subChain1Head.hash(), this.status.progress.subchains[0].next) === true ) { // only merge is we can integrate a big progress, as each merge leads // to disruption of the block fetcher to start a fresh @@ -482,7 +484,7 @@ export class Skeleton extends MetaDBManager { // from previous events especially if the previous subchains merge continue } else if (number === BigInt(0)) { - if (!this.chain.genesis.hash().equals(block.hash())) { + if (!equalsBytes(this.chain.genesis.hash(), block.hash())) { throw Error( `Skeleton pubBlocks with invalid genesis block number=${number} hash=${short( block.hash() @@ -495,7 +497,7 @@ export class Skeleton extends MetaDBManager { } // Extend subchain or create new segment if necessary - if (this.status.progress.subchains[0].next.equals(block.hash())) { + if (equalsBytes(this.status.progress.subchains[0].next, block.hash())) { await this.putBlock(block) this.pulled += BigInt(1) this.status.progress.subchains[0].tail = block.header.number @@ -573,13 +575,13 @@ export class Skeleton extends MetaDBManager { do { newTail = newTail + BigInt(this.config.skeletonFillCanonicalBackStep) tailBlock = await this.getBlock(newTail, true) - } while (!tailBlock && newTail <= head) + } while (tailBlock === undefined && newTail <= head) if (newTail > head) { newTail = head tailBlock = await this.getBlock(newTail, true) } - if (tailBlock && newTail) { + if (tailBlock !== undefined && newTail) { this.config.logger.info(`Backstepped skeleton head=${head} tail=${newTail}`) this.status.progress.subchains[0].tail = tailBlock.header.number this.status.progress.subchains[0].next = tailBlock.header.parentHash @@ -637,7 +639,7 @@ export class Skeleton extends MetaDBManager { // Get next block const number = canonicalHead + BigInt(1) const block = await this.getBlock(number) - if (!block) { + if (block === undefined) { // This shouldn't happen, but if it does because of some issues, we should back step // and fetch again this.config.logger.debug( @@ -714,13 +716,19 @@ export class Skeleton extends MetaDBManager { ) } - serialize({ hardfork, blockRLP }: { hardfork: Hardfork | string; blockRLP: Buffer }): Buffer { - const skeletonArr = [Buffer.from(hardfork), blockRLP] - return Buffer.from(RLP.encode(skeletonArr)) + serialize({ + hardfork, + blockRLP, + }: { + hardfork: Hardfork | string + blockRLP: Uint8Array + }): Uint8Array { + const skeletonArr = [utf8ToBytes(hardfork), blockRLP] + return RLP.encode(skeletonArr) } - deserialize(rlp: Buffer): { hardfork: Hardfork | string; blockRLP: Buffer } { - const [hardfork, blockRLP] = arrToBufArr(RLP.decode(Uint8Array.from(rlp))) as Buffer[] + deserialize(rlp: Uint8Array): { hardfork: Hardfork | string; blockRLP: Uint8Array } { + const [hardfork, blockRLP] = RLP.decode(rlp) as Uint8Array[] return { hardfork: hardfork.toString(), blockRLP } } @@ -730,11 +738,11 @@ export class Skeleton extends MetaDBManager { private async putBlock(block: Block): Promise { // Serialize the block with its hardfork so that its easy to load the block latter const rlp = this.serialize({ hardfork: block._common.hardfork(), blockRLP: block.serialize() }) - await this.put(DBKey.SkeletonBlock, bigIntToBuffer(block.header.number), rlp) + await this.put(DBKey.SkeletonBlock, bigIntToBytes(block.header.number), rlp) await this.put( DBKey.SkeletonBlockHashToNumber, block.hash(), - bigIntToBuffer(block.header.number) + bigIntToBytes(block.header.number) ) return true } @@ -744,7 +752,7 @@ export class Skeleton extends MetaDBManager { */ async getBlock(number: bigint, onlySkeleton = false): Promise { try { - const rlp = await this.get(DBKey.SkeletonBlock, bigIntToBuffer(number)) + const rlp = await this.get(DBKey.SkeletonBlock, bigIntToBytes(number)) const { hardfork, blockRLP } = this.deserialize(rlp!) const common = this.config.chainCommon.copy() common.setHardfork(hardfork) @@ -768,10 +776,10 @@ export class Skeleton extends MetaDBManager { /** * Gets a skeleton block from the db by hash */ - async getBlockByHash(hash: Buffer, onlySkeleton?: boolean): Promise { + async getBlockByHash(hash: Uint8Array, onlySkeleton?: boolean): Promise { const number = await this.get(DBKey.SkeletonBlockHashToNumber, hash) if (number) { - return this.getBlock(bufferToBigInt(number), onlySkeleton) + return this.getBlock(bytesToBigInt(number), onlySkeleton) } else { if (onlySkeleton === true || !this.status.linked) { return undefined @@ -790,7 +798,7 @@ export class Skeleton extends MetaDBManager { */ async deleteBlock(block: Block): Promise { try { - await this.delete(DBKey.SkeletonBlock, bigIntToBuffer(block.header.number)) + await this.delete(DBKey.SkeletonBlock, bigIntToBytes(block.header.number)) await this.delete(DBKey.SkeletonBlockHashToNumber, block.hash()) return true } catch (error: any) { @@ -814,7 +822,7 @@ export class Skeleton extends MetaDBManager { private async writeSyncStatus(): Promise { this.logSyncStatus('Writing') const encodedStatus = this.statusToRLP() - await this.put(DBKey.SkeletonStatus, Buffer.alloc(0), encodedStatus) + await this.put(DBKey.SkeletonStatus, new Uint8Array(0), encodedStatus) return true } @@ -822,7 +830,7 @@ export class Skeleton extends MetaDBManager { * Reads the {@link SkeletonStatus} from db */ private async getSyncStatus(): Promise { - const rawStatus = await this.get(DBKey.SkeletonStatus, Buffer.alloc(0)) + const rawStatus = await this.get(DBKey.SkeletonStatus, new Uint8Array(0)) if (!rawStatus) return const status = this.statusRLPtoObject(rawStatus) this.status = status @@ -832,27 +840,25 @@ export class Skeleton extends MetaDBManager { /** * Encodes a {@link SkeletonStatus} to RLP for saving to the db */ - private statusToRLP(): Buffer { + private statusToRLP(): Uint8Array { const subchains: SkeletonSubchainRLP[] = this.status.progress.subchains.map((subchain) => [ - bigIntToBuffer(subchain.head), - bigIntToBuffer(subchain.tail), + bigIntToBytes(subchain.head), + bigIntToBytes(subchain.tail), subchain.next, ]) - return Buffer.from( - RLP.encode([ - subchains, - // linked - intToBuffer(this.status.linked ? 1 : 0), - // canonocalHeadReset - intToBuffer(this.status.canonicalHeadReset ? 1 : 0), - ]) - ) + return RLP.encode([ + subchains, + // linked + intToBytes(this.status.linked ? 1 : 0), + // canonocalHeadReset + intToBytes(this.status.canonicalHeadReset ? 1 : 0), + ]) } /** * Decodes an RLP encoded {@link SkeletonStatus} */ - private statusRLPtoObject(serializedStatus: Buffer): SkeletonStatus { + private statusRLPtoObject(serializedStatus: Uint8Array): SkeletonStatus { const status: SkeletonStatus = { progress: { subchains: [] }, linked: false, @@ -860,17 +866,17 @@ export class Skeleton extends MetaDBManager { } const rawStatus = RLP.decode(serializedStatus) as unknown as [ SkeletonSubchainRLP[], - Buffer, - Buffer + Uint8Array, + Uint8Array ] const subchains: SkeletonSubchain[] = rawStatus[0].map((raw) => ({ - head: bufferToBigInt(raw[0]), - tail: bufferToBigInt(raw[1]), + head: bytesToBigInt(raw[0]), + tail: bytesToBigInt(raw[1]), next: raw[2], })) status.progress.subchains = subchains - status.linked = bufferToInt(rawStatus[1]) === 1 - status.canonicalHeadReset = bufferToInt(rawStatus[2]) === 1 + status.linked = bytesToInt(rawStatus[1]) === 1 + status.canonicalHeadReset = bytesToInt(rawStatus[2]) === 1 return status } } diff --git a/packages/client/lib/sync/snapsync.ts b/packages/client/lib/sync/snapsync.ts index 845e44ce71e..b0b2cd71189 100644 --- a/packages/client/lib/sync/snapsync.ts +++ b/packages/client/lib/sync/snapsync.ts @@ -1,4 +1,5 @@ import { DefaultStateManager } from '@ethereumjs/statemanager' +import { bytesToHex } from '@ethereumjs/util' import { Event } from '../types' @@ -123,9 +124,7 @@ export class SnapSynchronizer extends Synchronizer { // eslint-disable-next-line eqeqeq if (this.config.syncTargetHeight == null || this.config.syncTargetHeight < latest.number) { this.config.syncTargetHeight = height - this.config.logger.info( - `New sync target height=${height} hash=${latest.hash().toString('hex')}` - ) + this.config.logger.info(`New sync target height=${height} hash=${bytesToHex(latest.hash())}`) } // For convenient testing diff --git a/packages/client/lib/types.ts b/packages/client/lib/types.ts index bf56c3c84fe..051d16b7d83 100644 --- a/packages/client/lib/types.ts +++ b/packages/client/lib/types.ts @@ -86,7 +86,7 @@ export type EventBusType = EventBus & /** * Like types */ -export type Key = Buffer +export type Key = Uint8Array export type KeyLike = string | Key export type MultiaddrLike = string | string[] | Multiaddr | Multiaddr[] diff --git a/packages/client/lib/util/debug.ts b/packages/client/lib/util/debug.ts index 6428cc5772b..00d9702ed3b 100644 --- a/packages/client/lib/util/debug.ts +++ b/packages/client/lib/util/debug.ts @@ -1,3 +1,5 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' + import { DataDirectory } from '..' import type { VMExecution } from '../execution' @@ -35,17 +37,17 @@ const main = async () => { const common = new Common({ chain: '${execution.config.execCommon.chainName()}', hardfork: '${ execution.hardfork }' }) - const block = Block.fromRLPSerializedBlock(Buffer.from('${block - .serialize() - .toString('hex')}', 'hex'), { common }) + const block = Block.fromRLPSerializedBlock(hexStringToBytes('${bytesToHex( + block.serialize() + )}'), { common }) const stateDB = new Level('${execution.config.getDataDirectory(DataDirectory.State)}') const trie = new Trie({ db: stateDB, useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie, common }) // Ensure we run on the right root - stateManager.setStateRoot(Buffer.from('${( + stateManager.setStateRoot(hexStringToBytes('${bytesToHex( await execution.vm.stateManager.getStateRoot() - ).toString('hex')}', 'hex')) + )}')) const chainDB = new Level('${execution.config.getDataDirectory(DataDirectory.Chain)}') diff --git a/packages/client/lib/util/index.ts b/packages/client/lib/util/index.ts index f537c87f49f..755880a66b5 100644 --- a/packages/client/lib/util/index.ts +++ b/packages/client/lib/util/index.ts @@ -1,6 +1,7 @@ /** * @module util */ +import { bytesToPrefixedHexString } from '@ethereumjs/util' import { platform } from 'os' import { version as packageVersion } from '../../package.json' @@ -8,12 +9,12 @@ import { version as packageVersion } from '../../package.json' export * from './parse' export * from './rpc' -export function short(buf: Buffer | string): string { - if (buf === null || buf === undefined || buf === '') return '' - const bufStr = Buffer.isBuffer(buf) ? `0x${buf.toString('hex')}` : buf - let str = bufStr.substring(0, 6) + '…' - if (bufStr.length === 66) { - str += bufStr.substring(62) +export function short(bytes: Uint8Array | string): string { + if (bytes === null || bytes === undefined || bytes === '') return '' + const bytesString = bytes instanceof Uint8Array ? bytesToPrefixedHexString(bytes) : bytes + let str = bytesString.substring(0, 6) + '…' + if (bytesString.length === 66) { + str += bytesString.substring(62) } return str } diff --git a/packages/client/lib/util/metaDBManager.ts b/packages/client/lib/util/metaDBManager.ts index c56251acd57..c20feb68bd2 100644 --- a/packages/client/lib/util/metaDBManager.ts +++ b/packages/client/lib/util/metaDBManager.ts @@ -1,4 +1,4 @@ -import { intToBuffer } from '@ethereumjs/util' +import { concatBytes, intToBytes } from '@ethereumjs/util' import type { Chain } from '../blockchain' import type { Config } from '../config' @@ -29,7 +29,7 @@ export interface MetaDBManagerOptions { config: Config /* Meta database (receipts, logs, indexes) */ - metaDB: AbstractLevel + metaDB: AbstractLevel } /** @@ -38,7 +38,7 @@ export interface MetaDBManagerOptions { export class MetaDBManager { protected chain: Chain protected config: Config - private metaDB: AbstractLevel + private metaDB: AbstractLevel constructor(options: MetaDBManagerOptions) { this.chain = options.chain @@ -46,15 +46,15 @@ export class MetaDBManager { this.metaDB = options.metaDB } - private dbKey(type: DBKey, key: Buffer) { - return Buffer.concat([intToBuffer(type), key]) + private dbKey(type: DBKey, key: Uint8Array) { + return concatBytes(intToBytes(type), key) } - async put(type: DBKey, hash: Buffer, value: Buffer) { + async put(type: DBKey, hash: Uint8Array, value: Uint8Array) { await this.metaDB.put(this.dbKey(type, hash), value, encodingOpts) } - async get(type: DBKey, hash: Buffer): Promise { + async get(type: DBKey, hash: Uint8Array): Promise { try { return await this.metaDB.get(this.dbKey(type, hash), encodingOpts) } catch (error: any) { @@ -65,7 +65,7 @@ export class MetaDBManager { } } - async delete(type: DBKey, hash: Buffer) { + async delete(type: DBKey, hash: Uint8Array) { await this.metaDB.del(this.dbKey(type, hash), encodingOpts) } } diff --git a/packages/client/lib/util/parse.ts b/packages/client/lib/util/parse.ts index 8916168bddf..5694dcf97fe 100644 --- a/packages/client/lib/util/parse.ts +++ b/packages/client/lib/util/parse.ts @@ -1,3 +1,4 @@ +import { hexStringToBytes } from '@ethereumjs/util' import { Multiaddr, multiaddr } from 'multiaddr' import { URL } from 'url' @@ -83,12 +84,9 @@ export function parseTransports(transports: string[]) { } /** - * Returns Buffer from input hexadecimal string or Buffer - * @param input hexadecimal string or Buffer + * Returns Uint8Array from input hexadecimal string or Uint8Array + * @param input hexadecimal string or Uint8Array */ -export function parseKey(input: string | Buffer) { - if (Buffer.isBuffer(input)) { - return input - } - return Buffer.from(input, 'hex') +export function parseKey(input: string | Uint8Array): Uint8Array { + return input instanceof Uint8Array ? input : hexStringToBytes(input) } diff --git a/packages/client/lib/util/rpc.ts b/packages/client/lib/util/rpc.ts index e029ffc7931..54216b8c9b9 100644 --- a/packages/client/lib/util/rpc.ts +++ b/packages/client/lib/util/rpc.ts @@ -30,7 +30,7 @@ type CreateRPCServerListenerOpts = { withEngineMiddleware?: WithEngineMiddleware } type CreateWSServerOpts = CreateRPCServerListenerOpts & { httpServer?: HttpServer } -type WithEngineMiddleware = { jwtSecret: Buffer; unlessFn?: (req: IncomingMessage) => boolean } +type WithEngineMiddleware = { jwtSecret: Uint8Array; unlessFn?: (req: IncomingMessage) => boolean } export enum MethodConfig { WithEngine = 'withengine', @@ -150,7 +150,7 @@ export function createRPCServer( return { server, methods, namespaces } } -function checkHeaderAuth(req: any, jwtSecret: Buffer): void { +function checkHeaderAuth(req: any, jwtSecret: Uint8Array): void { const header = (req.headers['Authorization'] ?? req.headers['authorization']) as string if (!header) throw Error(`Missing auth header`) const token = header.trim().split(' ')[1] diff --git a/packages/client/test/blockchain/chain.spec.ts b/packages/client/test/blockchain/chain.spec.ts index 20283a089e6..e5a388f9db4 100644 --- a/packages/client/test/blockchain/chain.spec.ts +++ b/packages/client/test/blockchain/chain.spec.ts @@ -2,7 +2,7 @@ // needed for karma-typescript bundling import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' -import { Buffer } from 'buffer' // eslint-disable-line @typescript-eslint/no-unused-vars +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import * as util from 'util' // eslint-disable-line @typescript-eslint/no-unused-vars @@ -34,11 +34,11 @@ tape('[Chain]', (t) => { t.equal(chain.blocks.td.toString(10), '17179869184', 'get chain.blocks.td') t.equal(chain.blocks.height.toString(10), '0', 'get chain.blocks.height') t.equal( - chain.genesis.hash().toString('hex'), + bytesToHex(chain.genesis.hash()), 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', 'get chain.genesis' ) - t.ok(chain.genesis.hash().equals(chain.blocks.latest!.hash()), 'get chain.block.latest') + t.ok(equalsBytes(chain.genesis.hash(), chain.blocks.latest!.hash()), 'get chain.block.latest') await chain.close() t.end() }) diff --git a/packages/client/test/integration/fullethereumservice.spec.ts b/packages/client/test/integration/fullethereumservice.spec.ts index d9e11e4dce0..3f2996085a0 100644 --- a/packages/client/test/integration/fullethereumservice.spec.ts +++ b/packages/client/test/integration/fullethereumservice.spec.ts @@ -3,7 +3,7 @@ import { Blockchain } from '@ethereumjs/blockchain' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Account, toBuffer } from '@ethereumjs/util' +import { Account, bytesToHex, equalsBytes, hexStringToBytes, toBytes } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -51,12 +51,11 @@ tape('[Integration:FullEthereumService]', async (t) => { const [server, service] = await setup() const peer = await server.accept('peer0') const [reqId1, headers] = await peer.eth!.getBlockHeaders({ block: BigInt(1), max: 2 }) - const hash = Buffer.from( - 'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069', - 'hex' + const hash = hexStringToBytes( + 'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069' ) t.equal(reqId1, BigInt(1), 'handled GetBlockHeaders') - t.ok(headers![1].hash().equals(hash), 'handled GetBlockHeaders') + t.ok(equalsBytes(headers![1].hash(), hash), 'handled GetBlockHeaders') const res = await peer.eth!.getBlockBodies({ hashes: [hash] }) const [reqId2, bodies] = res t.equal(reqId2, BigInt(2), 'handled GetBlockBodies') @@ -89,7 +88,7 @@ tape('[Integration:FullEthereumService]', async (t) => { const txData = '0x02f901100180843b9aca00843b9aca008402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64' - const tx = FeeMarketEIP1559Transaction.fromSerializedTx(toBuffer(txData)) + const tx = FeeMarketEIP1559Transaction.fromSerializedTx(toBytes(txData)) await service.execution.vm.stateManager.putAccount( tx.getSenderAddress(), new Account(BigInt(0), BigInt('40000000000100000')) @@ -114,11 +113,7 @@ tape('[Integration:FullEthereumService]', async (t) => { Hardfork.London ) const [_, txs] = await peer.eth!.getPooledTransactions({ hashes: [tx.hash()] }) - t.equal( - txs[0].hash().toString('hex'), - tx.hash().toString('hex'), - 'handled GetPooledTransactions' - ) + t.ok(equalsBytes(txs[0].hash(), tx.hash()), 'handled GetPooledTransactions') peer.eth!.send('Transactions', [tx]) t.pass('handled Transactions') @@ -129,7 +124,7 @@ tape('[Integration:FullEthereumService]', async (t) => { const peer = await server.accept('peer0') const { headers } = await peer.les!.getBlockHeaders({ block: BigInt(1), max: 2 }) t.equals( - headers[1].hash().toString('hex'), + bytesToHex(headers[1].hash()), 'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069', 'handled GetBlockHeaders' ) diff --git a/packages/client/test/integration/merge.spec.ts b/packages/client/test/integration/merge.spec.ts index a2febee35a1..68722a4deee 100644 --- a/packages/client/test/integration/merge.spec.ts +++ b/packages/client/test/integration/merge.spec.ts @@ -7,7 +7,7 @@ import { ConsensusType, Hardfork, } from '@ethereumjs/common' -import { Address } from '@ethereumjs/util' +import { Address, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Chain } from '../../lib/blockchain' @@ -65,10 +65,10 @@ tape('[Integration:Merge]', async (t) => { }, { baseChain: ChainCommon.Ropsten, hardfork: Hardfork.London } ) - const accounts: [Address, Buffer][] = [ + const accounts: [Address, Uint8Array][] = [ [ - new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - Buffer.from('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', 'hex'), + new Address(hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + hexStringToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), ], ] async function minerSetup(common: Common): Promise<[MockServer, FullEthereumService]> { diff --git a/packages/client/test/integration/miner.spec.ts b/packages/client/test/integration/miner.spec.ts index 372fedf55c6..7bd5f118969 100644 --- a/packages/client/test/integration/miner.spec.ts +++ b/packages/client/test/integration/miner.spec.ts @@ -6,7 +6,7 @@ import { ConsensusType, Hardfork, } from '@ethereumjs/common' -import { Address } from '@ethereumjs/util' +import { Address, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Chain } from '../../lib/blockchain' @@ -42,10 +42,10 @@ tape('[Integration:Miner]', async (t) => { }, { baseChain: ChainCommon.Goerli, hardfork: Hardfork.London } ) - const accounts: [Address, Buffer][] = [ + const accounts: [Address, Uint8Array][] = [ [ - new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - Buffer.from('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', 'hex'), + new Address(hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + hexStringToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), ], ] async function minerSetup(): Promise<[MockServer, FullEthereumService]> { diff --git a/packages/client/test/integration/mocks/mockpeer.ts b/packages/client/test/integration/mocks/mockpeer.ts index 82d35a6b490..5cea1159b92 100644 --- a/packages/client/test/integration/mocks/mockpeer.ts +++ b/packages/client/test/integration/mocks/mockpeer.ts @@ -14,7 +14,7 @@ import type { RemoteStream } from './network' // TypeScript doesn't have support yet for ReturnType // with generic types, so this wrapper is used as a helper. -const wrapperPushable = () => pushable() +const wrapperPushable = () => pushable() export type Pushable = ReturnType interface MockPeerOptions extends PeerOptions { diff --git a/packages/client/test/miner/miner.spec.ts b/packages/client/test/miner/miner.spec.ts index 2c2ba5c7ae4..1c24e89bfe2 100644 --- a/packages/client/test/miner/miner.spec.ts +++ b/packages/client/test/miner/miner.spec.ts @@ -2,7 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' -import { Address } from '@ethereumjs/util' +import { Address, equalsBytes, hexStringToBytes } from '@ethereumjs/util' import { VmState } from '@ethereumjs/vm/dist/eei/vmState' import { AbstractLevel } from 'abstract-level' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -21,19 +21,13 @@ import type { CliqueConsensus } from '@ethereumjs/blockchain' import type { VM } from '@ethereumjs/vm' const A = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' - ), + address: new Address(hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexStringToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), } const B = { - address: new Address(Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex')), - privateKey: Buffer.from( - '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', - 'hex' - ), + address: new Address(hexStringToBytes('6f62d8382bf2587361db73ceca28be91b2acb6df')), + privateKey: hexStringToBytes('2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6'), } const setBalance = async (vm: VM, address: Address, balance: bigint) => { @@ -95,7 +89,7 @@ tape('[Miner]', async (t) => { const common = new Common({ chain: CommonChain.Rinkeby, hardfork: Hardfork.Berlin }) common.setMaxListeners(50) - const accounts: [Address, Buffer][] = [[A.address, A.privateKey]] + const accounts: [Address, Uint8Array][] = [[A.address, A.privateKey]] const config = new Config({ transports: [], accounts, mine: true, common }) config.events.setMaxListeners(50) @@ -263,7 +257,8 @@ tape('[Miner]', async (t) => { const msg = 'txs in block should be properly ordered by gasPrice and nonce' const expectedOrder = [txB01, txA01, txA02, txA03] for (const [index, tx] of expectedOrder.entries()) { - t.ok(blocks[0].transactions[index]?.hash().equals(tx.hash()), msg) + const txHash = blocks[0].transactions[index]?.hash() + t.ok(txHash !== undefined && equalsBytes(txHash, tx.hash()), msg) } miner.stop() txPool.stop() @@ -307,7 +302,8 @@ tape('[Miner]', async (t) => { const msg = 'txs in block should be properly ordered by gasPrice and nonce' const expectedOrder = [txB01, txA01, txA02, txA03] for (const [index, tx] of expectedOrder.entries()) { - t.ok(blocks[0].transactions[index]?.hash().equals(tx.hash()), msg) + const txHash = blocks[0].transactions[index]?.hash() + t.ok(txHash !== undefined && equalsBytes(txHash, tx.hash()), msg) } miner.stop() txPool.stop() @@ -452,7 +448,7 @@ tape('[Miner]', async (t) => { await setBalance(vm, A.address, BigInt('200000000000001')) // add many txs to slow assembling - let privateKey = Buffer.from(keccak256(Buffer.from(''))) + let privateKey = keccak256(new Uint8Array(0)) for (let i = 0; i < 1000; i++) { // In order not to pollute TxPool with too many txs from the same address // (or txs which are already known), keep generating a new address for each tx @@ -460,7 +456,7 @@ tape('[Miner]', async (t) => { await setBalance(vm, address, BigInt('200000000000001')) const tx = createTx({ address, privateKey }) await txPool.add(tx) - privateKey = Buffer.from(keccak256(privateKey)) + privateKey = keccak256(privateKey) } chain.putBlocks = () => { diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index 47ef4efb24c..cc8f56e820e 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -6,11 +6,18 @@ import { commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' -import { Account, Address, bufferToHex } from '@ethereumjs/util' +import { + Account, + Address, + bytesToHex, + bytesToPrefixedHexString, + equalsBytes, + hexStringToBytes, + randomBytes, +} from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' import { VmState } from '@ethereumjs/vm/dist/eei/vmState' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import * as tape from 'tape' import * as td from 'testdouble' @@ -23,19 +30,13 @@ import { mockBlockchain } from '../rpc/mockBlockchain' import type { TypedTransaction } from '@ethereumjs/tx' const A = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' - ), + address: new Address(hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexStringToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), } const B = { - address: new Address(Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex')), - privateKey: Buffer.from( - '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', - 'hex' - ), + address: new Address(hexStringToBytes('6f62d8382bf2587361db73ceca28be91b2acb6df')), + privateKey: hexStringToBytes('2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6'), } const setBalance = async (vm: VM, address: Address, balance: bigint) => { @@ -144,10 +145,10 @@ tape('[PendingBlock]', async (t) => { const parentBlock = await vm.blockchain.getCanonicalHeadBlock!() const payloadId = await pendingBlock.start(vm, parentBlock) t.equal(pendingBlock.pendingPayloads.size, 1, 'should set the pending payload') - const payload = pendingBlock.pendingPayloads.get(bufferToHex(payloadId)) + const payload = pendingBlock.pendingPayloads.get(bytesToPrefixedHexString(payloadId)) t.equal( (payload as any).transactions.filter( - (tx: TypedTransaction) => bufferToHex(tx.hash()) === bufferToHex(txA011.hash()) + (tx: TypedTransaction) => bytesToHex(tx.hash()) === bytesToHex(txA011.hash()) ).length, 1, 'txA011 should be in block' @@ -163,7 +164,7 @@ tape('[PendingBlock]', async (t) => { t.equal(block?.transactions.length, 2, 'should include txs from pool') t.equal( (payload as any).transactions.filter( - (tx: TypedTransaction) => bufferToHex(tx.hash()) === bufferToHex(txB011.hash()) + (tx: TypedTransaction) => bytesToHex(tx.hash()) === bytesToHex(txB011.hash()) ).length, 1, 'txB011 should be in block' @@ -274,11 +275,9 @@ tape('[PendingBlock]', async (t) => { const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const bufferedHashes = versionedHashes.map((el) => Buffer.from(el)) - const txA01 = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, maxFeePerDataGas: 100000000n, @@ -296,7 +295,8 @@ tape('[PendingBlock]', async (t) => { const parentBlock = await vm.blockchain.getCanonicalHeadBlock!() const payloadId = await pendingBlock.start(vm, parentBlock) await pendingBlock.build(payloadId) - st.ok(pendingBlock.blobBundles.get(bufferToHex(payloadId))?.blobs[0].equals(blobs[0])) + const pendingBlob = pendingBlock.blobBundles.get(bytesToPrefixedHexString(payloadId))?.blobs[0] + st.ok(pendingBlob !== undefined && equalsBytes(pendingBlob, blobs[0])) kzg.freeTrustedSetup() st.end() }) diff --git a/packages/client/test/net/protocol/ethprotocol.spec.ts b/packages/client/test/net/protocol/ethprotocol.spec.ts index 813daaf2adf..c66d7fe09f1 100644 --- a/packages/client/test/net/protocol/ethprotocol.spec.ts +++ b/packages/client/test/net/protocol/ethprotocol.spec.ts @@ -1,8 +1,7 @@ import { Block } from '@ethereumjs/block' import { Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction, TransactionFactory } from '@ethereumjs/tx' -import { bigIntToBuffer, bufferToBigInt } from '@ethereumjs/util' -import { randomBytes } from 'crypto' +import { bigIntToBytes, bytesToBigInt, hexStringToBytes, randomBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Chain } from '../../../lib/blockchain/chain' @@ -55,17 +54,17 @@ tape('[EthProtocol]', (t) => { t.deepEquals( p.encodeStatus(), { - networkId: Buffer.from('01', 'hex'), - td: Buffer.from('64', 'hex'), + networkId: hexStringToBytes('01'), + td: hexStringToBytes('64'), bestHash: '0xaa', genesisHash: '0xbb', - latestBlock: Buffer.from('0a', 'hex'), + latestBlock: hexStringToBytes('0a'), }, 'encode status' ) const status = p.decodeStatus({ networkId: [0x01], - td: Buffer.from('64', 'hex'), + td: hexStringToBytes('64'), bestHash: '0xaa', genesisHash: '0xbb', }) @@ -87,14 +86,14 @@ tape('[EthProtocol]', (t) => { const block = Block.fromBlockData({}, { common: config.chainCommon }) const res = p.decode(p.messages.filter((message) => message.name === 'NewBlock')[0], [ block.raw(), - bigIntToBuffer(td), + bigIntToBytes(td), ]) const res2 = p.encode(p.messages.filter((message) => message.name === 'NewBlock')[0], [ block, td, ]) t.deepEquals(res[0].hash(), block.hash(), 'correctly decoded block') - t.equal(bufferToBigInt(res2[1]), td, 'correctly encoded td') + t.equal(bytesToBigInt(res2[1]), td, 'correctly encoded td') t.end() }) @@ -104,7 +103,7 @@ tape('[EthProtocol]', (t) => { const p = new EthProtocol({ config, chain }) const block = Block.fromBlockData({}) const res = p.decode(p.messages.filter((message) => message.name === 'GetReceipts')[0], [ - BigInt(1), + bigIntToBytes(1n), [block.hash()], ]) const res2 = p.encode(p.messages.filter((message) => message.name === 'GetReceipts')[0], { @@ -112,9 +111,9 @@ tape('[EthProtocol]', (t) => { hashes: [block.hash()], }) t.equal(res.reqId, BigInt(1), 'correctly decoded reqId') - t.ok(res.hashes[0].equals(block.hash()), 'correctly decoded blockHash') - t.equal(bufferToBigInt(res2[0]), BigInt(1), 'correctly encoded reqId') - t.ok(res2[1][0].equals(block.hash()), 'correctly encoded blockHash') + t.deepEquals(res.hashes[0], block.hash(), 'correctly decoded blockHash') + t.equal(bytesToBigInt(res2[0]), BigInt(1), 'correctly encoded reqId') + t.deepEquals(res2[1][0], block.hash(), 'correctly encoded blockHash') t.end() }) @@ -138,7 +137,7 @@ tape('[EthProtocol]', (t) => { reqId: BigInt(1), txs: [tx], }) - t.equal(bufferToBigInt(res[0]), BigInt(1), 'correctly encoded reqId') + t.equal(bytesToBigInt(res[0]), BigInt(1), 'correctly encoded reqId') t.deepEqual(res[1][0], tx.serialize(), 'EIP1559 transaction correctly encoded') t.end() }) @@ -154,15 +153,27 @@ tape('[EthProtocol]', (t) => { { status: 1 as 0 | 1, cumulativeBlockGasUsed: BigInt(100), - bitvector: Buffer.alloc(256), - logs: [[Buffer.alloc(20), [Buffer.alloc(32), Buffer.alloc(32, 1)], Buffer.alloc(10)]], + bitvector: new Uint8Array(256), + logs: [ + [ + new Uint8Array(20), + [new Uint8Array(32), new Uint8Array(32).fill(1)], + new Uint8Array(10), + ], + ], txType: 2, }, { status: 0 as 0 | 1, cumulativeBlockGasUsed: BigInt(1000), - bitvector: Buffer.alloc(256, 1), - logs: [[Buffer.alloc(20, 1), [Buffer.alloc(32, 1), Buffer.alloc(32, 1)], Buffer.alloc(10)]], + bitvector: new Uint8Array(256).fill(1), + logs: [ + [ + new Uint8Array(20).fill(1), + [new Uint8Array(32).fill(1), new Uint8Array(32).fill(1)], + new Uint8Array(10), + ], + ], txType: 0, }, ] @@ -172,15 +183,13 @@ tape('[EthProtocol]', (t) => { reqId: BigInt(1), receipts, }) - t.equal(bufferToBigInt(res[0]), BigInt(1), 'correctly encoded reqId') + t.equal(bytesToBigInt(res[0]), BigInt(1), 'correctly encoded reqId') const expectedSerializedReceipts = [ - Buffer.from( - '02f9016d0164b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f866f864940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a001010101010101010101010101010101010101010101010101010101010101018a00000000000000000000', - 'hex' + hexStringToBytes( + '02f9016d0164b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f866f864940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a001010101010101010101010101010101010101010101010101010101010101018a00000000000000000000' ), - Buffer.from( - 'f9016f808203e8b9010001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101f866f864940101010101010101010101010101010101010101f842a00101010101010101010101010101010101010101010101010101010101010101a001010101010101010101010101010101010101010101010101010101010101018a00000000000000000000', - 'hex' + hexStringToBytes( + 'f9016f808203e8b9010001010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101f866f864940101010101010101010101010101010101010101f842a00101010101010101010101010101010101010101010101010101010101010101a001010101010101010101010101010101010101010101010101010101010101018a00000000000000000000' ), ] t.deepEqual(res[1], expectedSerializedReceipts, 'correctly encoded receipts') diff --git a/packages/client/test/net/protocol/lesprotocol.spec.ts b/packages/client/test/net/protocol/lesprotocol.spec.ts index 2d5c4139025..c3e195395c3 100644 --- a/packages/client/test/net/protocol/lesprotocol.spec.ts +++ b/packages/client/test/net/protocol/lesprotocol.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex } from '@ethereumjs/util' import * as tape from 'tape' import { Chain } from '../../../lib/blockchain' @@ -66,23 +67,23 @@ tape('[LesProtocol]', (t) => { }) let status = p.encodeStatus() t.ok( - status.networkId.toString('hex') === '01' && - status.headTd.toString('hex') === '64' && + bytesToHex(status.networkId) === '01' && + bytesToHex(status.headTd) === '64' && status.headHash === '0xaa' && - status.headNum.toString('hex') === '64' && + bytesToHex(status.headNum) === '64' && status.genesisHash === '0xbb' && - status.forkID[0].toString('hex') === 'fc64ec04' && - status.forkID[1].toString('hex') === '118c30' && - status.recentTxLookup.toString('hex') === '01' && + bytesToHex(status.forkID[0]) === 'fc64ec04' && + bytesToHex(status.forkID[1]) === '118c30' && + bytesToHex(status.recentTxLookup) === '01' && status.serveHeaders === 1 && status.serveChainSince === 0 && status.serveStateSince === 0 && //status.txRelay === 1 && TODO: uncomment with client tx pool functionality - status['flowControl/BL'].toString('hex') === '03e8' && - status['flowControl/MRR'].toString('hex') === '0a' && - status['flowControl/MRC'][0][0].toString('hex') === '02' && - status['flowControl/MRC'][0][1].toString('hex') === '0a' && - status['flowControl/MRC'][0][2].toString('hex') === '0a', + bytesToHex(status['flowControl/BL']) === '03e8' && + bytesToHex(status['flowControl/MRR']) === '0a' && + bytesToHex(status['flowControl/MRC'][0][0]) === '02' && + bytesToHex(status['flowControl/MRC'][0][1]) === '0a' && + bytesToHex(status['flowControl/MRC'][0][2]) === '0a', 'encode status' ) status = { ...status, networkId: [0x01] } @@ -93,9 +94,9 @@ tape('[LesProtocol]', (t) => { status.headHash === '0xaa' && status.headNum === BigInt(100) && status.genesisHash === '0xbb' && - status.forkID[0].toString('hex') === 'fc64ec04' && - status.forkID[1].toString('hex') === '118c30' && - status.recentTxLookup.toString('hex') === '01' && + bytesToHex(status.forkID[0]) === 'fc64ec04' && + bytesToHex(status.forkID[1]) === '118c30' && + bytesToHex(status.recentTxLookup) === '01' && status.serveHeaders === true && status.serveChainSince === 0 && status.serveStateSince === 0 && diff --git a/packages/client/test/net/protocol/libp2psender.spec.ts b/packages/client/test/net/protocol/libp2psender.spec.ts index 156f0a3e1fc..99cdbf8ce4d 100644 --- a/packages/client/test/net/protocol/libp2psender.spec.ts +++ b/packages/client/test/net/protocol/libp2psender.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Libp2pSender } from '../../../lib/net/protocol' @@ -10,11 +11,11 @@ tape('[Libp2pSender]', (t) => { const sender = new Libp2pSender(conn[0]) const receiver = new Libp2pSender(conn[1]) receiver.on('status', (status: any) => { - t.equal(status.id.toString('hex'), '05', 'status received') - t.equal(receiver.status.id.toString('hex'), '05', 'status getter') + t.equal(bytesToHex(status.id), '05', 'status received') + t.equal(bytesToHex(receiver.status.id), '05', 'status getter') t.end() }) - sender.sendStatus({ id: Buffer.from('05', 'hex') }) + sender.sendStatus({ id: hexStringToBytes('05') }) }) t.test('should send/receive message', (t) => { @@ -23,10 +24,10 @@ tape('[Libp2pSender]', (t) => { const receiver = new Libp2pSender(conn[1]) receiver.on('message', (message: any) => { t.equal(message.code, 1, 'message received (code)') - t.equal(message.payload.toString('hex'), '05', 'message received (payload)') + t.equal(bytesToHex(message.payload), '05', 'message received (payload)') t.end() }) - sender.sendMessage(1, Buffer.from('05', 'hex')) + sender.sendMessage(1, hexStringToBytes('05')) }) t.test('should catch errors', (t) => { diff --git a/packages/client/test/net/protocol/snapprotocol.spec.ts b/packages/client/test/net/protocol/snapprotocol.spec.ts index b5d3aec64eb..e206557198c 100644 --- a/packages/client/test/net/protocol/snapprotocol.spec.ts +++ b/packages/client/test/net/protocol/snapprotocol.spec.ts @@ -4,7 +4,10 @@ import { KECCAK256_NULL, KECCAK256_RLP, accountBodyToRLP, - bigIntToBuffer, + bigIntToBytes, + bytesToHex, + equalsBytes, + hexStringToBytes, setLengthLeft, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -43,15 +46,13 @@ tape('[SnapProtocol]', (t) => { const config = new Config({ transports: [] }) const chain = await Chain.create({ config }) const p = new SnapProtocol({ config, chain }) - const root = Buffer.from([]) + const root = new Uint8Array(0) const reqId = BigInt(1) - const origin = Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' + const origin = hexStringToBytes( + '0000000000000000000000000000000000000000000000000000000000000000' ) - const limit = Buffer.from( - '0000000000000000000000000f00000000000000000000000000000000000010', - 'hex' + const limit = hexStringToBytes( + '0000000000000000000000000f00000000000000000000000000000000000010' ) const bytes = BigInt(5000000) @@ -67,7 +68,7 @@ tape('[SnapProtocol]', (t) => { ) t.ok( - JSON.stringify(payload[0]) === JSON.stringify(bigIntToBuffer(BigInt(1))), + JSON.stringify(payload[0]) === JSON.stringify(bigIntToBytes(BigInt(1))), 'correctly encoded reqId' ) t.ok( @@ -77,7 +78,7 @@ tape('[SnapProtocol]', (t) => { t.ok(JSON.stringify(payload[2]) === JSON.stringify(origin), 'correctly encoded origin') t.ok(JSON.stringify(payload[3]) === JSON.stringify(limit), 'correctly encoded limit') t.ok( - JSON.stringify(payload[4]) === JSON.stringify(bigIntToBuffer(bytes)), + JSON.stringify(payload[4]) === JSON.stringify(bigIntToBytes(bytes)), 'correctly encoded bytes' ) t.ok(payload) @@ -104,7 +105,7 @@ tape('[SnapProtocol]', (t) => { const chain = await Chain.create({ config }) const p = new SnapProtocol({ config, chain }) /* eslint-disable @typescript-eslint/no-use-before-define */ - const data = RLP.decode(Buffer.from(contractAccountRangeRLP, 'hex')) as unknown + const data = RLP.decode(hexStringToBytes(contractAccountRangeRLP)) as unknown const { reqId, accounts, proof } = p.decode( p.messages.filter((message) => message.name === 'AccountRange')[0], data @@ -119,12 +120,12 @@ tape('[SnapProtocol]', (t) => { t.ok(firstAccount[2].length === 0, 'Slim format storageRoot for first account') t.ok(firstAccount[3].length === 0, 'Slim format codehash for first account') t.ok( - secondAccount[2].toString('hex') === + bytesToHex(secondAccount[2]) === '3dc6d3cfdc6210b8591ea852961d880821298c7891dea399e02d87550af9d40e', 'storageHash of the second account' ) t.ok( - secondAccount[3].toString('hex') === + bytesToHex(secondAccount[3]) === 'e68fe0bb7c4a483affd0f19cc2b989105242bd6b256c6de3afd738f8acd80c66', 'codeHash of the second account' ) @@ -136,7 +137,7 @@ tape('[SnapProtocol]', (t) => { }) ) t.ok( - contractAccountRangeRLP === Buffer.from(payload).toString('hex'), + contractAccountRangeRLP === bytesToHex(payload), 'Re-encoded payload should match with original' ) t.end() @@ -148,7 +149,7 @@ tape('[SnapProtocol]', (t) => { const pSlim = new SnapProtocol({ config, chain }) const pFull = new SnapProtocol({ config, chain, convertSlimBody: true }) // accountRangeRLP is the corresponding response to getAccountRangeRLP - const resData = RLP.decode(Buffer.from(accountRangeRLP, 'hex')) as unknown + const resData = RLP.decode(hexStringToBytes(accountRangeRLP)) const fullData = pFull.decode( pFull.messages.filter((message) => message.name === 'AccountRange')[0], @@ -157,8 +158,8 @@ tape('[SnapProtocol]', (t) => { const { accounts: accountsFull } = fullData t.ok(accountsFull.length === 3, '3 accounts should be decoded in accountsFull') const accountFull = accountsFull[0].body - t.ok(accountFull[2].equals(KECCAK256_RLP), 'storageRoot should be KECCAK256_RLP') - t.ok(accountFull[3].equals(KECCAK256_NULL), 'codeHash should be KECCAK256_NULL') + t.ok(equalsBytes(accountFull[2], KECCAK256_RLP), 'storageRoot should be KECCAK256_RLP') + t.ok(equalsBytes(accountFull[3], KECCAK256_NULL), 'codeHash should be KECCAK256_NULL') // Lets encode fullData as it should be encoded in slim format and upon decoding // we shpuld get slim format @@ -186,13 +187,13 @@ tape('[SnapProtocol]', (t) => { const p = new SnapProtocol({ config, chain }) /* eslint-disable @typescript-eslint/no-use-before-define */ - const reqData = RLP.decode(Buffer.from(getAccountRangeRLP, 'hex')) + const reqData = RLP.decode(hexStringToBytes(getAccountRangeRLP)) const { root: stateRoot } = p.decode( p.messages.filter((message) => message.name === 'GetAccountRange')[0], reqData ) // accountRangeRLP is the corresponding response to getAccountRangeRLP - const resData = RLP.decode(Buffer.from(accountRangeRLP, 'hex')) as unknown + const resData = RLP.decode(hexStringToBytes(accountRangeRLP)) const { accounts, proof } = p.decode( p.messages.filter((message) => message.name === 'AccountRange')[0], resData @@ -214,7 +215,7 @@ tape('[SnapProtocol]', (t) => { t.fail(`AccountRange proof verification failed with message=${(e as Error).message}`) } t.ok( - Buffer.from(keccak256(proof[0])).toString('hex') === stateRoot.toString('hex'), + equalsBytes(keccak256(proof[0]), stateRoot), 'Proof should link to the requested stateRoot' ) t.end() @@ -224,20 +225,18 @@ tape('[SnapProtocol]', (t) => { const config = new Config({ transports: [] }) const chain = await Chain.create({ config }) const p = new SnapProtocol({ config, chain }) - const root = Buffer.from([]) + const root = new Uint8Array(0) const reqId = BigInt(1) - const origin = Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' + const origin = hexStringToBytes( + '0000000000000000000000000000000000000000000000000000000000000000' ) - const limit = Buffer.from( - '0000000000000000000000000f00000000000000000000000000000000000010', - 'hex' + const limit = hexStringToBytes( + '0000000000000000000000000f00000000000000000000000000000000000010' ) const bytes = BigInt(5000000) const accounts = [ - Buffer.from(keccak256(Buffer.from('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'hex'))), - Buffer.from('0000000000000000000000000f00000000000000000000000000000000000010', 'hex'), + keccak256(hexStringToBytes('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')), + hexStringToBytes('0000000000000000000000000f00000000000000000000000000000000000010'), ] const payload = p.encode( @@ -253,7 +252,7 @@ tape('[SnapProtocol]', (t) => { ) t.ok( - JSON.stringify(payload[0]) === JSON.stringify(bigIntToBuffer(BigInt(1))), + JSON.stringify(payload[0]) === JSON.stringify(bigIntToBytes(BigInt(1))), 'correctly encoded reqId' ) t.ok( @@ -264,7 +263,7 @@ tape('[SnapProtocol]', (t) => { t.ok(JSON.stringify(payload[3]) === JSON.stringify(origin), 'correctly encoded origin') t.ok(JSON.stringify(payload[4]) === JSON.stringify(limit), 'correctly encoded limit') t.ok( - JSON.stringify(payload[5]) === JSON.stringify(bigIntToBuffer(bytes)), + JSON.stringify(payload[5]) === JSON.stringify(bigIntToBytes(bytes)), 'correctly encoded bytes' ) t.ok(payload) @@ -292,7 +291,7 @@ tape('[SnapProtocol]', (t) => { const p = new SnapProtocol({ config, chain }) /* eslint-disable @typescript-eslint/no-use-before-define */ - const data = RLP.decode(Buffer.from(storageRangesRLP, 'hex')) as unknown + const data = RLP.decode(hexStringToBytes(storageRangesRLP)) as unknown const { reqId, slots, proof } = p.decode( p.messages.filter((message) => message.name === 'StorageRanges')[0], data @@ -301,10 +300,10 @@ tape('[SnapProtocol]', (t) => { t.ok(slots.length === 1 && slots[0].length === 3, 'correctly decoded slots') const { hash, body } = slots[0][2] t.ok( - hash.toString('hex') === '60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79', + bytesToHex(hash) === '60264186ee63f748d340388f07b244d96d007fff5cbc397bbd69f8747c421f79', 'Slot 3 key' ) - t.ok(body.toString('hex') === '8462b66ae7', 'Slot 3 value') + t.ok(bytesToHex(body) === '8462b66ae7', 'Slot 3 value') const payload = RLP.encode( p.encode(p.messages.filter((message) => message.name === 'StorageRanges')[0], { @@ -313,10 +312,7 @@ tape('[SnapProtocol]', (t) => { proof, }) ) - t.ok( - storageRangesRLP === Buffer.from(payload).toString('hex'), - 'Re-encoded payload should match with original' - ) + t.ok(storageRangesRLP === bytesToHex(payload), 'Re-encoded payload should match with original') t.end() }) @@ -326,7 +322,7 @@ tape('[SnapProtocol]', (t) => { const p = new SnapProtocol({ config, chain }) // Get the handle on the data for the account for which storageRanges has been fetched - const accountsData = RLP.decode(Buffer.from(contractAccountRangeRLP, 'hex')) as unknown + const accountsData = RLP.decode(hexStringToBytes(contractAccountRangeRLP)) const { accounts } = p.decode( p.messages.filter((message) => message.name === 'AccountRange')[0], accountsData @@ -334,7 +330,7 @@ tape('[SnapProtocol]', (t) => { const lastAccount = accounts[accounts.length - 1] /* eslint-disable @typescript-eslint/no-use-before-define */ - const data = RLP.decode(Buffer.from(storageRangesRLP, 'hex')) as unknown + const data = RLP.decode(hexStringToBytes(storageRangesRLP)) const { proof, slots } = p.decode( p.messages.filter((message) => message.name === 'StorageRanges')[0], data @@ -359,7 +355,7 @@ tape('[SnapProtocol]', (t) => { t.fail(`StorageRange proof verification failed with message=${(e as Error).message}`) } t.ok( - Buffer.from(keccak256(proof[0])).toString('hex') === lastAccountStorageRoot.toString('hex'), + equalsBytes(keccak256(proof[0]), lastAccountStorageRoot), 'Proof should link to the accounts storageRoot' ) t.end() @@ -371,8 +367,8 @@ tape('[SnapProtocol]', (t) => { const p = new SnapProtocol({ config, chain }) const reqId = BigInt(1) const hashes = [ - Buffer.from(keccak256(Buffer.from('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'hex'))), - Buffer.from('0000000000000000000000000f00000000000000000000000000000000000010', 'hex'), + keccak256(hexStringToBytes('0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')), + hexStringToBytes('0000000000000000000000000f00000000000000000000000000000000000010'), ] const bytes = BigInt(5000000) @@ -383,12 +379,12 @@ tape('[SnapProtocol]', (t) => { }) t.ok( - JSON.stringify(payload[0]) === JSON.stringify(bigIntToBuffer(BigInt(1))), + JSON.stringify(payload[0]) === JSON.stringify(bigIntToBytes(BigInt(1))), 'correctly encoded reqId' ) t.ok(JSON.stringify(payload[1]) === JSON.stringify(hashes), 'correctly encoded hashes') t.ok( - JSON.stringify(payload[2]) === JSON.stringify(bigIntToBuffer(bytes)), + JSON.stringify(payload[2]) === JSON.stringify(bigIntToBytes(bytes)), 'correctly encoded bytes' ) t.ok(payload) @@ -410,7 +406,7 @@ tape('[SnapProtocol]', (t) => { const chain = await Chain.create({ config }) const p = new SnapProtocol({ config, chain }) - const codesRes = RLP.decode(Buffer.from(byteCodesRLP, 'hex')) as unknown + const codesRes = RLP.decode(hexStringToBytes(byteCodesRLP)) const { reqId, codes } = p.decode( p.messages.filter((message) => message.name === 'ByteCodes')[0], codesRes @@ -425,10 +421,7 @@ tape('[SnapProtocol]', (t) => { codes, }) ) - t.ok( - byteCodesRLP === Buffer.from(payload).toString('hex'), - 'Re-encoded payload should match with original' - ) + t.ok(byteCodesRLP === bytesToHex(payload), 'Re-encoded payload should match with original') t.end() }) @@ -438,22 +431,19 @@ tape('[SnapProtocol]', (t) => { const p = new SnapProtocol({ config, chain }) /* eslint-disable @typescript-eslint/no-use-before-define */ - const codesReq = RLP.decode(Buffer.from(getByteCodesRLP, 'hex')) as unknown + const codesReq = RLP.decode(hexStringToBytes(getByteCodesRLP)) const { hashes } = p.decode( p.messages.filter((message) => message.name === 'GetByteCodes')[0], codesReq ) const codeHash = hashes[0] - const codesRes = RLP.decode(Buffer.from(byteCodesRLP, 'hex')) as unknown + const codesRes = RLP.decode(hexStringToBytes(byteCodesRLP)) const { codes } = p.decode( p.messages.filter((message) => message.name === 'ByteCodes')[0], codesRes ) const code = codes[0] - t.ok( - Buffer.from(keccak256(code)).toString('hex') === codeHash.toString('hex'), - 'Code should match the requested codeHash' - ) + t.ok(equalsBytes(keccak256(code), codeHash), 'Code should match the requested codeHash') t.end() }) }) @@ -465,11 +455,10 @@ const accountRangeRLP = //await peer!.snap!.getAccountRange({ // root: stateRoot, -// origin: Buffer.from( -// '27be64f6a1510e4166b35201a920e543e0579df3b947b8743458736e51549f0c', -// 'hex' +// origin: hexToBytes( +// '27be64f6a1510e4166b35201a920e543e0579df3b947b8743458736e51549f0c' // ), -// limit: Buffer.from('f000000000000000000000000f00000000000000000000000000000000000010', 'hex'), +// limit: hexStringToBytes('f000000000000000000000000f00000000000000000000000000000000000010'), // bytes: BigInt(100), // }) const contractAccountRangeRLP = @@ -480,13 +469,11 @@ const contractAccountRangeRLP = // await peer!.snap!.getStorageRanges({ // root: stateRoot, // accounts: [ -// Buffer.from('27be7c29a7a7d6da542205ed52b91990e625039a545702874be74db9f40fb215', 'hex'), +// hexStringToBytes('27be7c29a7a7d6da542205ed52b91990e625039a545702874be74db9f40fb215'), // ], -// origin: Buffer.from( -// '0000000000000000000000000f00000000000000000000000000000000000000', -// 'hex' -// ), -// limit: Buffer.from('f000000000000000000000000f00000000000000000000000000000000000010', 'hex'), +// origin: hexStringToBytes( +// '0000000000000000000000000f00000000000000000000000000000000000000'), +// limit: hexStringToBytes('f000000000000000000000000f00000000000000000000000000000000000010'), // bytes: BigInt(100), // }) const _getStorageRangesRLP = @@ -496,7 +483,7 @@ const storageRangesRLP = // await peer!.snap!.getByteCodes({ // hashes: [ -// Buffer.from('e68fe0bb7c4a483affd0f19cc2b989105242bd6b256c6de3afd738f8acd80c66', 'hex'), +// hexStringToBytes('e68fe0bb7c4a483affd0f19cc2b989105242bd6b256c6de3afd738f8acd80c66'), // ], // bytes: BigInt(50000), // }) diff --git a/packages/client/test/net/server/libp2pserver.spec.ts b/packages/client/test/net/server/libp2pserver.spec.ts index 1685b8075c9..ea5c249c439 100644 --- a/packages/client/test/net/server/libp2pserver.spec.ts +++ b/packages/client/test/net/server/libp2pserver.spec.ts @@ -1,3 +1,4 @@ +import { bytesToUtf8, utf8ToBytes } from 'ethereum-cryptography/utils' import { EventEmitter } from 'events' import { multiaddr } from 'multiaddr' import * as tape from 'tape' @@ -50,7 +51,7 @@ tape('[Libp2pServer]', async (t) => { config, multiaddrs, bootnodes: ['0.0.0.0:3030', '1.1.1.1:3031'], - key: Buffer.from('abcd'), + key: utf8ToBytes('abcd'), }) t.deepEquals((server as any).multiaddrs, multiaddrs, 'multiaddrs correct') t.deepEquals( @@ -58,7 +59,7 @@ tape('[Libp2pServer]', async (t) => { [multiaddr('/ip4/0.0.0.0/tcp/3030'), multiaddr('/ip4/1.1.1.1/tcp/3031')], 'bootnodes split' ) - t.equals(server.key!.toString(), 'abcd', 'key is correct') + t.equals(bytesToUtf8(server.key!), 'abcd', 'key is correct') t.equals(server.name, 'libp2p', 'get name') t.equals( (await server.getPeerId()).toB58String(), @@ -96,7 +97,7 @@ tape('[Libp2pServer]', async (t) => { t.plan(12) const config = new Config({ transports: [], logger: getLogger({ loglevel: 'off' }) }) const multiaddrs = [multiaddr('/ip4/6.6.6.6')] - const server = new Libp2pServer({ config, multiaddrs, key: Buffer.from('4') }) + const server = new Libp2pServer({ config, multiaddrs, key: utf8ToBytes('4') }) const protos: any = [ { name: 'proto', versions: [1] }, { name: 'proto', versions: [2] }, diff --git a/packages/client/test/net/server/rlpxserver.spec.ts b/packages/client/test/net/server/rlpxserver.spec.ts index 6bfd7fbd5e5..f34ef523804 100644 --- a/packages/client/test/net/server/rlpxserver.spec.ts +++ b/packages/client/test/net/server/rlpxserver.spec.ts @@ -1,3 +1,4 @@ +import { equalsBytes, hexStringToBytes, utf8ToBytes } from '@ethereumjs/util' import { EventEmitter } from 'events' import { multiaddr } from 'multiaddr' import * as tape from 'tape' @@ -10,7 +11,7 @@ tape('[RlpxServer]', async (t) => { class RlpxPeer extends EventEmitter { accept(_: any, _2: any) {} getId() { - return Buffer.from([1]) + return new Uint8Array([1]) } getDisconnectPrefix(_: any) { return 'MockedReason' @@ -51,7 +52,7 @@ tape('[RlpxServer]', async (t) => { key: 'abcd', }) t.equals(server.name, 'rlpx', 'get name') - t.ok(server.key!.equals(Buffer.from('abcd', 'hex')), 'key parse') + t.ok(equalsBytes(server.key!, hexStringToBytes('abcd')), 'key parse') t.deepEquals( server.bootnodes, [multiaddr('/ip4/10.0.0.1/tcp/1234'), multiaddr('/ip4/10.0.0.2/tcp/1234')], @@ -111,7 +112,7 @@ tape('[RlpxServer]', async (t) => { t.test('should return rlpx server info with ip4 as default', async (t) => { const config = new Config({ transports: [] }) - const mockId = '123' + const mockId = '0123' const server = new RlpxServer({ config, bootnodes: '10.0.0.1:1234,10.0.0.2:1234', @@ -120,9 +121,9 @@ tape('[RlpxServer]', async (t) => { ;(server as any).initRlpx = td.func() server.dpt = td.object() ;(server as any).rlpx = td.object({ - _id: mockId, destroy: td.func(), }) + server.rlpx!._id = hexStringToBytes(mockId) td.when( server.dpt!.bootstrap({ address: '10.0.0.1', udpPort: 1234, tcpPort: 1234 }) ).thenResolve(undefined) @@ -130,6 +131,7 @@ tape('[RlpxServer]', async (t) => { (server.dpt! as any).bootstrap({ address: '10.0.0.2', udpPort: '1234', tcpPort: '1234' }) ).thenReject(new Error('err0')) config.events.on(Event.SERVER_ERROR, (err) => t.equals(err.message, 'err0', 'got error')) + await server.start() const nodeInfo = server.getRlpxInfo() t.deepEqual( @@ -149,7 +151,7 @@ tape('[RlpxServer]', async (t) => { t.test('should return rlpx server info with ip6', async (t) => { const config = new Config({ transports: [], extIP: '::' }) - const mockId = '123' + const mockId = '0123' const server = new RlpxServer({ config, bootnodes: '10.0.0.1:1234,10.0.0.2:1234', @@ -158,9 +160,9 @@ tape('[RlpxServer]', async (t) => { ;(server as any).initRlpx = td.func() server.dpt = td.object() ;(server as any).rlpx = td.object({ - _id: mockId, destroy: td.func(), }) + server.rlpx!._id = hexStringToBytes(mockId) td.when( server.dpt!.bootstrap({ address: '10.0.0.1', udpPort: 1234, tcpPort: 1234 }) ).thenResolve(undefined) @@ -231,7 +233,7 @@ tape('[RlpxServer]', async (t) => { const config = new Config({ transports: [] }) const server = new RlpxServer({ config }) const rlpxPeer = new RlpxPeer() - td.when(rlpxPeer.getId()).thenReturn(Buffer.from([1])) + td.when(rlpxPeer.getId()).thenReturn(new Uint8Array([1])) td.when(RlpxPeer.prototype.accept(rlpxPeer, td.matchers.isA(RlpxServer))).thenResolve() ;(server as any).initRlpx().catch((error: Error) => { throw error @@ -248,7 +250,7 @@ tape('[RlpxServer]', async (t) => { ;(server as any).peers.set('01', { id: '01' } as any) server.rlpx!.emit('peer:removed', rlpxPeer) server.rlpx!.emit('peer:error', rlpxPeer, new Error('err0')) - server.rlpx!._id = Buffer.from('ff', 'hex') + server.rlpx!._id = hexStringToBytes('ff') server.rlpx!.emit('listening') }) @@ -257,7 +259,7 @@ tape('[RlpxServer]', async (t) => { const config = new Config({ transports: [] }) const server = new RlpxServer({ config }) const rlpxPeer = new RlpxPeer() - td.when(rlpxPeer.getId()).thenReturn(Buffer.from('test')) + td.when(rlpxPeer.getId()).thenReturn(utf8ToBytes('test')) td.when(RlpxPeer.prototype.accept(rlpxPeer, td.matchers.isA(RlpxServer))).thenResolve() ;(server as any).initRlpx().catch((error: Error) => { throw error diff --git a/packages/client/test/rpc/debug/traceTransaction.spec.ts b/packages/client/test/rpc/debug/traceTransaction.spec.ts index 88eca2a3d14..48ef13e1810 100644 --- a/packages/client/test/rpc/debug/traceTransaction.spec.ts +++ b/packages/client/test/rpc/debug/traceTransaction.spec.ts @@ -1,6 +1,6 @@ import { Block } from '@ethereumjs/block' import { TransactionFactory } from '@ethereumjs/tx' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { INTERNAL_ERROR, INVALID_PARAMS } from '../../../lib/rpc/error-code' @@ -70,7 +70,7 @@ tape(`${method}: call with valid parameters`, async (t) => { block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) - const req = params(method, [bufferToHex(tx.hash()), {}]) + const req = params(method, [bytesToPrefixedHexString(tx.hash()), {}]) const expectRes = (res: any) => { t.equal(res.body.result.structLogs[0].op, 'PUSH1', 'produced a correct trace') } @@ -101,7 +101,7 @@ tape(`${method}: call with reverting code`, async (t) => { block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) - const req = params(method, [bufferToHex(tx.hash()), {}]) + const req = params(method, [bytesToPrefixedHexString(tx.hash()), {}]) const expectRes = (res: any) => { t.equal(res.body.result.failed, true, 'returns error result with reverting code') } @@ -132,7 +132,7 @@ tape(`${method}: call with memory enabled`, async (t) => { block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) - const req = params(method, [bufferToHex(tx.hash()), { enableMemory: true }]) + const req = params(method, [bytesToPrefixedHexString(tx.hash()), { enableMemory: true }]) const expectRes = (res: any) => { t.equal( res.body.result.structLogs[5].memory[0], @@ -167,7 +167,7 @@ tape(`${method}: call with stack disabled`, async (t) => { block.transactions[0] = tx await runBlockWithTxs(chain, execution, [tx], true) - const req = params(method, [bufferToHex(tx.hash()), { disableStack: true }]) + const req = params(method, [bytesToPrefixedHexString(tx.hash()), { disableStack: true }]) const expectRes = (res: any) => { t.ok(res.body.result.structLogs[1].stack === undefined, 'returns no stack with trace') } diff --git a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts index 69471e4cc06..feacb433fb9 100644 --- a/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts +++ b/packages/client/test/rpc/engine/forkchoiceUpdatedV1.spec.ts @@ -1,6 +1,6 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { bufferToHex, zeros } from '@ethereumjs/util' +import { bytesToHex, bytesToPrefixedHexString, zeros } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -161,7 +161,7 @@ tape(`${method}: invalid terminal block with only genesis block`, async (t) => { const req = params(method, [validForkChoiceState, null]) const expectRes = (res: any) => { t.equal(res.body.result.payloadStatus.status, 'INVALID') - t.equal(res.body.result.payloadStatus.latestValidHash, bufferToHex(zeros(32))) + t.equal(res.body.result.payloadStatus.latestValidHash, bytesToHex(zeros(32))) } await baseRequest(t, server, req, 200, expectRes) }) @@ -187,7 +187,7 @@ tape(`${method}: invalid terminal block with 1+ blocks`, async (t) => { number: blocks[0].blockNumber, parentHash: blocks[0].parentHash, difficulty: 1, - extraData: Buffer.alloc(97), + extraData: new Uint8Array(97), }, }, { common } @@ -195,12 +195,12 @@ tape(`${method}: invalid terminal block with 1+ blocks`, async (t) => { await chain.putBlocks([newBlock]) const req = params(method, [ - { ...validForkChoiceState, headBlockHash: '0x' + newBlock.hash().toString('hex') }, + { ...validForkChoiceState, headBlockHash: bytesToPrefixedHexString(newBlock.hash()) }, null, ]) const expectRes = (res: any) => { t.equal(res.body.result.payloadStatus.status, 'INVALID') - t.equal(res.body.result.payloadStatus.latestValidHash, bufferToHex(zeros(32))) + t.equal(res.body.result.payloadStatus.latestValidHash, bytesToHex(zeros(32))) } await baseRequest(t, server, req, 200, expectRes) }) diff --git a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts index d3aaab349f4..ce608ca7a8b 100644 --- a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts +++ b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts @@ -1,7 +1,7 @@ import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { TransactionFactory, initKZG } from '@ethereumjs/tx' -import { Address } from '@ethereumjs/util' +import { Address, hexStringToBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' @@ -61,10 +61,7 @@ tape(`${method}: call with known payload`, async (t) => { hardfork: Hardfork.ShardingForkDev, }) common.setHardfork(Hardfork.ShardingForkDev) - const pkey = Buffer.from( - '9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355', - 'hex' - ) + const pkey = hexStringToBytes('9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355') const address = Address.fromPrivateKey(pkey) const account = await service.execution.vm.stateManager.getAccount(address) diff --git a/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts b/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts index f776bed5f9a..32fb2073249 100644 --- a/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts +++ b/packages/client/test/rpc/engine/getPayloadBodiesByHashV1.spec.ts @@ -2,8 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { TransactionFactory } from '@ethereumjs/tx' -import { Address } from '@ethereumjs/util' -import { randomBytes } from 'crypto' +import { Address, bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' import * as tape from 'tape' import { TOO_LARGE_REQUEST } from '../../../lib/rpc/error-code' @@ -18,7 +17,7 @@ tape(`${method}: call with too many hashes`, async (t) => { const { server } = baseSetup({ engine: true, includeVM: true }) const tooManyHashes: string[] = [] for (let x = 0; x < 35; x++) { - tooManyHashes.push('0x' + randomBytes(32).toString('hex')) + tooManyHashes.push(bytesToPrefixedHexString(randomBytes(32))) } const req = params(method, [tooManyHashes]) const expectRes = checkError( @@ -42,10 +41,7 @@ tape(`${method}: call with valid parameters`, async (t) => { hardfork: Hardfork.ShardingForkDev, }) common.setHardfork(Hardfork.ShardingForkDev) - const pkey = Buffer.from( - '9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355', - 'hex' - ) + const pkey = hexStringToBytes('9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355') const address = Address.fromPrivateKey(pkey) const account = await service.execution.vm.stateManager.getAccount(address) @@ -97,15 +93,15 @@ tape(`${method}: call with valid parameters`, async (t) => { const req = params(method, [ [ - '0x' + block.hash().toString('hex'), - '0x' + randomBytes(32).toString('hex'), - '0x' + block2.hash().toString('hex'), + bytesToPrefixedHexString(block.hash()), + bytesToPrefixedHexString(randomBytes(32)), + bytesToPrefixedHexString(block2.hash()), ], ]) const expectRes = (res: any) => { t.equal( res.body.result[0].transactions[0], - '0x' + tx.serialize().toString('hex'), + bytesToPrefixedHexString(tx.serialize()), 'got expected transaction from first payload' ) t.equal(res.body.result[1], null, 'got null for block not found in chain') @@ -134,10 +130,7 @@ tape(`${method}: call with valid parameters on pre-Shanghai block`, async (t) => } ) common.setHardfork(Hardfork.London) - const pkey = Buffer.from( - '9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355', - 'hex' - ) + const pkey = hexStringToBytes('9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355') const address = Address.fromPrivateKey(pkey) const account = await service.execution.vm.stateManager.getAccount(address) @@ -189,9 +182,9 @@ tape(`${method}: call with valid parameters on pre-Shanghai block`, async (t) => const req = params(method, [ [ - '0x' + block.hash().toString('hex'), - '0x' + randomBytes(32).toString('hex'), - '0x' + block2.hash().toString('hex'), + bytesToPrefixedHexString(block.hash()), + bytesToPrefixedHexString(randomBytes(32)), + bytesToPrefixedHexString(block2.hash()), ], ]) const expectRes = (res: any) => { diff --git a/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts b/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts index ed63d819c1f..e83ba7d41ea 100644 --- a/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts +++ b/packages/client/test/rpc/engine/getPayloadBodiesByRangeV1.spec.ts @@ -2,7 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { TransactionFactory } from '@ethereumjs/tx' -import { Address } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS, TOO_LARGE_REQUEST } from '../../../lib/rpc/error-code' @@ -50,10 +50,7 @@ tape(`${method}: call with valid parameters`, async (t) => { hardfork: Hardfork.ShardingForkDev, }) common.setHardfork(Hardfork.ShardingForkDev) - const pkey = Buffer.from( - '9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355', - 'hex' - ) + const pkey = hexStringToBytes('9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355') const address = Address.fromPrivateKey(pkey) const account = await service.execution.vm.stateManager.getAccount(address) @@ -107,7 +104,7 @@ tape(`${method}: call with valid parameters`, async (t) => { const expectRes = (res: any) => { t.equal( res.body.result[0].transactions[0], - '0x' + tx.serialize().toString('hex'), + bytesToPrefixedHexString(tx.serialize()), 'got expected transaction from first payload' ) t.equal( @@ -145,10 +142,7 @@ tape(`${method}: call with valid parameters on pre-Shanghai hardfork`, async (t) hardfork: Hardfork.London, }) common.setHardfork(Hardfork.London) - const pkey = Buffer.from( - '9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355', - 'hex' - ) + const pkey = hexStringToBytes('9c9996335451aab4fc4eac58e31a8c300e095cdbcee532d53d09280e83360355') const address = Address.fromPrivateKey(pkey) const account = await service.execution.vm.stateManager.getAccount(address) diff --git a/packages/client/test/rpc/engine/newPayloadV1.spec.ts b/packages/client/test/rpc/engine/newPayloadV1.spec.ts index 73477dd1d06..0f98f834157 100644 --- a/packages/client/test/rpc/engine/newPayloadV1.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadV1.spec.ts @@ -1,6 +1,6 @@ import { BlockHeader } from '@ethereumjs/block' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Address, bufferToHex, zeros } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes, zeros } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -152,7 +152,7 @@ tape(`${method}: invalid terminal block`, async (t) => { const req = params(method, [blockData, null]) const expectRes = (res: any) => { t.equal(res.body.result.status, 'INVALID') - t.equal(res.body.result.latestValidHash, bufferToHex(zeros(32))) + t.equal(res.body.result.latestValidHash, bytesToPrefixedHexString(zeros(32))) } await baseRequest(t, server, req, 200, expectRes) }) @@ -203,7 +203,7 @@ tape(`${method}: call with valid data & valid transaction but not signed`, async { common } ) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, @@ -219,9 +219,8 @@ tape(`${method}: call with valid data & valid transaction but not signed`, async }) tape(`${method}: call with valid data & valid transaction`, async (t) => { - const accountPk = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const accountPk = hexStringToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) const accountAddress = Address.fromPrivateKey(accountPk) const newGenesisJSON = { @@ -244,7 +243,7 @@ tape(`${method}: call with valid data & valid transaction`, async (t) => { }, { common } ).sign(accountPk) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, diff --git a/packages/client/test/rpc/engine/newPayloadv2.spec.ts b/packages/client/test/rpc/engine/newPayloadv2.spec.ts index 03458e31d2e..425d485af66 100644 --- a/packages/client/test/rpc/engine/newPayloadv2.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadv2.spec.ts @@ -1,6 +1,6 @@ import { BlockHeader } from '@ethereumjs/block' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Address, bufferToHex, zeros } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes, zeros } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -147,7 +147,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { const req = params(method, [blockData, null]) const expectRes = (res: any) => { t.equal(res.body.result.status, 'INVALID') - t.equal(res.body.result.latestValidHash, bufferToHex(zeros(32))) + t.equal(res.body.result.latestValidHash, bytesToPrefixedHexString(zeros(32))) } await baseRequest(t, server, req, 200, expectRes) }) @@ -198,7 +198,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { { common } ) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, @@ -214,9 +214,8 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { }) v1.test(`${method}: call with valid data & valid transaction`, async (t) => { - const accountPk = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const accountPk = hexStringToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) const accountAddress = Address.fromPrivateKey(accountPk) const newGenesisJSON = { @@ -239,7 +238,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { }, { common } ).sign(accountPk) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, diff --git a/packages/client/test/rpc/engine/newPayloadv3.spec.ts b/packages/client/test/rpc/engine/newPayloadv3.spec.ts index 39b1879056c..efa1e8696b9 100644 --- a/packages/client/test/rpc/engine/newPayloadv3.spec.ts +++ b/packages/client/test/rpc/engine/newPayloadv3.spec.ts @@ -1,6 +1,6 @@ import { BlockHeader } from '@ethereumjs/block' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Address, bufferToHex, zeros } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes, zeros } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -147,7 +147,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { const req = params(method, [blockData, null]) const expectRes = (res: any) => { t.equal(res.body.result.status, 'INVALID') - t.equal(res.body.result.latestValidHash, bufferToHex(zeros(32))) + t.equal(res.body.result.latestValidHash, bytesToPrefixedHexString(zeros(32))) } await baseRequest(t, server, req, 200, expectRes) }) @@ -198,7 +198,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { { common } ) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, @@ -214,9 +214,8 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { }) v1.test(`${method}: call with valid data & valid transaction`, async (t) => { - const accountPk = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const accountPk = hexStringToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) const accountAddress = Address.fromPrivateKey(accountPk) const newGenesisJSON = { @@ -239,7 +238,7 @@ tape(`${method}: call with executionPayloadV1`, (v1) => { }, { common } ).sign(accountPk) - const transactions = ['0x' + tx.serialize().toString('hex')] + const transactions = [bytesToPrefixedHexString(tx.serialize())] const blockDataWithValidTransaction = { ...blockData, transactions, diff --git a/packages/client/test/rpc/engine/withdrawals.spec.ts b/packages/client/test/rpc/engine/withdrawals.spec.ts index 830ffa90e05..11fc32311d0 100644 --- a/packages/client/test/rpc/engine/withdrawals.spec.ts +++ b/packages/client/test/rpc/engine/withdrawals.spec.ts @@ -1,5 +1,5 @@ import { Block } from '@ethereumjs/block' -import { Withdrawal, bigIntToHex, intToHex } from '@ethereumjs/util' +import { Withdrawal, bigIntToHex, bytesToHex, intToHex } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS } from '../../../lib/rpc/error-code' @@ -103,9 +103,9 @@ for (const { name, withdrawals, withdrawalsRoot, gethBlockRlp } of testCases) { const validPayloadAttributesWithWithdrawals = { ...validPayloadAttributes, withdrawals } tape(name, async (t) => { // check withdrawals root computation - const computedWithdrawalsRoot = ( + const computedWithdrawalsRoot = bytesToHex( await Block.genWithdrawalsTrieRoot(withdrawals.map(Withdrawal.fromWithdrawalData)) - ).toString('hex') + ) t.equal(withdrawalsRoot, computedWithdrawalsRoot, 'withdrawalsRoot compuation should match') const { server } = await setupChain(genesisJSON, 'post-merge', { engine: true }) diff --git a/packages/client/test/rpc/eth/call.spec.ts b/packages/client/test/rpc/eth/call.spec.ts index 9c13c9b4293..205f66607e1 100644 --- a/packages/client/test/rpc/eth/call.spec.ts +++ b/packages/client/test/rpc/eth/call.spec.ts @@ -2,7 +2,7 @@ import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' -import { Address, bigIntToHex, bufferToHex } from '@ethereumjs/util' +import { Address, bigIntToHex, bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS } from '../../../lib/rpc/error-code' @@ -36,7 +36,7 @@ tape(`${method}: call with valid arguments`, async (t) => { /* // SPDX-License-Identifier: MIT pragma solidity ^0.7.4; - + contract HelloWorld { function myAddress() public view returns (address addr) { return msg.sender; @@ -98,21 +98,25 @@ tape(`${method}: call with valid arguments`, async (t) => { let req = params(method, [{ ...estimateTxData, gas: estimateTxData.gasLimit }, 'latest']) let expectRes = (res: any) => { const msg = 'should return the correct return value' - t.equal(res.body.result, bufferToHex(execResult.returnValue), msg) + t.equal(res.body.result, bytesToPrefixedHexString(execResult.returnValue), msg) } await baseRequest(t, server, req, 200, expectRes, false) req = params(method, [{ ...estimateTxData }, 'latest']) expectRes = (res: any) => { const msg = 'should return the correct return value with no gas limit provided' - t.equal(res.body.result, bufferToHex(execResult.returnValue), msg) + t.equal(res.body.result, bytesToPrefixedHexString(execResult.returnValue), msg) } await baseRequest(t, server, req, 200, expectRes, false) req = params(method, [{ gasLimit, data }, 'latest']) expectRes = (res: any) => { const msg = `should let run call without 'to' for contract creation` - t.equal(res.body.result, bufferToHex(result.results[0].execResult.returnValue), msg) + t.equal( + res.body.result, + bytesToPrefixedHexString(result.results[0].execResult.returnValue), + msg + ) } await baseRequest(t, server, req, 200, expectRes, true) }) diff --git a/packages/client/test/rpc/eth/getBlockByNumber.spec.ts b/packages/client/test/rpc/eth/getBlockByNumber.spec.ts index 9751fa3ac5f..7bf3ed7c30b 100644 --- a/packages/client/test/rpc/eth/getBlockByNumber.spec.ts +++ b/packages/client/test/rpc/eth/getBlockByNumber.spec.ts @@ -1,5 +1,6 @@ import { Block } from '@ethereumjs/block' import { Transaction } from '@ethereumjs/tx' +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS } from '../../../lib/rpc/error-code' @@ -10,13 +11,11 @@ const mockedTx1 = Transaction.fromTxData({}).sign(dummy.privKey) const mockedTx2 = Transaction.fromTxData({ nonce: 1 }).sign(dummy.privKey) function createChain() { - const genesisBlockHash = Buffer.from( - 'dcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5', - 'hex' + const genesisBlockHash = hexStringToBytes( + 'dcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5' ) - const blockHash = Buffer.from( - 'dcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5', - 'hex' + const blockHash = hexStringToBytes( + 'dcf93da321b27bca12087d6526d2c10540a4c8dc29db1b36610c3004e0e5d2d5' ) const transactions = [mockedTx1] const transactions2 = [mockedTx2] diff --git a/packages/client/test/rpc/eth/getLogs.spec.ts b/packages/client/test/rpc/eth/getLogs.spec.ts index 1a3811e26ce..27f67441b69 100644 --- a/packages/client/test/rpc/eth/getLogs.spec.ts +++ b/packages/client/test/rpc/eth/getLogs.spec.ts @@ -1,5 +1,5 @@ import { Transaction } from '@ethereumjs/tx' -import { Address, bufferToHex } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS } from '../../../lib/rpc/error-code' @@ -24,9 +24,8 @@ const method = 'eth_getLogs' } ``` */ -const logExampleBytecode = Buffer.from( - '608060405234801561001057600080fd5b50610257806100206000396000f3fe608060405234801561001057600080fd5b5060043610610048576000357c010000000000000000000000000000000000000000000000000000000090048063aefb4f0a1461004d575b600080fd5b610067600480360381019061006291906100de565b610069565b005b60005b858110156100c1578284867fbf642f3055e2ef2589825c2c0dd4855c1137a63f6260d9d112629e5cd034a3eb856040516100a69190610168565b60405180910390a480806100b99061018d565b91505061006c565b505050505050565b6000813590506100d88161020a565b92915050565b600080600080600060a086880312156100fa576100f9610205565b5b6000610108888289016100c9565b9550506020610119888289016100c9565b945050604061012a888289016100c9565b935050606061013b888289016100c9565b925050608061014c888289016100c9565b9150509295509295909350565b61016281610183565b82525050565b600060208201905061017d6000830184610159565b92915050565b6000819050919050565b600061019882610183565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156101cb576101ca6101d6565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61021381610183565b811461021e57600080fd5b5056fea2646970667358221220b98f45f4d4112e71fd287ab0ce7cc1872e53b463eb0abf1182b892192d3d8a1d64736f6c63430008070033', - 'hex' +const logExampleBytecode = hexStringToBytes( + '608060405234801561001057600080fd5b50610257806100206000396000f3fe608060405234801561001057600080fd5b5060043610610048576000357c010000000000000000000000000000000000000000000000000000000090048063aefb4f0a1461004d575b600080fd5b610067600480360381019061006291906100de565b610069565b005b60005b858110156100c1578284867fbf642f3055e2ef2589825c2c0dd4855c1137a63f6260d9d112629e5cd034a3eb856040516100a69190610168565b60405180910390a480806100b99061018d565b91505061006c565b505050505050565b6000813590506100d88161020a565b92915050565b600080600080600060a086880312156100fa576100f9610205565b5b6000610108888289016100c9565b9550506020610119888289016100c9565b945050604061012a888289016100c9565b935050606061013b888289016100c9565b925050608061014c888289016100c9565b9150509295509295909350565b61016281610183565b82525050565b600060208201905061017d6000830184610159565b92915050565b6000819050919050565b600061019882610183565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156101cb576101ca6101d6565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61021381610183565b811461021e57600080fd5b5056fea2646970667358221220b98f45f4d4112e71fd287ab0ce7cc1872e53b463eb0abf1182b892192d3d8a1d64736f6c63430008070033' ) tape(`${method}: call with valid arguments`, async (t) => { @@ -55,9 +54,8 @@ tape(`${method}: call with valid arguments`, async (t) => { const contractAddr2 = Address.generate(dummy.addr, BigInt(1)) // construct txs to emit the logs // data calls log(logCount: 10, num1: 1, num2: 2, num3: 3, num4: 4) - const data = Buffer.from( - 'aefb4f0a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004', - 'hex' + const data = hexStringToBytes( + 'aefb4f0a000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004' ) const tx3 = Transaction.fromTxData( { @@ -206,7 +204,7 @@ tape(`${method}: call with valid arguments`, async (t) => { const latestHeader = chain.headers.latest! req = params(method, [ { - blockHash: bufferToHex(latestHeader.hash()), + blockHash: bytesToPrefixedHexString(latestHeader.hash()), }, ]) expectRes = (res: any) => { diff --git a/packages/client/test/rpc/eth/getStorageAt.spec.ts b/packages/client/test/rpc/eth/getStorageAt.spec.ts index ea541afafea..a4374714f50 100644 --- a/packages/client/test/rpc/eth/getStorageAt.spec.ts +++ b/packages/client/test/rpc/eth/getStorageAt.spec.ts @@ -2,7 +2,7 @@ import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' -import { Address, bigIntToHex, bufferToHex, toBuffer } from '@ethereumjs/util' +import { Address, bigIntToHex, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import * as tape from 'tape' @@ -123,16 +123,13 @@ tape(`${method}: call with valid arguments`, async (t) => { // verify storage of pos1 is accurate // pos1["0xccfd725760a68823ff1e062f4cc97e1360e8d997"] - const key = toBuffer( - keccak256( - Buffer.from( - '000000000000000000000000ccfd725760a68823ff1e062f4cc97e1360e8d997' + - '0000000000000000000000000000000000000000000000000000000000000001', - 'hex' - ) + const key = keccak256( + hexStringToBytes( + '000000000000000000000000ccfd725760a68823ff1e062f4cc97e1360e8d997' + + '0000000000000000000000000000000000000000000000000000000000000001' ) ) - req = params(method, [createdAddress!.toString(), bufferToHex(key), 'latest']) + req = params(method, [createdAddress!.toString(), bytesToPrefixedHexString(key), 'latest']) expectRes = (res: any) => { const msg = 'should return the correct storage value (pos1)' t.equal( diff --git a/packages/client/test/rpc/eth/getTransactionByHash.spec.ts b/packages/client/test/rpc/eth/getTransactionByHash.spec.ts index 92afcc31847..cea1aedfae8 100644 --- a/packages/client/test/rpc/eth/getTransactionByHash.spec.ts +++ b/packages/client/test/rpc/eth/getTransactionByHash.spec.ts @@ -1,5 +1,5 @@ import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { @@ -27,16 +27,16 @@ tape(`${method}: call with legacy tx`, async (t) => { await runBlockWithTxs(chain, execution, [tx]) // get the tx - let req = params(method, [bufferToHex(tx.hash())]) + let req = params(method, [bytesToPrefixedHexString(tx.hash())]) let expectRes = (res: any) => { const msg = 'should return the correct tx' - t.equal(res.body.result.hash, bufferToHex(tx.hash()), msg) + t.equal(res.body.result.hash, bytesToPrefixedHexString(tx.hash()), msg) } await baseRequest(t, server, req, 200, expectRes, false) // run a block to ensure tx hash index is cleaned up when txLookupLimit=1 await runBlockWithTxs(chain, execution, []) - req = params(method, [bufferToHex(tx.hash())]) + req = params(method, [bytesToPrefixedHexString(tx.hash())]) expectRes = (res: any) => { const msg = 'should return null when past txLookupLimit' t.equal(res.body.result, null, msg) @@ -65,7 +65,7 @@ tape(`${method}: call with 1559 tx`, async (t) => { await runBlockWithTxs(chain, execution, [tx]) // get the tx - let req = params(method, [bufferToHex(tx.hash())]) + let req = params(method, [bytesToPrefixedHexString(tx.hash())]) let expectRes = (res: any) => { const msg = 'should return the correct tx type' t.equal(res.body.result.type, '0x2', msg) @@ -76,10 +76,10 @@ tape(`${method}: call with 1559 tx`, async (t) => { await runBlockWithTxs(chain, execution, []) await runBlockWithTxs(chain, execution, []) await runBlockWithTxs(chain, execution, []) - req = params(method, [bufferToHex(tx.hash())]) + req = params(method, [bytesToPrefixedHexString(tx.hash())]) expectRes = (res: any) => { const msg = 'should return the correct tx when txLookupLimit=0' - t.equal(res.body.result.hash, bufferToHex(tx.hash()), msg) + t.equal(res.body.result.hash, bytesToPrefixedHexString(tx.hash()), msg) } await baseRequest(t, server, req, 200, expectRes, true) // pass endOnFinish=true for last test }) diff --git a/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts b/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts index f1b182870d6..405cf98f3c3 100644 --- a/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts +++ b/packages/client/test/rpc/eth/getTransactionReceipt.spec.ts @@ -1,5 +1,5 @@ import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' -import { bufferToHex } from '@ethereumjs/util' +import { bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { @@ -31,10 +31,10 @@ tape(`${method}: call with legacy tx`, async (t) => { await runBlockWithTxs(chain, execution, [tx]) // get the tx - const req = params(method, [bufferToHex(tx.hash())]) + const req = params(method, [bytesToPrefixedHexString(tx.hash())]) const expectRes = (res: any) => { const msg = 'should return the correct tx' - t.equal(res.body.result.transactionHash, bufferToHex(tx.hash()), msg) + t.equal(res.body.result.transactionHash, bytesToPrefixedHexString(tx.hash()), msg) } await baseRequest(t, server, req, 200, expectRes) }) @@ -59,10 +59,10 @@ tape(`${method}: call with 1559 tx`, async (t) => { await runBlockWithTxs(chain, execution, [tx]) // get the tx - const req = params(method, [bufferToHex(tx.hash())]) + const req = params(method, [bytesToPrefixedHexString(tx.hash())]) const expectRes = (res: any) => { const msg = 'should return the correct tx' - t.equal(res.body.result.transactionHash, bufferToHex(tx.hash()), msg) + t.equal(res.body.result.transactionHash, bytesToPrefixedHexString(tx.hash()), msg) } await baseRequest(t, server, req, 200, expectRes) }) diff --git a/packages/client/test/rpc/eth/getUncleCountByBlockNumber.spec.ts b/packages/client/test/rpc/eth/getUncleCountByBlockNumber.spec.ts index 47005c2adcc..a1f6c680738 100644 --- a/packages/client/test/rpc/eth/getUncleCountByBlockNumber.spec.ts +++ b/packages/client/test/rpc/eth/getUncleCountByBlockNumber.spec.ts @@ -9,7 +9,7 @@ function createChain() { uncleHeaders: ['0x1', '0x2', '0x3'], transactions: [], header: { - hash: () => Buffer.from([1]), + hash: () => new Uint8Array([1]), number: BigInt('5'), }, } diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index f0c0f39903d..4bbfd31ed99 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -12,9 +12,8 @@ import { commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' -import { toBuffer } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import * as tape from 'tape' import { INTERNAL_ERROR, INVALID_PARAMS, PARSE_ERROR } from '../../../lib/rpc/error-code' @@ -39,9 +38,7 @@ tape(`${method}: call with valid arguments`, async (t) => { // Mainnet EIP-1559 tx const txData = '0x02f90108018001018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64' - const transaction = FeeMarketEIP1559Transaction.fromSerializedTx( - Buffer.from(txData.slice(2), 'hex') - ) + const transaction = FeeMarketEIP1559Transaction.fromSerializedTx(hexStringToBytes(txData)) const address = transaction.getSenderAddress() const vm = (client.services.find((s) => s.name === 'eth') as FullEthereumService).execution.vm @@ -75,9 +72,9 @@ tape(`${method}: send local tx with gasprice lower than minimum`, async (t) => { gasLimit: 21000, gasPrice: 0, nonce: 0, - }).sign(Buffer.from('42'.repeat(32), 'hex')) + }).sign(hexStringToBytes('42'.repeat(32))) - const txData = '0x' + transaction.serialize().toString('hex') + const txData = bytesToPrefixedHexString(transaction.serialize()) const req = params(method, [txData]) const expectRes = (res: any) => { @@ -150,14 +147,14 @@ tape(`${method}: call with unsigned tx`, async (t) => { const txData = '0x02f90108018001018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const tx = FeeMarketEIP1559Transaction.fromSerializedTx(toBuffer(txData), { + const tx = FeeMarketEIP1559Transaction.fromSerializedTx(hexStringToBytes(txData), { common, freeze: false, }) ;(tx as any).v = undefined ;(tx as any).r = undefined ;(tx as any).s = undefined - const txHex = '0x' + tx.serialize().toString('hex') + const txHex = bytesToPrefixedHexString(tx.serialize()) const req = params(method, [txHex]) const expectRes = checkError(t, INVALID_PARAMS, 'tx needs to be signed') @@ -185,7 +182,7 @@ tape(`${method}: call with no peers`, async (t) => { // Mainnet EIP-1559 tx const txData = '0x02f90108018001018402625a0094cccccccccccccccccccccccccccccccccccccccc830186a0b8441a8451e600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85bf859940000000000000000000000000000000000000101f842a00000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000060a701a0afb6e247b1c490e284053c87ab5f6b59e219d51f743f7a4d83e400782bc7e4b9a0479a268e0e0acd4de3f1e28e4fac2a6b32a4195e8dfa9d19147abe8807aa6f64' - const transaction = FeeMarketEIP1559Transaction.fromSerializedTx(toBuffer(txData)) + const transaction = FeeMarketEIP1559Transaction.fromSerializedTx(hexStringToBytes(txData)) const address = transaction.getSenderAddress() const vm = (client.services.find((s) => s.name === 'eth') as FullEthereumService).execution.vm @@ -236,11 +233,10 @@ tape('blob EIP 4844 transaction', async (t) => { const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) const proof = kzg.computeAggregateKzgProof(blobs.map((blob) => Uint8Array.from(blob))) - const bufferedHashes = versionedHashes.map((el) => Buffer.from(el)) const pk = randomBytes(32) const tx = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, kzgProof: proof, @@ -255,7 +251,7 @@ tape('blob EIP 4844 transaction', async (t) => { const replacementTx = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, kzgProof: proof, @@ -272,8 +268,8 @@ tape('blob EIP 4844 transaction', async (t) => { account.balance = BigInt(0xfffffffffffff) await vm.stateManager.putAccount(tx.getSenderAddress(), account) - const req = params(method, ['0x' + tx.serializeNetworkWrapper().toString('hex')]) - const req2 = params(method, ['0x' + replacementTx.serializeNetworkWrapper().toString('hex')]) + const req = params(method, [bytesToPrefixedHexString(tx.serializeNetworkWrapper())]) + const req2 = params(method, [bytesToPrefixedHexString(replacementTx.serializeNetworkWrapper())]) const expectRes = (res: any) => { t.equal(res.body.error, undefined, 'initial blob transaction accepted') } diff --git a/packages/client/test/rpc/helpers.ts b/packages/client/test/rpc/helpers.ts index ec48e15abbc..53325fac280 100644 --- a/packages/client/test/rpc/helpers.ts +++ b/packages/client/test/rpc/helpers.ts @@ -1,7 +1,7 @@ import { BlockHeader } from '@ethereumjs/block' import { Blockchain, parseGethGenesisState } from '@ethereumjs/blockchain' import { Chain as ChainEnum, Common, parseGethGenesis } from '@ethereumjs/common' -import { Address, KECCAK256_RLP } from '@ethereumjs/util' +import { Address, KECCAK256_RLP, hexStringToBytes } from '@ethereumjs/util' import { Server as RPCServer } from 'jayson/promise' import { MemoryLevel } from 'memory-level' @@ -30,7 +30,7 @@ const config: any = {} config.logger = getLogger(config) type StartRPCOpts = { port?: number; wsServer?: boolean } -type WithEngineMiddleware = { jwtSecret: Buffer; unlessFn?: (req: IncomingMessage) => boolean } +type WithEngineMiddleware = { jwtSecret: Uint8Array; unlessFn?: (req: IncomingMessage) => boolean } type createClientArgs = { includeVM: boolean // Instantiates the VM when creating the test client @@ -87,7 +87,7 @@ export function createClient(clientOpts: Partial = {}) { } const clientConfig = { ...defaultClientConfig, ...clientOpts } - chain.getTd = async (_hash: Buffer, _num: bigint) => BigInt(1000) + chain.getTd = async (_hash: Uint8Array, _num: bigint) => BigInt(1000) if ((chain as any)._headers !== undefined) { ;(chain as any)._headers.latest = BlockHeader.fromHeaderData( { withdrawalsRoot: common.isActivatedEIP(4895) ? KECCAK256_RLP : undefined }, @@ -304,6 +304,6 @@ export function gethGenesisStartLondon(gethGenesis: any) { * This address has preallocated balance in file `testdata/geth-genesis/pow.json` */ export const dummy = { - addr: Address.fromString('0xcde098d93535445768e8a2345a2f869139f45641'), - privKey: Buffer.from('5831aac354d13ff96a0c051af0d44c0931c2a20bdacee034ffbaa2354d84f5f8', 'hex'), + addr: new Address(hexStringToBytes('0xcde098d93535445768e8a2345a2f869139f45641')), + privKey: hexStringToBytes('5831aac354d13ff96a0c051af0d44c0931c2a20bdacee034ffbaa2354d84f5f8'), } diff --git a/packages/client/test/rpc/mockBlockchain.ts b/packages/client/test/rpc/mockBlockchain.ts index ea6849fafd1..40b04c05c0a 100644 --- a/packages/client/test/rpc/mockBlockchain.ts +++ b/packages/client/test/rpc/mockBlockchain.ts @@ -1,6 +1,6 @@ import { Block } from '@ethereumjs/block' import { Transaction } from '@ethereumjs/tx' -import { toBuffer } from '@ethereumjs/util' +import { equalsBytes, toBytes } from '@ethereumjs/util' import { dummy } from './helpers' @@ -10,7 +10,7 @@ export function mockBlockchain(options: any = {}) { options.hash ?? '0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf' const transactions = options.transactions ?? [Transaction.fromTxData({}).sign(dummy.privKey)] const block = { - hash: () => toBuffer(blockHash), + hash: () => toBytes(blockHash), header: { number: BigInt(number), }, @@ -25,7 +25,7 @@ export function mockBlockchain(options: any = {}) { return { blocks: { latest: block }, getBlock: async (val: any) => { - if (Buffer.isBuffer(val) && val.equals(Buffer.alloc(32))) { + if (val instanceof Uint8Array && equalsBytes(val, new Uint8Array(32))) { throw Error } return block diff --git a/packages/client/test/rpc/rpc.spec.ts b/packages/client/test/rpc/rpc.spec.ts index f99b1585e1b..8fb6e688a8e 100644 --- a/packages/client/test/rpc/rpc.spec.ts +++ b/packages/client/test/rpc/rpc.spec.ts @@ -1,3 +1,4 @@ +import { randomBytes } from '@ethereumjs/util' import { encode } from 'jwt-simple' import * as tape from 'tape' @@ -9,7 +10,7 @@ import type { TAlgorithm } from 'jwt-simple' const request = require('supertest') -const jwtSecret = Buffer.from(Array.from({ length: 32 }, () => Math.round(Math.random() * 255))) +const jwtSecret = randomBytes(32) tape('call JSON-RPC without Content-Type header', (t) => { const server = startRPC({}) diff --git a/packages/client/test/rpc/txpool/content.spec.ts b/packages/client/test/rpc/txpool/content.spec.ts index 4b5ad0619d6..4585eaf8bf0 100644 --- a/packages/client/test/rpc/txpool/content.spec.ts +++ b/packages/client/test/rpc/txpool/content.spec.ts @@ -2,7 +2,7 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' -import { randomBytes } from 'crypto' +import { randomBytes } from '@ethereumjs/util' import * as tape from 'tape' import { baseRequest, createClient, createManager, params, startRPC } from '../helpers' diff --git a/packages/client/test/rpc/validation.spec.ts b/packages/client/test/rpc/validation.spec.ts index 88523e57d5e..2c562d69239 100644 --- a/packages/client/test/rpc/validation.spec.ts +++ b/packages/client/test/rpc/validation.spec.ts @@ -1,5 +1,4 @@ -import { bufferToHex } from '@ethereumjs/util' -import { randomBytes } from 'crypto' +import { bytesToHex, bytesToPrefixedHexString, randomBytes } from '@ethereumjs/util' import * as tape from 'tape' import { INVALID_PARAMS } from '../../lib/rpc/error-code' @@ -209,7 +208,7 @@ tape(`${prefix} byteVectors`, (t) => { } t.test('Bytes8', (st) => { // valid - st.ok(validatorResult(validators.bytes8([bufferToHex(randomBytes(8))], 0))) + st.ok(validatorResult(validators.bytes8([bytesToPrefixedHexString(randomBytes(8))], 0))) st.ok(validatorResult(validators.bytes8([bytes(8)], 0))) st.ok(validatorResult(validators.bytes8([bytes(1)], 0))) st.ok(validatorResult(validators.bytes8([bytes(2)], 0))) @@ -217,12 +216,12 @@ tape(`${prefix} byteVectors`, (t) => { // invalid st.notOk(validatorResult(validators.bytes8([bytes(10)], 0))) st.notOk(validatorResult(validators.bytes8([bytes(8, false)], 0))) - st.notOk(validatorResult(validators.bytes8([randomBytes(8).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes8([bytesToHex(randomBytes(8))], 0))) st.end() }) t.test('Uint64', (st) => { // valid - st.ok(validatorResult(validators.uint64([bufferToHex(randomBytes(8))], 0))) + st.ok(validatorResult(validators.uint64([bytesToPrefixedHexString(randomBytes(8))], 0))) st.ok(validatorResult(validators.uint64([bytes(8)], 0))) st.ok(validatorResult(validators.uint64([bytes(1)], 0))) st.ok(validatorResult(validators.uint64([bytes(2)], 0))) @@ -232,12 +231,12 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.bytes8([badhex(8)], 0))) st.notOk(validatorResult(validators.uint64([bytes(10)], 0))) st.notOk(validatorResult(validators.uint64([bytes(8, false)], 0))) - st.notOk(validatorResult(validators.uint64([randomBytes(8).toString('hex')], 0))) + st.notOk(validatorResult(validators.uint64([bytesToHex(randomBytes(8))], 0))) st.end() }) t.test('Bytes16', (st) => { // valid - st.ok(validatorResult(validators.bytes16([bufferToHex(randomBytes(16))], 0))) + st.ok(validatorResult(validators.bytes16([bytesToPrefixedHexString(randomBytes(16))], 0))) st.ok(validatorResult(validators.bytes16([bytes(16)], 0))) st.ok(validatorResult(validators.bytes16([bytes(1)], 0))) st.ok(validatorResult(validators.bytes16([bytes(2)], 0))) @@ -247,25 +246,25 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.bytes16([badhex(16)], 0))) st.notOk(validatorResult(validators.bytes16([bytes(20)], 0))) st.notOk(validatorResult(validators.bytes16([bytes(16, false)], 0))) - st.notOk(validatorResult(validators.bytes16([randomBytes(16).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes16([bytesToHex(randomBytes(16))], 0))) st.end() }) t.test('Bytes20', (st) => { // valid st.ok(validatorResult(validators.bytes20([bytes(20)], 0))) - st.ok(validatorResult(validators.bytes20([bufferToHex(randomBytes(20))], 0))) + st.ok(validatorResult(validators.bytes20([bytesToPrefixedHexString(randomBytes(20))], 0))) st.ok(validatorResult(validators.bytes20([bytes(8)], 0))) st.ok(validatorResult(validators.bytes20([bytes(16)], 0))) // invalid st.notOk(validatorResult(validators.bytes20([badhex(20)], 0))) st.notOk(validatorResult(validators.bytes20([bytes(20, false)], 0))) st.notOk(validatorResult(validators.bytes20([bytes(32)], 0))) - st.notOk(validatorResult(validators.bytes20([randomBytes(20).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes20([bytesToHex(randomBytes(20))], 0))) st.end() }) t.test('Bytes32', (st) => { // valid - st.ok(validatorResult(validators.bytes32([bufferToHex(randomBytes(32))], 0))) + st.ok(validatorResult(validators.bytes32([bytesToPrefixedHexString(randomBytes(32))], 0))) st.ok(validatorResult(validators.bytes32([bytes(32)], 0))) st.ok(validatorResult(validators.bytes32([bytes(8)], 0))) st.ok(validatorResult(validators.bytes32([bytes(16)], 0))) @@ -274,12 +273,12 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.bytes32([badhex(32)], 0))) st.notOk(validatorResult(validators.bytes32([bytes(48)], 0))) st.notOk(validatorResult(validators.bytes32([bytes(32, false)], 0))) - st.notOk(validatorResult(validators.bytes32([randomBytes(32).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes32([bytesToHex(randomBytes(32))], 0))) st.end() }) t.test('Uint256', (st) => { // valid - st.ok(validatorResult(validators.uint256([bufferToHex(randomBytes(32))], 0))) + st.ok(validatorResult(validators.uint256([bytesToPrefixedHexString(randomBytes(32))], 0))) st.ok(validatorResult(validators.uint256([bytes(32)], 0))) st.ok(validatorResult(validators.uint256([bytes(8)], 0))) st.ok(validatorResult(validators.uint256([bytes(16)], 0))) @@ -288,12 +287,12 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.uint256([badhex(32)], 0))) st.notOk(validatorResult(validators.uint256([bytes(48)], 0))) st.notOk(validatorResult(validators.uint256([bytes(32, false)], 0))) - st.notOk(validatorResult(validators.uint256([randomBytes(32).toString('hex')], 0))) + st.notOk(validatorResult(validators.uint256([bytesToHex(randomBytes(32))], 0))) st.end() }) t.test('Bytes48', (st) => { // valid - st.ok(validatorResult(validators.bytes48([bufferToHex(randomBytes(48))], 0))) + st.ok(validatorResult(validators.bytes48([bytesToPrefixedHexString(randomBytes(48))], 0))) st.ok(validatorResult(validators.bytes48([bytes(48)], 0))) st.ok(validatorResult(validators.bytes48([bytes(8)], 0))) st.ok(validatorResult(validators.bytes48([bytes(16)], 0))) @@ -304,12 +303,12 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.bytes48([badhex(48)], 0))) st.notOk(validatorResult(validators.bytes48([bytes(64)], 0))) st.notOk(validatorResult(validators.bytes48([bytes(48, false)], 0))) - st.notOk(validatorResult(validators.bytes48([randomBytes(48).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes48([bytesToHex(randomBytes(48))], 0))) st.end() }) t.test('Bytes256', (st) => { // valid - st.ok(validatorResult(validators.bytes256([bufferToHex(randomBytes(256))], 0))) + st.ok(validatorResult(validators.bytes256([bytesToPrefixedHexString(randomBytes(256))], 0))) st.ok(validatorResult(validators.bytes256([bytes(256)], 0))) st.ok(validatorResult(validators.bytes256([bytes(8)], 0))) st.ok(validatorResult(validators.bytes256([bytes(16)], 0))) @@ -321,7 +320,7 @@ tape(`${prefix} byteVectors`, (t) => { st.notOk(validatorResult(validators.bytes256([badhex(256)], 0))) st.notOk(validatorResult(validators.bytes256([bytes(512)], 0))) st.notOk(validatorResult(validators.bytes256([bytes(256, false)], 0))) - st.notOk(validatorResult(validators.bytes256([randomBytes(256).toString('hex')], 0))) + st.notOk(validatorResult(validators.bytes256([bytesToHex(randomBytes(256))], 0))) st.end() }) diff --git a/packages/client/test/rpc/websocket.spec.ts b/packages/client/test/rpc/websocket.spec.ts index 52d3c5bd4cb..0c361d734c2 100644 --- a/packages/client/test/rpc/websocket.spec.ts +++ b/packages/client/test/rpc/websocket.spec.ts @@ -1,3 +1,4 @@ +import { randomBytes } from '@ethereumjs/util' import { encode } from 'jwt-simple' import * as tape from 'tape' @@ -9,7 +10,7 @@ import type { TAlgorithm } from 'jwt-simple' const request = require('superwstest') -const jwtSecret = Buffer.from(Array.from({ length: 32 }, () => Math.round(Math.random() * 255))) +const jwtSecret = randomBytes(32) const wsPort = 3000 tape('call JSON-RPC auth protected server with valid token', (t) => { diff --git a/packages/client/test/service/fullethereumservice.spec.ts b/packages/client/test/service/fullethereumservice.spec.ts index 96de836797e..0bbe155f7ae 100644 --- a/packages/client/test/service/fullethereumservice.spec.ts +++ b/packages/client/test/service/fullethereumservice.spec.ts @@ -1,6 +1,7 @@ import { Common, Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' -import { randomBytes } from 'crypto' +import { hexStringToBytes, randomBytes } from '@ethereumjs/util' +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import * as td from 'testdouble' @@ -227,23 +228,31 @@ tape('[FullEthereumService]', async (t) => { service.execution = { receiptsManager: { getReceipts: td.func() }, } as any - const blockHash = Buffer.alloc(32, 1) + const blockHash = new Uint8Array(32).fill(1) const receipts = [ { status: 1 as 0 | 1, cumulativeBlockGasUsed: BigInt(100), - bitvector: Buffer.alloc(256), + bitvector: new Uint8Array(256), logs: [ - [Buffer.alloc(20), [Buffer.alloc(32), Buffer.alloc(32, 1)], Buffer.alloc(10)], + [ + new Uint8Array(20), + [new Uint8Array(32), new Uint8Array(32).fill(1)], + new Uint8Array(10), + ], ] as Log[], txType: 2, }, { status: 0 as 0 | 1, cumulativeBlockGasUsed: BigInt(1000), - bitvector: Buffer.alloc(256, 1), + bitvector: new Uint8Array(25).fill(1), logs: [ - [Buffer.alloc(20, 1), [Buffer.alloc(32, 1), Buffer.alloc(32, 1)], Buffer.alloc(10)], + [ + new Uint8Array(20).fill(1), + [new Uint8Array(32).fill(1), new Uint8Array(32).fill(1)], + new Uint8Array(10), + ], ] as Log[], txType: 0, }, @@ -285,14 +294,14 @@ tape('[FullEthereumService]', async (t) => { const chain = await Chain.create({ config }) const service = new FullEthereumService({ config, chain }) service.txPool.handleAnnouncedTxHashes = async (msg, _peer, _pool) => { - st.deepEqual(msg[0], Buffer.from('0xabcd', 'hex'), 'handled NewPooledTransactionhashes') + st.deepEqual(msg[0], hexStringToBytes('0xabcd'), 'handled NewPooledTransactionhashes') st.end() } await service.handle( { name: 'NewPooledTransactionHashes', - data: [Buffer.from('0xabcd', 'hex')], + data: [hexToBytes('0xabcd')], }, 'eth', undefined as any @@ -314,7 +323,7 @@ tape('[FullEthereumService]', async (t) => { { eth: { send: (_: string, data: any): any => { - st.ok(data.txs[0].hash().equals(tx.hash()), 'handled getPooledTransactions') + st.ok(equalsBytes(data.txs[0].hash(), tx.hash()), 'handled getPooledTransactions') st.end() }, } as any, diff --git a/packages/client/test/sim/eof.spec.ts b/packages/client/test/sim/eof.spec.ts index 155a3cc2232..c2dea486bf3 100644 --- a/packages/client/test/sim/eof.spec.ts +++ b/packages/client/test/sim/eof.spec.ts @@ -1,5 +1,5 @@ import { Common } from '@ethereumjs/common' -import { privateToAddress } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import * as tape from 'tape' @@ -11,8 +11,8 @@ import { waitForELStart, } from './simutils' -const pkey = Buffer.from('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e', 'hex') -const sender = '0x' + privateToAddress(pkey).toString('hex') +const pkey = hexStringToBytes('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e') +const sender = bytesToPrefixedHexString(privateToAddress(pkey)) const client = Client.http({ port: 8545 }) const network = 'eof' diff --git a/packages/client/test/sim/mainnet.spec.ts b/packages/client/test/sim/mainnet.spec.ts index feba6dd97c2..390e19060a2 100644 --- a/packages/client/test/sim/mainnet.spec.ts +++ b/packages/client/test/sim/mainnet.spec.ts @@ -1,5 +1,5 @@ import { Common } from '@ethereumjs/common' -import { privateToAddress } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import * as tape from 'tape' @@ -12,8 +12,8 @@ import { waitForELStart, } from './simutils' -const pkey = Buffer.from('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e', 'hex') -const sender = '0x' + privateToAddress(pkey).toString('hex') +const pkey = hexStringToBytes('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e') +const sender = bytesToPrefixedHexString(privateToAddress(pkey)) const client = Client.http({ port: 8545 }) const network = 'mainnet' diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 7664da54597..7393bf018a4 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -1,6 +1,6 @@ import { Common } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' -import { privateToAddress } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import * as tape from 'tape' @@ -15,8 +15,8 @@ import { waitForELStart, } from './simutils' -const pkey = Buffer.from('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8', 'hex') -const sender = '0x' + privateToAddress(pkey).toString('hex') +const pkey = hexStringToBytes('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8') +const sender = bytesToPrefixedHexString(privateToAddress(pkey)) const client = Client.http({ port: 8545 }) const network = 'sharding' @@ -89,7 +89,7 @@ tape('sharding/eip4844 hardfork tests', async (t) => { st.equal( eth2kzgs[0], - '0x' + txResult.tx.kzgCommitments![0].toString('hex'), + bytesToPrefixedHexString(txResult.tx.kzgCommitments![0]), 'found expected blob commitments on CL' ) st.end() @@ -136,9 +136,8 @@ tape('sharding/eip4844 hardfork tests', async (t) => { */ const txData = { - data: Buffer.from( - 'f9031103830186a0830f42408080b902c0608060405234801561001057600080fd5b50604051610260380380610260833981810160405281019061003291906101ca565b60008060c0835160145afa61004657600080fd5b50610213565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100b38261006a565b810181811067ffffffffffffffff821117156100d2576100d161007b565b5b80604052505050565b60006100e561004c565b90506100f182826100aa565b919050565b600067ffffffffffffffff8211156101115761011061007b565b5b61011a8261006a565b9050602081019050919050565b60005b8381101561014557808201518184015260208101905061012a565b83811115610154576000848401525b50505050565b600061016d610168846100f6565b6100db565b90508281526020810184848401111561018957610188610065565b5b610194848285610127565b509392505050565b600082601f8301126101b1576101b0610060565b5b81516101c184826020860161015a565b91505092915050565b6000602082840312156101e0576101df610056565b5b600082015167ffffffffffffffff8111156101fe576101fd61005b565b5b61020a8482850161019c565b91505092915050565b603f806102216000396000f3fe6080604052600080fdfea2646970667358221220cbb964afe0f584a89b887bf992e18697c0ebd77a40a102c121f54213f23d4d9464736f6c634300080f00330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000212340000000000000000000000000000000000000000000000000000000000001ba002e89a44a4e4da739fed1ed658079a75dbcb59eebbd8ea0cb11f88a41d611dfaa025fe1645a1d3c9828be471fac5cd3e4be59c90ea304c94d774ff88c84349d8db', - 'hex' + data: hexStringToBytes( + 'f9031103830186a0830f42408080b902c0608060405234801561001057600080fd5b50604051610260380380610260833981810160405281019061003291906101ca565b60008060c0835160145afa61004657600080fd5b50610213565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100b38261006a565b810181811067ffffffffffffffff821117156100d2576100d161007b565b5b80604052505050565b60006100e561004c565b90506100f182826100aa565b919050565b600067ffffffffffffffff8211156101115761011061007b565b5b61011a8261006a565b9050602081019050919050565b60005b8381101561014557808201518184015260208101905061012a565b83811115610154576000848401525b50505050565b600061016d610168846100f6565b6100db565b90508281526020810184848401111561018957610188610065565b5b610194848285610127565b509392505050565b600082601f8301126101b1576101b0610060565b5b81516101c184826020860161015a565b91505092915050565b6000602082840312156101e0576101df610056565b5b600082015167ffffffffffffffff8111156101fe576101fd61005b565b5b61020a8482850161019c565b91505092915050565b603f806102216000396000f3fe6080604052600080fdfea2646970667358221220cbb964afe0f584a89b887bf992e18697c0ebd77a40a102c121f54213f23d4d9464736f6c634300080f00330000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000212340000000000000000000000000000000000000000000000000000000000001ba002e89a44a4e4da739fed1ed658079a75dbcb59eebbd8ea0cb11f88a41d611dfaa025fe1645a1d3c9828be471fac5cd3e4be59c90ea304c94d774ff88c84349d8db' ), nonce: BigInt(nonce.result), gasLimit: 0xffffff, @@ -150,7 +149,7 @@ tape('sharding/eip4844 hardfork tests', async (t) => { const txResult = await client.request( 'eth_sendRawTransaction', - ['0x' + tx.serialize().toString('hex')], + [bytesToPrefixedHexString(tx.serialize())], 2.0 ) let receipt = await client.request('eth_getTransactionReceipt', [txResult.result], 2.0) diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 24e885132fb..4efe3d3d7a1 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -5,9 +5,14 @@ import { commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' -import { Address } from '@ethereumjs/util' +import { + Address, + bytesToHex, + bytesToPrefixedHexString, + bytesToUtf8, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import * as fs from 'fs/promises' import { Level } from 'level' import { execSync, spawn } from 'node:child_process' @@ -133,7 +138,7 @@ export function runNetwork( const runProcPrefix = withPeer !== undefined ? 'peer1' : '' let lastPrintedDot = false runProc.stdout.on('data', (chunk) => { - const str = Buffer.from(chunk).toString('utf8') + const str = bytesToUtf8(chunk) const filterStr = filterKeywords.reduce((acc, next) => acc || str.includes(next), false) const filterOutStr = filterOutWords.reduce((acc, next) => acc || str.includes(next), false) if (filterStr && !filterOutStr) { @@ -143,16 +148,16 @@ export function runNetwork( } process.stdout.write(`data:${runProcPrefix}: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line } else { - if (str.includes('Synchronized')) { + if (str.includes('Synchronized') === true) { process.stdout.write('.') lastPrintedDot = true - } else if (str.includes('Synced') && !str.includes('skipped')) { + } else if (str.includes('Synced') === true && str.includes('skipped') === false) { process.stdout.write('`') } } }) runProc.stderr.on('data', (chunk) => { - const str = Buffer.from(chunk).toString('utf8') + const str = bytesToUtf8(chunk) const filterStr = filterKeywords.reduce((acc, next) => acc || str.includes(next), false) const filterOutStr = filterOutWords.reduce((acc, next) => acc || str.includes(next), false) if (filterStr && !filterOutStr) { @@ -178,7 +183,7 @@ export function runNetwork( let lastPrintedDot = false peerRunProc.stdout.on('data', (chunk) => { - const str = Buffer.from(chunk).toString('utf8') + const str = bytesToUtf8(chunk) const filterStr = filterKeywords.reduce((acc, next) => acc || str.includes(next), false) const filterOutStr = filterOutWords.reduce((acc, next) => acc || str.includes(next), false) if (filterStr && !filterOutStr) { @@ -188,14 +193,14 @@ export function runNetwork( } process.stdout.write(`${withPeer}:el<>cl: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line } else { - if (str.includes('Synchronized')) { + if (str.includes('Synchronized') === true) { process.stdout.write('.') lastPrintedDot = true } } }) peerRunProc.stderr.on('data', (chunk) => { - const str = Buffer.from(chunk).toString('utf8') + const str = bytesToUtf8(chunk) const filterOutStr = filterOutWords.reduce((acc, next) => acc || str.includes(next), false) if (!filterOutStr) { process.stderr.write(`${withPeer}:el<>cl: ${runProc.pid}: ${str}`) // str already contains a new line. console.log adds a new line @@ -241,7 +246,7 @@ export async function startNetwork( } export async function runTxHelper( - opts: { client: Client; common: Common; sender: string; pkey: Buffer }, + opts: { client: Client; common: Common; sender: string; pkey: Uint8Array }, data: string, to?: string, value?: bigint @@ -267,7 +272,7 @@ export async function runTxHelper( const res = await client.request( 'eth_sendRawTransaction', - ['0x' + tx.serialize().toString('hex')], + [bytesToPrefixedHexString(tx.serialize())], 2.0 ) let mined = false @@ -290,11 +295,11 @@ export async function runTxHelper( export const runBlobTx = async ( client: Client, blobSize: number, - pkey: Buffer, + pkey: Uint8Array, to?: string, value?: bigint ) => { - const blobs = getBlobs(randomBytes(blobSize).toString('hex')) + const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) const hashes = commitmentsToVersionedHashes(commitments) @@ -328,7 +333,7 @@ export const runBlobTx = async ( const res = await client.request( 'eth_sendRawTransaction', - ['0x' + serializedWrapper.toString('hex')], + [bytesToPrefixedHexString(serializedWrapper)], 2.0 ) @@ -352,13 +357,13 @@ export const runBlobTx = async ( export const createBlobTxs = async ( numTxs: number, blobSize = 2 ** 17 - 1, - pkey: Buffer, + pkey: Uint8Array, to?: string, value?: bigint ) => { const txHashes: any = [] - const blobs = getBlobs(randomBytes(blobSize).toString('hex')) + const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) const hashes = commitmentsToVersionedHashes(commitments) @@ -389,8 +394,8 @@ export const createBlobTxs = async ( const blobTx = BlobEIP4844Transaction.fromTxData(txData).sign(pkey) const serializedWrapper = blobTx.serializeNetworkWrapper() - await fs.appendFile('./blobs.txt', '0x' + serializedWrapper.toString('hex') + '\n') - txHashes.push('0x' + blobTx.hash().toString('hex')) + await fs.appendFile('./blobs.txt', bytesToPrefixedHexString(serializedWrapper) + '\n') + txHashes.push(bytesToPrefixedHexString(blobTx.hash())) } return txHashes } @@ -409,13 +414,13 @@ export const runBlobTxsFromFile = async (client: Client, path: string) => { export async function createInlineClient(config: any, common: any, customGenesisState: any) { config.events.setMaxListeners(50) const datadir = Config.DATADIR_DEFAULT - const chainDB = new Level( + const chainDB = new Level( `${datadir}/${common.chainName()}/chainDB` ) - const stateDB = new Level( + const stateDB = new Level( `${datadir}/${common.chainName()}/stateDB` ) - const metaDB = new Level( + const metaDB = new Level( `${datadir}/${common.chainName()}/metaDB` ) diff --git a/packages/client/test/sim/snapsync.spec.ts b/packages/client/test/sim/snapsync.spec.ts index 3a032b23c5c..aa6f8f62786 100644 --- a/packages/client/test/sim/snapsync.spec.ts +++ b/packages/client/test/sim/snapsync.spec.ts @@ -2,6 +2,7 @@ import { parseGethGenesisState } from '@ethereumjs/blockchain' import { Common } from '@ethereumjs/common' import { privateToAddress } from '@ethereumjs/util' import debug from 'debug' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { Client } from 'jayson/promise' import * as tape from 'tape' @@ -21,8 +22,8 @@ import { import type { EthereumClient } from '../../lib/client' import type { RlpxServer } from '../../lib/net/server' -const pkey = Buffer.from('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e', 'hex') -const sender = '0x' + privateToAddress(pkey).toString('hex') +const pkey = hexToBytes('ae557af4ceefda559c924516cabf029bedc36b68109bf8d6183fe96e04121f4e') +const sender = '0x' + bytesToHex(privateToAddress(pkey)) const client = Client.http({ port: 8545 }) const network = 'mainnet' diff --git a/packages/client/test/sim/txGenerator.ts b/packages/client/test/sim/txGenerator.ts index 60b32585d47..9f2985c6485 100644 --- a/packages/client/test/sim/txGenerator.ts +++ b/packages/client/test/sim/txGenerator.ts @@ -4,9 +4,8 @@ import { blobsToCommitments, commitmentsToVersionedHashes, } from '@ethereumjs/tx/test/utils/blobHelpers' -import { Address } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import { Client } from 'jayson/promise' const clientPort = process.argv[2] const input = process.argv[3] @@ -19,12 +18,12 @@ const MAX_USEFUL_BYTES_PER_TX = USEFUL_BYTES_PER_BLOB * MAX_BLOBS_PER_TX - 1 const BLOB_SIZE = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB initKZG(kzg, __dirname + '/../../lib/trustedSetup/devnet4.txt') -const pkey = Buffer.from('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8', 'hex') +const pkey = hexStringToBytes('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8') const sender = Address.fromPrivateKey(pkey) function get_padded(data: any, blobs_len: number) { - const pdata = Buffer.alloc(blobs_len * USEFUL_BYTES_PER_BLOB) - const datalen = Buffer.byteLength(data) + const pdata = new Uint8Array(blobs_len * USEFUL_BYTES_PER_BLOB) + const datalen = (data as Uint8Array).byteLength pdata.fill(data, 0, datalen) // TODO: if data already fits in a pad, then ka-boom pdata[datalen] = 0x80 @@ -32,11 +31,11 @@ function get_padded(data: any, blobs_len: number) { } function get_blob(data: any) { - const blob = Buffer.alloc(BLOB_SIZE, 'binary') + const blob = new Uint8Array(BLOB_SIZE) for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { - const chunk = Buffer.alloc(32, 'binary') + const chunk = new Uint8Array(32) chunk.fill(data.subarray(i * 31, (i + 1) * 31), 0, 31) - blob.fill(chunk, i * 32, (i + 1) * 32) + blob.fill(chunk as any, i * 32, (i + 1) * 32) } return blob @@ -44,8 +43,8 @@ function get_blob(data: any) { // ref: https://github.com/asn-d6/blobbers/blob/packing_benchmarks/src/packer_naive.rs function get_blobs(data: any) { - data = Buffer.from(data, 'binary') - const len = Buffer.byteLength(data) + data = hexStringToBytes(data) + const len = (data as Uint8Array).byteLength if (len === 0) { throw Error('invalid blob data') } @@ -57,7 +56,7 @@ function get_blobs(data: any) { const pdata = get_padded(data, blobs_len) - const blobs: Buffer[] = [] + const blobs: Uint8Array[] = [] for (let i = 0; i < blobs_len; i++) { const chunk = pdata.subarray(i * USEFUL_BYTES_PER_BLOB, (i + 1) * USEFUL_BYTES_PER_BLOB) const blob = get_blob(chunk) @@ -123,7 +122,7 @@ async function run(data: any) { const res = await client.request( 'eth_sendRawTransaction', - ['0x' + serializedWrapper.toString('hex')], + [bytesToPrefixedHexString(serializedWrapper)], 2.0 ) @@ -165,8 +164,8 @@ async function run(data: any) { return false } - const expected_kzgs = '0x' + blobTx.kzgCommitments![0].toString('hex') - if (blob_kzg !== '0x' + blobTx.kzgCommitments![0].toString('hex')) { + const expected_kzgs = bytesToPrefixedHexString(blobTx.kzgCommitments![0]) + if (blob_kzg !== bytesToPrefixedHexString(blobTx.kzgCommitments![0])) { console.log(`Unexpected KZG commitment: expected ${expected_kzgs}, got ${blob_kzg}`) return false } else { diff --git a/packages/client/test/sync/beaconsync.spec.ts b/packages/client/test/sync/beaconsync.spec.ts index 85f85172775..c9be394c3fe 100644 --- a/packages/client/test/sync/beaconsync.spec.ts +++ b/packages/client/test/sync/beaconsync.spec.ts @@ -127,7 +127,7 @@ tape('[BeaconSynchronizer]', async (t) => { td.when(sync.best()).thenResolve('peer') td.when(sync.latest('peer' as any)).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(ReverseBlockFetcher.prototype.fetch(), { delay: 100, times: 3 }).thenResolve(undefined) ;(skeleton as any).status.progress.subchains = [ @@ -178,7 +178,7 @@ tape('[BeaconSynchronizer]', async (t) => { td.when(sync.best()).thenResolve('peer') td.when(sync.latest('peer' as any)).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(ReverseBlockFetcher.prototype.fetch(), { delay: 100, times: 1 }).thenResolve(undefined) ;(skeleton as any).status.progress.subchains = [{ head: BigInt(10), tail: BigInt(6) }] diff --git a/packages/client/test/sync/fetcher/accountfetcher.spec.ts b/packages/client/test/sync/fetcher/accountfetcher.spec.ts index 37803efd7a1..bdb94476347 100644 --- a/packages/client/test/sync/fetcher/accountfetcher.spec.ts +++ b/packages/client/test/sync/fetcher/accountfetcher.spec.ts @@ -1,5 +1,5 @@ import { RLP } from '@ethereumjs/rlp' -import { bufferToBigInt } from '@ethereumjs/util' +import { bytesToBigInt, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import * as td from 'testdouble' @@ -30,7 +30,7 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from(''), + root: new Uint8Array(0), first: BigInt(1), count: BigInt(10), }) @@ -52,28 +52,28 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from(''), + root: new Uint8Array(0), first: BigInt(1), count: BigInt(10), }) const fullResult: any = [ { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, ] const accountDataResponse: any = [ { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, ] accountDataResponse.completed = true @@ -88,18 +88,18 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from(''), + root: new Uint8Array(0), first: BigInt(1), count: BigInt(10), }) const accountDataResponse: any = [ { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, ] accountDataResponse.completed = false @@ -114,8 +114,8 @@ tape('[AccountFetcher]', async (t) => { const remainingAccountData: any = [ { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, ] remainingAccountData.completed = true @@ -131,19 +131,19 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from(''), + root: new Uint8Array(0), first: BigInt(1), count: BigInt(3), }) const partialResult: any = [ [ { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, { - hash: Buffer.from(''), - body: [Buffer.from(''), Buffer.from(''), Buffer.from(''), Buffer.from('')], + hash: new Uint8Array(0), + body: [new Uint8Array(0), new Uint8Array(0), new Uint8Array(0), new Uint8Array(0)], }, ], ] @@ -158,7 +158,7 @@ tape('[AccountFetcher]', async (t) => { await fetcher.request(job as any) td.verify( job.peer.snap.getAccountRange({ - root: Buffer.from(''), + root: new Uint8Array(0), origin: td.matchers.anything(), limit: td.matchers.anything(), bytes: BigInt(50000), @@ -175,18 +175,18 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from('39ed8daab7679c0b1b7cf3667c50108185d4d9d1431c24a1c35f696a58277f8f', 'hex'), - first: bufferToBigInt( - Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') + root: hexStringToBytes('39ed8daab7679c0b1b7cf3667c50108185d4d9d1431c24a1c35f696a58277f8f'), + first: bytesToBigInt( + hexStringToBytes('0000000000000000000000000000000000000000000000000000000000000001') ), - count: bufferToBigInt( - Buffer.from('000010c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea9', 'hex') + count: bytesToBigInt( + hexStringToBytes('000010c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea9') ), }) t.ok(fetcher.storageFetcher !== undefined, 'storageFetcher should be created') const task = { count: 3, first: BigInt(1) } - const resData = RLP.decode(Buffer.from(_accountRangeRLP, 'hex')) as unknown + const resData = RLP.decode(hexStringToBytes(_accountRangeRLP)) const { accounts, proof } = p.decode( p.messages.filter((message) => message.name === 'AccountRange')[0], resData @@ -251,7 +251,7 @@ tape('[AccountFetcher]', async (t) => { const fetcher = new AccountFetcher({ config, pool, - root: Buffer.from(''), + root: new Uint8Array(0), first: BigInt(1), count: BigInt(10), }) diff --git a/packages/client/test/sync/fullsync.spec.ts b/packages/client/test/sync/fullsync.spec.ts index 26c2ace4736..eb135fd22ab 100644 --- a/packages/client/test/sync/fullsync.spec.ts +++ b/packages/client/test/sync/fullsync.spec.ts @@ -125,7 +125,7 @@ tape('[FullSynchronizer]', async (t) => { td.when(sync.best()).thenResolve('peer') td.when(sync.latest('peer' as any)).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(BlockFetcher.prototype.fetch(), { delay: 20, times: 2 }).thenResolve(undefined) ;(sync as any).chain = { blocks: { height: BigInt(3) } } diff --git a/packages/client/test/sync/lightsync.spec.ts b/packages/client/test/sync/lightsync.spec.ts index 0038390db90..9f5b4f57602 100644 --- a/packages/client/test/sync/lightsync.spec.ts +++ b/packages/client/test/sync/lightsync.spec.ts @@ -76,7 +76,7 @@ tape('[LightSynchronizer]', async (t) => { td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(HeaderFetcher.prototype.fetch(), { delay: 20, times: 2 }).thenResolve(undefined) ;(sync as any).chain = { headers: { height: BigInt(3) } } @@ -113,7 +113,7 @@ tape('[LightSynchronizer]', async (t) => { td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(HeaderFetcher.prototype.fetch()).thenResolve(undefined) td.when(HeaderFetcher.prototype.fetch()).thenDo(() => @@ -147,7 +147,7 @@ tape('[LightSynchronizer]', async (t) => { td.when(sync.best()).thenResolve({ les: { status: { headNum: BigInt(2) } } } as any) td.when(sync.latest(td.matchers.anything())).thenResolve({ number: BigInt(2), - hash: () => Buffer.from([]), + hash: () => new Uint8Array(0), }) td.when(HeaderFetcher.prototype.fetch()).thenResolve(undefined) td.when(HeaderFetcher.prototype.fetch()).thenDo(() => diff --git a/packages/client/test/sync/skeleton.spec.ts b/packages/client/test/sync/skeleton.spec.ts index 456ac9c4116..c8480b3f6ab 100644 --- a/packages/client/test/sync/skeleton.spec.ts +++ b/packages/client/test/sync/skeleton.spec.ts @@ -1,5 +1,6 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Common } from '@ethereumjs/common' +import { equalsBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import * as tape from 'tape' import * as td from 'testdouble' @@ -19,7 +20,7 @@ type Subchain = { const common = new Common({ chain: 1 }) const block49 = Block.fromBlockData({ header: { number: 49 } }, { common }) const block49B = Block.fromBlockData( - { header: { number: 49, extraData: Buffer.from('B') } }, + { header: { number: 49, extraData: utf8ToBytes('B') } }, { common } ) const block50 = Block.fromBlockData( @@ -724,8 +725,9 @@ tape('[Skeleton] / setHead', async (t) => { BigInt(4), 'canonical height should now be at head with correct chain' ) + const latestHash = chain.headers.latest?.hash() st.ok( - chain.headers.latest?.hash().equals(block4PoS.hash()), + latestHash !== undefined && equalsBytes(latestHash, block4PoS.hash()), 'canonical height should now be at head with correct chain' ) await skeleton.setHead(block5, true) @@ -881,8 +883,9 @@ tape('[Skeleton] / setHead', async (t) => { BigInt(3), 'canonical height should now be at head with correct chain' ) + const latestHash = chain.headers.latest?.hash() st.ok( - chain.headers.latest?.hash().equals(block3.hash()), + latestHash !== undefined && equalsBytes(latestHash, block3.hash()), 'canonical height should now be at head with correct chain' ) diff --git a/packages/client/test/sync/sync.spec.ts b/packages/client/test/sync/sync.spec.ts index 0e05dc0d7f6..51d3892f6c4 100644 --- a/packages/client/test/sync/sync.spec.ts +++ b/packages/client/test/sync/sync.spec.ts @@ -44,7 +44,7 @@ tape('[Synchronizer]', async (t) => { }) void sync.start() ;(sync as any).chain._headers = { - latest: { hash: () => Buffer.from([]), number: BigInt(1) }, + latest: { hash: () => new Uint8Array(0), number: BigInt(1) }, td: BigInt(0), height: BigInt(1), } diff --git a/packages/client/test/sync/txpool.spec.ts b/packages/client/test/sync/txpool.spec.ts index 13116a2fa4d..7d46da2b277 100644 --- a/packages/client/test/sync/txpool.spec.ts +++ b/packages/client/test/sync/txpool.spec.ts @@ -2,7 +2,13 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { AccessListEIP2930Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Account, privateToAddress } from '@ethereumjs/util' +import { + Account, + bytesToHex, + concatBytes, + hexStringToBytes, + privateToAddress, +} from '@ethereumjs/util' import * as tape from 'tape' import { Config } from '../../lib/config' @@ -23,7 +29,7 @@ const setup = () => { vm: { stateManager: { getAccount: () => new Account(BigInt(0), BigInt('50000000000000000000')), - setStateRoot: async (_root: Buffer) => {}, + setStateRoot: async (_root: Uint8Array) => {}, }, copy: () => service.execution.vm, }, @@ -48,7 +54,7 @@ const handleTxs = async ( try { if (stateManager !== undefined) { ;(pool).service.execution.vm.stateManager = stateManager - ;(pool).service.execution.vm.stateManager.setStateRoot = async (_root: Buffer) => {} + ;(pool).service.execution.vm.stateManager.setStateRoot = async (_root: Uint8Array) => {} } pool.open() @@ -89,18 +95,16 @@ tape('[TxPool]', async (t) => { DefaultStateManager.prototype.setStateRoot = (): any => {} const A = { - address: Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex'), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' + address: hexStringToBytes('0b90087d864e82a284dca15923f3776de6bb016f'), + privateKey: hexStringToBytes( + '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993' ), } const B = { - address: Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex'), - privateKey: Buffer.from( - '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', - 'hex' + address: hexStringToBytes('6f62d8382bf2587361db73ceca28be91b2acb6df'), + privateKey: hexStringToBytes( + '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6' ), } @@ -202,15 +206,15 @@ tape('[TxPool]', async (t) => { t.equal((pool as any).knownByPeer.get(peer.id).length, 1, 'one tx added for peer 1') t.equal( (pool as any).knownByPeer.get(peer.id)[0].hash, - txA01.hash().toString('hex'), + bytesToHex(txA01.hash()), 'new known tx hashes entry for announcing peer' ) const txs = pool.getByHash([txA01.hash()]) t.equal(txs.length, 1, 'should get correct number of txs by hash') t.equal( - txs[0].serialize().toString('hex'), - txA01.serialize().toString('hex'), + bytesToHex(txs[0].serialize()), + bytesToHex(txA01.serialize()), 'should get correct tx by hash' ) @@ -247,7 +251,7 @@ tape('[TxPool]', async (t) => { const hashes = [] for (let i = 1; i <= TX_RETRIEVAL_LIMIT + 1; i++) { // One more than TX_RETRIEVAL_LIMIT - hashes.push(Buffer.from(i.toString().padStart(64, '0'), 'hex')) // '0000000000000000000000000000000000000000000000000000000000000001',... + hashes.push(hexStringToBytes(i.toString().padStart(64, '0'))) // '0000000000000000000000000000000000000000000000000000000000000001',... } await pool.handleAnnouncedTxHashes(hashes, peer as any, peerPool) @@ -291,7 +295,7 @@ tape('[TxPool]', async (t) => { await pool.handleAnnouncedTxHashes([txA01.hash(), txA02.hash()], peer, peerPool) t.equal(pool.pool.size, 1, 'pool size 1') - const address = A.address.toString('hex') + const address = bytesToHex(A.address) const poolContent = pool.pool.get(address)! t.equal(poolContent.length, 1, 'only one tx') t.deepEqual(poolContent[0].tx.hash(), txA02.hash(), 'only later-added tx') @@ -336,7 +340,7 @@ tape('[TxPool]', async (t) => { e.message.includes('replacement gas too low'), 'successfully failed adding underpriced txn' ) - const poolObject = pool['handled'].get(txA02_Underpriced.hash().toString('hex')) + const poolObject = pool['handled'].get(bytesToHex(txA02_Underpriced.hash())) t.equal(poolObject?.error, e, 'should have an errored poolObject') const poolTxs = pool.getByHash([txA02_Underpriced.hash()]) t.equal(poolTxs.length, 0, `should not be added in pool`) @@ -349,7 +353,7 @@ tape('[TxPool]', async (t) => { 'NewPooledTransactionHashes', 'should have errored sendObject for NewPooledTransactionHashes broadcast' ) - const address = A.address.toString('hex') + const address = bytesToHex(A.address) const poolContent = pool.pool.get(address)! t.equal(poolContent.length, 1, 'only one tx') t.deepEqual(poolContent[0].tx.hash(), txA01.hash(), 'only later-added tx') @@ -383,7 +387,7 @@ tape('[TxPool]', async (t) => { await pool.handleAnnouncedTxHashes([txA01.hash(), txA02_Underpriced.hash()], peer, peerPool) t.equal(pool.pool.size, 1, 'pool size 1') - const address = A.address.toString('hex') + const address = bytesToHex(A.address) const poolContent = pool.pool.get(address)! t.equal(poolContent.length, 1, 'only one tx') t.deepEqual(poolContent[0].tx.hash(), txA01.hash(), 'only later-added tx') @@ -396,10 +400,10 @@ tape('[TxPool]', async (t) => { // Setup 5001 txs const txs = [] for (let account = 0; account < 51; account++) { - const pkey = Buffer.concat([ - Buffer.from('aa'.repeat(31), 'hex'), - Buffer.from(account.toString(16).padStart(2, '0'), 'hex'), - ]) + const pkey = concatBytes( + hexStringToBytes('aa'.repeat(31)), + hexStringToBytes(account.toString(16).padStart(2, '0')) + ) const from = { address: privateToAddress(pkey), privateKey: pkey, @@ -651,7 +655,7 @@ tape('[TxPool]', async (t) => { await pool.handleAnnouncedTxs([txA01], peer, peerPool) t.equal(pool.pool.size, 1, 'pool size 1') - const address = A.address.toString('hex') + const address = bytesToHex(A.address) const poolContent = pool.pool.get(address)! t.equal(poolContent.length, 1, 'one tx') t.deepEqual(poolContent[0].tx.hash(), txA01.hash(), 'correct tx') @@ -695,7 +699,7 @@ tape('[TxPool]', async (t) => { } await pool.handleAnnouncedTxHashes([txB01.hash(), txB02.hash()], peer, peerPool) t.equal(pool.pool.size, 1, 'pool size 1') - const address = B.address.toString('hex') + const address = bytesToHex(B.address) let poolContent = pool.pool.get(address)! t.equal(poolContent.length, 2, 'two txs') @@ -770,7 +774,7 @@ tape('[TxPool]', async (t) => { knownByPeerObj1.added = Date.now() - pool.POOLED_STORAGE_TIME_LIMIT * 1000 * 60 - 1 ;(pool as any).knownByPeer.set(peer.id, [knownByPeerObj1, knownByPeerObj2]) - const hash = txB01.hash().toString('hex') + const hash = bytesToHex(txB01.hash()) const handledObj = (pool as any).handled.get(hash) handledObj.added = Date.now() - pool.HANDLED_CLEANUP_TIME_LIMIT * 1000 * 60 - 1 ;(pool as any).handled.set(hash, handledObj) diff --git a/packages/client/test/util/rpc.spec.ts b/packages/client/test/util/rpc.spec.ts index c8727d4503e..b58c707c01f 100644 --- a/packages/client/test/util/rpc.spec.ts +++ b/packages/client/test/util/rpc.spec.ts @@ -1,3 +1,4 @@ +import { bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { EthereumClient } from '../../lib/client' @@ -24,14 +25,17 @@ tape('[Util/RPC]', (t) => { const { server } = createRPCServer(manager, { methodConfig, rpcDebug, logger }) const httpServer = createRPCServerListener({ server, - withEngineMiddleware: { jwtSecret: Buffer.alloc(32) }, + withEngineMiddleware: { jwtSecret: new Uint8Array(32) }, }) const wsServer = createWsRPCServerListener({ server, - withEngineMiddleware: { jwtSecret: Buffer.alloc(32) }, + withEngineMiddleware: { jwtSecret: new Uint8Array(32) }, }) const req = { id: 1, method: 'eth_getCanonicalHeadBlock', params: [] } - const resp = { id: 1, result: { test: '0x' + Buffer.alloc(64, 1).toString('hex') } } + const resp = { + id: 1, + result: { test: bytesToPrefixedHexString(new Uint8Array(64).fill(1)) }, + } const reqBulk = [req, req] const respBulk = [resp, { id: 2, error: { err0: '456' } }] // Valid diff --git a/packages/common/src/common.ts b/packages/common/src/common.ts index 57cff62c9f5..5c690a05e43 100644 --- a/packages/common/src/common.ts +++ b/packages/common/src/common.ts @@ -1,4 +1,11 @@ -import { TypeOutput, intToBuffer, toType } from '@ethereumjs/util' +import { + TypeOutput, + bytesToHex, + concatBytes, + hexStringToBytes, + intToBytes, + toType, +} from '@ethereumjs/util' import { buf as crc32Buffer } from 'crc-32' import { EventEmitter } from 'events' @@ -828,8 +835,8 @@ export class Common extends EventEmitter { * @param genesisHash Genesis block hash of the chain * @returns Fork hash as hex string */ - _calcForkHash(hardfork: string | Hardfork, genesisHash: Buffer) { - let hfBuffer = Buffer.alloc(0) + _calcForkHash(hardfork: string | Hardfork, genesisHash: Uint8Array) { + let hfBytes = new Uint8Array(0) let prevBlockOrTime = 0 for (const hf of this.hardforks()) { const { block, timestamp, name } = hf @@ -847,18 +854,18 @@ export class Common extends EventEmitter { blockOrTime !== prevBlockOrTime && name !== Hardfork.Merge ) { - const hfBlockBuffer = Buffer.from(blockOrTime.toString(16).padStart(16, '0'), 'hex') - hfBuffer = Buffer.concat([hfBuffer, hfBlockBuffer]) + const hfBlockBytes = hexStringToBytes(blockOrTime.toString(16).padStart(16, '0')) + hfBytes = concatBytes(hfBytes, hfBlockBytes) prevBlockOrTime = blockOrTime } if (hf.name === hardfork) break } - const inputBuffer = Buffer.concat([genesisHash, hfBuffer]) + const inputBytes = concatBytes(genesisHash, hfBytes) // CRC32 delivers result as signed (negative) 32-bit integer, // convert to hex string - const forkhash = intToBuffer(crc32Buffer(inputBuffer) >>> 0).toString('hex') + const forkhash = bytesToHex(intToBytes(crc32Buffer(inputBytes) >>> 0)) return `0x${forkhash}` } @@ -867,7 +874,7 @@ export class Common extends EventEmitter { * @param hardfork Hardfork name, optional if HF set * @param genesisHash Genesis block hash of the chain, optional if already defined and not needed to be calculated */ - forkHash(hardfork?: string | Hardfork, genesisHash?: Buffer): string { + forkHash(hardfork?: string | Hardfork, genesisHash?: Uint8Array): string { hardfork = hardfork ?? this._hardfork const data = this._getHardfork(hardfork) if ( @@ -901,7 +908,7 @@ export class Common extends EventEmitter { * @param common The {@link Common} to set the forkHashes for * @param genesisHash The genesis block hash */ - setForkHashes(genesisHash: Buffer) { + setForkHashes(genesisHash: Uint8Array) { for (const hf of this.hardforks()) { const blockOrTime = hf.timestamp ?? hf.block if ( diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 5c6088b8a95..a3e5e437bc5 100644 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -117,6 +117,6 @@ export interface CustomCommonOpts extends BaseOpts { export interface GethConfigOpts extends BaseOpts { chain?: string - genesisHash?: Buffer + genesisHash?: Uint8Array mergeForkIdPostMerge?: boolean } diff --git a/packages/common/test/hardforks.spec.ts b/packages/common/test/hardforks.spec.ts index b8c4152cfa0..5841faf6bfa 100644 --- a/packages/common/test/hardforks.spec.ts +++ b/packages/common/test/hardforks.spec.ts @@ -1,3 +1,4 @@ +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Chain, Common, ConsensusAlgorithm, ConsensusType, Hardfork } from '../src' @@ -270,26 +271,26 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) { }) t.test('_calcForkHash()', function (st: tape.Test) { - const chains: [Chain, Buffer][] = [ + const chains: [Chain, Uint8Array][] = [ [ Chain.Mainnet, - Buffer.from('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', 'hex'), + hexStringToBytes('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'), ], [ Chain.Ropsten, - Buffer.from('41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d', 'hex'), + hexStringToBytes('41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d'), ], [ Chain.Rinkeby, - Buffer.from('6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177', 'hex'), + hexStringToBytes('6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177'), ], [ Chain.Goerli, - Buffer.from('bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a', 'hex'), + hexStringToBytes('bf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a'), ], [ Chain.Sepolia, - Buffer.from('25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9', 'hex'), + hexStringToBytes('25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9'), ], ] @@ -323,9 +324,8 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) { msg = 'should provide correct forkHash for HF provided' st.equal(c.forkHash(Hardfork.SpuriousDragon), '0x3edd5b10', msg) - const genesisHash = Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' + const genesisHash = hexStringToBytes( + 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) st.equal(c.forkHash(Hardfork.SpuriousDragon, genesisHash), '0x3edd5b10', msg) diff --git a/packages/common/test/timestamp.spec.ts b/packages/common/test/timestamp.spec.ts index f92ff3120bb..510ddf8472e 100644 --- a/packages/common/test/timestamp.spec.ts +++ b/packages/common/test/timestamp.spec.ts @@ -1,3 +1,4 @@ +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Chain, Common, Hardfork } from '../src' @@ -97,9 +98,8 @@ tape('[Common]: Timestamp Hardfork logic', function (t: tape.Test) { ]) const c = Common.custom({ hardforks }, { baseChain: Chain.Mainnet }) - const mainnetGenesisHash = Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' + const mainnetGenesisHash = hexStringToBytes( + 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) for (const hf of c.hardforks()) { if (typeof hf.forkHash === 'string') { @@ -141,9 +141,8 @@ tape('[Common]: Timestamp Hardfork logic', function (t: tape.Test) { ]) const c = Common.custom({ hardforks }, { baseChain: Chain.Mainnet }) - const mainnetGenesisHash = Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' + const mainnetGenesisHash = hexStringToBytes( + 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3' ) let noForkHashes = c.hardforks().reduce((acc, hf) => { diff --git a/packages/common/test/utils.spec.ts b/packages/common/test/utils.spec.ts index 9e258243970..fd7111c39f8 100644 --- a/packages/common/test/utils.spec.ts +++ b/packages/common/test/utils.spec.ts @@ -1,3 +1,4 @@ +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Common } from '../src/common' @@ -63,9 +64,8 @@ tape('[Utils/Parse]', (t) => { const json = require(`../../blockchain/test/testdata/geth-genesis-kiln.json`) const common = Common.fromGethGenesis(json, { chain: 'customChain', - genesisHash: Buffer.from( - '51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8', - 'hex' + genesisHash: hexStringToBytes( + '51c7fe41be669f69c45c33a56982cbde405313342d9e2b00d7c91a7b284dd4f8' ), mergeForkIdPostMerge: false, }) diff --git a/packages/devp2p/examples/peer-communication-les.ts b/packages/devp2p/examples/peer-communication-les.ts index 40d0fc884e4..f6ee474f974 100644 --- a/packages/devp2p/examples/peer-communication-les.ts +++ b/packages/devp2p/examples/peer-communication-les.ts @@ -1,4 +1,4 @@ -import { randomBytes } from 'crypto' +import { bytesToInt, intToBytes, randomBytes } from '@ethereumjs/util' import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { TypedTransaction } from '@ethereumjs/tx' @@ -7,14 +7,12 @@ import ms = require('ms') import * as devp2p from '../src/index' import { LES, Peer } from '../src/index' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' const PRIVATE_KEY = randomBytes(32) const GENESIS_TD = 1 -const GENESIS_HASH = Buffer.from( - '6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177', - 'hex' -) +const GENESIS_HASH = hexToBytes('6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177') const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.London }) const bootstrapNodes = common.bootstrapNodes() @@ -79,19 +77,24 @@ rlpx.on('peer:added', (peer) => { ) les.sendStatus({ - headTd: devp2p.int2buffer(GENESIS_TD), + headTd: intToBytes(GENESIS_TD), headHash: GENESIS_HASH, - headNum: Buffer.from([]), + headNum: Uint8Array.from([]), genesisHash: GENESIS_HASH, - announceType: devp2p.int2buffer(0), - recentTxLookup: devp2p.int2buffer(1), - forkID: [Buffer.from('3b8e0691', 'hex'), devp2p.int2buffer(1)], + announceType: intToBytes(0), + recentTxLookup: intToBytes(1), + forkID: [hexToBytes('3b8e0691'), intToBytes(1)], }) les.once('status', (status: LES.Status) => { const msg = [ - Buffer.from([]), - [devp2p.buffer2int(status['headNum']), Buffer.from([1]), Buffer.from([]), Buffer.from([1])], + Uint8Array.from([]), + [ + bytesToInt(status['headNum']), + Uint8Array.from([1]), + Uint8Array.from([]), + Uint8Array.from([1]), + ], ] les.sendMessage(devp2p.LES.MESSAGE_CODES.GET_BLOCK_HEADERS, msg) }) @@ -109,7 +112,7 @@ rlpx.on('peer:added', (peer) => { setTimeout(() => { les.sendMessage(devp2p.LES.MESSAGE_CODES.GET_BLOCK_BODIES, [ - Buffer.from([1]), + Uint8Array.from([1]), [header.hash()], ]) requests.bodies.push(header) @@ -196,7 +199,7 @@ dpt.addPeer({ address: '127.0.0.1', udpPort: 30303, tcpPort: 30303 }) .catch((err) => console.log(`error on connection to local node: ${err.stack ?? err}`)) */ function onNewBlock(block: Block, peer: Peer) { - const blockHashHex = block.hash().toString('hex') + const blockHashHex = bytesToHex(block.hash()) const blockNumber = block.header.number console.log( diff --git a/packages/devp2p/examples/peer-communication.ts b/packages/devp2p/examples/peer-communication.ts index 64a79a1077a..f71f3ce0c2d 100644 --- a/packages/devp2p/examples/peer-communication.ts +++ b/packages/devp2p/examples/peer-communication.ts @@ -1,15 +1,15 @@ -import { randomBytes } from 'crypto' +import { bytesToInt, intToBytes, randomBytes } from '@ethereumjs/util' import { Block, BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { TransactionFactory, TypedTransaction } from '@ethereumjs/tx' -import { arrToBufArr } from '@ethereumjs/util' import chalk from 'chalk' import * as LRUCache from 'lru-cache' import ms = require('ms') import * as devp2p from '../src/index' import { ETH, Peer } from '../src/index' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' const PRIVATE_KEY = randomBytes(32) @@ -37,10 +37,8 @@ const REMOTE_CLIENTID_FILTER = [ const CHECK_BLOCK_TITLE = 'Berlin Fork' // Only for debugging/console output const CHECK_BLOCK_NR = 12244000 const CHECK_BLOCK = '1638380ab737e0e916bd1c7f23bd2bab2a532e44b90047f045f262ee21c42b21' -const CHECK_BLOCK_HEADER = arrToBufArr( - RLP.decode( - '0xf90219a0d44a4d33e28d7ea9edd12b69bd32b394587eee498b0e2543ce2bad1877ffbeaca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347941ad91ee08f21be3de0ba2ba6918e714da6b45836a0fdec060ee45e55da9e36060fc95dddd0bdc47e447224666a895d9f0dc9adaa0ca0092d9fcc02ca9b372daec726704ce720d3aa366739868f4820ecaabadb9ac309a0974fee017515a46303f467b6fd50872994db1b0ea64d3455bad93ff9678aced9b90100356050004c5c89691add79838a01d4c302419252a4d3c96e9273908b7ee84660886c070607b4928c416a1800746a0d1dbb442d0baf06eea321422263726748600cc200e82aec08336863514d12d665718016989189c116bc0947046cc6718110586c11464a189000a11a41cc96991970153d88840768170244197e164c6204249b9091a0052ac85088c8108a4418dd2903690a036722623888ea14e90458a390a305a2342cb02766094f68c4100036330719848b48411614686717ab6068a46318204232429dc42020608802ceecd66c3c33a3a1fc6e82522049470328a4a81ba07c6604228ba94f008476005087a6804463696b41002650c0fdf548448a90408717ca31b6d618e883bad42083be153b83bdfbb1846078104798307834383639373636353666366532303530366636663663a0ae1de0acd35a98e211c7e276ad7524bb84a5e1b8d33dd7d1c052b095b564e8b888cca66773148b6e12' - ) +const CHECK_BLOCK_HEADER = RLP.decode( + '0xf90219a0d44a4d33e28d7ea9edd12b69bd32b394587eee498b0e2543ce2bad1877ffbeaca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347941ad91ee08f21be3de0ba2ba6918e714da6b45836a0fdec060ee45e55da9e36060fc95dddd0bdc47e447224666a895d9f0dc9adaa0ca0092d9fcc02ca9b372daec726704ce720d3aa366739868f4820ecaabadb9ac309a0974fee017515a46303f467b6fd50872994db1b0ea64d3455bad93ff9678aced9b90100356050004c5c89691add79838a01d4c302419252a4d3c96e9273908b7ee84660886c070607b4928c416a1800746a0d1dbb442d0baf06eea321422263726748600cc200e82aec08336863514d12d665718016989189c116bc0947046cc6718110586c11464a189000a11a41cc96991970153d88840768170244197e164c6204249b9091a0052ac85088c8108a4418dd2903690a036722623888ea14e90458a390a305a2342cb02766094f68c4100036330719848b48411614686717ab6068a46318204232429dc42020608802ceecd66c3c33a3a1fc6e82522049470328a4a81ba07c6604228ba94f008476005087a6804463696b41002650c0fdf548448a90408717ca31b6d618e883bad42083be153b83bdfbb1846078104798307834383639373636353666366532303530366636663663a0ae1de0acd35a98e211c7e276ad7524bb84a5e1b8d33dd7d1c052b095b564e8b888cca66773148b6e12' ) const getPeerAddr = (peer: Peer) => `${peer._socket.remoteAddress}:${peer._socket.remotePort}` @@ -88,15 +86,9 @@ rlpx.on('peer:added', (peer) => { ) eth.sendStatus({ - td: devp2p.int2buffer(17179869184), // total difficulty in genesis block - bestHash: Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' - ), - genesisHash: Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' - ), + td: intToBytes(17179869184), // total difficulty in genesis block + bestHash: hexToBytes('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'), + genesisHash: hexToBytes('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3'), }) // check CHECK_BLOCK @@ -104,8 +96,8 @@ rlpx.on('peer:added', (peer) => { let forkVerified = false eth.once('status', () => { eth.sendMessage(devp2p.ETH.MESSAGE_CODES.GET_BLOCK_HEADERS, [ - Buffer.from([1]), - [devp2p.int2buffer(CHECK_BLOCK_NR), Buffer.from([1]), Buffer.from([]), Buffer.from([])], + Uint8Array.from([1]), + [intToBytes(CHECK_BLOCK_NR), Uint8Array.from([1]), Uint8Array.from([]), Uint8Array.from([])], ]) forkDrop = setTimeout(() => { peer.disconnect(devp2p.DISCONNECT_REASONS.USELESS_PEER) @@ -126,11 +118,11 @@ rlpx.on('peer:added', (peer) => { for (const item of payload) { const blockHash = item[0] - if (blocksCache.has(blockHash.toString('hex'))) continue + if (blocksCache.has(bytesToHex(blockHash))) continue setTimeout(() => { eth.sendMessage(devp2p.ETH.MESSAGE_CODES.GET_BLOCK_HEADERS, [ - Buffer.from([2]), - [blockHash, Buffer.from([1]), Buffer.from([]), Buffer.from([])], + Uint8Array.from([2]), + [blockHash, Uint8Array.from([1]), Uint8Array.from([]), Uint8Array.from([])], ]) requests.headers.push(blockHash) }, ms('0.1s')) @@ -150,7 +142,7 @@ rlpx.on('peer:added', (peer) => { case devp2p.ETH.MESSAGE_CODES.GET_BLOCK_HEADERS: { const headers = [] // hack - if (devp2p.buffer2int(payload[1][0]) === CHECK_BLOCK_NR) { + if (bytesToInt(payload[1][0]) === CHECK_BLOCK_NR) { headers.push(CHECK_BLOCK_HEADER) } @@ -174,7 +166,7 @@ rlpx.on('peer:added', (peer) => { const expectedHash = CHECK_BLOCK const header = BlockHeader.fromValuesArray(payload[1][0], { common }) - if (header.hash().toString('hex') === expectedHash) { + if (bytesToHex(header.hash()) === expectedHash) { console.log(`${addr} verified to be on the same side of the ${CHECK_BLOCK_TITLE}`) clearTimeout(forkDrop) forkVerified = true @@ -191,11 +183,11 @@ rlpx.on('peer:added', (peer) => { const header = BlockHeader.fromValuesArray(payload[1][0], { common }) while (requests.headers.length > 0) { const blockHash = requests.headers.shift() - if (header.hash().equals(blockHash)) { + if (equalsBytes(header.hash(), blockHash)) { isValidPayload = true setTimeout(() => { eth.sendMessage(devp2p.ETH.MESSAGE_CODES.GET_BLOCK_BODIES, [ - Buffer.from([3]), + Uint8Array.from([3]), [blockHash], ]) requests.bodies.push(header) @@ -205,7 +197,7 @@ rlpx.on('peer:added', (peer) => { } if (!isValidPayload) { - console.log(`${addr} received wrong block header ${header.hash().toString('hex')}`) + console.log(`${addr} received wrong block header ${bytesToHex(header.hash())}`) } } @@ -338,7 +330,7 @@ dpt.addPeer({ address: '127.0.0.1', udpPort: 30303, tcpPort: 30303 }) const txCache = new LRUCache({ max: 1000 }) function onNewTx(tx: TypedTransaction, peer: Peer) { - const txHashHex = tx.hash().toString('hex') + const txHashHex = bytesToHex(tx.hash()) if (txCache.has(txHashHex)) return txCache.set(txHashHex, true) @@ -347,7 +339,7 @@ function onNewTx(tx: TypedTransaction, peer: Peer) { const blocksCache = new LRUCache({ max: 100 }) function onNewBlock(block: Block, peer: Peer) { - const blockHashHex = block.hash().toString('hex') + const blockHashHex = bytesToHex(block.hash()) const blockNumber = block.header.number if (blocksCache.has(blockHashHex)) return diff --git a/packages/devp2p/examples/simple.ts b/packages/devp2p/examples/simple.ts index 6105551209a..8cf80e3c1c7 100644 --- a/packages/devp2p/examples/simple.ts +++ b/packages/devp2p/examples/simple.ts @@ -1,5 +1,6 @@ import { Chain, Common } from '@ethereumjs/common' import chalk from 'chalk' +import { hexToBytes } from 'ethereum-cryptography/utils' import { DPT } from '../src/index' @@ -15,7 +16,7 @@ const BOOTNODES = bootstrapNodes.map((node: any) => { } }) -const dpt = new DPT(Buffer.from(PRIVATE_KEY, 'hex'), { +const dpt = new DPT(hexToBytes(PRIVATE_KEY), { endpoint: { address: '0.0.0.0', udpPort: null, @@ -27,14 +28,12 @@ const dpt = new DPT(Buffer.from(PRIVATE_KEY, 'hex'), { dpt.on('error', (err) => console.error(chalk.red(err.stack ?? err))) dpt.on('peer:added', (peer) => { - const info = `(${peer.id.toString('hex')},${peer.address},${peer.udpPort},${peer.tcpPort})` + const info = `(${bytesToHex(peer.id)},${peer.address},${peer.udpPort},${peer.tcpPort})` console.log(chalk.green(`New peer: ${info} (total: ${dpt.getPeers().length})`)) }) dpt.on('peer:removed', (peer) => { - console.log( - chalk.yellow(`Remove peer: ${peer.id.toString('hex')} (total: ${dpt.getPeers().length})`) - ) + console.log(chalk.yellow(`Remove peer: ${bytesToHex(peer.id)} (total: ${dpt.getPeers().length})`)) }) // for accept incoming connections uncomment next line diff --git a/packages/devp2p/package.json b/packages/devp2p/package.json index 722b94192fd..72befc7883c 100644 --- a/packages/devp2p/package.json +++ b/packages/devp2p/package.json @@ -53,13 +53,10 @@ "@ethereumjs/rlp": "^4.0.1", "@ethereumjs/util": "^8.0.5", "@scure/base": "1.1.1", - "@types/bl": "^2.1.0", "@types/k-bucket": "^5.0.0", "@types/lru-cache": "^5.1.0", - "bl": "^1.1.2", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", - "ip": "^1.1.3", "k-bucket": "^5.0.0", "lru-cache": "^5.1.1", "ms": "^0.7.1", diff --git a/packages/devp2p/scripts/singlePeerRun.ts b/packages/devp2p/scripts/singlePeerRun.ts index c2de2eaa6e6..18fdd64f821 100644 --- a/packages/devp2p/scripts/singlePeerRun.ts +++ b/packages/devp2p/scripts/singlePeerRun.ts @@ -1,4 +1,4 @@ -import { randomBytes } from 'crypto' +import { randomBytes } from '@ethereumjs/util' import { Chain, Common } from '@ethereumjs/common' import * as devp2p from '../src/index' diff --git a/packages/devp2p/src/@types/snappyjs/index.d.ts b/packages/devp2p/src/@types/snappyjs/index.d.ts index 2a77dae6241..2968938de8b 100644 --- a/packages/devp2p/src/@types/snappyjs/index.d.ts +++ b/packages/devp2p/src/@types/snappyjs/index.d.ts @@ -1,4 +1,4 @@ declare module 'snappyjs' { - function uncompress(data: Buffer): Buffer - function compress(data: Buffer): Buffer + function uncompress(data: Uint8Array): Uint8Array + function compress(data: Uint8Array): Uint8Array } diff --git a/packages/devp2p/src/dns/enr.ts b/packages/devp2p/src/dns/enr.ts index d5813edb622..e741de40385 100644 --- a/packages/devp2p/src/dns/enr.ts +++ b/packages/devp2p/src/dns/enr.ts @@ -1,7 +1,7 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr, bufArrToArr } from '@ethereumjs/util' import { base32, base64url } from '@scure/base' import { ecdsaVerify } from 'ethereum-cryptography/secp256k1-compat' +import { bytesToUtf8, utf8ToBytes } from 'ethereum-cryptography/utils' import { Multiaddr } from 'multiaddr' import { sscanf } from 'scanf' @@ -60,21 +60,21 @@ export class ENR { while (enrMod.length % 4 !== 0) { enrMod = enrMod + '=' } - const base64BufferEnr = Buffer.from(base64url.decode(enrMod)) - const decoded = arrToBufArr(RLP.decode(Uint8Array.from(base64BufferEnr))) as Buffer[] + const base64BytesEnr = base64url.decode(enrMod) + const decoded = RLP.decode(base64BytesEnr) const [signature, seq, ...kvs] = decoded // Convert ENR key/value pairs to object - const obj: Record = {} + const obj: Record = {} for (let i = 0; i < kvs.length; i += 2) { - obj[kvs[i].toString()] = Buffer.from(kvs[i + 1]) + obj[bytesToUtf8(kvs[i] as Uint8Array)] = kvs[i + 1] as Uint8Array } // Validate sig const isVerified = ecdsaVerify( - signature, - keccak256(Buffer.from(RLP.encode(bufArrToArr([seq, ...kvs])))), + signature as Uint8Array, + keccak256(RLP.encode([seq, ...kvs])), obj.secp256k1 ) @@ -123,14 +123,14 @@ export class ENR { // of the record content, excluding the `sig=` part, encoded as URL-safe base64 string // (Trailing recovery bit must be trimmed to pass `ecdsaVerify` method) const signedComponent = root.split(' sig')[0] - const signedComponentBuffer = Buffer.from(signedComponent) - const signatureBuffer = Buffer.from( + const signedComponentBytes = utf8ToBytes(signedComponent) + const signatureBytes = Uint8Array.from( [...base64url.decode(rootVals.signature + '=').values()].slice(0, 64) ) - const keyBuffer = Buffer.from(decodedPublicKey) + const keyBytes = Uint8Array.from(decodedPublicKey) - const isVerified = ecdsaVerify(signatureBuffer, keccak256(signedComponentBuffer), keyBuffer) + const isVerified = ecdsaVerify(signatureBytes, keccak256(signedComponentBytes), keyBytes) if (!isVerified) throw new Error('Unable to verify ENR root signature') @@ -177,13 +177,13 @@ export class ENR { /** * Gets relevant multiaddr conversion codes for ipv4, ipv6 and tcp, udp formats - * @param {Buffer} protocolId + * @param {Uint8Array} protocolId * @return {ProtocolCodes} */ - static _getIpProtocolConversionCodes(protocolId: Buffer): ProtocolCodes { + static _getIpProtocolConversionCodes(protocolId: Uint8Array): ProtocolCodes { let ipCode - switch (protocolId.toString()) { + switch (bytesToUtf8(protocolId)) { case 'v4': ipCode = Multiaddr.protocols.names.ip4.code break diff --git a/packages/devp2p/src/dpt/ban-list.ts b/packages/devp2p/src/dpt/ban-list.ts index 25243ecac73..fe130f50d8e 100644 --- a/packages/devp2p/src/dpt/ban-list.ts +++ b/packages/devp2p/src/dpt/ban-list.ts @@ -16,14 +16,14 @@ export class BanList { this.lru = new LRUCache({ max: 30000 }) // 10k should be enough (each peer obj can has 3 keys) } - add(obj: string | Buffer | PeerInfo, maxAge?: number) { + add(obj: string | Uint8Array | PeerInfo, maxAge?: number) { for (const key of KBucket.getKeys(obj)) { this.lru.set(key, true, maxAge) debug(`Added peer ${formatLogId(key, verbose)}, size: ${this.lru.length}`) } } - has(obj: string | Buffer | PeerInfo): boolean { + has(obj: string | Uint8Array | PeerInfo): boolean { return KBucket.getKeys(obj).some((key: string) => Boolean(this.lru.get(key))) } } diff --git a/packages/devp2p/src/dpt/dpt.ts b/packages/devp2p/src/dpt/dpt.ts index 90840f83a3d..224730c6ffc 100644 --- a/packages/devp2p/src/dpt/dpt.ts +++ b/packages/devp2p/src/dpt/dpt.ts @@ -1,10 +1,10 @@ -import { randomBytes } from 'crypto' +import { bytesToInt, randomBytes } from '@ethereumjs/util' import { getPublicKey } from 'ethereum-cryptography/secp256k1' import { EventEmitter } from 'events' import ms = require('ms') import { DNS } from '../dns' -import { buffer2int, devp2pDebug, pk2id } from '../util' +import { devp2pDebug, pk2id } from '../util' import { BanList } from './ban-list' import { KBucket } from './kbucket' @@ -15,7 +15,7 @@ import type { Debugger } from 'debug' const DEBUG_BASE_NAME = 'dpt' export interface PeerInfo { - id?: Uint8Array | Buffer + id?: Uint8Array address?: string udpPort?: number | null tcpPort?: number | null @@ -87,12 +87,12 @@ export interface DPTOptions { } export class DPT extends EventEmitter { - privateKey: Buffer + privateKey: Uint8Array banlist: BanList dns: DNS _debug: Debugger - private _id: Buffer | undefined + private _id: Uint8Array | undefined private _kbucket: KBucket private _server: DPTServer private _refreshIntervalId: NodeJS.Timeout @@ -103,11 +103,11 @@ export class DPT extends EventEmitter { private _dnsNetworks: string[] private _dnsAddr: string - constructor(privateKey: Buffer, options: DPTOptions) { + constructor(privateKey: Uint8Array, options: DPTOptions) { super() - this.privateKey = Buffer.from(privateKey) - this._id = pk2id(Buffer.from(getPublicKey(this.privateKey, false))) + this.privateKey = privateKey + this._id = pk2id(getPublicKey(this.privateKey, false)) this._shouldFindNeighbours = options.shouldFindNeighbours ?? true this._shouldGetDnsPeers = options.shouldGetDnsPeers ?? false // By default, tries to connect to 12 new peers every 3s @@ -220,7 +220,7 @@ export class DPT extends EventEmitter { } } - getPeer(obj: string | Buffer | PeerInfo) { + getPeer(obj: string | Uint8Array | PeerInfo) { return this._kbucket.get(obj) } @@ -228,7 +228,7 @@ export class DPT extends EventEmitter { return this._kbucket.getAll() } - getClosestPeers(id: string) { + getClosestPeers(id: Uint8Array) { return this._kbucket.closest(id) } @@ -236,7 +236,7 @@ export class DPT extends EventEmitter { this._kbucket.remove(obj) } - banPeer(obj: string | Buffer | PeerInfo, maxAge?: number) { + banPeer(obj: string | Uint8Array | PeerInfo, maxAge?: number) { this.banlist.add(obj, maxAge) this._kbucket.remove(obj) } @@ -258,7 +258,7 @@ export class DPT extends EventEmitter { for (const peer of peers) { // Randomly distributed selector based on peer ID // to decide on subdivided execution - const selector = buffer2int((peer.id as Buffer).slice(0, 1)) % 10 + const selector = bytesToInt((peer.id as Uint8Array).subarray(0, 1)) % 10 if (selector === this._refreshIntervalSelectionCounter) { this._server.findneighbours(peer, randomBytes(64)) } diff --git a/packages/devp2p/src/dpt/kbucket.ts b/packages/devp2p/src/dpt/kbucket.ts index c9a99e262ee..ece927dc610 100644 --- a/packages/devp2p/src/dpt/kbucket.ts +++ b/packages/devp2p/src/dpt/kbucket.ts @@ -1,3 +1,4 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' import { EventEmitter } from 'events' import _KBucket = require('k-bucket') @@ -7,14 +8,14 @@ const KBUCKET_SIZE = 16 const KBUCKET_CONCURRENCY = 3 export interface CustomContact extends PeerInfo { - id: Uint8Array | Buffer + id: Uint8Array vectorClock: number } export class KBucket extends EventEmitter { _peers: Map = new Map() _kbucket: _KBucket - constructor(localNodeId: Buffer) { + constructor(localNodeId: Uint8Array) { super() this._kbucket = new _KBucket({ @@ -42,12 +43,12 @@ export class KBucket extends EventEmitter { }) } - static getKeys(obj: Buffer | string | PeerInfo): string[] { - if (Buffer.isBuffer(obj)) return [obj.toString('hex')] + static getKeys(obj: Uint8Array | string | PeerInfo): string[] { + if (obj instanceof Uint8Array) return [bytesToHex(obj)] if (typeof obj === 'string') return [obj] const keys = [] - if (Buffer.isBuffer(obj.id)) keys.push(obj.id.toString('hex')) + if (obj.id instanceof Uint8Array) keys.push(bytesToHex(obj.id)) if (obj.address !== undefined && typeof obj.tcpPort === 'number') keys.push(`${obj.address}:${obj.tcpPort}`) return keys @@ -58,7 +59,7 @@ export class KBucket extends EventEmitter { if (!isExists) this._kbucket.add(peer as CustomContact) } - get(obj: Buffer | string | PeerInfo) { + get(obj: Uint8Array | string | PeerInfo) { for (const key of KBucket.getKeys(obj)) { const peer = this._peers.get(key) if (peer !== undefined) return peer @@ -71,11 +72,11 @@ export class KBucket extends EventEmitter { return this._kbucket.toArray() } - closest(id: string): PeerInfo[] { - return this._kbucket.closest(Buffer.from(id), KBUCKET_SIZE) + closest(id: Uint8Array): PeerInfo[] { + return this._kbucket.closest(id, KBUCKET_SIZE) } - remove(obj: Buffer | string | PeerInfo) { + remove(obj: Uint8Array | string | PeerInfo) { const peer = this.get(obj) if (peer !== null) this._kbucket.remove((peer as CustomContact).id) } diff --git a/packages/devp2p/src/dpt/message.ts b/packages/devp2p/src/dpt/message.ts index 01326b8fbc7..995d7e08c61 100644 --- a/packages/devp2p/src/dpt/message.ts +++ b/packages/devp2p/src/dpt/message.ts @@ -1,10 +1,18 @@ import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' +import { bytesToInt, intToBytes } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' import { ecdsaRecover, ecdsaSign } from 'ethereum-cryptography/secp256k1-compat' -import * as ip from 'ip' - -import { assertEq, buffer2int, int2buffer, keccak256, unstrictDecode } from '../util' +import { bytesToHex, bytesToUtf8, concatBytes } from 'ethereum-cryptography/utils' + +import { + assertEq, + ipToBytes, + ipToString, + isV4Format, + isV6Format, + keccak256, + unstrictDecode, +} from '../util' import type { PeerInfo } from './dpt' @@ -16,57 +24,55 @@ function getTimestamp() { const timestamp = { encode(value = getTimestamp() + 60) { - const buffer = Buffer.allocUnsafe(4) - buffer.writeUInt32BE(value, 0) - return buffer + const bytes = new Uint8Array(4) + new DataView(bytes.buffer).setUint32(0, value) + return bytes }, - decode(buffer: Buffer) { - if (buffer.length !== 4) - throw new RangeError(`Invalid timestamp buffer :${buffer.toString('hex')}`) - return buffer.readUInt32BE(0) + decode(bytes: Uint8Array) { + if (bytes.length !== 4) throw new RangeError(`Invalid timestamp bytes :${bytesToHex(bytes)}`) + return new DataView(bytes.buffer).getUint32(0) }, } const address = { encode(value: string) { - if (ip.isV4Format(value)) return ip.toBuffer(value) - if (ip.isV6Format(value)) return ip.toBuffer(value) + if (isV4Format(value)) return ipToBytes(value) + if (isV6Format(value)) return ipToBytes(value) throw new Error(`Invalid address: ${value}`) }, - decode(buffer: Buffer) { - if (buffer.length === 4) return ip.toString(buffer) - if (buffer.length === 16) return ip.toString(buffer) + decode(bytes: Uint8Array) { + if (bytes.length === 4) return ipToString(bytes) + if (bytes.length === 16) return ipToString(bytes) - const str = buffer.toString() - if (ip.isV4Format(str) || ip.isV6Format(str)) return str + const str = bytesToUtf8(bytes) + if (isV4Format(str) || isV6Format(str)) return str // also can be host, but skip it right now (because need async function for resolve) - throw new Error(`Invalid address buffer: ${buffer.toString('hex')}`) + throw new Error(`Invalid address bytes: ${bytesToHex(bytes)}`) }, } const port = { - encode(value: number | null): Buffer { - if (value === null) return Buffer.allocUnsafe(0) + encode(value: number | null): Uint8Array { + if (value === null) return new Uint8Array() if (value >>> 16 > 0) throw new RangeError(`Invalid port: ${value}`) - return Buffer.from([(value >>> 8) & 0xff, (value >>> 0) & 0xff]) + return Uint8Array.from([(value >>> 8) & 0xff, (value >>> 0) & 0xff]) }, - decode(buffer: Buffer): number | null { - if (buffer.length === 0) return null - // if (buffer.length !== 2) throw new RangeError(`Invalid port buffer: ${buffer.toString('hex')}`) - return buffer2int(buffer) + decode(bytes: Uint8Array): number | null { + if (bytes.length === 0) return null + return bytesToInt(bytes) }, } const endpoint = { - encode(obj: PeerInfo): Buffer[] { + encode(obj: PeerInfo): Uint8Array[] { return [ address.encode(obj.address!), port.encode(obj.udpPort ?? null), port.encode(obj.tcpPort ?? null), ] }, - decode(payload: Buffer[]): PeerInfo { + decode(payload: Uint8Array[]): PeerInfo { return { address: address.decode(payload[0]), udpPort: port.decode(payload[1]), @@ -75,12 +81,12 @@ const endpoint = { }, } -type InPing = { [0]: Buffer; [1]: Buffer[]; [2]: Buffer[]; [3]: Buffer } +type InPing = { [0]: Uint8Array; [1]: Uint8Array[]; [2]: Uint8Array[]; [3]: Uint8Array } type OutPing = { version: number; from: PeerInfo; to: PeerInfo; timestamp: number } const ping = { encode(obj: OutPing): InPing { return [ - int2buffer(obj.version), + intToBytes(obj.version), endpoint.encode(obj.from), endpoint.encode(obj.to), timestamp.encode(obj.timestamp), @@ -88,7 +94,7 @@ const ping = { }, decode(payload: InPing): OutPing { return { - version: buffer2int(payload[0]), + version: bytesToInt(payload[0]), from: endpoint.decode(payload[1]), to: endpoint.decode(payload[2]), timestamp: timestamp.decode(payload[3]), @@ -96,8 +102,8 @@ const ping = { }, } -type OutPong = { to: PeerInfo; hash: Buffer; timestamp: number } -type InPong = { [0]: Buffer[]; [1]: Buffer[]; [2]: Buffer } +type OutPong = { to: PeerInfo; hash: Uint8Array; timestamp: number } +type InPong = { [0]: Uint8Array[]; [1]: Uint8Array[]; [2]: Uint8Array } const pong = { encode(obj: OutPong) { return [endpoint.encode(obj.to), obj.hash, timestamp.encode(obj.timestamp)] @@ -112,7 +118,7 @@ const pong = { } type OutFindMsg = { id: string; timestamp: number } -type InFindMsg = { [0]: string; [1]: Buffer } +type InFindMsg = { [0]: string; [1]: Uint8Array } const findneighbours = { encode(obj: OutFindMsg): InFindMsg { return [obj.id, timestamp.encode(obj.timestamp)] @@ -126,11 +132,11 @@ const findneighbours = { } type InNeighborMsg = { peers: PeerInfo[]; timestamp: number } -type OutNeighborMsg = { [0]: Buffer[][]; [1]: Buffer } +type OutNeighborMsg = { [0]: Uint8Array[][]; [1]: Uint8Array } const neighbours = { encode(obj: InNeighborMsg): OutNeighborMsg { return [ - obj.peers.map((peer: PeerInfo) => endpoint.encode(peer).concat(peer.id! as Buffer)), + obj.peers.map((peer: PeerInfo) => endpoint.encode(peer).concat(peer.id! as Uint8Array)), timestamp.encode(obj.timestamp), ] }, @@ -168,36 +174,32 @@ const types: Types = { // 97 type // [98, length) data -export function encode(typename: string, data: T, privateKey: Buffer) { +export function encode(typename: string, data: T, privateKey: Uint8Array) { const type: number = types.byName[typename] as number if (type === undefined) throw new Error(`Invalid typename: ${typename}`) const encodedMsg = messages[typename].encode(data) - const typedata = Buffer.concat([ - Buffer.from([type]), - Buffer.from(RLP.encode(bufArrToArr(encodedMsg))), - ]) + const typedata = concatBytes(Uint8Array.from([type]), RLP.encode(encodedMsg)) const sighash = keccak256(typedata) const sig = ecdsaSign(sighash, privateKey) - const hashdata = Buffer.concat([Buffer.from(sig.signature), Buffer.from([sig.recid]), typedata]) + const hashdata = concatBytes(sig.signature, Uint8Array.from([sig.recid]), typedata) const hash = keccak256(hashdata) - return Buffer.concat([hash, hashdata]) + return concatBytes(hash, hashdata) } -export function decode(buffer: Buffer) { - const hash = keccak256(buffer.slice(32)) - assertEq(buffer.slice(0, 32), hash, 'Hash verification failed', debug) +export function decode(bytes: Uint8Array) { + const hash = keccak256(bytes.subarray(32)) + assertEq(bytes.subarray(0, 32), hash, 'Hash verification failed', debug) - const typedata = buffer.slice(97) + const typedata = bytes.subarray(97) const type = typedata[0] const typename = types.byType[type] if (typename === undefined) throw new Error(`Invalid type: ${type}`) - const data = messages[typename].decode(unstrictDecode(typedata.slice(1))) + const data = messages[typename].decode(unstrictDecode(typedata.subarray(1))) const sighash = keccak256(typedata) - const signature = buffer.slice(32, 96) - const recoverId = buffer[96] - const publicKey = Buffer.from(ecdsaRecover(signature, recoverId, sighash, false)) - + const signature = bytes.subarray(32, 96) + const recoverId = bytes[96] + const publicKey = ecdsaRecover(signature, recoverId, sighash, false) return { typename, data, publicKey } } diff --git a/packages/devp2p/src/dpt/server.ts b/packages/devp2p/src/dpt/server.ts index 1fd4abf41f9..8c70048fa25 100644 --- a/packages/devp2p/src/dpt/server.ts +++ b/packages/devp2p/src/dpt/server.ts @@ -1,5 +1,6 @@ import { debug as createDebugLogger } from 'debug' import * as dgram from 'dgram' +import { bytesToHex } from 'ethereum-cryptography/utils' import { EventEmitter } from 'events' import LRUCache = require('lru-cache') import ms = require('ms') @@ -42,7 +43,7 @@ export interface DPTServerOptions { export class Server extends EventEmitter { _dpt: DPT - _privateKey: Buffer + _privateKey: Uint8Array _timeout: number _endpoint: PeerInfo _requests: Map @@ -50,7 +51,7 @@ export class Server extends EventEmitter { _socket: DgramSocket | null _debug: Debugger - constructor(dpt: DPT, privateKey: Buffer, options: DPTServerOptions) { + constructor(dpt: DPT, privateKey: Uint8Array, options: DPTServerOptions) { super() this._dpt = dpt @@ -68,7 +69,7 @@ export class Server extends EventEmitter { this._socket.once('listening', () => this.emit('listening')) this._socket.once('close', () => this.emit('close')) this._socket.on('error', (err) => this.emit('error', err)) - this._socket.on('message', (msg: Buffer, rinfo: RemoteInfo) => { + this._socket.on('message', (msg: Uint8Array, rinfo: RemoteInfo) => { try { this._handler(msg, rinfo) } catch (err: any) { @@ -109,7 +110,7 @@ export class Server extends EventEmitter { }) const deferred = createDeferred() - const rkey = hash.toString('hex') + const rkey = bytesToHex(hash) this._requests.set(rkey, { peer, deferred, @@ -117,7 +118,7 @@ export class Server extends EventEmitter { if (this._requests.get(rkey) !== undefined) { this._debug( `ping timeout: ${peer.address}:${peer.udpPort} ${ - peer.id ? formatLogId(peer.id.toString('hex'), verbose) : '-' + peer.id ? formatLogId(bytesToHex(peer.id), verbose) : '-' }` ) this._requests.delete(rkey) @@ -131,7 +132,7 @@ export class Server extends EventEmitter { return deferred.promise } - findneighbours(peer: PeerInfo, id: Buffer) { + findneighbours(peer: PeerInfo, id: Uint8Array) { this._isAliveCheck() this._send(peer, 'findneighbours', { id }) } @@ -142,7 +143,7 @@ export class Server extends EventEmitter { _send(peer: PeerInfo, typename: string, data: any) { const debugMsg = `send ${typename} to ${peer.address}:${peer.udpPort} (peerId: ${ - peer.id ? formatLogId(peer.id.toString('hex'), verbose) : '-' + peer.id ? formatLogId(bytesToHex(peer.id), verbose) : '-' })` this.debug(typename, debugMsg) @@ -150,15 +151,15 @@ export class Server extends EventEmitter { if (this._socket && typeof peer.udpPort === 'number') this._socket.send(msg, 0, msg.length, peer.udpPort, peer.address) - return msg.slice(0, 32) // message id + return msg.subarray(0, 32) // message id } - _handler(msg: Buffer, rinfo: RemoteInfo) { - const info = decode(msg) + _handler(msg: Uint8Array, rinfo: RemoteInfo) { + const info = decode(msg) // Dgram serializes everything to `Uint8Array` const peerId = pk2id(info.publicKey) const debugMsg = `received ${info.typename} from ${rinfo.address}:${ rinfo.port - } (peerId: ${formatLogId(peerId.toString('hex'), verbose)})` + } (peerId: ${formatLogId(bytesToHex(peerId), verbose)})` this.debug(info.typename.toString(), debugMsg) // add peer if not in our table @@ -180,13 +181,13 @@ export class Server extends EventEmitter { udpPort: rinfo.port, tcpPort: info.data.from.tcpPort, }, - hash: msg.slice(0, 32), + hash: msg.subarray(0, 32), }) break } case 'pong': { - const rkey = info.data.hash.toString('hex') + const rkey = bytesToHex(info.data.hash) const request = this._requests.get(rkey) if (request !== undefined) { this._requests.delete(rkey) diff --git a/packages/devp2p/src/protocol/eth.ts b/packages/devp2p/src/protocol/eth.ts index ecaa5a49081..1b88a27b041 100644 --- a/packages/devp2p/src/protocol/eth.ts +++ b/packages/devp2p/src/protocol/eth.ts @@ -1,14 +1,16 @@ import { RLP } from '@ethereumjs/rlp' import { - arrToBufArr, - bigIntToBuffer, - bufArrToArr, - bufferToBigInt, - bufferToHex, + bigIntToBytes, + bytesToBigInt, + bytesToHex, + bytesToInt, + bytesToPrefixedHexString, + intToBytes, } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as snappy from 'snappyjs' -import { assertEq, buffer2int, formatLogData, formatLogId, int2buffer } from '../util' +import { assertEq, formatLogData, formatLogId } from '../util' import { EthProtocol, Protocol } from './protocol' @@ -52,14 +54,14 @@ export class ETH extends Protocol { static eth66 = { name: 'eth', version: 66, length: 17, constructor: ETH } _handleMessage(code: ETH.MESSAGE_CODES, data: any) { - const payload = arrToBufArr(RLP.decode(bufArrToArr(data))) + const payload = RLP.decode(data) const messageName = this.getMsgPrefix(code) const debugMsg = this.DEBUG ? `Received ${messageName} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}` : undefined if (code !== ETH.MESSAGE_CODES.STATUS && this.DEBUG) { - const logData = formatLogData(data.toString('hex'), this._verbose) + const logData = formatLogData(bytesToHex(data), this._verbose) this.debug(messageName, `${debugMsg}: ${logData}`) } switch (code) { @@ -116,11 +118,11 @@ export class ETH extends Protocol { * Eth 64 Fork ID validation (EIP-2124) * @param forkId Remote fork ID */ - _validateForkId(forkId: Buffer[]) { + _validateForkId(forkId: Uint8Array[]) { const c = this._peer._common - const peerForkHash = bufferToHex(forkId[0]) - const peerNextFork = bufferToBigInt(forkId[1]) + const peerForkHash = bytesToPrefixedHexString(forkId[0]) + const peerNextFork = bytesToBigInt(forkId[1]) if (this._forkHash === peerForkHash) { // There is a known next fork @@ -187,9 +189,9 @@ export class ETH extends Protocol { const status: any = { networkId: this._peerStatus[1], - td: Buffer.from(this._peerStatus[2] as Buffer), - bestHash: Buffer.from(this._peerStatus[3] as Buffer), - genesisHash: Buffer.from(this._peerStatus[4] as Buffer), + td: this._peerStatus[2] as Uint8Array, + bestHash: this._peerStatus[3] as Uint8Array, + genesisHash: this._peerStatus[4] as Uint8Array, } if (this._version >= 64) { @@ -200,7 +202,7 @@ export class ETH extends Protocol { this.debug.bind(this), 'STATUS' ) - this._validateForkId(this._peerStatus[5] as Buffer[]) + this._validateForkId(this._peerStatus[5] as Uint8Array[]) status['forkId'] = this._peerStatus[5] } @@ -214,28 +216,28 @@ export class ETH extends Protocol { return this._version } - _forkHashFromForkId(forkId: Buffer): string { - return `0x${forkId.toString('hex')}` + _forkHashFromForkId(forkId: Uint8Array): string { + return bytesToPrefixedHexString(forkId) } - _nextForkFromForkId(forkId: Buffer): number { - return buffer2int(forkId) + _nextForkFromForkId(forkId: Uint8Array): number { + return bytesToInt(forkId) } _getStatusString(status: ETH.StatusMsg) { - let sStr = `[V:${buffer2int(status[0] as Buffer)}, NID:${buffer2int(status[1] as Buffer)}, TD:${ - status[2].length === 0 ? 0 : buffer2int(status[2] as Buffer) - }` - sStr += `, BestH:${formatLogId(status[3].toString('hex'), this._verbose)}, GenH:${formatLogId( - status[4].toString('hex'), + let sStr = `[V:${bytesToInt(status[0] as Uint8Array)}, NID:${bytesToInt( + status[1] as Uint8Array + )}, TD:${status[2].length === 0 ? 0 : bytesToInt(status[2] as Uint8Array)}` + sStr += `, BestH:${formatLogId( + bytesToHex(status[3] as Uint8Array), this._verbose - )}` + )}, GenH:${formatLogId(bytesToHex(status[4] as Uint8Array), this._verbose)}` if (this._version >= 64) { sStr += `, ForkHash: ${ - status[5] !== undefined ? '0x' + (status[5][0] as Buffer).toString('hex') : '-' + status[5] !== undefined ? bytesToPrefixedHexString(status[5][0] as Uint8Array) : '-' }` sStr += `, ForkNext: ${ - (status[5][1] as Buffer).length > 0 ? buffer2int(status[5][1] as Buffer) : '-' + (status[5][1] as Uint8Array).length > 0 ? bytesToInt(status[5][1] as Uint8Array) : '-' }` } sStr += `]` @@ -245,15 +247,15 @@ export class ETH extends Protocol { sendStatus(status: ETH.StatusOpts) { if (this._status !== null) return this._status = [ - int2buffer(this._version), - bigIntToBuffer(this._peer._common.chainId()), + intToBytes(this._version), + bigIntToBytes(this._peer._common.chainId()), status.td, status.bestHash, status.genesisHash, ] if (this._version >= 64) { if (status.latestBlock) { - const latestBlock = bufferToBigInt(status.latestBlock) + const latestBlock = bytesToBigInt(status.latestBlock) if (latestBlock < this._latestBlock) { throw new Error( 'latest block provided is not matching the HF setting of the Common instance (Rlpx)' @@ -261,12 +263,10 @@ export class ETH extends Protocol { } this._latestBlock = latestBlock } - const forkHashB = Buffer.from(this._forkHash.substr(2), 'hex') + const forkHashB = hexToBytes(this._forkHash.substr(2)) const nextForkB = - this._nextForkBlock === BigInt(0) - ? Buffer.from('', 'hex') - : bigIntToBuffer(this._nextForkBlock) + this._nextForkBlock === BigInt(0) ? new Uint8Array() : bigIntToBytes(this._nextForkBlock) this._status.push([forkHashB, nextForkB]) } @@ -280,7 +280,7 @@ export class ETH extends Protocol { ) } - let payload = Buffer.from(RLP.encode(bufArrToArr(this._status))) + let payload = RLP.encode(this._status) // Use snappy compression if peer supports DevP2P >=v5 if (this._peer._hello !== null && this._peer._hello.protocolVersion >= 5) { @@ -292,10 +292,7 @@ export class ETH extends Protocol { } sendMessage(code: ETH.MESSAGE_CODES, payload: any) { - const logData = formatLogData( - Buffer.from(RLP.encode(bufArrToArr(payload))).toString('hex'), - this._verbose - ) + const logData = formatLogData(bytesToHex(RLP.encode(payload)), this._verbose) if (this.DEBUG) { const messageName = this.getMsgPrefix(code) const debugMsg = `Send ${messageName} message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: ${logData}` @@ -334,7 +331,7 @@ export class ETH extends Protocol { throw new Error(`Unknown code ${code}`) } - payload = Buffer.from(RLP.encode(bufArrToArr(payload))) + payload = RLP.encode(payload) // Use snappy compression if peer supports DevP2P >=v5 if (this._peer._hello !== null && this._peer._hello.protocolVersion >= 5) { @@ -350,13 +347,13 @@ export class ETH extends Protocol { } export namespace ETH { - export interface StatusMsg extends Array {} + export interface StatusMsg extends Array {} export type StatusOpts = { - td: Buffer - bestHash: Buffer - latestBlock?: Buffer - genesisHash: Buffer + td: Uint8Array + bestHash: Uint8Array + latestBlock?: Uint8Array + genesisHash: Uint8Array } export enum MESSAGE_CODES { diff --git a/packages/devp2p/src/protocol/les.ts b/packages/devp2p/src/protocol/les.ts index cdfe303adb2..0247f9bb8b3 100644 --- a/packages/devp2p/src/protocol/les.ts +++ b/packages/devp2p/src/protocol/les.ts @@ -1,10 +1,17 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr, bigIntToBuffer, bufArrToArr } from '@ethereumjs/util' +import { + bigIntToBytes, + bytesToHex, + bytesToInt, + bytesToUtf8, + intToBytes, + utf8ToBytes, +} from '@ethereumjs/util' import ms = require('ms') import * as snappy from 'snappyjs' import { DISCONNECT_REASONS } from '../rlpx/peer' -import { assertEq, buffer2int, formatLogData, int2buffer } from '../util' +import { assertEq, formatLogData } from '../util' import { EthProtocol, Protocol } from './protocol' @@ -30,12 +37,11 @@ export class LES extends Protocol { static les4 = { name: 'les', version: 4, length: 23, constructor: LES } _handleMessage(code: LES.MESSAGE_CODES, data: any) { - const payload = arrToBufArr(RLP.decode(bufArrToArr(data))) + const payload = RLP.decode(data) const messageName = this.getMsgPrefix(code) const debugMsg = `Received ${messageName} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}` - if (code !== LES.MESSAGE_CODES.STATUS) { - const logData = formatLogData(data.toString('hex'), this._verbose) + const logData = formatLogData(bytesToHex(data), this._verbose) this.debug(messageName, `${debugMsg}: ${logData}`) } switch (code) { @@ -49,7 +55,7 @@ export class LES extends Protocol { ) const statusArray: any = {} for (const value of payload as any) { - statusArray[value[0].toString()] = value[1] + statusArray[bytesToUtf8(value[0])] = value[1] } this._peerStatus = statusArray const peerStatusMsg = `${this._peerStatus ? this._getStatusString(this._peerStatus) : ''}` @@ -130,27 +136,27 @@ export class LES extends Protocol { } _getStatusString(status: LES.Status) { - let sStr = `[V:${buffer2int(status['protocolVersion'])}, ` - sStr += `NID:${buffer2int(status['networkId'] as Buffer)}, HTD:${buffer2int( + let sStr = `[V:${bytesToInt(status['protocolVersion'])}, ` + sStr += `NID:${bytesToInt(status['networkId'] as Uint8Array)}, HTD:${bytesToInt( status['headTd'] )}, ` - sStr += `HeadH:${status['headHash'].toString('hex')}, HeadN:${buffer2int(status['headNum'])}, ` - sStr += `GenH:${status['genesisHash'].toString('hex')}` + sStr += `HeadH:${bytesToHex(status['headHash'])}, HeadN:${bytesToInt(status['headNum'])}, ` + sStr += `GenH:${bytesToHex(status['genesisHash'])}` if (status['serveHeaders'] !== undefined) sStr += `, serveHeaders active` if (status['serveChainSince'] !== undefined) - sStr += `, ServeCS: ${buffer2int(status['serveChainSince'])}` + sStr += `, ServeCS: ${bytesToInt(status['serveChainSince'])}` if (status['serveStateSince'] !== undefined) - sStr += `, ServeSS: ${buffer2int(status['serveStateSince'])}` + sStr += `, ServeSS: ${bytesToInt(status['serveStateSince'])}` if (status['txRelay'] !== undefined) sStr += `, txRelay active` if (status['flowControl/BL)'] !== undefined) sStr += `, flowControl/BL set` if (status['flowControl/MRR)'] !== undefined) sStr += `, flowControl/MRR set` if (status['flowControl/MRC)'] !== undefined) sStr += `, flowControl/MRC set` if (status['forkID'] !== undefined) - sStr += `, forkID: [crc32: ${status['forkID'][0].toString('hex')}, nextFork: ${buffer2int( + sStr += `, forkID: [crc32: ${bytesToHex(status['forkID'][0])}, nextFork: ${bytesToInt( status['forkID'][1] )}]` if (status['recentTxLookup'] !== undefined) - sStr += `, recentTxLookup: ${buffer2int(status['recentTxLookup'])}` + sStr += `, recentTxLookup: ${bytesToInt(status['recentTxLookup'])}` sStr += `]` return sStr } @@ -159,16 +165,16 @@ export class LES extends Protocol { if (this._status !== null) return if (status.announceType === undefined) { - status['announceType'] = int2buffer(DEFAULT_ANNOUNCE_TYPE) + status['announceType'] = intToBytes(DEFAULT_ANNOUNCE_TYPE) } - status['protocolVersion'] = int2buffer(this._version) - status['networkId'] = bigIntToBuffer(this._peer._common.chainId()) + status['protocolVersion'] = intToBytes(this._version) + status['networkId'] = bigIntToBytes(this._peer._common.chainId()) this._status = status const statusList: any[][] = [] for (const key of Object.keys(status)) { - statusList.push([Buffer.from(key), status[key]]) + statusList.push([utf8ToBytes(key), status[key]]) } this.debug( @@ -178,7 +184,7 @@ export class LES extends Protocol { } (les${this._version}): ${this._getStatusString(this._status)}` ) - let payload = Buffer.from(RLP.encode(bufArrToArr(statusList))) + let payload = RLP.encode(statusList) // Use snappy compression if peer supports DevP2P >=v5 if (this._peer._hello !== null && this._peer._hello.protocolVersion >= 5) { @@ -196,10 +202,7 @@ export class LES extends Protocol { */ sendMessage(code: LES.MESSAGE_CODES, payload: any) { const messageName = this.getMsgPrefix(code) - const logData = formatLogData( - Buffer.from(RLP.encode(bufArrToArr(payload))).toString('hex'), - this._verbose - ) + const logData = formatLogData(bytesToHex(RLP.encode(payload)), this._verbose) const debugMsg = `Send ${messageName} message to ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}: ${logData}` this.debug(messageName, debugMsg) @@ -241,7 +244,7 @@ export class LES extends Protocol { throw new Error(`Unknown code ${code}`) } - payload = Buffer.from(RLP.encode(payload)) + payload = RLP.encode(payload) // Use snappy compression if peer supports DevP2P >=v5 if (this._peer._hello !== null && this._peer._hello.protocolVersion >= 5) { @@ -259,22 +262,22 @@ export class LES extends Protocol { export namespace LES { export interface Status { [key: string]: any - protocolVersion: Buffer - networkId: Buffer - headTd: Buffer - headHash: Buffer - headNum: Buffer - genesisHash: Buffer - serveHeaders: Buffer - serveChainSince: Buffer - serveStateSince: Buffer - txRelay: Buffer - 'flowControl/BL': Buffer - 'flowControl/MRR': Buffer - 'flowControl/MRC': Buffer - announceType: Buffer - forkID: [Buffer, Buffer] - recentTxLookup: Buffer + protocolVersion: Uint8Array + networkId: Uint8Array + headTd: Uint8Array + headHash: Uint8Array + headNum: Uint8Array + genesisHash: Uint8Array + serveHeaders: Uint8Array + serveChainSince: Uint8Array + serveStateSince: Uint8Array + txRelay: Uint8Array + 'flowControl/BL': Uint8Array + 'flowControl/MRR': Uint8Array + 'flowControl/MRC': Uint8Array + announceType: Uint8Array + forkID: [Uint8Array, Uint8Array] + recentTxLookup: Uint8Array } export enum MESSAGE_CODES { diff --git a/packages/devp2p/src/protocol/protocol.ts b/packages/devp2p/src/protocol/protocol.ts index 5f239ed9631..08e76240753 100644 --- a/packages/devp2p/src/protocol/protocol.ts +++ b/packages/devp2p/src/protocol/protocol.ts @@ -16,7 +16,7 @@ export enum EthProtocol { // What does this represent? type MessageCodes = { [key: number | string]: number | string } -export type SendMethod = (code: number, data: Buffer) => any +export type SendMethod = (code: number, data: Uint8Array) => any export class Protocol extends EventEmitter { _version: number diff --git a/packages/devp2p/src/protocol/snap.ts b/packages/devp2p/src/protocol/snap.ts index a3c03c11c7f..accead154d6 100644 --- a/packages/devp2p/src/protocol/snap.ts +++ b/packages/devp2p/src/protocol/snap.ts @@ -1,4 +1,5 @@ import { RLP, utils } from '@ethereumjs/rlp' +import { bytesToHex } from 'ethereum-cryptography/utils' import * as snappy from 'snappyjs' import { formatLogData } from '../util' @@ -21,7 +22,7 @@ export class SNAP extends Protocol { // Note, this needs optimization, see issue #1882 const debugMsg = `Received ${messageName} message from ${this._peer._socket.remoteAddress}:${this._peer._socket.remotePort}` - const logData = formatLogData(data.toString('hex'), this._verbose) + const logData = formatLogData(bytesToHex(data), this._verbose) this.debug(messageName, `${debugMsg}: ${logData}`) switch (code) { diff --git a/packages/devp2p/src/rlpx/ecies.ts b/packages/devp2p/src/rlpx/ecies.ts index 0df8f1f7c2f..5c7e540042a 100644 --- a/packages/devp2p/src/rlpx/ecies.ts +++ b/packages/devp2p/src/rlpx/ecies.ts @@ -1,16 +1,16 @@ import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' +import { bytesToInt, concatBytes, intToBytes } from '@ethereumjs/util' import * as crypto from 'crypto' import { debug as createDebugLogger } from 'debug' +import { getRandomBytesSync } from 'ethereum-cryptography/random' import { getPublicKey } from 'ethereum-cryptography/secp256k1' import { ecdh, ecdsaRecover, ecdsaSign } from 'ethereum-cryptography/secp256k1-compat' +import { hexToBytes } from 'ethereum-cryptography/utils' import { assertEq, - buffer2int, genPrivateKey, id2pk, - int2buffer, keccak256, pk2id, unstrictDecode, @@ -23,15 +23,15 @@ type Decipher = crypto.Decipher const debug = createDebugLogger('devp2p:rlpx:peer') -function ecdhX(publicKey: Buffer, privateKey: Buffer) { +function ecdhX(publicKey: Uint8Array, privateKey: Uint8Array) { // return (publicKey * privateKey).x function hashfn(x: Uint8Array, y: Uint8Array) { const pubKey = new Uint8Array(33) pubKey[0] = (y[31] & 1) === 0 ? 0x02 : 0x03 pubKey.set(x, 1) - return pubKey.slice(1) + return pubKey.subarray(1) } - return Buffer.from(ecdh(publicKey, privateKey, { hashfn }, Buffer.alloc(32))) + return ecdh(publicKey, privateKey, { hashfn }, new Uint8Array(32)) } // a straight rip from python interop w/go ecies implementation @@ -41,121 +41,125 @@ function ecdhX(publicKey: Buffer, privateKey: Buffer) { // https://github.com/ethereum/pydevp2p/blob/master/devp2p/crypto.py#L295 // https://github.com/ethereum/go-ethereum/blob/fe532a98f9f32bb81ef0d8d013cf44327830d11e/crypto/ecies/ecies.go#L165 // https://github.com/ethereum/cpp-ethereum/blob/develop/libdevcrypto/CryptoPP.cpp#L36 -function concatKDF(keyMaterial: Buffer, keyLength: number) { +function concatKDF(keyMaterial: Uint8Array, keyLength: number) { const SHA256BlockSize = 64 const reps = ((keyLength + 7) * 8) / (SHA256BlockSize * 8) - const buffers = [] - for (let counter = 0, tmp = Buffer.allocUnsafe(4); counter <= reps; ) { + const bytes = [] + for (let counter = 0, tmp = new Uint8Array(4); counter <= reps; ) { counter += 1 - tmp.writeUInt32BE(counter, 0) - buffers.push(crypto.createHash('sha256').update(tmp).update(keyMaterial).digest()) + new DataView(tmp.buffer).setUint32(0, counter) + bytes.push( + Uint8Array.from(crypto.createHash('sha256').update(tmp).update(keyMaterial).digest()) + ) } - return Buffer.concat(buffers).slice(0, keyLength) + return concatBytes(...bytes).subarray(0, keyLength) } export class ECIES { - _privateKey: Buffer - _publicKey: Buffer - _remotePublicKey: Buffer | null - _nonce: Buffer - _remoteNonce: Buffer | null = null - _initMsg: Buffer | null | undefined = null - _remoteInitMsg: Buffer | null = null + _privateKey: Uint8Array + _publicKey: Uint8Array + _remotePublicKey: Uint8Array | null + _nonce: Uint8Array + _remoteNonce: Uint8Array | null = null + _initMsg: Uint8Array | null | undefined = null + _remoteInitMsg: Uint8Array | null = null _gotEIP8Auth: boolean = false _gotEIP8Ack: boolean = false _ingressAes: Decipher | null = null _egressAes: Decipher | null = null _ingressMac: MAC | null = null _egressMac: MAC | null = null - _ephemeralPrivateKey: Buffer - _ephemeralPublicKey: Buffer - _remoteEphemeralPublicKey: Buffer | null = null // we don't need store this key, but why don't? - _ephemeralSharedSecret: Buffer | null = null + _ephemeralPrivateKey: Uint8Array + _ephemeralPublicKey: Uint8Array + _remoteEphemeralPublicKey: Uint8Array | null = null // we don't need store this key, but why don't? + _ephemeralSharedSecret: Uint8Array | null = null _bodySize: number | null = null - constructor(privateKey: Buffer, id: Buffer, remoteId: Buffer) { + constructor(privateKey: Uint8Array, id: Uint8Array, remoteId: Uint8Array) { this._privateKey = privateKey this._publicKey = id2pk(id) this._remotePublicKey = remoteId !== null ? id2pk(remoteId) : null - this._nonce = crypto.randomBytes(32) + this._nonce = getRandomBytesSync(32) this._ephemeralPrivateKey = genPrivateKey() - this._ephemeralPublicKey = Buffer.from(getPublicKey(this._ephemeralPrivateKey, false)) + this._ephemeralPublicKey = getPublicKey(this._ephemeralPrivateKey, false) } - _encryptMessage(data: Buffer, sharedMacData: Buffer | null = null): Buffer | undefined { + _encryptMessage( + data: Uint8Array, + sharedMacData: Uint8Array | null = null + ): Uint8Array | undefined { const privateKey = genPrivateKey() if (!this._remotePublicKey) return const x = ecdhX(this._remotePublicKey, privateKey) const key = concatKDF(x, 32) - const ekey = key.slice(0, 16) // encryption key - const mkey = crypto.createHash('sha256').update(key.slice(16, 32)).digest() // MAC key + const ekey = key.subarray(0, 16) // encryption key + const mkey = crypto.createHash('sha256').update(key.subarray(16, 32)).digest() // MAC key // encrypt - const IV = crypto.randomBytes(16) + const IV = getRandomBytesSync(16) const cipher = crypto.createCipheriv('aes-128-ctr', ekey, IV) - const encryptedData = cipher.update(data) - const dataIV = Buffer.concat([IV, encryptedData]) + const encryptedData = Uint8Array.from(cipher.update(data)) + const dataIV = concatBytes(IV, encryptedData) // create tag if (!sharedMacData) { - sharedMacData = Buffer.from([]) + sharedMacData = Uint8Array.from([]) } - const tag = crypto - .createHmac('sha256', mkey) - .update(Buffer.concat([dataIV, sharedMacData])) - .digest() + const tag = Uint8Array.from( + crypto.createHmac('sha256', mkey).update(concatBytes(dataIV, sharedMacData)).digest() + ) const publicKey = getPublicKey(privateKey, false) - return Buffer.concat([publicKey, dataIV, tag]) + return concatBytes(publicKey, dataIV, tag) } - _decryptMessage(data: Buffer, sharedMacData: Buffer | null = null): Buffer { + _decryptMessage(data: Uint8Array, sharedMacData: Uint8Array | null = null): Uint8Array { assertEq( - data.slice(0, 1), - Buffer.from('04', 'hex'), + data.subarray(0, 1), + hexToBytes('04'), 'wrong ecies header (possible cause: EIP8 upgrade)', debug ) - const publicKey = data.slice(0, 65) - const dataIV = data.slice(65, -32) - const tag = data.slice(-32) + const publicKey = data.subarray(0, 65) + const dataIV = data.subarray(65, -32) + const tag = data.subarray(-32) // derive keys const x = ecdhX(publicKey, this._privateKey) const key = concatKDF(x, 32) - const ekey = key.slice(0, 16) // encryption key - const mkey = crypto.createHash('sha256').update(key.slice(16, 32)).digest() // MAC key + const ekey = key.subarray(0, 16) // encryption key + const mkey = Uint8Array.from(crypto.createHash('sha256').update(key.subarray(16, 32)).digest()) // MAC key // check the tag if (!sharedMacData) { - sharedMacData = Buffer.from([]) + sharedMacData = Uint8Array.from([]) } const _tag = crypto .createHmac('sha256', mkey) - .update(Buffer.concat([dataIV, sharedMacData])) + .update(concatBytes(dataIV, sharedMacData)) .digest() assertEq(_tag, tag, 'should have valid tag', debug) // decrypt data - const IV = dataIV.slice(0, 16) - const encryptedData = dataIV.slice(16) + const IV = dataIV.subarray(0, 16) + const encryptedData = dataIV.subarray(16) const decipher = crypto.createDecipheriv('aes-128-ctr', ekey, IV) - return decipher.update(encryptedData) + return Uint8Array.from(decipher.update(encryptedData)) } - _setupFrame(remoteData: Buffer, incoming: boolean): void { + _setupFrame(remoteData: Uint8Array, incoming: boolean): void { if (!this._remoteNonce) return const nonceMaterial = incoming - ? Buffer.concat([this._nonce, this._remoteNonce]) - : Buffer.concat([this._remoteNonce, this._nonce]) + ? concatBytes(this._nonce, this._remoteNonce) + : concatBytes(this._remoteNonce, this._nonce) const hNonce = keccak256(nonceMaterial) if (!this._ephemeralSharedSecret) return - const IV = Buffer.allocUnsafe(16).fill(0x00) + const IV = new Uint8Array(16).fill(0x00) const sharedSecret = keccak256(this._ephemeralSharedSecret, hNonce) const aesSecret = keccak256(this._ephemeralSharedSecret, sharedSecret) @@ -164,11 +168,11 @@ export class ECIES { const macSecret = keccak256(this._ephemeralSharedSecret, aesSecret) this._ingressMac = new MAC(macSecret) - this._ingressMac.update(Buffer.concat([xor(macSecret, this._nonce), remoteData])) + this._ingressMac.update(concatBytes(xor(macSecret, this._nonce), remoteData)) this._egressMac = new MAC(macSecret) if (this._initMsg === null || this._initMsg === undefined) return - this._egressMac.update(Buffer.concat([xor(macSecret, this._remoteNonce), this._initMsg])) + this._egressMac.update(concatBytes(xor(macSecret, this._remoteNonce), this._initMsg)) } createAuthEIP8() { @@ -176,44 +180,47 @@ export class ECIES { const x = ecdhX(this._remotePublicKey, this._privateKey) const sig = ecdsaSign(xor(x, this._nonce), this._ephemeralPrivateKey) const data = [ - Buffer.concat([Buffer.from(sig.signature), Buffer.from([sig.recid])]), + concatBytes(sig.signature, Uint8Array.from([sig.recid])), // keccak256(pk2id(this._ephemeralPublicKey)), pk2id(this._publicKey), this._nonce, - Buffer.from([0x04]), + Uint8Array.from([0x04]), ] - const dataRLP = Buffer.from(RLP.encode(bufArrToArr(data))) - const pad = crypto.randomBytes(100 + Math.floor(Math.random() * 151)) // Random padding between 100, 250 - const authMsg = Buffer.concat([dataRLP, pad]) + const dataRLP = RLP.encode(data) + const pad = getRandomBytesSync(100 + Math.floor(Math.random() * 151)) // Random padding between 100, 250 + const authMsg = concatBytes(dataRLP, pad) const overheadLength = 113 - const sharedMacData = int2buffer(authMsg.length + overheadLength) + const sharedMacData = intToBytes(authMsg.length + overheadLength) const encryptedMsg = this._encryptMessage(authMsg, sharedMacData) if (!encryptedMsg) return - this._initMsg = Buffer.concat([sharedMacData, encryptedMsg]) + this._initMsg = concatBytes(sharedMacData, encryptedMsg) return this._initMsg } - createAuthNonEIP8(): Buffer | undefined { + createAuthNonEIP8(): Uint8Array | undefined { if (!this._remotePublicKey) return const x = ecdhX(this._remotePublicKey, this._privateKey) const sig = ecdsaSign(xor(x, this._nonce), this._ephemeralPrivateKey) - const data = Buffer.concat([ - Buffer.from(sig.signature), - Buffer.from([sig.recid]), + const data = concatBytes( + sig.signature, + Uint8Array.from([sig.recid]), keccak256(pk2id(this._ephemeralPublicKey)), pk2id(this._publicKey), this._nonce, - Buffer.from([0x00]), - ]) + Uint8Array.from([0x00]) + ) this._initMsg = this._encryptMessage(data) return this._initMsg } - parseAuthPlain(data: Buffer, sharedMacData: Buffer | null = null): Buffer | undefined { - const prefix = sharedMacData !== null ? sharedMacData : Buffer.from([]) - this._remoteInitMsg = Buffer.concat([prefix, data]) + parseAuthPlain( + data: Uint8Array, + sharedMacData: Uint8Array | null = null + ): Uint8Array | undefined { + const prefix = sharedMacData !== null ? sharedMacData : new Uint8Array() + this._remoteInitMsg = concatBytes(prefix, data) const decrypted = this._decryptMessage(data, sharedMacData) let signature = null @@ -225,15 +232,15 @@ export class ECIES { if (!this._gotEIP8Auth) { assertEq(decrypted.length, 194, 'invalid packet length', debug) - signature = decrypted.slice(0, 64) + signature = decrypted.subarray(0, 64) recoveryId = decrypted[64] - heid = decrypted.slice(65, 97) // 32 bytes - remotePublicKey = id2pk(decrypted.slice(97, 161)) - nonce = decrypted.slice(161, 193) + heid = decrypted.subarray(65, 97) // 32 bytes + remotePublicKey = id2pk(decrypted.subarray(97, 161)) + nonce = decrypted.subarray(161, 193) } else { - const decoded = unstrictDecode(decrypted) as Buffer[] + const decoded = unstrictDecode(decrypted) as Uint8Array[] - signature = decoded[0].slice(0, 64) + signature = decoded[0].subarray(0, 64) recoveryId = decoded[0][64] remotePublicKey = id2pk(decoded[1]) nonce = decoded[2] @@ -249,8 +256,11 @@ export class ECIES { if (this._remoteNonce === null) { return } - this._remoteEphemeralPublicKey = Buffer.from( - ecdsaRecover(signature, recoveryId, xor(x, this._remoteNonce), false) + this._remoteEphemeralPublicKey = ecdsaRecover( + signature, + recoveryId, + xor(x, this._remoteNonce), + false ) if (this._remoteEphemeralPublicKey === null) return @@ -265,31 +275,31 @@ export class ECIES { } } - parseAuthEIP8(data: Buffer): void { - const size = buffer2int(data.slice(0, 2)) + 2 + parseAuthEIP8(data: Uint8Array): void { + const size = bytesToInt(data.subarray(0, 2)) + 2 assertEq(data.length, size, 'message length different from specified size (EIP8)', debug) - this.parseAuthPlain(data.slice(2), data.slice(0, 2)) + this.parseAuthPlain(data.subarray(2), data.subarray(0, 2)) } - createAckEIP8(): Buffer | undefined { - const data = [pk2id(this._ephemeralPublicKey), this._nonce, Buffer.from([0x04])] + createAckEIP8(): Uint8Array | undefined { + const data = [pk2id(this._ephemeralPublicKey), this._nonce, Uint8Array.from([0x04])] - const dataRLP = Buffer.from(RLP.encode(bufArrToArr(data))) - const pad = crypto.randomBytes(100 + Math.floor(Math.random() * 151)) // Random padding between 100, 250 - const ackMsg = Buffer.concat([dataRLP, pad]) + const dataRLP = RLP.encode(data) + const pad = getRandomBytesSync(100 + Math.floor(Math.random() * 151)) // Random padding between 100, 250 + const ackMsg = concatBytes(dataRLP, pad) const overheadLength = 113 - const sharedMacData = int2buffer(ackMsg.length + overheadLength) + const sharedMacData = intToBytes(ackMsg.length + overheadLength) const encryptedMsg = this._encryptMessage(ackMsg, sharedMacData) if (!encryptedMsg) return - this._initMsg = Buffer.concat([sharedMacData, encryptedMsg]) + this._initMsg = concatBytes(sharedMacData, encryptedMsg) if (!this._remoteInitMsg) return this._setupFrame(this._remoteInitMsg, true) return this._initMsg } - createAckOld(): Buffer | undefined { - const data = Buffer.concat([pk2id(this._ephemeralPublicKey), this._nonce, Buffer.from([0x00])]) + createAckOld(): Uint8Array | undefined { + const data = concatBytes(pk2id(this._ephemeralPublicKey), this._nonce, new Uint8Array([0x00])) this._initMsg = this._encryptMessage(data) @@ -298,7 +308,7 @@ export class ECIES { return this._initMsg } - parseAckPlain(data: Buffer, sharedMacData: Buffer | null = null): void { + parseAckPlain(data: Uint8Array, sharedMacData: Uint8Array | null = null): void { const decrypted = this._decryptMessage(data, sharedMacData) let remoteEphemeralPublicKey = null @@ -308,10 +318,10 @@ export class ECIES { assertEq(decrypted.length, 97, 'invalid packet length', debug) assertEq(decrypted[96], 0, 'invalid postfix', debug) - remoteEphemeralPublicKey = id2pk(decrypted.slice(0, 64)) - remoteNonce = decrypted.slice(64, 96) + remoteEphemeralPublicKey = id2pk(decrypted.subarray(0, 64)) + remoteNonce = decrypted.subarray(64, 96) } else { - const decoded = unstrictDecode(decrypted) as Buffer[] + const decoded = unstrictDecode(decrypted) as Uint8Array[] remoteEphemeralPublicKey = id2pk(decoded[0]) remoteNonce = decoded[1] @@ -323,74 +333,74 @@ export class ECIES { this._ephemeralSharedSecret = ecdhX(this._remoteEphemeralPublicKey, this._ephemeralPrivateKey) if (!sharedMacData) { - sharedMacData = Buffer.from([]) + sharedMacData = Uint8Array.from([]) } - this._setupFrame(Buffer.concat([sharedMacData, data]), false) + this._setupFrame(concatBytes(sharedMacData, data), false) } - parseAckEIP8(data: Buffer): void { - const size = buffer2int(data.slice(0, 2)) + 2 + parseAckEIP8(data: Uint8Array): void { + const size = bytesToInt(data.subarray(0, 2)) + 2 assertEq(data.length, size, 'message length different from specified size (EIP8)', debug) - this.parseAckPlain(data.slice(2), data.slice(0, 2)) + this.parseAckPlain(data.subarray(2), data.subarray(0, 2)) } - createHeader(size: number): Buffer | undefined { - const bufSize = zfill(int2buffer(size), 3) - const headerData = Buffer.from(RLP.encode([0, 0])) // [capability-id, context-id] (currently unused in spec) - let header = Buffer.concat([bufSize, headerData]) + createHeader(size: number): Uint8Array | undefined { + const bufSize = zfill(intToBytes(size), 3) + const headerData = RLP.encode([0, 0]) // [capability-id, context-id] (currently unused in spec) + let header = concatBytes(bufSize, headerData) header = zfill(header, 16, false) if (!this._egressAes) return - header = this._egressAes.update(header) + header = Uint8Array.from(this._egressAes.update(header)) if (!this._egressMac) return this._egressMac.updateHeader(header) - const tag = this._egressMac.digest() + const tag = Uint8Array.from(this._egressMac.digest()) - return Buffer.concat([header, tag]) + return concatBytes(header, tag) } - parseHeader(data: Buffer): number | undefined { + parseHeader(data: Uint8Array): number | undefined { // parse header - let header = data.slice(0, 16) - const mac = data.slice(16, 32) + let header = data.subarray(0, 16) + const mac = data.subarray(16, 32) if (!this._ingressMac) return this._ingressMac.updateHeader(header) - const _mac = this._ingressMac.digest() + const _mac = Uint8Array.from(this._ingressMac.digest()) assertEq(_mac, mac, 'Invalid MAC', debug) if (!this._ingressAes) return - header = this._ingressAes.update(header) - this._bodySize = buffer2int(header.slice(0, 3)) + header = Uint8Array.from(this._ingressAes.update(header)) + this._bodySize = bytesToInt(header.subarray(0, 3)) return this._bodySize } - createBody(data: Buffer): Buffer | undefined { + createBody(data: Uint8Array): Uint8Array | undefined { data = zfill(data, Math.ceil(data.length / 16) * 16, false) if (!this._egressAes) return - const encryptedData = this._egressAes.update(data) + const encryptedData = Uint8Array.from(this._egressAes.update(data)) if (!this._egressMac) return this._egressMac.updateBody(encryptedData) - const tag = this._egressMac.digest() - return Buffer.concat([encryptedData, tag]) + const tag = Uint8Array.from(this._egressMac.digest()) + return concatBytes(encryptedData, tag) } - parseBody(data: Buffer): Buffer | undefined { + parseBody(data: Uint8Array): Uint8Array | undefined { if (this._bodySize === null) throw new Error('need to parse header first') - const body = data.slice(0, -16) - const mac = data.slice(-16) + const body = data.subarray(0, -16) + const mac = data.subarray(-16) if (!this._ingressMac) return this._ingressMac.updateBody(body) - const _mac = this._ingressMac.digest() + const _mac = Uint8Array.from(this._ingressMac.digest()) assertEq(_mac, mac, 'Invalid MAC', debug) const size = this._bodySize this._bodySize = null if (!this._ingressAes) return - return this._ingressAes.update(body).slice(0, size) + return Uint8Array.from(this._ingressAes.update(body)).subarray(0, size) } } diff --git a/packages/devp2p/src/rlpx/mac.ts b/packages/devp2p/src/rlpx/mac.ts index a19c7a3b08c..8a50c89c8f7 100644 --- a/packages/devp2p/src/rlpx/mac.ts +++ b/packages/devp2p/src/rlpx/mac.ts @@ -7,23 +7,23 @@ export type Hash = ReturnType export class MAC { _hash: Hash - _secret: Buffer - constructor(secret: Buffer) { + _secret: Uint8Array + constructor(secret: Uint8Array) { this._hash = keccak256.create() this._secret = secret } - update(data: Buffer | string) { + update(data: Uint8Array | string) { this._hash.update(data) } - updateHeader(data: Buffer | string) { + updateHeader(data: Uint8Array | string) { const aes = createCipheriv('aes-256-ecb', this._secret, '') const encrypted = aes.update(this.digest()) this._hash.update(xor(encrypted, data)) } - updateBody(data: Buffer | string) { + updateBody(data: Uint8Array | string) { this._hash.update(data) const prev = this.digest() const aes = createCipheriv('aes-256-ecb', this._secret, '') @@ -32,6 +32,6 @@ export class MAC { } digest() { - return Buffer.from(this._hash.clone().digest().slice(0, 16)) + return Uint8Array.from(this._hash.clone().digest().subarray(0, 16)) } } diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 152f8f8e1d2..06adf22e37d 100644 --- a/packages/devp2p/src/rlpx/peer.ts +++ b/packages/devp2p/src/rlpx/peer.ts @@ -1,12 +1,19 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr, bufArrToArr } from '@ethereumjs/util' -import BufferList = require('bl') +import { + bytesToHex, + bytesToInt, + concatBytes, + equalsBytes, + intToBytes, + utf8ToBytes, +} from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' +import { bytesToUtf8, hexToBytes } from 'ethereum-cryptography/utils' import { EventEmitter } from 'events' import ms = require('ms') import * as snappy from 'snappyjs' -import { buffer2int, devp2pDebug, formatLogData, int2buffer } from '../util' +import { devp2pDebug, formatLogData } from '../util' import { ECIES } from './ecies' @@ -47,11 +54,11 @@ export enum DISCONNECT_REASONS { } export type HelloMsg = { - 0: Buffer - 1: Buffer - 2: Buffer[][] - 3: Buffer - 4: Buffer + 0: Uint8Array + 1: Uint8Array + 2: Uint8Array[][] + 3: Uint8Array + 4: Uint8Array length: 5 } @@ -77,25 +84,25 @@ export interface Hello { clientId: string capabilities: Capabilities[] port: number - id: Buffer + id: Uint8Array } export class Peer extends EventEmitter { - _clientId: Buffer + _clientId: Uint8Array _capabilities?: Capabilities[] _common: Common _port: number - _id: Buffer + _id: Uint8Array _remoteClientIdFilter: any - _remoteId: Buffer - _EIP8: Buffer | boolean + _remoteId: Uint8Array + _EIP8: Uint8Array | boolean _eciesSession: ECIES _state: string _weHello: HelloMsg | null _hello: Hello | null _nextPacketSize: number _socket: Socket - _socketData: BufferList + _socketData: Uint8Array _pingIntervalId: NodeJS.Timeout | null _pingTimeoutId: NodeJS.Timeout | null _closed: boolean @@ -135,7 +142,7 @@ export class Peer extends EventEmitter { // socket this._socket = options.socket - this._socketData = new BufferList() + this._socketData = new Uint8Array() this._socket.on('data', this._onSocketData.bind(this)) this._socket.on('error', (err: Error) => this.emit('error', err)) this._socket.once('close', this._onSocketClose.bind(this)) @@ -208,10 +215,10 @@ export class Peer extends EventEmitter { * @param code * @param data */ - _sendMessage(code: number, data: Buffer) { + _sendMessage(code: number, data: Uint8Array) { if (this._closed) return false - const msg = Buffer.concat([Buffer.from(RLP.encode(code)), data]) + const msg = concatBytes(RLP.encode(code), data) const header = this._eciesSession.createHeader(msg.length) if (!header || this._socket.destroyed) return this._socket.write(header) @@ -236,22 +243,19 @@ export class Peer extends EventEmitter { // TODO: Remove when we can also serve snap requests from other peers .filter((c) => c.name !== 'snap') .map((c) => `${c.name}${c.version}`) - .join(',')} clientId=${this._clientId}` + .join(',')} clientId=${bytesToUtf8(this._clientId)}` this.debug('HELLO', debugMsg) const payload: HelloMsg = [ - int2buffer(BASE_PROTOCOL_VERSION), + intToBytes(BASE_PROTOCOL_VERSION), this._clientId, - this._capabilities!.map((c) => [Buffer.from(c.name), int2buffer(c.version)]), - this._port === null ? Buffer.allocUnsafe(0) : int2buffer(this._port), + this._capabilities!.map((c) => [utf8ToBytes(c.name), intToBytes(c.version)]), + this._port === null ? new Uint8Array(0) : intToBytes(this._port), this._id, ] if (!this._closed) { if ( - this._sendMessage( - PREFIXES.HELLO, - Buffer.from(RLP.encode(bufArrToArr(payload as unknown as Buffer[]))) - ) === true + this._sendMessage(PREFIXES.HELLO, RLP.encode(payload as never as Uint8Array[])) === true ) { this._weHello = payload } @@ -269,7 +273,7 @@ export class Peer extends EventEmitter { const reasonName = this.getDisconnectPrefix(reason) const debugMsg = `Send DISCONNECT to ${this._socket.remoteAddress}:${this._socket.remotePort} (reason: ${reasonName})` this.debug('DISCONNECT', debugMsg, reasonName) - const data = Buffer.from(RLP.encode(reason)) + const data = RLP.encode(reason) if (this._sendMessage(PREFIXES.DISCONNECT, data) !== true) return this._disconnectReason = reason @@ -284,7 +288,7 @@ export class Peer extends EventEmitter { _sendPing() { const debugMsg = `Send PING to ${this._socket.remoteAddress}:${this._socket.remotePort}` this.debug('PING', debugMsg) - let data = Buffer.from(RLP.encode([])) + let data = RLP.encode([]) if (this._hello !== null && this._hello.protocolVersion >= 5) { data = snappy.compress(data) } @@ -303,7 +307,7 @@ export class Peer extends EventEmitter { _sendPong() { const debugMsg = `Send PONG to ${this._socket.remoteAddress}:${this._socket.remotePort}` this.debug('PONG', debugMsg) - let data = Buffer.from(RLP.encode([])) + let data = RLP.encode([]) if (this._hello !== null && this._hello.protocolVersion >= 5) { data = snappy.compress(data) @@ -316,13 +320,13 @@ export class Peer extends EventEmitter { */ _handleAuth() { const bytesCount = this._nextPacketSize - const parseData = this._socketData.slice(0, bytesCount) + const parseData = this._socketData.subarray(0, bytesCount) if (!this._eciesSession._gotEIP8Auth) { - if (parseData.slice(0, 1) === Buffer.from('04', 'hex')) { + if (parseData.subarray(0, 1) === hexToBytes('04')) { this._eciesSession.parseAuthPlain(parseData) } else { this._eciesSession._gotEIP8Auth = true - this._nextPacketSize = buffer2int(this._socketData.slice(0, 2)) + 2 + this._nextPacketSize = bytesToInt(this._socketData.subarray(0, 2)) + 2 return } } else { @@ -331,7 +335,7 @@ export class Peer extends EventEmitter { this._state = 'Header' this._nextPacketSize = 32 process.nextTick(() => this._sendAck()) - this._socketData.consume(bytesCount) + this._socketData = this._socketData.subarray(bytesCount) } /** @@ -339,16 +343,16 @@ export class Peer extends EventEmitter { */ _handleAck() { const bytesCount = this._nextPacketSize - const parseData = this._socketData.slice(0, bytesCount) + const parseData = this._socketData.subarray(0, bytesCount) if (!this._eciesSession._gotEIP8Ack) { - if (parseData.slice(0, 1) === Buffer.from('04', 'hex')) { + if (parseData.subarray(0, 1) === hexToBytes('04')) { this._eciesSession.parseAckPlain(parseData) this._logger( `Received ack (old format) from ${this._socket.remoteAddress}:${this._socket.remotePort}` ) } else { this._eciesSession._gotEIP8Ack = true - this._nextPacketSize = buffer2int(this._socketData.slice(0, 2)) + 2 + this._nextPacketSize = bytesToInt(this._socketData.subarray(0, 2)) + 2 return } } else { @@ -360,7 +364,7 @@ export class Peer extends EventEmitter { this._state = 'Header' this._nextPacketSize = 32 process.nextTick(() => this._sendHello()) - this._socketData.consume(bytesCount) + this._socketData = this._socketData.subarray(bytesCount) } /** @@ -368,12 +372,12 @@ export class Peer extends EventEmitter { */ _handleHello(payload: any) { this._hello = { - protocolVersion: buffer2int(payload[0]), - clientId: payload[1].toString(), + protocolVersion: bytesToInt(payload[0]), + clientId: bytesToUtf8(payload[1]), capabilities: payload[2].map((item: any) => { - return { name: item[0].toString(), version: buffer2int(item[1]) } + return { name: bytesToUtf8(item[0]), version: bytesToInt(item[1]) } }), - port: buffer2int(payload[3]), + port: bytesToInt(payload[3]), id: payload[4], } @@ -385,8 +389,8 @@ export class Peer extends EventEmitter { this.debug('HELLO', debugMsg) if (this._remoteId === null) { - this._remoteId = Buffer.from(this._hello.id) - } else if (!this._remoteId.equals(this._hello.id)) { + this._remoteId = this._hello.id + } else if (!equalsBytes(this._remoteId, this._hello.id)) { return this.disconnect(DISCONNECT_REASONS.INVALID_IDENTITY) } @@ -418,7 +422,7 @@ export class Peer extends EventEmitter { // The send method handed over to the subprotocol object (e.g. an `ETH` instance). // The subprotocol is then calling into the lower level method // (e.g. `ETH` calling into `Peer._sendMessage()`). - const sendMethod = (code: number, data: Buffer) => { + const sendMethod = (code: number, data: Uint8Array) => { if (code > obj.length) throw new Error('Code out of range') this._sendMessage(_offset + code, data) } @@ -447,10 +451,11 @@ export class Peer extends EventEmitter { */ _handleDisconnect(payload: any) { this._closed = true - // When `payload` is from rlpx it is `Buffer` and when from subprotocol it is `[Buffer]` - this._disconnectReason = Buffer.isBuffer(payload) - ? buffer2int(payload) - : buffer2int(payload[0] ?? Buffer.from([0])) + // When `payload` is from rlpx it is `Uint8Array` and when from subprotocol it is `[Uint8Array]` + this._disconnectReason = + payload instanceof Uint8Array + ? bytesToInt(payload) + : bytesToInt(payload[0] ?? Uint8Array.from([0])) const reason = DISCONNECT_REASONS[this._disconnectReason as number] const debugMsg = `DISCONNECT reason: ${reason} ${this._socket.remoteAddress}:${this._socket.remotePort}` this.debug('DISCONNECT', debugMsg, reason) @@ -477,7 +482,7 @@ export class Peer extends EventEmitter { * @param code * @param msg */ - _handleMessage(code: PREFIXES, msg: Buffer) { + _handleMessage(code: PREFIXES, msg: Uint8Array) { switch (code) { case PREFIXES.HELLO: this._handleHello(msg) @@ -499,7 +504,7 @@ export class Peer extends EventEmitter { */ _handleHeader() { const bytesCount = this._nextPacketSize - const parseData = this._socketData.slice(0, bytesCount) + const parseData = this._socketData.subarray(0, bytesCount) this._logger(`Received header ${this._socket.remoteAddress}:${this._socket.remotePort}`) const size = this._eciesSession.parseHeader(parseData) if (size === undefined) { @@ -510,7 +515,7 @@ export class Peer extends EventEmitter { this._state = 'Body' this._nextPacketSize = size + 16 if (size % 16 > 0) this._nextPacketSize += 16 - (size % 16) - this._socketData.consume(bytesCount) + this._socketData = this._socketData.subarray(bytesCount) } /** @@ -518,7 +523,7 @@ export class Peer extends EventEmitter { */ _handleBody() { const bytesCount = this._nextPacketSize - const parseData = this._socketData.slice(0, bytesCount) + const parseData = this._socketData.subarray(0, bytesCount) const body = this._eciesSession.parseBody(parseData) if (!body) { this._logger('empty body!') @@ -526,7 +531,7 @@ export class Peer extends EventEmitter { } this._logger( `Received body ${this._socket.remoteAddress}:${this._socket.remotePort} ${formatLogData( - body.toString('hex'), + bytesToHex(body), verbose )}` ) @@ -557,7 +562,7 @@ export class Peer extends EventEmitter { } try { - let payload: any = body.slice(1) + let payload: any = body.subarray(1) // Use snappy uncompression if peer supports DevP2P >=v5 let compressed = false @@ -582,13 +587,13 @@ export class Peer extends EventEmitter { // if (protocolName === 'Peer') { try { - payload = arrToBufArr(RLP.decode(Uint8Array.from(payload))) + payload = RLP.decode(Uint8Array.from(payload)) } catch (e: any) { if (msgCode === PREFIXES.DISCONNECT) { if (compressed) { - payload = arrToBufArr(RLP.decode(Uint8Array.from(origPayload))) + payload = RLP.decode(Uint8Array.from(origPayload)) } else { - payload = arrToBufArr(RLP.decode(Uint8Array.from(snappy.uncompress(payload)))) + payload = RLP.decode(snappy.uncompress(payload)) } } else { throw new Error(e) @@ -601,16 +606,16 @@ export class Peer extends EventEmitter { this._logger(`Error on peer subprotocol message handling: ${err}`) this.emit('error', err) } - this._socketData.consume(bytesCount) + this._socketData = this._socketData.subarray(bytesCount) } /** * Process socket data * @param data */ - _onSocketData(data: Buffer) { + _onSocketData(data: Uint8Array) { if (this._closed) return - this._socketData.append(data) + this._socketData = concatBytes(this._socketData, data) try { while (this._socketData.length >= this._nextPacketSize) { switch (this._state) { @@ -660,7 +665,7 @@ export class Peer extends EventEmitter { getId() { if (this._remoteId === null) return null - return Buffer.from(this._remoteId) + return this._remoteId } getHelloMessage() { diff --git a/packages/devp2p/src/rlpx/rlpx.ts b/packages/devp2p/src/rlpx/rlpx.ts index 7a5eae9136b..030fdc75f1c 100644 --- a/packages/devp2p/src/rlpx/rlpx.ts +++ b/packages/devp2p/src/rlpx/rlpx.ts @@ -1,12 +1,14 @@ +import { bytesToInt } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' import { getPublicKey } from 'ethereum-cryptography/secp256k1' +import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import { EventEmitter } from 'events' import * as LRUCache from 'lru-cache' import ms = require('ms') import * as net from 'net' import * as os from 'os' -import { buffer2int, createDeferred, devp2pDebug, formatLogId, pk2id } from '../util' +import { createDeferred, devp2pDebug, formatLogId, pk2id } from '../util' import { DISCONNECT_REASONS, Peer } from './peer' @@ -22,7 +24,7 @@ const DEBUG_BASE_NAME = 'rlpx' const verbose = createDebugLogger('verbose').enabled export interface RLPxOptions { - clientId?: Buffer + clientId?: Uint8Array /* Timeout (default: 10s) */ timeout?: number dpt?: DPT | null @@ -35,12 +37,12 @@ export interface RLPxOptions { } export class RLPx extends EventEmitter { - _privateKey: Buffer - _id: Buffer + _privateKey: Uint8Array + _id: Uint8Array _debug: Debugger _timeout: number _maxPeers: number - _clientId: Buffer + _clientId: Uint8Array _remoteClientIdFilter?: string[] _capabilities: Capabilities[] _common: Common @@ -55,19 +57,19 @@ export class RLPx extends EventEmitter { _refillIntervalId: NodeJS.Timeout _refillIntervalSelectionCounter: number = 0 - constructor(privateKey: Buffer, options: RLPxOptions) { + constructor(privateKey: Uint8Array, options: RLPxOptions) { super() - this._privateKey = Buffer.from(privateKey) - this._id = pk2id(Buffer.from(getPublicKey(this._privateKey, false))) + this._privateKey = privateKey + this._id = pk2id(getPublicKey(this._privateKey, false)) // options this._timeout = options.timeout ?? ms('10s') this._maxPeers = options.maxPeers ?? 10 this._clientId = options.clientId - ? Buffer.from(options.clientId) - : Buffer.from(`ethereumjs-devp2p/v${pVersion}/${os.platform()}-${os.arch()}/nodejs`) + ? options.clientId + : utf8ToBytes(`ethereumjs-devp2p/v${pVersion}/${os.platform()}-${os.arch()}/nodejs`) this._remoteClientIdFilter = options.remoteClientIdFilter this._capabilities = options.capabilities @@ -84,8 +86,8 @@ export class RLPx extends EventEmitter { return } - if (this._peersLRU.has(peer.id!.toString('hex'))) return - this._peersLRU.set(peer.id!.toString('hex'), true) + if (this._peersLRU.has(bytesToHex(peer.id!))) return + this._peersLRU.set(bytesToHex(peer.id!), true) if (this._getOpenSlots() > 0) { return this._connectToPeer(peer) @@ -96,7 +98,7 @@ export class RLPx extends EventEmitter { this._dpt.on('peer:removed', (peer: PeerInfo) => { // remove from queue this._peersQueue = this._peersQueue.filter( - (item) => !(item.peer.id! as Buffer).equals(peer.id as Buffer) + (item) => !equalsBytes(item.peer.id! as Uint8Array, peer.id as Uint8Array) ) }) } @@ -135,15 +137,15 @@ export class RLPx extends EventEmitter { if (this._server) this._server.close(...args) this._server = null - for (const peerKey of this._peers.keys()) this.disconnect(Buffer.from(peerKey, 'hex')) + for (const peerKey of this._peers.keys()) this.disconnect(hexToBytes(peerKey)) } async connect(peer: PeerInfo) { if (peer.tcpPort === undefined || peer.tcpPort === null || peer.address === undefined) return this._isAliveCheck() - if (!Buffer.isBuffer(peer.id)) throw new TypeError('Expected peer.id as Buffer') - const peerKey = peer.id.toString('hex') + if (!(peer.id instanceof Uint8Array)) throw new TypeError('Expected peer.id as Uint8Array') + const peerKey = bytesToHex(peer.id) if (this._peers.has(peerKey)) throw new Error('Already connected') if (this._getOpenSlots() === 0) throw new Error('Too many peers already connected') @@ -170,8 +172,8 @@ export class RLPx extends EventEmitter { return Array.from(this._peers.values()).filter((item) => item instanceof Peer) } - disconnect(id: Buffer) { - const peer = this._peers.get(id.toString('hex')) + disconnect(id: Uint8Array) { + const peer = this._peers.get(bytesToHex(id)) if (peer instanceof Peer) peer.disconnect(DISCONNECT_REASONS.CLIENT_QUITTING) } @@ -200,7 +202,7 @@ export class RLPx extends EventEmitter { }) } - _onConnect(socket: net.Socket, peerId: Buffer | null) { + _onConnect(socket: net.Socket, peerId: Uint8Array | null) { this._debug(`connected to ${socket.remoteAddress}:${socket.remotePort}, handshake waiting..`) const peer: Peer = new Peer({ @@ -234,11 +236,11 @@ export class RLPx extends EventEmitter { } this._debug(msg) const id = peer.getId() - if (id && id.equals(this._id)) { + if (id && equalsBytes(id, this._id)) { return peer.disconnect(DISCONNECT_REASONS.SAME_IDENTITY) } - const peerKey = id!.toString('hex') + const peerKey = bytesToHex(id!) const item = this._peers.get(peerKey) if (item && item instanceof Peer) { return peer.disconnect(DISCONNECT_REASONS.ALREADY_CONNECTED) @@ -272,7 +274,7 @@ export class RLPx extends EventEmitter { const id = peer.getId() if (id) { - const peerKey = id.toString('hex') + const peerKey = bytesToHex(id) this._peers.delete(peerKey) this.emit('peer:removed', peer, reason, disconnectWe) } @@ -299,7 +301,7 @@ export class RLPx extends EventEmitter { // Randomly distributed selector based on peer ID // to decide on subdivided execution - const selector = buffer2int((item.peer.id! as Buffer).slice(0, 1)) % 10 + const selector = bytesToInt((item.peer.id! as Uint8Array).subarray(0, 1)) % 10 if (selector === this._refillIntervalSelectionCounter) { this._connectToPeer(item.peer) return false diff --git a/packages/devp2p/src/util.ts b/packages/devp2p/src/util.ts index de3f94761d8..fa0afc447c0 100644 --- a/packages/devp2p/src/util.ts +++ b/packages/devp2p/src/util.ts @@ -1,68 +1,51 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' import { keccak256 as _keccak256 } from 'ethereum-cryptography/keccak' import { utils } from 'ethereum-cryptography/secp256k1' import { publicKeyConvert } from 'ethereum-cryptography/secp256k1-compat' +import { bytesToHex, concatBytes, equalsBytes } from 'ethereum-cryptography/utils' import type { ETH } from './protocol/eth' import type { LES } from './protocol/les' export const devp2pDebug = createDebugLogger('devp2p') -export function keccak256(...buffers: Buffer[]) { - const buffer = Buffer.concat(buffers) - return Buffer.from(_keccak256(buffer)) +export function keccak256(...bytes: Uint8Array[]) { + const allBytes = concatBytes(...bytes) + return _keccak256(allBytes) } -export function genPrivateKey(): Buffer { +export function genPrivateKey(): Uint8Array { const privateKey = utils.randomPrivateKey() - return utils.isValidPrivateKey(privateKey) ? Buffer.from(privateKey) : genPrivateKey() + return utils.isValidPrivateKey(privateKey) ? privateKey : genPrivateKey() } -export function pk2id(pk: Buffer): Buffer { +export function pk2id(pk: Uint8Array): Uint8Array { if (pk.length === 33) { - pk = Buffer.from(publicKeyConvert(pk, false)) + pk = publicKeyConvert(pk, false) } - return pk.slice(1) + return pk.subarray(1) } -export function id2pk(id: Buffer): Buffer { - return Buffer.concat([Buffer.from([0x04]), id]) +export function id2pk(id: Uint8Array): Uint8Array { + return concatBytes(Uint8Array.from([0x04]), id) } -export function int2buffer(v: number | null): Buffer { - if (v === null) { - return Buffer.alloc(0) - } - let hex = v.toString(16) - if (hex.length % 2 === 1) hex = '0' + hex - return Buffer.from(hex, 'hex') -} - -export function buffer2int(buffer: Buffer): number { - if (buffer.length === 0) return NaN - - let n = 0 - for (let i = 0; i < buffer.length; ++i) n = n * 256 + buffer[i] - return n -} - -export function zfill(buffer: Buffer, size: number, leftpad: boolean = true): Buffer { - if (buffer.length >= size) return buffer +export function zfill(bytes: Uint8Array, size: number, leftpad: boolean = true): Uint8Array { + if (bytes.length >= size) return bytes if (leftpad === undefined) leftpad = true - const pad = Buffer.allocUnsafe(size - buffer.length).fill(0x00) - return leftpad ? Buffer.concat([pad, buffer]) : Buffer.concat([buffer, pad]) + const pad = new Uint8Array(size - bytes.length).fill(0x00) + return leftpad ? concatBytes(pad, bytes) : concatBytes(bytes, pad) } -export function xor(a: Buffer, b: any): Buffer { +export function xor(a: Uint8Array, b: any): Uint8Array { const length = Math.min(a.length, b.length) - const buffer = Buffer.allocUnsafe(length) - for (let i = 0; i < length; ++i) buffer[i] = a[i] ^ b[i] - return buffer + const bytes = new Uint8Array(length) + for (let i = 0; i < length; ++i) bytes[i] = a[i] ^ b[i] + return bytes } -type assertInput = Buffer | Buffer[] | ETH.StatusMsg | LES.Status | number | null +type assertInput = Uint8Array | Uint8Array[] | ETH.StatusMsg | LES.Status | number | null export function assertEq( expected: assertInput, @@ -72,9 +55,10 @@ export function assertEq( messageName?: string ): void { let fullMsg - if (Buffer.isBuffer(expected) && Buffer.isBuffer(actual)) { - if (expected.equals(actual)) return - fullMsg = `${msg}: ${expected.toString('hex')} / ${actual.toString('hex')}` + + if (expected instanceof Uint8Array && actual instanceof Uint8Array) { + if (equalsBytes(expected, actual)) return + fullMsg = `${msg}: ${bytesToHex(expected)} / ${bytesToHex(actual)}` const debugMsg = `[ERROR] ${fullMsg}` if (messageName !== undefined) { debug(messageName, debugMsg) @@ -128,10 +112,10 @@ export function createDeferred(): Deferred { return new Deferred() } -export function unstrictDecode(value: Buffer) { +export function unstrictDecode(value: Uint8Array) { // rlp library throws on remainder.length !== 0 // this utility function bypasses that - return arrToBufArr(RLP.decode(Uint8Array.from(value), true).data) + return RLP.decode(value, true).data } // multiaddr 8.0.0 expects an Uint8Array with internal buffer starting at 0 offset @@ -139,3 +123,100 @@ export function toNewUint8Array(buf: Uint8Array): Uint8Array { const arrayBuffer = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength) return new Uint8Array(arrayBuffer) } + +/*************************** ************************************************************/ +// Methods borrowed from `node-ip` by Fedor Indutny (https://github.com/indutny/node-ip) +// and modified to use Uint8Arrays instead of Buffers +export const ipToString = (bytes: Uint8Array, offset?: number, length?: number) => { + offset = offset !== undefined ? ~~offset : 0 + length = length ?? bytes.length - offset + + let result: any = [] + let i + if (length === 4) { + // IPv4 + for (i = 0; i < length; i++) { + result.push(bytes[offset + i]) + } + result = result.join('.') + } else if (length === 16) { + // IPv6 + for (i = 0; i < length; i += 2) { + result.push(new DataView(bytes.buffer).getUint16(offset + i).toString(16)) + } + result = result.join(':') + result = result.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3') + result = result.replace(/:{3,4}/, '::') + } + + return result +} + +const ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/ +const ipv6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i + +export const isV4Format = function (ip: string) { + return ipv4Regex.test(ip) +} + +export const isV6Format = function (ip: string) { + return ipv6Regex.test(ip) +} + +export const ipToBytes = (ip: string, bytes?: Uint8Array, offset: number = 0) => { + offset = ~~offset + + let result + + if (isV4Format(ip)) { + result = bytes ?? new Uint8Array(offset + 4) + ip.split(/\./g).map((byte) => { + result[offset++] = parseInt(byte, 10) & 0xff + }) + } else if (isV6Format(ip)) { + const sections = ip.split(':', 8) + + let i + for (i = 0; i < sections.length; i++) { + const isv4 = isV4Format(sections[i]) + let v4Bytes: Uint8Array = new Uint8Array([]) + + if (isv4) { + v4Bytes = ipToBytes(sections[i]) + sections[i] = bytesToHex(v4Bytes.subarray(0, 2)) + } + + if (v4Bytes.length > 0 && ++i < 8) { + sections.splice(i, 0, bytesToHex(v4Bytes.subarray(2, 4))) + } + } + + if (sections[0] === '') { + while (sections.length < 8) sections.unshift('0') + } else if (sections[sections.length - 1] === '') { + while (sections.length < 8) sections.push('0') + } else if (sections.length < 8) { + for (i = 0; i < sections.length && sections[i] !== ''; i++); + const argv: any = [i, 1] + for (i = 9 - sections.length; i > 0; i--) { + argv.push('0') + } + sections.splice.apply(sections, argv) + } + + result = bytes ?? new Uint8Array(offset + 16) + for (i = 0; i < sections.length; i++) { + const word = parseInt(sections[i], 16) + result[offset++] = (word >> 8) & 0xff + result[offset++] = word & 0xff + } + } + + if (!result) { + throw Error(`Invalid ip address: ${ip}`) + } + + return result +} + +/************ End of methods borrowed from `node-ip` ***************************/ diff --git a/packages/devp2p/test/dpt-message.spec.ts b/packages/devp2p/test/dpt-message.spec.ts index c1a4bd496fc..f7142166846 100644 --- a/packages/devp2p/test/dpt-message.spec.ts +++ b/packages/devp2p/test/dpt-message.spec.ts @@ -1,20 +1,17 @@ import { publicKeyCreate } from 'ethereum-cryptography/secp256k1-compat' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import * as message from '../src/dpt/message' -const privateKey = Buffer.from( - 'b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291', - 'hex' -) -const publicKey = Buffer.from(publicKeyCreate(privateKey, false)) +const privateKey = hexToBytes('b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291') +const publicKey = publicKeyCreate(privateKey, false) test('ping packet with version 4, additional list elements', (t) => { - const buffer = Buffer.from( - 'e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102', - 'hex' + const bytes = hexToBytes( + 'e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663aaa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000000000000000000000018208ae820d058443b9a3550102' ) - const msg = message.decode(buffer) + const msg = message.decode(bytes) t.same(msg.typename, 'ping') t.same(msg.data.version, 4) @@ -24,11 +21,10 @@ test('ping packet with version 4, additional list elements', (t) => { }) test('ping packet with version 555, additional list elements and additional random data:', (t) => { - const buffer = Buffer.from( - '577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba76023fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee1917084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c76d922dc3', - 'hex' + const bytes = hexToBytes( + '577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba76023fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee1917084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c76d922dc3' ) - const msg = message.decode(buffer) + const msg = message.decode(bytes) t.same(msg.typename, 'ping') t.same(msg.data.version, 555) @@ -38,11 +34,10 @@ test('ping packet with version 555, additional list elements and additional rand }) test('pong packet with additional list elements and additional random data', (t) => { - const buffer = Buffer.from( - '09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b2069869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f055542124e', - 'hex' + const bytes = hexToBytes( + '09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b2069869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f055542124e' ) - const msg = message.decode(buffer) + const msg = message.decode(bytes) t.same(msg.typename, 'pong') t.same(msg.publicKey, publicKey) @@ -51,11 +46,10 @@ test('pong packet with additional list elements and additional random data', (t) }) test('findnode packet with additional list elements and additional random data', (t) => { - const buffer = Buffer.from( - 'c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396', - 'hex' + const bytes = hexToBytes( + 'c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260add7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396' ) - const msg = message.decode(buffer) + const msg = message.decode(bytes) t.same(msg.typename, 'findneighbours') t.same(msg.publicKey, publicKey) @@ -64,11 +58,10 @@ test('findnode packet with additional list elements and additional random data', }) test('neighbours packet with additional list elements and additional random data', (t) => { - const buffer = Buffer.from( - 'c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0', - 'hex' + const bytes = hexToBytes( + 'c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db8403155e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa829115d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e829f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d313198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df738443b9a355010203b525a138aa34383fec3d2719a0' ) - const msg = message.decode(buffer) + const msg = message.decode(bytes) t.same(msg.typename, 'neighbours') t.same(msg.publicKey, publicKey) diff --git a/packages/devp2p/test/enr.spec.ts b/packages/devp2p/test/enr.spec.ts index b17e40713b9..c829579b24a 100644 --- a/packages/devp2p/test/enr.spec.ts +++ b/packages/devp2p/test/enr.spec.ts @@ -1,3 +1,4 @@ +import { utf8ToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import { ENR } from '../src/dns' @@ -107,7 +108,6 @@ test('ENR (branch): should error if DNS branch entry is mis-prefixed', (t) => { // ENR DNS entries test('ENR (enr): should convert an Ethereum Name Record string', (t) => { const { address, tcpPort, udpPort } = ENR.parseAndVerifyRecord(dns.enr) - t.equal(address, '40.113.111.135', 'returns correct address') t.equal(tcpPort, 30303, 'returns correct tcpPort') t.equal(udpPort, 30303, 'returns correct udpPort') @@ -125,7 +125,7 @@ test('ENR (enr): should convert non-padded Ethereum Name Record string', (t) => test('ENR (enr): should return correct multiaddr conversion codes for ipv6', (t) => { const expected = { ipCode: 41, tcpCode: 6, udpCode: 273 } - const protocolId = Buffer.from('v6') + const protocolId = utf8ToBytes('v6') const codes = ENR._getIpProtocolConversionCodes(protocolId) t.deepEqual(codes, expected, 'returns correct codes') @@ -145,7 +145,7 @@ test('ENR (enr): should error if record mis-prefixed', (t) => { }) test('ENR (enr): should error when converting to unrecognized ip protocol id', (t) => { - const protocolId = Buffer.from('v7') + const protocolId = utf8ToBytes('v7') try { ENR._getIpProtocolConversionCodes(protocolId) } catch (e: any) { diff --git a/packages/devp2p/test/integration/dpt-simulator.spec.ts b/packages/devp2p/test/integration/dpt-simulator.spec.ts index 30545fdbf70..8543d7a41c5 100644 --- a/packages/devp2p/test/integration/dpt-simulator.spec.ts +++ b/packages/devp2p/test/integration/dpt-simulator.spec.ts @@ -130,7 +130,7 @@ test('DPT: simulate bootstrap', async (t) => { util.destroyDPTs(dpts) }) -test('DPT: simulate acquiring peers via DNS', async () => { +test('DPT: simulate acquiring peers via DNS', async (t) => { const dpts = util.getTestDPTsWithDns(1) const mockDns = { @@ -138,10 +138,11 @@ test('DPT: simulate acquiring peers via DNS', async () => { return [[testdata.dns.enr]] }, } - + dpts[0]._addPeerBatch = () => { + dpts[0].destroy() + t.pass('got peer from DNS') + t.end() + } dpts[0].dns.__setNativeDNSModuleResolve(mockDns) - dpts[0].refresh() - await util.delay(400) - - util.destroyDPTs(dpts) + await dpts[0].refresh() }) diff --git a/packages/devp2p/test/integration/eth-simulator.spec.ts b/packages/devp2p/test/integration/eth-simulator.spec.ts index 09e8d629a1b..c65ea644db6 100644 --- a/packages/devp2p/test/integration/eth-simulator.spec.ts +++ b/packages/devp2p/test/integration/eth-simulator.spec.ts @@ -1,4 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { intToBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import * as devp2p from '../../src' @@ -7,15 +9,12 @@ import { ETH } from '../../src' import * as util from './util' const GENESIS_TD = 17179869184 -const GENESIS_HASH = Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' -) +const GENESIS_HASH = hexToBytes('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3') const capabilities = [devp2p.ETH.eth63, devp2p.ETH.eth62] const status = { - td: devp2p.int2buffer(GENESIS_TD), + td: intToBytes(GENESIS_TD), bestHash: GENESIS_HASH, genesisHash: GENESIS_HASH, } @@ -55,7 +54,7 @@ test('ETH: send status message (Genesis block mismatch)', (t) => { const opts: any = {} opts.status0 = Object.assign({}, status) const status1 = Object.assign({}, status) - status1['genesisHash'] = Buffer.alloc(32) + status1['genesisHash'] = new Uint8Array(32) opts.status1 = status1 opts.onPeerError0 = function (err: Error, rlpxs: any) { const msg = @@ -116,7 +115,7 @@ test('ETH -> Eth64 -> sendStatus(): should throw on non-matching latest block pr const cap = [devp2p.ETH.eth65] const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium }) const status0: any = Object.assign({}, status) - status0['latestBlock'] = 100000 // lower than Byzantium fork block 4370000 + status0['latestBlock'] = intToBytes(100000) // lower than Byzantium fork block 4370000 const rlpxs = util.initTwoPeerRLPXSetup(null, cap, common) rlpxs[0].on('peer:added', function (peer: any) { @@ -145,7 +144,7 @@ test('ETH -> Eth64 -> ForkId validation 1a)', (t) => { const status0: any = Object.assign({}, status) // Take a latest block > next mainnet fork block (constantinople) // to trigger validation condition - status0['latestBlock'] = 9069000 + status0['latestBlock'] = intToBytes(9069000) opts.status0 = status0 opts.status1 = Object.assign({}, status) opts.onPeerError0 = function (err: Error, rlpxs: any) { diff --git a/packages/devp2p/test/integration/les-simulator.spec.ts b/packages/devp2p/test/integration/les-simulator.spec.ts index 7ebeeb9da3f..6190ad4a760 100644 --- a/packages/devp2p/test/integration/les-simulator.spec.ts +++ b/packages/devp2p/test/integration/les-simulator.spec.ts @@ -1,4 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { intToBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import * as devp2p from '../../src' @@ -6,17 +8,14 @@ import * as devp2p from '../../src' import * as util from './util' const GENESIS_TD = 17179869184 -const GENESIS_HASH = Buffer.from( - 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', - 'hex' -) +const GENESIS_HASH = hexToBytes('d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3') const capabilities = [devp2p.LES.les4] const status = { - headTd: devp2p.int2buffer(GENESIS_TD), // total difficulty in genesis block + headTd: intToBytes(GENESIS_TD), // total difficulty in genesis block headHash: GENESIS_HASH, - headNum: devp2p.int2buffer(0), + headNum: intToBytes(0), genesisHash: GENESIS_HASH, } @@ -69,7 +68,7 @@ test('ETH: send status message (Genesis block mismatch)', (t) => { const opts: any = {} opts.status0 = Object.assign({}, status) const status1 = Object.assign({}, status) - status1['genesisHash'] = Buffer.alloc(32) + status1['genesisHash'] = new Uint8Array(32) opts.status1 = status1 opts.onPeerError0 = function (err: Error, rlpxs: any) { const msg = diff --git a/packages/devp2p/test/integration/rlpx-simulator.spec.ts b/packages/devp2p/test/integration/rlpx-simulator.spec.ts index 078772a3d73..4e6f1c70ebb 100644 --- a/packages/devp2p/test/integration/rlpx-simulator.spec.ts +++ b/packages/devp2p/test/integration/rlpx-simulator.spec.ts @@ -1,3 +1,4 @@ +import { hexToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import { DISCONNECT_REASONS } from '../../src/rlpx/peer' @@ -22,7 +23,7 @@ test('RLPX: ban node with missing tcp port', (t) => { rlpxs[0].on('peer:added', async () => { const peer = { - id: Buffer.from('abcd', 'hex'), + id: hexToBytes('abcd'), address: '127.0.0.1', udpPort: 30308, tcpPort: null, diff --git a/packages/devp2p/test/integration/util.ts b/packages/devp2p/test/integration/util.ts index 5d4ea3b86b4..4c11feec3fa 100644 --- a/packages/devp2p/test/integration/util.ts +++ b/packages/devp2p/test/integration/util.ts @@ -185,7 +185,7 @@ export function twoPeerMsgExchange2( clientId: 'fakePeer', capabilities: [ETH.eth66], port: 30303, - id: Buffer.alloc(12), + id: new Uint8Array(12), } // Set peer's devp2p protocol version to 4 protocol._peer._hello = v4Hello diff --git a/packages/devp2p/test/rlpx-ecies.spec.ts b/packages/devp2p/test/rlpx-ecies.spec.ts index 5ba45fd6f54..3539d238527 100644 --- a/packages/devp2p/test/rlpx-ecies.spec.ts +++ b/packages/devp2p/test/rlpx-ecies.spec.ts @@ -1,5 +1,6 @@ -import { randomBytes } from 'crypto' +import { getRandomBytesSync } from 'ethereum-cryptography/random' import { publicKeyCreate } from 'ethereum-cryptography/secp256k1-compat' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import { ECIES } from '../src/rlpx/ecies' @@ -14,8 +15,8 @@ declare module 'tape' { context: { a: ECIES b: ECIES - h0?: { auth: Buffer; ack: Buffer } - h1?: { auth: Buffer; ack: Buffer } + h0?: { auth: Uint8Array; ack: Uint8Array } + h1?: { auth: Uint8Array; ack: Uint8Array } } } } @@ -24,8 +25,8 @@ function randomBefore(fn: Function) { return (t: Test) => { const privateKey1 = util.genPrivateKey() const privateKey2 = util.genPrivateKey() - const publicKey1 = Buffer.from(publicKeyCreate(privateKey1, false)) - const publicKey2 = Buffer.from(publicKeyCreate(privateKey2, false)) + const publicKey1 = publicKeyCreate(privateKey1, false) + const publicKey2 = publicKeyCreate(privateKey2, false) t.context = { a: new ECIES(privateKey1, util.pk2id(publicKey1), util.pk2id(publicKey2)), b: new ECIES(privateKey2, util.pk2id(publicKey2), util.pk2id(publicKey1)), @@ -38,22 +39,22 @@ function randomBefore(fn: Function) { function testdataBefore(fn: Function) { return (t: Test) => { const v = testdata.eip8Values - const keyA = Buffer.from(v.keyA, 'hex') - const keyB = Buffer.from(v.keyB, 'hex') - const pubA = Buffer.from(v.pubA, 'hex') - const pubB = Buffer.from(v.pubB, 'hex') + const keyA = hexToBytes(v.keyA) + const keyB = hexToBytes(v.keyB) + const pubA = hexToBytes(v.pubA) + const pubB = hexToBytes(v.pubB) const h = testdata.eip8Handshakes t.context = { a: new ECIES(keyA, util.pk2id(pubA), util.pk2id(pubB)), b: new ECIES(keyB, util.pk2id(pubB), util.pk2id(pubA)), h0: { - auth: Buffer.from(h[0].auth.join(''), 'hex'), - ack: Buffer.from(h[0].ack.join(''), 'hex'), + auth: hexToBytes(h[0].auth.join('')), + ack: hexToBytes(h[0].ack.join('')), }, h1: { - auth: Buffer.from(h[1].auth.join(''), 'hex'), - ack: Buffer.from(h[1].ack.join(''), 'hex'), + auth: hexToBytes(h[1].auth.join('')), + ack: hexToBytes(h[1].ack.join('')), }, } fn(t) @@ -63,9 +64,9 @@ function testdataBefore(fn: Function) { test( 'Random: message encryption', randomBefore((t: Test) => { - const message = Buffer.from('The Magic Words are Squeamish Ossifrage') + const message = utf8ToBytes('The Magic Words are Squeamish Ossifrage') const encrypted = t.context.a._encryptMessage(message) - const decrypted = t.context.b._decryptMessage(encrypted as Buffer) + const decrypted = t.context.b._decryptMessage(encrypted as Uint8Array) t.same(message, decrypted, 'encryptMessage -> decryptMessage should lead to same') t.end() }) @@ -77,21 +78,21 @@ test( t.doesNotThrow(() => { const auth = t.context.a.createAuthNonEIP8() t.context.b._gotEIP8Auth = false - t.context.b.parseAuthPlain(auth as Buffer) + t.context.b.parseAuthPlain(auth as Uint8Array) }, 'should not throw on auth creation/parsing') t.doesNotThrow(() => { t.context.b._gotEIP8Ack = false const ack = t.context.b.createAckOld() - t.context.a.parseAckPlain(ack as Buffer) + t.context.a.parseAckPlain(ack as Uint8Array) }, 'should not throw on ack creation/parsing') - const body = randomBytes(600) + const body = getRandomBytesSync(600) - const header = t.context.b.parseHeader(t.context.a.createHeader(body.length) as Buffer) + const header = t.context.b.parseHeader(t.context.a.createHeader(body.length) as Uint8Array) t.same(header, body.length, 'createHeader -> parseHeader should lead to same') - const parsedBody = t.context.b.parseBody(t.context.a.createBody(body) as Buffer) + const parsedBody = t.context.b.parseBody(t.context.a.createBody(body) as Uint8Array) t.same(parsedBody, body, 'createBody -> parseBody should lead to same') t.end() @@ -104,13 +105,13 @@ test( t.doesNotThrow(() => { const auth = t.context.a.createAuthEIP8() t.context.b._gotEIP8Auth = true - t.context.b.parseAuthEIP8(auth as Buffer) + t.context.b.parseAuthEIP8(auth as Uint8Array) }, 'should not throw on auth creation/parsing') t.doesNotThrow(() => { const ack = t.context.b.createAckEIP8() t.context.a._gotEIP8Ack = true - t.context.a.parseAckEIP8(ack as Buffer) + t.context.a.parseAckEIP8(ack as Uint8Array) }, 'should not throw on ack creation/parsing') t.end() @@ -122,13 +123,13 @@ test( testdataBefore((t: Test) => { t.doesNotThrow(() => { t.context.b._gotEIP8Auth = false - t.context.b.parseAuthPlain(t.context.h0?.auth as Buffer) + t.context.b.parseAuthPlain(t.context.h0?.auth as Uint8Array) t.context.a._initMsg = t.context.h0?.auth }, 'should not throw on auth parsing') t.doesNotThrow(() => { t.context.a._gotEIP8Ack = false - t.context.a.parseAckPlain(t.context.h0?.ack as Buffer) + t.context.a.parseAckPlain(t.context.h0?.ack as Uint8Array) }, 'should not throw on ack parsing') t.end() @@ -140,12 +141,12 @@ test( testdataBefore((t: Test) => { t.doesNotThrow(() => { t.context.b._gotEIP8Auth = true - t.context.b.parseAuthEIP8(t.context.h1?.auth as Buffer) + t.context.b.parseAuthEIP8(t.context.h1?.auth as Uint8Array) t.context.a._initMsg = t.context.h1?.auth }, 'should not throw on auth parsing') t.doesNotThrow(() => { t.context.a._gotEIP8Ack = true - t.context.a.parseAckEIP8(t.context.h1?.ack as Buffer) + t.context.a.parseAckEIP8(t.context.h1?.ack as Uint8Array) }, 'should not throw on ack parsing') t.end() diff --git a/packages/devp2p/test/rlpx-mac.spec.ts b/packages/devp2p/test/rlpx-mac.spec.ts index a2a769bb0d1..d35e8fa1249 100644 --- a/packages/devp2p/test/rlpx-mac.spec.ts +++ b/packages/devp2p/test/rlpx-mac.spec.ts @@ -1,36 +1,34 @@ +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as test from 'tape' import { MAC } from '../src/rlpx/mac' -const secret = Buffer.from( - '4caf4671e713d083128973de159d02688dc86f51535a80178264631e193ed2ea', - 'hex' -) +const secret = hexToBytes('4caf4671e713d083128973de159d02688dc86f51535a80178264631e193ed2ea') test('digest should work on empty data', (t) => { const mac = new MAC(secret) - t.equal(mac.digest().toString('hex'), 'c5d2460186f7233c927e7db2dcc703c0') + t.equal(bytesToHex(mac.digest()), 'c5d2460186f7233c927e7db2dcc703c0') t.end() }) test('#update', (t) => { const mac = new MAC(secret) mac.update('test') - t.equal(mac.digest().toString('hex'), '9c22ff5f21f0b81b113e63f7db6da94f') + t.equal(bytesToHex(mac.digest()), '9c22ff5f21f0b81b113e63f7db6da94f') t.end() }) test('#updateHeader', (t) => { const mac = new MAC(secret) mac.updateHeader('this is a header data struct') - t.equal(mac.digest().toString('hex'), '52235ed491a4c9224d94788762ead6a6') + t.equal(bytesToHex(mac.digest()), '52235ed491a4c9224d94788762ead6a6') t.end() }) test('#updateBody', (t) => { const mac = new MAC(secret) mac.updateBody('this is a body data struct') - t.equal(mac.digest().toString('hex'), '134a755450b1ed9d3ff90ef5dcecdd7d') + t.equal(bytesToHex(mac.digest()), '134a755450b1ed9d3ff90ef5dcecdd7d') t.end() }) @@ -38,6 +36,6 @@ test('#updateHeader and #updateBody', (t) => { const mac = new MAC(secret) mac.updateHeader('this is a header data struct') mac.updateBody('this is a body data struct') - t.equal(mac.digest().toString('hex'), '5d98967578ec8edbb45e1d75992f394c') + t.equal(bytesToHex(mac.digest()), '5d98967578ec8edbb45e1d75992f394c') t.end() }) diff --git a/packages/ethash/examples/example.ts b/packages/ethash/examples/example.ts index 441a6d6923e..ad1aa738fe2 100644 --- a/packages/ethash/examples/example.ts +++ b/packages/ethash/examples/example.ts @@ -1,10 +1,12 @@ +import { bytesToHex } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import { Ethash } from '../src' const ethash = new Ethash() // make the 1000 cache items with a seed of 0 * 32 -ethash.mkcache(1000, Buffer.alloc(32).fill(0)) +ethash.mkcache(1000, new Uint8Array(32).fill(0)) -const result = ethash.run(Buffer.from('test'), Buffer.from([0]), 1000) +const result = ethash.run(hexToBytes('test'), Uint8Array.from([0]), 1000) -console.log(result.hash.toString('hex')) +console.log(bytesToHex(result.hash)) diff --git a/packages/ethash/examples/rawExample.ts b/packages/ethash/examples/rawExample.ts index 8e198482b5c..213312c399a 100644 --- a/packages/ethash/examples/rawExample.ts +++ b/packages/ethash/examples/rawExample.ts @@ -1,28 +1,27 @@ -import Ethash, { EthashCacheDB } from '../src' +import { Ethash } from '../src' import { MemoryLevel } from 'memory-level' +import { bytesToHex } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' const ethash = new Ethash(new MemoryLevel()) const verifySubmit = async ( ethash: Ethash, number: number, - headerHash: Buffer, - nonce: Buffer -): Promise => { + headerHash: Uint8Array, + nonce: Uint8Array +): Promise => { console.log('Verifying number: ', number) await ethash.loadEpoc(BigInt(number)) console.log('EPOC set') - console.log('Seed: ', ethash.seed!.toString('hex')) + console.log('Seed: ', bytesToHex(ethash.seed!)) const a = ethash.run(headerHash, nonce) return a.hash } -const headerHash = Buffer.from( - '0e2887aa1a0668bf8254d1a6ae518927de99e3e5d7f30fd1f16096e2608fe05e', - 'hex' -) -const nonce = Buffer.from('e360b6170c229d15', 'hex') +const headerHash = hexToBytes('0e2887aa1a0668bf8254d1a6ae518927de99e3e5d7f30fd1f16096e2608fe05e') +const nonce = hexToBytes('e360b6170c229d15') verifySubmit(ethash, 35414, headerHash, nonce).then((result) => { - console.log('Result: ', result.toString('hex')) + console.log('Result: ', bytesToHex(result)) }) diff --git a/packages/ethash/package.json b/packages/ethash/package.json index ade619568c0..17f11016b39 100644 --- a/packages/ethash/package.json +++ b/packages/ethash/package.json @@ -32,6 +32,7 @@ "lint:diff": "../../config/cli/lint-diff.sh", "lint:fix": "../../config/cli/lint-fix.sh", "prepublishOnly": "../../config/cli/prepublish.sh", + "tape": "tape -r ts-node/register", "test": "tape -r ts-node/register test/*.spec.ts", "tsc": "../../config/cli/ts-compile.sh" }, diff --git a/packages/ethash/src/index.ts b/packages/ethash/src/index.ts index 6fabc2d65d6..88c78852141 100644 --- a/packages/ethash/src/index.ts +++ b/packages/ethash/src/index.ts @@ -2,18 +2,19 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { RLP } from '@ethereumjs/rlp' import { TWO_POW256, - bigIntToBuffer, - bufArrToArr, - bufferToBigInt, + bigIntToBytes, + bytesToBigInt, + concatBytes, + equalsBytes, setLengthLeft, zeros, } from '@ethereumjs/util' import { keccak256, keccak512 } from 'ethereum-cryptography/keccak' import { - bufReverse, + bytesReverse, fnv, - fnvBuffer, + fnvBytes, getCacheSize, getEpoc, getFullSize, @@ -24,9 +25,9 @@ import { import type { BlockData, HeaderData } from '@ethereumjs/block' import type { AbstractLevel } from 'abstract-level' -function xor(a: Buffer, b: Buffer) { +function xor(a: Uint8Array, b: Uint8Array) { const len = Math.max(a.length, b.length) - const res = Buffer.alloc(len) + const res = new Uint8Array(len) for (let i = 0; i < len; i++) { res[i] = a[i] ^ b[i] } @@ -34,8 +35,8 @@ function xor(a: Buffer, b: Buffer) { } export type Solution = { - mixHash: Buffer - nonce: Buffer + mixHash: Uint8Array + nonce: Uint8Array } export class Miner { @@ -46,7 +47,7 @@ export class Miner { public solution?: Solution private currentNonce: bigint - private headerHash?: Buffer + private headerHash?: Uint8Array private stopMining: boolean /** @@ -122,10 +123,10 @@ export class Miner { // Without this, for high-difficulty blocks JS never jumps out of the Promise const solution: Solution | null = await new Promise((resolve) => { setTimeout(() => { - const nonce = setLengthLeft(bigIntToBuffer(this.currentNonce), 8) + const nonce = setLengthLeft(bigIntToBytes(this.currentNonce), 8) const a = this.ethash.run(headerHash, nonce) - const result = bufferToBigInt(a.hash) + const result = bytesToBigInt(a.hash) if (TWO_POW256 / difficulty > result) { const solution: Solution = { @@ -152,24 +153,24 @@ export class Miner { } export type EthashCacheDB = AbstractLevel< - string | Buffer | Uint8Array, - string | Buffer, + string | Uint8Array, + string | Uint8Array, { - cache: Buffer[] + cache: Uint8Array[] fullSize: number cacheSize: number - seed: Buffer + seed: Uint8Array } > export class Ethash { dbOpts: Object cacheDB?: EthashCacheDB - cache: Buffer[] + cache: Uint8Array[] epoc?: number fullSize?: number cacheSize?: number - seed?: Buffer + seed?: Uint8Array constructor(cacheDB?: EthashCacheDB) { this.dbOpts = { @@ -179,20 +180,19 @@ export class Ethash { this.cache = [] } - mkcache(cacheSize: number, seed: Buffer) { - // console.log(`generating cache\nsize: ${cacheSize}\nseed: ${seed.toString('hex')}`) + mkcache(cacheSize: number, seed: Uint8Array) { const n = Math.floor(cacheSize / params.HASH_BYTES) - const o = [Buffer.from(keccak512(seed))] + const o = [keccak512(seed)] let i for (i = 1; i < n; i++) { - o.push(Buffer.from(keccak512(o[o.length - 1]))) + o.push(keccak512(o[o.length - 1])) } for (let _ = 0; _ < params.CACHE_ROUNDS; _++) { for (i = 0; i < n; i++) { - const v = o[i].readUInt32LE(0) % n - o[i] = Buffer.from(keccak512(xor(o[(i - 1 + n) % n], o[v]))) + const v = new DataView(o[i].buffer).getUint32(0, true) % n + o[i] = keccak512(xor(o[(i - 1 + n) % n], o[v])) } } @@ -200,20 +200,21 @@ export class Ethash { return this.cache } - calcDatasetItem(i: number): Buffer { + calcDatasetItem(i: number): Uint8Array { const n = this.cache.length const r = Math.floor(params.HASH_BYTES / params.WORD_BYTES) - let mix = Buffer.from(this.cache[i % n]) - mix.writeInt32LE(mix.readUInt32LE(0) ^ i, 0) - mix = Buffer.from(keccak512(mix)) + let mix = new Uint8Array(this.cache[i % n]) + const mixView = new DataView(mix.buffer) + mixView.setUint32(0, mixView.getUint32(0, true) ^ i, true) + mix = keccak512(mix) for (let j = 0; j < params.DATASET_PARENTS; j++) { - const cacheIndex = fnv(i ^ j, mix.readUInt32LE((j % r) * 4)) - mix = fnvBuffer(mix, this.cache[cacheIndex % n]) + const cacheIndex = fnv(i ^ j, new DataView(mix.buffer).getUint32((j % r) * 4, true)) + mix = fnvBytes(mix, this.cache[cacheIndex % n]) } - return Buffer.from(keccak512(mix)) + return keccak512(mix) } - run(val: Buffer, nonce: Buffer, fullSize?: number) { + run(val: Uint8Array, nonce: Uint8Array, fullSize?: number) { if (fullSize === undefined) { if (this.fullSize === undefined) { throw new Error('fullSize needed') @@ -223,42 +224,59 @@ export class Ethash { } const n = Math.floor(fullSize / params.HASH_BYTES) const w = Math.floor(params.MIX_BYTES / params.WORD_BYTES) - const s = Buffer.from(keccak512(Buffer.concat([val, bufReverse(nonce)]))) + const s = keccak512(concatBytes(val, bytesReverse(nonce))) const mixhashes = Math.floor(params.MIX_BYTES / params.HASH_BYTES) - let mix = Buffer.concat(Array(mixhashes).fill(s)) + let mix = concatBytes(...Array(mixhashes).fill(s)) let i for (i = 0; i < params.ACCESSES; i++) { const p = - (fnv(i ^ s.readUInt32LE(0), mix.readUInt32LE((i % w) * 4)) % Math.floor(n / mixhashes)) * + (fnv( + i ^ new DataView(s.buffer).getUint32(0, true), + new DataView(mix.buffer).getUint32((i % w) * 4, true) + ) % + Math.floor(n / mixhashes)) * mixhashes - const newdata = [] + const newdata: Uint8Array[] = [] for (let j = 0; j < mixhashes; j++) { newdata.push(this.calcDatasetItem(p + j)) } - mix = fnvBuffer(mix, Buffer.concat(newdata)) + mix = fnvBytes(mix, concatBytes(...newdata)) } - const cmix = Buffer.alloc(mix.length / 4) + const cmix = new Uint8Array(mix.length / 4) + const cmixView = new DataView(cmix.buffer) + const mixView = new DataView(mix.buffer) for (i = 0; i < mix.length / 4; i = i + 4) { - const a = fnv(mix.readUInt32LE(i * 4), mix.readUInt32LE((i + 1) * 4)) - const b = fnv(a, mix.readUInt32LE((i + 2) * 4)) - const c = fnv(b, mix.readUInt32LE((i + 3) * 4)) - cmix.writeUInt32LE(c, i) + const a = fnv(mixView.getUint32(i * 4, true), mixView.getUint32((i + 1) * 4, true)) + const b = fnv(a, mixView.getUint32((i + 2) * 4, true)) + const c = fnv(b, mixView.getUint32((i + 3) * 4, true)) + cmixView.setUint32(i, c, true) } return { mix: cmix, - hash: Buffer.from(keccak256(Buffer.concat([s, cmix]))), + hash: keccak256(concatBytes(s, cmix)), } } cacheHash() { - return Buffer.from(keccak256(Buffer.concat(this.cache))) + // Concatenate all the cache bytes together + // We can't use `concatBytes` because calling `concatBytes(...this.cache)` results + // in a `Max call stack size exceeded` error due to the spread operator pushing all + // of the array elements onto the stack and the ethash cache can be quite large + const length = this.cache.reduce((a, arr) => a + arr.length, 0) + const result = new Uint8Array(length) + for (let i = 0, pad = 0; i < this.cache.length; i++) { + const arr = this.cache[i] + result.set(arr, pad) + pad += arr.length + } + return keccak256(result) } - headerHash(rawHeader: Buffer[]) { - return Buffer.from(keccak256(RLP.encode(bufArrToArr(rawHeader.slice(0, -2))))) + headerHash(rawHeader: Uint8Array[]) { + return keccak256(RLP.encode(rawHeader.slice(0, -2))) } /** @@ -278,7 +296,7 @@ export class Ethash { } // gives the seed the first epoc found - const findLastSeed = async (epoc: number): Promise<[Buffer, number]> => { + const findLastSeed = async (epoc: number): Promise<[Uint8Array, number]> => { if (epoc === 0) { return [zeros(32), 0] } @@ -325,12 +343,12 @@ export class Ethash { this.dbOpts ) } else { - this.cache = data.cache.map((a: Buffer) => { - return Buffer.from(a) + this.cache = data.cache.map((a: Uint8Array) => { + return Uint8Array.from(a) }) this.cacheSize = data.cacheSize this.fullSize = data.fullSize - this.seed = Buffer.from(data.seed) + this.seed = Uint8Array.from(data.seed) } } @@ -350,9 +368,8 @@ export class Ethash { await this.loadEpoc(number) const a = this.run(headerHash, nonce) - const result = bufferToBigInt(a.hash) - - return a.mix.equals(mixHash) && TWO_POW256 / difficulty > result + const result = bytesToBigInt(a.hash) + return equalsBytes(a.mix, mixHash) && TWO_POW256 / difficulty > result } async verifyPOW(block: Block) { diff --git a/packages/ethash/src/util.ts b/packages/ethash/src/util.ts index f4247cc28e3..1804780c9d7 100644 --- a/packages/ethash/src/util.ts +++ b/packages/ethash/src/util.ts @@ -44,13 +44,13 @@ export function getEpoc(blockNumber: bigint) { * Generates a seed give the end epoc and optional the beginning epoc and the * beginning epoc seed * @method getSeed - * @param seed Buffer + * @param seed Uint8Array * @param begin Number * @param end Number */ -export function getSeed(seed: Buffer, begin: number, end: number) { +export function getSeed(seed: Uint8Array, begin: number, end: number) { for (let i = begin; i < end; i++) { - seed = Buffer.from(keccak256(seed)) + seed = keccak256(seed) } return seed } @@ -59,17 +59,22 @@ export function fnv(x: number, y: number) { return ((((x * 0x01000000) | 0) + ((x * 0x193) | 0)) ^ y) >>> 0 } -export function fnvBuffer(a: Buffer, b: Buffer) { - const r = Buffer.alloc(a.length) +export function fnvBytes(a: Uint8Array, b: Uint8Array) { + const r = new Uint8Array(a.length) + const rView = new DataView(r.buffer) for (let i = 0; i < a.length; i = i + 4) { - r.writeUInt32LE(fnv(a.readUInt32LE(i), b.readUInt32LE(i)), i) + rView.setUint32( + i, + fnv(new DataView(a.buffer).getUint32(i, true), new DataView(b.buffer).getUint32(i, true)), + true + ) } return r } -export function bufReverse(a: Buffer) { +export function bytesReverse(a: Uint8Array) { const length = a.length - const b = Buffer.alloc(length) + const b = new Uint8Array(length) for (let i = 0; i < length; i++) { b[i] = a[length - i - 1] } diff --git a/packages/ethash/test/block.spec.ts b/packages/ethash/test/block.spec.ts index bdcc9735184..11aa520e7a4 100644 --- a/packages/ethash/test/block.spec.ts +++ b/packages/ethash/test/block.spec.ts @@ -1,13 +1,14 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr, toBuffer } from '@ethereumjs/util' +import { toBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import * as tape from 'tape' import { Ethash } from '../src' -import type { BlockBuffer } from '@ethereumjs/block' +import type { BlockBytes } from '@ethereumjs/block' const cacheDB = new MemoryLevel() @@ -22,21 +23,21 @@ tape('Verify POW for valid and invalid blocks', async function (t) { const genesisResult = await e.verifyPOW(genesis) t.ok(genesisResult, 'genesis block should be valid') - const validRlp = Buffer.from(validBlockRlp, 'hex') + const validRlp = hexToBytes(validBlockRlp) const validBlock = Block.fromRLPSerializedBlock(validRlp, { common }) const validBlockResult = await e.verifyPOW(validBlock) t.ok(validBlockResult, 'should be valid') - const invalidRlp = Buffer.from(invalidBlockRlp, 'hex') + const invalidRlp = hexToBytes(invalidBlockRlp) // Put correct amount of extraData in block extraData field so block can be deserialized - const values = arrToBufArr(RLP.decode(Uint8Array.from(invalidRlp))) as BlockBuffer - values[0][12] = Buffer.alloc(32) + const values = RLP.decode(Uint8Array.from(invalidRlp)) as BlockBytes + values[0][12] = new Uint8Array(32) const invalidBlock = Block.fromValuesArray(values, { common }) const invalidBlockResult = await e.verifyPOW(invalidBlock) t.ok(!invalidBlockResult, 'should be invalid') const testData = require('./block_tests_data.json') - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) const uncleBlockResult = await e.verifyPOW(block) t.ok(uncleBlockResult, 'should be valid') diff --git a/packages/ethash/test/ethash.spec.ts b/packages/ethash/test/ethash.spec.ts index d38e7053e7f..c72b7b34fee 100644 --- a/packages/ethash/test/ethash.spec.ts +++ b/packages/ethash/test/ethash.spec.ts @@ -1,5 +1,7 @@ import { BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Ethash } from '../src' @@ -14,20 +16,20 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) tape('POW tests', async function (t) { for (const key of tests) { const test = powTests[key] - const header = BlockHeader.fromRLPSerializedHeader(Buffer.from(test.header, 'hex'), { common }) + const header = BlockHeader.fromRLPSerializedHeader(hexToBytes(test.header), { common }) const headerHash = ethash.headerHash(header.raw()) - t.equal(headerHash.toString('hex'), test.header_hash, 'generate header hash') + t.equal(bytesToHex(headerHash), test.header_hash, 'generate header hash') const epoc = getEpoc(header.number) t.equal(await getCacheSize(epoc), test.cache_size, 'generate cache size') t.equal(await getFullSize(epoc), test.full_size, 'generate full cache size') - ethash.mkcache(test.cache_size, Buffer.from(test.seed, 'hex')) - t.equal(ethash.cacheHash().toString('hex'), test.cache_hash, 'generate cache') + ethash.mkcache(test.cache_size, hexToBytes(test.seed)) + t.equal(bytesToHex(ethash.cacheHash()), test.cache_hash, 'generate cache') - const r = ethash.run(headerHash, Buffer.from(test.nonce, 'hex'), test.full_size) - t.equal(r.hash.toString('hex'), test.result, 'generate result') - t.equal(r.mix.toString('hex'), test.mixHash, 'generate mix hash') + const r = ethash.run(headerHash, hexToBytes(test.nonce), test.full_size) + t.equal(bytesToHex(r.hash), test.result, 'generate result') + t.equal(bytesToHex(r.mix), test.mixHash, 'generate mix hash') } }) diff --git a/packages/ethash/test/miner.spec.ts b/packages/ethash/test/miner.spec.ts index 4327e540432..5bd1e035cc5 100644 --- a/packages/ethash/test/miner.spec.ts +++ b/packages/ethash/test/miner.spec.ts @@ -7,11 +7,10 @@ import { Ethash } from '../src' import type { BlockHeader } from '@ethereumjs/block' -const cacheDB = new MemoryLevel() const common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.Petersburg }) tape('Check if miner works as expected', async function (t) { - const e = new Ethash(cacheDB as any) + const e = new Ethash(new MemoryLevel()) const block = Block.fromBlockData( { @@ -55,7 +54,7 @@ tape('Check if miner works as expected', async function (t) { }) tape('Check if it is possible to mine Blocks and BlockHeaders', async function (t) { - const e = new Ethash(cacheDB as any) + const e = new Ethash(new MemoryLevel()) const block = Block.fromBlockData( { @@ -66,7 +65,6 @@ tape('Check if it is possible to mine Blocks and BlockHeaders', async function ( }, { common } ) - const miner = e.getMiner(block.header) const solution = await miner.mine(-1) @@ -84,7 +82,7 @@ tape('Check if it is possible to mine Blocks and BlockHeaders', async function ( }) tape('Check if it is possible to stop the miner', async function (t) { - const e = new Ethash(cacheDB as any) + const e = new Ethash(new MemoryLevel()) const block = Block.fromBlockData( { @@ -95,7 +93,6 @@ tape('Check if it is possible to stop the miner', async function (t) { }, { common } ) - const miner = e.getMiner(block.header) setTimeout(function () { miner.stop() @@ -107,7 +104,7 @@ tape('Check if it is possible to stop the miner', async function (t) { }) tape('Check if it is possible to stop the miner', async function (t) { - const e = new Ethash(cacheDB as any) + const e = new Ethash(new MemoryLevel()) const block: any = {} @@ -119,7 +116,7 @@ tape('Check if it is possible to stop the miner', async function (t) { }) tape('Should keep common when mining blocks or headers', async function (t) { - const e = new Ethash(cacheDB as any) + const e = new Ethash(new MemoryLevel()) const block = Block.fromBlockData( { diff --git a/packages/evm/examples/decode-opcodes.ts b/packages/evm/examples/decode-opcodes.ts index 2e6ae419d3b..d2cc372dd64 100644 --- a/packages/evm/examples/decode-opcodes.ts +++ b/packages/evm/examples/decode-opcodes.ts @@ -3,6 +3,7 @@ // 1. Takes binary EVM code and decodes it into opcodes import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { getOpcodesForHF } from '../src/opcodes' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) @@ -11,10 +12,10 @@ const opcodes = getOpcodesForHF(common).opcodes const data = '6107608061000e6000396000f30060003560e060020a90048063141961bc1461006e57806319ac74bd146100cf578063278ecde1146100e75780632c0f7b6f146100f8578063a87430ba1461010a578063ac273aa21461011f578063c06f4c1d14610133578063c1cbbca714610159578063e11523431461016a57005b610079600435610183565b8b6000528a60205289600160a060020a031660405288600160a060020a0316606052876080528660a0528560c0528460e05283610100528261012052816101405280600160a060020a0316610160526101806000f35b6100dd6004356024356106e8565b8060005260206000f35b6100f2600435610454565b60006000f35b61010061017c565b8060005260206000f35b6101156004356101da565b8060005260206000f35b61012d600435602435610729565b60006000f35b61015360043560243560443560643560843560a43560c43560e4356101ee565b60006000f35b610164600435610302565b60006000f35b6101756004356105dd565b60006000f35b5b60005481565b5b6000526001602052604060002080549080600101549080600201549080600301549080600401549080600501549080600601549080600701549080600801549080600901549080600c01549080600d015490508c565b5b600052600260205260406000208054905081565b600060006000600060008811801561020557504287115b61020e576102f4565b600080549081600101905593506001600085815260200190815260200160002092508b83819055508a83600101819055503383600201819055508883600301819055508783600501819055508683600401819055508583600701819055508983600c01819055508483600d01819055506002600033600160a060020a03168152602001908152602001600020915081805490816001019055905083826001016000838152602001908152602001600020819055508333600160a060020a03167f882da991e52c8933ce57314c9ba3f934798d912d862790c40d0feeb7025af08a60006000a35b505050505050505050505050565b600060006000600034116103155761044e565b600160008581526020019081526020016000209250428360040154101561033b5761044d565b82600901805490816001019055915082600a0160008381526020019081526020016000209050338181905550348160010181905550806001015483600601818154019150819055508183600b01600033600160a060020a03168152602001908152602001600020819055508333600160a060020a03167fc5e578961e5bd7481ccf1d1bdfbad97b9f1ddfad520f061ca764a57018f3febe6000866006015481526020016000a3600083600d0154600160a060020a031614156103fc5761044c565b82600d0154600160a060020a03166249f068600060008260e060020a02600052600488815260200133600160a060020a03168152602001348152602001600060008660325a03f161044957005b50505b5b5b50505050565b60006000600160008481526020019081526020016000209150816004015442118015610487575081600501548260060154105b8015610497575060008260060154115b6104a0576105d8565b81600a01600083600b01600033600160a060020a03168152602001908152602001600020548152602001908152602001600020905060008160010154116104e6576105d7565b8054600160a060020a0316600082600101546000600060006000848787f161050a57005b505050806001015482600601818154039150819055508233600160a060020a03167fe139691e7435f1fb40ec50ed3729009226be49087fd00e9e5bac276c2a8f40cf6000846001015481526020016000a360008160010181905550600082600d0154600160a060020a03161415610580576105d6565b81600d0154600160a060020a031663b71f3cde600060008260e060020a0260005260048781526020018554600160a060020a0316815260200185600101548152602001600060008660325a03f16105d357005b50505b5b5b505050565b6000600160008381526020019081526020016000209050806005015481600601541015610609576106e4565b8060030154600160a060020a0316600082600601546000600060006000848787f161063057005b5050508133600160a060020a03167f6be92574b1386f424263a096e8b66ff6cc223ab0f9d18702563aa339a372cf986000846006015481526020016000a36000816006018190555060018160080181905550600081600d0154600160a060020a0316141561069d576106e3565b80600d0154600160a060020a031663484ec26c600060008260e060020a02600052600486815260200185600601548152602001600060008660325a03f16106e057005b50505b5b5050565b600060006002600085600160a060020a0316815260200190815260200160002090508060010160008481526020019081526020016000205491505092915050565b6000600060016000858152602001908152602001600020905080600a0160008481526020019081526020016000209150509291505056' -nameOpCodes(Buffer.from(data, 'hex')) +nameOpCodes(hexToBytes(data)) -function nameOpCodes(raw: Buffer) { - let pushData +function nameOpCodes(raw: Uint8Array) { + let pushData = new Uint8Array() for (let i = 0; i < raw.length; i++) { const pc = i @@ -23,15 +24,19 @@ function nameOpCodes(raw: Buffer) { // no destinations into the middle of PUSH if (curOpCode?.slice(0, 4) === 'PUSH') { const jumpNum = raw[pc] - 0x5f - pushData = raw.slice(pc + 1, pc + jumpNum + 1) + pushData = raw.subarray(pc + 1, pc + jumpNum + 1) i += jumpNum } console.log( - pad(pc, roundLog(raw.length, 10)) + ' ' + curOpCode + ' ' + pushData?.toString('hex') + pad(pc, roundLog(raw.length, 10)) + + ' ' + + curOpCode + + ' ' + + (pushData?.length > 0 ? bytesToHex(pushData as Uint8Array) : '') ) - pushData = '' + pushData = new Uint8Array() } } diff --git a/packages/evm/examples/runCode.ts b/packages/evm/examples/runCode.ts index 91a1c75f357..96a843a8c32 100644 --- a/packages/evm/examples/runCode.ts +++ b/packages/evm/examples/runCode.ts @@ -3,6 +3,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { DefaultStateManager } from '@ethereumjs/statemanager' import { EEI } from '@ethereumjs/vm' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' const main = async () => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) @@ -29,11 +30,11 @@ const main = async () => { evm .runCode({ - code: Buffer.from(code.join(''), 'hex'), + code: hexToBytes(code.join('')), gasLimit: BigInt(0xffff), }) .then((results) => { - console.log(`Returned: ${results.returnValue.toString('hex')}`) + console.log(`Returned: ${bytesToHex(results.returnValue)}`) console.log(`gasUsed: ${results.executionGasUsed.toString()}`) }) .catch(console.error) diff --git a/packages/evm/package.json b/packages/evm/package.json index fb64a886f3c..cffcd0d0138 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -28,7 +28,8 @@ "src" ], "scripts": { - "build": "../../config/cli/ts-build.sh", + "rustbnHotFix": "sed -i -e \"s/.toString('hex')), 'hex'//g\" ../../node_modules/rustbn.js/index.js && sed -i -e \"s/Buffer.from(//g\" ../../node_modules/rustbn.js/index.js", + "build": "npm run rustbnHotFix && ../../config/cli/ts-build.sh", "clean": "../../config/cli/clean-package.sh", "coverage": "c8 --all --reporter=lcov --reporter=text npm run coverage:test", "coverage:test": "npm run test && cd ../vm && npm run tester -- --state", diff --git a/packages/evm/src/eof.ts b/packages/evm/src/eof.ts index 10217b11c6f..1bbc7380918 100644 --- a/packages/evm/src/eof.ts +++ b/packages/evm/src/eof.ts @@ -6,13 +6,13 @@ export const VERSION = 0x01 /** * - * @param container A `Buffer` containing bytecode to be checked for EOF1 compliance + * @param container A `Uint8Array` containing bytecode to be checked for EOF1 compliance * @returns an object containing the size of the code section and data sections for a valid * EOF1 container or else undefined if `container` is not valid EOF1 bytecode * * Note: See https://eips.ethereum.org/EIPS/eip-3540 for further details */ -export const codeAnalysis = (container: Buffer) => { +export const codeAnalysis = (container: Uint8Array) => { const secCode = 0x01 const secData = 0x02 const secTerminator = 0x00 @@ -62,7 +62,7 @@ export const codeAnalysis = (container: Buffer) => { return sectionSizes } -export const validOpcodes = (code: Buffer) => { +export const validOpcodes = (code: Uint8Array) => { // EIP-3670 - validate all opcodes const opcodes = new Set(handlers.keys()) opcodes.add(0xfe) // Add INVALID opcode to set @@ -92,13 +92,13 @@ export const validOpcodes = (code: Buffer) => { return true } -export const getEOFCode = (code: Buffer) => { +export const getEOFCode = (code: Uint8Array) => { const sectionSizes = codeAnalysis(code) if (sectionSizes === undefined) { return code } else { const codeStart = sectionSizes.data > 0 ? 10 : 7 - return code.slice(codeStart, codeStart + sectionSizes.code) + return code.subarray(codeStart, codeStart + sectionSizes.code) } } diff --git a/packages/evm/src/evm.ts b/packages/evm/src/evm.ts index 5cd91707c01..f822b54fb93 100644 --- a/packages/evm/src/evm.ts +++ b/packages/evm/src/evm.ts @@ -4,7 +4,9 @@ import { AsyncEventEmitter, KECCAK256_NULL, MAX_INTEGER, - bigIntToBuffer, + bigIntToBytes, + bytesToHex, + equalsBytes, generateAddress, generateAddress2, short, @@ -388,7 +390,7 @@ export class EVM implements EVMInterface { gasRefund: message.gasRefund, executionGasUsed: BigInt(0), exceptionError: errorMessage, // Only defined if addToBalance failed - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), }, } } @@ -430,7 +432,7 @@ export class EVM implements EVMInterface { return { createdAddress: message.to, execResult: { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), exceptionError: new EvmError(ERROR.INITCODE_SIZE_VIOLATION), executionGasUsed: message.gasLimit, }, @@ -439,7 +441,7 @@ export class EVM implements EVMInterface { } message.code = message.data - message.data = Buffer.alloc(0) + message.data = new Uint8Array(0) message.to = await this._generateAddress(message) if (this.DEBUG) { debug(`Generated CREATE contract address ${message.to}`) @@ -449,7 +451,7 @@ export class EVM implements EVMInterface { // Check for collision if ( (toAccount.nonce && toAccount.nonce > BigInt(0)) || - !toAccount.codeHash.equals(KECCAK256_NULL) + !(equalsBytes(toAccount.codeHash, KECCAK256_NULL) === true) ) { if (this.DEBUG) { debug(`Returning on address collision`) @@ -457,7 +459,7 @@ export class EVM implements EVMInterface { return { createdAddress: message.to, execResult: { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), exceptionError: new EvmError(ERROR.CREATE_COLLISION), executionGasUsed: message.gasLimit, }, @@ -507,7 +509,7 @@ export class EVM implements EVMInterface { executionGasUsed: BigInt(0), gasRefund: message.gasRefund, exceptionError: errorMessage, // only defined if addToBalance failed - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), }, } } @@ -562,7 +564,7 @@ export class EVM implements EVMInterface { // in the bytecode of the contract if ( !EOF.validOpcodes( - result.returnValue.slice(codeStart, codeStart + eof1CodeAnalysisResults.code) + result.returnValue.subarray(codeStart, codeStart + eof1CodeAnalysisResults.code) ) ) { result = { @@ -637,9 +639,9 @@ export class EVM implements EVMInterface { const env = { address: message.to ?? Address.zero(), caller: message.caller ?? Address.zero(), - callData: message.data ?? Buffer.from([0]), + callData: message.data ?? Uint8Array.from([0]), callValue: message.value ?? BigInt(0), - code: message.code as Buffer, + code: message.code as Uint8Array, isStatic: message.isStatic ?? false, depth: message.depth ?? 0, gasPrice: this._tx!.gasPrice, @@ -654,10 +656,10 @@ export class EVM implements EVMInterface { const interpreter = new Interpreter(this, this.eei, env, message.gasLimit) if (message.selfdestruct) { - interpreter._result.selfdestruct = message.selfdestruct as { [key: string]: Buffer } + interpreter._result.selfdestruct = message.selfdestruct as { [key: string]: Uint8Array } } - const interpreterRes = await interpreter.run(message.code as Buffer, opts) + const interpreterRes = await interpreter.run(message.code as Uint8Array, opts) let result = interpreter._result let gasUsed = message.gasLimit - interpreterRes.runState!.gasLeft @@ -688,7 +690,7 @@ export class EVM implements EVMInterface { gas: interpreterRes.runState?.gasLeft, executionGasUsed: gasUsed, gasRefund: interpreterRes.runState!.gasRefund, - returnValue: result.returnValue ? result.returnValue : Buffer.alloc(0), + returnValue: result.returnValue ? result.returnValue : new Uint8Array(0), } } @@ -750,7 +752,7 @@ export class EVM implements EVMInterface { if (!message.to && this._common.isActivatedEIP(2929) === true) { message.code = message.data - this.eei.addWarmedAddress((await this._generateAddress(message)).buf) + this.eei.addWarmedAddress((await this._generateAddress(message)).bytes) } await this.eei.checkpoint() @@ -853,7 +855,7 @@ export class EVM implements EVMInterface { * if no such precompile exists. */ getPrecompile(address: Address): PrecompileFunc | undefined { - return this.precompiles.get(address.buf.toString('hex')) + return this.precompiles.get(bytesToHex(address.bytes)) } /** @@ -861,7 +863,7 @@ export class EVM implements EVMInterface { */ protected runPrecompile( code: PrecompileFunc, - data: Buffer, + data: Uint8Array, gasLimit: bigint ): Promise | ExecResult { if (typeof code !== 'function') { @@ -900,11 +902,11 @@ export class EVM implements EVMInterface { protected async _generateAddress(message: Message): Promise
{ let addr if (message.salt) { - addr = generateAddress2(message.caller.buf, message.salt, message.code as Buffer) + addr = generateAddress2(message.caller.bytes, message.salt, message.code as Uint8Array) } else { const acc = await this.eei.getAccount(message.caller) const newNonce = acc.nonce - BigInt(1) - addr = generateAddress(message.caller.buf, bigIntToBuffer(newNonce)) + addr = generateAddress(message.caller.bytes, bigIntToBytes(newNonce)) } return new Address(addr) } @@ -995,7 +997,7 @@ export interface ExecResult { /** * Return value from the contract */ - returnValue: Buffer + returnValue: Uint8Array /** * Array of logs that the contract emitted */ @@ -1003,7 +1005,7 @@ export interface ExecResult { /** * A map from the accounts that have self-destructed to the addresses to send their funds to */ - selfdestruct?: { [k: string]: Buffer } + selfdestruct?: { [k: string]: Uint8Array } /** * The gas refund counter */ @@ -1012,7 +1014,7 @@ export interface ExecResult { export function OOGResult(gasLimit: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasLimit, exceptionError: new EvmError(ERROR.OUT_OF_GAS), } @@ -1020,7 +1022,7 @@ export function OOGResult(gasLimit: bigint): ExecResult { // CodeDeposit OOG Result export function COOGResult(gasUsedCreateCode: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasUsedCreateCode, exceptionError: new EvmError(ERROR.CODESTORE_OUT_OF_GAS), } @@ -1028,7 +1030,7 @@ export function COOGResult(gasUsedCreateCode: bigint): ExecResult { export function INVALID_BYTECODE_RESULT(gasLimit: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasLimit, exceptionError: new EvmError(ERROR.INVALID_BYTECODE_RESULT), } @@ -1036,7 +1038,7 @@ export function INVALID_BYTECODE_RESULT(gasLimit: bigint): ExecResult { export function INVALID_EOF_RESULT(gasLimit: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasLimit, exceptionError: new EvmError(ERROR.INVALID_EOF_FORMAT), } @@ -1044,7 +1046,7 @@ export function INVALID_EOF_RESULT(gasLimit: bigint): ExecResult { export function CodesizeExceedsMaximumError(gasUsed: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasUsed, exceptionError: new EvmError(ERROR.CODESIZE_EXCEEDS_MAXIMUM), } @@ -1052,7 +1054,7 @@ export function CodesizeExceedsMaximumError(gasUsed: bigint): ExecResult { export function EvmErrorResult(error: EvmError, gasUsed: bigint): ExecResult { return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: gasUsed, exceptionError: error, } diff --git a/packages/evm/src/interpreter.ts b/packages/evm/src/interpreter.ts index d38489c0106..dbc3afe86f8 100644 --- a/packages/evm/src/interpreter.ts +++ b/packages/evm/src/interpreter.ts @@ -1,5 +1,5 @@ import { ConsensusAlgorithm } from '@ethereumjs/common' -import { MAX_UINT64, bigIntToHex, bufferToBigInt, intToHex } from '@ethereumjs/util' +import { MAX_UINT64, bigIntToHex, bytesToBigInt, bytesToHex, intToHex } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' import { EOF } from './eof' @@ -26,19 +26,19 @@ export interface InterpreterOpts { */ export interface RunResult { logs: Log[] - returnValue?: Buffer + returnValue?: Uint8Array /** * A map from the accounts that have self-destructed to the addresses to send their funds to */ - selfdestruct: { [k: string]: Buffer } + selfdestruct: { [k: string]: Uint8Array } } export interface Env { address: Address caller: Address - callData: Buffer + callData: Uint8Array callValue: bigint - code: Buffer + code: Uint8Array isStatic: boolean depth: number gasPrice: bigint @@ -47,8 +47,8 @@ export interface Env { contract: Account codeAddress: Address /* Different than address for DELEGATECALL and CALLCODE */ gasRefund: bigint /* Current value (at begin of the frame) of the gas refund */ - containerCode?: Buffer /** Full container code for EOF1 contracts */ - versionedHashes: Buffer[] /** Versioned hashes for blob transactions */ + containerCode?: Uint8Array /** Full container code for EOF1 contracts */ + versionedHashes: Uint8Array[] /** Versioned hashes for blob transactions */ } export interface RunState { @@ -59,7 +59,7 @@ export interface RunState { highestMemCost: bigint stack: Stack returnStack: Stack - code: Buffer + code: Uint8Array shouldDoJumpAnalysis: boolean validJumps: Uint8Array // array of values where validJumps[index] has value 0 (default), 1 (jumpdest), 2 (beginsub) eei: EEIInterface @@ -69,7 +69,7 @@ export interface RunState { gasRefund: bigint // Tracks the current refund gasLeft: bigint // Current gas left auth?: Address /** EIP-3074 AUTH parameter */ - returnBuffer: Buffer /* Current bytes in the return buffer. Cleared each time a CALL/CREATE is made in the current frame. */ + returnBytes: Uint8Array /* Current bytes in the return Uint8Array. Cleared each time a CALL/CREATE is made in the current frame. */ } export interface InterpreterResult { @@ -93,7 +93,7 @@ export interface InterpreterStep { } account: Account address: Address - memory: Buffer + memory: Uint8Array memoryWordCount: bigint codeAddress: Address } @@ -131,7 +131,7 @@ export class Interpreter { highestMemCost: BigInt(0), stack: new Stack(), returnStack: new Stack(1023), // 1023 return stack height limit per EIP 2315 spec - code: Buffer.alloc(0), + code: new Uint8Array(0), validJumps: Uint8Array.from([]), eei: this._eei, env, @@ -139,7 +139,7 @@ export class Interpreter { interpreter: this, gasRefund: env.gasRefund, gasLeft, - returnBuffer: Buffer.alloc(0), + returnBytes: new Uint8Array(0), } this._env = env this._result = { @@ -149,7 +149,7 @@ export class Interpreter { } } - async run(code: Buffer, opts: InterpreterOpts = {}): Promise { + async run(code: Uint8Array, opts: InterpreterOpts = {}): Promise { if (!this._common.isActivatedEIP(3540) || code[0] !== EOF.FORMAT) { // EIP-3540 isn't active and first byte is not 0xEF - treat as legacy bytecode this._runState.code = code @@ -180,10 +180,10 @@ export class Interpreter { if (codeSections.data) { // Set code to EOF container code section which starts at byte position 10 if data section is present - this._runState.code = code.slice(10, 10 + codeSections!.code) + this._runState.code = code.subarray(10, 10 + codeSections!.code) } else { // Set code to EOF container code section which starts at byte position 7 if no data section is present - this._runState.code = code.slice(7, 7 + codeSections!.code) + this._runState.code = code.subarray(7, 7 + codeSections!.code) } } this._runState.programCounter = opts.pc ?? this._runState.programCounter @@ -349,12 +349,12 @@ export class Interpreter { * @property {BigInt} gasLeft amount of gasLeft * @property {BigInt} gasRefund gas refund * @property {StateManager} stateManager a {@link StateManager} instance - * @property {Array} stack an `Array` of `Buffers` containing the stack + * @property {Array} stack an `Array` of `Uint8Arrays` containing the stack * @property {Array} returnStack the return stack * @property {Account} account the Account which owns the code running * @property {Address} address the address of the `account` * @property {Number} depth the current number of calls deep the contract is - * @property {Buffer} memory the memory of the EVM as a `buffer` + * @property {Uint8Array} memory the memory of the EVM as a `Uint8Array` * @property {BigInt} memoryWordCount current size of memory in words * @property {Address} codeAddress the address of the code which is currently being ran (this differs from `address` in a `DELEGATECALL` and `CALLCODE` call) */ @@ -362,7 +362,7 @@ export class Interpreter { } // Returns all valid jump and jumpsub destinations. - _getValidJumpDests(code: Buffer) { + _getValidJumpDests(code: Uint8Array) { const jumps = new Uint8Array(code.length).fill(0) for (let i = 0; i < code.length; i++) { @@ -471,7 +471,7 @@ export class Interpreter { /** * Store 256-bit a value in memory to persistent storage. */ - async storageStore(key: Buffer, value: Buffer): Promise { + async storageStore(key: Uint8Array, value: Uint8Array): Promise { await this._eei.storageStore(this._env.address, key, value) const account = await this._eei.getAccount(this._env.address) this._env.contract = account @@ -482,7 +482,7 @@ export class Interpreter { * @param key - Storage key * @param original - If true, return the original storage value (default: false) */ - async storageLoad(key: Buffer, original = false): Promise { + async storageLoad(key: Uint8Array, original = false): Promise { return this._eei.storageLoad(this._env.address, key, original) } @@ -492,7 +492,7 @@ export class Interpreter { * @param key Storage key * @param value Storage value */ - transientStorageStore(key: Buffer, value: Buffer): void { + transientStorageStore(key: Uint8Array, value: Uint8Array): void { return this._evm._transientStorage.put(this._env.address, key, value) } @@ -501,7 +501,7 @@ export class Interpreter { * @param address Address to use * @param key Storage key */ - transientStorageLoad(key: Buffer): Buffer { + transientStorageLoad(key: Uint8Array): Uint8Array { return this._evm._transientStorage.get(this._env.address, key) } @@ -509,7 +509,7 @@ export class Interpreter { * Set the returning output data for the execution. * @param returnData - Output data to return */ - finish(returnData: Buffer): void { + finish(returnData: Uint8Array): void { this._result.returnValue = returnData trap(ERROR.STOP) } @@ -519,7 +519,7 @@ export class Interpreter { * execution immediately and set the execution result to "reverted". * @param returnData - Output data to return */ - revert(returnData: Buffer): void { + revert(returnData: Uint8Array): void { this._result.returnValue = returnData trap(ERROR.REVERT) } @@ -550,7 +550,7 @@ export class Interpreter { * Returns input data in current environment. This pertains to the input * data passed with the message call instruction or transaction. */ - getCallData(): Buffer { + getCallData(): Uint8Array { return this._env.callData } @@ -567,7 +567,7 @@ export class Interpreter { * that is directly responsible for this execution. */ getCaller(): bigint { - return bufferToBigInt(this._env.caller.buf) + return bytesToBigInt(this._env.caller.bytes) } /** @@ -580,7 +580,7 @@ export class Interpreter { /** * Returns the code running in current environment. */ - getCode(): Buffer { + getCode(): Uint8Array { return this._env.containerCode ?? this._env.code } @@ -597,7 +597,7 @@ export class Interpreter { * Note: create only fills the return data buffer in case of a failure. */ getReturnDataSize(): bigint { - return BigInt(this._runState.returnBuffer.length) + return BigInt(this._runState.returnBytes.length) } /** @@ -605,8 +605,8 @@ export class Interpreter { * from last executed call, callCode, callDelegate, callStatic or create. * Note: create only fills the return data buffer in case of a failure. */ - getReturnData(): Buffer { - return this._runState.returnBuffer + getReturnData(): Uint8Array { + return this._runState.returnBytes } /** @@ -629,7 +629,7 @@ export class Interpreter { * non-empty associated code. */ getTxOrigin(): bigint { - return bufferToBigInt(this._env.origin.buf) + return bytesToBigInt(this._env.origin.bytes) } /** @@ -649,7 +649,7 @@ export class Interpreter { } else { coinbase = this._env.block.header.coinbase } - return bufferToBigInt(coinbase.toBuffer()) + return bytesToBigInt(coinbase.toBytes()) } /** @@ -670,7 +670,7 @@ export class Interpreter { * Returns the block's prevRandao field. */ getBlockPrevRandao(): bigint { - return bufferToBigInt(this._env.block.header.prevRandao) + return bytesToBigInt(this._env.block.header.prevRandao) } /** @@ -703,7 +703,7 @@ export class Interpreter { /** * Sends a message with arbitrary data to a given address path. */ - async call(gasLimit: bigint, address: Address, value: bigint, data: Buffer): Promise { + async call(gasLimit: bigint, address: Address, value: bigint, data: Uint8Array): Promise { const msg = new Message({ caller: this._env.address, gasLimit, @@ -720,7 +720,12 @@ export class Interpreter { /** * Sends a message with arbitrary data to a given address path. */ - async authcall(gasLimit: bigint, address: Address, value: bigint, data: Buffer): Promise { + async authcall( + gasLimit: bigint, + address: Address, + value: bigint, + data: Uint8Array + ): Promise { const msg = new Message({ caller: this._runState.auth, gasLimit, @@ -738,7 +743,12 @@ export class Interpreter { /** * Message-call into this account with an alternative account's code. */ - async callCode(gasLimit: bigint, address: Address, value: bigint, data: Buffer): Promise { + async callCode( + gasLimit: bigint, + address: Address, + value: bigint, + data: Uint8Array + ): Promise { const msg = new Message({ caller: this._env.address, gasLimit, @@ -762,7 +772,7 @@ export class Interpreter { gasLimit: bigint, address: Address, value: bigint, - data: Buffer + data: Uint8Array ): Promise { const msg = new Message({ caller: this._env.address, @@ -785,7 +795,7 @@ export class Interpreter { gasLimit: bigint, address: Address, value: bigint, - data: Buffer + data: Uint8Array ): Promise { const msg = new Message({ caller: this._env.caller, @@ -807,8 +817,8 @@ export class Interpreter { msg.selfdestruct = selfdestruct msg.gasRefund = this._runState.gasRefund - // empty the return data buffer - this._runState.returnBuffer = Buffer.alloc(0) + // empty the return data Uint8Array + this._runState.returnBytes = new Uint8Array(0) // Check if account has enough ether and max depth not exceeded if ( @@ -833,7 +843,7 @@ export class Interpreter { (!results.execResult.exceptionError || results.execResult.exceptionError.error === ERROR.REVERT) ) { - this._runState.returnBuffer = results.execResult.returnValue + this._runState.returnBytes = results.execResult.returnValue } if (!results.execResult.exceptionError) { @@ -850,13 +860,18 @@ export class Interpreter { /** * Creates a new contract with a given value. */ - async create(gasLimit: bigint, value: bigint, data: Buffer, salt?: Buffer): Promise { + async create( + gasLimit: bigint, + value: bigint, + data: Uint8Array, + salt?: Uint8Array + ): Promise { const selfdestruct = { ...this._result.selfdestruct } const caller = this._env.address const depth = this._env.depth + 1 // empty the return data buffer - this._runState.returnBuffer = Buffer.alloc(0) + this._runState.returnBytes = new Uint8Array(0) // Check if account has enough ether and max depth not exceeded if ( @@ -908,7 +923,7 @@ export class Interpreter { results.execResult.exceptionError && results.execResult.exceptionError.error === ERROR.REVERT ) { - this._runState.returnBuffer = results.execResult.returnValue + this._runState.returnBytes = results.execResult.returnValue } if ( @@ -922,7 +937,7 @@ export class Interpreter { this._runState.gasRefund = results.execResult.gasRefund ?? BigInt(0) if (results.createdAddress) { // push the created address to the stack - return bufferToBigInt(results.createdAddress.buf) + return bytesToBigInt(results.createdAddress.bytes) } } @@ -933,7 +948,12 @@ export class Interpreter { * Creates a new contract with a given value. Generates * a deterministic address via CREATE2 rules. */ - async create2(gasLimit: bigint, value: bigint, data: Buffer, salt: Buffer): Promise { + async create2( + gasLimit: bigint, + value: bigint, + data: Uint8Array, + salt: Uint8Array + ): Promise { return this.create(gasLimit, value, data, salt) } @@ -949,11 +969,11 @@ export class Interpreter { async _selfDestruct(toAddress: Address): Promise { // only add to refund if this is the first selfdestruct for the address - if (this._result.selfdestruct[this._env.address.buf.toString('hex')] === undefined) { + if (this._result.selfdestruct[bytesToHex(this._env.address.bytes)] === undefined) { this.refundGas(this._common.param('gasPrices', 'selfdestructRefund')) } - this._result.selfdestruct[this._env.address.buf.toString('hex')] = toAddress.buf + this._result.selfdestruct[bytesToHex(this._env.address.bytes)] = toAddress.bytes // Add to beneficiary balance const toAccount = await this._eei.getAccount(toAddress) @@ -971,7 +991,7 @@ export class Interpreter { /** * Creates a new log in the current environment. */ - log(data: Buffer, numberOfTopics: number, topics: Buffer[]): void { + log(data: Uint8Array, numberOfTopics: number, topics: Uint8Array[]): void { if (numberOfTopics < 0 || numberOfTopics > 4) { trap(ERROR.OUT_OF_RANGE) } @@ -980,7 +1000,7 @@ export class Interpreter { trap(ERROR.INTERNAL_ERROR) } - const log: Log = [this._env.address.buf, topics, data] + const log: Log = [this._env.address.bytes, topics, data] this._result.logs.push(log) } diff --git a/packages/evm/src/memory.ts b/packages/evm/src/memory.ts index c063df37387..85eafd06041 100644 --- a/packages/evm/src/memory.ts +++ b/packages/evm/src/memory.ts @@ -1,3 +1,5 @@ +import { concatBytesNoTypeCheck } from '@ethereumjs/util' + const ceil = (value: number, ceiling: number): number => { const r = value % ceiling if (r === 0) { @@ -14,10 +16,10 @@ const CONTAINER_SIZE = 8192 * for the ethereum virtual machine. */ export class Memory { - _store: Buffer + _store: Uint8Array constructor() { - this._store = Buffer.alloc(0) + this._store = new Uint8Array(0) } /** @@ -32,10 +34,10 @@ export class Memory { const newSize = ceil(offset + size, 32) const sizeDiff = newSize - this._store.length if (sizeDiff > 0) { - this._store = Buffer.concat([ + this._store = concatBytesNoTypeCheck( this._store, - Buffer.alloc(Math.ceil(sizeDiff / CONTAINER_SIZE) * CONTAINER_SIZE), - ]) + new Uint8Array(Math.ceil(sizeDiff / CONTAINER_SIZE) * CONTAINER_SIZE) + ) } } @@ -45,7 +47,7 @@ export class Memory { * @param size - How many bytes to write * @param value - Value */ - write(offset: number, size: number, value: Buffer) { + write(offset: number, size: number, value: Uint8Array) { if (size === 0) { return } @@ -55,24 +57,27 @@ export class Memory { if (value.length !== size) throw new Error('Invalid value size') if (offset + size > this._store.length) throw new Error('Value exceeds memory capacity') - value.copy(this._store, offset) + this._store.set(value, offset) } /** - * Reads a slice of memory from `offset` till `offset + size` as a `Buffer`. + * Reads a slice of memory from `offset` till `offset + size` as a `Uint8Array`. * It fills up the difference between memory's length and `offset + size` with zeros. * @param offset - Starting position * @param size - How many bytes to read * @param avoidCopy - Avoid memory copy if possible for performance reasons (optional) */ - read(offset: number, size: number, avoidCopy?: boolean): Buffer { + read(offset: number, size: number, avoidCopy?: boolean): Uint8Array { this.extend(offset, size) - const loaded = this._store.slice(offset, offset + size) + const loaded = this._store.subarray(offset, offset + size) if (avoidCopy === true) { return loaded } + const returnBytes = new Uint8Array(size) + // Copy the stored "buffer" from memory into the return Buffer + returnBytes.set(loaded) - return Buffer.from(loaded) + return returnBytes } } diff --git a/packages/evm/src/message.ts b/packages/evm/src/message.ts index 5928bb73857..e8d43af8778 100644 --- a/packages/evm/src/message.ts +++ b/packages/evm/src/message.ts @@ -5,7 +5,7 @@ import type { PrecompileFunc } from './precompiles' const defaults = { value: BigInt(0), caller: Address.zero(), - data: Buffer.alloc(0), + data: new Uint8Array(0), depth: 0, isStatic: false, isCompiled: false, @@ -18,21 +18,21 @@ interface MessageOpts { value?: bigint caller?: Address gasLimit: bigint - data?: Buffer + data?: Uint8Array depth?: number - code?: Buffer | PrecompileFunc + code?: Uint8Array | PrecompileFunc codeAddress?: Address isStatic?: boolean isCompiled?: boolean - salt?: Buffer + salt?: Uint8Array /** * A map of addresses to selfdestruct, see {@link Message.selfdestruct} */ - selfdestruct?: { [key: string]: boolean } | { [key: string]: Buffer } + selfdestruct?: { [key: string]: boolean } | { [key: string]: Uint8Array } delegatecall?: boolean authcallOrigin?: Address gasRefund?: bigint - versionedHashes?: Buffer[] + versionedHashes?: Uint8Array[] } export class Message { @@ -40,19 +40,19 @@ export class Message { value: bigint caller: Address gasLimit: bigint - data: Buffer + data: Uint8Array depth: number - code?: Buffer | PrecompileFunc + code?: Uint8Array | PrecompileFunc _codeAddress?: Address isStatic: boolean isCompiled: boolean - salt?: Buffer - containerCode?: Buffer /** container code for EOF1 contracts - used by CODECOPY/CODESIZE */ + salt?: Uint8Array + containerCode?: Uint8Array /** container code for EOF1 contracts - used by CODECOPY/CODESIZE */ /** * Map of addresses to selfdestruct. Key is the unprefixed address. - * Value is a boolean when marked for destruction and replaced with a Buffer containing the address where the remaining funds are sent. + * Value is a boolean when marked for destruction and replaced with a Uint8Array containing the address where the remaining funds are sent. */ - selfdestruct?: { [key: string]: boolean } | { [key: string]: Buffer } + selfdestruct?: { [key: string]: boolean } | { [key: string]: Uint8Array } delegatecall: boolean /** * This is used to store the origin of the AUTHCALL, @@ -63,7 +63,7 @@ export class Message { /** * List of versioned hashes if message is a blob transaction in the outer VM */ - versionedHashes?: Buffer[] + versionedHashes?: Uint8Array[] constructor(opts: MessageOpts) { this.to = opts.to diff --git a/packages/evm/src/opcodes/EIP1283.ts b/packages/evm/src/opcodes/EIP1283.ts index b4341ae2012..8682811eb13 100644 --- a/packages/evm/src/opcodes/EIP1283.ts +++ b/packages/evm/src/opcodes/EIP1283.ts @@ -1,3 +1,5 @@ +import { equalsBytes } from 'ethereum-cryptography/utils' + import type { RunState } from '../interpreter' import type { Common } from '@ethereumjs/common' @@ -5,24 +7,24 @@ import type { Common } from '@ethereumjs/common' * Adjusts gas usage and refunds of SStore ops per EIP-1283 (Constantinople) * * @param {RunState} runState - * @param {Buffer} currentStorage - * @param {Buffer} originalStorage - * @param {Buffer} value + * @param {Uint8Array} currentStorage + * @param {Uint8Array} originalStorage + * @param {Uint8Array} value * @param {Common} common */ export function updateSstoreGasEIP1283( runState: RunState, - currentStorage: Buffer, - originalStorage: Buffer, - value: Buffer, + currentStorage: Uint8Array, + originalStorage: Uint8Array, + value: Uint8Array, common: Common ) { - if (currentStorage.equals(value)) { + if (equalsBytes(currentStorage, value)) { // If current value equals new value (this is a no-op), 200 gas is deducted. return common.param('gasPrices', 'netSstoreNoopGas') } // If current value does not equal new value - if (originalStorage.equals(currentStorage)) { + if (equalsBytes(originalStorage, currentStorage)) { // If original value equals current value (this storage slot has not been changed by the current execution context) if (originalStorage.length === 0) { // If original value is 0, 20000 gas is deducted. @@ -55,7 +57,7 @@ export function updateSstoreGasEIP1283( ) } } - if (originalStorage.equals(value)) { + if (equalsBytes(originalStorage, value)) { // If original value equals new value (this storage slot is reset) if (originalStorage.length === 0) { // If original value is 0, add 19800 gas to refund counter. diff --git a/packages/evm/src/opcodes/EIP2200.ts b/packages/evm/src/opcodes/EIP2200.ts index 3b6e9efe46d..5b548fde03c 100644 --- a/packages/evm/src/opcodes/EIP2200.ts +++ b/packages/evm/src/opcodes/EIP2200.ts @@ -1,3 +1,5 @@ +import { equalsBytes } from 'ethereum-cryptography/utils' + import { ERROR } from '../exceptions' import { adjustSstoreGasEIP2929 } from './EIP2929' @@ -10,17 +12,17 @@ import type { Common } from '@ethereumjs/common' * Adjusts gas usage and refunds of SStore ops per EIP-2200 (Istanbul) * * @param {RunState} runState - * @param {Buffer} currentStorage - * @param {Buffer} originalStorage - * @param {Buffer} value + * @param {Uint8Array} currentStorage + * @param {Uint8Array} originalStorage + * @param {Uint8Array} value * @param {Common} common */ export function updateSstoreGasEIP2200( runState: RunState, - currentStorage: Buffer, - originalStorage: Buffer, - value: Buffer, - key: Buffer, + currentStorage: Uint8Array, + originalStorage: Uint8Array, + value: Uint8Array, + key: Uint8Array, common: Common ) { // Fail if not enough gas is left @@ -29,11 +31,11 @@ export function updateSstoreGasEIP2200( } // Noop - if (currentStorage.equals(value)) { + if (equalsBytes(currentStorage, value)) { const sstoreNoopCost = common.param('gasPrices', 'sstoreNoopGasEIP2200') return adjustSstoreGasEIP2929(runState, key, sstoreNoopCost, 'noop', common) } - if (originalStorage.equals(currentStorage)) { + if (equalsBytes(originalStorage, currentStorage)) { // Create slot if (originalStorage.length === 0) { return common.param('gasPrices', 'sstoreInitGasEIP2200') @@ -63,7 +65,7 @@ export function updateSstoreGasEIP2200( ) } } - if (originalStorage.equals(value)) { + if (equalsBytes(originalStorage, value)) { if (originalStorage.length === 0) { // Reset to original non-existent slot const sstoreInitRefund = common.param('gasPrices', 'sstoreInitRefundEIP2200') diff --git a/packages/evm/src/opcodes/EIP2929.ts b/packages/evm/src/opcodes/EIP2929.ts index 8e6ee5c3d2a..bb702ff6e63 100644 --- a/packages/evm/src/opcodes/EIP2929.ts +++ b/packages/evm/src/opcodes/EIP2929.ts @@ -22,7 +22,7 @@ export function accessAddressEIP2929( if (common.isActivatedEIP(2929) === false) return BigInt(0) const eei = runState.eei - const addressStr = address.buf + const addressStr = address.bytes // Cold if (!eei.isWarmedAddress(addressStr)) { @@ -45,19 +45,19 @@ export function accessAddressEIP2929( * Adjusts cost incurred for executing opcode based on whether storage read * is warm/cold. (EIP 2929) * @param {RunState} runState - * @param {Buffer} key (to storage slot) + * @param {Uint8Array} key (to storage slot) * @param {Common} common */ export function accessStorageEIP2929( runState: RunState, - key: Buffer, + key: Uint8Array, isSstore: boolean, common: Common ): bigint { if (common.isActivatedEIP(2929) === false) return BigInt(0) const eei = runState.eei - const address = runState.interpreter.getAddress().buf + const address = runState.interpreter.getAddress().bytes const slotIsCold = !eei.isWarmedStorage(address, key) // Cold (SLOAD and SSTORE) @@ -74,7 +74,7 @@ export function accessStorageEIP2929( * Adjusts cost of SSTORE_RESET_GAS or SLOAD (aka sstorenoop) (EIP-2200) downward when storage * location is already warm * @param {RunState} runState - * @param {Buffer} key storage slot + * @param {Uint8Array} key storage slot * @param {BigInt} defaultCost SSTORE_RESET_GAS / SLOAD * @param {string} costName parameter name ('noop') * @param {Common} common @@ -82,7 +82,7 @@ export function accessStorageEIP2929( */ export function adjustSstoreGasEIP2929( runState: RunState, - key: Buffer, + key: Uint8Array, defaultCost: bigint, costName: string, common: Common @@ -90,7 +90,7 @@ export function adjustSstoreGasEIP2929( if (common.isActivatedEIP(2929) === false) return defaultCost const eei = runState.eei - const address = runState.interpreter.getAddress().buf + const address = runState.interpreter.getAddress().bytes const warmRead = common.param('gasPrices', 'warmstorageread') const coldSload = common.param('gasPrices', 'coldsload') diff --git a/packages/evm/src/opcodes/functions.ts b/packages/evm/src/opcodes/functions.ts index 09256f79719..0fe763996ff 100644 --- a/packages/evm/src/opcodes/functions.ts +++ b/packages/evm/src/opcodes/functions.ts @@ -3,20 +3,21 @@ import { MAX_INTEGER_BIGINT, SECP256K1_ORDER_DIV_2, TWO_POW256, - bigIntToBuffer, - bufferToBigInt, + bigIntToBytes, + bytesToBigInt, + concatBytesNoTypeCheck, ecrecover, publicToAddress, setLengthLeft, setLengthRight, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { ERROR } from '../exceptions' import { - addressToBuffer, + addresstoBytes, describeLocation, exponentiation, fromTwos, @@ -32,7 +33,7 @@ import { import type { RunState } from '../interpreter' import type { Common } from '@ethereumjs/common' -const EIP3074MAGIC = Buffer.from('03', 'hex') +const EIP3074MAGIC = hexToBytes('03') export interface SyncOpHandler { (runState: RunState, common: Common): void @@ -369,7 +370,7 @@ export const handlers: Map = new Map([ 0x20, function (runState) { const [offset, length] = runState.stack.popN(2) - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (length !== BigInt(0)) { data = runState.memory.read(Number(offset), Number(length)) } @@ -382,7 +383,7 @@ export const handlers: Map = new Map([ [ 0x30, function (runState) { - const address = bufferToBigInt(runState.interpreter.getAddress().buf) + const address = bytesToBigInt(runState.interpreter.getAddress().bytes) runState.stack.push(address) }, ], @@ -391,7 +392,7 @@ export const handlers: Map = new Map([ 0x31, async function (runState) { const addressBigInt = runState.stack.pop() - const address = new Address(addressToBuffer(addressBigInt)) + const address = new Address(addresstoBytes(addressBigInt)) const balance = await runState.interpreter.getExternalBalance(address) runState.stack.push(balance) }, @@ -428,9 +429,9 @@ export const handlers: Map = new Map([ } const i = Number(pos) - let loaded = runState.interpreter.getCallData().slice(i, i + 32) - loaded = loaded.length ? loaded : Buffer.from([0]) - let r = bufferToBigInt(loaded) + let loaded = runState.interpreter.getCallData().subarray(i, i + 32) + loaded = loaded.length ? loaded : Uint8Array.from([0]) + let r = bytesToBigInt(loaded) if (loaded.length < 32) { r = r << (BigInt(8) * BigInt(32 - loaded.length)) } @@ -486,7 +487,7 @@ export const handlers: Map = new Map([ async function (runState) { const addressBigInt = runState.stack.pop() const size = BigInt( - (await runState.eei.getContractCode(new Address(addressToBuffer(addressBigInt)))).length + (await runState.eei.getContractCode(new Address(addresstoBytes(addressBigInt)))).length ) runState.stack.push(size) }, @@ -498,7 +499,7 @@ export const handlers: Map = new Map([ const [addressBigInt, memOffset, codeOffset, dataLength] = runState.stack.popN(4) if (dataLength !== BigInt(0)) { - const code = await runState.eei.getContractCode(new Address(addressToBuffer(addressBigInt))) + const code = await runState.eei.getContractCode(new Address(addresstoBytes(addressBigInt))) const data = getDataSlice(code, codeOffset, dataLength) const memOffsetNum = Number(memOffset) @@ -512,14 +513,14 @@ export const handlers: Map = new Map([ 0x3f, async function (runState) { const addressBigInt = runState.stack.pop() - const address = new Address(addressToBuffer(addressBigInt)) + const address = new Address(addresstoBytes(addressBigInt)) const account = await runState.eei.getAccount(address) if (account.isEmpty()) { runState.stack.push(BigInt(0)) return } - runState.stack.push(BigInt('0x' + account.codeHash.toString('hex'))) + runState.stack.push(BigInt('0x' + bytesToHex(account.codeHash))) }, ], // 0x3d: RETURNDATASIZE @@ -638,7 +639,7 @@ export const handlers: Map = new Map([ function (runState) { const index = runState.stack.pop() if (runState.env.versionedHashes.length > Number(index)) { - runState.stack.push(bufferToBigInt(runState.env.versionedHashes[Number(index)])) + runState.stack.push(bytesToBigInt(runState.env.versionedHashes[Number(index)])) } else { runState.stack.push(BigInt(0)) } @@ -658,7 +659,7 @@ export const handlers: Map = new Map([ function (runState) { const pos = runState.stack.pop() const word = runState.memory.read(Number(pos), 32, true) - runState.stack.push(bufferToBigInt(word)) + runState.stack.push(bytesToBigInt(word)) }, ], // 0x52: MSTORE @@ -666,7 +667,7 @@ export const handlers: Map = new Map([ 0x52, function (runState) { const [offset, word] = runState.stack.popN(2) - const buf = setLengthLeft(bigIntToBuffer(word), 32) + const buf = setLengthLeft(bigIntToBytes(word), 32) const offsetNum = Number(offset) runState.memory.write(offsetNum, 32, buf) }, @@ -677,7 +678,7 @@ export const handlers: Map = new Map([ function (runState) { const [offset, byte] = runState.stack.popN(2) - const buf = bigIntToBuffer(byte & BigInt(0xff)) + const buf = bigIntToBytes(byte & BigInt(0xff)) const offsetNum = Number(offset) runState.memory.write(offsetNum, 1, buf) }, @@ -687,9 +688,9 @@ export const handlers: Map = new Map([ 0x54, async function (runState) { const key = runState.stack.pop() - const keyBuf = setLengthLeft(bigIntToBuffer(key), 32) + const keyBuf = setLengthLeft(bigIntToBytes(key), 32) const value = await runState.interpreter.storageLoad(keyBuf) - const valueBigInt = value.length ? bufferToBigInt(value) : BigInt(0) + const valueBigInt = value.length ? bytesToBigInt(value) : BigInt(0) runState.stack.push(valueBigInt) }, ], @@ -699,13 +700,13 @@ export const handlers: Map = new Map([ async function (runState) { const [key, val] = runState.stack.popN(2) - const keyBuf = setLengthLeft(bigIntToBuffer(key), 32) + const keyBuf = setLengthLeft(bigIntToBytes(key), 32) // NOTE: this should be the shortest representation let value if (val === BigInt(0)) { - value = Buffer.from([]) + value = Uint8Array.from([]) } else { - value = bigIntToBuffer(val) + value = bigIntToBytes(val) } await runState.interpreter.storageStore(keyBuf, value) @@ -830,8 +831,8 @@ export const handlers: Map = new Map([ trap(ERROR.OUT_OF_RANGE) } - const loaded = bufferToBigInt( - runState.code.slice(runState.programCounter, runState.programCounter + numToPush) + const loaded = bytesToBigInt( + runState.code.subarray(runState.programCounter, runState.programCounter + numToPush) ) runState.programCounter += numToPush runState.stack.push(loaded) @@ -863,10 +864,10 @@ export const handlers: Map = new Map([ const topics = runState.stack.popN(topicsCount) const topicsBuf = topics.map(function (a: bigint) { - return setLengthLeft(bigIntToBuffer(a), 32) + return setLengthLeft(bigIntToBytes(a), 32) }) - let mem = Buffer.alloc(0) + let mem = new Uint8Array(0) if (memLength !== BigInt(0)) { mem = runState.memory.read(Number(memOffset), Number(memLength)) } @@ -879,9 +880,9 @@ export const handlers: Map = new Map([ 0xb3, function (runState) { const key = runState.stack.pop() - const keyBuf = setLengthLeft(bigIntToBuffer(key), 32) + const keyBuf = setLengthLeft(bigIntToBytes(key), 32) const value = runState.interpreter.transientStorageLoad(keyBuf) - const valueBN = value.length ? bufferToBigInt(value) : BigInt(0) + const valueBN = value.length ? bytesToBigInt(value) : BigInt(0) runState.stack.push(valueBN) }, ], @@ -894,13 +895,13 @@ export const handlers: Map = new Map([ } const [key, val] = runState.stack.popN(2) - const keyBuf = setLengthLeft(bigIntToBuffer(key), 32) + const keyBuf = setLengthLeft(bigIntToBytes(key), 32) // NOTE: this should be the shortest representation let value if (val === BigInt(0)) { - value = Buffer.from([]) + value = Uint8Array.from([]) } else { - value = bigIntToBuffer(val) + value = bigIntToBytes(val) } runState.interpreter.transientStorageStore(keyBuf, value) @@ -916,7 +917,7 @@ export const handlers: Map = new Map([ const gasLimit = runState.messageGasLimit! runState.messageGasLimit = undefined - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (length !== BigInt(0)) { data = runState.memory.read(Number(offset), Number(length), true) } @@ -938,7 +939,7 @@ export const handlers: Map = new Map([ const gasLimit = runState.messageGasLimit! runState.messageGasLimit = undefined - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (length !== BigInt(0)) { data = runState.memory.read(Number(offset), Number(length), true) } @@ -947,7 +948,7 @@ export const handlers: Map = new Map([ gasLimit, value, data, - setLengthLeft(bigIntToBuffer(salt), 32) + setLengthLeft(bigIntToBytes(salt), 32) ) runState.stack.push(ret) }, @@ -958,9 +959,9 @@ export const handlers: Map = new Map([ async function (runState: RunState) { const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7) - const toAddress = new Address(addressToBuffer(toAddr)) + const toAddress = new Address(addresstoBytes(toAddr)) - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (inLength !== BigInt(0)) { data = runState.memory.read(Number(inOffset), Number(inLength), true) } @@ -980,12 +981,12 @@ export const handlers: Map = new Map([ async function (runState: RunState) { const [_currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7) - const toAddress = new Address(addressToBuffer(toAddr)) + const toAddress = new Address(addresstoBytes(toAddr)) const gasLimit = runState.messageGasLimit! runState.messageGasLimit = undefined - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (inLength !== BigInt(0)) { data = runState.memory.read(Number(inOffset), Number(inLength), true) } @@ -1003,9 +1004,9 @@ export const handlers: Map = new Map([ const value = runState.interpreter.getCallValue() const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6) - const toAddress = new Address(addressToBuffer(toAddr)) + const toAddress = new Address(addresstoBytes(toAddr)) - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (inLength !== BigInt(0)) { data = runState.memory.read(Number(inOffset), Number(inLength), true) } @@ -1036,18 +1037,18 @@ export const handlers: Map = new Map([ } const yParity = BigInt(mem[31]) - const r = mem.slice(32, 64) - const s = mem.slice(64, 96) - const commit = mem.slice(96, 128) + const r = mem.subarray(32, 64) + const s = mem.subarray(64, 96) + const commit = mem.subarray(96, 128) - if (bufferToBigInt(s) > SECP256K1_ORDER_DIV_2) { + if (bytesToBigInt(s) > SECP256K1_ORDER_DIV_2) { trap(ERROR.AUTH_INVALID_S) } - const paddedInvokerAddress = setLengthLeft(runState.interpreter._env.address.buf, 32) - const chainId = setLengthLeft(bigIntToBuffer(runState.interpreter.getChainId()), 32) - const message = Buffer.concat([EIP3074MAGIC, chainId, paddedInvokerAddress, commit]) - const msgHash = Buffer.from(keccak256(message)) + const paddedInvokerAddress = setLengthLeft(runState.interpreter._env.address.bytes, 32) + const chainId = setLengthLeft(bigIntToBytes(runState.interpreter.getChainId()), 32) + const message = concatBytesNoTypeCheck(EIP3074MAGIC, chainId, paddedInvokerAddress, commit) + const msgHash = keccak256(message) let recover try { @@ -1063,7 +1064,7 @@ export const handlers: Map = new Map([ const address = new Address(addressBuffer) runState.auth = address - const expectedAddress = new Address(setLengthLeft(bigIntToBuffer(authority), 20)) + const expectedAddress = new Address(setLengthLeft(bigIntToBytes(authority), 20)) if (!expectedAddress.equals(address)) { // expected address does not equal the recovered address, clear auth variable @@ -1091,12 +1092,12 @@ export const handlers: Map = new Map([ retLength, ] = runState.stack.popN(8) - const toAddress = new Address(addressToBuffer(addr)) + const toAddress = new Address(addresstoBytes(addr)) const gasLimit = runState.messageGasLimit! runState.messageGasLimit = undefined - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (argsLength !== BigInt(0)) { data = runState.memory.read(Number(argsOffset), Number(argsLength)) } @@ -1114,12 +1115,12 @@ export const handlers: Map = new Map([ const value = BigInt(0) const [_currentGasLimit, toAddr, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6) - const toAddress = new Address(addressToBuffer(toAddr)) + const toAddress = new Address(addresstoBytes(toAddr)) const gasLimit = runState.messageGasLimit! runState.messageGasLimit = undefined - let data = Buffer.alloc(0) + let data = new Uint8Array(0) if (inLength !== BigInt(0)) { data = runState.memory.read(Number(inOffset), Number(inLength), true) } @@ -1135,7 +1136,7 @@ export const handlers: Map = new Map([ 0xf3, function (runState) { const [offset, length] = runState.stack.popN(2) - let returnData = Buffer.alloc(0) + let returnData = new Uint8Array(0) if (length !== BigInt(0)) { returnData = runState.memory.read(Number(offset), Number(length)) } @@ -1147,7 +1148,7 @@ export const handlers: Map = new Map([ 0xfd, function (runState) { const [offset, length] = runState.stack.popN(2) - let returnData = Buffer.alloc(0) + let returnData = new Uint8Array(0) if (length !== BigInt(0)) { returnData = runState.memory.read(Number(offset), Number(length)) } @@ -1160,7 +1161,7 @@ export const handlers: Map = new Map([ 0xff, async function (runState) { const selfdestructToAddressBigInt = runState.stack.pop() - const selfdestructToAddress = new Address(addressToBuffer(selfdestructToAddressBigInt)) + const selfdestructToAddress = new Address(addresstoBytes(selfdestructToAddressBigInt)) return runState.interpreter.selfDestruct(selfdestructToAddress) }, ], diff --git a/packages/evm/src/opcodes/gas.ts b/packages/evm/src/opcodes/gas.ts index 321bb76853f..3b10ea773d8 100644 --- a/packages/evm/src/opcodes/gas.ts +++ b/packages/evm/src/opcodes/gas.ts @@ -1,5 +1,5 @@ import { Hardfork } from '@ethereumjs/common' -import { Address, bigIntToBuffer, setLengthLeft } from '@ethereumjs/util' +import { Address, bigIntToBytes, setLengthLeft } from '@ethereumjs/util' import { ERROR } from '../exceptions' @@ -7,7 +7,7 @@ import { updateSstoreGasEIP1283 } from './EIP1283' import { updateSstoreGasEIP2200 } from './EIP2200' import { accessAddressEIP2929, accessStorageEIP2929 } from './EIP2929' import { - addressToBuffer, + addresstoBytes, divCeil, maxCallGas, setLengthLeftStorage, @@ -74,7 +74,7 @@ export const dynamicGasHandlers: Map { if (common.isActivatedEIP(2929) === true) { const addressBigInt = runState.stack.peek()[0] - const address = new Address(addressToBuffer(addressBigInt)) + const address = new Address(addresstoBytes(addressBigInt)) gas += accessAddressEIP2929(runState, address, common) } return gas @@ -112,7 +112,7 @@ export const dynamicGasHandlers: Map { if (common.isActivatedEIP(2929) === true) { const addressBigInt = runState.stack.peek()[0] - const address = new Address(addressToBuffer(addressBigInt)) + const address = new Address(addresstoBytes(addressBigInt)) gas += accessAddressEIP2929(runState, address, common) } return gas @@ -127,7 +127,7 @@ export const dynamicGasHandlers: Map { if (common.isActivatedEIP(2929) === true) { const addressBigInt = runState.stack.peek()[0] - const address = new Address(addressToBuffer(addressBigInt)) + const address = new Address(addresstoBytes(addressBigInt)) gas += accessAddressEIP2929(runState, address, common) } return gas @@ -199,7 +199,7 @@ export const dynamicGasHandlers: Map { const key = runState.stack.peek()[0] - const keyBuf = setLengthLeft(bigIntToBuffer(key), 32) + const keyBuf = setLengthLeft(bigIntToBytes(key), 32) if (common.isActivatedEIP(2929) === true) { gas += accessStorageEIP2929(runState, keyBuf, false, common) @@ -216,18 +216,20 @@ export const dynamicGasHandlers: Map { const [currentGasLimit, toAddr, value, inOffset, inLength, outOffset, outLength] = runState.stack.peek(7) - const toAddress = new Address(addressToBuffer(toAddr)) + const toAddress = new Address(addresstoBytes(toAddr)) if (runState.interpreter.isStatic() && value !== BigInt(0)) { trap(ERROR.STATIC_STATE_CHANGE) @@ -379,7 +381,7 @@ export const dynamicGasHandlers: Map len) { offset = len @@ -84,7 +84,7 @@ export function getDataSlice(data: Buffer, offset: bigint, length: bigint): Buff end = len } - data = data.slice(Number(offset), Number(end)) + data = data.subarray(Number(offset), Number(end)) // Right-pad with zeros to fill dataLength bytes data = setLengthRight(data, Number(length)) @@ -201,8 +201,8 @@ export function writeCallOutput(runState: RunState, outOffset: bigint, outLength */ export function updateSstoreGas( runState: RunState, - currentStorage: Buffer, - value: Buffer, + currentStorage: Uint8Array, + value: Uint8Array, common: Common ): bigint { if ( diff --git a/packages/evm/src/precompiles/01-ecrecover.ts b/packages/evm/src/precompiles/01-ecrecover.ts index a886bc31486..0d48b119010 100644 --- a/packages/evm/src/precompiles/01-ecrecover.ts +++ b/packages/evm/src/precompiles/01-ecrecover.ts @@ -1,5 +1,7 @@ import { - bufferToBigInt, + bytesToBigInt, + bytesToHex, + bytesToPrefixedHexString, ecrecover, publicToAddress, setLengthLeft, @@ -14,7 +16,7 @@ import type { PrecompileInput } from './types' export function precompile01(opts: PrecompileInput): ExecResult { const gasUsed = opts._common.param('gasPrices', 'ecRecover') - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run ECRECOVER (0x01) precompile data=${short(opts.data)} length=${ opts.data.length @@ -23,7 +25,7 @@ export function precompile01(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECRECOVER (0x01) failed: OOG`) } return OOGResult(opts.gasLimit) @@ -31,48 +33,48 @@ export function precompile01(opts: PrecompileInput): ExecResult { const data = setLengthRight(opts.data, 128) - const msgHash = data.slice(0, 32) - const v = data.slice(32, 64) - const vBigInt = bufferToBigInt(v) + const msgHash = data.subarray(0, 32) + const v = data.subarray(32, 64) + const vBigInt = bytesToBigInt(v) // Guard against util's `ecrecover`: without providing chainId this will return // a signature in most of the cases in the cases that `v=0` or `v=1` // However, this should throw, only 27 and 28 is allowed as input if (vBigInt !== BigInt(27) && vBigInt !== BigInt(28)) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECRECOVER (0x01) failed: v neither 27 nor 28`) } return { executionGasUsed: gasUsed, - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(), } } - const r = data.slice(64, 96) - const s = data.slice(96, 128) + const r = data.subarray(64, 96) + const s = data.subarray(96, 128) let publicKey try { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( - `ECRECOVER (0x01): PK recovery with msgHash=${msgHash.toString('hex')} v=${v.toString( - 'hex' - )} r=${r.toString('hex')}s=${s.toString('hex')}}` + `ECRECOVER (0x01): PK recovery with msgHash=${bytesToHex(msgHash)} v=${bytesToHex( + v + )} r=${bytesToHex(r)}s=${bytesToHex(s)}}` ) } - publicKey = ecrecover(msgHash, bufferToBigInt(v), r, s) + publicKey = ecrecover(msgHash, bytesToBigInt(v), r, s) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECRECOVER (0x01) failed: PK recovery failed`) } return { executionGasUsed: gasUsed, - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), } } const address = setLengthLeft(publicToAddress(publicKey), 32) - if (opts._debug) { - opts._debug(`ECRECOVER (0x01) return address=${address.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`ECRECOVER (0x01) return address=${bytesToPrefixedHexString(address)}`) } return { executionGasUsed: gasUsed, diff --git a/packages/evm/src/precompiles/02-sha256.ts b/packages/evm/src/precompiles/02-sha256.ts index 7e74dfa03e1..7e62b825110 100644 --- a/packages/evm/src/precompiles/02-sha256.ts +++ b/packages/evm/src/precompiles/02-sha256.ts @@ -1,4 +1,4 @@ -import { short, toBuffer } from '@ethereumjs/util' +import { bytesToHex, short } from '@ethereumjs/util' import { sha256 } from 'ethereum-cryptography/sha256' import { OOGResult } from '../evm' @@ -12,7 +12,7 @@ export function precompile02(opts: PrecompileInput): ExecResult { let gasUsed = opts._common.param('gasPrices', 'sha256') gasUsed += opts._common.param('gasPrices', 'sha256Word') * BigInt(Math.ceil(data.length / 32)) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run KECCAK256 (0x02) precompile data=${short(opts.data)} length=${ opts.data.length @@ -21,19 +21,19 @@ export function precompile02(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`KECCAK256 (0x02) failed: OOG`) } return OOGResult(opts.gasLimit) } - const hash = toBuffer(sha256(data)) - if (opts._debug) { - opts._debug(`KECCAK256 (0x02) return hash=${hash.toString('hex')}`) + const hash = sha256(data) + if (opts._debug !== undefined) { + opts._debug(`KECCAK256 (0x02) return hash=${bytesToHex(hash)}`) } return { executionGasUsed: gasUsed, - returnValue: hash, + returnValue: sha256(data), } } diff --git a/packages/evm/src/precompiles/03-ripemd160.ts b/packages/evm/src/precompiles/03-ripemd160.ts index 88d6baad512..79ed9e91301 100644 --- a/packages/evm/src/precompiles/03-ripemd160.ts +++ b/packages/evm/src/precompiles/03-ripemd160.ts @@ -1,4 +1,4 @@ -import { setLengthLeft, short, toBuffer } from '@ethereumjs/util' +import { bytesToHex, setLengthLeft, short } from '@ethereumjs/util' import { ripemd160 } from 'ethereum-cryptography/ripemd160' import { OOGResult } from '../evm' @@ -12,7 +12,7 @@ export function precompile03(opts: PrecompileInput): ExecResult { let gasUsed = opts._common.param('gasPrices', 'ripemd160') gasUsed += opts._common.param('gasPrices', 'ripemd160Word') * BigInt(Math.ceil(data.length / 32)) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run RIPEMD160 (0x03) precompile data=${short(opts.data)} length=${ opts.data.length @@ -21,19 +21,19 @@ export function precompile03(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`RIPEMD160 (0x03) failed: OOG`) } return OOGResult(opts.gasLimit) } - const hash = setLengthLeft(toBuffer(ripemd160(data)), 32) - if (opts._debug) { - opts._debug(`RIPEMD160 (0x03) return hash=${hash.toString('hex')}`) + const hash = setLengthLeft(ripemd160(data), 32) + if (opts._debug !== undefined) { + opts._debug(`RIPEMD160 (0x03) return hash=${bytesToHex(hash)}`) } return { executionGasUsed: gasUsed, - returnValue: hash, + returnValue: setLengthLeft(ripemd160(data), 32), } } diff --git a/packages/evm/src/precompiles/04-identity.ts b/packages/evm/src/precompiles/04-identity.ts index eddfb3c953a..0e50083c957 100644 --- a/packages/evm/src/precompiles/04-identity.ts +++ b/packages/evm/src/precompiles/04-identity.ts @@ -10,7 +10,7 @@ export function precompile04(opts: PrecompileInput): ExecResult { let gasUsed = opts._common.param('gasPrices', 'identity') gasUsed += opts._common.param('gasPrices', 'identityWord') * BigInt(Math.ceil(data.length / 32)) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run IDENTITY (0x04) precompile data=${short(opts.data)} length=${ opts.data.length @@ -19,18 +19,18 @@ export function precompile04(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`IDENTITY (0x04) failed: OOG`) } return OOGResult(opts.gasLimit) } - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`IDENTITY (0x04) return data=${short(opts.data)}`) } return { executionGasUsed: gasUsed, - returnValue: Buffer.from(data), // Copy the memory (`Buffer.from()`) + returnValue: Uint8Array.from(data), // Copy the memory (`Uint8Array.from()`) } } diff --git a/packages/evm/src/precompiles/05-modexp.ts b/packages/evm/src/precompiles/05-modexp.ts index 70d8cd0bbdb..9398e98f1f9 100644 --- a/packages/evm/src/precompiles/05-modexp.ts +++ b/packages/evm/src/precompiles/05-modexp.ts @@ -1,6 +1,7 @@ import { - bigIntToBuffer, - bufferToBigInt, + bigIntToBytes, + bytesToBigInt, + bytesToHex, setLengthLeft, setLengthRight, short, @@ -34,18 +35,18 @@ function multComplexityEIP2565(x: bigint): bigint { return words * words } -function getAdjustedExponentLength(data: Buffer): bigint { +function getAdjustedExponentLength(data: Uint8Array): bigint { let expBytesStart try { - const baseLen = bufferToBigInt(data.slice(0, 32)) + const baseLen = bytesToBigInt(data.subarray(0, 32)) expBytesStart = 96 + Number(baseLen) // 96 for base length, then exponent length, and modulus length, then baseLen for the base data, then exponent bytes start } catch (e: any) { expBytesStart = Number.MAX_SAFE_INTEGER - 32 } - const expLen = bufferToBigInt(data.slice(32, 64)) - let firstExpBytes = Buffer.from(data.slice(expBytesStart, expBytesStart + 32)) // first word of the exponent data + const expLen = bytesToBigInt(data.subarray(32, 64)) + let firstExpBytes = data.subarray(expBytesStart, expBytesStart + 32) // first word of the exponent data firstExpBytes = setLengthRight(firstExpBytes, 32) // reading past the data reads virtual zeros - let firstExpBigInt = bufferToBigInt(firstExpBytes) + let firstExpBigInt = bytesToBigInt(firstExpBytes) let max32expLen = 0 if (expLen < BigInt(32)) { max32expLen = 32 - Number(expLen) @@ -90,9 +91,9 @@ export function precompile05(opts: PrecompileInput): ExecResult { adjustedELen = BigInt(1) } - const bLen = bufferToBigInt(data.slice(0, 32)) - const eLen = bufferToBigInt(data.slice(32, 64)) - const mLen = bufferToBigInt(data.slice(64, 96)) + const bLen = bytesToBigInt(data.subarray(0, 32)) + const eLen = bytesToBigInt(data.subarray(32, 64)) + const mLen = bytesToBigInt(data.subarray(64, 96)) let maxLen = bLen if (maxLen < mLen) { @@ -116,7 +117,7 @@ export function precompile05(opts: PrecompileInput): ExecResult { gasUsed = BigInt(200) } } - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run MODEXP (0x05) precompile data=${short(opts.data)} length=${opts.data.length} gasLimit=${ opts.gasLimit @@ -125,7 +126,7 @@ export function precompile05(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`MODEXP (0x05) failed: OOG`) } return OOGResult(opts.gasLimit) @@ -134,14 +135,14 @@ export function precompile05(opts: PrecompileInput): ExecResult { if (bLen === BigInt(0)) { return { executionGasUsed: gasUsed, - returnValue: setLengthLeft(bigIntToBuffer(BigInt(0)), Number(mLen)), + returnValue: setLengthLeft(bigIntToBytes(BigInt(0)), Number(mLen)), } } if (mLen === BigInt(0)) { return { executionGasUsed: gasUsed, - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), } } @@ -149,18 +150,18 @@ export function precompile05(opts: PrecompileInput): ExecResult { const maxSize = BigInt(2147483647) // @ethereumjs/util setLengthRight limitation if (bLen > maxSize || eLen > maxSize || mLen > maxSize) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`MODEXP (0x05) failed: OOG`) } return OOGResult(opts.gasLimit) } - const B = bufferToBigInt(setLengthRight(data.slice(Number(bStart), Number(bEnd)), Number(bLen))) - const E = bufferToBigInt(setLengthRight(data.slice(Number(eStart), Number(eEnd)), Number(eLen))) - const M = bufferToBigInt(setLengthRight(data.slice(Number(mStart), Number(mEnd)), Number(mLen))) + const B = bytesToBigInt(setLengthRight(data.subarray(Number(bStart), Number(bEnd)), Number(bLen))) + const E = bytesToBigInt(setLengthRight(data.subarray(Number(eStart), Number(eEnd)), Number(eLen))) + const M = bytesToBigInt(setLengthRight(data.subarray(Number(mStart), Number(mEnd)), Number(mLen))) if (mEnd > maxInt) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`MODEXP (0x05) failed: OOG`) } return OOGResult(opts.gasLimit) @@ -173,13 +174,13 @@ export function precompile05(opts: PrecompileInput): ExecResult { R = expmod(B, E, M) } - const res = setLengthLeft(bigIntToBuffer(R), Number(mLen)) - if (opts._debug) { - opts._debug(`MODEXP (0x05) return value=${res.toString('hex')}`) + const res = setLengthLeft(bigIntToBytes(R), Number(mLen)) + if (opts._debug !== undefined) { + opts._debug(`MODEXP (0x05) return value=${bytesToHex(res)}`) } return { executionGasUsed: gasUsed, - returnValue: res, + returnValue: setLengthLeft(bigIntToBytes(R), Number(mLen)), } } diff --git a/packages/evm/src/precompiles/06-ecadd.ts b/packages/evm/src/precompiles/06-ecadd.ts index 5bd67a39910..aee4b5efb19 100644 --- a/packages/evm/src/precompiles/06-ecadd.ts +++ b/packages/evm/src/precompiles/06-ecadd.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { OOGResult } from '../evm' @@ -8,10 +9,10 @@ import type { PrecompileInput } from './types' const bn128 = require('rustbn.js') export function precompile06(opts: PrecompileInput): ExecResult { - const inputData = opts.data.slice(0, 128) + const inputData = bytesToHex(opts.data.subarray(0, 128)) const gasUsed = opts._common.param('gasPrices', 'ecAdd') - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run ECADD (0x06) precompile data=${short(opts.data)} length=${opts.data.length} gasLimit=${ opts.gasLimit @@ -19,24 +20,24 @@ export function precompile06(opts: PrecompileInput): ExecResult { ) } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECADD (0x06) failed: OOG`) } return OOGResult(opts.gasLimit) } - const returnData: Buffer = bn128.add(inputData) + const returnData = hexToBytes(bn128.add(inputData)) // check ecadd success or failure by comparing the output length if (returnData.length !== 64) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECADD (0x06) failed: OOG`) } return OOGResult(opts.gasLimit) } - if (opts._debug) { - opts._debug(`ECADD (0x06) return value=${returnData.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`ECADD (0x06) return value=${bytesToHex(returnData)}`) } return { diff --git a/packages/evm/src/precompiles/07-ecmul.ts b/packages/evm/src/precompiles/07-ecmul.ts index 7670b3bc11b..8591fe3f1fa 100644 --- a/packages/evm/src/precompiles/07-ecmul.ts +++ b/packages/evm/src/precompiles/07-ecmul.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { OOGResult } from '../evm' @@ -8,9 +9,9 @@ import type { PrecompileInput } from './types' const bn128 = require('rustbn.js') export function precompile07(opts: PrecompileInput): ExecResult { - const inputData = opts.data.slice(0, 128) + const inputData = bytesToHex(opts.data.subarray(0, 128)) const gasUsed = opts._common.param('gasPrices', 'ecMul') - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run ECMUL (0x07) precompile data=${short(opts.data)} length=${opts.data.length} gasLimit=${ opts.gasLimit @@ -19,24 +20,25 @@ export function precompile07(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECMUL (0x07) failed: OOG`) } return OOGResult(opts.gasLimit) } - const returnData = bn128.mul(inputData) + const returnData = hexToBytes(bn128.mul(inputData)) + // check ecmul success or failure by comparing the output length if (returnData.length !== 64) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECMUL (0x07) failed: OOG`) } // TODO: should this really return OOG? return OOGResult(opts.gasLimit) } - if (opts._debug) { - opts._debug(`ECMUL (0x07) return value=${returnData.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`ECMUL (0x07) return value=${bytesToHex(returnData)}`) } return { diff --git a/packages/evm/src/precompiles/08-ecpairing.ts b/packages/evm/src/precompiles/08-ecpairing.ts index ac45df8b0fe..9f9d16793f9 100644 --- a/packages/evm/src/precompiles/08-ecpairing.ts +++ b/packages/evm/src/precompiles/08-ecpairing.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { OOGResult } from '../evm' @@ -14,7 +15,7 @@ export function precompile08(opts: PrecompileInput): ExecResult { const gasUsed = opts._common.param('gasPrices', 'ecPairing') + inputDataSize * opts._common.param('gasPrices', 'ecPairingWord') - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run ECPAIRING (0x08) precompile data=${short(opts.data)} length=${ opts.data.length @@ -23,25 +24,25 @@ export function precompile08(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECPAIRING (0x08) failed: OOG`) } return OOGResult(opts.gasLimit) } - const returnData = bn128.pairing(inputData) + const returnData = hexToBytes(bn128.pairing(bytesToHex(inputData))) // check ecpairing success or failure by comparing the output length if (returnData.length !== 32) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`ECPAIRING (0x08) failed: OOG`) } // TODO: should this really return OOG? return OOGResult(opts.gasLimit) } - if (opts._debug) { - opts._debug(`ECPAIRING (0x08) return value=${returnData.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`ECPAIRING (0x08) return value=${bytesToHex(returnData)}`) } return { diff --git a/packages/evm/src/precompiles/09-blake2f.ts b/packages/evm/src/precompiles/09-blake2f.ts index 1dcb4981cb4..95567000896 100644 --- a/packages/evm/src/precompiles/09-blake2f.ts +++ b/packages/evm/src/precompiles/09-blake2f.ts @@ -1,4 +1,4 @@ -import { short } from '@ethereumjs/util' +import { bytesToHex, short } from '@ethereumjs/util' import { OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -160,37 +160,37 @@ export function F(h: Uint32Array, m: Uint32Array, t: Uint32Array, f: boolean, ro export function precompile09(opts: PrecompileInput): ExecResult { const data = opts.data if (data.length !== 213) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLAKE2F (0x09) failed: OUT_OF_RANGE dataLength=${data.length}`) } return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, exceptionError: new EvmError(ERROR.OUT_OF_RANGE), } } - const lastByte = data.slice(212, 213)[0] + const lastByte = data.subarray(212, 213)[0] if (lastByte !== 1 && lastByte !== 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLAKE2F (0x09) failed: OUT_OF_RANGE lastByte=${lastByte}`) } return { - returnValue: Buffer.alloc(0), + returnValue: new Uint8Array(0), executionGasUsed: opts.gasLimit, exceptionError: new EvmError(ERROR.OUT_OF_RANGE), } } - const rounds = data.slice(0, 4).readUInt32BE(0) - const hRaw = data.slice(4, 68) - const mRaw = data.slice(68, 196) - const tRaw = data.slice(196, 212) + const rounds = new DataView(data.subarray(0, 4).buffer).getUint32(0) + const hRaw = new DataView(data.buffer, 4, 64) + const mRaw = new DataView(data.buffer, 68, 128) + const tRaw = new DataView(data.buffer, 196, 16) // final const f = lastByte === 1 let gasUsed = opts._common.param('gasPrices', 'blake2Round') gasUsed *= BigInt(rounds) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLAKE2F (0x09) precompile data=${short(opts.data)} length=${opts.data.length} gasLimit=${ opts.gasLimit @@ -199,7 +199,7 @@ export function precompile09(opts: PrecompileInput): ExecResult { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLAKE2F (0x09) failed: OOG`) } return OOGResult(opts.gasLimit) @@ -207,28 +207,29 @@ export function precompile09(opts: PrecompileInput): ExecResult { const h = new Uint32Array(16) for (let i = 0; i < 16; i++) { - h[i] = hRaw.readUInt32LE(i * 4) + h[i] = hRaw.getUint32(i * 4, true) } const m = new Uint32Array(32) for (let i = 0; i < 32; i++) { - m[i] = mRaw.readUInt32LE(i * 4) + m[i] = mRaw.getUint32(i * 4, true) } const t = new Uint32Array(4) for (let i = 0; i < 4; i++) { - t[i] = tRaw.readUInt32LE(i * 4) + t[i] = tRaw.getUint32(i * 4, true) } F(h, m, t, f, rounds) - const output = Buffer.alloc(64) + const output = new Uint8Array(64) + const outputView = new DataView(output.buffer) for (let i = 0; i < 16; i++) { - output.writeUInt32LE(h[i], i * 4) + outputView.setUint32(i * 4, h[i], true) } - if (opts._debug) { - opts._debug(`BLAKE2F (0x09) return hash=${output.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`BLAKE2F (0x09) return hash=${bytesToHex(output)}`) } return { diff --git a/packages/evm/src/precompiles/0a-bls12-g1add.ts b/packages/evm/src/precompiles/0a-bls12-g1add.ts index aa3853eaba2..edbd32748c9 100644 --- a/packages/evm/src/precompiles/0a-bls12-g1add.ts +++ b/packages/evm/src/precompiles/0a-bls12-g1add.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -15,7 +16,7 @@ export async function precompile0a(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381G1AddGas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12G1ADD (0x0a) precompile data=${short(opts.data)} length=${ opts.data.length @@ -24,21 +25,21 @@ export async function precompile0a(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1ADD (0x0a) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 256) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1ADD (0x0a) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -47,9 +48,9 @@ export async function precompile0a(opts: PrecompileInput): Promise { ] for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice(zeroByteCheck[index][0], zeroByteCheck[index][1]) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + const slicedBuffer = opts.data.subarray(zeroByteCheck[index][0], zeroByteCheck[index][1]) + if (!(equalsBytes(slicedBuffer, zeroBytes16) === true)) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1ADD (0x0a) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -60,10 +61,10 @@ export async function precompile0a(opts: PrecompileInput): Promise { let mclPoint1 let mclPoint2 try { - mclPoint1 = BLS12_381_ToG1Point(opts.data.slice(0, 128), mcl) - mclPoint2 = BLS12_381_ToG1Point(opts.data.slice(128, 256), mcl) + mclPoint1 = BLS12_381_ToG1Point(opts.data.subarray(0, 128), mcl) + mclPoint2 = BLS12_381_ToG1Point(opts.data.subarray(128, 256), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1ADD (0x0a) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) @@ -73,7 +74,7 @@ export async function precompile0a(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG1Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1ADD (0x0a) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/0b-bls12-g1mul.ts b/packages/evm/src/precompiles/0b-bls12-g1mul.ts index 704255835a2..b6253c48dad 100644 --- a/packages/evm/src/precompiles/0b-bls12-g1mul.ts +++ b/packages/evm/src/precompiles/0b-bls12-g1mul.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -19,7 +20,7 @@ export async function precompile0b(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381G1MulGas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12G1MUL (0x0b) precompile data=${short(opts.data)} length=${ opts.data.length @@ -28,30 +29,30 @@ export async function precompile0b(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1MUL (0x0b) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 160) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1MUL (0x0b) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], ] for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice(zeroByteCheck[index][0], zeroByteCheck[index][1]) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + const slicedBuffer = opts.data.subarray(zeroByteCheck[index][0], zeroByteCheck[index][1]) + if (!equalsBytes(slicedBuffer, zeroBytes16)) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1MUL (0x0b) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -62,21 +63,21 @@ export async function precompile0b(opts: PrecompileInput): Promise { let mclPoint try { - mclPoint = BLS12_381_ToG1Point(opts.data.slice(0, 128), mcl) + mclPoint = BLS12_381_ToG1Point(opts.data.subarray(0, 128), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1MUL (0x0b) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) } - const frPoint = BLS12_381_ToFrPoint(opts.data.slice(128, 160), mcl) + const frPoint = BLS12_381_ToFrPoint(opts.data.subarray(128, 160), mcl) const result = mcl.mul(mclPoint, frPoint) const returnValue = BLS12_381_FromG1Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G1MUL (0x0b) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/0c-bls12-g1multiexp.ts b/packages/evm/src/precompiles/0c-bls12-g1multiexp.ts index cee17ba9736..884b23d6a19 100644 --- a/packages/evm/src/precompiles/0c-bls12-g1multiexp.ts +++ b/packages/evm/src/precompiles/0c-bls12-g1multiexp.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -18,7 +19,7 @@ export async function precompile0c(opts: PrecompileInput): Promise { const inputData = opts.data if (inputData.length === 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) failed: Empty input`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) // follow Geths implementation @@ -46,7 +47,7 @@ export async function precompile0c(opts: PrecompileInput): Promise { } const gasUsed = (gasUsedPerPair * BigInt(numPairs) * BigInt(gasDiscountMultiplier)) / BigInt(1000) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12MULTIEXP (0x0c) precompile data=${short(opts.data)} length=${ opts.data.length @@ -55,14 +56,14 @@ export async function precompile0c(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length % 160 !== 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) @@ -70,7 +71,7 @@ export async function precompile0c(opts: PrecompileInput): Promise { // prepare pairing list and check for mandatory zero bytes - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -83,12 +84,12 @@ export async function precompile0c(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 160 * k for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice( + const slicedBuffer = opts.data.subarray( zeroByteCheck[index][0] + pairStart, zeroByteCheck[index][1] + pairStart ) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + if (!(equalsBytes(slicedBuffer, zeroBytes16) === true)) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -96,14 +97,14 @@ export async function precompile0c(opts: PrecompileInput): Promise { } let G1 try { - G1 = BLS12_381_ToG1Point(opts.data.slice(pairStart, pairStart + 128), mcl) + G1 = BLS12_381_ToG1Point(opts.data.subarray(pairStart, pairStart + 128), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) } - const Fr = BLS12_381_ToFrPoint(opts.data.slice(pairStart + 128, pairStart + 160), mcl) + const Fr = BLS12_381_ToFrPoint(opts.data.subarray(pairStart + 128, pairStart + 160), mcl) G1Array.push(G1) FrArray.push(Fr) @@ -113,7 +114,7 @@ export async function precompile0c(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG1Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MULTIEXP (0x0c) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/0d-bls12-g2add.ts b/packages/evm/src/precompiles/0d-bls12-g2add.ts index 963c740c018..3fdb64351ce 100644 --- a/packages/evm/src/precompiles/0d-bls12-g2add.ts +++ b/packages/evm/src/precompiles/0d-bls12-g2add.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -15,7 +16,7 @@ export async function precompile0d(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381G2AddGas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12G2ADD (0x0d) precompile data=${short(opts.data)} length=${ opts.data.length @@ -24,21 +25,21 @@ export async function precompile0d(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2ADD (0x0d) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 512) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2ADD (0x0d) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -51,9 +52,9 @@ export async function precompile0d(opts: PrecompileInput): Promise { ] for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice(zeroByteCheck[index][0], zeroByteCheck[index][1]) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + const slicedBuffer = opts.data.subarray(zeroByteCheck[index][0], zeroByteCheck[index][1]) + if (!(equalsBytes(slicedBuffer, zeroBytes16) === true)) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2ADD (0x0d) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -67,8 +68,8 @@ export async function precompile0d(opts: PrecompileInput): Promise { let mclPoint2 try { - mclPoint1 = BLS12_381_ToG2Point(opts.data.slice(0, 256), mcl) - mclPoint2 = BLS12_381_ToG2Point(opts.data.slice(256, 512), mcl) + mclPoint1 = BLS12_381_ToG2Point(opts.data.subarray(0, 256), mcl) + mclPoint2 = BLS12_381_ToG2Point(opts.data.subarray(256, 512), mcl) } catch (e: any) { return EvmErrorResult(e, opts.gasLimit) } @@ -77,7 +78,7 @@ export async function precompile0d(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG2Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2ADD (0x0d) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/0e-bls12-g2mul.ts b/packages/evm/src/precompiles/0e-bls12-g2mul.ts index ca72e353866..1b25251d658 100644 --- a/packages/evm/src/precompiles/0e-bls12-g2mul.ts +++ b/packages/evm/src/precompiles/0e-bls12-g2mul.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -19,7 +20,7 @@ export async function precompile0e(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381G2MulGas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12G2MUL (0x0e) precompile data=${short(opts.data)} length=${ opts.data.length @@ -28,21 +29,21 @@ export async function precompile0e(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MUL (0x0e) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 288) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MUL (0x0e) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -51,9 +52,9 @@ export async function precompile0e(opts: PrecompileInput): Promise { ] for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice(zeroByteCheck[index][0], zeroByteCheck[index][1]) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + const slicedBuffer = opts.data.subarray(zeroByteCheck[index][0], zeroByteCheck[index][1]) + if (!equalsBytes(slicedBuffer, zeroBytes16)) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MUL (0x0e) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -65,21 +66,21 @@ export async function precompile0e(opts: PrecompileInput): Promise { // convert input to mcl G2 point/Fr point, add them, and convert the output to a Buffer. let mclPoint try { - mclPoint = BLS12_381_ToG2Point(opts.data.slice(0, 256), mcl) + mclPoint = BLS12_381_ToG2Point(opts.data.subarray(0, 256), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MUL (0x0e) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) } - const frPoint = BLS12_381_ToFrPoint(opts.data.slice(256, 288), mcl) + const frPoint = BLS12_381_ToFrPoint(opts.data.subarray(256, 288), mcl) const result = mcl.mul(mclPoint, frPoint) const returnValue = BLS12_381_FromG2Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MUL (0x0e) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/0f-bls12-g2multiexp.ts b/packages/evm/src/precompiles/0f-bls12-g2multiexp.ts index 791c462d6a2..d1f4939f2f5 100644 --- a/packages/evm/src/precompiles/0f-bls12-g2multiexp.ts +++ b/packages/evm/src/precompiles/0f-bls12-g2multiexp.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -20,7 +21,7 @@ export async function precompile0f(opts: PrecompileInput): Promise { const inputData = opts.data if (inputData.length === 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) failed: Empty input`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) // follow Geths implementation @@ -44,7 +45,7 @@ export async function precompile0f(opts: PrecompileInput): Promise { } const gasUsed = (gasUsedPerPair * BigInt(numPairs) * BigInt(gasDiscountMultiplier)) / BigInt(1000) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12G2MULTIEXP (0x0f) precompile data=${short(opts.data)} length=${ opts.data.length @@ -53,14 +54,14 @@ export async function precompile0f(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length % 288 !== 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) @@ -68,7 +69,7 @@ export async function precompile0f(opts: PrecompileInput): Promise { // prepare pairing list and check for mandatory zero bytes - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -83,12 +84,12 @@ export async function precompile0f(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 288 * k for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice( + const slicedBuffer = opts.data.subarray( zeroByteCheck[index][0] + pairStart, zeroByteCheck[index][1] + pairStart ) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + if (!(equalsBytes(slicedBuffer, zeroBytes16) === true)) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -96,14 +97,14 @@ export async function precompile0f(opts: PrecompileInput): Promise { } let G2 try { - G2 = BLS12_381_ToG2Point(opts.data.slice(pairStart, pairStart + 256), mcl) + G2 = BLS12_381_ToG2Point(opts.data.subarray(pairStart, pairStart + 256), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) } - const Fr = BLS12_381_ToFrPoint(opts.data.slice(pairStart + 256, pairStart + 288), mcl) + const Fr = BLS12_381_ToFrPoint(opts.data.subarray(pairStart + 256, pairStart + 288), mcl) G2Array.push(G2) FrArray.push(Fr) @@ -113,7 +114,7 @@ export async function precompile0f(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG2Point(result) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12G2MULTIEXP (0x0f) return value=${returnValue.toString('hex')}`) } diff --git a/packages/evm/src/precompiles/10-bls12-pairing.ts b/packages/evm/src/precompiles/10-bls12-pairing.ts index 606e0f351f3..ac3de1f165d 100644 --- a/packages/evm/src/precompiles/10-bls12-pairing.ts +++ b/packages/evm/src/precompiles/10-bls12-pairing.ts @@ -1,4 +1,5 @@ -import { short } from '@ethereumjs/util' +import { concatBytesNoTypeCheck, short } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -8,8 +9,8 @@ import type { PrecompileInput } from './types' const { BLS12_381_ToG1Point, BLS12_381_ToG2Point } = require('./util/bls12_381') -const zeroBuffer = Buffer.alloc(32, 0) -const oneBuffer = Buffer.concat([Buffer.alloc(31, 0), Buffer.from('01', 'hex')]) +const zeroBuffer = new Uint8Array(32) +const oneBuffer = concatBytesNoTypeCheck(new Uint8Array(31), hexToBytes('01')) export async function precompile10(opts: PrecompileInput): Promise { const mcl = (opts._EVM)._mcl! @@ -19,7 +20,7 @@ export async function precompile10(opts: PrecompileInput): Promise { const baseGas = opts._common.paramByEIP('gasPrices', 'Bls12381PairingBaseGas', 2537) ?? BigInt(0) if (inputData.length === 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: Empty input`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INPUT_EMPTY), opts.gasLimit) @@ -29,7 +30,7 @@ export async function precompile10(opts: PrecompileInput): Promise { opts._common.paramByEIP('gasPrices', 'Bls12381PairingPerPairGas', 2537) ?? BigInt(0) const gasUsed = baseGas + gasUsedPerPair * BigInt(Math.floor(inputData.length / 384)) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12PAIRING (0x10) precompile data=${short(opts.data)} length=${ opts.data.length @@ -38,14 +39,14 @@ export async function precompile10(opts: PrecompileInput): Promise { } if (inputData.length % 384 !== 0) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: OOG`) } return OOGResult(opts.gasLimit) @@ -55,7 +56,7 @@ export async function precompile10(opts: PrecompileInput): Promise { const pairs = [] - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], @@ -69,12 +70,12 @@ export async function precompile10(opts: PrecompileInput): Promise { // zero bytes check const pairStart = 384 * k for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice( + const slicedBuffer = opts.data.subarray( zeroByteCheck[index][0] + pairStart, zeroByteCheck[index][1] + pairStart ) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + if (!equalsBytes(slicedBuffer, zeroBytes16)) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -82,9 +83,9 @@ export async function precompile10(opts: PrecompileInput): Promise { } let G1 try { - G1 = BLS12_381_ToG1Point(opts.data.slice(pairStart, pairStart + 128), mcl) + G1 = BLS12_381_ToG1Point(opts.data.subarray(pairStart, pairStart + 128), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) @@ -93,9 +94,9 @@ export async function precompile10(opts: PrecompileInput): Promise { const g2start = pairStart + 128 let G2 try { - G2 = BLS12_381_ToG2Point(opts.data.slice(g2start, g2start + 256), mcl) + G2 = BLS12_381_ToG2Point(opts.data.subarray(g2start, g2start + 256), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12PAIRING (0x10) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) @@ -131,8 +132,8 @@ export async function precompile10(opts: PrecompileInput): Promise { returnValue = zeroBuffer } - if (opts._debug) { - opts._debug(`BLS12PAIRING (0x10) return value=${returnValue.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`BLS12PAIRING (0x10) return value=${bytesToHex(returnValue)}`) } return { diff --git a/packages/evm/src/precompiles/11-bls12-map-fp-to-g1.ts b/packages/evm/src/precompiles/11-bls12-map-fp-to-g1.ts index f7ca9ec4dfb..721e8b89ade 100644 --- a/packages/evm/src/precompiles/11-bls12-map-fp-to-g1.ts +++ b/packages/evm/src/precompiles/11-bls12-map-fp-to-g1.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -15,7 +16,7 @@ export async function precompile11(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381MapG1Gas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12MAPFPTOG1 (0x11) precompile data=${short(opts.data)} length=${ opts.data.length @@ -24,23 +25,23 @@ export async function precompile11(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFPTOG1 (0x11) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 64) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFPTOG1 (0x11) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) - if (!opts.data.slice(0, 16).equals(zeroBytes16)) { - if (opts._debug) { + const zeroBytes16 = new Uint8Array(16) + if (!equalsBytes(opts.data.subarray(0, 16), zeroBytes16)) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFPTOG1 (0x11) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -50,9 +51,9 @@ export async function precompile11(opts: PrecompileInput): Promise { let Fp1Point try { - Fp1Point = BLS12_381_ToFpPoint(opts.data.slice(0, 64), mcl) + Fp1Point = BLS12_381_ToFpPoint(opts.data.subarray(0, 64), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFPTOG1 (0x11) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) @@ -63,8 +64,8 @@ export async function precompile11(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG1Point(result) - if (opts._debug) { - opts._debug(`BLS12MAPFPTOG1 (0x11) return value=${returnValue.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`BLS12MAPFPTOG1 (0x11) return value=${bytesToHex(returnValue)}`) } return { diff --git a/packages/evm/src/precompiles/12-bls12-map-fp2-to-g2.ts b/packages/evm/src/precompiles/12-bls12-map-fp2-to-g2.ts index 006b7249106..7c573a97d57 100644 --- a/packages/evm/src/precompiles/12-bls12-map-fp2-to-g2.ts +++ b/packages/evm/src/precompiles/12-bls12-map-fp2-to-g2.ts @@ -1,4 +1,5 @@ import { short } from '@ethereumjs/util' +import { bytesToHex, equalsBytes } from 'ethereum-cryptography/utils' import { EvmErrorResult, OOGResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -15,7 +16,7 @@ export async function precompile12(opts: PrecompileInput): Promise { // note: the gas used is constant; even if the input is incorrect. const gasUsed = opts._common.paramByEIP('gasPrices', 'Bls12381MapG2Gas', 2537) ?? BigInt(0) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run BLS12MAPFP2TOG2 (0x12) precompile data=${short(opts.data)} length=${ opts.data.length @@ -24,30 +25,30 @@ export async function precompile12(opts: PrecompileInput): Promise { } if (opts.gasLimit < gasUsed) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFP2TOG2 (0x12) failed: OOG`) } return OOGResult(opts.gasLimit) } if (inputData.length !== 128) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFP2TOG2 (0x12) failed: Invalid input length length=${inputData.length}`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_INVALID_INPUT_LENGTH), opts.gasLimit) } // check if some parts of input are zero bytes. - const zeroBytes16 = Buffer.alloc(16, 0) + const zeroBytes16 = new Uint8Array(16) const zeroByteCheck = [ [0, 16], [64, 80], ] for (const index in zeroByteCheck) { - const slicedBuffer = opts.data.slice(zeroByteCheck[index][0], zeroByteCheck[index][1]) - if (!slicedBuffer.equals(zeroBytes16)) { - if (opts._debug) { + const slicedBuffer = opts.data.subarray(zeroByteCheck[index][0], zeroByteCheck[index][1]) + if (!(equalsBytes(slicedBuffer, zeroBytes16) === true)) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFP2TOG2 (0x12) failed: Point not on curve`) } return EvmErrorResult(new EvmError(ERROR.BLS_12_381_POINT_NOT_ON_CURVE), opts.gasLimit) @@ -58,9 +59,9 @@ export async function precompile12(opts: PrecompileInput): Promise { let Fp2Point try { - Fp2Point = BLS12_381_ToFp2Point(opts.data.slice(0, 64), opts.data.slice(64, 128), mcl) + Fp2Point = BLS12_381_ToFp2Point(opts.data.subarray(0, 64), opts.data.subarray(64, 128), mcl) } catch (e: any) { - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug(`BLS12MAPFP2TOG2 (0x12) failed: ${e.message}`) } return EvmErrorResult(e, opts.gasLimit) @@ -70,8 +71,8 @@ export async function precompile12(opts: PrecompileInput): Promise { const returnValue = BLS12_381_FromG2Point(result) - if (opts._debug) { - opts._debug(`BLS12MAPFP2TOG2 (0x12) return value=${returnValue.toString('hex')}`) + if (opts._debug !== undefined) { + opts._debug(`BLS12MAPFP2TOG2 (0x12) return value=${bytesToHex(returnValue)}`) } return { diff --git a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts index a41fdfc8d06..5db899a67b6 100644 --- a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts +++ b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts @@ -1,5 +1,12 @@ import { computeVersionedHash, kzg } from '@ethereumjs/tx' -import { bigIntToBuffer, bufferToBigInt, bufferToHex, setLengthLeft, short } from '@ethereumjs/util' +import { + bigIntToBytes, + bytesToBigInt, + bytesToHex, + concatBytesNoTypeCheck, + setLengthLeft, + short, +} from '@ethereumjs/util' import { EvmErrorResult } from '../evm' import { ERROR, EvmError } from '../exceptions' @@ -13,7 +20,7 @@ export const BLS_MODULUS = BigInt( export async function precompile14(opts: PrecompileInput): Promise { const gasUsed = opts._common.param('gasPrices', 'kzgPointEvaluationGasPrecompilePrice') - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( `Run KZG_POINT_EVALUATION (0x14) precompile data=${short(opts.data)} length=${ opts.data.length @@ -23,52 +30,50 @@ export async function precompile14(opts: PrecompileInput): Promise { const version = Number(opts._common.paramByEIP('sharding', 'blobCommitmentVersionKzg', 4844)) const fieldElementsPerBlob = opts._common.paramByEIP('sharding', 'fieldElementsPerBlob', 4844)! - const versionedHash = opts.data.slice(0, 32) - const z = opts.data.slice(32, 64) - const y = opts.data.slice(64, 96) - const commitment = opts.data.slice(96, 144) - const kzgProof = opts.data.slice(144, 192) + const versionedHash = opts.data.subarray(0, 32) + const z = opts.data.subarray(32, 64) + const y = opts.data.subarray(64, 96) + const commitment = opts.data.subarray(96, 144) + const kzgProof = opts.data.subarray(144, 192) - if (bufferToBigInt(z) >= BLS_MODULUS || bufferToBigInt(y) >= BLS_MODULUS) { - if (opts._debug) { + if (bytesToBigInt(z) >= BLS_MODULUS || bytesToBigInt(y) >= BLS_MODULUS) { + if (opts._debug !== undefined) { opts._debug(`KZG_POINT_EVALUATION (0x14) failed: POINT_GREATER_THAN_BLS_MODULUS`) } + return EvmErrorResult(new EvmError(ERROR.POINT_GREATER_THAN_BLS_MODULUS), opts.gasLimit) } - if ( - bufferToHex(Buffer.from(computeVersionedHash(commitment, version))) !== - bufferToHex(versionedHash) - ) { - if (opts._debug) { + if (bytesToHex(computeVersionedHash(commitment, version)) !== bytesToHex(versionedHash)) { + if (opts._debug !== undefined) { opts._debug(`KZG_POINT_EVALUATION (0x14) failed: INVALID_COMMITMENT`) } return EvmErrorResult(new EvmError(ERROR.INVALID_COMMITMENT), opts.gasLimit) } - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( - `KZG_POINT_EVALUATION (0x14): proof verification with commitment=${commitment.toString( - 'hex' - )} z=${z.toString('hex')} y=${y.toString('hex')} kzgProof=${kzgProof.toString('hex')}` + `KZG_POINT_EVALUATION (0x14): proof verification with commitment=${bytesToHex( + commitment + )} z=${bytesToHex(z)} y=${bytesToHex(y)} kzgProof=${bytesToHex(kzgProof)}` ) } kzg.verifyKzgProof(commitment, z, y, kzgProof) // Return value - FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values - const fieldElements = setLengthLeft(bigIntToBuffer(fieldElementsPerBlob), 32) - const modulus = setLengthLeft(bigIntToBuffer(BLS_MODULUS), 32) + const fieldElementsBuffer = setLengthLeft(bigIntToBytes(fieldElementsPerBlob), 32) + const modulusBuffer = setLengthLeft(bigIntToBytes(BLS_MODULUS), 32) - if (opts._debug) { + if (opts._debug !== undefined) { opts._debug( - `KZG_POINT_EVALUATION (0x14) return fieldElements=${fieldElements.toString( - 'hex' - )} modulus=${modulus.toString('hex')}` + `KZG_POINT_EVALUATION (0x14) return fieldElements=${bytesToHex( + fieldElementsBuffer + )} modulus=${bytesToHex(modulusBuffer)}` ) } return { executionGasUsed: gasUsed, - returnValue: Buffer.concat([fieldElements, modulus]), + returnValue: concatBytesNoTypeCheck(fieldElementsBuffer, modulusBuffer), } } diff --git a/packages/evm/src/precompiles/index.ts b/packages/evm/src/precompiles/index.ts index 843ed394f65..866a9f7f343 100644 --- a/packages/evm/src/precompiles/index.ts +++ b/packages/evm/src/precompiles/index.ts @@ -1,5 +1,6 @@ import { Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { precompile01 } from './01-ecrecover' import { precompile02 } from './02-sha256' @@ -154,7 +155,7 @@ const precompileAvailability: PrecompileAvailability = { } function getPrecompile(address: Address, common: Common): PrecompileFunc { - const addr = address.buf.toString('hex') + const addr = bytesToHex(address.bytes) if (precompiles[addr] !== undefined) { const availability = precompileAvailability[addr] if ( @@ -188,7 +189,7 @@ function getActivePrecompiles( if (customPrecompiles) { for (const precompile of customPrecompiles) { precompileMap.set( - precompile.address.buf.toString('hex'), + bytesToHex(precompile.address.bytes), 'function' in precompile ? precompile.function : undefined ) } @@ -197,7 +198,8 @@ function getActivePrecompiles( if (precompileMap.has(addressString)) { continue } - const address = new Address(Buffer.from(addressString, 'hex')) + + const address = new Address(hexToBytes(addressString)) const precompileFunc = getPrecompile(address, common) if (precompileFunc !== undefined) { precompileMap.set(addressString, precompileFunc) diff --git a/packages/evm/src/precompiles/types.ts b/packages/evm/src/precompiles/types.ts index 8bd66ec91ec..d9c1c5f2dbf 100644 --- a/packages/evm/src/precompiles/types.ts +++ b/packages/evm/src/precompiles/types.ts @@ -8,7 +8,7 @@ export interface PrecompileFunc { } export interface PrecompileInput { - data: Buffer + data: Uint8Array gasLimit: bigint _common: Common _EVM: EVMInterface diff --git a/packages/evm/src/precompiles/util/bls12_381.ts b/packages/evm/src/precompiles/util/bls12_381.ts index 522ae0536ce..0fe8b79814b 100644 --- a/packages/evm/src/precompiles/util/bls12_381.ts +++ b/packages/evm/src/precompiles/util/bls12_381.ts @@ -1,4 +1,5 @@ -import { bufferToBigInt, padToEven } from '@ethereumjs/util' +import { bytesToBigInt, concatBytesNoTypeCheck, padToEven } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import { ERROR, EvmError } from '../../exceptions' @@ -141,9 +142,9 @@ export const gasDiscountPairs = [ // convert an input Buffer to a mcl G1 point // this does /NOT/ do any input checks. the input Buffer needs to be of length 128 // it does raise an error if the point is not on the curve. -function BLS12_381_ToG1Point(input: Buffer, mcl: any): any { - const p_x = input.slice(16, 64).toString('hex') - const p_y = input.slice(80, 128).toString('hex') +function BLS12_381_ToG1Point(input: Uint8Array, mcl: any): any { + const p_x = bytesToHex(input.subarray(16, 64)) + const p_y = bytesToHex(input.subarray(80, 128)) const ZeroString48Bytes = '0'.repeat(96) if (p_x === p_y && p_x === ZeroString48Bytes) { @@ -178,13 +179,13 @@ function BLS12_381_ToG1Point(input: Buffer, mcl: any): any { // input: a mcl G1 point // output: a 128-byte Buffer -function BLS12_381_FromG1Point(input: any): Buffer { +function BLS12_381_FromG1Point(input: any): Uint8Array { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 " const decoded = decodeStr.match(/"?[0-9a-f]+"?/g) // match above pattern. if (decodeStr === '0') { - return Buffer.alloc(128, 0) + return new Uint8Array(128) } // note: decoded[0] === 1 @@ -193,27 +194,27 @@ function BLS12_381_FromG1Point(input: any): Buffer { // convert to buffers. - const xBuffer = Buffer.concat([Buffer.alloc(64 - xval.length / 2, 0), Buffer.from(xval, 'hex')]) - const yBuffer = Buffer.concat([Buffer.alloc(64 - yval.length / 2, 0), Buffer.from(yval, 'hex')]) + const xBuffer = concatBytesNoTypeCheck(new Uint8Array(64 - xval.length / 2), hexToBytes(xval)) + const yBuffer = concatBytesNoTypeCheck(new Uint8Array(64 - yval.length / 2), hexToBytes(yval)) - return Buffer.concat([xBuffer, yBuffer]) + return concatBytesNoTypeCheck(xBuffer, yBuffer) } // convert an input Buffer to a mcl G2 point // this does /NOT/ do any input checks. the input Buffer needs to be of length 256 -function BLS12_381_ToG2Point(input: Buffer, mcl: any): any { - const p_x_1 = input.slice(0, 64) - const p_x_2 = input.slice(64, 128) - const p_y_1 = input.slice(128, 192) - const p_y_2 = input.slice(192, 256) +function BLS12_381_ToG2Point(input: Uint8Array, mcl: any): any { + const p_x_1 = input.subarray(0, 64) + const p_x_2 = input.subarray(64, 128) + const p_y_1 = input.subarray(128, 192) + const p_y_2 = input.subarray(192, 256) - const ZeroBytes64 = Buffer.alloc(64, 0) + const ZeroBytes64 = new Uint8Array(64) // check if we have to do with a zero point if ( - p_x_1.equals(p_x_2) && - p_x_1.equals(p_y_1) && - p_x_1.equals(p_y_2) && - p_x_1.equals(ZeroBytes64) + equalsBytes(p_x_1, p_x_2) && + equalsBytes(p_x_1, p_y_1) && + equalsBytes(p_x_1, p_y_2) && + equalsBytes(p_x_1, ZeroBytes64) ) { return new mcl.G2() } @@ -251,11 +252,11 @@ function BLS12_381_ToG2Point(input: Buffer, mcl: any): any { // input: a mcl G2 point // output: a 256-byte Buffer -function BLS12_381_FromG2Point(input: any): Buffer { +function BLS12_381_FromG2Point(input: any): Uint8Array { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 " if (decodeStr === '0') { - return Buffer.alloc(256, 0) + return new Uint8Array(256) } const decoded = decodeStr.match(/"?[0-9a-f]+"?/g) // match above pattern. @@ -267,19 +268,19 @@ function BLS12_381_FromG2Point(input: any): Buffer { // convert to buffers. - const xBuffer1 = Buffer.concat([Buffer.alloc(64 - x_1.length / 2, 0), Buffer.from(x_1, 'hex')]) - const xBuffer2 = Buffer.concat([Buffer.alloc(64 - x_2.length / 2, 0), Buffer.from(x_2, 'hex')]) - const yBuffer1 = Buffer.concat([Buffer.alloc(64 - y_1.length / 2, 0), Buffer.from(y_1, 'hex')]) - const yBuffer2 = Buffer.concat([Buffer.alloc(64 - y_2.length / 2, 0), Buffer.from(y_2, 'hex')]) + const xBuffer1 = concatBytesNoTypeCheck(new Uint8Array(64 - x_1.length / 2), hexToBytes(x_1)) + const xBuffer2 = concatBytesNoTypeCheck(new Uint8Array(64 - x_2.length / 2), hexToBytes(x_2)) + const yBuffer1 = concatBytesNoTypeCheck(new Uint8Array(64 - y_1.length / 2), hexToBytes(y_1)) + const yBuffer2 = concatBytesNoTypeCheck(new Uint8Array(64 - y_2.length / 2), hexToBytes(y_2)) - return Buffer.concat([xBuffer1, xBuffer2, yBuffer1, yBuffer2]) + return concatBytesNoTypeCheck(xBuffer1, xBuffer2, yBuffer1, yBuffer2) } // input: a 32-byte hex scalar Buffer // output: a mcl Fr point -function BLS12_381_ToFrPoint(input: Buffer, mcl: any): any { - const mclHex = mcl.fromHexStr(input.toString('hex')) +function BLS12_381_ToFrPoint(input: Uint8Array, mcl: any): any { + const mclHex = mcl.fromHexStr(bytesToHex(input)) const Fr = new mcl.Fr() Fr.setBigEndianMod(mclHex) return Fr @@ -288,15 +289,15 @@ function BLS12_381_ToFrPoint(input: Buffer, mcl: any): any { // input: a 64-byte buffer // output: a mcl Fp point -function BLS12_381_ToFpPoint(fpCoordinate: Buffer, mcl: any): any { +function BLS12_381_ToFpPoint(fpCoordinate: Uint8Array, mcl: any): any { // check if point is in field - if (bufferToBigInt(fpCoordinate) >= fieldModulus) { + if (bytesToBigInt(fpCoordinate) >= fieldModulus) { throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } const fp = new mcl.Fp() - fp.setBigEndianMod(mcl.fromHexStr(fpCoordinate.toString('hex'))) + fp.setBigEndianMod(mcl.fromHexStr(bytesToHex(fpCoordinate))) return fp } @@ -304,12 +305,12 @@ function BLS12_381_ToFpPoint(fpCoordinate: Buffer, mcl: any): any { // input: two 64-byte buffers // output: a mcl Fp2 point -function BLS12_381_ToFp2Point(fpXCoordinate: Buffer, fpYCoordinate: Buffer, mcl: any): any { +function BLS12_381_ToFp2Point(fpXCoordinate: Uint8Array, fpYCoordinate: Uint8Array, mcl: any): any { // check if the coordinates are in the field - if (bufferToBigInt(fpXCoordinate) >= fieldModulus) { + if (bytesToBigInt(fpXCoordinate) >= fieldModulus) { throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } - if (bufferToBigInt(fpYCoordinate) >= fieldModulus) { + if (bytesToBigInt(fpYCoordinate) >= fieldModulus) { throw new EvmError(ERROR.BLS_12_381_FP_NOT_IN_FIELD) } @@ -317,8 +318,8 @@ function BLS12_381_ToFp2Point(fpXCoordinate: Buffer, fpYCoordinate: Buffer, mcl: const fp_y = new mcl.Fp() const fp2 = new mcl.Fp2() - fp_x.setStr(fpXCoordinate.slice(16).toString('hex'), 16) - fp_y.setStr(fpYCoordinate.slice(16).toString('hex'), 16) + fp_x.setStr(bytesToHex(fpXCoordinate.subarray(16)), 16) + fp_y.setStr(bytesToHex(fpYCoordinate.subarray(16)), 16) fp2.set_a(fp_x) fp2.set_b(fp_y) diff --git a/packages/evm/src/transientStorage.ts b/packages/evm/src/transientStorage.ts index 8c641cad92e..4331e8dc587 100644 --- a/packages/evm/src/transientStorage.ts +++ b/packages/evm/src/transientStorage.ts @@ -1,12 +1,14 @@ +import { bytesToHex } from '@ethereumjs/util' + import type { TransientStorageInterface } from './types' import type { Address } from '@ethereumjs/util' -type TransientStorageCurrent = Map> +type TransientStorageCurrent = Map> interface TransientStorageModification { addr: string key: string - prevValue: Buffer + prevValue: Uint8Array } type TransientStorageJournal = TransientStorageModification[] @@ -30,14 +32,14 @@ export class TransientStorage implements TransientStorageInterface { * @param addr the address for which transient storage is accessed * @param key the key of the address to get */ - public get(addr: Address, key: Buffer): Buffer { + public get(addr: Address, key: Uint8Array): Uint8Array { const map = this._storage.get(addr.toString()) if (!map) { - return Buffer.alloc(32) + return new Uint8Array(32) } - const value = map.get(key.toString('hex')) + const value = map.get(bytesToHex(key)) if (!value) { - return Buffer.alloc(32) + return new Uint8Array(32) } return value } @@ -48,7 +50,7 @@ export class TransientStorage implements TransientStorageInterface { * @param key the slot to set for the address * @param value the new value of the transient storage slot to set */ - public put(addr: Address, key: Buffer, value: Buffer) { + public put(addr: Address, key: Uint8Array, value: Uint8Array) { if (key.length !== 32) { throw new Error('Transient storage key must be 32 bytes long') } @@ -63,8 +65,8 @@ export class TransientStorage implements TransientStorageInterface { } const map = this._storage.get(addrString)! - const keyStr = key.toString('hex') - const prevValue = map.get(keyStr) ?? Buffer.alloc(32) + const keyStr = bytesToHex(key) + const prevValue = map.get(keyStr) ?? new Uint8Array(32) this._changeJournal.push({ addr: addrString, @@ -113,7 +115,7 @@ export class TransientStorage implements TransientStorageInterface { for (const [address, map] of this._storage.entries()) { result[address] = {} for (const [key, value] of map.entries()) { - result[address][key] = value.toString('hex') + result[address][key] = bytesToHex(value) } } return result diff --git a/packages/evm/src/types.ts b/packages/evm/src/types.ts index df8310955e7..339e4fc7fa6 100644 --- a/packages/evm/src/types.ts +++ b/packages/evm/src/types.ts @@ -28,8 +28,8 @@ export interface EVMInterface { */ export interface EEIInterface extends EVMStateAccess { getBlockHash(num: bigint): Promise - storageStore(address: Address, key: Buffer, value: Buffer): Promise - storageLoad(address: Address, key: Buffer, original: boolean): Promise + storageStore(address: Address, key: Uint8Array, value: Uint8Array): Promise + storageLoad(address: Address, key: Uint8Array, original: boolean): Promise copy(): EEIInterface } @@ -41,10 +41,10 @@ export interface EEIInterface extends EVMStateAccess { * An implementation of this can be found in the `@ethereumjs/vm` package. */ export interface EVMStateAccess extends StateAccess { - addWarmedAddress(address: Buffer): void - isWarmedAddress(address: Buffer): boolean - addWarmedStorage(address: Buffer, slot: Buffer): void - isWarmedStorage(address: Buffer, slot: Buffer): boolean + addWarmedAddress(address: Uint8Array): void + isWarmedAddress(address: Uint8Array): boolean + addWarmedStorage(address: Uint8Array, slot: Uint8Array): void + isWarmedStorage(address: Uint8Array, slot: Uint8Array): boolean clearWarmedAccounts(): void generateAccessList?(addressesRemoved: Address[], addressesOnlyStorage: Address[]): AccessList clearOriginalStorageCache(): void @@ -101,11 +101,11 @@ export interface EVMRunCallOpts { /** * The data for the call. */ - data?: Buffer + data?: Uint8Array /** * This is for CALLCODE where the code to load is different than the code from the `opts.to` address. */ - code?: Buffer + code?: Uint8Array /** * The call depth. Defaults to `0` */ @@ -121,7 +121,7 @@ export interface EVMRunCallOpts { /** * An optional salt to pass to CREATE2. */ - salt?: Buffer + salt?: Uint8Array /** * Addresses to selfdestruct. Defaults to none. */ @@ -146,7 +146,7 @@ export interface EVMRunCallOpts { /** * Versioned hashes for each blob in a blob transaction */ - versionedHashes?: Buffer[] + versionedHashes?: Uint8Array[] } /** @@ -176,11 +176,11 @@ export interface EVMRunCodeOpts { /** * The EVM code to run. */ - code?: Buffer + code?: Uint8Array /** * The input data. */ - data?: Buffer + data?: Uint8Array /** * The gas limit for the call. */ @@ -212,13 +212,13 @@ export interface EVMRunCodeOpts { /** * Versioned hashes for each blob in a blob transaction */ - versionedHashes?: Buffer[] + versionedHashes?: Uint8Array[] } interface NewContractEvent { address: Address // The deployment code - code: Buffer + code: Uint8Array } export type EVMEvents = { @@ -231,7 +231,7 @@ export type EVMEvents = { /** * Log that the contract emits. */ -export type Log = [address: Buffer, topics: Buffer[], data: Buffer] +export type Log = [address: Uint8Array, topics: Uint8Array[], data: Uint8Array] declare type AccessListItem = { address: PrefixedHexString @@ -264,19 +264,19 @@ interface StateAccess { accountIsEmpty(address: Address): Promise deleteAccount(address: Address): Promise modifyAccountFields(address: Address, accountFields: AccountFields): Promise - putContractCode(address: Address, value: Buffer): Promise - getContractCode(address: Address): Promise - getContractStorage(address: Address, key: Buffer): Promise - putContractStorage(address: Address, key: Buffer, value: Buffer): Promise + putContractCode(address: Address, value: Uint8Array): Promise + getContractCode(address: Address): Promise + getContractStorage(address: Address, key: Uint8Array): Promise + putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise clearContractStorage(address: Address): Promise checkpoint(): Promise commit(): Promise revert(): Promise - getStateRoot(): Promise - setStateRoot(stateRoot: Buffer): Promise - getProof?(address: Address, storageSlots: Buffer[]): Promise + getStateRoot(): Promise + setStateRoot(stateRoot: Uint8Array): Promise + getProof?(address: Address, storageSlots: Uint8Array[]): Promise verifyProof?(proof: Proof): Promise - hasStateRoot(root: Buffer): Promise + hasStateRoot(root: Uint8Array): Promise } export type Block = { @@ -286,15 +286,15 @@ export type Block = { coinbase: Address timestamp: bigint difficulty: bigint - prevRandao: Buffer + prevRandao: Uint8Array gasLimit: bigint baseFeePerGas?: bigint } } export interface TransientStorageInterface { - get(addr: Address, key: Buffer): Buffer - put(addr: Address, key: Buffer, value: Buffer): void + get(addr: Address, key: Uint8Array): Uint8Array + put(addr: Address, key: Uint8Array, value: Uint8Array): void commit(): void checkpoint(): void revert(): void diff --git a/packages/evm/test/asyncEvents.spec.ts b/packages/evm/test/asyncEvents.spec.ts index 2d0735d87b1..d6033e0a5b7 100644 --- a/packages/evm/test/asyncEvents.spec.ts +++ b/packages/evm/test/asyncEvents.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src' @@ -8,7 +9,7 @@ import { getEEI } from './utils' tape('async events', async (t) => { t.plan(2) - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Constantinople }) const eei = await getEEI() const evm = await EVM.create({ common, eei }) @@ -22,7 +23,7 @@ tape('async events', async (t) => { const runCallArgs = { caller, // call address gasLimit: BigInt(0xffffffffff), - data: Buffer.from('600000', 'hex'), + data: hexToBytes('600000'), } await evm.runCall(runCallArgs) }) diff --git a/packages/evm/test/customOpcodes.spec.ts b/packages/evm/test/customOpcodes.spec.ts index 2d4155ba25e..6439c6dac30 100644 --- a/packages/evm/test/customOpcodes.spec.ts +++ b/packages/evm/test/customOpcodes.spec.ts @@ -1,3 +1,4 @@ +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src/evm' @@ -38,7 +39,7 @@ tape('VM: custom opcodes', (t) => { } }) const res = await evm.runCode({ - code: Buffer.from('21', 'hex'), + code: hexToBytes('21'), gasLimit: BigInt(gas), }) st.ok(res.executionGasUsed === totalFee, 'successfully charged correct gas') @@ -53,7 +54,7 @@ tape('VM: custom opcodes', (t) => { }) const gas = BigInt(123456) const res = await evm.runCode({ - code: Buffer.from('20', 'hex'), + code: hexToBytes('20'), gasLimit: BigInt(gas), }) st.ok(res.executionGasUsed === gas, 'successfully deleted opcode') @@ -68,7 +69,7 @@ tape('VM: custom opcodes', (t) => { }) const gas = BigInt(123456) const res = await evm.runCode({ - code: Buffer.from('01', 'hex'), + code: hexToBytes('01'), gasLimit: BigInt(gas), }) st.ok(res.executionGasUsed === gas, 'successfully deleted opcode') @@ -85,10 +86,10 @@ tape('VM: custom opcodes', (t) => { // PUSH 1F // RETURNDATA offset // RETURN // Returns 0x05 const result = await evmDefault.runCode!({ - code: Buffer.from('60046001016000526001601FF3', 'hex'), + code: hexToBytes('60046001016000526001601FF3'), gasLimit: BigInt(gas), }) - st.ok(result.returnValue.equals(Buffer.from('05', 'hex'))) + st.ok(equalsBytes(result.returnValue, hexToBytes('05'))) }) t.test('should override opcodes in the EVM', async (st) => { @@ -99,7 +100,7 @@ tape('VM: custom opcodes', (t) => { }) const gas = 123456 const res = await evm.runCode({ - code: Buffer.from('20', 'hex'), + code: hexToBytes('20'), gasLimit: BigInt(gas), }) st.ok(res.executionGasUsed === totalFee, 'successfully charged correct gas') diff --git a/packages/evm/test/customPrecompiles.spec.ts b/packages/evm/test/customPrecompiles.spec.ts index 5492c12fde8..ba6af03d9e1 100644 --- a/packages/evm/test/customPrecompiles.spec.ts +++ b/packages/evm/test/customPrecompiles.spec.ts @@ -1,4 +1,5 @@ import { Address } from '@ethereumjs/util' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src/evm' @@ -8,10 +9,10 @@ import { getEEI } from './utils' import type { ExecResult } from '../src/evm' import type { PrecompileInput } from '../src/precompiles' -const sender = new Address(Buffer.from('44'.repeat(20), 'hex')) -const newPrecompile = new Address(Buffer.from('ff'.repeat(20), 'hex')) -const shaAddress = new Address(Buffer.from('0000000000000000000000000000000000000002', 'hex')) -const expectedReturn = Buffer.from('1337', 'hex') +const sender = new Address(hexToBytes('44'.repeat(20))) +const newPrecompile = new Address(hexToBytes('ff'.repeat(20))) +const shaAddress = new Address(hexToBytes('0000000000000000000000000000000000000002')) +const expectedReturn = utf8ToBytes('1337') const expectedGas = BigInt(10) function customPrecompile(_input: PrecompileInput): ExecResult { @@ -35,11 +36,12 @@ tape('EVM -> custom precompiles', (t) => { const result = await EVMOverride.runCall({ to: shaAddress, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: utf8ToBytes(''), caller: sender, }) - st.ok(result.execResult.returnValue.equals(expectedReturn), 'return value is correct') - st.ok(result.execResult.executionGasUsed === expectedGas, 'gas used is correct') + + st.deepEquals(result.execResult.returnValue, expectedReturn, 'return value is correct') + st.equals(result.execResult.executionGasUsed, expectedGas, 'gas used is correct') }) t.test('should delete existing precompiles', async (st) => { @@ -54,11 +56,11 @@ tape('EVM -> custom precompiles', (t) => { const result = await EVMOverride.runCall({ to: shaAddress, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: hexToBytes(''), caller: sender, }) - st.ok(result.execResult.returnValue.equals(Buffer.from('')), 'return value is correct') - st.ok(result.execResult.executionGasUsed === BigInt(0), 'gas used is correct') + st.deepEquals(result.execResult.returnValue, utf8ToBytes(''), 'return value is correct') + st.equals(result.execResult.executionGasUsed, BigInt(0), 'gas used is correct') }) t.test('should add precompiles', async (st) => { @@ -74,11 +76,11 @@ tape('EVM -> custom precompiles', (t) => { const result = await EVMOverride.runCall({ to: newPrecompile, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: hexToBytes(''), caller: sender, }) - st.ok(result.execResult.returnValue.equals(expectedReturn), 'return value is correct') - st.ok(result.execResult.executionGasUsed === expectedGas, 'gas used is correct') + st.deepEquals(result.execResult.returnValue, expectedReturn, 'return value is correct') + st.equals(result.execResult.executionGasUsed, expectedGas, 'gas used is correct') }) t.test('should not persist changes to precompiles', async (st) => { @@ -86,7 +88,7 @@ tape('EVM -> custom precompiles', (t) => { const shaResult = await EVMSha.runCall({ to: shaAddress, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: hexToBytes(''), caller: sender, }) const EVMOverride = await EVM.create({ @@ -101,25 +103,27 @@ tape('EVM -> custom precompiles', (t) => { const result = await EVMOverride.runCall({ to: shaAddress, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: hexToBytes(''), caller: sender, }) // sanity: check we have overridden - st.ok(result.execResult.returnValue.equals(expectedReturn), 'return value is correct') - st.ok(result.execResult.executionGasUsed === expectedGas, 'gas used is correct') + st.deepEquals(result.execResult.returnValue, expectedReturn, 'return value is correct') + st.equals(result.execResult.executionGasUsed, expectedGas, 'gas used is correct') EVMSha = await EVM.create({ eei: await getEEI() }) const shaResult2 = await EVMSha.runCall({ to: shaAddress, gasLimit: BigInt(30000), - data: Buffer.from(''), + data: hexToBytes(''), caller: sender, }) - st.ok( - shaResult.execResult.returnValue.equals(shaResult2.execResult.returnValue), + st.deepEquals( + shaResult.execResult.returnValue, + shaResult2.execResult.returnValue, 'restored sha precompile - returndata correct' ) - st.ok( - shaResult.execResult.executionGasUsed === shaResult2.execResult.executionGasUsed, + st.equals( + shaResult.execResult.executionGasUsed, + shaResult2.execResult.executionGasUsed, 'restored sha precompile - gas correct' ) }) diff --git a/packages/evm/test/eips/eip-3860.spec.ts b/packages/evm/test/eips/eip-3860.spec.ts index 5bb80003189..e4a0b43efd8 100644 --- a/packages/evm/test/eips/eip-3860.spec.ts +++ b/packages/evm/test/eips/eip-3860.spec.ts @@ -1,11 +1,12 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, privateToAddress } from '@ethereumjs/util' +import { Address, concatBytesNoTypeCheck, privateToAddress } from '@ethereumjs/util' +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../../src' import { getEEI } from '../utils' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const sender = new Address(privateToAddress(pkey)) tape('EIP 3860 tests', (t) => { @@ -18,7 +19,7 @@ tape('EIP 3860 tests', (t) => { const eei = await getEEI() const evm = await EVM.create({ common, eei }) - const buffer = Buffer.allocUnsafe(1000000).fill(0x60) + const buffer = new Uint8Array(1000000).fill(0x60) // setup the call arguments const runCallArgs = { @@ -27,13 +28,12 @@ tape('EIP 3860 tests', (t) => { // Simple test, PUSH PUSH 0 RETURN // It tries to deploy a contract too large, where the code is all zeros // (since memory which is not allocated/resized to yet is always defaulted to 0) - data: Buffer.concat([ - Buffer.from( - '0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3', - 'hex' + data: concatBytesNoTypeCheck( + hexToBytes( + '0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3' ), - buffer, - ]), + buffer + ), } const result = await evm.runCall(runCallArgs) st.ok( @@ -62,17 +62,13 @@ tape('EIP 3860 tests', (t) => { const contractAccount = await evm.eei.getAccount(contractFactory) await evm.eei.putAccount(contractFactory, contractAccount) await evmWithout3860.eei.putAccount(contractFactory, contractAccount) - const factoryCode = Buffer.from( - '7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a8160006000f05a8203600a55806000556001600155505050', - 'hex' + const factoryCode = hexToBytes( + '7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a8160006000f05a8203600a55806000556001600155505050' ) await evm.eei.putContractCode(contractFactory, factoryCode) await evmWithout3860.eei.putContractCode(contractFactory, factoryCode) - const data = Buffer.from( - '000000000000000000000000000000000000000000000000000000000000c000', - 'hex' - ) + const data = hexToBytes('000000000000000000000000000000000000000000000000000000000000c000') const runCallArgs = { from: caller, to: contractFactory, @@ -108,17 +104,13 @@ tape('EIP 3860 tests', (t) => { const contractAccount = await evm.eei.getAccount(contractFactory) await evm.eei.putAccount(contractFactory, contractAccount) await evmWithout3860.eei.putAccount(contractFactory, contractAccount) - const factoryCode = Buffer.from( - '7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a60008260006000f55a8203600a55806000556001600155505050', - 'hex' + const factoryCode = hexToBytes( + '7f600a80600080396000f3000000000000000000000000000000000000000000006000526000355a60008260006000f55a8203600a55806000556001600155505050' ) await evm.eei.putContractCode(contractFactory, factoryCode) await evmWithout3860.eei.putContractCode(contractFactory, factoryCode) - const data = Buffer.from( - '000000000000000000000000000000000000000000000000000000000000c000', - 'hex' - ) + const data = hexToBytes('000000000000000000000000000000000000000000000000000000000000c000') const runCallArgs = { from: caller, to: contractFactory, @@ -213,11 +205,11 @@ tape('EIP 3860 tests', (t) => { const storageInactive = await evmDisabled.eei.getContractStorage(contractFactory, key0) st.ok( - !storageActive.equals(Buffer.from('')), + !equalsBytes(storageActive, new Uint8Array()), 'created contract with MAX_INITCODE_SIZE + 1 length, allowUnlimitedInitCodeSize=true' ) st.ok( - storageInactive.equals(Buffer.from('')), + equalsBytes(storageInactive, new Uint8Array()), 'did not create contract with MAX_INITCODE_SIZE + 1 length, allowUnlimitedInitCodeSize=false' ) diff --git a/packages/evm/test/eof.spec.ts b/packages/evm/test/eof.spec.ts index 8764f5c057e..8af17a19809 100644 --- a/packages/evm/test/eof.spec.ts +++ b/packages/evm/test/eof.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { getEOFCode } from '../src/eof' @@ -18,12 +19,12 @@ tape('getEOFCode()', (t) => { const invalidEofCode = generateInvalidEOFCode(code) t.equal( - getEOFCode(Buffer.from(validEofCode.slice(2), 'hex')).toString('hex'), + bytesToHex(getEOFCode(hexToBytes(validEofCode.slice(2)))), code, 'returned just code section of EOF container' ) t.equal( - getEOFCode(Buffer.from(invalidEofCode.slice(2), 'hex')).toString('hex'), + bytesToHex(getEOFCode(hexToBytes(invalidEofCode.slice(2)))), invalidEofCode.toLowerCase().slice(2), 'returns entire code string for non EOF code' ) diff --git a/packages/evm/test/memory.spec.ts b/packages/evm/test/memory.spec.ts index 6a0d4934677..746e87d5515 100644 --- a/packages/evm/test/memory.spec.ts +++ b/packages/evm/test/memory.spec.ts @@ -10,7 +10,7 @@ tape('Memory', (t) => { }) t.test('should return zeros from empty memory', (st) => { - st.ok(m.read(0, 3).equals(Buffer.from([0, 0, 0]))) + st.deepEquals(m.read(0, 3), Uint8Array.from([0, 0, 0])) st.end() }) @@ -21,18 +21,18 @@ tape('Memory', (t) => { }) t.test('should return zeros before writing', (st) => { - st.ok(m.read(0, 2).equals(Buffer.from([0, 0]))) + st.deepEquals(m.read(0, 2), Uint8Array.from([0, 0])) st.end() }) t.test('should write value', (st) => { - m.write(29, 3, Buffer.from([1, 2, 3])) - st.ok(m.read(29, 5).equals(Buffer.from([1, 2, 3, 0, 0]))) + m.write(29, 3, Uint8Array.from([1, 2, 3])) + st.deepEquals(m.read(29, 5), Uint8Array.from([1, 2, 3, 0, 0])) st.end() }) t.test('should fail when value len and size are inconsistent', (st) => { - st.throws(() => m.write(0, 5, Buffer.from([8, 8, 8])), /size/) + st.throws(() => m.write(0, 5, Uint8Array.from([8, 8, 8])), /size/) st.end() }) @@ -41,7 +41,7 @@ tape('Memory', (t) => { (st) => { const memory = new Memory() st.equal(memory._store.length, 0, 'memory should start with zero length') - memory.write(0, 1, Buffer.from([1])) + memory.write(0, 1, Uint8Array.from([1])) st.equal(memory._store.length, 8192, 'memory buffer length expanded to 8192 bytes') st.end() diff --git a/packages/evm/test/precompiles/06-ecadd.spec.ts b/packages/evm/test/precompiles/06-ecadd.spec.ts index bb1e56a8d96..113d5db14ca 100644 --- a/packages/evm/test/precompiles/06-ecadd.spec.ts +++ b/packages/evm/test/precompiles/06-ecadd.spec.ts @@ -14,7 +14,7 @@ tape('Precompiles: ECADD', (t) => { const ECADD = getActivePrecompiles(common).get(addressStr)! const result = await ECADD({ - data: Buffer.alloc(0), + data: new Uint8Array(0), gasLimit: BigInt(0xffff), _common: common, _EVM: evm, diff --git a/packages/evm/test/precompiles/07-ecmul.spec.ts b/packages/evm/test/precompiles/07-ecmul.spec.ts index a48fd1f8542..73f57ee4dee 100644 --- a/packages/evm/test/precompiles/07-ecmul.spec.ts +++ b/packages/evm/test/precompiles/07-ecmul.spec.ts @@ -13,7 +13,7 @@ tape('Precompiles: ECMUL', (t) => { const ECMUL = getActivePrecompiles(common).get('0000000000000000000000000000000000000007')! const result = await ECMUL({ - data: Buffer.alloc(0), + data: new Uint8Array(0), gasLimit: BigInt(0xffff), _common: common, _EVM: evm, diff --git a/packages/evm/test/precompiles/08-ecpairing.spec.ts b/packages/evm/test/precompiles/08-ecpairing.spec.ts index 57550d8f84d..95e96c4d0e0 100644 --- a/packages/evm/test/precompiles/08-ecpairing.spec.ts +++ b/packages/evm/test/precompiles/08-ecpairing.spec.ts @@ -1,4 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../../src' @@ -12,11 +13,9 @@ tape('Precompiles: ECPAIRING', (t) => { const evm = await EVM.create({ common, eei }) const addressStr = '0000000000000000000000000000000000000008' const ECPAIRING = getActivePrecompiles(common).get(addressStr)! - const result = await ECPAIRING({ - data: Buffer.from( - '00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa', - 'hex' + data: hexToBytes( + '00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa' ), gasLimit: BigInt(0xffffff), _common: common, diff --git a/packages/evm/test/precompiles/14-pointevaluation.spec.ts b/packages/evm/test/precompiles/14-pointevaluation.spec.ts index 2d658a469be..b6a4ca18632 100644 --- a/packages/evm/test/precompiles/14-pointevaluation.spec.ts +++ b/packages/evm/test/precompiles/14-pointevaluation.spec.ts @@ -1,7 +1,8 @@ import { Common, Hardfork } from '@ethereumjs/common' import { computeVersionedHash, initKZG } from '@ethereumjs/tx' -import { bigIntToBuffer, bufferToBigInt, unpadBuffer } from '@ethereumjs/util' +import { bigIntToBytes, bytesToBigInt, concatBytesNoTypeCheck, unpadBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM, getActivePrecompiles } from '../../src' @@ -24,33 +25,25 @@ tape('Precompiles: point evaluation', async (t) => { const pointEvaluation = getActivePrecompiles(common).get(addressStr)! const testCase = { - Proof: Buffer.from( - '8ad6f539bc7280de6af4c95e7cef39bb6873f18c46ee5eb67299324ee7c6e6da71be2dbd5e2cbafbae4b2d60b40a808c', - 'hex' + Proof: hexToBytes( + '8ad6f539bc7280de6af4c95e7cef39bb6873f18c46ee5eb67299324ee7c6e6da71be2dbd5e2cbafbae4b2d60b40a808c' ), - Commitment: Buffer.from( - 'abb6bcbe313530ce7779abdf633d5a3594a41fbad9a79f4a9b46b89c0cfe78f6a15948dec92c4404aedac8b5e7dd6059', - 'hex' - ), - InputPoint: Buffer.from( - '0120000000000000000000000000000000000000000000000000000000000000', - 'hex' - ), - ClaimedValue: Buffer.from( - '48cdd065593bd932707001e88674108ade9dd71d2e849e9a55fa71b70f06690f', - 'hex' + Commitment: hexToBytes( + 'abb6bcbe313530ce7779abdf633d5a3594a41fbad9a79f4a9b46b89c0cfe78f6a15948dec92c4404aedac8b5e7dd6059' ), + InputPoint: hexToBytes('0120000000000000000000000000000000000000000000000000000000000000'), + ClaimedValue: hexToBytes('48cdd065593bd932707001e88674108ade9dd71d2e849e9a55fa71b70f06690f'), } const versionedHash = computeVersionedHash(testCase.Commitment, 1) const opts: PrecompileInput = { - data: Buffer.concat([ + data: concatBytesNoTypeCheck( versionedHash, testCase.InputPoint, testCase.ClaimedValue, testCase.Commitment, - testCase.Proof, - ]), + testCase.Proof + ), gasLimit: 0xfffffffffn, _EVM: evm, _common: common, @@ -58,19 +51,19 @@ tape('Precompiles: point evaluation', async (t) => { let res = await pointEvaluation(opts) t.equal( - bufferToBigInt(unpadBuffer(res.returnValue.slice(32))), + bytesToBigInt(unpadBytes(res.returnValue.slice(32))), BLS_MODULUS, 'point evaluation precompile returned expected output' ) const optsWithBigNumbers: PrecompileInput = { - data: Buffer.concat([ + data: concatBytesNoTypeCheck( versionedHash, testCase.InputPoint, - bigIntToBuffer(BLS_MODULUS + 5n), + bigIntToBytes(BLS_MODULUS + 5n), testCase.Commitment, - testCase.Proof, - ]), + testCase.Proof + ), gasLimit: 0xfffffffffn, _EVM: evm, _common: common, @@ -84,13 +77,13 @@ tape('Precompiles: point evaluation', async (t) => { ) const optsWithInvalidCommitment: PrecompileInput = { - data: Buffer.concat([ - Buffer.concat([Uint8Array.from([0]), versionedHash.slice(1)]), + data: concatBytesNoTypeCheck( + concatBytesNoTypeCheck(Uint8Array.from([0]), versionedHash.slice(1)), testCase.InputPoint, testCase.ClaimedValue, testCase.Commitment, - testCase.Proof, - ]), + testCase.Proof + ), gasLimit: 0xfffffffffn, _EVM: evm, _common: common, diff --git a/packages/evm/test/precompiles/eip-2537-BLS.spec.ts b/packages/evm/test/precompiles/eip-2537-BLS.spec.ts index 9c4da676d50..dc8b3c50301 100644 --- a/packages/evm/test/precompiles/eip-2537-BLS.spec.ts +++ b/packages/evm/test/precompiles/eip-2537-BLS.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, bufferToHex } from '@ethereumjs/util' +import { Address, bytesToPrefixedHexString } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { isRunningInKarma } from '../../../vm/test/util' @@ -27,13 +28,13 @@ tape('EIP-2537 BLS tests', (t) => { const evm = await EVM.create({ common, eei }) for (const address of precompiles) { - const to = new Address(Buffer.from(address, 'hex')) + const to = new Address(hexToBytes(address)) const result = await evm.runCall({ caller: Address.zero(), gasLimit: BigInt(0xffffffffff), to, value: BigInt(0), - data: Buffer.alloc(0), + data: new Uint8Array(0), }) if (result.execResult.executionGasUsed !== BigInt(0)) { @@ -59,13 +60,13 @@ tape('EIP-2537 BLS tests', (t) => { const evm = await EVM.create({ common, eei }) for (const address of precompiles) { - const to = new Address(Buffer.from(address, 'hex')) + const to = new Address(hexToBytes(address)) const result = await evm.runCall({ caller: Address.zero(), gasLimit: BigInt(0xffffffffff), to, value: BigInt(0), - data: Buffer.alloc(0), + data: new Uint8Array(0), }) if (result.execResult.executionGasUsed !== BigInt(0xffffffffff)) { @@ -106,7 +107,7 @@ tape('EIP-2537 BLS tests', (t) => { '0x00000000000000000000000000000000083ad744b34f6393bc983222b004657494232c5d9fbc978d76e2377a28a34c4528da5d91cbc0977dc953397a6d21eca20000000000000000000000000000000015aec6526e151cf5b8403353517dfb9a162087a698b71f32b266d3c5c936a83975d5567c25b3a5994042ec1379c8e526000000000000000000000000000000000e3647185d1a20efad19f975729908840dc33909a583600f7915025f906aef9c022fd34e618170b11178aaa824ae36b300000000000000000000000000000000159576d1d53f6cd12c39d651697e11798321f17cd287118d7ebeabf68281bc03109ee103ee8ef2ef93c71dd1dcbaf1e0' const result = await BLS12G2MultiExp({ - data: Buffer.from(testVector, 'hex'), + data: hexToBytes(testVector), gasLimit: BigInt(5000000), _common: common, _EVM: evm, @@ -114,7 +115,7 @@ tape('EIP-2537 BLS tests', (t) => { st.deepEqual( testVectorResult, - bufferToHex(result.returnValue), + bytesToPrefixedHexString(result.returnValue), 'return value should match testVectorResult' ) st.end() diff --git a/packages/evm/test/precompiles/hardfork.spec.ts b/packages/evm/test/precompiles/hardfork.spec.ts index 42e1a3ab3cd..0ec4ce680ae 100644 --- a/packages/evm/test/precompiles/hardfork.spec.ts +++ b/packages/evm/test/precompiles/hardfork.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../../src' @@ -9,7 +10,7 @@ import { getEEI } from '../utils' tape('Precompiles: hardfork availability', (t) => { t.test('Test ECPAIRING availability', async (st) => { const ECPAIR_AddressStr = '0000000000000000000000000000000000000008' - const ECPAIR_Address = new Address(Buffer.from(ECPAIR_AddressStr, 'hex')) + const ECPAIR_Address = new Address(hexToBytes(ECPAIR_AddressStr)) // ECPAIR was introduced in Byzantium; check if available from Byzantium. const commonByzantium = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium }) diff --git a/packages/evm/test/runCall.spec.ts b/packages/evm/test/runCall.spec.ts index 320f6f6754c..1f5c75d046f 100644 --- a/packages/evm/test/runCall.spec.ts +++ b/packages/evm/test/runCall.spec.ts @@ -1,6 +1,14 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Account, Address, MAX_UINT64, padToEven, unpadBuffer } from '@ethereumjs/util' +import { + Account, + Address, + MAX_UINT64, + concatBytesNoTypeCheck, + padToEven, + unpadBytes, +} from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src' @@ -10,11 +18,11 @@ import { getEEI } from './utils' import type { EVMRunCallOpts } from '../src/types' -// Non-protected Create2Address generator. Does not check if buffers have the right padding. -function create2address(sourceAddress: Address, codeHash: Buffer, salt: Buffer): Address { - const rlp_proc_buffer = Buffer.from('ff', 'hex') - const hashBuffer = Buffer.concat([rlp_proc_buffer, sourceAddress.buf, salt, codeHash]) - return new Address(Buffer.from(keccak256(hashBuffer)).slice(12)) +// Non-protected Create2Address generator. Does not check if Uint8Arrays have the right padding. +function create2address(sourceAddress: Address, codeHash: Uint8Array, salt: Uint8Array): Address { + const rlp_proc_bytes = hexToBytes('ff') + const hashBytes = concatBytesNoTypeCheck(rlp_proc_bytes, sourceAddress.bytes, salt, codeHash) + return new Address(keccak256(hashBytes).slice(12)) } tape('Create where FROM account nonce is 0', async (t) => { @@ -40,10 +48,8 @@ tape('Create where FROM account nonce is 0', async (t) => { tape('Constantinople: EIP-1014 CREATE2 creates the right contract address', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const contractAddress = new Address( - Buffer.from('00000000000000000000000000000000000000ff', 'hex') - ) // contract address + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const contractAddress = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // contract address // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Constantinople }) const eei = await getEEI() @@ -63,9 +69,9 @@ tape('Constantinople: EIP-1014 CREATE2 creates the right contract address', asyn RETURN [0x00, 0x20] */ - await eei.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await eei.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code await eei.putAccount(caller, new Account(BigInt(0), BigInt(0x11111111))) // give the calling account a big balance so we don't run out of funds - const codeHash = Buffer.from(keccak256(Buffer.from(''))) + const codeHash = keccak256(hexToBytes('')) for (let value = 0; value <= 1000; value += 20) { // setup the call arguments const runCallArgs = { @@ -76,14 +82,14 @@ tape('Constantinople: EIP-1014 CREATE2 creates the right contract address', asyn } const hexString = padToEven(value.toString(16)) - let valueBuffer = Buffer.from(hexString, 'hex') - // pad buffer - if (valueBuffer.length < 32) { - const diff = 32 - valueBuffer.length - valueBuffer = Buffer.concat([Buffer.alloc(diff), valueBuffer]) + let valueBytes = hexToBytes(hexString) + // pad bytes + if (valueBytes.length < 32) { + const diff = 32 - valueBytes.length + valueBytes = concatBytesNoTypeCheck(new Uint8Array(diff), valueBytes) } // calculate expected CREATE2 address - const expectedAddress = create2address(contractAddress, codeHash, valueBuffer) + const expectedAddress = create2address(contractAddress, codeHash, valueBytes) // run the actual call const res = await evm.runCall(runCallArgs) // retrieve the return value and convert it to an address (remove the first 12 bytes from the 32-byte return value) @@ -101,10 +107,8 @@ tape('Constantinople: EIP-1014 CREATE2 creates the right contract address', asyn tape('Byzantium cannot access Constantinople opcodes', async (t) => { t.plan(2) // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const contractAddress = new Address( - Buffer.from('00000000000000000000000000000000000000ff', 'hex') - ) // contract address + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const contractAddress = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // contract address // setup the evm const eeiByzantium = await getEEI() const eeiConstantinople = await getEEI() @@ -125,8 +129,8 @@ tape('Byzantium cannot access Constantinople opcodes', async (t) => { STOP */ - await eeiByzantium.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code - await eeiConstantinople.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await eeiByzantium.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code + await eeiConstantinople.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code const runCallArgs = { caller, // call address @@ -152,15 +156,15 @@ tape('Byzantium cannot access Constantinople opcodes', async (t) => { tape('Ensure that Istanbul sstoreCleanRefundEIP2200 gas is applied correctly', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const eei = await getEEI() const evm = await EVM.create({ common, eei }) const code = '61000260005561000160005500' /* - idea: store the original value in the storage slot, except it is now a 1-length buffer instead of a 32-length buffer + idea: store the original value in the storage slot, except it is now a 1-length Uint8Array instead of a 32-length Uint8Array code: PUSH2 0x0002 PUSH1 0x00 @@ -180,12 +184,8 @@ tape('Ensure that Istanbul sstoreCleanRefundEIP2200 gas is applied correctly', a */ - await eei.putContractCode(address, Buffer.from(code, 'hex')) - await eei.putContractStorage( - address, - Buffer.alloc(32, 0), - Buffer.from('00'.repeat(31) + '01', 'hex') - ) + await eei.putContractCode(address, hexToBytes(code)) + await eei.putContractStorage(address, new Uint8Array(32), hexToBytes('00'.repeat(31) + '01')) // setup the call arguments const runCallArgs = { @@ -204,8 +204,8 @@ tape('Ensure that Istanbul sstoreCleanRefundEIP2200 gas is applied correctly', a tape('ensure correct gas for pre-constantinople sstore', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const eei = await getEEI() @@ -213,7 +213,7 @@ tape('ensure correct gas for pre-constantinople sstore', async (t) => { // push 1 push 0 sstore stop const code = '600160015500' - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) // setup the call arguments const runCallArgs = { @@ -232,8 +232,8 @@ tape('ensure correct gas for pre-constantinople sstore', async (t) => { tape('ensure correct gas for calling non-existent accounts in homestead', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Homestead }) const eei = await getEEI() @@ -241,7 +241,7 @@ tape('ensure correct gas for calling non-existent accounts in homestead', async // code to call 0x00..00dd, which does not exist const code = '6000600060006000600060DD61FFFF5A03F100' - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) // setup the call arguments const runCallArgs = { @@ -264,8 +264,8 @@ tape( 'ensure callcode goes OOG if the gas argument is more than the gas left in the homestead fork', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Homestead }) const eei = await getEEI() @@ -274,7 +274,7 @@ tape( // but using too much memory const code = '61FFFF60FF60006000600060EE6000F200' - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) // setup the call arguments const runCallArgs = { @@ -295,8 +295,8 @@ tape( tape('ensure selfdestruct pays for creating new accounts', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.TangerineWhistle }) const eei = await getEEI() @@ -306,7 +306,7 @@ tape('ensure selfdestruct pays for creating new accounts', async (t) => { // this should thus go OOG const code = '60FEFF' - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) // setup the call arguments const runCallArgs = { @@ -326,8 +326,8 @@ tape('ensure selfdestruct pays for creating new accounts', async (t) => { tape('ensure that sstores pay for the right gas costs pre-byzantium', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Chainstart }) const eei = await getEEI() @@ -337,7 +337,7 @@ tape('ensure that sstores pay for the right gas costs pre-byzantium', async (t) // this should thus go OOG const code = '3460005500' - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) const account = await eei.getAccount(caller) account.balance = BigInt(100) @@ -395,10 +395,10 @@ tape( 'Ensure that contracts cannot exceed nonce of MAX_UINT64 when creating new contracts (EIP-2681)', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) - const slot = Buffer.from('00'.repeat(32), 'hex') - const emptyBuffer = Buffer.from('') + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) + const slot = hexToBytes('00'.repeat(32)) + const emptyBytes = hexToBytes('') // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const eei = await getEEI() @@ -416,7 +416,7 @@ tape( STOP */ - await eei.putContractCode(address, Buffer.from(code, 'hex')) + await eei.putContractCode(address, hexToBytes(code)) const account = await eei.getAccount(address) account.nonce = MAX_UINT64 - BigInt(1) @@ -433,14 +433,15 @@ tape( let storage = await eei.getContractStorage(address, slot) // The nonce is MAX_UINT64 - 1, so we are allowed to create a contract (nonce of creating contract is now MAX_UINT64) - t.ok(!storage.equals(emptyBuffer), 'successfully created contract') + t.notDeepEqual(storage, emptyBytes, 'successfully created contract') await evm.runCall(runCallArgs) // The nonce is MAX_UINT64, so we are NOT allowed to create a contract (nonce of creating contract is now MAX_UINT64) storage = await eei.getContractStorage(address, slot) - t.ok( - storage.equals(emptyBuffer), + t.deepEquals( + storage, + emptyBytes, 'failed to create contract; nonce of creating contract is too high (MAX_UINT64)' ) @@ -453,7 +454,7 @@ tape('Ensure that IDENTITY precompile copies the memory', async (t) => { // Exploit post-mortem: https://github.com/ethereum/go-ethereum/blob/master/docs/postmortems/2021-08-22-split-postmortem.md // Permalink: https://github.com/ethereum/go-ethereum/blob/90987db7334c1d10eb866ca550efedb66dea8a20/docs/postmortems/2021-08-22-split-postmortem.md // setup the accounts for this test - const caller = new Address(Buffer.from('1a02a619e51cc5f8a2a61d2a60f6c80476ee8ead', 'hex')) // caller address + const caller = new Address(hexToBytes('1a02a619e51cc5f8a2a61d2a60f6c80476ee8ead')) // caller address // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) const eei = await getEEI() @@ -469,7 +470,7 @@ tape('Ensure that IDENTITY precompile copies the memory', async (t) => { const runCallArgs = { caller, // call address gasLimit: BigInt(150000), - data: Buffer.from(code, 'hex'), + data: hexToBytes(code), gasPrice: BigInt(70000000000), } @@ -480,7 +481,7 @@ tape('Ensure that IDENTITY precompile copies the memory', async (t) => { t.equals(result.createdAddress?.toString(), expectedAddress, 'created address correct') const deployedCode = await eei.getContractCode(result.createdAddress!) - t.equals(deployedCode.toString('hex'), expectedCode, 'deployed code correct') + t.equals(bytesToHex(deployedCode), expectedCode, 'deployed code correct') t.end() }) @@ -514,13 +515,10 @@ tape('runCall() -> skipBalance behavior', async (t) => { const evm = await EVM.create({ common, eei }) // runCall against a contract to reach `_reduceSenderBalance` - const contractCode = Buffer.from('00', 'hex') // 00: STOP + const contractCode = hexToBytes('00') // 00: STOP const contractAddress = Address.fromString('0x000000000000000000000000636F6E7472616374') await eei.putContractCode(contractAddress, contractCode) - const senderKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' - ) + const senderKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const sender = Address.fromPrivateKey(senderKey) const runCallArgs = { @@ -554,7 +552,7 @@ tape('runCall() -> skipBalance behavior', async (t) => { tape('runCall() => allows to detect for max code size deposit errors', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address // setup the evm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const eei = await getEEI() @@ -567,7 +565,7 @@ tape('runCall() => allows to detect for max code size deposit errors', async (t) // Simple test, PUSH PUSH 0 RETURN // It tries to deploy a contract too large, where the code is all zeros // (since memory which is not allocated/resized to yet is always defaulted to 0) - data: Buffer.from('62FFFFFF6000F3', 'hex'), + data: hexToBytes('62FFFFFF6000F3'), } const result = await evm.runCall(runCallArgs) @@ -591,12 +589,12 @@ tape('runCall() => use DATAHASH opcode from EIP 4844', async (t) => { const runCallArgs: EVMRunCallOpts = { gasLimit: BigInt(0xffffffffff), // calldata -- retrieves the versioned hash at index 0 and returns it from memory - data: Buffer.from('60004960005260206000F3', 'hex'), - versionedHashes: [Buffer.from('ab', 'hex')], + data: hexToBytes('60004960005260206000F3'), + versionedHashes: [hexToBytes('ab')], } const res = await evm.runCall(runCallArgs) t.equal( - unpadBuffer(res.execResult.returnValue).toString('hex'), + bytesToHex(unpadBytes(res.execResult.returnValue)), 'ab', 'retrieved correct versionedHash from runState' ) @@ -605,12 +603,12 @@ tape('runCall() => use DATAHASH opcode from EIP 4844', async (t) => { const runCall2Args: EVMRunCallOpts = { gasLimit: BigInt(0xffffffffff), // calldata -- tries to retrieve the versioned hash at index 1 and return it from memory - data: Buffer.from('60014960005260206000F3', 'hex'), - versionedHashes: [Buffer.from('ab', 'hex')], + data: hexToBytes('60014960005260206000F3'), + versionedHashes: [hexToBytes('ab')], } const res2 = await evm.runCall(runCall2Args) t.equal( - unpadBuffer(res2.execResult.returnValue).toString('hex'), + bytesToHex(unpadBytes(res2.execResult.returnValue)), '', 'retrieved no versionedHash when specified versionedHash does not exist in runState' ) diff --git a/packages/evm/test/runCode.spec.ts b/packages/evm/test/runCode.spec.ts index 5e195545f05..93502fe1fdd 100644 --- a/packages/evm/test/runCode.spec.ts +++ b/packages/evm/test/runCode.spec.ts @@ -1,3 +1,4 @@ +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src' @@ -26,7 +27,7 @@ tape('VM.runCode: initial program counter', async (t) => { for (const [i, testData] of testCases.entries()) { const runCodeArgs = { - code: Buffer.from(testData.code.join(''), 'hex'), + code: hexToBytes(testData.code.join('')), pc: testData.pc, gasLimit: BigInt(0xffff), } @@ -62,7 +63,7 @@ tape('VM.runCode: interpreter', (t) => { const INVALID_opcode = 'fe' const runCodeArgs = { - code: Buffer.from(INVALID_opcode, 'hex'), + code: hexToBytes(INVALID_opcode), gasLimit: BigInt(0xffff), } @@ -86,7 +87,7 @@ tape('VM.runCode: interpreter', (t) => { const SSTORE = '55' const runCodeArgs = { - code: Buffer.from([PUSH1, '01', PUSH1, '05', SSTORE].join(''), 'hex'), + code: hexToBytes([PUSH1, '01', PUSH1, '05', SSTORE].join('')), gasLimit: BigInt(0xffff), } diff --git a/packages/evm/test/stack.spec.ts b/packages/evm/test/stack.spec.ts index 580f6fb36b1..4ebc724bf3a 100644 --- a/packages/evm/test/stack.spec.ts +++ b/packages/evm/test/stack.spec.ts @@ -1,4 +1,5 @@ -import { Account, Address, bigIntToBuffer, setLengthLeft } from '@ethereumjs/util' +import { Account, Address, bigIntToBytes, setLengthLeft } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { EVM } from '../src' @@ -126,13 +127,13 @@ tape('Stack', (t) => { }) t.test('stack items should not change if they are DUPed', async (st) => { - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) - const addr = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) + const addr = new Address(hexToBytes('00000000000000000000000000000000000000ff')) const eei = await getEEI() const evm = await EVM.create({ eei }) const account = createAccount(BigInt(0), BigInt(0)) const code = '60008080808060013382F15060005260206000F3' - const expectedReturnValue = setLengthLeft(bigIntToBuffer(BigInt(0)), 32) + const expectedReturnValue = setLengthLeft(bigIntToBytes(BigInt(0)), 32) /* code: remarks: (top of the stack is at the zero index) PUSH1 0x00 @@ -152,7 +153,7 @@ tape('Stack', (t) => { RETURN stack: [0, 0x20] (we thus return the stack item which was originally pushed as 0, and then DUPed) */ await eei.putAccount(addr, account) - await eei.putContractCode(addr, Buffer.from(code, 'hex')) + await eei.putContractCode(addr, hexToBytes(code)) await eei.putAccount(caller, new Account(BigInt(0), BigInt(0x11))) const runCallArgs = { caller, @@ -163,7 +164,7 @@ tape('Stack', (t) => { try { const res = await evm.runCall(runCallArgs) const executionReturnValue = res.execResult.returnValue - st.assert(executionReturnValue.equals(expectedReturnValue)) + st.deepEquals(executionReturnValue, expectedReturnValue) st.end() } catch (e: any) { st.fail(e.message) diff --git a/packages/evm/test/transientStorage.spec.ts b/packages/evm/test/transientStorage.spec.ts index 2d49cb86398..60e390c2599 100644 --- a/packages/evm/test/transientStorage.spec.ts +++ b/packages/evm/test/transientStorage.spec.ts @@ -9,8 +9,8 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value = Buffer.alloc(32, 0x99) + const key = new Uint8Array(32).fill(0xff) + const value = new Uint8Array(32).fill(0x99) transientStorage.put(address, key, value) const got = transientStorage.get(address, key) @@ -22,17 +22,17 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value = Buffer.alloc(32, 0x11) + const key = new Uint8Array(32).fill(0xff) + const value = new Uint8Array(32).fill(0x11) // No address set const got = transientStorage.get(address, key) - t.deepEqual(Buffer.alloc(32, 0x00), got) + t.deepEqual(new Uint8Array(32).fill(0x00), got) // Address set, no key set transientStorage.put(address, key, value) - const got2 = transientStorage.get(address, Buffer.alloc(32, 0x22)) - t.deepEqual(Buffer.alloc(32, 0x00), got2) + const got2 = transientStorage.get(address, new Uint8Array(32).fill(0x22)) + t.deepEqual(new Uint8Array(32).fill(0x00), got2) t.end() }) @@ -40,14 +40,14 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value = Buffer.alloc(32, 0x99) + const key = new Uint8Array(32).fill(0xff) + const value = new Uint8Array(32).fill(0x99) transientStorage.put(address, key, value) transientStorage.checkpoint() - const value2 = Buffer.alloc(32, 0x22) + const value2 = new Uint8Array(32).fill(0x22) transientStorage.put(address, key, value2) const got = transientStorage.get(address, key) t.deepEqual(got, value2) @@ -63,8 +63,8 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value = Buffer.alloc(32, 0x99) + const key = new Uint8Array(32).fill(0xff) + const value = new Uint8Array(32).fill(0x99) transientStorage.put(address, key, value) @@ -82,11 +82,11 @@ tape('Transient Storage', (tester) => { const address = Address.fromString('0xff00000000000000000000000000000000000002') t.throws(() => { - transientStorage.put(address, Buffer.alloc(10), Buffer.alloc(1)) + transientStorage.put(address, new Uint8Array(10), new Uint8Array(1)) }, /Transient storage key must be 32 bytes long/) t.throws(() => { - transientStorage.put(address, Buffer.alloc(32), Buffer.alloc(33)) + transientStorage.put(address, new Uint8Array(32), new Uint8Array(33)) }, /Transient storage value cannot be longer than 32 bytes/) t.end() @@ -96,14 +96,14 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value = Buffer.alloc(32, 0x99) + const key = new Uint8Array(32).fill(0xff) + const value = new Uint8Array(32).fill(0x99) transientStorage.put(address, key, value) t.deepEqual( transientStorage.get( Address.fromString('0xff00000000000000000000000000000000000002'), - Buffer.alloc(32, 0xff) + new Uint8Array(32).fill(0xff) ), value ) @@ -114,10 +114,10 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value1 = Buffer.alloc(32, 0x01) - const value2 = Buffer.alloc(32, 0x02) - const value3 = Buffer.alloc(32, 0x03) + const key = new Uint8Array(32).fill(0xff) + const value1 = new Uint8Array(32).fill(0x01) + const value2 = new Uint8Array(32).fill(0x02) + const value3 = new Uint8Array(32).fill(0x03) transientStorage.put(address, key, value1) transientStorage.checkpoint() @@ -133,11 +133,11 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value0 = Buffer.alloc(32, 0x00) - const value1 = Buffer.alloc(32, 0x01) - const value2 = Buffer.alloc(32, 0x02) - const value3 = Buffer.alloc(32, 0x03) + const key = new Uint8Array(32).fill(0xff) + const value0 = new Uint8Array(32).fill(0x00) + const value1 = new Uint8Array(32).fill(0x01) + const value2 = new Uint8Array(32).fill(0x02) + const value3 = new Uint8Array(32).fill(0x03) transientStorage.put(address, key, value1) transientStorage.checkpoint() @@ -165,10 +165,10 @@ tape('Transient Storage', (tester) => { const transientStorage = new TransientStorage() const address = Address.fromString('0xff00000000000000000000000000000000000002') - const key = Buffer.alloc(32, 0xff) - const value1 = Buffer.alloc(32, 0x01) - const value2 = Buffer.alloc(32, 0x02) - const value3 = Buffer.alloc(32, 0x03) + const key = new Uint8Array(32).fill(0xff) + const value1 = new Uint8Array(32).fill(0x01) + const value2 = new Uint8Array(32).fill(0x02) + const value3 = new Uint8Array(32).fill(0x03) transientStorage.put(address, key, value1) transientStorage.checkpoint() diff --git a/packages/statemanager/src/baseStateManager.ts b/packages/statemanager/src/baseStateManager.ts index 8bc471fcaba..778be20e1c5 100644 --- a/packages/statemanager/src/baseStateManager.ts +++ b/packages/statemanager/src/baseStateManager.ts @@ -99,9 +99,9 @@ export abstract class BaseStateManager { return account.isEmpty() } - abstract putContractCode(address: Address, value: Buffer): Promise - abstract getContractStorage(address: Address, key: Buffer): Promise - abstract putContractStorage(address: Address, key: Buffer, value: Buffer): Promise + abstract putContractCode(address: Address, value: Uint8Array): Promise + abstract getContractStorage(address: Address, key: Uint8Array): Promise + abstract putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise /** * Checkpoints the current state of the StateManager instance. diff --git a/packages/statemanager/src/cache.ts b/packages/statemanager/src/cache.ts index fc56f8140ef..8cd0607d3e9 100644 --- a/packages/statemanager/src/cache.ts +++ b/packages/statemanager/src/cache.ts @@ -1,12 +1,12 @@ -import { Account } from '@ethereumjs/util' +import { Account, bytesToHex, hexStringToBytes } from '@ethereumjs/util' import { OrderedMap } from 'js-sdsl' import type { Address } from '@ethereumjs/util' import type { OrderedMapIterator } from 'js-sdsl' export type getCb = (address: Address) => Promise -export type putCb = (keyBuf: Buffer, accountRlp: Buffer) => Promise -export type deleteCb = (keyBuf: Buffer) => Promise +export type putCb = (keyBuf: Uint8Array, accountRlp: Uint8Array) => Promise +export type deleteCb = (keyBuf: Uint8Array) => Promise export interface CacheOpts { getCb: getCb @@ -59,7 +59,7 @@ export class Cache { * @param key - Address of account */ lookup(key: Address): Account | undefined { - const keyStr = key.buf.toString('hex') + const keyStr = bytesToHex(key.bytes) const it = this._cache.find(keyStr) if (!it.equals(this._cacheEnd)) { @@ -76,7 +76,7 @@ export class Cache { * @param key - trie key to lookup */ keyIsDeleted(key: Address): boolean { - const keyStr = key.buf.toString('hex') + const keyStr = bytesToHex(key.bytes) const it = this._cache.find(keyStr) if (!it.equals(this._cacheEnd)) { return it.pointer[1].deleted @@ -116,7 +116,7 @@ export class Cache { const value = it.pointer[1] if (value.modified === true) { value.modified = false - const keyBuf = Buffer.from(it.pointer[0], 'hex') + const keyBuf = hexStringToBytes(it.pointer[0]) if (value.deleted === false) { const accountRlp = value.val await this._putCb(keyBuf, accountRlp) @@ -185,7 +185,7 @@ export class Cache { deleted: boolean, virtual = false ): void { - const keyHex = key.buf.toString('hex') + const keyHex = bytesToHex(key.bytes) const val = value.serialize() this._cache.setElement(keyHex, { val, modified, deleted, virtual }) } diff --git a/packages/statemanager/src/ethersStateManager.ts b/packages/statemanager/src/ethersStateManager.ts index a9d7c32a3e9..80c825bf9de 100644 --- a/packages/statemanager/src/ethersStateManager.ts +++ b/packages/statemanager/src/ethersStateManager.ts @@ -1,12 +1,5 @@ import { Trie } from '@ethereumjs/trie' -import { - Account, - bigIntToHex, - bufferToBigInt, - bufferToHex, - setLengthLeft, - toBuffer, -} from '@ethereumjs/util' +import { Account, bigIntToHex, bytesToBigInt, bytesToHex, toBytes } from '@ethereumjs/util' import { debug } from 'debug' import { keccak256 } from 'ethereum-cryptography/keccak' import { ethers } from 'ethers' @@ -29,8 +22,8 @@ export interface EthersStateManagerOpts { export class EthersStateManager extends BaseStateManager implements StateManager { private provider: ethers.providers.StaticJsonRpcProvider | ethers.providers.JsonRpcProvider - private contractCache: Map - private storageCache: Map> + private contractCache: Map + private storageCache: Map> private blockTag: string _cache: Cache @@ -55,7 +48,7 @@ export class EthersStateManager extends BaseStateManager implements StateManager const putCb: putCb = async (_keyBuf, _accountRlp) => { return Promise.resolve() } - const deleteCb = async (_keyBuf: Buffer) => { + const deleteCb = async (_keyBuf: Uint8Array) => { return Promise.resolve() } this._cache = new Cache({ getCb, putCb, deleteCb }) @@ -95,16 +88,16 @@ export class EthersStateManager extends BaseStateManager implements StateManager /** * Gets the code corresponding to the provided `address`. * @param address - Address to get the `code` for - * @returns {Promise} - Resolves with the code corresponding to the provided address. - * Returns an empty `Buffer` if the account has no associated code. + * @returns {Promise} - Resolves with the code corresponding to the provided address. + * Returns an empty `Uint8Array` if the account has no associated code. */ - async getContractCode(address: Address): Promise { - let codeBuffer = this.contractCache.get(address.toString()) - if (codeBuffer !== undefined) return codeBuffer + async getContractCode(address: Address): Promise { + let codeBytes = this.contractCache.get(address.toString()) + if (codeBytes !== undefined) return codeBytes const code = await this.provider.getCode(address.toString(), this.blockTag) - codeBuffer = toBuffer(code) - this.contractCache.set(address.toString(), codeBuffer) - return codeBuffer + codeBytes = toBytes(code) + this.contractCache.set(address.toString(), codeBytes) + return codeBytes } /** @@ -113,7 +106,7 @@ export class EthersStateManager extends BaseStateManager implements StateManager * @param address - Address of the `account` to add the `code` for * @param value - The value of the `code` */ - async putContractCode(address: Address, value: Buffer): Promise { + async putContractCode(address: Address, value: Uint8Array): Promise { // Store contract code in the cache this.contractCache.set(address.toString(), value) } @@ -123,18 +116,18 @@ export class EthersStateManager extends BaseStateManager implements StateManager * the shortest representation of the stored value. * @param address - Address of the account to get the storage for * @param key - Key in the account's storage to get the value for. Must be 32 bytes long. - * @returns {Buffer} - The storage value for the account + * @returns {Uint8Array} - The storage value for the account * corresponding to the provided address at the provided key. - * If this does not exist an empty `Buffer` is returned. + * If this does not exist an empty `Uint8Array` is returned. */ - async getContractStorage(address: Address, key: Buffer): Promise { + async getContractStorage(address: Address, key: Uint8Array): Promise { // Check storage slot in cache - const accountStorage: Map | undefined = this.storageCache.get( + const accountStorage: Map | undefined = this.storageCache.get( address.toString() ) - let storage: Buffer | string | undefined + let storage: Uint8Array | string | undefined if (accountStorage !== undefined) { - storage = accountStorage.get(key.toString('hex')) + storage = accountStorage.get(bytesToHex(key)) if (storage !== undefined) { return storage } @@ -143,10 +136,10 @@ export class EthersStateManager extends BaseStateManager implements StateManager // Retrieve storage slot from provider if not found in cache storage = await this.provider.getStorageAt( address.toString(), - bufferToBigInt(key), + bytesToBigInt(key), this.blockTag ) - const value = toBuffer(storage) + const value = toBytes(storage) await this.putContractStorage(address, key, value) return value @@ -161,13 +154,13 @@ export class EthersStateManager extends BaseStateManager implements StateManager * Cannot be more than 32 bytes. Leading zeros are stripped. * If it is empty or filled with zeros, deletes the value. */ - async putContractStorage(address: Address, key: Buffer, value: Buffer): Promise { + async putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { let accountStorage = this.storageCache.get(address.toString()) if (accountStorage === undefined) { - this.storageCache.set(address.toString(), new Map()) + this.storageCache.set(address.toString(), new Map()) accountStorage = this.storageCache.get(address.toString()) } - accountStorage?.set(key.toString('hex'), value) + accountStorage?.set(bytesToHex(key), value) } /** @@ -190,7 +183,7 @@ export class EthersStateManager extends BaseStateManager implements StateManager const dump: StorageDump = {} if (addressStorage !== undefined) { for (const slot of addressStorage) { - dump[slot[0]] = bufferToHex(slot[1]) + dump[slot[0]] = bytesToHex(slot[1]) } } return Promise.resolve(dump) @@ -208,14 +201,10 @@ export class EthersStateManager extends BaseStateManager implements StateManager // Get merkle proof for `address` from provider const proof = await this.provider.send('eth_getProof', [address.toString(), [], this.blockTag]) - const proofBuf = proof.accountProof.map((proofNode: string) => toBuffer(proofNode)) + const proofBuf = proof.accountProof.map((proofNode: string) => toBytes(proofNode)) const trie = new Trie({ useKeyHashing: true }) - const verified = await trie.verifyProof( - Buffer.from(keccak256(proofBuf[0])), - address.buf, - proofBuf - ) + const verified = await trie.verifyProof(keccak256(proofBuf[0]), address.bytes, proofBuf) // if not verified (i.e. verifyProof returns null), account does not exist return verified === null ? false : true } @@ -223,8 +212,8 @@ export class EthersStateManager extends BaseStateManager implements StateManager /** * Gets the code corresponding to the provided `address`. * @param address - Address to get the `code` for - * @returns {Promise} - Resolves with the code corresponding to the provided address. - * Returns an empty `Buffer` if the account has no associated code. + * @returns {Promise} - Resolves with the code corresponding to the provided address. + * Returns an empty `Uint8Array` if the account has no associated code. */ async getAccount(address: Address): Promise { const account = this._cache.getOrLoad(address) @@ -246,8 +235,8 @@ export class EthersStateManager extends BaseStateManager implements StateManager const account = Account.fromAccountData({ balance: BigInt(accountData.balance), nonce: BigInt(accountData.nonce), - codeHash: toBuffer(accountData.codeHash), - storageRoot: toBuffer(accountData.storageHash), + codeHash: toBytes(accountData.codeHash), + storageRoot: toBytes(accountData.storageHash), }) return account } @@ -267,10 +256,10 @@ export class EthersStateManager extends BaseStateManager implements StateManager * @param storageSlots storage slots to get proof of * @returns an EIP-1186 formatted proof */ - async getProof(address: Address, storageSlots: Buffer[] = []): Promise { + async getProof(address: Address, storageSlots: Uint8Array[] = []): Promise { const proof = await this.provider.send('eth_getProof', [ address.toString(), - [storageSlots.map((slot) => bufferToHex(slot))], + [storageSlots.map((slot) => bytesToHex(slot))], this.blockTag, ]) @@ -318,13 +307,13 @@ export class EthersStateManager extends BaseStateManager implements StateManager * @deprecated This method is not used by the Ethers State Manager and is a stub required by the State Manager interface */ getStateRoot = async () => { - return setLengthLeft(Buffer.from([]), 32) + return new Uint8Array(32) } /** * @deprecated This method is not used by the Ethers State Manager and is a stub required by the State Manager interface */ - setStateRoot = async (_root: Buffer) => {} + setStateRoot = async (_root: Uint8Array) => {} /** * @deprecated This method is not used by the Ethers State Manager and is a stub required by the State Manager interface diff --git a/packages/statemanager/src/interface.ts b/packages/statemanager/src/interface.ts index 28614d14319..b7353515cd4 100644 --- a/packages/statemanager/src/interface.ts +++ b/packages/statemanager/src/interface.ts @@ -17,19 +17,19 @@ export interface StateAccess { accountIsEmpty(address: Address): Promise deleteAccount(address: Address): Promise modifyAccountFields(address: Address, accountFields: AccountFields): Promise - putContractCode(address: Address, value: Buffer): Promise - getContractCode(address: Address): Promise - getContractStorage(address: Address, key: Buffer): Promise - putContractStorage(address: Address, key: Buffer, value: Buffer): Promise + putContractCode(address: Address, value: Uint8Array): Promise + getContractCode(address: Address): Promise + getContractStorage(address: Address, key: Uint8Array): Promise + putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise clearContractStorage(address: Address): Promise checkpoint(): Promise commit(): Promise revert(): Promise - getStateRoot(): Promise - setStateRoot(stateRoot: Buffer): Promise - getProof?(address: Address, storageSlots: Buffer[]): Promise + getStateRoot(): Promise + setStateRoot(stateRoot: Uint8Array): Promise + getProof?(address: Address, storageSlots: Uint8Array[]): Promise verifyProof?(proof: Proof): Promise - hasStateRoot(root: Buffer): Promise + hasStateRoot(root: Uint8Array): Promise } export interface StateManager extends StateAccess { diff --git a/packages/statemanager/src/stateManager.ts b/packages/statemanager/src/stateManager.ts index 8d454ead868..3a631663170 100644 --- a/packages/statemanager/src/stateManager.ts +++ b/packages/statemanager/src/stateManager.ts @@ -5,11 +5,15 @@ import { KECCAK256_NULL, KECCAK256_RLP, bigIntToHex, - bufferToHex, + bytesToHex, + bytesToPrefixedHexString, + concatBytes, + equalsBytes, + hexStringToBytes, setLengthLeft, short, - toBuffer, - unpadBuffer, + unpadBytes, + utf8ToBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -44,7 +48,7 @@ export type Proof = { * will be the same as the hash of the empty trie which leads to * misbehaviour in the underlying trie library. */ -const CODEHASH_PREFIX = Buffer.from('c') +const CODEHASH_PREFIX = utf8ToBytes('c') /** * Options for constructing a {@link StateManager}. @@ -97,14 +101,14 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * desired backend. */ const getCb: getCb = async (address) => { - const rlp = await this._trie.get(address.buf) + const rlp = await this._trie.get(address.bytes) return rlp ? Account.fromRlpSerializedAccount(rlp) : undefined } const putCb: putCb = async (keyBuf, accountRlp) => { const trie = this._trie await trie.put(keyBuf, accountRlp) } - const deleteCb = async (keyBuf: Buffer) => { + const deleteCb = async (keyBuf: Uint8Array) => { const trie = this._trie await trie.del(keyBuf) } @@ -128,14 +132,14 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * @param address - Address of the `account` to add the `code` for * @param value - The value of the `code` */ - async putContractCode(address: Address, value: Buffer): Promise { - const codeHash = Buffer.from(keccak256(value)) + async putContractCode(address: Address, value: Uint8Array): Promise { + const codeHash = keccak256(value) - if (codeHash.equals(KECCAK256_NULL)) { + if (equalsBytes(codeHash, KECCAK256_NULL)) { return } - const key = this._prefixCodeHashes ? Buffer.concat([CODEHASH_PREFIX, codeHash]) : codeHash + const key = this._prefixCodeHashes ? concatBytes(CODEHASH_PREFIX, codeHash) : codeHash // @ts-expect-error await this._trie._db.put(key, value) @@ -148,20 +152,20 @@ export class DefaultStateManager extends BaseStateManager implements StateManage /** * Gets the code corresponding to the provided `address`. * @param address - Address to get the `code` for - * @returns {Promise} - Resolves with the code corresponding to the provided address. - * Returns an empty `Buffer` if the account has no associated code. + * @returns {Promise} - Resolves with the code corresponding to the provided address. + * Returns an empty `Uint8Array` if the account has no associated code. */ - async getContractCode(address: Address): Promise { + async getContractCode(address: Address): Promise { const account = await this.getAccount(address) if (!account.isContract()) { - return Buffer.alloc(0) + return new Uint8Array(0) } const key = this._prefixCodeHashes - ? Buffer.concat([CODEHASH_PREFIX, account.codeHash]) + ? concatBytes(CODEHASH_PREFIX, account.codeHash) : account.codeHash // @ts-expect-error const code = await this._trie._db.get(key) - return code ?? Buffer.alloc(0) + return code ?? new Uint8Array(0) } /** @@ -185,7 +189,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage */ async _getStorageTrie(address: Address): Promise { // from storage cache - const addressHex = address.buf.toString('hex') + const addressHex = bytesToHex(address.bytes) let storageTrie = this._storageTries[addressHex] if (storageTrie === undefined || storageTrie === null) { // lookup from state @@ -199,18 +203,18 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * the shortest representation of the stored value. * @param address - Address of the account to get the storage for * @param key - Key in the account's storage to get the value for. Must be 32 bytes long. - * @returns {Promise} - The storage value for the account + * @returns {Promise} - The storage value for the account * corresponding to the provided address at the provided key. - * If this does not exist an empty `Buffer` is returned. + * If this does not exist an empty `Uint8Array` is returned. */ - async getContractStorage(address: Address, key: Buffer): Promise { + async getContractStorage(address: Address, key: Uint8Array): Promise { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } const trie = await this._getStorageTrie(address) const value = await trie.get(key) - const decoded = Buffer.from(RLP.decode(Uint8Array.from(value ?? [])) as Uint8Array) + const decoded = RLP.decode(value ?? new Uint8Array(0)) as Uint8Array return decoded } @@ -230,7 +234,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage modifyTrie(storageTrie, async () => { // update storage cache - const addressHex = address.buf.toString('hex') + const addressHex = bytesToHex(address.bytes) this._storageTries[addressHex] = storageTrie // update contract storageRoot @@ -250,7 +254,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * @param key - Key to set the value at. Must be 32 bytes long. * @param value - Value to set at `key` for account corresponding to `address`. Cannot be more than 32 bytes. Leading zeros are stripped. If it is a empty or filled with zeros, deletes the value. */ - async putContractStorage(address: Address, key: Buffer, value: Buffer): Promise { + async putContractStorage(address: Address, key: Uint8Array, value: Uint8Array): Promise { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } @@ -259,12 +263,12 @@ export class DefaultStateManager extends BaseStateManager implements StateManage throw new Error('Storage value cannot be longer than 32 bytes') } - value = unpadBuffer(value) + value = unpadBytes(value) await this._modifyContractStorage(address, async (storageTrie, done) => { - if (Buffer.isBuffer(value) && value.length) { + if (value instanceof Uint8Array && value.length) { // format input - const encodedValue = Buffer.from(RLP.encode(Uint8Array.from(value))) + const encodedValue = RLP.encode(value) if (this.DEBUG) { this._debug(`Update contract storage for account ${address} to ${short(value)}`) } @@ -327,22 +331,24 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * @param address address to get proof of * @param storageSlots storage slots to get proof of */ - async getProof(address: Address, storageSlots: Buffer[] = []): Promise { + async getProof(address: Address, storageSlots: Uint8Array[] = []): Promise { const account = await this.getAccount(address) - const accountProof: PrefixedHexString[] = (await this._trie.createProof(address.buf)).map((p) => - bufferToHex(p) + const accountProof: PrefixedHexString[] = (await this._trie.createProof(address.bytes)).map( + (p) => bytesToPrefixedHexString(p) ) const storageProof: StorageProof[] = [] const storageTrie = await this._getStorageTrie(address) for (const storageKey of storageSlots) { - const proof = (await storageTrie.createProof(storageKey)).map((p) => bufferToHex(p)) - let value = bufferToHex(await this.getContractStorage(address, storageKey)) + const proof = (await storageTrie.createProof(storageKey)).map((p) => + bytesToPrefixedHexString(p) + ) + let value = bytesToPrefixedHexString(await this.getContractStorage(address, storageKey)) if (value === '0x') { value = '0x0' } const proofItem: StorageProof = { - key: bufferToHex(storageKey), + key: bytesToPrefixedHexString(storageKey), value, proof, } @@ -352,9 +358,9 @@ export class DefaultStateManager extends BaseStateManager implements StateManage const returnValue: Proof = { address: address.toString(), balance: bigIntToHex(account.balance), - codeHash: bufferToHex(account.codeHash), + codeHash: bytesToPrefixedHexString(account.codeHash), nonce: bigIntToHex(account.nonce), - storageHash: bufferToHex(account.storageRoot), + storageHash: bytesToPrefixedHexString(account.storageRoot), accountProof, storageProof, } @@ -366,10 +372,10 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * @param proof the proof to prove */ async verifyProof(proof: Proof): Promise { - const rootHash = Buffer.from(keccak256(toBuffer(proof.accountProof[0]))) - const key = toBuffer(proof.address) + const rootHash = keccak256(hexStringToBytes(proof.accountProof[0])) + const key = hexStringToBytes(proof.address) const accountProof = proof.accountProof.map((rlpString: PrefixedHexString) => - toBuffer(rlpString) + hexStringToBytes(rlpString) ) // This returns the account if the proof is valid. @@ -378,22 +384,23 @@ export class DefaultStateManager extends BaseStateManager implements StateManage if (value === null) { // Verify that the account is empty in the proof. - const emptyBuffer = Buffer.from('') + const emptyBytes = new Uint8Array(0) const notEmptyErrorMsg = 'Invalid proof provided: account is not empty' - const nonce = unpadBuffer(toBuffer(proof.nonce)) - if (!nonce.equals(emptyBuffer)) { + + const nonce = unpadBytes(hexStringToBytes(proof.nonce)) + if (!equalsBytes(nonce, emptyBytes)) { throw new Error(`${notEmptyErrorMsg} (nonce is not zero)`) } - const balance = unpadBuffer(toBuffer(proof.balance)) - if (!balance.equals(emptyBuffer)) { + const balance = unpadBytes(hexStringToBytes(proof.balance)) + if (!equalsBytes(balance, emptyBytes)) { throw new Error(`${notEmptyErrorMsg} (balance is not zero)`) } - const storageHash = toBuffer(proof.storageHash) - if (!storageHash.equals(KECCAK256_RLP)) { + const storageHash = hexStringToBytes(proof.storageHash) + if (!equalsBytes(storageHash, KECCAK256_RLP)) { throw new Error(`${notEmptyErrorMsg} (storageHash does not equal KECCAK256_RLP)`) } - const codeHash = toBuffer(proof.codeHash) - if (!codeHash.equals(KECCAK256_NULL)) { + const codeHash = hexStringToBytes(proof.codeHash) + if (!equalsBytes(codeHash, KECCAK256_NULL)) { throw new Error(`${notEmptyErrorMsg} (codeHash does not equal KECCAK256_NULL)`) } } else { @@ -406,30 +413,30 @@ export class DefaultStateManager extends BaseStateManager implements StateManage if (balance !== BigInt(proof.balance)) { throw new Error(`${invalidErrorMsg} balance does not match`) } - if (!storageRoot.equals(toBuffer(proof.storageHash))) { + if (!equalsBytes(storageRoot, hexStringToBytes(proof.storageHash))) { throw new Error(`${invalidErrorMsg} storageHash does not match`) } - if (!codeHash.equals(toBuffer(proof.codeHash))) { + if (!equalsBytes(codeHash, hexStringToBytes(proof.codeHash))) { throw new Error(`${invalidErrorMsg} codeHash does not match`) } } - const storageRoot = toBuffer(proof.storageHash) + const storageRoot = hexStringToBytes(proof.storageHash) for (const stProof of proof.storageProof) { - const storageProof = stProof.proof.map((value: PrefixedHexString) => toBuffer(value)) - const storageValue = setLengthLeft(toBuffer(stProof.value), 32) - const storageKey = toBuffer(stProof.key) + const storageProof = stProof.proof.map((value: PrefixedHexString) => hexStringToBytes(value)) + const storageValue = setLengthLeft(hexStringToBytes(stProof.value), 32) + const storageKey = hexStringToBytes(stProof.key) const proofValue = await new Trie({ useKeyHashing: true }).verifyProof( storageRoot, storageKey, storageProof ) const reportedValue = setLengthLeft( - Buffer.from(RLP.decode(Uint8Array.from((proofValue as Buffer) ?? [])) as Uint8Array), + RLP.decode(proofValue ?? new Uint8Array(0)) as Uint8Array, 32 ) - if (!reportedValue.equals(storageValue)) { + if (!equalsBytes(reportedValue, storageValue)) { throw new Error('Reported trie value does not match storage') } } @@ -440,9 +447,9 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * Gets the state-root of the Merkle-Patricia trie representation * of the state of this StateManager. Will error if there are uncommitted * checkpoints on the instance. - * @returns {Promise} - Returns the state-root of the `StateManager` + * @returns {Promise} - Returns the state-root of the `StateManager` */ - async getStateRoot(): Promise { + async getStateRoot(): Promise { await this._cache.flush() return this._trie.root() } @@ -454,10 +461,10 @@ export class DefaultStateManager extends BaseStateManager implements StateManage * the state trie. * @param stateRoot - The state-root to reset the instance to */ - async setStateRoot(stateRoot: Buffer): Promise { + async setStateRoot(stateRoot: Uint8Array): Promise { await this._cache.flush() - if (!stateRoot.equals(this._trie.EMPTY_TRIE_ROOT)) { + if (!equalsBytes(stateRoot, this._trie.EMPTY_TRIE_ROOT)) { const hasRoot = await this._trie.checkRoot(stateRoot) if (!hasRoot) { throw new Error('State trie does not contain state root') @@ -484,7 +491,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage const stream = trie.createReadStream() stream.on('data', (val: any) => { - storage[val.key.toString('hex')] = val.value.toString('hex') + storage[bytesToHex(val.key)] = bytesToHex(val.value) }) stream.on('end', () => { resolve(storage) @@ -499,7 +506,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage /** * Checks whether there is a state corresponding to a stateRoot */ - async hasStateRoot(root: Buffer): Promise { + async hasStateRoot(root: Uint8Array): Promise { return this._trie.checkRoot(root) } @@ -517,7 +524,7 @@ export class DefaultStateManager extends BaseStateManager implements StateManage ) { return true } - if (await this._trie.get(address.buf)) { + if (await this._trie.get(address.bytes)) { return true } return false diff --git a/packages/statemanager/test/cache.spec.ts b/packages/statemanager/test/cache.spec.ts index 20a58fadf68..ff761023d45 100644 --- a/packages/statemanager/test/cache.spec.ts +++ b/packages/statemanager/test/cache.spec.ts @@ -1,5 +1,5 @@ import { Trie } from '@ethereumjs/trie' -import { Account, Address } from '@ethereumjs/util' +import { Account, Address, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Cache } from '../src/cache' @@ -13,14 +13,14 @@ tape('cache initialization', (t) => { const trie = new Trie({ useKeyHashing: true }) const getCb: getCb = async (address) => { const innerTrie = trie - const rlp = await innerTrie.get(address.buf) + const rlp = await innerTrie.get(address.bytes) return rlp ? Account.fromRlpSerializedAccount(rlp) : undefined } const putCb: putCb = async (keyBuf, accountRlp) => { const innerTrie = trie await innerTrie.put(keyBuf, accountRlp) } - const deleteCb = async (keyBuf: Buffer) => { + const deleteCb = async (keyBuf: Uint8Array) => { const innerTrie = trie await innerTrie.del(keyBuf) } @@ -35,20 +35,20 @@ tape('cache put and get account', (t) => { const trie = new Trie({ useKeyHashing: true }) const getCb: getCb = async (address) => { const innerTrie = trie - const rlp = await innerTrie.get(address.buf) + const rlp = await innerTrie.get(address.bytes) return rlp ? Account.fromRlpSerializedAccount(rlp) : undefined } const putCb: putCb = async (keyBuf, accountRlp) => { const innerTrie = trie await innerTrie.put(keyBuf, accountRlp) } - const deleteCb = async (keyBuf: Buffer) => { + const deleteCb = async (keyBuf: Uint8Array) => { const innerTrie = trie await innerTrie.del(keyBuf) } const cache = new Cache({ getCb, putCb, deleteCb }) - const addr = new Address(Buffer.from('cd2a3d9f938e13cd947ec05abc7fe734df8dd826', 'hex')) + const addr = new Address(hexStringToBytes('cd2a3d9f938e13cd947ec05abc7fe734df8dd826')) const acc = createAccount(BigInt(0), BigInt(0xff11)) t.test('should fail to get non-existent account', async (st) => { @@ -65,7 +65,7 @@ tape('cache put and get account', (t) => { }) t.test('should not have flushed to trie', async (st) => { - const res = await trie.get(addr.buf) + const res = await trie.get(addr.bytes) st.notOk(res) st.end() }) @@ -76,7 +76,7 @@ tape('cache put and get account', (t) => { }) t.test('trie should contain flushed account', async (st) => { - const raw = await trie.get(addr.buf) + const raw = await trie.get(addr.bytes) const res = Account.fromRlpSerializedAccount(raw!) st.equal(res.balance, acc.balance) st.end() @@ -95,7 +95,7 @@ tape('cache put and get account', (t) => { cache.put(addr, updatedAcc) await cache.flush() - const raw = await trie.get(addr.buf) + const raw = await trie.get(addr.bytes) const res = Account.fromRlpSerializedAccount(raw!) st.equal(res.balance, updatedAcc.balance) st.end() @@ -106,20 +106,20 @@ tape('cache checkpointing', (t) => { const trie = new Trie({ useKeyHashing: true }) const getCb: getCb = async (address) => { const innerTrie = trie - const rlp = await innerTrie.get(address.buf) + const rlp = await innerTrie.get(address.bytes) return rlp ? Account.fromRlpSerializedAccount(rlp) : undefined } const putCb: putCb = async (keyBuf, accountRlp) => { const innerTrie = trie await innerTrie.put(keyBuf, accountRlp) } - const deleteCb = async (keyBuf: Buffer) => { + const deleteCb = async (keyBuf: Uint8Array) => { const innerTrie = trie await innerTrie.del(keyBuf) } const cache = new Cache({ getCb, putCb, deleteCb }) - const addr = new Address(Buffer.from('cd2a3d9f938e13cd947ec05abc7fe734df8dd826', 'hex')) + const addr = new Address(hexStringToBytes('cd2a3d9f938e13cd947ec05abc7fe734df8dd826')) const acc = createAccount(BigInt(0), BigInt(0xff11)) const updatedAcc = createAccount(BigInt(0x00), BigInt(0xff00)) diff --git a/packages/statemanager/test/ethersStateManager.spec.ts b/packages/statemanager/test/ethersStateManager.spec.ts index c629f739cee..45ab64a462e 100644 --- a/packages/statemanager/test/ethersStateManager.spec.ts +++ b/packages/statemanager/test/ethersStateManager.spec.ts @@ -1,7 +1,14 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction, TransactionFactory } from '@ethereumjs/tx' -import { Address, bigIntToBuffer, setLengthLeft } from '@ethereumjs/util' +import { + Address, + bigIntToBytes, + equalsBytes, + hexStringToBytes, + setLengthLeft, + utf8ToBytes, +} from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' import { BaseProvider, JsonRpcProvider, StaticJsonRpcProvider } from '@ethersproject/providers' import * as tape from 'tape' @@ -77,20 +84,20 @@ tape('Ethers State Manager API tests', async (t) => { const storageSlot = await state.getContractStorage( UNIerc20ContractAddress, - setLengthLeft(bigIntToBuffer(1n), 32) + setLengthLeft(bigIntToBytes(1n), 32) ) t.ok(storageSlot.length > 0, 'was able to retrieve storage slot 1 for the UNI contract') await state.putContractStorage( UNIerc20ContractAddress, - setLengthLeft(bigIntToBuffer(2n), 32), - Buffer.from('abcd') + setLengthLeft(bigIntToBytes(2n), 32), + utf8ToBytes('abcd') ) const slotValue = await state.getContractStorage( UNIerc20ContractAddress, - setLengthLeft(bigIntToBuffer(2n), 32) + setLengthLeft(bigIntToBytes(2n), 32) ) - t.ok(slotValue.equals(Buffer.from('abcd')), 'should retrieve slot 2 value') + t.ok(equalsBytes(slotValue, utf8ToBytes('abcd')), 'should retrieve slot 2 value') // Verify that provider is not called for cached data ;(provider as any).getStorageAt = function () { @@ -99,14 +106,14 @@ tape('Ethers State Manager API tests', async (t) => { t.doesNotThrow( async () => - state.getContractStorage(UNIerc20ContractAddress, setLengthLeft(bigIntToBuffer(2n), 32)), + state.getContractStorage(UNIerc20ContractAddress, setLengthLeft(bigIntToBytes(2n), 32)), 'should not call provider.getStorageAt' ) await state.putContractStorage( UNIerc20ContractAddress, - setLengthLeft(bigIntToBuffer(2n), 32), - Buffer.from('') + setLengthLeft(bigIntToBytes(2n), 32), + new Uint8Array(0) ) // Verify that provider is not called @@ -126,7 +133,7 @@ tape('Ethers State Manager API tests', async (t) => { const deletedSlot = await state.getContractStorage( UNIerc20ContractAddress, - setLengthLeft(bigIntToBuffer(2n), 32) + setLengthLeft(bigIntToBytes(2n), 32) ) t.equal(deletedSlot.length, 0, 'deleted slot from storage cache') @@ -181,9 +188,8 @@ tape('runTx custom transaction test', async (t) => { const vm = await VM.create({ common, stateManager: state }) const vitalikDotEth = Address.fromString('0xd8da6bf26964af9d7eed9e03e53415d37aa96045') - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const privateKey = hexStringToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) const tx = FeeMarketEIP1559Transaction.fromTxData( { to: vitalikDotEth, value: '0x100', gasLimit: 500000n, maxFeePerGas: 7 }, diff --git a/packages/statemanager/test/proofStateManager.spec.ts b/packages/statemanager/test/proofStateManager.spec.ts index caae1cbc117..51ddc44ba2d 100644 --- a/packages/statemanager/test/proofStateManager.spec.ts +++ b/packages/statemanager/test/proofStateManager.spec.ts @@ -1,5 +1,5 @@ import { Trie } from '@ethereumjs/trie' -import { Address, toBuffer, zeros } from '@ethereumjs/util' +import { Address, bytesToHex, hexStringToBytes, zeros } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import * as tape from 'tape' @@ -13,8 +13,8 @@ tape('ProofStateManager', (t) => { t.test('should get and verify EIP 1178 proofs', async (st) => { const address = Address.zero() const key = zeros(32) - const value = Buffer.from('0000aabb00', 'hex') - const code = Buffer.from('6000', 'hex') + const value = hexStringToBytes('0000aabb00') + const code = hexStringToBytes('6000') const stateManager = new DefaultStateManager() await stateManager.checkpoint() await stateManager.putContractStorage(address, key, value) @@ -23,7 +23,7 @@ tape('ProofStateManager', (t) => { account.balance = BigInt(1) account.nonce = BigInt(2) await stateManager.putAccount(address, account) - const address2 = new Address(Buffer.from('20'.repeat(20), 'hex')) + const address2 = new Address(hexStringToBytes('20'.repeat(20))) const account2 = await stateManager.getAccount(address2) account.nonce = BigInt(2) await stateManager.putAccount(address2, account2) @@ -44,10 +44,10 @@ tape('ProofStateManager', (t) => { const trie = new Trie({ useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie }) // Dump all the account proof data in the DB - let stateRoot: Buffer | undefined + let stateRoot: Uint8Array | undefined for (const proofData of ropsten_validAccount.accountProof) { - const bufferData = toBuffer(proofData) - const key = Buffer.from(keccak256(bufferData)) + const bufferData = hexStringToBytes(proofData) + const key = keccak256(bufferData) if (stateRoot === undefined) { stateRoot = key } @@ -72,10 +72,10 @@ tape('ProofStateManager', (t) => { const trie = new Trie({ useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie }) // Dump all the account proof data in the DB - let stateRoot: Buffer | undefined + let stateRoot: Uint8Array | undefined for (const proofData of ropsten_nonexistentAccount.accountProof) { - const bufferData = toBuffer(proofData) - const key = Buffer.from(keccak256(bufferData)) + const bufferData = hexStringToBytes(proofData) + const key = keccak256(bufferData) if (stateRoot === undefined) { stateRoot = key } @@ -101,10 +101,10 @@ tape('ProofStateManager', (t) => { const trie = new Trie({ useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie }) // Dump all the account proof data in the DB - let stateRoot: Buffer | undefined + let stateRoot: Uint8Array | undefined for (const proofData of ropsten_contractWithStorage.accountProof) { - const bufferData = toBuffer(proofData) - const key = Buffer.from(keccak256(bufferData)) + const bufferData = hexStringToBytes(proofData) + const key = keccak256(bufferData) if (stateRoot === undefined) { stateRoot = key } @@ -113,17 +113,17 @@ tape('ProofStateManager', (t) => { } const storageRoot = ropsten_contractWithStorage.storageHash const storageTrie = new Trie({ useKeyHashing: true }) - const storageKeys: Buffer[] = [] + const storageKeys: Uint8Array[] = [] for (const storageProofsData of ropsten_contractWithStorage.storageProof) { - storageKeys.push(toBuffer(storageProofsData.key)) + storageKeys.push(hexStringToBytes(storageProofsData.key)) for (const storageProofData of storageProofsData.proof) { - const key = Buffer.from(keccak256(toBuffer(storageProofData))) + const key = keccak256(hexStringToBytes(storageProofData)) // @ts-expect-error - await storageTrie._db.put(key, toBuffer(storageProofData)) + await storageTrie._db.put(key, hexStringToBytes(storageProofData)) } } - storageTrie.root(toBuffer(storageRoot)) - const addressHex = address.buf.toString('hex') + storageTrie.root(hexStringToBytes(storageRoot)) + const addressHex = bytesToHex(address.bytes) stateManager._storageTries[addressHex] = storageTrie trie.root(stateRoot!) @@ -143,10 +143,10 @@ tape('ProofStateManager', (t) => { const trie = new Trie({ useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie }) // Dump all the account proof data in the DB - let stateRoot: Buffer | undefined + let stateRoot: Uint8Array | undefined for (const proofData of ropsten_contractWithStorage.accountProof) { - const bufferData = toBuffer(proofData) - const key = Buffer.from(keccak256(bufferData)) + const bufferData = hexStringToBytes(proofData) + const key = keccak256(bufferData) if (stateRoot === undefined) { stateRoot = key } @@ -155,17 +155,17 @@ tape('ProofStateManager', (t) => { } const storageRoot = ropsten_contractWithStorage.storageHash const storageTrie = new Trie({ useKeyHashing: true }) - const storageKeys: Buffer[] = [] + const storageKeys: Uint8Array[] = [] for (const storageProofsData of ropsten_contractWithStorage.storageProof) { - storageKeys.push(toBuffer(storageProofsData.key)) + storageKeys.push(hexStringToBytes(storageProofsData.key)) for (const storageProofData of storageProofsData.proof) { - const key = Buffer.from(keccak256(toBuffer(storageProofData))) + const key = keccak256(hexStringToBytes(storageProofData)) // @ts-expect-error - await storageTrie._db.put(key, toBuffer(storageProofData)) + await storageTrie._db.put(key, hexStringToBytes(storageProofData)) } } - storageTrie.root(toBuffer(storageRoot)) - const addressHex = address.buf.toString('hex') + storageTrie.root(hexStringToBytes(storageRoot)) + const addressHex = bytesToHex(address.bytes) stateManager._storageTries[addressHex] = storageTrie trie.root(stateRoot!) @@ -212,10 +212,10 @@ tape('ProofStateManager', (t) => { const trie = new Trie({ useKeyHashing: true }) const stateManager = new DefaultStateManager({ trie }) // Dump all the account proof data in the DB - let stateRoot: Buffer | undefined + let stateRoot: Uint8Array | undefined for (const proofData of ropsten_nonexistentAccount.accountProof) { - const bufferData = toBuffer(proofData) - const key = Buffer.from(keccak256(bufferData)) + const bufferData = hexStringToBytes(proofData) + const key = keccak256(bufferData) if (stateRoot === undefined) { stateRoot = key } @@ -224,8 +224,8 @@ tape('ProofStateManager', (t) => { } const storageRoot = ropsten_nonexistentAccount.storageHash const storageTrie = new Trie({ useKeyHashing: true }) - storageTrie.root(toBuffer(storageRoot)) - const addressHex = address.buf.toString('hex') + storageTrie.root(hexStringToBytes(storageRoot)) + const addressHex = bytesToHex(address.bytes) stateManager._storageTries[addressHex] = storageTrie trie.root(stateRoot!) diff --git a/packages/statemanager/test/stateManager.spec.ts b/packages/statemanager/test/stateManager.spec.ts index bc9c1004ee8..23480495dd2 100644 --- a/packages/statemanager/test/stateManager.spec.ts +++ b/packages/statemanager/test/stateManager.spec.ts @@ -3,12 +3,12 @@ import { Address, KECCAK256_RLP, KECCAK256_RLP_S, - toBuffer, - unpadBuffer, + hexStringToBytes, + unpadBytes, zeros, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' +import { bytesToHex, concatBytes, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' // explicitly import `inherits` to fix karma-typescript issue // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -30,29 +30,29 @@ tape('StateManager', (t) => { t.test('should set the state root to empty', async (st) => { const stateManager = new DefaultStateManager() - st.ok(stateManager._trie.root().equals(KECCAK256_RLP), 'it has default root') + st.ok(equalsBytes(stateManager._trie.root(), KECCAK256_RLP), 'it has default root') // commit some data to the trie - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccount(BigInt(0), BigInt(1000)) await stateManager.checkpoint() await stateManager.putAccount(address, account) await stateManager.commit() await stateManager.flush() - st.ok(!stateManager._trie.root().equals(KECCAK256_RLP), 'it has a new root') + st.ok(!equalsBytes(stateManager._trie.root(), KECCAK256_RLP), 'it has a new root') // set state root to empty trie root - const emptyTrieRoot = Buffer.from(KECCAK256_RLP_S, 'hex') + const emptyTrieRoot = hexToBytes(KECCAK256_RLP_S) await stateManager.setStateRoot(emptyTrieRoot) const res = await stateManager.getStateRoot() - st.ok(res.equals(KECCAK256_RLP), 'it has default root') + st.ok(equalsBytes(res, KECCAK256_RLP), 'it has default root') st.end() }) t.test('should clear the cache when the state root is set', async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccount() // test account storage cache @@ -73,12 +73,14 @@ tape('StateManager', (t) => { // test contract storage cache await stateManager.checkpoint() - const key = toBuffer('0x1234567890123456789012345678901234567890123456789012345678901234') - const value = Buffer.from('0x1234') + const key = hexStringToBytes( + '0x1234567890123456789012345678901234567890123456789012345678901234' + ) + const value = hexStringToBytes('0x1234') await stateManager.putContractStorage(address, key, value) const contract0 = await stateManager.getContractStorage(address, key) - st.ok(contract0.equals(value), "contract key's value is set in the _storageTries cache") + st.ok(equalsBytes(contract0, value), "contract key's value is set in the _storageTries cache") await stateManager.commit() await stateManager.setStateRoot(initialStateRoot) @@ -93,7 +95,7 @@ tape('StateManager', (t) => { async (st) => { const stateManager = new DefaultStateManager() const account = createAccount() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) @@ -106,8 +108,8 @@ tape('StateManager', (t) => { const res2 = await stateManager.getAccount(address) - st.equal(stateManager._cache._cache.begin().pointer[0], address.buf.toString('hex')) - st.ok(res1.serialize().equals(res2.serialize())) + st.equal(stateManager._cache._cache.begin().pointer[0], bytesToHex(address.bytes)) + st.ok(equalsBytes(res1.serialize(), res2.serialize())) st.end() } @@ -117,7 +119,7 @@ tape('StateManager', (t) => { 'should call the callback with a boolean representing emptiness, when the account is empty', async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const res = await stateManager.accountIsEmpty(address) @@ -129,7 +131,7 @@ tape('StateManager', (t) => { t.test('should return false for a non-existent account', async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const res = await stateManager.accountExists(address) @@ -141,7 +143,7 @@ tape('StateManager', (t) => { t.test('should return true for an existent account', async (st) => { const stateManager = new DefaultStateManager() const account = createAccount(BigInt(0x1), BigInt(0x1)) - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) @@ -157,7 +159,7 @@ tape('StateManager', (t) => { async (st) => { const stateManager = new DefaultStateManager() const account = createAccount(BigInt(0x1), BigInt(0x1)) - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) @@ -172,7 +174,7 @@ tape('StateManager', (t) => { t.test('should modify account fields correctly', async (st) => { const stateManager = new DefaultStateManager() const account = createAccount() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.putAccount(address, account) await stateManager.modifyAccountFields(address, { balance: BigInt(1234) }) @@ -188,24 +190,22 @@ tape('StateManager', (t) => { st.equal(res2.nonce, BigInt(1)) await stateManager.modifyAccountFields(address, { - codeHash: Buffer.from( - 'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b', - 'hex' + codeHash: hexStringToBytes( + 'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' ), - storageRoot: Buffer.from( - 'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7', - 'hex' + storageRoot: hexStringToBytes( + 'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7' ), }) const res3 = await stateManager.getAccount(address) st.equal( - res3.codeHash.toString('hex'), + bytesToHex(res3.codeHash), 'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' ) st.equal( - res3.storageRoot.toString('hex'), + bytesToHex(res3.storageRoot), 'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7' ) @@ -216,7 +216,7 @@ tape('StateManager', (t) => { 'should modify account fields correctly on previously non-existent account', async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) await stateManager.modifyAccountFields(address, { balance: BigInt(1234) }) const res1 = await stateManager.getAccount(address) @@ -226,13 +226,11 @@ tape('StateManager', (t) => { const res2 = await stateManager.getAccount(address) st.equal(res2.nonce, BigInt(1)) - const newCodeHash = Buffer.from( - 'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b', - 'hex' + const newCodeHash = hexStringToBytes( + 'd748bf26ab37599c944babfdbeecf6690801bd61bf2670efb0a34adfc6dca10b' ) - const newStorageRoot = Buffer.from( - 'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7', - 'hex' + const newStorageRoot = hexStringToBytes( + 'cafd881ab193703b83816c49ff6c2bf6ba6f464a1be560c42106128c8dbc35e7' ) await stateManager.modifyAccountFields(address, { codeHash: newCodeHash, @@ -240,21 +238,23 @@ tape('StateManager', (t) => { }) const res3 = await stateManager.getAccount(address) - st.ok(res3.codeHash.equals(newCodeHash)) - st.ok(res3.storageRoot.equals(newStorageRoot)) + st.ok(equalsBytes(res3.codeHash, newCodeHash)) + st.ok(equalsBytes(res3.storageRoot, newStorageRoot)) st.end() } ) t.test('should dump storage', async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccount() await stateManager.putAccount(address, account) - const key = toBuffer('0x1234567890123456789012345678901234567890123456789012345678901234') - const value = toBuffer('0x0a') // We used this value as its RLP encoding is also 0a + const key = hexStringToBytes( + '0x1234567890123456789012345678901234567890123456789012345678901234' + ) + const value = hexStringToBytes('0x0a') // We used this value as its RLP encoding is also 0a await stateManager.putContractStorage(address, key, value) const data = await stateManager.dumpStorage(address) @@ -266,9 +266,9 @@ tape('StateManager', (t) => { t.test("should validate the key's length when modifying a contract's storage", async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) try { - await stateManager.putContractStorage(address, Buffer.alloc(12), toBuffer('0x1231')) + await stateManager.putContractStorage(address, new Uint8Array(12), hexStringToBytes('0x1231')) } catch (e: any) { st.equal(e.message, 'Storage key must be 32 bytes long') st.end() @@ -281,9 +281,9 @@ tape('StateManager', (t) => { t.test("should validate the key's length when reading a contract's storage", async (st) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) try { - await stateManager.getContractStorage(address, Buffer.alloc(12)) + await stateManager.getContractStorage(address, new Uint8Array(12)) } catch (e: any) { st.equal(e.message, 'Storage key must be 32 bytes long') st.end() @@ -308,9 +308,9 @@ tape('StateManager', (t) => { // Setup const stateManager = new DefaultStateManager() const codeStateManager = new DefaultStateManager() - const address1 = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) - const key1 = Buffer.from('00'.repeat(32), 'hex') - const key2 = Buffer.from('00'.repeat(31) + '01', 'hex') + const address1 = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) + const key1 = hexStringToBytes('00'.repeat(32)) + const key2 = hexStringToBytes('00'.repeat(31) + '01') await stateManager.putContractStorage(address1, key1, key2) await stateManager.putContractStorage(address1, key2, key2) @@ -367,10 +367,9 @@ tape('StateManager - Contract code', (tester) => { const it = tester.test it('should set and get code', async (t) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) - const code = Buffer.from( - '73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157', - 'hex' + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) + const code = hexStringToBytes( + '73095e7baea6a6c7c4c2dfeb977efac326af552d873173095e7baea6a6c7c4c2dfeb977efac326af552d873157' ) const raw = { nonce: '0x0', @@ -382,13 +381,13 @@ tape('StateManager - Contract code', (tester) => { await stateManager.putAccount(address, account) await stateManager.putContractCode(address, code) const codeRetrieved = await stateManager.getContractCode(address) - t.ok(code.equals(codeRetrieved)) + t.ok(equalsBytes(code, codeRetrieved)) t.end() }) it('should not get code if is not contract', async (t) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw = { nonce: '0x0', balance: '0x03e7', @@ -396,33 +395,33 @@ tape('StateManager - Contract code', (tester) => { const account = Account.fromAccountData(raw) await stateManager.putAccount(address, account) const code = await stateManager.getContractCode(address) - t.ok(code.equals(Buffer.alloc(0))) + t.ok(equalsBytes(code, new Uint8Array(0))) t.end() }) it('should set empty code', async (t) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const raw = { nonce: '0x0', balance: '0x03e7', } const account = Account.fromAccountData(raw) - const code = Buffer.alloc(0) + const code = new Uint8Array(0) await stateManager.putAccount(address, account) await stateManager.putContractCode(address, code) const codeRetrieved = await stateManager.getContractCode(address) - t.ok(codeRetrieved.equals(Buffer.alloc(0))) + t.ok(equalsBytes(codeRetrieved, new Uint8Array(0))) t.end() }) it('should prefix codehashes by default', async (t) => { const stateManager = new DefaultStateManager() - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) - const code = Buffer.from('80', 'hex') + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) + const code = hexStringToBytes('80') await stateManager.putContractCode(address, code) const codeRetrieved = await stateManager.getContractCode(address) - t.ok(codeRetrieved.equals(code)) + t.ok(equalsBytes(codeRetrieved, code)) t.end() }) @@ -430,8 +429,8 @@ tape('StateManager - Contract code', (tester) => { const stateManager = new DefaultStateManager({ prefixCodeHashes: false, }) - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) - const code = Buffer.from('80', 'hex') + const address = new Address(hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) + const code = hexStringToBytes('80') try { await stateManager.putContractCode(address, code) t.fail('should throw') @@ -450,7 +449,7 @@ tape('StateManager - Contract storage', (tester) => { const stateManager = new DefaultStateManager() const address = Address.zero() const key = zeros(32) - const value = Buffer.from('aa'.repeat(33), 'hex') + const value = hexStringToBytes('aa'.repeat(33)) try { await stateManager.putContractStorage(address, key, value) t.fail('did not throw') @@ -465,26 +464,26 @@ tape('StateManager - Contract storage', (tester) => { const address = Address.zero() const key0 = zeros(32) - const value0 = Buffer.from('00' + 'aa'.repeat(30), 'hex') // put a value of 31-bytes length with a leading zero byte - const expect0 = unpadBuffer(value0) + const value0 = hexStringToBytes('00' + 'aa'.repeat(30)) // put a value of 31-bytes length with a leading zero byte + const expect0 = unpadBytes(value0) await stateManager.putContractStorage(address, key0, value0) const slot0 = await stateManager.getContractStorage(address, key0) - t.ok(slot0.equals(expect0), 'value of 31 bytes padded correctly') + t.ok(equalsBytes(slot0, expect0), 'value of 31 bytes padded correctly') - const key1 = Buffer.concat([zeros(31), Buffer.from('01', 'hex')]) - const value1 = Buffer.from('0000' + 'aa'.repeat(1), 'hex') // put a value of 1-byte length with two leading zero bytes - const expect1 = unpadBuffer(value1) + const key1 = concatBytes(zeros(31), hexStringToBytes('01')) + const value1 = hexStringToBytes('0000' + 'aa'.repeat(1)) // put a value of 1-byte length with two leading zero bytes + const expect1 = unpadBytes(value1) await stateManager.putContractStorage(address, key1, value1) const slot1 = await stateManager.getContractStorage(address, key1) - t.ok(slot1.equals(expect1), 'value of 1 byte padded correctly') + t.ok(equalsBytes(slot1, expect1), 'value of 1 byte padded correctly') t.end() }) it('should delete storage values which only consist of zero bytes', async (t) => { const address = Address.zero() const key = zeros(32) - const startValue = Buffer.from('01', 'hex') + const startValue = hexStringToBytes('01') const zeroLengths = [0, 1, 31, 32] // checks for arbitrary-length zeros t.plan(zeroLengths.length) @@ -494,14 +493,14 @@ tape('StateManager - Contract storage', (tester) => { const value = zeros(length) await stateManager.putContractStorage(address, key, startValue) const currentValue = await stateManager.getContractStorage(address, key) - if (!currentValue.equals(startValue)) { + if (!equalsBytes(currentValue, startValue)) { // sanity check t.fail('contract value not set correctly') } else { // delete the value await stateManager.putContractStorage(address, key, value) const deleted = await stateManager.getContractStorage(address, key) - t.ok(deleted.equals(zeros(0)), 'the storage key should be deleted') + t.ok(equalsBytes(deleted, zeros(0)), 'the storage key should be deleted') } } t.end() @@ -510,12 +509,12 @@ tape('StateManager - Contract storage', (tester) => { it('should not strip trailing zeros', async (t) => { const address = Address.zero() const key = zeros(32) - const value = Buffer.from('0000aabb00', 'hex') - const expect = Buffer.from('aabb00', 'hex') + const value = hexToBytes('0000aabb00') + const expect = hexToBytes('aabb00') const stateManager = new DefaultStateManager() await stateManager.putContractStorage(address, key, value) const contractValue = await stateManager.getContractStorage(address, key) - t.ok(contractValue.equals(expect), 'trailing zeros are not stripped') + t.ok(equalsBytes(contractValue, expect), 'trailing zeros are not stripped') t.end() }) }) diff --git a/packages/trie/benchmarks/engines/level.ts b/packages/trie/benchmarks/engines/level.ts index 73623019d66..e172c9adfee 100644 --- a/packages/trie/benchmarks/engines/level.ts +++ b/packages/trie/benchmarks/engines/level.ts @@ -4,14 +4,14 @@ import { MemoryLevel } from 'memory-level' import type { BatchDBOp, DB } from '../../src/types' import type { AbstractLevel } from 'abstract-level' -export const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } +export const ENCODING_OPTS = { keyEncoding: 'Uint8Array', valueEncoding: 'Uint8Array' } /** * LevelDB is a thin wrapper around the underlying levelup db, * which validates inputs and sets encoding type. */ export class LevelDB implements DB { - _leveldb: AbstractLevel + _leveldb: AbstractLevel /** * Initialize a DB instance. If `leveldb` is not provided, DB @@ -19,7 +19,7 @@ export class LevelDB implements DB { * @param leveldb - An abstract-leveldown compliant store */ constructor( - leveldb?: AbstractLevel | null + leveldb?: AbstractLevel | null ) { this._leveldb = leveldb ?? new MemoryLevel(ENCODING_OPTS) } @@ -27,8 +27,8 @@ export class LevelDB implements DB { /** * @inheritDoc */ - async get(key: Buffer): Promise { - let value: Buffer | null = null + async get(key: Uint8Array): Promise { + let value: Uint8Array | null = null try { value = await this._leveldb.get(key, ENCODING_OPTS) } catch (error: any) { @@ -45,14 +45,14 @@ export class LevelDB implements DB { /** * @inheritDoc */ - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { await this._leveldb.put(key, val, ENCODING_OPTS) } /** * @inheritDoc */ - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._leveldb.del(key, ENCODING_OPTS) } diff --git a/packages/trie/benchmarks/keys.ts b/packages/trie/benchmarks/keys.ts index 86d7451a388..15e0ff52e5d 100644 --- a/packages/trie/benchmarks/keys.ts +++ b/packages/trie/benchmarks/keys.ts @@ -2,8 +2,8 @@ import { keccak256 } from 'ethereum-cryptography/keccak' let curr = keccak256(new Uint8Array(32)) -export const keys: Buffer[] = [] +export const keys: Uint8Array[] = [] for (let i = 0; i < 5000; curr = keccak256(curr), i++) { - keys.push(Buffer.from(curr)) + keys.push(curr) } diff --git a/packages/trie/benchmarks/suite.ts b/packages/trie/benchmarks/suite.ts index 2704feb525c..6bd60c4af55 100644 --- a/packages/trie/benchmarks/suite.ts +++ b/packages/trie/benchmarks/suite.ts @@ -27,15 +27,15 @@ export function createSuite(db: DB) { ['1k-1k-32-mir', 1000, true], ]) { await mark(title, async () => { - let key = Buffer.alloc(KEY_SIZE) + let key = new Uint8Array(KEY_SIZE) for (let i = 0; i <= ROUNDS; i++) { - key = Buffer.from(keccak256(key)) + key = keccak256(key) if (symmetric) { await trie.put(key, key) } else { - await trie.put(key, Buffer.from(key)) + await trie.put(key, key) } if (i % (eraSize as number) === 0) { diff --git a/packages/trie/examples/level.js b/packages/trie/examples/level.js index 97c4619e923..e6789a084d5 100644 --- a/packages/trie/examples/level.js +++ b/packages/trie/examples/level.js @@ -3,7 +3,7 @@ const { MemoryLevel } = require('memory-level') const { Trie } = require('../dist') -const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } +const ENCODING_OPTS = { keyEncoding: 'view', valueEncoding: 'view' } class LevelDB { _leveldb diff --git a/packages/trie/recipes/level-legacy.ts b/packages/trie/recipes/level-legacy.ts index c06c008e0a0..0040f872693 100644 --- a/packages/trie/recipes/level-legacy.ts +++ b/packages/trie/recipes/level-legacy.ts @@ -12,8 +12,8 @@ export class LevelDB implements DB { this._leveldb = leveldb ?? level() } - async get(key: Buffer): Promise { - let value: Buffer | null = null + async get(key: Uint8Array): Promise { + let value: Uint8Array | null = null try { value = await this._leveldb.get(key, ENCODING_OPTS) } catch (error: any) { @@ -27,11 +27,11 @@ export class LevelDB implements DB { return value } - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { await this._leveldb.put(key, val, ENCODING_OPTS) } - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._leveldb.del(key, ENCODING_OPTS) } diff --git a/packages/trie/recipes/level.ts b/packages/trie/recipes/level.ts index 62ac888cdb4..6a7744a441a 100644 --- a/packages/trie/recipes/level.ts +++ b/packages/trie/recipes/level.ts @@ -6,16 +6,24 @@ import type { AbstractLevel } from 'abstract-level' const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } export class LevelDB implements DB { - readonly _leveldb: AbstractLevel + readonly _leveldb: AbstractLevel< + string | Uint8Array | Uint8Array, + string | Uint8Array, + string | Uint8Array + > constructor( - leveldb?: AbstractLevel | null + leveldb?: AbstractLevel< + string | Uint8Array | Uint8Array, + string | Uint8Array, + string | Uint8Array + > | null ) { this._leveldb = leveldb ?? new MemoryLevel(ENCODING_OPTS) } - async get(key: Buffer): Promise { - let value: Buffer | null = null + async get(key: Uint8Array): Promise { + let value: Uint8Array | null = null try { value = await this._leveldb.get(key, ENCODING_OPTS) } catch (error: any) { @@ -29,11 +37,11 @@ export class LevelDB implements DB { return value } - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { await this._leveldb.put(key, val, ENCODING_OPTS) } - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._leveldb.del(key, ENCODING_OPTS) } diff --git a/packages/trie/recipes/lmdb.ts b/packages/trie/recipes/lmdb.ts index 91fc693b51e..07d2ba4290e 100644 --- a/packages/trie/recipes/lmdb.ts +++ b/packages/trie/recipes/lmdb.ts @@ -15,15 +15,15 @@ export class LMDB implements DB { }) } - async get(key: Buffer): Promise { + async get(key: Uint8Array): Promise { return this._database.get(key) } - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { await this._database.put(key, val) } - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._database.remove(key) } diff --git a/packages/trie/src/db/checkpoint.ts b/packages/trie/src/db/checkpoint.ts index 4a7a0b2218d..26c13e101f1 100644 --- a/packages/trie/src/db/checkpoint.ts +++ b/packages/trie/src/db/checkpoint.ts @@ -1,3 +1,5 @@ +import { bytesToHex, hexStringToBytes } from '@ethereumjs/util' + import type { BatchDBOp, Checkpoint, DB } from '../types' /** @@ -43,8 +45,8 @@ export class CheckpointDB implements DB { * Adds a new checkpoint to the stack * @param root */ - checkpoint(root: Buffer) { - this.checkpoints.push({ keyValueMap: new Map(), root }) + checkpoint(root: Uint8Array) { + this.checkpoints.push({ keyValueMap: new Map(), root }) } /** @@ -59,12 +61,12 @@ export class CheckpointDB implements DB { if (value === null) { batchOp.push({ type: 'del', - key: Buffer.from(key, 'binary'), + key: hexStringToBytes(key), }) } else { batchOp.push({ type: 'put', - key: Buffer.from(key, 'binary'), + key: hexStringToBytes(key), value, }) } @@ -90,10 +92,10 @@ export class CheckpointDB implements DB { /** * @inheritDoc */ - async get(key: Buffer): Promise { + async get(key: Uint8Array): Promise { // Lookup the value in our cache. We return the latest checkpointed value (which should be the value on disk) for (let index = this.checkpoints.length - 1; index >= 0; index--) { - const value = this.checkpoints[index].keyValueMap.get(key.toString('binary')) + const value = this.checkpoints[index].keyValueMap.get(bytesToHex(key)) if (value !== undefined) { return value } @@ -103,7 +105,7 @@ export class CheckpointDB implements DB { const value = await this.db.get(key) if (this.hasCheckpoints()) { // Since we are a checkpoint, put this value in cache, so future `get` calls will not look the key up again from disk. - this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), value) + this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(bytesToHex(key), value) } return value @@ -112,10 +114,10 @@ export class CheckpointDB implements DB { /** * @inheritDoc */ - async put(key: Buffer, val: Buffer): Promise { + async put(key: Uint8Array, val: Uint8Array): Promise { if (this.hasCheckpoints()) { // put value in cache - this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), val) + this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(bytesToHex(key), val) } else { await this.db.put(key, val) } @@ -124,10 +126,10 @@ export class CheckpointDB implements DB { /** * @inheritDoc */ - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { if (this.hasCheckpoints()) { // delete the value in the current cache - this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(key.toString('binary'), null) + this.checkpoints[this.checkpoints.length - 1].keyValueMap.set(bytesToHex(key), null) } else { // delete the value on disk await this.db.del(key) diff --git a/packages/trie/src/db/map.ts b/packages/trie/src/db/map.ts index c272264320e..1a45b2879b4 100644 --- a/packages/trie/src/db/map.ts +++ b/packages/trie/src/db/map.ts @@ -1,14 +1,16 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' + import type { BatchDBOp, DB } from '../types' export class MapDB implements DB { - _database: Map + _database: Map - constructor(database?: Map) { + constructor(database?: Map) { this._database = database ?? new Map() } - async get(key: Buffer): Promise { - const result = this._database.get(key.toString('hex')) + async get(key: Uint8Array): Promise { + const result = this._database.get(bytesToHex(key)) if (result !== undefined) { return result @@ -17,12 +19,12 @@ export class MapDB implements DB { return null } - async put(key: Buffer, val: Buffer): Promise { - this._database.set(key.toString('hex'), val) + async put(key: Uint8Array, val: Uint8Array): Promise { + this._database.set(bytesToHex(key), val) } - async del(key: Buffer): Promise { - this._database.delete(key.toString('hex')) + async del(key: Uint8Array): Promise { + this._database.delete(bytesToHex(key)) } async batch(opStack: BatchDBOp[]): Promise { diff --git a/packages/trie/src/proof/range.ts b/packages/trie/src/proof/range.ts index 8e734e49d7e..9e370a07f21 100644 --- a/packages/trie/src/proof/range.ts +++ b/packages/trie/src/proof/range.ts @@ -1,5 +1,7 @@ +import { equalsBytes } from 'ethereum-cryptography/utils' + import { BranchNode, ExtensionNode, LeafNode, Trie } from '../trie' -import { nibblesCompare, nibblesToBuffer } from '../util/nibbles' +import { nibblesCompare, nibblestoBytes } from '../util/nibbles' import type { HashKeysFunction, Nibbles, TrieNode } from '../types' @@ -160,8 +162,8 @@ async function unsetInternal(trie: Trie, left: Nibbles, right: Nibbles): Promise } // Stop searching if `left` and `right` are not equal - if (!(leftNode instanceof Buffer)) { - if (rightNode instanceof Buffer) { + if (!(leftNode instanceof Uint8Array)) { + if (rightNode instanceof Uint8Array) { break } @@ -171,7 +173,7 @@ async function unsetInternal(trie: Trie, left: Nibbles, right: Nibbles): Promise let abort = false for (let i = 0; i < leftNode.length; i++) { - if (leftNode[i].compare(rightNode[i]) !== 0) { + if (!equalsBytes(leftNode[i], rightNode[i])) { abort = true break } @@ -180,11 +182,11 @@ async function unsetInternal(trie: Trie, left: Nibbles, right: Nibbles): Promise break } } else { - if (!(rightNode instanceof Buffer)) { + if (!(rightNode instanceof Uint8Array)) { break } - if (leftNode.compare(rightNode) !== 0) { + if (!equalsBytes(leftNode, rightNode)) { break } } @@ -314,11 +316,11 @@ async function unsetInternal(trie: Trie, left: Nibbles, right: Nibbles): Promise * @returns The value from the key, or null if valid proof of non-existence. */ async function verifyProof( - rootHash: Buffer, - key: Buffer, - proof: Buffer[], + rootHash: Uint8Array, + key: Uint8Array, + proof: Uint8Array[], useKeyHashingFunction: HashKeysFunction -): Promise<{ value: Buffer | null; trie: Trie }> { +): Promise<{ value: Uint8Array | null; trie: Trie }> { const proofTrie = new Trie({ root: rootHash, useKeyHashingFunction }) try { await proofTrie.fromProof(proof) @@ -408,12 +410,12 @@ async function hasRightElement(trie: Trie, key: Nibbles): Promise { * @returns a flag to indicate whether there exists more trie node in the trie */ export async function verifyRangeProof( - rootHash: Buffer, + rootHash: Uint8Array, firstKey: Nibbles | null, lastKey: Nibbles | null, keys: Nibbles[], - values: Buffer[], - proof: Buffer[] | null, + values: Uint8Array[], + proof: Uint8Array[] | null, useKeyHashingFunction: HashKeysFunction ): Promise { if (keys.length !== values.length) { @@ -437,9 +439,9 @@ export async function verifyRangeProof( if (proof === null && firstKey === null && lastKey === null) { const trie = new Trie({ useKeyHashingFunction }) for (let i = 0; i < keys.length; i++) { - await trie.put(nibblesToBuffer(keys[i]), values[i]) + await trie.put(nibblestoBytes(keys[i]), values[i]) } - if (rootHash.compare(trie.root()) !== 0) { + if (!equalsBytes(rootHash, trie.root())) { throw new Error('invalid all elements proof: root mismatch') } return false @@ -455,7 +457,7 @@ export async function verifyRangeProof( if (keys.length === 0) { const { trie, value } = await verifyProof( rootHash, - nibblesToBuffer(firstKey), + nibblestoBytes(firstKey), proof, useKeyHashingFunction ) @@ -471,7 +473,7 @@ export async function verifyRangeProof( if (keys.length === 1 && nibblesCompare(firstKey, lastKey) === 0) { const { trie, value } = await verifyProof( rootHash, - nibblesToBuffer(firstKey), + nibblestoBytes(firstKey), proof, useKeyHashingFunction ) @@ -479,7 +481,7 @@ export async function verifyRangeProof( if (nibblesCompare(firstKey, keys[0]) !== 0) { throw new Error('invalid one element proof: firstKey should be equal to keys[0]') } - if (value === null || value.compare(values[0]) !== 0) { + if (value === null || !equalsBytes(value, values[0])) { throw new Error('invalid one element proof: value mismatch') } @@ -507,11 +509,11 @@ export async function verifyRangeProof( // Put all elements to the trie for (let i = 0; i < keys.length; i++) { - await trie.put(nibblesToBuffer(keys[i]), values[i]) + await trie.put(nibblestoBytes(keys[i]), values[i]) } // Compare rootHash - if (trie.root().compare(rootHash) !== 0) { + if (!equalsBytes(trie.root(), rootHash)) { throw new Error('invalid two edge elements proof: root mismatch') } diff --git a/packages/trie/src/trie/node/branch.ts b/packages/trie/src/trie/node/branch.ts index c0b50679db7..f9945d17e4f 100644 --- a/packages/trie/src/trie/node/branch.ts +++ b/packages/trie/src/trie/node/branch.ts @@ -1,25 +1,24 @@ import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' import type { EmbeddedNode } from '../../types' export class BranchNode { _branches: (EmbeddedNode | null)[] - _value: Buffer | null + _value: Uint8Array | null constructor() { this._branches = new Array(16).fill(null) this._value = null } - static fromArray(arr: Buffer[]): BranchNode { + static fromArray(arr: Uint8Array[]): BranchNode { const node = new BranchNode() node._branches = arr.slice(0, 16) node._value = arr[16] return node } - value(v?: Buffer | null): Buffer | null { + value(v?: Uint8Array | null): Uint8Array | null { if (v !== null && v !== undefined) { this._value = v } @@ -35,8 +34,8 @@ export class BranchNode { return [...this._branches, this._value] } - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw() as Buffer[]))) + serialize(): Uint8Array { + return RLP.encode(this.raw() as Uint8Array[]) } getBranch(i: number) { diff --git a/packages/trie/src/trie/node/extension.ts b/packages/trie/src/trie/node/extension.ts index 75615868b38..8cfbaf1020a 100644 --- a/packages/trie/src/trie/node/extension.ts +++ b/packages/trie/src/trie/node/extension.ts @@ -5,7 +5,7 @@ import { Node } from './node' import type { Nibbles } from '../../types' export class ExtensionNode extends Node { - constructor(nibbles: Nibbles, value: Buffer) { + constructor(nibbles: Nibbles, value: Uint8Array) { super(nibbles, value, false) } diff --git a/packages/trie/src/trie/node/leaf.ts b/packages/trie/src/trie/node/leaf.ts index 198c3ee4e22..64b3c53a4a8 100644 --- a/packages/trie/src/trie/node/leaf.ts +++ b/packages/trie/src/trie/node/leaf.ts @@ -5,7 +5,7 @@ import { Node } from './node' import type { Nibbles } from '../../types' export class LeafNode extends Node { - constructor(nibbles: Nibbles, value: Buffer) { + constructor(nibbles: Nibbles, value: Uint8Array) { super(nibbles, value, true) } diff --git a/packages/trie/src/trie/node/node.ts b/packages/trie/src/trie/node/node.ts index a24623bcd67..e5b3ea5bcf9 100644 --- a/packages/trie/src/trie/node/node.ts +++ b/packages/trie/src/trie/node/node.ts @@ -1,17 +1,16 @@ import { RLP } from '@ethereumjs/rlp' -import { bufArrToArr } from '@ethereumjs/util' import { addHexPrefix, removeHexPrefix } from '../../util/hex' -import { nibblesToBuffer } from '../../util/nibbles' +import { nibblestoBytes } from '../../util/nibbles' import type { Nibbles } from '../../types' export class Node { _nibbles: Nibbles - _value: Buffer + _value: Uint8Array _terminator: boolean - constructor(nibbles: Nibbles, value: Buffer, terminator: boolean) { + constructor(nibbles: Nibbles, value: Uint8Array, terminator: boolean) { this._nibbles = nibbles this._value = value this._terminator = terminator @@ -33,7 +32,7 @@ export class Node { return this._nibbles.length } - value(v?: Buffer) { + value(v?: Uint8Array) { if (v !== undefined) { this._value = v } @@ -45,11 +44,11 @@ export class Node { return addHexPrefix(this._nibbles.slice(0), this._terminator) } - raw(): [Buffer, Buffer] { - return [nibblesToBuffer(this.encodedKey()), this._value] + raw(): [Uint8Array, Uint8Array] { + return [nibblestoBytes(this.encodedKey()), this._value] } - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw()))) + serialize(): Uint8Array { + return RLP.encode(this.raw()) } } diff --git a/packages/trie/src/trie/node/util.ts b/packages/trie/src/trie/node/util.ts index 8a9ac0946d2..10f471655d2 100644 --- a/packages/trie/src/trie/node/util.ts +++ b/packages/trie/src/trie/node/util.ts @@ -1,18 +1,17 @@ import { RLP } from '@ethereumjs/rlp' -import { arrToBufArr } from '@ethereumjs/util' import { isTerminator } from '../../util/hex' -import { bufferToNibbles } from '../../util/nibbles' +import { bytesToNibbles } from '../../util/nibbles' import { BranchNode } from './branch' import { ExtensionNode } from './extension' import { LeafNode } from './leaf' -export function decodeRawNode(raw: Buffer[]) { +export function decodeRawNode(raw: Uint8Array[]) { if (raw.length === 17) { return BranchNode.fromArray(raw) } else if (raw.length === 2) { - const nibbles = bufferToNibbles(raw[0]) + const nibbles = bytesToNibbles(raw[0]) if (isTerminator(nibbles)) { return new LeafNode(LeafNode.decodeKey(nibbles), raw[1]) } @@ -22,8 +21,8 @@ export function decodeRawNode(raw: Buffer[]) { } } -export function decodeNode(raw: Buffer) { - const des = arrToBufArr(RLP.decode(Uint8Array.from(raw))) as Buffer[] +export function decodeNode(raw: Uint8Array) { + const des = RLP.decode(Uint8Array.from(raw)) as Uint8Array[] if (!Array.isArray(des)) { throw new Error('Invalid node') } @@ -31,5 +30,5 @@ export function decodeNode(raw: Buffer) { } export function isRawNode(n: any) { - return Array.isArray(n) && !Buffer.isBuffer(n) + return Array.isArray(n) && !(n instanceof Uint8Array) } diff --git a/packages/trie/src/trie/trie.ts b/packages/trie/src/trie/trie.ts index dd2bf0e9491..dafa5b61596 100644 --- a/packages/trie/src/trie/trie.ts +++ b/packages/trie/src/trie/trie.ts @@ -1,11 +1,11 @@ -import { RLP_EMPTY_STRING } from '@ethereumjs/util' +import { RLP_EMPTY_STRING, bytesToHex, bytesToUtf8, equalsBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { CheckpointDB, MapDB } from '../db' import { verifyRangeProof } from '../proof/range' import { ROOT_DB_KEY } from '../types' import { Lock } from '../util/lock' -import { bufferToNibbles, doKeysMatch, matchingNibbleLength } from '../util/nibbles' +import { bytesToNibbles, doKeysMatch, matchingNibbleLength } from '../util/nibbles' import { TrieReadStream as ReadStream } from '../util/readStream' import { WalkController } from '../util/walkController' @@ -42,13 +42,13 @@ export class Trie { } /** The root for an empty trie */ - EMPTY_TRIE_ROOT: Buffer + EMPTY_TRIE_ROOT: Uint8Array /** The backend DB */ protected _db!: CheckpointDB protected _hashLen: number protected _lock = new Lock() - protected _root: Buffer + protected _root: Uint8Array /** * Creates a new trie. @@ -76,11 +76,9 @@ export class Trie { let key = ROOT_DB_KEY if (opts?.useKeyHashing === true) { - key = (opts?.useKeyHashingFunction ?? keccak256)(ROOT_DB_KEY) as Buffer + key = (opts?.useKeyHashingFunction ?? keccak256)(ROOT_DB_KEY) as Uint8Array } - key = Buffer.from(key) - if (opts?.db !== undefined && opts?.useRootPersistence === true) { if (opts?.root === undefined) { opts.root = (await opts?.db.get(key)) ?? undefined @@ -107,7 +105,7 @@ export class Trie { /** * Gets and/or Sets the current root of the `trie` */ - root(value?: Buffer | null): Buffer { + root(value?: Uint8Array | null): Uint8Array { if (value !== undefined) { if (value === null) { value = this.EMPTY_TRIE_ROOT @@ -126,7 +124,7 @@ export class Trie { /** * Checks if a given root exists. */ - async checkRoot(root: Buffer): Promise { + async checkRoot(root: Uint8Array): Promise { try { const value = await this.lookupNode(root) return value !== null @@ -143,11 +141,11 @@ export class Trie { * Gets a value given a `key` * @param key - the key to search for * @param throwIfMissing - if true, throws if any nodes are missing. Used for verifying proofs. (default: false) - * @returns A Promise that resolves to `Buffer` if a value was found or `null` if no value was found. + * @returns A Promise that resolves to `Uint8Array` if a value was found or `null` if no value was found. */ - async get(key: Buffer, throwIfMissing = false): Promise { + async get(key: Uint8Array, throwIfMissing = false): Promise { const { node, remaining } = await this.findPath(this.appliedKey(key), throwIfMissing) - let value: Buffer | null = null + let value: Uint8Array | null = null if (node && remaining.length === 0) { value = node.value() } @@ -161,9 +159,9 @@ export class Trie { * @param value * @returns A Promise that resolves once value is stored. */ - async put(key: Buffer, value: Buffer): Promise { - if (this._opts.useRootPersistence && key.equals(ROOT_DB_KEY)) { - throw new Error(`Attempted to set '${ROOT_DB_KEY.toString()}' key but it is not allowed.`) + async put(key: Uint8Array, value: Uint8Array): Promise { + if (this._opts.useRootPersistence && equalsBytes(key, ROOT_DB_KEY) === true) { + throw new Error(`Attempted to set '${bytesToUtf8(ROOT_DB_KEY)}' key but it is not allowed.`) } // If value is empty, delete @@ -173,7 +171,7 @@ export class Trie { await this._lock.acquire() const appliedKey = this.appliedKey(key) - if (this.root().equals(this.EMPTY_TRIE_ROOT)) { + if (equalsBytes(this.root(), this.EMPTY_TRIE_ROOT) === true) { // If no root, initialize this trie await this._createInitialNode(appliedKey, value) } else { @@ -184,7 +182,7 @@ export class Trie { const val = await this.get(key) // Only delete keys if it either does not exist, or if it gets updated // (The update will update the hash of the node, thus we can delete the original leaf node) - if (val === null || !val.equals(value)) { + if (val === null || equalsBytes(val, value) === false) { // All items of the stack are going to change. // (This is the path from the root node to wherever it needs to insert nodes) // The items change, because the leaf value is updated, thus all keyhashes in the @@ -215,7 +213,7 @@ export class Trie { * @param key * @returns A Promise that resolves once value is deleted. */ - async del(key: Buffer): Promise { + async del(key: Uint8Array): Promise { await this._lock.acquire() const appliedKey = this.appliedKey(key) const { node, stack } = await this.findPath(appliedKey) @@ -250,11 +248,11 @@ export class Trie { * @param key - the search key * @param throwIfMissing - if true, throws if any nodes are missing. Used for verifying proofs. (default: false) */ - async findPath(key: Buffer, throwIfMissing = false): Promise { + async findPath(key: Uint8Array, throwIfMissing = false): Promise { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { const stack: TrieNode[] = [] - const targetKey = bufferToNibbles(key) + const targetKey = bytesToNibbles(key) const onFound: FoundNodeFunction = async (_, node, keyProgress, walkController) => { if (node === null) { @@ -321,7 +319,7 @@ export class Trie { * @param onFound - callback to call when a node is found. This schedules new tasks. If no tasks are available, the Promise resolves. * @returns Resolves when finished walking trie. */ - async walkTrie(root: Buffer, onFound: FoundNodeFunction): Promise { + async walkTrie(root: Uint8Array, onFound: FoundNodeFunction): Promise { await WalkController.newWalk(onFound, this, root) } @@ -329,8 +327,8 @@ export class Trie { * Creates the initial node from an empty tree. * @private */ - async _createInitialNode(key: Buffer, value: Buffer): Promise { - const newNode = new LeafNode(bufferToNibbles(key), value) + async _createInitialNode(key: Uint8Array, value: Uint8Array): Promise { + const newNode = new LeafNode(bytesToNibbles(key), value) const encoded = newNode.serialize() this.root(this.hash(encoded)) @@ -341,13 +339,13 @@ export class Trie { /** * Retrieves a node from db by hash. */ - async lookupNode(node: Buffer | Buffer[]): Promise { + async lookupNode(node: Uint8Array | Uint8Array[]): Promise { if (isRawNode(node)) { - return decodeRawNode(node as Buffer[]) + return decodeRawNode(node as Uint8Array[]) } let value = null let foundNode = null - value = await this._db.get(node as Buffer) + value = await this._db.get(node as Uint8Array) if (value) { foundNode = decodeNode(value) } else { @@ -366,8 +364,8 @@ export class Trie { * @param stack */ async _updateNode( - k: Buffer, - value: Buffer, + k: Uint8Array, + value: Uint8Array, keyRemainder: Nibbles, stack: TrieNode[] ): Promise { @@ -378,7 +376,7 @@ export class Trie { } // add the new nodes - const key = bufferToNibbles(k) + const key = bytesToNibbles(k) // Check if the last node is a leaf and the key matches to this let matchLeaf = false @@ -468,7 +466,7 @@ export class Trie { * Deletes a node from the trie. * @private */ - async _deleteNode(k: Buffer, stack: TrieNode[]): Promise { + async _deleteNode(k: Uint8Array, stack: TrieNode[]): Promise { const processBranchNode = ( key: Nibbles, branchKey: number, @@ -531,7 +529,7 @@ export class Trie { let parentNode = stack.pop() const opStack: BatchDBOp[] = [] - let key = bufferToNibbles(k) + let key = bytesToNibbles(k) if (!parentNode) { // the root here has to be a leaf. @@ -575,7 +573,7 @@ export class Trie { if (this._opts.useNodePruning) { opStack.push({ type: 'del', - key: branchNode as Buffer, + key: branchNode as Uint8Array, }) } @@ -628,7 +626,7 @@ export class Trie { node.setBranch(branchKey!, lastRoot) } } - lastRoot = this._formatNode(node, stack.length === 0, opStack) as Buffer + lastRoot = this._formatNode(node, stack.length === 0, opStack) as Uint8Array } if (lastRoot) { @@ -653,11 +651,11 @@ export class Trie { topLevel: boolean, opStack: BatchDBOp[], remove: boolean = false - ): Buffer | (EmbeddedNode | null)[] { + ): Uint8Array | (EmbeddedNode | null)[] { const encoded = node.serialize() if (encoded.length >= 32 || topLevel) { - const hashRoot = Buffer.from(this.hash(encoded)) + const hashRoot = this.hash(encoded) if (remove) { if (this._opts.useNodePruning) { @@ -685,11 +683,11 @@ export class Trie { * (delete operations are only executed on DB with `deleteFromDB` set to `true`) * @example * const ops = [ - * { type: 'del', key: Buffer.from('father') } - * , { type: 'put', key: Buffer.from('name'), value: Buffer.from('Yuri Irsenovich Kim') } - * , { type: 'put', key: Buffer.from('dob'), value: Buffer.from('16 February 1941') } - * , { type: 'put', key: Buffer.from('spouse'), value: Buffer.from('Kim Young-sook') } - * , { type: 'put', key: Buffer.from('occupation'), value: Buffer.from('Clown') } + * { type: 'del', key: Uint8Array.from('father') } + * , { type: 'put', key: Uint8Array.from('name'), value: Uint8Array.from('Yuri Irsenovich Kim') } + * , { type: 'put', key: Uint8Array.from('dob'), value: Uint8Array.from('16 February 1941') } + * , { type: 'put', key: Uint8Array.from('spouse'), value: Uint8Array.from('Kim Young-sook') } + * , { type: 'put', key: Uint8Array.from('occupation'), value: Uint8Array.from('Clown') } * ] * await trie.batch(ops) * @param ops @@ -716,7 +714,7 @@ export class Trie { const opStack = proof.map((nodeValue) => { return { type: 'put', - key: Buffer.from(this.hash(nodeValue)), + key: Uint8Array.from(this.hash(nodeValue)), value: nodeValue, } as PutBatch }) @@ -734,7 +732,7 @@ export class Trie { * Creates a proof from a trie and key that can be verified using {@link Trie.verifyProof}. * @param key */ - async createProof(key: Buffer): Promise { + async createProof(key: Uint8Array): Promise { const { stack } = await this.findPath(this.appliedKey(key)) const p = stack.map((stackElem) => { return stackElem.serialize() @@ -750,7 +748,11 @@ export class Trie { * @throws If proof is found to be invalid. * @returns The value from the key, or null if valid proof of non-existence. */ - async verifyProof(rootHash: Buffer, key: Buffer, proof: Proof): Promise { + async verifyProof( + rootHash: Uint8Array, + key: Uint8Array, + proof: Proof + ): Promise { const proofTrie = new Trie({ root: rootHash, useKeyHashingFunction: this._opts.useKeyHashingFunction, @@ -776,18 +778,18 @@ export class Trie { * {@link verifyRangeProof} */ verifyRangeProof( - rootHash: Buffer, - firstKey: Buffer | null, - lastKey: Buffer | null, - keys: Buffer[], - values: Buffer[], - proof: Buffer[] | null + rootHash: Uint8Array, + firstKey: Uint8Array | null, + lastKey: Uint8Array | null, + keys: Uint8Array[], + values: Uint8Array[], + proof: Uint8Array[] | null ): Promise { return verifyRangeProof( rootHash, - firstKey && bufferToNibbles(this.appliedKey(firstKey)), - lastKey && bufferToNibbles(this.appliedKey(lastKey)), - keys.map((k) => this.appliedKey(k)).map(bufferToNibbles), + firstKey && bytesToNibbles(this.appliedKey(firstKey)), + lastKey && bytesToNibbles(this.appliedKey(lastKey)), + keys.map((k) => this.appliedKey(k)).map(bytesToNibbles), values, proof, this._opts.useKeyHashingFunction @@ -799,7 +801,7 @@ export class Trie { // (i.e. the Trie is not correctly pruned) // If this method returns `true`, the Trie is correctly pruned and all keys are reachable async verifyPrunedIntegrity(): Promise { - const roots = [this.root().toString('hex'), this.appliedKey(ROOT_DB_KEY).toString('hex')] + const roots = [bytesToHex(this.root()), bytesToHex(this.appliedKey(ROOT_DB_KEY))] for (const dbkey of (this)._db.db._database.keys()) { if (roots.includes(dbkey)) { // The root key can never be found from the trie, otherwise this would @@ -818,7 +820,7 @@ export class Trie { if (node instanceof BranchNode) { for (const item of node._branches) { // If one of the branches matches the key, then it is found - if (item && item.toString('hex') === dbkey) { + if (item !== null && bytesToHex(item as Uint8Array) === dbkey) { found = true return } @@ -828,7 +830,7 @@ export class Trie { } if (node instanceof ExtensionNode) { // If the value of the ExtensionNode points to the dbkey, then it is found - if (node.value().toString('hex') === dbkey) { + if (bytesToHex(node.value()) === dbkey) { found = true return } @@ -846,7 +848,7 @@ export class Trie { } /** - * The `data` event is given an `Object` that has two properties; the `key` and the `value`. Both should be Buffers. + * The `data` event is given an `Object` that has two properties; the `key` and the `value`. Both should be Uint8Arrays. * @return Returns a [stream](https://nodejs.org/dist/latest-v12.x/docs/api/stream.html#stream_class_stream_readable) of the contents of the `trie` */ createReadStream(): ReadStream { @@ -902,15 +904,15 @@ export class Trie { * depending on the `useKeyHashing` option being set or not. * @param key */ - protected appliedKey(key: Buffer) { + protected appliedKey(key: Uint8Array) { if (this._opts.useKeyHashing) { return this.hash(key) } return key } - protected hash(msg: Uint8Array): Buffer { - return Buffer.from(this._opts.useKeyHashingFunction(msg)) + protected hash(msg: Uint8Array): Uint8Array { + return Uint8Array.from(this._opts.useKeyHashingFunction(msg)) } /** diff --git a/packages/trie/src/types.ts b/packages/trie/src/types.ts index 5a73e08b582..68cb4cd0c08 100644 --- a/packages/trie/src/types.ts +++ b/packages/trie/src/types.ts @@ -1,3 +1,5 @@ +import { utf8ToBytes } from 'ethereum-cryptography/utils' + import type { BranchNode, ExtensionNode, LeafNode } from './trie' import type { WalkController } from './util/walkController' @@ -7,12 +9,12 @@ export type Nibbles = number[] // Branch and extension nodes might store // hash to next node, or embed it if its len < 32 -export type EmbeddedNode = Buffer | Buffer[] +export type EmbeddedNode = Uint8Array | Uint8Array[] -export type Proof = Buffer[] +export type Proof = Uint8Array[] export type FoundNodeFunction = ( - nodeRef: Buffer, + nodeRef: Uint8Array, node: TrieNode | null, key: Nibbles, walkController: WalkController @@ -27,9 +29,9 @@ export interface TrieOpts { db?: DB /** - * A `Buffer` for the root of a previously stored trie + * A `Uint8Array` for the root of a previously stored trie */ - root?: Buffer + root?: Uint8Array /** * Create as a secure Trie where the keys are automatically hashed using the @@ -73,35 +75,35 @@ export type BatchDBOp = PutBatch | DelBatch export interface PutBatch { type: 'put' - key: Buffer - value: Buffer + key: Uint8Array + value: Uint8Array } export interface DelBatch { type: 'del' - key: Buffer + key: Uint8Array } export interface DB { /** * Retrieves a raw value from leveldb. * @param key - * @returns A Promise that resolves to `Buffer` if a value is found or `null` if no value is found. + * @returns A Promise that resolves to `Uint8Array` if a value is found or `null` if no value is found. */ - get(key: Buffer): Promise + get(key: Uint8Array): Promise /** * Writes a value directly to leveldb. - * @param key The key as a `Buffer` + * @param key The key as a `Uint8Array` * @param value The value to be stored */ - put(key: Buffer, val: Buffer): Promise + put(key: Uint8Array, val: Uint8Array): Promise /** * Removes a raw value in the underlying leveldb. * @param keys */ - del(key: Buffer): Promise + del(key: Uint8Array): Promise /** * Performs a batch operation on db. @@ -117,10 +119,10 @@ export interface DB { } export type Checkpoint = { - // We cannot use a Buffer => Buffer map directly. If you create two Buffers with the same internal value, + // We cannot use a Uint8Array => Uint8Array map directly. If you create two Uint8Arrays with the same internal value, // then when setting a value on the Map, it actually creates two indices. - keyValueMap: Map - root: Buffer + keyValueMap: Map + root: Uint8Array } -export const ROOT_DB_KEY = Buffer.from('__root__') +export const ROOT_DB_KEY = utf8ToBytes('__root__') diff --git a/packages/trie/src/util/nibbles.ts b/packages/trie/src/util/nibbles.ts index fd7fbefc5d7..944909d17b5 100644 --- a/packages/trie/src/util/nibbles.ts +++ b/packages/trie/src/util/nibbles.ts @@ -1,13 +1,15 @@ +import { toBytes } from '@ethereumjs/util' + import type { Nibbles } from '../types' /** - * Converts a buffer to a nibble array. + * Converts a bytes to a nibble array. * @private * @param key */ -export function bufferToNibbles(key: Buffer): Nibbles { - const bkey = Buffer.from(key) - const nibbles = [] as any +export function bytesToNibbles(key: Uint8Array): Nibbles { + const bkey = toBytes(key) + const nibbles = [] as Nibbles for (let i = 0; i < bkey.length; i++) { let q = i * 2 @@ -20,12 +22,12 @@ export function bufferToNibbles(key: Buffer): Nibbles { } /** - * Converts a nibble array into a buffer. + * Converts a nibble array into bytes. * @private * @param arr - Nibble array */ -export function nibblesToBuffer(arr: Nibbles): Buffer { - const buf = Buffer.alloc(arr.length / 2) +export function nibblestoBytes(arr: Nibbles): Uint8Array { + const buf = new Uint8Array(arr.length / 2) for (let i = 0; i < buf.length; i++) { let q = i * 2 buf[i] = (arr[q] << 4) + arr[++q] diff --git a/packages/trie/src/util/readStream.ts b/packages/trie/src/util/readStream.ts index 76d31f4986e..70ea90cf2cd 100644 --- a/packages/trie/src/util/readStream.ts +++ b/packages/trie/src/util/readStream.ts @@ -2,7 +2,7 @@ import { Readable } from 'readable-stream' import { BranchNode, LeafNode } from '../trie' -import { nibblesToBuffer } from './nibbles' +import { nibblestoBytes } from './nibbles' import type { Trie } from '../trie' import type { FoundNodeFunction } from '../types' @@ -27,7 +27,7 @@ export class TrieReadStream extends Readable { await this._findValueNodes(async (_, node, key, walkController) => { if (node !== null) { this.push({ - key: nibblesToBuffer(key), + key: nibblestoBytes(key), value: node.value(), }) walkController.allChildren(node, key) diff --git a/packages/trie/src/util/walkController.ts b/packages/trie/src/util/walkController.ts index 9d0301f5345..12b566e1ecf 100644 --- a/packages/trie/src/util/walkController.ts +++ b/packages/trie/src/util/walkController.ts @@ -39,14 +39,14 @@ export class WalkController { static async newWalk( onNode: FoundNodeFunction, trie: Trie, - root: Buffer, + root: Uint8Array, poolSize?: number ): Promise { const strategy = new WalkController(onNode, trie, poolSize ?? 500) await strategy.startWalk(root) } - private async startWalk(root: Buffer): Promise { + private async startWalk(root: Uint8Array): Promise { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { this.resolve = resolve @@ -81,7 +81,7 @@ export class WalkController { } for (const child of children) { const keyExtension = child[0] as Nibbles - const childRef = child[1] as Buffer + const childRef = child[1] as Uint8Array const childKey = key.concat(keyExtension) const priority = childKey.length this.pushNodeToQueue(childRef, childKey, priority) @@ -94,7 +94,7 @@ export class WalkController { * @param key - The current key. * @param priority - Optional priority, defaults to key length */ - pushNodeToQueue(nodeRef: Buffer, key: Nibbles = [], priority?: number) { + pushNodeToQueue(nodeRef: Uint8Array, key: Nibbles = [], priority?: number) { this.taskExecutor.executeOrQueue( priority ?? key.length, async (taskFinishedCallback: Function) => { @@ -105,7 +105,7 @@ export class WalkController { return this.reject(error) } taskFinishedCallback() // this marks the current task as finished. If there are any tasks left in the queue, this will immediately execute the first task. - this.processNode(nodeRef as Buffer, childNode as TrieNode, key) + this.processNode(nodeRef as Uint8Array, childNode as TrieNode, key) } ) } @@ -128,10 +128,10 @@ export class WalkController { const childKey = key.slice() // This copies the key to a new array. childKey.push(childIndex) const prio = priority ?? childKey.length - this.pushNodeToQueue(childRef as Buffer, childKey, prio) + this.pushNodeToQueue(childRef as Uint8Array, childKey, prio) } - private processNode(nodeRef: Buffer, node: TrieNode | null, key: Nibbles = []) { + private processNode(nodeRef: Uint8Array, node: TrieNode | null, key: Nibbles = []) { this.onNode(nodeRef, node, key, this) if (this.taskExecutor.finished()) { // onNode should schedule new tasks. If no tasks was added and the queue is empty, then we have finished our walk. diff --git a/packages/trie/test/db/checkpoint.spec.ts b/packages/trie/test/db/checkpoint.spec.ts index d164de487be..8514d2078b1 100644 --- a/packages/trie/test/db/checkpoint.spec.ts +++ b/packages/trie/test/db/checkpoint.spec.ts @@ -1,3 +1,4 @@ +import { hexStringToBytes, utf8ToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { CheckpointDB, MapDB } from '../../src' @@ -5,14 +6,14 @@ import { CheckpointDB, MapDB } from '../../src' import type { BatchDBOp } from '../../src' tape('DB tests', (t) => { - const k = Buffer.from('k1') - const v = Buffer.from('v1') - const v2 = Buffer.from('v2') - const v3 = Buffer.from('v3') + const k = utf8ToBytes('k1') + const v = utf8ToBytes('v1') + const v2 = utf8ToBytes('v2') + const v3 = utf8ToBytes('v3') t.test('Checkpointing: revert -> put (add)', async (st) => { const db = new CheckpointDB(new MapDB()) - db.checkpoint(Buffer.from('1', 'hex')) + db.checkpoint(hexStringToBytes('01')) await db.put(k, v) st.deepEqual(await db.get(k), v, 'before revert: v1') await db.revert() @@ -24,7 +25,7 @@ tape('DB tests', (t) => { const db = new CheckpointDB(new MapDB()) await db.put(k, v) st.deepEqual(await db.get(k), v, 'before CP: v1') - db.checkpoint(Buffer.from('1', 'hex')) + db.checkpoint(hexStringToBytes('01')) await db.put(k, v2) await db.put(k, v3) await db.revert() @@ -36,7 +37,7 @@ tape('DB tests', (t) => { const db = new CheckpointDB(new MapDB()) await db.put(k, v) st.deepEqual(await db.get(k), v, 'before CP: v1') - db.checkpoint(Buffer.from('1', 'hex')) + db.checkpoint(hexStringToBytes('01')) const ops = [ { type: 'put', key: k, value: v2 }, { type: 'put', key: k, value: v3 }, @@ -51,7 +52,7 @@ tape('DB tests', (t) => { const db = new CheckpointDB(new MapDB()) await db.put(k, v) st.deepEqual(await db.get(k), v, 'before CP: v1') - db.checkpoint(Buffer.from('1', 'hex')) + db.checkpoint(hexStringToBytes('01')) await db.del(k) st.deepEqual(await db.get(k), null, 'before revert: null') await db.revert() @@ -62,10 +63,11 @@ tape('DB tests', (t) => { t.test('Checkpointing: nested checkpoints -> commit -> revert', async (st) => { const db = new CheckpointDB(new MapDB()) await db.put(k, v) + st.deepEqual(await db.get(k), v, 'before CP: v1') - db.checkpoint(Buffer.from('1', 'hex')) + db.checkpoint(hexStringToBytes('01')) await db.put(k, v2) - db.checkpoint(Buffer.from('2', 'hex')) + db.checkpoint(hexStringToBytes('02')) await db.put(k, v3) await db.commit() st.deepEqual(await db.get(k), v3, 'after commit (second CP): v3') diff --git a/packages/trie/test/db/db.spec.ts b/packages/trie/test/db/db.spec.ts index 5798e9dedd5..0f3f1969e5a 100644 --- a/packages/trie/test/db/db.spec.ts +++ b/packages/trie/test/db/db.spec.ts @@ -1,3 +1,4 @@ +import { equalsBytes, utf8ToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { MapDB } from '../../src' @@ -7,15 +8,15 @@ import type { BatchDBOp } from '../../src' tape('DB tests', (t) => { const db = new MapDB() - const k = Buffer.from('k1') - const v = Buffer.from('v1') - const k2 = Buffer.from('k2') - const v2 = Buffer.from('v2') + const k = utf8ToBytes('k1') + const v = utf8ToBytes('v1') + const k2 = utf8ToBytes('k2') + const v2 = utf8ToBytes('v2') t.test('Operations: puts and gets value', async (st) => { await db.put(k, v) const res = await db.get(k) - st.ok(v.equals(res!)) + st.ok(equalsBytes(v, res!)) st.end() }) @@ -33,7 +34,7 @@ tape('DB tests', (t) => { ] as BatchDBOp[] await db.batch(ops) const res = await db.get(k2) - st.ok(v2.equals(res!)) + st.ok(equalsBytes(v2, res!)) st.end() }) }) diff --git a/packages/trie/test/encoding.spec.ts b/packages/trie/test/encoding.spec.ts index a55b8ea8743..84c3d19b18c 100644 --- a/packages/trie/test/encoding.spec.ts +++ b/packages/trie/test/encoding.spec.ts @@ -1,4 +1,4 @@ -import { toBuffer } from '@ethereumjs/util' +import { bytesToHex, hexStringToBytes, toBytes, utf8ToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Trie } from '../src' @@ -8,8 +8,8 @@ const trie2 = new Trie() const hex = 'FF44A3B3' tape('encoding hex prefixes', async function (t) { - await trie.put(Buffer.from(hex, 'hex'), Buffer.from('test')) - await trie2.put(toBuffer(`0x${hex}`), Buffer.from('test')) - t.equal(trie.root().toString('hex'), trie2.root().toString('hex')) + await trie.put(hexStringToBytes(hex), utf8ToBytes('test')) + await trie2.put(toBytes(`0x${hex}`), utf8ToBytes('test')) + t.equal(bytesToHex(trie.root()), bytesToHex(trie2.root())) t.end() }) diff --git a/packages/trie/test/index.spec.ts b/packages/trie/test/index.spec.ts index c4f97e3d147..65b7ab0544e 100644 --- a/packages/trie/test/index.spec.ts +++ b/packages/trie/test/index.spec.ts @@ -1,14 +1,18 @@ -// explicitly import buffer, -// needed for karma-typescript bundling import { RLP } from '@ethereumjs/rlp' -import { KECCAK256_NULL, KECCAK256_RLP_S, bufArrToArr } from '@ethereumjs/util' -import { Buffer } from 'buffer' +import { + KECCAK256_NULL, + KECCAK256_RLP_S, + bytesToHex, + hexStringToBytes, + utf8ToBytes, +} from '@ethereumjs/util' import { blake2b } from 'ethereum-cryptography/blake2b' import { keccak256 } from 'ethereum-cryptography/keccak' +import { bytesToUtf8, concatBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { LeafNode, Trie } from '../src' -import { bufferToNibbles } from '../src/util/nibbles' +import { bytesToNibbles } from '../src/util/nibbles' import type { HashKeysFunction } from '../src' @@ -16,12 +20,12 @@ tape('simple save and retrieve', function (tester) { const it = tester.test it('should not crash if given a non-existent root', async function (t) { - const root = Buffer.from( - '3f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817d', - 'hex' + const root = hexStringToBytes( + '3f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817d' ) + const trie = new Trie({ root }) - const value = await trie.get(Buffer.from('test')) + const value = await trie.get(utf8ToBytes('test')) t.equal(value, null) t.end() }) @@ -29,59 +33,60 @@ tape('simple save and retrieve', function (tester) { const trie = new Trie() it('save a value', async function (t) { - await trie.put(Buffer.from('test'), Buffer.from('one')) + await trie.put(utf8ToBytes('test'), utf8ToBytes('one')) t.end() }) it('should get a value', async function (t) { - const value = await trie.get(Buffer.from('test')) - t.equal(value!.toString(), 'one') + const value = await trie.get(utf8ToBytes('test')) + t.equal(bytesToUtf8(value!), 'one') t.end() }) it('should update a value', async function (t) { - await trie.put(Buffer.from('test'), Buffer.from('two')) - const value = await trie.get(Buffer.from('test')) - t.equal(value!.toString(), 'two') + await trie.put(utf8ToBytes('test'), utf8ToBytes('two')) + const value = await trie.get(utf8ToBytes('test')) + + t.equal(bytesToUtf8(value!), 'two') t.end() }) it('should delete a value', async function (t) { - await trie.del(Buffer.from('test')) - const value = await trie.get(Buffer.from('test')) + await trie.del(utf8ToBytes('test')) + const value = await trie.get(utf8ToBytes('test')) t.notok(value) t.end() }) it('should recreate a value', async function (t) { - await trie.put(Buffer.from('test'), Buffer.from('one')) + await trie.put(utf8ToBytes('test'), utf8ToBytes('one')) t.end() }) it('should get updated a value', async function (t) { - const value = await trie.get(Buffer.from('test')) - t.equal(value!.toString(), 'one') + const value = await trie.get(utf8ToBytes('test')) + t.equal(bytesToUtf8(value!), 'one') t.end() }) it('should create a branch here', async function (t) { - await trie.put(Buffer.from('doge'), Buffer.from('coin')) + await trie.put(utf8ToBytes('doge'), utf8ToBytes('coin')) t.equal( 'de8a34a8c1d558682eae1528b47523a483dd8685d6db14b291451a66066bf0fc', - trie.root().toString('hex') + bytesToHex(trie.root()) ) t.end() }) it('should get a value that is in a branch', async function (t) { - const value = await trie.get(Buffer.from('doge')) - t.equal(value!.toString(), 'coin') + const value = await trie.get(utf8ToBytes('doge')) + t.equal(bytesToUtf8(value!), 'coin') t.end() }) it('should delete from a branch', async function (t) { - await trie.del(Buffer.from('doge')) - const value = await trie.get(Buffer.from('doge')) + await trie.del(utf8ToBytes('doge')) + const value = await trie.get(utf8ToBytes('doge')) t.equal(value, null) t.end() }) @@ -93,20 +98,20 @@ tape('simple save and retrieve', function (tester) { const longStringRoot = 'b173e2db29e79c78963cff5196f8a983fbe0171388972106b114ef7f5c24dfa3' it('should store a longer string', async function (t) { - await trie.put(Buffer.from('done'), Buffer.from(longString)) - await trie.put(Buffer.from('doge'), Buffer.from('coin')) - t.equal(longStringRoot, trie.root().toString('hex')) + await trie.put(utf8ToBytes('done'), utf8ToBytes(longString)) + await trie.put(utf8ToBytes('doge'), utf8ToBytes('coin')) + t.equal(longStringRoot, bytesToHex(trie.root())) t.end() }) it('should retrieve a longer value', async function (t) { - const value = await trie.get(Buffer.from('done')) - t.equal(value!.toString(), longString) + const value = await trie.get(utf8ToBytes('done')) + t.equal(bytesToUtf8(value!), longString) t.end() }) it('should when being modified delete the old value', async function (t) { - await trie.put(Buffer.from('done'), Buffer.from('test')) + await trie.put(utf8ToBytes('done'), utf8ToBytes('test')) t.end() }) }) @@ -116,24 +121,24 @@ tape('simple save and retrieve', function (tester) { const trie = new Trie() it('should store a value', async function (t) { - await trie.put(Buffer.from('doge'), Buffer.from('coin')) + await trie.put(utf8ToBytes('doge'), utf8ToBytes('coin')) t.end() }) it('should create extension to store this value', async function (t) { - await trie.put(Buffer.from('do'), Buffer.from('verb')) + await trie.put(utf8ToBytes('do'), utf8ToBytes('verb')) t.equal( 'f803dfcb7e8f1afd45e88eedb4699a7138d6c07b71243d9ae9bff720c99925f9', - trie.root().toString('hex') + bytesToHex(trie.root()) ) t.end() }) it('should store this value under the extension', async function (t) { - await trie.put(Buffer.from('done'), Buffer.from('finished')) + await trie.put(utf8ToBytes('done'), utf8ToBytes('finished')) t.equal( '409cff4d820b394ed3fb1cd4497bdd19ffa68d30ae34157337a7043c94a3e8cb', - trie.root().toString('hex') + bytesToHex(trie.root()) ) t.end() }) @@ -144,20 +149,20 @@ tape('simple save and retrieve', function (tester) { const trie = new Trie() it('should create extension to store this value', async function (t) { - await trie.put(Buffer.from('do'), Buffer.from('verb')) + await trie.put(utf8ToBytes('do'), utf8ToBytes('verb')) t.end() }) it('should store a value', async function (t) { - await trie.put(Buffer.from('doge'), Buffer.from('coin')) + await trie.put(utf8ToBytes('doge'), utf8ToBytes('coin')) t.end() }) it('should store this value under the extension', async function (t) { - await trie.put(Buffer.from('done'), Buffer.from('finished')) + await trie.put(utf8ToBytes('done'), utf8ToBytes('finished')) t.equal( '409cff4d820b394ed3fb1cd4497bdd19ffa68d30ae34157337a7043c94a3e8cb', - trie.root().toString('hex') + bytesToHex(trie.root()) ) t.end() }) @@ -172,51 +177,51 @@ tape('testing deletion cases', function (tester) { } it('should delete from a branch->branch-branch', async function (t) { - await trieSetup.trie.put(Buffer.from([11, 11, 11]), Buffer.from('first')) - await trieSetup.trie.put(Buffer.from([12, 22, 22]), Buffer.from('create the first branch')) - await trieSetup.trie.put(Buffer.from([12, 34, 44]), Buffer.from('create the last branch')) + await trieSetup.trie.put(new Uint8Array([11, 11, 11]), utf8ToBytes('first')) + await trieSetup.trie.put(new Uint8Array([12, 22, 22]), utf8ToBytes('create the first branch')) + await trieSetup.trie.put(new Uint8Array([12, 34, 44]), utf8ToBytes('create the last branch')) - await trieSetup.trie.del(Buffer.from([12, 22, 22])) - const val = await trieSetup.trie.get(Buffer.from([12, 22, 22])) + await trieSetup.trie.del(new Uint8Array([12, 22, 22])) + const val = await trieSetup.trie.get(new Uint8Array([12, 22, 22])) t.equal(null, val, trieSetup.msg) t.end() }) it('should delete from a branch->branch-extension', async function (t) { - await trieSetup.trie.put(Buffer.from([11, 11, 11]), Buffer.from('first')) - await trieSetup.trie.put(Buffer.from([12, 22, 22]), Buffer.from('create the first branch')) - await trieSetup.trie.put(Buffer.from([12, 33, 33]), Buffer.from('create the middle branch')) - await trieSetup.trie.put(Buffer.from([12, 34, 44]), Buffer.from('create the last branch')) + await trieSetup.trie.put(new Uint8Array([11, 11, 11]), utf8ToBytes('first')) + await trieSetup.trie.put(new Uint8Array([12, 22, 22]), utf8ToBytes('create the first branch')) + await trieSetup.trie.put(new Uint8Array([12, 33, 33]), utf8ToBytes('create the middle branch')) + await trieSetup.trie.put(new Uint8Array([12, 34, 44]), utf8ToBytes('create the last branch')) - await trieSetup.trie.del(Buffer.from([12, 22, 22])) - const val = await trieSetup.trie.get(Buffer.from([12, 22, 22])) + await trieSetup.trie.del(new Uint8Array([12, 22, 22])) + const val = await trieSetup.trie.get(new Uint8Array([12, 22, 22])) t.equal(null, val, trieSetup.msg) t.end() }) it('should delete from a extension->branch-extension', async function (t) { - await trieSetup.trie.put(Buffer.from([11, 11, 11]), Buffer.from('first')) - await trieSetup.trie.put(Buffer.from([12, 22, 22]), Buffer.from('create the first branch')) - await trieSetup.trie.put(Buffer.from([12, 33, 33]), Buffer.from('create the middle branch')) - await trieSetup.trie.put(Buffer.from([12, 34, 44]), Buffer.from('create the last branch')) + await trieSetup.trie.put(new Uint8Array([11, 11, 11]), utf8ToBytes('first')) + await trieSetup.trie.put(new Uint8Array([12, 22, 22]), utf8ToBytes('create the first branch')) + await trieSetup.trie.put(new Uint8Array([12, 33, 33]), utf8ToBytes('create the middle branch')) + await trieSetup.trie.put(new Uint8Array([12, 34, 44]), utf8ToBytes('create the last branch')) // delete the middle branch - await trieSetup.trie.del(Buffer.from([11, 11, 11])) - const val = await trieSetup.trie.get(Buffer.from([11, 11, 11])) + await trieSetup.trie.del(new Uint8Array([11, 11, 11])) + const val = await trieSetup.trie.get(new Uint8Array([11, 11, 11])) t.equal(null, val, trieSetup.msg) t.end() }) it('should delete from a extension->branch-branch', async function (t) { - await trieSetup.trie.put(Buffer.from([11, 11, 11]), Buffer.from('first')) - await trieSetup.trie.put(Buffer.from([12, 22, 22]), Buffer.from('create the first branch')) - await trieSetup.trie.put(Buffer.from([12, 33, 33]), Buffer.from('create the middle branch')) - await trieSetup.trie.put(Buffer.from([12, 34, 44]), Buffer.from('create the last branch')) + await trieSetup.trie.put(new Uint8Array([11, 11, 11]), utf8ToBytes('first')) + await trieSetup.trie.put(new Uint8Array([12, 22, 22]), utf8ToBytes('create the first branch')) + await trieSetup.trie.put(new Uint8Array([12, 33, 33]), utf8ToBytes('create the middle branch')) + await trieSetup.trie.put(new Uint8Array([12, 34, 44]), utf8ToBytes('create the last branch')) // delete the middle branch - await trieSetup.trie.del(Buffer.from([11, 11, 11])) - const val = await trieSetup.trie.get(Buffer.from([11, 11, 11])) + await trieSetup.trie.del(new Uint8Array([11, 11, 11])) + const val = await trieSetup.trie.get(new Uint8Array([11, 11, 11])) t.equal(null, val, trieSetup.msg) t.end() @@ -225,9 +230,9 @@ tape('testing deletion cases', function (tester) { tape('shall handle the case of node not found correctly', async (t) => { const trie = new Trie() - await trie.put(Buffer.from('a'), Buffer.from('value1')) - await trie.put(Buffer.from('aa'), Buffer.from('value2')) - await trie.put(Buffer.from('aaa'), Buffer.from('value3')) + await trie.put(utf8ToBytes('a'), utf8ToBytes('value1')) + await trie.put(utf8ToBytes('aa'), utf8ToBytes('value2')) + await trie.put(utf8ToBytes('aaa'), utf8ToBytes('value3')) /* Setups a trie which consists of ExtensionNode -> @@ -237,15 +242,15 @@ tape('shall handle the case of node not found correctly', async (t) => { LeafNode -> value3 */ - let path = await trie.findPath(Buffer.from('aaa')) + let path = await trie.findPath(utf8ToBytes('aaa')) t.ok(path.node !== null, 'findPath should find a node') - const { stack } = await trie.findPath(Buffer.from('aaa')) + const { stack } = await trie.findPath(utf8ToBytes('aaa')) // @ts-expect-error - await trie._db.del(Buffer.from(keccak256(stack[1].serialize()))) // delete the BranchNode -> value1 from the DB + await trie._db.del(keccak256(stack[1].serialize())) // delete the BranchNode -> value1 from the DB - path = await trie.findPath(Buffer.from('aaa')) + path = await trie.findPath(utf8ToBytes('aaa')) t.ok(path.node === null, 'findPath should not return a node now') t.ok( @@ -260,50 +265,49 @@ tape('it should create the genesis state root from ethereum', function (tester) const it = tester.test const trie4 = new Trie() - const g = Buffer.from('8a40bfaa73256b60764c1bf40675a99083efb075', 'hex') - const j = Buffer.from('e6716f9544a56c530d868e4bfbacb172315bdead', 'hex') - const v = Buffer.from('1e12515ce3e0f817a4ddef9ca55788a1d66bd2df', 'hex') - const a = Buffer.from('1a26338f0d905e295fccb71fa9ea849ffa12aaf4', 'hex') + const g = hexStringToBytes('8a40bfaa73256b60764c1bf40675a99083efb075') + const j = hexStringToBytes('e6716f9544a56c530d868e4bfbacb172315bdead') + const v = hexStringToBytes('1e12515ce3e0f817a4ddef9ca55788a1d66bd2df') + const a = hexStringToBytes('1a26338f0d905e295fccb71fa9ea849ffa12aaf4') - const storageRoot = Buffer.alloc(32) + const storageRoot = new Uint8Array(32) storageRoot.fill(0) - const startAmount = Buffer.alloc(26) + const startAmount = new Uint8Array(26) startAmount.fill(0) startAmount[0] = 1 const account = [startAmount, 0, storageRoot, KECCAK256_NULL] - const rlpAccount = Buffer.from(RLP.encode(bufArrToArr(account as Buffer[]))) + const rlpAccount = RLP.encode(account) const cppRlp = 'f85e9a010000000000000000000000000000000000000000000000000080a00000000000000000000000000000000000000000000000000000000000000000a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' const genesisStateRoot = '2f4399b08efe68945c1cf90ffe85bbe3ce978959da753f9e649f034015b8817d' - tester.equal(cppRlp, rlpAccount.toString('hex')) + tester.equal(cppRlp, bytesToHex(rlpAccount)) it('shall match the root', async function (t) { await trie4.put(g, rlpAccount) await trie4.put(j, rlpAccount) await trie4.put(v, rlpAccount) await trie4.put(a, rlpAccount) - t.equal(trie4.root().toString('hex'), genesisStateRoot) + t.equal(bytesToHex(trie4.root()), genesisStateRoot) t.end() }) }) tape('setting back state root (deleteFromDB)', async (t) => { - const k1 = Buffer.from('1') + const k1 = utf8ToBytes('1') /* Testing with longer value due to `rlpNode.length >= 32` check in `_formatNode()` * Reasoning from https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/: * "When one node is referenced inside another node, what is included is `H(rlp.encode(x))`, * where `H(x) = sha3(x) if len(x) >= 32 else x`" */ - const v1 = Buffer.from('this-is-some-longer-value-to-test-the-delete-operation-value1') - const k2 = Buffer.from('2') - const v2 = Buffer.from('this-is-some-longer-value-to-test-the-delete-operation-value2') + const v1 = utf8ToBytes('this-is-some-longer-value-to-test-the-delete-operation-value1') + const k2 = utf8ToBytes('2') + const v2 = utf8ToBytes('this-is-some-longer-value-to-test-the-delete-operation-value2') - const rootAfterK1 = Buffer.from( - '809e75931f394603657e113eb7244794f35b8d326cff99407111d600722e9425', - 'hex' + const rootAfterK1 = hexStringToBytes( + '809e75931f394603657e113eb7244794f35b8d326cff99407111d600722e9425' ) const trieSetup = { @@ -331,30 +335,28 @@ tape('dummy hash', async (t) => { const useKeyHashingFunction: HashKeysFunction = (msg) => { const hashLen = 32 if (msg.length <= hashLen - 5) { - return Buffer.concat([Buffer.from('hash_'), Buffer.alloc(hashLen - msg.length, 0), msg]) + return concatBytes(utf8ToBytes('hash_'), new Uint8Array(hashLen - msg.length).fill(0), msg) } else { - return Buffer.concat([Buffer.from('hash_'), msg.slice(0, hashLen - 5)]) + return concatBytes(utf8ToBytes('hash_'), msg.slice(0, hashLen - 5)) } } - const [k, v] = [Buffer.from('foo'), Buffer.from('bar')] - const expectedRoot = Buffer.from( - useKeyHashingFunction(new LeafNode(bufferToNibbles(k), v).serialize()) - ) + const [k, v] = [utf8ToBytes('foo'), utf8ToBytes('bar')] + const expectedRoot = useKeyHashingFunction(new LeafNode(bytesToNibbles(k), v).serialize()) const trie = new Trie({ useKeyHashingFunction }) await trie.put(k, v) - t.equal(trie.root().toString('hex'), expectedRoot.toString('hex')) + t.equal(bytesToHex(trie.root()), bytesToHex(expectedRoot)) t.end() }) tape('blake2b256 trie root', async (t) => { const trie = new Trie({ useKeyHashingFunction: (msg) => blake2b(msg, 32) }) - await trie.put(Buffer.from('foo'), Buffer.from('bar')) + await trie.put(utf8ToBytes('foo'), utf8ToBytes('bar')) t.equal( - trie.root().toString('hex'), + bytesToHex(trie.root()), 'e118db4e01512253df38daafa16fc1d69e03e755595b5847d275d7404ebdc74a' ) t.end() @@ -363,6 +365,6 @@ tape('blake2b256 trie root', async (t) => { tape('empty root', async (t) => { const trie = new Trie() - t.equal(trie.root().toString('hex'), KECCAK256_RLP_S) + t.equal(bytesToHex(trie.root()), KECCAK256_RLP_S) t.end() }) diff --git a/packages/trie/test/official.spec.ts b/packages/trie/test/official.spec.ts index 5d93fc4c579..141c315a466 100644 --- a/packages/trie/test/official.spec.ts +++ b/packages/trie/test/official.spec.ts @@ -1,3 +1,4 @@ +import { bytesToPrefixedHexString, hexStringToBytes, utf8ToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Trie } from '../src' @@ -12,15 +13,15 @@ tape('official tests', async function (t) { const expect = jsonTests[testName].root for (const input of inputs) { for (let i = 0; i < 2; i++) { - if (input[i] !== undefined && input[i] !== null && input[i].slice(0, 2) === '0x') { - input[i] = Buffer.from(input[i].slice(2), 'hex') + if (typeof input[i] === 'string' && input[i].slice(0, 2) === '0x') { + input[i] = hexStringToBytes(input[i]) } else if (typeof input[i] === 'string') { - input[i] = Buffer.from(input[i]) + input[i] = utf8ToBytes(input[i]) } - await trie.put(Buffer.from(input[0]), input[1]) + await trie.put(input[0], input[1]) } } - t.equal('0x' + trie.root().toString('hex'), expect) + t.equal(bytesToPrefixedHexString(trie.root()), expect) trie = new Trie() } t.end() @@ -37,17 +38,21 @@ tape('official tests any order', async function (t) { for (key of keys) { let val = test.in[key] - if (key.slice(0, 2) === '0x') { - key = Buffer.from(key.slice(2), 'hex') + if (typeof key === 'string' && key.slice(0, 2) === '0x') { + key = hexStringToBytes(key) + } else if (typeof key === 'string') { + key = utf8ToBytes(key) } - if (val !== undefined && val !== null && val.slice(0, 2) === '0x') { - val = Buffer.from(val.slice(2), 'hex') + if (typeof val === 'string' && val.slice(0, 2) === '0x') { + val = hexStringToBytes(val) + } else if (typeof val === 'string') { + val = utf8ToBytes(val) } - await trie.put(Buffer.from(key), Buffer.from(val)) + await trie.put(key, val) } - t.equal('0x' + trie.root().toString('hex'), test.root) + t.equal(bytesToPrefixedHexString(trie.root()), test.root) trie = new Trie() } t.end() diff --git a/packages/trie/test/proof.spec.ts b/packages/trie/test/proof.spec.ts index fd20d4795a2..34cef14b281 100644 --- a/packages/trie/test/proof.spec.ts +++ b/packages/trie/test/proof.spec.ts @@ -1,3 +1,4 @@ +import { bytesToUtf8, utf8ToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { Trie } from '../src' @@ -8,52 +9,52 @@ tape('simple merkle proofs generation and verification', function (tester) { it('create a merkle proof and verify it', async (t) => { const trie = new Trie() - await trie.put(Buffer.from('key1aa'), Buffer.from('0123456789012345678901234567890123456789xx')) - await trie.put(Buffer.from('key2bb'), Buffer.from('aval2')) - await trie.put(Buffer.from('key3cc'), Buffer.from('aval3')) + await trie.put(utf8ToBytes('key1aa'), utf8ToBytes('0123456789012345678901234567890123456789xx')) + await trie.put(utf8ToBytes('key2bb'), utf8ToBytes('aval2')) + await trie.put(utf8ToBytes('key3cc'), utf8ToBytes('aval3')) - let proof = await trie.createProof(Buffer.from('key2bb')) - let val = await trie.verifyProof(trie.root(), Buffer.from('key2bb'), proof) - t.equal(val!.toString('utf8'), 'aval2') + let proof = await trie.createProof(utf8ToBytes('key2bb')) + let val = await trie.verifyProof(trie.root(), utf8ToBytes('key2bb'), proof) + t.equal(bytesToUtf8(val!), 'aval2') - proof = await trie.createProof(Buffer.from('key1aa')) - val = await trie.verifyProof(trie.root(), Buffer.from('key1aa'), proof) - t.equal(val!.toString('utf8'), '0123456789012345678901234567890123456789xx') + proof = await trie.createProof(utf8ToBytes('key1aa')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('key1aa'), proof) + t.equal(bytesToUtf8(val!), '0123456789012345678901234567890123456789xx') - proof = await trie.createProof(Buffer.from('key2bb')) - val = await trie.verifyProof(trie.root(), Buffer.from('key2'), proof) + proof = await trie.createProof(utf8ToBytes('key2bb')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('key2'), proof) // In this case, the proof _happens_ to contain enough nodes to prove `key2` because // traversing into `key22` would touch all the same nodes as traversing into `key2` t.equal(val, null, 'Expected value at a random key to be null') - let myKey = Buffer.from('anyrandomkey') + let myKey = utf8ToBytes('anyrandomkey') proof = await trie.createProof(myKey) val = await trie.verifyProof(trie.root(), myKey, proof) t.equal(val, null, 'Expected value to be null') - myKey = Buffer.from('anothergarbagekey') // should generate a valid proof of null + myKey = utf8ToBytes('anothergarbagekey') // should generate a valid proof of null proof = await trie.createProof(myKey) - proof.push(Buffer.from('123456')) // extra nodes are just ignored + proof.push(utf8ToBytes('123456')) // extra nodes are just ignored val = await trie.verifyProof(trie.root(), myKey, proof) t.equal(val, null, 'Expected value to be null') - await trie.put(Buffer.from('another'), Buffer.from('3498h4riuhgwe')) + await trie.put(utf8ToBytes('another'), utf8ToBytes('3498h4riuhgwe')) // to fail our proof we can request a proof for one key - proof = await trie.createProof(Buffer.from('another')) + proof = await trie.createProof(utf8ToBytes('another')) // and try to use that proof on another key try { - await trie.verifyProof(trie.root(), Buffer.from('key1aa'), proof) + await trie.verifyProof(trie.root(), utf8ToBytes('key1aa'), proof) t.fail('expected error: Invalid proof provided') } catch (e: any) { t.equal(e.message, 'Invalid proof provided') } // we can also corrupt a valid proof - proof = await trie.createProof(Buffer.from('key2bb')) + proof = await trie.createProof(utf8ToBytes('key2bb')) proof[0].reverse() try { - await trie.verifyProof(trie.root(), Buffer.from('key2bb'), proof) + await trie.verifyProof(trie.root(), utf8ToBytes('key2bb'), proof) t.fail('expected error: Invalid proof provided') } catch (e: any) { t.equal(e.message, 'Invalid proof provided') @@ -61,12 +62,12 @@ tape('simple merkle proofs generation and verification', function (tester) { // test an invalid exclusion proof by creating // a valid exclusion proof then making it non-null - myKey = Buffer.from('anyrandomkey') + myKey = utf8ToBytes('anyrandomkey') proof = await trie.createProof(myKey) val = await trie.verifyProof(trie.root(), myKey, proof) t.equal(val, null, 'Expected value to be null') // now make the key non-null so the exclusion proof becomes invalid - await trie.put(myKey, Buffer.from('thisisavalue')) + await trie.put(myKey, utf8ToBytes('thisisavalue')) try { await trie.verifyProof(trie.root(), myKey, proof) t.fail('expected error: Invalid proof provided') @@ -80,11 +81,11 @@ tape('simple merkle proofs generation and verification', function (tester) { it('create a merkle proof and verify it with a single long key', async (t) => { const trie = new Trie() - await trie.put(Buffer.from('key1aa'), Buffer.from('0123456789012345678901234567890123456789xx')) + await trie.put(utf8ToBytes('key1aa'), utf8ToBytes('0123456789012345678901234567890123456789xx')) - const proof = await trie.createProof(Buffer.from('key1aa')) - const val = await trie.verifyProof(trie.root(), Buffer.from('key1aa'), proof) - t.equal(val!.toString('utf8'), '0123456789012345678901234567890123456789xx') + const proof = await trie.createProof(utf8ToBytes('key1aa')) + const val = await trie.verifyProof(trie.root(), utf8ToBytes('key1aa'), proof) + t.equal(bytesToUtf8(val!), '0123456789012345678901234567890123456789xx') t.end() }) @@ -92,11 +93,11 @@ tape('simple merkle proofs generation and verification', function (tester) { it('create a merkle proof and verify it with a single short key', async (t) => { const trie = new Trie() - await trie.put(Buffer.from('key1aa'), Buffer.from('01234')) + await trie.put(utf8ToBytes('key1aa'), utf8ToBytes('01234')) - const proof = await trie.createProof(Buffer.from('key1aa')) - const val = await trie.verifyProof(trie.root(), Buffer.from('key1aa'), proof) - t.equal(val!.toString('utf8'), '01234') + const proof = await trie.createProof(utf8ToBytes('key1aa')) + const val = await trie.verifyProof(trie.root(), utf8ToBytes('key1aa'), proof) + t.equal(bytesToUtf8(val!), '01234') t.end() }) @@ -105,29 +106,29 @@ tape('simple merkle proofs generation and verification', function (tester) { const trie = new Trie() await trie.put( - Buffer.from('key1aa'), - Buffer.from('0123456789012345678901234567890123456789xxx') + utf8ToBytes('key1aa'), + utf8ToBytes('0123456789012345678901234567890123456789xxx') ) await trie.put( - Buffer.from('key1'), - Buffer.from('0123456789012345678901234567890123456789Very_Long') + utf8ToBytes('key1'), + utf8ToBytes('0123456789012345678901234567890123456789Very_Long') ) - await trie.put(Buffer.from('key2bb'), Buffer.from('aval3')) - await trie.put(Buffer.from('key2'), Buffer.from('short')) - await trie.put(Buffer.from('key3cc'), Buffer.from('aval3')) - await trie.put(Buffer.from('key3'), Buffer.from('1234567890123456789012345678901')) + await trie.put(utf8ToBytes('key2bb'), utf8ToBytes('aval3')) + await trie.put(utf8ToBytes('key2'), utf8ToBytes('short')) + await trie.put(utf8ToBytes('key3cc'), utf8ToBytes('aval3')) + await trie.put(utf8ToBytes('key3'), utf8ToBytes('1234567890123456789012345678901')) - let proof = await trie.createProof(Buffer.from('key1')) - let val = await trie.verifyProof(trie.root(), Buffer.from('key1'), proof) - t.equal(val!.toString('utf8'), '0123456789012345678901234567890123456789Very_Long') + let proof = await trie.createProof(utf8ToBytes('key1')) + let val = await trie.verifyProof(trie.root(), utf8ToBytes('key1'), proof) + t.equal(bytesToUtf8(val!), '0123456789012345678901234567890123456789Very_Long') - proof = await trie.createProof(Buffer.from('key2')) - val = await trie.verifyProof(trie.root(), Buffer.from('key2'), proof) - t.equal(val!.toString('utf8'), 'short') + proof = await trie.createProof(utf8ToBytes('key2')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('key2'), proof) + t.equal(bytesToUtf8(val!), 'short') - proof = await trie.createProof(Buffer.from('key3')) - val = await trie.verifyProof(trie.root(), Buffer.from('key3'), proof) - t.equal(val!.toString('utf8'), '1234567890123456789012345678901') + proof = await trie.createProof(utf8ToBytes('key3')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('key3'), proof) + t.equal(bytesToUtf8(val!), '1234567890123456789012345678901') t.end() }) @@ -135,21 +136,21 @@ tape('simple merkle proofs generation and verification', function (tester) { it('should succeed with a simple embedded extension-branch', async (t) => { const trie = new Trie() - await trie.put(Buffer.from('a'), Buffer.from('a')) - await trie.put(Buffer.from('b'), Buffer.from('b')) - await trie.put(Buffer.from('c'), Buffer.from('c')) + await trie.put(utf8ToBytes('a'), utf8ToBytes('a')) + await trie.put(utf8ToBytes('b'), utf8ToBytes('b')) + await trie.put(utf8ToBytes('c'), utf8ToBytes('c')) - let proof = await trie.createProof(Buffer.from('a')) - let val = await trie.verifyProof(trie.root(), Buffer.from('a'), proof) - t.equal(val!.toString('utf8'), 'a') + let proof = await trie.createProof(utf8ToBytes('a')) + let val = await trie.verifyProof(trie.root(), utf8ToBytes('a'), proof) + t.equal(bytesToUtf8(val!), 'a') - proof = await trie.createProof(Buffer.from('b')) - val = await trie.verifyProof(trie.root(), Buffer.from('b'), proof) - t.equal(val!.toString('utf8'), 'b') + proof = await trie.createProof(utf8ToBytes('b')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('b'), proof) + t.equal(bytesToUtf8(val!), 'b') - proof = await trie.createProof(Buffer.from('c')) - val = await trie.verifyProof(trie.root(), Buffer.from('c'), proof) - t.equal(val!.toString('utf8'), 'c') + proof = await trie.createProof(utf8ToBytes('c')) + val = await trie.verifyProof(trie.root(), utf8ToBytes('c'), proof) + t.equal(bytesToUtf8(val!), 'c') t.end() }) diff --git a/packages/trie/test/proof/range.spec.ts b/packages/trie/test/proof/range.spec.ts index 214c55317ab..f1c2118f634 100644 --- a/packages/trie/test/proof/range.spec.ts +++ b/packages/trie/test/proof/range.spec.ts @@ -1,4 +1,10 @@ -import { setLengthLeft, toBuffer } from '@ethereumjs/util' +import { + compareBytes, + concatBytes, + hexStringToBytes, + setLengthLeft, + toBytes, +} from '@ethereumjs/util' import * as crypto from 'crypto' import * as tape from 'tape' @@ -16,13 +22,13 @@ const TRIE_SIZE = 512 * @returns Trie object and sorted entries */ async function randomTrie(db: DB, addKey: boolean = true) { - const entries: [Buffer, Buffer][] = [] + const entries: [Uint8Array, Uint8Array][] = [] const trie = new Trie({ db }) if (addKey) { for (let i = 0; i < 100; i++) { - const key = setLengthLeft(toBuffer(i), 32) - const val = toBuffer(i) + const key = setLengthLeft(toBytes(i), 32) + const val = toBytes(i) await trie.put(key, val) entries.push([key, val]) } @@ -39,7 +45,7 @@ async function randomTrie(db: DB, addKey: boolean = true) { return { trie, - entries: entries.sort(([k1], [k2]) => k1.compare(k2)), + entries: entries.sort(([k1], [k2]) => compareBytes(k1, k2)), } } @@ -52,31 +58,31 @@ function getRandomIntInclusive(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min } -function decreaseKey(key: Buffer) { +function decreaseKey(key: Uint8Array) { for (let i = key.length - 1; i >= 0; i--) { if (key[i] > 0) { - return Buffer.concat([key.slice(0, i), toBuffer(key[i] - 1), key.slice(i + 1)]) + return concatBytes(key.slice(0, i), toBytes(key[i] - 1), key.slice(i + 1)) } } } -function increaseKey(key: Buffer) { +function increaseKey(key: Uint8Array) { for (let i = key.length - 1; i >= 0; i--) { if (key[i] < 255) { - return Buffer.concat([key.slice(0, i), toBuffer(key[i] + 1), key.slice(i + 1)]) + return concatBytes(key.slice(0, i), toBytes(key[i] + 1), key.slice(i + 1)) } } } async function verify( trie: Trie, - entries: [Buffer, Buffer][], + entries: [Uint8Array, Uint8Array][], start: number, end: number, - startKey?: Buffer, - endKey?: Buffer, - keys?: Buffer[], - vals?: Buffer[] + startKey?: Uint8Array, + endKey?: Uint8Array, + keys?: Uint8Array[], + vals?: Uint8Array[] ) { startKey = startKey ?? entries[start][0] endKey = endKey ?? entries[end][0] @@ -116,12 +122,18 @@ tape('simple merkle range proofs generation and verification', function (tester) const end = getRandomIntInclusive(start, entries.length - 1) const startKey = decreaseKey(entries[start][0]) - if (!startKey || (start > 0 && entries[start - 1][0].compare(startKey) >= 0)) { + if ( + startKey === undefined || + (start > 0 && compareBytes(entries[start - 1][0], startKey) >= 0) + ) { continue } const endKey = increaseKey(entries[end][0]) - if (!endKey || (end < entries.length - 1 && endKey.compare(entries[end + 1][0]) >= 0)) { + if ( + endKey === undefined || + (end < entries.length - 1 && compareBytes(endKey, entries[end + 1][0]) >= 0) + ) { continue } @@ -129,8 +141,8 @@ tape('simple merkle range proofs generation and verification', function (tester) } // Special case, two edge proofs for two edge key. - const startKey = Buffer.from('00'.repeat(32), 'hex') - const endKey = Buffer.from('ff'.repeat(32), 'hex') + const startKey = hexStringToBytes('00'.repeat(32)) + const endKey = hexStringToBytes('ff'.repeat(32)) t.equal(await verify(trie, entries, 0, entries.length - 1, startKey, endKey), false) t.end() @@ -187,10 +199,12 @@ tape('simple merkle range proofs generation and verification', function (tester) // Test the mini trie with only a single element. const tinyTrie = new Trie() - const tinyEntries: [Buffer, Buffer][] = [[crypto.randomBytes(32), crypto.randomBytes(20)]] + const tinyEntries: [Uint8Array, Uint8Array][] = [ + [crypto.randomBytes(32), crypto.randomBytes(20)], + ] await tinyTrie.put(tinyEntries[0][0], tinyEntries[0][1]) - const tinyStartKey = Buffer.from('00'.repeat(32), 'hex') + const tinyStartKey = hexStringToBytes('00'.repeat(32)) t.equal(await verify(tinyTrie, tinyEntries, 0, 0, tinyStartKey), false) }) @@ -219,15 +233,15 @@ tape('simple merkle range proofs generation and verification', function (tester) entries, 0, entries.length - 1, - Buffer.from('00'.repeat(32), 'hex'), - Buffer.from('ff'.repeat(32), 'hex') + hexStringToBytes('00'.repeat(32)), + hexStringToBytes('ff'.repeat(32)) ), false ) }) it('create a single side range proof and verify it', async (t) => { - const startKey = Buffer.from('00'.repeat(32), 'hex') + const startKey = hexStringToBytes('00'.repeat(32)) const { trie, entries } = await randomTrie(new MapDB(), false) const cases = [0, 1, 200, entries.length - 1] @@ -237,7 +251,7 @@ tape('simple merkle range proofs generation and verification', function (tester) }) it('create a revert single side range proof and verify it', async (t) => { - const endKey = Buffer.from('ff'.repeat(32), 'hex') + const endKey = hexStringToBytes('ff'.repeat(32)) const { trie, entries } = await randomTrie(new MapDB(), false) const cases = [0, 1, 200, entries.length - 1] @@ -247,7 +261,9 @@ tape('simple merkle range proofs generation and verification', function (tester) }) it('create a bad range proof and verify it', async (t) => { - const runTest = async (cb: (trie: Trie, entries: [Buffer, Buffer][]) => Promise) => { + const runTest = async ( + cb: (trie: Trie, entries: [Uint8Array, Uint8Array][]) => Promise + ) => { const { trie, entries } = await randomTrie(new MapDB(), false) let result = false @@ -306,17 +322,17 @@ tape('simple merkle range proofs generation and verification', function (tester) it('create a gapped range proof and verify it', async (t) => { const trie = new Trie() - const entries: [Buffer, Buffer][] = [] + const entries: [Uint8Array, Uint8Array][] = [] for (let i = 0; i < 10; i++) { - const key = setLengthLeft(toBuffer(i), 32) - const val = toBuffer(i) + const key = setLengthLeft(toBytes(i), 32) + const val = toBytes(i) await trie.put(key, val) entries.push([key, val]) } const start = 2 const end = 8 - const targetRange: [Buffer, Buffer][] = [] + const targetRange: [Uint8Array, Uint8Array][] = [] for (let i = start; i <= end; i++) { if (i === (start + end) / 2) { continue @@ -433,19 +449,19 @@ tape('simple merkle range proofs generation and verification', function (tester) // eslint-disable-next-line prefer-const for (let { start, end, expect } of cases) { - let startKey: Buffer - let endKey: Buffer + let startKey: Uint8Array + let endKey: Uint8Array if (start === -1) { start = 0 - startKey = Buffer.from('00'.repeat(32), 'hex') + startKey = hexStringToBytes('00'.repeat(32)) } else { startKey = entries[start][0] } if (end === -1) { end = entries.length - 1 - endKey = Buffer.from('ff'.repeat(32), 'hex') + endKey = hexStringToBytes('ff'.repeat(32)) } else { endKey = entries[end][0] } @@ -457,7 +473,7 @@ tape('simple merkle range proofs generation and verification', function (tester) it('create a bloated range proof and verify it', async (t) => { const { trie, entries } = await randomTrie(new MapDB(), false) - let bloatedProof: Buffer[] = [] + let bloatedProof: Uint8Array[] = [] for (let i = 0; i < TRIE_SIZE; i++) { bloatedProof = bloatedProof.concat(await trie.createProof(entries[i][0])) } diff --git a/packages/trie/test/stream.spec.ts b/packages/trie/test/stream.spec.ts index 54daf12f6a7..c08606befa7 100644 --- a/packages/trie/test/stream.spec.ts +++ b/packages/trie/test/stream.spec.ts @@ -1,3 +1,4 @@ +import { utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Trie } from '../src' @@ -10,87 +11,87 @@ tape('kv stream test', function (tester) { const ops = [ { type: 'del', - key: Buffer.from('father'), + key: utf8ToBytes('father'), }, { type: 'put', - key: Buffer.from('name'), - value: Buffer.from('Yuri Irsenovich Kim'), + key: utf8ToBytes('name'), + value: utf8ToBytes('Yuri Irsenovich Kim'), }, { type: 'put', - key: Buffer.from('dob'), - value: Buffer.from('16 February 1941'), + key: utf8ToBytes('dob'), + value: utf8ToBytes('16 February 1941'), }, { type: 'put', - key: Buffer.from('spouse'), - value: Buffer.from('Kim Young-sook'), + key: utf8ToBytes('spouse'), + value: utf8ToBytes('Kim Young-sook'), }, { type: 'put', - key: Buffer.from('occupation'), - value: Buffer.from('Clown'), + key: utf8ToBytes('occupation'), + value: utf8ToBytes('Clown'), }, { type: 'put', - key: Buffer.from('nameads'), - value: Buffer.from('Yuri Irsenovich Kim'), + key: utf8ToBytes('nameads'), + value: utf8ToBytes('Yuri Irsenovich Kim'), }, { type: 'put', - key: Buffer.from('namfde'), - value: Buffer.from('Yuri Irsenovich Kim'), + key: utf8ToBytes('namfde'), + value: utf8ToBytes('Yuri Irsenovich Kim'), }, { type: 'put', - key: Buffer.from('namsse'), - value: Buffer.from('Yuri Irsenovich Kim'), + key: utf8ToBytes('namsse'), + value: utf8ToBytes('Yuri Irsenovich Kim'), }, { type: 'put', - key: Buffer.from('dofab'), - value: Buffer.from('16 February 1941'), + key: utf8ToBytes('dofab'), + value: utf8ToBytes('16 February 1941'), }, { type: 'put', - key: Buffer.from('spoudse'), - value: Buffer.from('Kim Young-sook'), + key: utf8ToBytes('spoudse'), + value: utf8ToBytes('Kim Young-sook'), }, { type: 'put', - key: Buffer.from('occupdsation'), - value: Buffer.from('Clown'), + key: utf8ToBytes('occupdsation'), + value: utf8ToBytes('Clown'), }, { type: 'put', - key: Buffer.from('dozzzb'), - value: Buffer.from('16 February 1941'), + key: utf8ToBytes('dozzzb'), + value: utf8ToBytes('16 February 1941'), }, { type: 'put', - key: Buffer.from('spouszze'), - value: Buffer.from('Kim Young-sook'), + key: utf8ToBytes('spouszze'), + value: utf8ToBytes('Kim Young-sook'), }, { type: 'put', - key: Buffer.from('occupatdfion'), - value: Buffer.from('Clown'), + key: utf8ToBytes('occupatdfion'), + value: utf8ToBytes('Clown'), }, { type: 'put', - key: Buffer.from('dssob'), - value: Buffer.from('16 February 1941'), + key: utf8ToBytes('dssob'), + value: utf8ToBytes('16 February 1941'), }, { type: 'put', - key: Buffer.from('spossuse'), - value: Buffer.from('Kim Young-sook'), + key: utf8ToBytes('spossuse'), + value: utf8ToBytes('Kim Young-sook'), }, { type: 'put', - key: Buffer.from('occupssation'), - value: Buffer.from('Clown'), + key: utf8ToBytes('occupssation'), + value: utf8ToBytes('Clown'), }, ] as BatchDBOp[] @@ -128,33 +129,33 @@ tape('db stream test', function (tester) { const ops = [ { type: 'put', - key: Buffer.from('color'), - value: Buffer.from('purple'), + key: utf8ToBytes('color'), + value: utf8ToBytes('purple'), }, { type: 'put', - key: Buffer.from('food'), - value: Buffer.from('sushi'), + key: utf8ToBytes('food'), + value: utf8ToBytes('sushi'), }, { type: 'put', - key: Buffer.from('fight'), - value: Buffer.from('fire'), + key: utf8ToBytes('fight'), + value: utf8ToBytes('fire'), }, { type: 'put', - key: Buffer.from('colo'), - value: Buffer.from('trolo'), + key: utf8ToBytes('colo'), + value: utf8ToBytes('trolo'), }, { type: 'put', - key: Buffer.from('color'), - value: Buffer.from('blue'), + key: utf8ToBytes('color'), + value: utf8ToBytes('blue'), }, { type: 'put', - key: Buffer.from('color'), - value: Buffer.from('pink'), + key: utf8ToBytes('color'), + value: utf8ToBytes('pink'), }, ] as BatchDBOp[] diff --git a/packages/trie/test/trie/checkpoint.spec.ts b/packages/trie/test/trie/checkpoint.spec.ts index 87472caad00..ebd737adfe0 100644 --- a/packages/trie/test/trie/checkpoint.spec.ts +++ b/packages/trie/test/trie/checkpoint.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex, bytesToUtf8, equalsBytes, utf8ToBytes } from '@ethereumjs/util' import { createHash } from 'crypto' import { keccak256 } from 'ethereum-cryptography/keccak' import * as tape from 'tape' @@ -16,17 +17,17 @@ tape('testing checkpoints', function (tester) { it('setup', async function (t) { trie = new Trie() - await trie.put(Buffer.from('do'), Buffer.from('verb')) - await trie.put(Buffer.from('doge'), Buffer.from('coin')) - preRoot = trie.root().toString('hex') + await trie.put(utf8ToBytes('do'), utf8ToBytes('verb')) + await trie.put(utf8ToBytes('doge'), utf8ToBytes('coin')) + preRoot = bytesToHex(trie.root()) t.end() }) it('should copy trie and get value added to original trie', async function (t) { trieCopy = trie.copy() - t.equal(trieCopy.root().toString('hex'), preRoot) - const res = await trieCopy.get(Buffer.from('do')) - t.ok(Buffer.from('verb').equals(Buffer.from(res!))) + t.equal(bytesToHex(trieCopy.root()), preRoot) + const res = await trieCopy.get(utf8ToBytes('do')) + t.ok(equalsBytes(utf8ToBytes('verb'), res!)) t.end() }) @@ -37,34 +38,34 @@ tape('testing checkpoints', function (tester) { }) it('should save to the cache', async function (t) { - await trie.put(Buffer.from('test'), Buffer.from('something')) - await trie.put(Buffer.from('love'), Buffer.from('emotion')) - postRoot = trie.root().toString('hex') + await trie.put(utf8ToBytes('test'), utf8ToBytes('something')) + await trie.put(utf8ToBytes('love'), utf8ToBytes('emotion')) + postRoot = bytesToHex(trie.root()) t.end() }) it('should get values from before checkpoint', async function (t) { - const res = await trie.get(Buffer.from('doge')) - t.ok(Buffer.from('coin').equals(Buffer.from(res!))) + const res = await trie.get(utf8ToBytes('doge')) + t.ok(equalsBytes(utf8ToBytes('coin'), res!)) t.end() }) it('should get values from cache', async function (t) { - const res = await trie.get(Buffer.from('love')) - t.ok(Buffer.from('emotion').equals(Buffer.from(res!))) + const res = await trie.get(utf8ToBytes('love')) + t.ok(equalsBytes(utf8ToBytes('emotion'), res!)) t.end() }) it('should copy trie and get upstream and cache values after checkpoint', async function (t) { trieCopy = trie.copy() - t.equal(trieCopy.root().toString('hex'), postRoot) + t.equal(bytesToHex(trieCopy.root()), postRoot) // @ts-expect-error t.equal(trieCopy._db.checkpoints.length, 1) t.ok(trieCopy.hasCheckpoints()) - const res = await trieCopy.get(Buffer.from('do')) - t.ok(Buffer.from('verb').equals(Buffer.from(res!))) - const res2 = await trieCopy.get(Buffer.from('love')) - t.ok(Buffer.from('emotion').equals(Buffer.from(res2!))) + const res = await trieCopy.get(utf8ToBytes('do')) + t.ok(equalsBytes(utf8ToBytes('verb'), res!)) + const res2 = await trieCopy.get(utf8ToBytes('love')) + t.ok(equalsBytes(utf8ToBytes('emotion'), res2!)) t.end() }) @@ -75,62 +76,62 @@ tape('testing checkpoints', function (tester) { useKeyHashingFunction: (value) => createHash('sha256').update(value).digest(), }) - await trie.put(Buffer.from('key1'), Buffer.from('value1')) + await trie.put(utf8ToBytes('key1'), utf8ToBytes('value1')) trie.checkpoint() - await trie.put(Buffer.from('key2'), Buffer.from('value2')) + await trie.put(utf8ToBytes('key2'), utf8ToBytes('value2')) const trieCopy = trie.copy() - const value = await trieCopy.get(Buffer.from('key1')) - t.equal(value!.toString(), 'value1') + const value = await trieCopy.get(utf8ToBytes('key1')) + t.equal(bytesToUtf8(value!), 'value1') t.end() }) it('should revert to the original root', async function (t) { t.ok(trie.hasCheckpoints()) await trie.revert() - t.equal(trie.root().toString('hex'), preRoot) + t.equal(bytesToHex(trie.root()), preRoot) t.notOk(trie.hasCheckpoints()) t.end() }) it('should not get values from cache after revert', async function (t) { - const res = await trie.get(Buffer.from('love')) + const res = await trie.get(utf8ToBytes('love')) t.notOk(res) t.end() }) it('should commit a checkpoint', async function (t) { trie.checkpoint() - await trie.put(Buffer.from('test'), Buffer.from('something')) - await trie.put(Buffer.from('love'), Buffer.from('emotion')) + await trie.put(utf8ToBytes('test'), utf8ToBytes('something')) + await trie.put(utf8ToBytes('love'), utf8ToBytes('emotion')) await trie.commit() t.equal(trie.hasCheckpoints(), false) - t.equal(trie.root().toString('hex'), postRoot) + t.equal(bytesToHex(trie.root()), postRoot) t.end() }) it('should get new values after commit', async function (t) { - const res = await trie.get(Buffer.from('love')) - t.ok(Buffer.from('emotion').equals(Buffer.from(res!))) + const res = await trie.get(utf8ToBytes('love')) + t.ok(equalsBytes(utf8ToBytes('emotion'), res!)) t.end() }) it('should commit a nested checkpoint', async function (t) { trie.checkpoint() - await trie.put(Buffer.from('test'), Buffer.from('something else')) + await trie.put(utf8ToBytes('test'), utf8ToBytes('something else')) const root = trie.root() trie.checkpoint() - await trie.put(Buffer.from('the feels'), Buffer.from('emotion')) + await trie.put(utf8ToBytes('the feels'), utf8ToBytes('emotion')) await trie.revert() await trie.commit() t.equal(trie.hasCheckpoints(), false) - t.equal(trie.root().toString('hex'), root.toString('hex')) + t.equal(trie.root(), root) t.end() }) - const k1 = Buffer.from('k1') - const v1 = Buffer.from('v1') - const v12 = Buffer.from('v12') - const v123 = Buffer.from('v123') + const k1 = utf8ToBytes('k1') + const v1 = utf8ToBytes('v1') + const v12 = utf8ToBytes('v12') + const v123 = utf8ToBytes('v123') it('revert -> put', async function (t) { trie = new Trie() @@ -213,8 +214,8 @@ tape('testing checkpoints', function (tester) { See PR 2203 and 2236. */ it('Checkpointing: nested checkpoints -> with pruning, verify that checkpoints are deep-copied', async (t) => { - const KEY = Buffer.from('last_block_height') - const KEY_ROOT = Buffer.from(keccak256(ROOT_DB_KEY)) + const KEY = utf8ToBytes('last_block_height') + const KEY_ROOT = keccak256(ROOT_DB_KEY) // Initialise State const CommittedState = await Trie.create({ @@ -224,7 +225,7 @@ tape('testing checkpoints', function (tester) { }) // Put some initial data - await CommittedState.put(KEY, Buffer.from('1')) + await CommittedState.put(KEY, utf8ToBytes('1')) // Take a checkpoint to enable nested checkpoints // From this point, CommittedState will not write on disk @@ -235,18 +236,18 @@ tape('testing checkpoints', function (tester) { MemoryState.checkpoint() // Test changes on MemoryState - await MemoryState.put(KEY, Buffer.from('2')) + await MemoryState.put(KEY, utf8ToBytes('2')) await MemoryState.commit() // The CommittedState should not change (not the key/value pairs, not the root, and not the root in DB) - t.equal((await CommittedState.get(KEY))?.toString(), '1') + t.equal(bytesToUtf8((await CommittedState.get(KEY))!), '1') t.equal( // @ts-expect-error - (await CommittedState._db.get(KEY_ROOT))?.toString('hex'), + bytesToHex(await CommittedState._db.get(KEY_ROOT)), '77ddd505d2a5b76a2a6ee34b827a0d35ca19f8d358bee3d74a84eab59794487c' ) t.equal( - CommittedState.root().toString('hex'), + bytesToHex(CommittedState.root()), '77ddd505d2a5b76a2a6ee34b827a0d35ca19f8d358bee3d74a84eab59794487c' ) @@ -269,13 +270,13 @@ tape('testing checkpoints', function (tester) { // I.e. the trie is pruned. t.deepEqual( // @ts-expect-error - [...CommittedState._db.db._database.values()].map((value) => value.toString('hex')), + [...CommittedState._db.db._database.values()].map((value) => bytesToHex(value)), [ 'd7eba6ee0f011acb031b79554d57001c42fbfabb150eb9fdd3b6d434f7b791eb', 'e3a1202418cf7414b1e6c2c8d92b4673eecdb4aac88f7f58623e3be903aefb2fd4655c32', ] ) // Verify that the key is updated - t.equal((await CommittedState.get(KEY))?.toString(), '2') + t.equal(bytesToUtf8((await CommittedState.get(KEY))!), '2') }) }) diff --git a/packages/trie/test/trie/prune.spec.ts b/packages/trie/test/trie/prune.spec.ts index f80bb17bda8..d89950edad0 100644 --- a/packages/trie/test/trie/prune.spec.ts +++ b/packages/trie/test/trie/prune.spec.ts @@ -1,49 +1,53 @@ -import { KECCAK256_RLP } from '@ethereumjs/util' +import { + KECCAK256_RLP, + equalsBytes, + hexStringToBytes, + randomBytes, + utf8ToBytes, +} from '@ethereumjs/util' import * as tape from 'tape' import { Trie } from '../../src' -const crypto = require('crypto') - tape('Pruned trie tests', function (tester) { const it = tester.test it('should default to not prune the trie', async function (st) { const trie = new Trie() - const key = Buffer.from('test') - await trie.put(key, Buffer.from('1')) - await trie.put(key, Buffer.from('2')) - await trie.put(key, Buffer.from('3')) - await trie.put(key, Buffer.from('4')) - await trie.put(key, Buffer.from('5')) - await trie.put(key, Buffer.from('6')) + const key = utf8ToBytes('test') + await trie.put(key, utf8ToBytes('1')) + await trie.put(key, utf8ToBytes('2')) + await trie.put(key, utf8ToBytes('3')) + await trie.put(key, utf8ToBytes('4')) + await trie.put(key, utf8ToBytes('5')) + await trie.put(key, utf8ToBytes('6')) st.equals((trie)._db.db._database.size, 6, 'DB size correct') }) it('should prune simple trie', async function (st) { const trie = new Trie({ useNodePruning: true }) - const key = Buffer.from('test') - await trie.put(key, Buffer.from('1')) - await trie.put(key, Buffer.from('2')) - await trie.put(key, Buffer.from('3')) - await trie.put(key, Buffer.from('4')) - await trie.put(key, Buffer.from('5')) - await trie.put(key, Buffer.from('6')) + const key = utf8ToBytes('test') + await trie.put(key, utf8ToBytes('1')) + await trie.put(key, utf8ToBytes('2')) + await trie.put(key, utf8ToBytes('3')) + await trie.put(key, utf8ToBytes('4')) + await trie.put(key, utf8ToBytes('5')) + await trie.put(key, utf8ToBytes('6')) st.equals((trie)._db.db._database.size, 1, 'DB size correct') }) it('should prune simple trie', async function (st) { const trie = new Trie({ useNodePruning: true }) - const key = Buffer.from('test') - await trie.put(key, Buffer.from('1')) + const key = utf8ToBytes('test') + await trie.put(key, utf8ToBytes('1')) st.equals((trie)._db.db._database.size, 1, 'DB size correct') await trie.del(key) st.equals((trie)._db.db._database.size, 0, 'DB size correct') - await trie.put(key, Buffer.from('1')) + await trie.put(key, utf8ToBytes('1')) st.equals((trie)._db.db._database.size, 1, 'DB size correct') }) @@ -54,7 +58,7 @@ tape('Pruned trie tests', function (tester) { const values = ['00', '02', '03', '04', '05'] for (let i = 0; i < keys.length; i++) { - await trie.put(Buffer.from(keys[i], 'hex'), Buffer.from(values[i], 'hex')) + await trie.put(hexStringToBytes(keys[i]), hexStringToBytes(values[i])) } st.end() @@ -62,25 +66,25 @@ tape('Pruned trie tests', function (tester) { it('should not prune if the same value is put twice', async function (st) { const trie = new Trie() - const key = Buffer.from('01') - const value = Buffer.from('02') + const key = utf8ToBytes('01') + const value = utf8ToBytes('02') await trie.put(key, value) await trie.put(key, value) const reported = await trie.get(key) - st.ok(reported?.equals(value), 'value matches expected value') + st.ok(equalsBytes(reported!, value), 'value matches expected value') }) it('should not throw if a key is either non-existent or deleted twice', async function (st) { const trie = new Trie() - const key = Buffer.from('01') - const value = Buffer.from('02') + const key = utf8ToBytes('01') + const value = utf8ToBytes('02') // key does not exist (empty trie) await trie.del(key) - const key2 = Buffer.from('AA') - const value2 = Buffer.from('ee') + const key2 = utf8ToBytes('AA') + const value2 = utf8ToBytes('ee') await trie.put(key2, value2) // key does not exist (non-empty trie) await trie.del(key) @@ -91,16 +95,17 @@ tape('Pruned trie tests', function (tester) { const reported = await trie.get(key) st.ok(reported === null, 'value is null') const reported2 = await trie.get(key2) - st.ok(reported2?.equals(value2), 'value matches expected value') + st.ok(equalsBytes(reported2!, value2), 'value matches expected value') }) it('should prune when keys are updated or deleted', async (st) => { for (let testID = 0; testID < 1; testID++) { const trie = new Trie({ useNodePruning: true }) - const keys: string[] = [] + const keys: Uint8Array[] = [] for (let i = 0; i < 100; i++) { - keys.push(crypto.randomBytes(32)) + keys.push(randomBytes(32)) } + const values: string[] = [] for (let i = 0; i < 1000; i++) { let val = Math.floor(Math.random() * 16384) @@ -113,7 +118,8 @@ tape('Pruned trie tests', function (tester) { for (let i = 0; i < keys.length; i++) { const idx = Math.floor(Math.random() * keys.length) const key = keys[idx] - await trie.put(Buffer.from(key), Buffer.from(values[i])) + + await trie.put(key, utf8ToBytes(values[i])) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') @@ -121,7 +127,7 @@ tape('Pruned trie tests', function (tester) { // Randomly delete keys for (let i = 0; i < 20; i++) { const idx = Math.floor(Math.random() * keys.length) - await trie.del(Buffer.from(keys[idx])) + await trie.del(keys[idx]) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') @@ -131,9 +137,9 @@ tape('Pruned trie tests', function (tester) { const idx = Math.floor(Math.random() * keys.length) const key = keys[idx] if (Math.random() < 0.5) { - await trie.put(Buffer.from(key), Buffer.from(values[i])) + await trie.put(key, utf8ToBytes(values[i])) } else { - await trie.del(Buffer.from(key)) + await trie.del(key) } } @@ -141,11 +147,11 @@ tape('Pruned trie tests', function (tester) { // Delete all keys for (let idx = 0; idx < 100; idx++) { - await trie.del(Buffer.from(keys[idx])) + await trie.del(keys[idx]) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') - st.ok(trie.root().equals(KECCAK256_RLP), 'trie is empty') + st.ok(equalsBytes(trie.root(), KECCAK256_RLP), 'trie is empty') let dbKeys = 0 for (const _dbkey of (trie)._db.db._database.keys()) { @@ -159,30 +165,30 @@ tape('Pruned trie tests', function (tester) { // Create empty Trie (is pruned) let trie = new Trie() // Create a new value (still is pruned) - await trie.put(Buffer.from('aa', 'hex'), Buffer.from('bb', 'hex')) + await trie.put(hexStringToBytes('aa'), hexStringToBytes('bb')) // Overwrite this value (trie is now not pruned anymore) - await trie.put(Buffer.from('aa', 'hex'), Buffer.from('aa', 'hex')) + await trie.put(hexStringToBytes('aa'), hexStringToBytes('aa')) st.ok(!(await trie.verifyPrunedIntegrity()), 'trie is not pruned') // Create new empty Trie (is pruned) trie = new Trie() // Create a new value raw in DB (is not pruned) - await (trie)._db.db.put(Buffer.from('aa', 'hex')) + await (trie)._db.db.put(utf8ToBytes('aa')) st.ok(!(await trie.verifyPrunedIntegrity()), 'trie is not pruned') - await (trie)._db.db.del(Buffer.from('aa', 'hex')) + await (trie)._db.db.del(utf8ToBytes('aa')) st.ok(await trie.verifyPrunedIntegrity(), 'trie is pruned') - await trie.put(Buffer.from('aa', 'hex'), Buffer.from('bb', 'hex')) + await trie.put(utf8ToBytes('aa'), utf8ToBytes('bb')) st.ok(await trie.verifyPrunedIntegrity(), 'trie is pruned') - await (trie)._db.db.put(Buffer.from('aa', 'hex')) + await (trie)._db.db.put(utf8ToBytes('aa')) st.ok(!(await trie.verifyPrunedIntegrity()), 'trie is not pruned') }) it('should prune when keys are updated or deleted (with `useRootPersistence` enabled)', async (st) => { for (let testID = 0; testID < 1; testID++) { const trie = await Trie.create({ useNodePruning: true, useRootPersistence: true }) - const keys: string[] = [] + const keys: Uint8Array[] = [] for (let i = 0; i < 100; i++) { - keys.push(crypto.randomBytes(32)) + keys.push(randomBytes(32)) } const values: string[] = [] for (let i = 0; i < 1000; i++) { @@ -196,7 +202,7 @@ tape('Pruned trie tests', function (tester) { for (let i = 0; i < keys.length; i++) { const idx = Math.floor(Math.random() * keys.length) const key = keys[idx] - await trie.put(Buffer.from(key), Buffer.from(values[i])) + await trie.put(key, utf8ToBytes(values[i])) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') @@ -204,7 +210,7 @@ tape('Pruned trie tests', function (tester) { // Randomly delete keys for (let i = 0; i < 20; i++) { const idx = Math.floor(Math.random() * keys.length) - await trie.del(Buffer.from(keys[idx])) + await trie.del(keys[idx]) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') @@ -214,9 +220,9 @@ tape('Pruned trie tests', function (tester) { const idx = Math.floor(Math.random() * keys.length) const key = keys[idx] if (Math.random() < 0.5) { - await trie.put(Buffer.from(key), Buffer.from(values[i])) + await trie.put(key, utf8ToBytes(values[i])) } else { - await trie.del(Buffer.from(key)) + await trie.del(key) } } @@ -224,11 +230,11 @@ tape('Pruned trie tests', function (tester) { // Delete all keys for (let idx = 0; idx < 100; idx++) { - await trie.del(Buffer.from(keys[idx])) + await trie.del(keys[idx]) } st.ok(await trie.verifyPrunedIntegrity(), 'trie is correctly pruned') - st.ok(trie.root().equals(KECCAK256_RLP), 'trie is empty') + st.ok(equalsBytes(trie.root(), KECCAK256_RLP), 'trie is empty') let dbKeys = 0 for (const _dbkey of (trie)._db.db._database.keys()) { diff --git a/packages/trie/test/trie/secure.spec.ts b/packages/trie/test/trie/secure.spec.ts index 1bd5ba52a58..30645e3fe20 100644 --- a/packages/trie/test/trie/secure.spec.ts +++ b/packages/trie/test/trie/secure.spec.ts @@ -1,3 +1,10 @@ +import { + bytesToPrefixedHexString, + bytesToUtf8, + equalsBytes, + hexStringToBytes, + utf8ToBytes, +} from '@ethereumjs/util' import { createHash } from 'crypto' import * as tape from 'tape' @@ -5,31 +12,31 @@ import { MapDB, ROOT_DB_KEY, Trie } from '../../src' tape('SecureTrie', function (t) { const trie = new Trie({ useKeyHashing: true, db: new MapDB() }) - const k = Buffer.from('foo') - const v = Buffer.from('bar') + const k = utf8ToBytes('foo') + const v = utf8ToBytes('bar') t.test('put and get value', async function (st) { await trie.put(k, v) const res = await trie.get(k) - st.ok(v.equals(res!)) + st.ok(equalsBytes(v, res!)) st.end() }) t.test('copy trie', async function (st) { const t = trie.copy() const res = await t.get(k) - st.ok(v.equals(res!)) + st.ok(equalsBytes(v, res!)) st.end() }) tape('SecureTrie proof', function (t) { t.test('create a merkle proof and verify it with a single short key', async function (st) { const trie = new Trie({ useKeyHashing: true, db: new MapDB() }) - await trie.put(Buffer.from('key1aa'), Buffer.from('01234')) + await trie.put(utf8ToBytes('key1aa'), utf8ToBytes('01234')) - const proof = await trie.createProof(Buffer.from('key1aa')) - const val = await trie.verifyProof(trie.root(), Buffer.from('key1aa'), proof) - st.equal(val!.toString('utf8'), '01234') + const proof = await trie.createProof(utf8ToBytes('key1aa')) + const val = await trie.verifyProof(trie.root(), utf8ToBytes('key1aa'), proof) + st.equal(bytesToUtf8(val!), '01234') st.end() }) }) @@ -42,11 +49,11 @@ tape('SecureTrie', function (t) { for (const row of jsonTests.emptyValues.in) { const val = row[1] !== undefined && row[1] !== null - ? Buffer.from(row[1]) - : (null as unknown as Buffer) - await trie.put(Buffer.from(row[0]), val) + ? utf8ToBytes(row[1]) + : (null as unknown as Uint8Array) + await trie.put(utf8ToBytes(row[0]), val) } - t.equal('0x' + trie.root().toString('hex'), jsonTests.emptyValues.root) + t.equal(bytesToPrefixedHexString(trie.root()), jsonTests.emptyValues.root) t.end() }) @@ -55,11 +62,11 @@ tape('SecureTrie', function (t) { for (const row of jsonTests.branchingTests.in) { const val = row[1] !== undefined && row[1] !== null - ? Buffer.from(row[1]) - : (null as unknown as Buffer) - await trie.put(Buffer.from(row[0]), val) + ? utf8ToBytes(row[1]) + : (null as unknown as Uint8Array) + await trie.put(utf8ToBytes(row[0]), val) } - t.equal('0x' + trie.root().toString('hex'), jsonTests.branchingTests.root) + t.equal(bytesToPrefixedHexString(trie.root()), jsonTests.branchingTests.root) t.end() }) @@ -67,11 +74,11 @@ tape('SecureTrie', function (t) { for (const row of jsonTests.jeff.in) { let val = row[1] if (val !== undefined && val !== null) { - val = Buffer.from(row[1].slice(2), 'hex') + val = hexStringToBytes(row[1].slice(2)) } - await trie.put(Buffer.from(row[0].slice(2), 'hex'), val) + await trie.put(hexStringToBytes(row[0].slice(2)), val) } - t.equal('0x' + trie.root().toString('hex'), jsonTests.jeff.root.toString('hex')) + t.equal(bytesToPrefixedHexString(trie.root()), jsonTests.jeff.root) t.end() }) @@ -79,7 +86,7 @@ tape('SecureTrie', function (t) { const trie = new Trie({ useKeyHashing: true, db: new MapDB(), useRootPersistence: true }) try { - await trie.put(ROOT_DB_KEY, Buffer.from('bar')) + await trie.put(ROOT_DB_KEY, utf8ToBytes('bar')) st.fail("Attempting to set '__root__' should fail but it did not.") } catch ({ message }) { @@ -90,45 +97,38 @@ tape('SecureTrie', function (t) { }) const trie = new Trie({ useKeyHashing: true, db: new MapDB() }) -const a = Buffer.from( - 'f8448080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf', - 'hex' +const a = hexStringToBytes( + 'f8448080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf' ) -const ak = Buffer.from('095e7baea6a6c7c4c2dfeb977efac326af552d87', 'hex') -const b = Buffer.from( - 'f844802ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0db94dc4aab9b6a1a11956906ea34f3252f394576aece12199b23b269bb2738ab', - 'hex' +const ak = hexStringToBytes('095e7baea6a6c7c4c2dfeb977efac326af552d87') +const b = hexStringToBytes( + 'f844802ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0db94dc4aab9b6a1a11956906ea34f3252f394576aece12199b23b269bb2738ab' ) -const bk = Buffer.from('945304eb96065b2a98b57a48a06ae28d285a71b5', 'hex') -const c = Buffer.from( - 'f84c80880de0b6b3a7640000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex' +const bk = hexStringToBytes('945304eb96065b2a98b57a48a06ae28d285a71b5') +const c = hexStringToBytes( + 'f84c80880de0b6b3a7640000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) -const ck = Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex') +const ck = hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b') // checkpoint // checkpoint // commit -const d = Buffer.from( - 'f8488084535500b1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf', - 'hex' +const d = hexStringToBytes( + 'f8488084535500b1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf' ) -const dk = Buffer.from('095e7baea6a6c7c4c2dfeb977efac326af552d87', 'hex') -const e = Buffer.from( - 'f8478083010851a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0db94dc4aab9b6a1a11956906ea34f3252f394576aece12199b23b269bb2738ab', - 'hex' +const dk = hexStringToBytes('095e7baea6a6c7c4c2dfeb977efac326af552d87') +const e = hexStringToBytes( + 'f8478083010851a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0db94dc4aab9b6a1a11956906ea34f3252f394576aece12199b23b269bb2738ab' ) -const ek = Buffer.from('945304eb96065b2a98b57a48a06ae28d285a71b5', 'hex') -const f = Buffer.from( - 'f84c01880de0b6b3540df72ca056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex' +const ek = hexStringToBytes('945304eb96065b2a98b57a48a06ae28d285a71b5') +const f = hexStringToBytes( + 'f84c01880de0b6b3540df72ca056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) -const fk = Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex') +const fk = hexStringToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b') // commit -const g = Buffer.from( - 'f8488084535500b1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf', - 'hex' +const g = hexStringToBytes( + 'f8488084535500b1a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0a155280bc3c09fd31b0adebbdd4ef3d5128172c0d2008be964dc9e10e0f0fedf' ) -const gk = Buffer.from('095e7baea6a6c7c4c2dfeb977efac326af552d87', 'hex') +const gk = hexStringToBytes('095e7baea6a6c7c4c2dfeb977efac326af552d87') tape('secure tests should not crash', async function (t) { await trie.put(ak, a) @@ -149,24 +149,24 @@ tape('SecureTrie.copy', function (it) { it.test('created copy includes values added after checkpoint', async function (t) { const trie = new Trie({ useKeyHashing: true, db: new MapDB() }) - await trie.put(Buffer.from('key1'), Buffer.from('value1')) + await trie.put(utf8ToBytes('key1'), utf8ToBytes('value1')) trie.checkpoint() - await trie.put(Buffer.from('key2'), Buffer.from('value2')) + await trie.put(utf8ToBytes('key2'), utf8ToBytes('value2')) const trieCopy = trie.copy() - const value = await trieCopy.get(Buffer.from('key2')) - t.equal(value!.toString(), 'value2') + const value = await trieCopy.get(utf8ToBytes('key2')) + t.equal(bytesToUtf8(value!), 'value2') t.end() }) it.test('created copy includes values added before checkpoint', async function (t) { const trie = new Trie({ useKeyHashing: true, db: new MapDB() }) - await trie.put(Buffer.from('key1'), Buffer.from('value1')) + await trie.put(utf8ToBytes('key1'), utf8ToBytes('value1')) trie.checkpoint() - await trie.put(Buffer.from('key2'), Buffer.from('value2')) + await trie.put(utf8ToBytes('key2'), utf8ToBytes('value2')) const trieCopy = trie.copy() - const value = await trieCopy.get(Buffer.from('key1')) - t.equal(value!.toString(), 'value1') + const value = await trieCopy.get(utf8ToBytes('key1')) + t.equal(bytesToUtf8(value!), 'value1') t.end() }) @@ -177,12 +177,12 @@ tape('SecureTrie.copy', function (it) { useKeyHashingFunction: (value) => createHash('sha256').update(value).digest(), }) - await trie.put(Buffer.from('key1'), Buffer.from('value1')) + await trie.put(utf8ToBytes('key1'), utf8ToBytes('value1')) trie.checkpoint() - await trie.put(Buffer.from('key2'), Buffer.from('value2')) + await trie.put(utf8ToBytes('key2'), utf8ToBytes('value2')) const trieCopy = trie.copy() - const value = await trieCopy.get(Buffer.from('key1')) - t.equal(value!.toString(), 'value1') + const value = await trieCopy.get(utf8ToBytes('key1')) + t.equal(bytesToUtf8(value!), 'value1') t.end() }) }) diff --git a/packages/trie/test/trie/trie.spec.ts b/packages/trie/test/trie/trie.spec.ts index 564530a99c5..8ffcf946070 100644 --- a/packages/trie/test/trie/trie.spec.ts +++ b/packages/trie/test/trie/trie.spec.ts @@ -1,13 +1,9 @@ -import { KECCAK256_RLP } from '@ethereumjs/util' +import { KECCAK256_RLP, bytesToHex, equalsBytes, utf8ToBytes } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import * as tape from 'tape' import { ROOT_DB_KEY as BASE_DB_KEY, MapDB, Trie } from '../../src' -function bytesToHex(bytes: Buffer | null) { - return bytes?.toString('hex') -} - for (const { constructor, defaults, title } of [ { constructor: Trie, @@ -23,9 +19,9 @@ for (const { constructor, defaults, title } of [ ]) { const IS_SECURE_TRIE = title === 'SecureTrie' - let ROOT_DB_KEY: Buffer + let ROOT_DB_KEY: Uint8Array if (IS_SECURE_TRIE) { - ROOT_DB_KEY = Buffer.from(keccak256(BASE_DB_KEY)) + ROOT_DB_KEY = keccak256(BASE_DB_KEY) } else { ROOT_DB_KEY = BASE_DB_KEY } @@ -104,7 +100,7 @@ for (const { constructor, defaults, title } of [ // @ts-expect-error st.equal(await trie._db.get(ROOT_DB_KEY), null) - await trie.put(Buffer.from('foo'), Buffer.from('bar')) + await trie.put(utf8ToBytes('foo'), utf8ToBytes('bar')) // @ts-expect-error st.equal(bytesToHex(await trie._db.get(ROOT_DB_KEY)), EXPECTED_ROOTS) @@ -121,12 +117,12 @@ for (const { constructor, defaults, title } of [ }) // @ts-expect-error - st.true((await trie._db.get(ROOT_DB_KEY))?.equals(KECCAK256_RLP)) + st.ok(equalsBytes((await trie._db.get(ROOT_DB_KEY))!, KECCAK256_RLP)) - await trie.put(Buffer.from('foo'), Buffer.from('bar')) + await trie.put(utf8ToBytes('foo'), utf8ToBytes('bar')) // @ts-expect-error - st.false((await trie._db.get(ROOT_DB_KEY))?.equals(KECCAK256_RLP)) + st.false(equalsBytes((await trie._db.get(ROOT_DB_KEY))!, KECCAK256_RLP)) st.end() }) @@ -143,7 +139,7 @@ for (const { constructor, defaults, title } of [ // @ts-expect-error st.equal(await trie._db.get(ROOT_DB_KEY), null) - await trie.put(Buffer.from('do_not_persist_with_db'), Buffer.from('bar')) + await trie.put(utf8ToBytes('do_not_persist_with_db'), utf8ToBytes('bar')) // @ts-expect-error st.equal(await trie._db.get(ROOT_DB_KEY), null) @@ -158,7 +154,7 @@ for (const { constructor, defaults, title } of [ // @ts-expect-error st.equal(await trie._db.get(ROOT_DB_KEY), null) - await trie.put(Buffer.from('do_not_persist_without_db'), Buffer.from('bar')) + await trie.put(utf8ToBytes('do_not_persist_without_db'), utf8ToBytes('bar')) // @ts-expect-error st.notEqual(await trie._db.get(ROOT_DB_KEY), null) @@ -172,7 +168,7 @@ for (const { constructor, defaults, title } of [ const trie = await constructor.create({ ...defaults, db, useRootPersistence: true }) // @ts-expect-error st.equal(await trie._db.get(ROOT_DB_KEY), null) - await trie.put(Buffer.from('foo'), Buffer.from('bar')) + await trie.put(utf8ToBytes('foo'), utf8ToBytes('bar')) // @ts-expect-error st.equal(bytesToHex(await trie._db.get(ROOT_DB_KEY)), EXPECTED_ROOTS) @@ -197,7 +193,7 @@ for (const { constructor, defaults, title } of [ const trie = new constructor({ ...defaults, db: new MapDB(), useRootPersistence: true }) try { - await trie.put(BASE_DB_KEY, Buffer.from('bar')) + await trie.put(BASE_DB_KEY, utf8ToBytes('bar')) st.fail("Attempting to set '__root__' should fail but it did not.") } catch ({ message }) { st.equal(message, "Attempted to set '__root__' key but it is not allowed.") diff --git a/packages/tx/examples/custom-chain-tx.ts b/packages/tx/examples/custom-chain-tx.ts index 1e600470581..ecb8cf60175 100644 --- a/packages/tx/examples/custom-chain-tx.ts +++ b/packages/tx/examples/custom-chain-tx.ts @@ -1,6 +1,7 @@ import { Address } from '@ethereumjs/util' import { Common } from '@ethereumjs/common' import { Transaction } from '../src' +import { hexToBytes } from 'ethereum-cryptography/utils' // In this example we create a transaction for a custom network. @@ -34,10 +35,7 @@ const tx = Transaction.fromTxData( // Once we created the transaction using the custom Common object, we can use it as a normal tx. // Here we sign it and validate its signature -const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const privateKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const signedTx = tx.sign(privateKey) const address = Address.fromPrivateKey(privateKey) diff --git a/packages/tx/examples/ropsten-tx.ts b/packages/tx/examples/ropsten-tx.ts index 0b1f50c823a..4f4803dc779 100644 --- a/packages/tx/examples/ropsten-tx.ts +++ b/packages/tx/examples/ropsten-tx.ts @@ -1,8 +1,8 @@ import { Transaction } from '../src' -import { toBuffer } from '@ethereumjs/util' +import { toBytes } from '@ethereumjs/util' import { Chain, Common, Hardfork } from '@ethereumjs/common' -const txData = toBuffer( +const txData = toBytes( '0xf9010b82930284d09dc30083419ce0942d18de92e0f9aee1a29770c3b15c6cf8ac5498e580b8a42f43f4fb0000000000000000000000000000000000000000000000000000016b78998da900000000000000000000000000000000000000000000000000000000000cb1b70000000000000000000000000000000000000000000000000000000000000fa00000000000000000000000000000000000000000000000000000000001363e4f00000000000000000000000000000000000000000000000000000000000186a029a0fac36e66d329af0e831b2e61179b3ec8d7c7a8a2179e303cfed3364aff2bc3e4a07cb73d56e561ccbd838818dd3dea5fa0b5158577ffc61c0e6ec1f0ed55716891' ) diff --git a/packages/tx/examples/transactions.ts b/packages/tx/examples/transactions.ts index dc3a8dc1c9a..67a00c554f8 100644 --- a/packages/tx/examples/transactions.ts +++ b/packages/tx/examples/transactions.ts @@ -3,7 +3,8 @@ // Install the dependencies and run `npx ts-node examples/transactions.ts` import { Transaction } from '../src' -import { toBuffer } from '@ethereumjs/util' +import { bytesToPrefixedHexString, toBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' // We create an unsigned transaction. // Notice we don't set the `to` field because we are creating a new contract. @@ -17,10 +18,7 @@ const tx = Transaction.fromTxData({ }) // We sign the transaction with this private key. -const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const privateKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const signedTx = tx.sign(privateKey) @@ -33,7 +31,7 @@ console.log('Total Amount of wei needed:' + feeCost.toString()) // Lets serialize the transaction console.log('---Serialized TX----') -console.log(signedTx.serialize().toString('hex')) +console.log(bytesToPrefixedHexString(signedTx.serialize())) console.log('--------------------') // Parsing & Validating Transactions @@ -53,7 +51,7 @@ const rawTx = [ '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13', ] -const tx2 = Transaction.fromValuesArray(rawTx.map(toBuffer)) // This is also a mainnet transaction +const tx2 = Transaction.fromValuesArray(rawTx.map(toBytes)) // This is also a mainnet transaction // So assuming that you were able to parse the transaction, we will now get the sender's address. diff --git a/packages/tx/src/baseTransaction.ts b/packages/tx/src/baseTransaction.ts index 06ba3a82463..5cb2ffb13f5 100644 --- a/packages/tx/src/baseTransaction.ts +++ b/packages/tx/src/baseTransaction.ts @@ -4,12 +4,12 @@ import { MAX_INTEGER, MAX_UINT64, SECP256K1_ORDER_DIV_2, - bufferToBigInt, - bufferToHex, + bytesToBigInt, + bytesToHex, ecsign, publicToAddress, - toBuffer, - unpadBuffer, + toBytes, + unpadBytes, } from '@ethereumjs/util' import { Capability } from './types' @@ -28,7 +28,7 @@ import type { import type { BigIntLike } from '@ethereumjs/util' interface TransactionCache { - hash: Buffer | undefined + hash: Uint8Array | undefined dataFee?: { value: bigint hardfork: string | Hardfork @@ -49,7 +49,7 @@ export abstract class BaseTransaction { public readonly gasLimit: bigint public readonly to?: Address public readonly value: bigint - public readonly data: Buffer + public readonly data: Uint8Array public readonly v?: bigint public readonly r?: bigint @@ -91,24 +91,24 @@ export abstract class BaseTransaction { constructor(txData: TxData | AccessListEIP2930TxData | FeeMarketEIP1559TxData, opts: TxOptions) { const { nonce, gasLimit, to, value, data, v, r, s, type } = txData - this._type = Number(bufferToBigInt(toBuffer(type))) + this._type = Number(bytesToBigInt(toBytes(type))) this.txOptions = opts - const toB = toBuffer(to === '' ? '0x' : to) - const vB = toBuffer(v === '' ? '0x' : v) - const rB = toBuffer(r === '' ? '0x' : r) - const sB = toBuffer(s === '' ? '0x' : s) + const toB = toBytes(to === '' ? '0x' : to) + const vB = toBytes(v === '' ? '0x' : v) + const rB = toBytes(r === '' ? '0x' : r) + const sB = toBytes(s === '' ? '0x' : s) - this.nonce = bufferToBigInt(toBuffer(nonce === '' ? '0x' : nonce)) - this.gasLimit = bufferToBigInt(toBuffer(gasLimit === '' ? '0x' : gasLimit)) + this.nonce = bytesToBigInt(toBytes(nonce === '' ? '0x' : nonce)) + this.gasLimit = bytesToBigInt(toBytes(gasLimit === '' ? '0x' : gasLimit)) this.to = toB.length > 0 ? new Address(toB) : undefined - this.value = bufferToBigInt(toBuffer(value === '' ? '0x' : value)) - this.data = toBuffer(data === '' ? '0x' : data) + this.value = bytesToBigInt(toBytes(value === '' ? '0x' : value)) + this.data = toBytes(data === '' ? '0x' : data) - this.v = vB.length > 0 ? bufferToBigInt(vB) : undefined - this.r = rB.length > 0 ? bufferToBigInt(rB) : undefined - this.s = sB.length > 0 ? bufferToBigInt(sB) : undefined + this.v = vB.length > 0 ? bytesToBigInt(vB) : undefined + this.r = rB.length > 0 ? bytesToBigInt(rB) : undefined + this.s = sB.length > 0 ? bytesToBigInt(sB) : undefined this._validateCannotExceedMaxInteger({ value: this.value, r: this.r, s: this.s }) @@ -242,16 +242,16 @@ export abstract class BaseTransaction { * If the tx's `to` is to the creation address */ toCreationAddress(): boolean { - return this.to === undefined || this.to.buf.length === 0 + return this.to === undefined || this.to.bytes.length === 0 } /** - * Returns a Buffer Array of the raw Buffers of this transaction, in order. + * Returns a Uint8Array Array of the raw Bytes of this transaction, in order. * * Use {@link BaseTransaction.serialize} to add a transaction to a block * with {@link Block.fromValuesArray}. * - * For an unsigned tx this method uses the empty Buffer values for the + * For an unsigned tx this method uses the empty Bytes values for the * signature parameters `v`, `r` and `s` for encoding. For an EIP-155 compliant * representation for external signing use {@link BaseTransaction.getMessageToSign}. */ @@ -260,18 +260,18 @@ export abstract class BaseTransaction { /** * Returns the encoding of the transaction. */ - abstract serialize(): Buffer + abstract serialize(): Uint8Array // Returns the unsigned tx (hashed or raw), which is used to sign the transaction. // // Note: do not use code docs here since VS Studio is then not able to detect the // comments from the inherited methods - abstract getMessageToSign(hashMessage: false): Buffer | Buffer[] - abstract getMessageToSign(hashMessage?: true): Buffer + abstract getMessageToSign(hashMessage: false): Uint8Array | Uint8Array[] + abstract getMessageToSign(hashMessage?: true): Uint8Array - abstract hash(): Buffer + abstract hash(): Uint8Array - abstract getMessageToVerifySignature(): Buffer + abstract getMessageToVerifySignature(): Uint8Array public isSigned(): boolean { const { v, r, s } = this @@ -289,7 +289,7 @@ export abstract class BaseTransaction { try { // Main signature verification is done in `getSenderPublicKey()` const publicKey = this.getSenderPublicKey() - return unpadBuffer(publicKey).length !== 0 + return unpadBytes(publicKey).length !== 0 } catch (e: any) { return false } @@ -305,7 +305,7 @@ export abstract class BaseTransaction { /** * Returns the public key of the sender */ - abstract getSenderPublicKey(): Buffer + abstract getSenderPublicKey(): Uint8Array /** * Signs a transaction. @@ -316,7 +316,7 @@ export abstract class BaseTransaction { * const signedTx = tx.sign(privateKey) * ``` */ - sign(privateKey: Buffer): TransactionObject { + sign(privateKey: Uint8Array): TransactionObject { if (privateKey.length !== 32) { const msg = this._errorMsg('Private key must be 32 bytes in length.') throw new Error(msg) @@ -357,7 +357,7 @@ export abstract class BaseTransaction { abstract toJSON(): JsonTx // Accept the v,r,s values from the `sign` method, and convert this into a TransactionObject - protected abstract _processSignature(v: bigint, r: Buffer, s: Buffer): TransactionObject + protected abstract _processSignature(v: bigint, r: Uint8Array, s: Uint8Array): TransactionObject /** * Does chain ID checks on common and returns a common @@ -370,7 +370,7 @@ export abstract class BaseTransaction { protected _getCommon(common?: Common, chainId?: BigIntLike) { // Chain ID provided if (chainId !== undefined) { - const chainIdBigInt = bufferToBigInt(toBuffer(chainId)) + const chainIdBigInt = bytesToBigInt(toBytes(chainId)) if (common) { if (common.chainId() !== chainIdBigInt) { const msg = this._errorMsg('The chain ID does not match the chain ID of Common') @@ -504,7 +504,7 @@ export abstract class BaseTransaction { protected _getSharedErrorPostfix() { let hash = '' try { - hash = this.isSigned() ? bufferToHex(this.hash()) : 'not available (unsigned)' + hash = this.isSigned() ? bytesToHex(this.hash()) : 'not available (unsigned)' } catch (e: any) { hash = 'error' } diff --git a/packages/tx/src/eip1559Transaction.ts b/packages/tx/src/eip1559Transaction.ts index a1532f6f0b5..a98af66b003 100644 --- a/packages/tx/src/eip1559Transaction.ts +++ b/packages/tx/src/eip1559Transaction.ts @@ -1,13 +1,16 @@ import { RLP } from '@ethereumjs/rlp' import { MAX_INTEGER, - arrToBufArr, bigIntToHex, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToHex, + bytesToPrefixedHexString, + concatBytes, ecrecover, - toBuffer, + equalsBytes, + hexStringToBytes, + toBytes, validateNoLeadingZeroes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -17,7 +20,7 @@ import { AccessLists } from './util' import type { AccessList, - AccessListBuffer, + AccessListBytes, FeeMarketEIP1559TxData, FeeMarketEIP1559ValuesArray, JsonTx, @@ -26,7 +29,7 @@ import type { import type { Common } from '@ethereumjs/common' const TRANSACTION_TYPE = 2 -const TRANSACTION_TYPE_BUFFER = Buffer.from(TRANSACTION_TYPE.toString(16).padStart(2, '0'), 'hex') +const TRANSACTION_TYPE_BYTES = hexStringToBytes(TRANSACTION_TYPE.toString(16).padStart(2, '0')) /** * Typed transaction with a new gas fee market mechanism @@ -36,7 +39,7 @@ const TRANSACTION_TYPE_BUFFER = Buffer.from(TRANSACTION_TYPE.toString(16).padSta */ export class FeeMarketEIP1559Transaction extends BaseTransaction { public readonly chainId: bigint - public readonly accessList: AccessListBuffer + public readonly accessList: AccessListBytes public readonly AccessListJSON: AccessList public readonly maxPriorityFeePerGas: bigint public readonly maxFeePerGas: bigint @@ -71,16 +74,16 @@ export class FeeMarketEIP1559Transaction extends BaseTransaction { public readonly chainId: bigint - public readonly accessList: AccessListBuffer + public readonly accessList: AccessListBytes public readonly AccessListJSON: AccessList public readonly gasPrice: bigint @@ -70,22 +73,25 @@ export class AccessListEIP2930Transaction extends BaseTransaction { public readonly chainId: bigint - public readonly accessList: AccessListBuffer + public readonly accessList: AccessListBytes public readonly AccessListJSON: AccessList public readonly maxPriorityFeePerGas: bigint public readonly maxFeePerGas: bigint public readonly maxFeePerDataGas: bigint public readonly common: Common - public versionedHashes: Buffer[] - blobs?: Buffer[] // This property should only be populated when the transaction is in the "Network Wrapper" format - kzgCommitments?: Buffer[] // This property should only be populated when the transaction is in the "Network Wrapper" format - aggregateKzgProof?: Buffer // This property should only be populated when the transaction is in the "Network Wrapper" format + public versionedHashes: Uint8Array[] + blobs?: Uint8Array[] // This property should only be populated when the transaction is in the "Network Wrapper" format + kzgCommitments?: Uint8Array[] // This property should only be populated when the transaction is in the "Network Wrapper" format + aggregateKzgProof?: Uint8Array // This property should only be populated when the transaction is in the "Network Wrapper" format /** * This constructor takes the values, validates them, assigns them and freezes the object. @@ -111,9 +114,9 @@ export class BlobEIP4844Transaction extends BaseTransaction toBuffer(vh)) + this.versionedHashes = (txData.versionedHashes ?? []).map((vh) => toBytes(vh)) this._validateYParity() this._validateHighS() @@ -160,9 +163,9 @@ export class BlobEIP4844Transaction extends BaseTransaction toBuffer(blob)) - this.kzgCommitments = txData.kzgCommitments?.map((commitment) => toBuffer(commitment)) - this.aggregateKzgProof = toBuffer(txData.kzgProof) + this.blobs = txData.blobs?.map((blob) => toBytes(blob)) + this.kzgCommitments = txData.kzgCommitments?.map((commitment) => toBytes(commitment)) + this.aggregateKzgProof = toBytes(txData.kzgProof) const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) @@ -199,14 +202,14 @@ export class BlobEIP4844Transaction extends BaseTransaction Buffer.from(key)) - const accessListItem: AccessListBufferItem = [address, storageKeys] + const accessListItem: AccessListBytesItem = [listItem.address, listItem.storageKeys] accessList.push(accessListItem) } const to = decodedTx.to.value === null ? undefined - : Address.fromString(bufferToHex(Buffer.from(decodedTx.to.value))) + : Address.fromString(bytesToPrefixedHexString(decodedTx.to.value)) - const versionedHashes = decodedTx.blobVersionedHashes.map((el) => Buffer.from(el)) - const commitments = wrapper.blobKzgs.map((el) => Buffer.from(el)) - const blobs = wrapper.blobs.map((el) => Buffer.from(el)) const txData = { ...decodedTx, ...{ - versionedHashes, + versionedHashes: decodedTx.blobVersionedHashes, accessList, to, - blobs, - kzgCommitments: commitments, - kzgProof: Buffer.from(wrapper.kzgAggregatedProof), + blobs: wrapper.blobKzgs, + kzgCommitments: wrapper.blobKzgs, + kzgProof: wrapper.kzgAggregatedProof, r: wrapper.tx.signature.r, s: wrapper.tx.signature.s, v: BigInt(wrapper.tx.signature.yParity), @@ -255,23 +253,20 @@ export class BlobEIP4844Transaction extends BaseTransaction Buffer.from(key)) - const accessListItem: AccessListBufferItem = [address, storageKeys] + const accessListItem: AccessListBytesItem = [listItem.address, listItem.storageKeys] accessList.push(accessListItem) } - const to = - tx.to.value === null ? undefined : Address.fromString(bufferToHex(Buffer.from(tx.to.value))) - const versionedHashes = tx.blobVersionedHashes.map((el) => Buffer.from(el)) + const to = tx.to.value === null ? undefined : Address.fromString(bytesToHex(tx.to.value)) + const versionedHashes = tx.blobVersionedHashes const txData = { ...tx, ...{ @@ -310,7 +305,7 @@ export class BlobEIP4844Transaction extends BaseTransaction { const to = { selector: this.to !== undefined ? 1 : 0, - value: this.to?.toBuffer() ?? null, + value: this.to?.toBytes() ?? null, } return { message: { @@ -341,15 +336,15 @@ export class BlobEIP4844Transaction extends BaseTransaction Uint8Array.from(blob)) ?? [] @@ -371,35 +366,35 @@ export class BlobEIP4844Transaction extends BaseTransaction bufferToHex(hash)), + versionedHashes: this.versionedHashes.map((hash) => bytesToHex(hash)), } } - _processSignature(v: bigint, r: Buffer, s: Buffer): BlobEIP4844Transaction { + _processSignature(v: bigint, r: Uint8Array, s: Uint8Array): BlobEIP4844Transaction { const opts = { ...this.txOptions, common: this.common } return BlobEIP4844Transaction.fromTxData( @@ -458,8 +453,8 @@ export class BlobEIP4844Transaction extends BaseTransaction { // strict byte length checking txParams.to = txParams.to !== null && txParams.to !== undefined - ? setLengthLeft(toBuffer(txParams.to), 20) + ? setLengthLeft(toBytes(txParams.to), 20) : null txParams.v = toType(txParams.v, TypeOutput.BigInt) diff --git a/packages/tx/src/legacyTransaction.ts b/packages/tx/src/legacyTransaction.ts index fa67af06bbc..3933de928dc 100644 --- a/packages/tx/src/legacyTransaction.ts +++ b/packages/tx/src/legacyTransaction.ts @@ -1,14 +1,13 @@ import { RLP } from '@ethereumjs/rlp' import { MAX_INTEGER, - arrToBufArr, bigIntToHex, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToPrefixedHexString, ecrecover, - toBuffer, - unpadBuffer, + toBytes, + unpadBytes, validateNoLeadingZeroes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' @@ -52,14 +51,14 @@ export class Transaction extends BaseTransaction { * * Format: `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` */ - public static fromSerializedTx(serialized: Buffer, opts: TxOptions = {}) { - const values = arrToBufArr(RLP.decode(Uint8Array.from(serialized))) as Buffer[] + public static fromSerializedTx(serialized: Uint8Array, opts: TxOptions = {}) { + const values = RLP.decode(serialized) if (!Array.isArray(values)) { throw new Error('Invalid serialized tx input. Must be array') } - return this.fromValuesArray(values, opts) + return this.fromValuesArray(values as TxValuesArray, opts) } /** @@ -68,7 +67,7 @@ export class Transaction extends BaseTransaction { * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]` */ public static fromValuesArray(values: TxValuesArray, opts: TxOptions = {}) { - // If length is not 6, it has length 9. If v/r/s are empty Buffers, it is still an unsigned transaction + // If length is not 6, it has length 9. If v/r/s are empty Uint8Arrays, it is still an unsigned transaction // This happens if you get the RLP data from `raw()` if (values.length !== 6 && values.length !== 9) { throw new Error( @@ -108,7 +107,7 @@ export class Transaction extends BaseTransaction { this.common = this._validateTxV(this.v, opts.common) - this.gasPrice = bufferToBigInt(toBuffer(txData.gasPrice === '' ? '0x' : txData.gasPrice)) + this.gasPrice = bytesToBigInt(toBytes(txData.gasPrice === '' ? '0x' : txData.gasPrice)) if (this.gasPrice * this.gasLimit > MAX_INTEGER) { const msg = this._errorMsg('gas limit * gasPrice cannot exceed MAX_INTEGER (2^256-1)') @@ -140,7 +139,7 @@ export class Transaction extends BaseTransaction { } /** - * Returns a Buffer Array of the raw Buffers of the legacy transaction, in order. + * Returns a Uint8Array Array of the raw Bytes of the legacy transaction, in order. * * Format: `[nonce, gasPrice, gasLimit, to, value, data, v, r, s]` * @@ -148,21 +147,21 @@ export class Transaction extends BaseTransaction { * to a block with {@link Block.fromValuesArray} (use the `serialize()` method * for typed txs). * - * For an unsigned tx this method returns the empty Buffer values + * For an unsigned tx this method returns the empty Bytes values * for the signature parameters `v`, `r` and `s`. For an EIP-155 compliant * representation have a look at {@link Transaction.getMessageToSign}. */ raw(): TxValuesArray { return [ - bigIntToUnpaddedBuffer(this.nonce), - bigIntToUnpaddedBuffer(this.gasPrice), - bigIntToUnpaddedBuffer(this.gasLimit), - this.to !== undefined ? this.to.buf : Buffer.from([]), - bigIntToUnpaddedBuffer(this.value), + bigIntToUnpaddedBytes(this.nonce), + bigIntToUnpaddedBytes(this.gasPrice), + bigIntToUnpaddedBytes(this.gasLimit), + this.to !== undefined ? this.to.bytes : new Uint8Array(0), + bigIntToUnpaddedBytes(this.value), this.data, - this.v !== undefined ? bigIntToUnpaddedBuffer(this.v) : Buffer.from([]), - this.r !== undefined ? bigIntToUnpaddedBuffer(this.r) : Buffer.from([]), - this.s !== undefined ? bigIntToUnpaddedBuffer(this.s) : Buffer.from([]), + this.v !== undefined ? bigIntToUnpaddedBytes(this.v) : new Uint8Array(0), + this.r !== undefined ? bigIntToUnpaddedBytes(this.r) : new Uint8Array(0), + this.s !== undefined ? bigIntToUnpaddedBytes(this.s) : new Uint8Array(0), ] } @@ -171,28 +170,28 @@ export class Transaction extends BaseTransaction { * * Format: `rlp([nonce, gasPrice, gasLimit, to, value, data, v, r, s])` * - * For an unsigned tx this method uses the empty Buffer values for the + * For an unsigned tx this method uses the empty Uint8Array values for the * signature parameters `v`, `r` and `s` for encoding. For an EIP-155 compliant * representation for external signing use {@link Transaction.getMessageToSign}. */ - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw()))) + serialize(): Uint8Array { + return RLP.encode(this.raw()) } private _getMessageToSign() { const values = [ - bigIntToUnpaddedBuffer(this.nonce), - bigIntToUnpaddedBuffer(this.gasPrice), - bigIntToUnpaddedBuffer(this.gasLimit), - this.to !== undefined ? this.to.buf : Buffer.from([]), - bigIntToUnpaddedBuffer(this.value), + bigIntToUnpaddedBytes(this.nonce), + bigIntToUnpaddedBytes(this.gasPrice), + bigIntToUnpaddedBytes(this.gasLimit), + this.to !== undefined ? this.to.bytes : new Uint8Array(0), + bigIntToUnpaddedBytes(this.value), this.data, ] if (this.supports(Capability.EIP155ReplayProtection)) { - values.push(toBuffer(this.common.chainId())) - values.push(unpadBuffer(toBuffer(0))) - values.push(unpadBuffer(toBuffer(0))) + values.push(toBytes(this.common.chainId())) + values.push(unpadBytes(toBytes(0))) + values.push(unpadBytes(toBytes(0))) } return values @@ -206,20 +205,19 @@ export class Transaction extends BaseTransaction { * and you might need to do yourself with: * * ```javascript - * import { bufArrToArr } from '@ethereumjs/util' * import { RLP } from '@ethereumjs/rlp' * const message = tx.getMessageToSign(false) - * const serializedMessage = Buffer.from(RLP.encode(bufArrToArr(message))) // use this for the HW wallet input + * const serializedMessage = RLP.encode(message)) // use this for the HW wallet input * ``` * * @param hashMessage - Return hashed message if set to true (default: true) */ - getMessageToSign(hashMessage: false): Buffer[] - getMessageToSign(hashMessage?: true): Buffer + getMessageToSign(hashMessage: false): Uint8Array[] + getMessageToSign(hashMessage?: true): Uint8Array getMessageToSign(hashMessage = true) { const message = this._getMessageToSign() if (hashMessage) { - return Buffer.from(keccak256(RLP.encode(bufArrToArr(message)))) + return keccak256(RLP.encode(message)) } else { return message } @@ -256,7 +254,7 @@ export class Transaction extends BaseTransaction { * This method can only be used for signed txs (it throws otherwise). * Use {@link Transaction.getMessageToSign} to get a tx hash for the purpose of signing. */ - hash(): Buffer { + hash(): Uint8Array { if (!this.isSigned()) { const msg = this._errorMsg('Cannot call hash method if transaction is not signed') throw new Error(msg) @@ -264,12 +262,12 @@ export class Transaction extends BaseTransaction { if (Object.isFrozen(this)) { if (!this.cache.hash) { - this.cache.hash = Buffer.from(keccak256(RLP.encode(bufArrToArr(this.raw())))) + this.cache.hash = keccak256(RLP.encode(this.raw())) } return this.cache.hash } - return Buffer.from(keccak256(RLP.encode(bufArrToArr(this.raw())))) + return keccak256(RLP.encode(this.raw())) } /** @@ -281,13 +279,13 @@ export class Transaction extends BaseTransaction { throw new Error(msg) } const message = this._getMessageToSign() - return Buffer.from(keccak256(RLP.encode(bufArrToArr(message)))) + return keccak256(RLP.encode(message)) } /** * Returns the public key of the sender */ - getSenderPublicKey(): Buffer { + getSenderPublicKey(): Uint8Array { const msgHash = this.getMessageToVerifySignature() const { v, r, s } = this @@ -298,8 +296,8 @@ export class Transaction extends BaseTransaction { return ecrecover( msgHash, v!, - bigIntToUnpaddedBuffer(r!), - bigIntToUnpaddedBuffer(s!), + bigIntToUnpaddedBytes(r!), + bigIntToUnpaddedBytes(s!), this.supports(Capability.EIP155ReplayProtection) ? this.common.chainId() : undefined ) } catch (e: any) { @@ -311,7 +309,7 @@ export class Transaction extends BaseTransaction { /** * Process the v, r, s values from the `sign` method of the base transaction. */ - protected _processSignature(v: bigint, r: Buffer, s: Buffer) { + protected _processSignature(v: bigint, r: Uint8Array, s: Uint8Array) { if (this.supports(Capability.EIP155ReplayProtection)) { v += this.common.chainId() * BigInt(2) + BigInt(8) } @@ -327,8 +325,8 @@ export class Transaction extends BaseTransaction { value: this.value, data: this.data, v, - r: bufferToBigInt(r), - s: bufferToBigInt(s), + r: bytesToBigInt(r), + s: bytesToBigInt(s), }, opts ) @@ -344,7 +342,7 @@ export class Transaction extends BaseTransaction { gasLimit: bigIntToHex(this.gasLimit), to: this.to !== undefined ? this.to.toString() : undefined, value: bigIntToHex(this.value), - data: '0x' + this.data.toString('hex'), + data: bytesToPrefixedHexString(this.data), v: this.v !== undefined ? bigIntToHex(this.v) : undefined, r: this.r !== undefined ? bigIntToHex(this.r) : undefined, s: this.s !== undefined ? bigIntToHex(this.s) : undefined, diff --git a/packages/tx/src/transactionFactory.ts b/packages/tx/src/transactionFactory.ts index c0537a93e35..7809d0bcff4 100644 --- a/packages/tx/src/transactionFactory.ts +++ b/packages/tx/src/transactionFactory.ts @@ -1,4 +1,4 @@ -import { bufferToBigInt, toBuffer } from '@ethereumjs/util' +import { bytesToBigInt, toBytes } from '@ethereumjs/util' import { JsonRpcProvider } from '@ethersproject/providers' import { FeeMarketEIP1559Transaction } from './eip1559Transaction' @@ -34,7 +34,7 @@ export class TransactionFactory { // Assume legacy transaction return Transaction.fromTxData(txData, txOptions) } else { - const txType = Number(bufferToBigInt(toBuffer(txData.type))) + const txType = Number(bytesToBigInt(toBytes(txData.type))) if (txType === 0) { return Transaction.fromTxData(txData, txOptions) } else if (txType === 1) { @@ -52,10 +52,10 @@ export class TransactionFactory { /** * This method tries to decode serialized data. * - * @param data - The data Buffer + * @param data - The data Uint8Array * @param txOptions - The transaction options */ - public static fromSerializedData(data: Buffer, txOptions: TxOptions = {}): TypedTransaction { + public static fromSerializedData(data: Uint8Array, txOptions: TxOptions = {}): TypedTransaction { if (data[0] <= 0x7f) { // Determine the type. switch (data[0]) { @@ -75,15 +75,15 @@ export class TransactionFactory { /** * When decoding a BlockBody, in the transactions field, a field is either: - * A Buffer (a TypedTransaction - encoded as TransactionType || rlp(TransactionPayload)) - * A Buffer[] (Legacy Transaction) + * A Uint8Array (a TypedTransaction - encoded as TransactionType || rlp(TransactionPayload)) + * A Uint8Array[] (Legacy Transaction) * This method returns the right transaction. * - * @param data - A Buffer or Buffer[] + * @param data - A Uint8Array or Uint8Array[] * @param txOptions - The transaction options */ - public static fromBlockBodyData(data: Buffer | Buffer[], txOptions: TxOptions = {}) { - if (Buffer.isBuffer(data)) { + public static fromBlockBodyData(data: Uint8Array | Uint8Array[], txOptions: TxOptions = {}) { + if (data instanceof Uint8Array) { return this.fromSerializedData(data, txOptions) } else if (Array.isArray(data)) { // It is a legacy transaction diff --git a/packages/tx/src/types.ts b/packages/tx/src/types.ts index d4aaff01fd1..ebc95b241ff 100644 --- a/packages/tx/src/types.ts +++ b/packages/tx/src/types.ts @@ -24,7 +24,7 @@ import type { AccessListEIP2930Transaction } from './eip2930Transaction' import type { BlobEIP4844Transaction } from './eip4844Transaction' import type { Transaction } from './legacyTransaction' import type { Common } from '@ethereumjs/common' -import type { AddressLike, BigIntLike, BufferLike, PrefixedHexString } from '@ethereumjs/util' +import type { AddressLike, BigIntLike, BytesLike, PrefixedHexString } from '@ethereumjs/util' const Bytes20 = new ByteVectorType(20) const Bytes32 = new ByteVectorType(32) @@ -108,15 +108,13 @@ export type AccessListItem = { } /* - * An Access List as a tuple of [address: Buffer, storageKeys: Buffer[]] + * An Access List as a tuple of [address: Uint8Array, storageKeys: Uint8Array[]] */ -export type AccessListBufferItem = [Buffer, Buffer[]] -export type AccessListBuffer = AccessListBufferItem[] +export type AccessListBytesItem = [Uint8Array, Uint8Array[]] +export type AccessListBytes = AccessListBytesItem[] export type AccessList = AccessListItem[] -export function isAccessListBuffer( - input: AccessListBuffer | AccessList -): input is AccessListBuffer { +export function isAccessListBytes(input: AccessListBytes | AccessList): input is AccessListBytes { if (input.length === 0) { return true } @@ -127,8 +125,8 @@ export function isAccessListBuffer( return false } -export function isAccessList(input: AccessListBuffer | AccessList): input is AccessList { - return !isAccessListBuffer(input) // This is exactly the same method, except the output is negated. +export function isAccessList(input: AccessListBytes | AccessList): input is AccessList { + return !isAccessListBytes(input) // This is exactly the same method, except the output is negated. } /** @@ -175,7 +173,7 @@ export type TxData = { /** * This will contain the data of the message or the init of a contract. */ - data?: BufferLike + data?: BytesLike /** * EC recovery ID. @@ -211,7 +209,7 @@ export interface AccessListEIP2930TxData extends TxData { /** * The access list which contains the addresses/storage slots which the transaction wishes to access */ - accessList?: AccessListBuffer | AccessList | null + accessList?: AccessListBytes | AccessList | null } /** @@ -240,7 +238,7 @@ export interface BlobEIP4844TxData extends FeeMarketEIP1559TxData { /** * The versioned hashes used to validate the blobs attached to a transaction */ - versionedHashes?: BufferLike[] + versionedHashes?: BytesLike[] /** * The maximum fee per data gas paid for the transaction */ @@ -248,55 +246,55 @@ export interface BlobEIP4844TxData extends FeeMarketEIP1559TxData { /** * The blobs associated with a transaction */ - blobs?: BufferLike[] + blobs?: BytesLike[] /** * The KZG commitments corresponding to the versioned hashes for each blob */ - kzgCommitments?: BufferLike[] + kzgCommitments?: BytesLike[] /** * The aggregate KZG proof associated with the transaction */ - kzgProof?: BufferLike + kzgProof?: BytesLike } /** * Buffer values array for a legacy {@link Transaction} */ -export type TxValuesArray = Buffer[] +export type TxValuesArray = Uint8Array[] /** * Buffer values array for an {@link AccessListEIP2930Transaction} */ export type AccessListEIP2930ValuesArray = [ - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - AccessListBuffer, - Buffer?, - Buffer?, - Buffer? + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + AccessListBytes, + Uint8Array?, + Uint8Array?, + Uint8Array? ] /** * Buffer values array for a {@link FeeMarketEIP1559Transaction} */ export type FeeMarketEIP1559ValuesArray = [ - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - Buffer, - AccessListBuffer, - Buffer?, - Buffer?, - Buffer? + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + Uint8Array, + AccessListBytes, + Uint8Array?, + Uint8Array?, + Uint8Array? ] type JsonAccessListItem = { address: string; storageKeys: string[] } diff --git a/packages/tx/src/util.ts b/packages/tx/src/util.ts index 958002c2d5a..bded6cba968 100644 --- a/packages/tx/src/util.ts +++ b/packages/tx/src/util.ts @@ -1,9 +1,9 @@ -import { bufferToHex, setLengthLeft, toBuffer } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, setLengthLeft } from '@ethereumjs/util' import { isAccessList } from './types' import type { BlobEIP4844Transaction } from './eip4844Transaction' -import type { AccessList, AccessListBuffer, AccessListItem } from './types' +import type { AccessList, AccessListBytes, AccessListItem } from './types' import type { Common } from '@ethereumjs/common' export function checkMaxInitCodeSize(common: Common, length: number) { @@ -19,21 +19,21 @@ export function checkMaxInitCodeSize(common: Common, length: number) { } export class AccessLists { - public static getAccessListData(accessList: AccessListBuffer | AccessList) { + public static getAccessListData(accessList: AccessListBytes | AccessList) { let AccessListJSON let bufferAccessList if (isAccessList(accessList)) { AccessListJSON = accessList - const newAccessList: AccessListBuffer = [] + const newAccessList: AccessListBytes = [] for (let i = 0; i < accessList.length; i++) { const item: AccessListItem = accessList[i] - const addressBuffer = toBuffer(item.address) - const storageItems: Buffer[] = [] + const addressBytes = hexStringToBytes(item.address) + const storageItems: Uint8Array[] = [] for (let index = 0; index < item.storageKeys.length; index++) { - storageItems.push(toBuffer(item.storageKeys[index])) + storageItems.push(hexStringToBytes(item.storageKeys[index])) } - newAccessList.push([addressBuffer, storageItems]) + newAccessList.push([addressBytes, storageItems]) } bufferAccessList = newAccessList } else { @@ -42,10 +42,10 @@ export class AccessLists { const json: AccessList = [] for (let i = 0; i < bufferAccessList.length; i++) { const data = bufferAccessList[i] - const address = bufferToHex(data[0]) + const address = bytesToPrefixedHexString(data[0]) const storageKeys: string[] = [] for (let item = 0; item < data[1].length; item++) { - storageKeys.push(bufferToHex(data[1][item])) + storageKeys.push(bytesToPrefixedHexString(data[1][item])) } const jsonItem: AccessListItem = { address, @@ -62,11 +62,11 @@ export class AccessLists { } } - public static verifyAccessList(accessList: AccessListBuffer) { + public static verifyAccessList(accessList: AccessListBytes) { for (let key = 0; key < accessList.length; key++) { const accessListItem = accessList[key] - const address = accessListItem[0] - const storageSlots = accessListItem[1] + const address = accessListItem[0] + const storageSlots = accessListItem[1] if ((accessListItem)[2] !== undefined) { throw new Error( 'Access list item cannot have 3 elements. It can only have an address, and an array of storage slots.' @@ -83,25 +83,25 @@ export class AccessLists { } } - public static getAccessListJSON(accessList: AccessListBuffer) { + public static getAccessListJSON(accessList: AccessListBytes) { const accessListJSON = [] for (let index = 0; index < accessList.length; index++) { const item: any = accessList[index] const JSONItem: any = { - address: '0x' + setLengthLeft(item[0], 20).toString('hex'), + address: bytesToPrefixedHexString(setLengthLeft(item[0], 20)), storageKeys: [], } - const storageSlots: Buffer[] = item[1] + const storageSlots: Uint8Array[] = item[1] for (let slot = 0; slot < storageSlots.length; slot++) { const storageSlot = storageSlots[slot] - JSONItem.storageKeys.push('0x' + setLengthLeft(storageSlot, 32).toString('hex')) + JSONItem.storageKeys.push(bytesToPrefixedHexString(setLengthLeft(storageSlot, 32))) } accessListJSON.push(JSONItem) } return accessListJSON } - public static getDataFeeEIP2930(accessList: AccessListBuffer, common: Common): number { + public static getDataFeeEIP2930(accessList: AccessListBytes, common: Common): number { const accessListStorageKeyCost = common.param('gasPrices', 'accessListStorageKeyCost') const accessListAddressCost = common.param('gasPrices', 'accessListAddressCost') @@ -120,7 +120,7 @@ export class AccessLists { export const blobTxToNetworkWrapperDataFormat = (tx: BlobEIP4844Transaction) => { const to = { selector: tx.to !== undefined ? 1 : 0, - value: tx.to?.toBuffer() ?? null, + value: tx.to?.toBytes() ?? null, } return { message: { diff --git a/packages/tx/src/utils/blobHelpers.ts b/packages/tx/src/utils/blobHelpers.ts index c3080da2c5d..3e5808b609b 100644 --- a/packages/tx/src/utils/blobHelpers.ts +++ b/packages/tx/src/utils/blobHelpers.ts @@ -1,4 +1,5 @@ import { sha256 } from 'ethereum-cryptography/sha256' +import { utf8ToBytes } from 'ethereum-cryptography/utils' import { kzg } from '../kzg/kzg' @@ -12,28 +13,27 @@ const MAX_BLOBS_PER_TX = 2 const MAX_USEFUL_BYTES_PER_TX = USEFUL_BYTES_PER_BLOB * MAX_BLOBS_PER_TX - 1 const BLOB_SIZE = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB -function get_padded(data: Buffer, blobs_len: number) { - const pdata = Buffer.alloc(blobs_len * USEFUL_BYTES_PER_BLOB) - const datalen = Buffer.byteLength(data) - pdata.fill(data, 0, datalen) - pdata[datalen] = 0x80 +function get_padded(data: Uint8Array, blobs_len: number): Uint8Array { + const pdata = new Uint8Array(blobs_len * USEFUL_BYTES_PER_BLOB).fill(0) + pdata.set(data) + pdata[data.byteLength] = 0x80 return pdata } -function get_blob(data: Buffer) { - const blob = Buffer.alloc(BLOB_SIZE, 'binary') +function get_blob(data: Uint8Array): Uint8Array { + const blob = new Uint8Array(BLOB_SIZE) for (let i = 0; i < FIELD_ELEMENTS_PER_BLOB; i++) { - const chunk = Buffer.alloc(32, 'binary') - chunk.fill(data.subarray(i * 31, (i + 1) * 31), 0, 31) - blob.fill(chunk, i * 32, (i + 1) * 32) + const chunk = new Uint8Array(32) + chunk.set(data.subarray(i * 31, (i + 1) * 31), 0) + blob.set(chunk, i * 32) } return blob } export const getBlobs = (input: string) => { - const data = Buffer.from(input, 'binary') - const len = Buffer.byteLength(data) + const data = utf8ToBytes(input) + const len = data.byteLength if (len === 0) { throw Error('invalid blob data') } @@ -45,7 +45,7 @@ export const getBlobs = (input: string) => { const pdata = get_padded(data, blobs_len) - const blobs = [] + const blobs: Uint8Array[] = [] for (let i = 0; i < blobs_len; i++) { const chunk = pdata.subarray(i * USEFUL_BYTES_PER_BLOB, (i + 1) * USEFUL_BYTES_PER_BLOB) const blob = get_blob(chunk) @@ -55,10 +55,10 @@ export const getBlobs = (input: string) => { return blobs } -export const blobsToCommitments = (blobs: Buffer[]) => { - const commitments = [] +export const blobsToCommitments = (blobs: Uint8Array[]) => { + const commitments: Uint8Array[] = [] for (const blob of blobs) { - commitments.push(Buffer.from(kzg.blobToKzgCommitment(blob))) + commitments.push(kzg.blobToKzgCommitment(blob)) } return commitments } @@ -74,7 +74,7 @@ export const blobsToCommitments = (blobs: Buffer[]) => { export const computeVersionedHash = (commitment: Uint8Array, blobCommitmentVersion: number) => { const computedVersionedHash = new Uint8Array(32) computedVersionedHash.set([blobCommitmentVersion], 0) - computedVersionedHash.set(sha256(commitment).slice(1), 1) + computedVersionedHash.set(sha256(commitment).subarray(1), 1) return computedVersionedHash } @@ -84,10 +84,10 @@ export const computeVersionedHash = (commitment: Uint8Array, blobCommitmentVersi * @returns array of versioned hashes * Note: assumes KZG commitments (version 1 version hashes) */ -export const commitmentsToVersionedHashes = (commitments: Buffer[]) => { - const hashes = [] +export const commitmentsToVersionedHashes = (commitments: Uint8Array[]) => { + const hashes: Uint8Array[] = [] for (const commitment of commitments) { - hashes.push(Buffer.from(computeVersionedHash(commitment, 0x01))) + hashes.push(computeVersionedHash(commitment, 0x01)) } return hashes } diff --git a/packages/tx/test/base.spec.ts b/packages/tx/test/base.spec.ts index ed8a89e5871..4717acccdeb 100644 --- a/packages/tx/test/base.spec.ts +++ b/packages/tx/test/base.spec.ts @@ -3,9 +3,12 @@ import { MAX_INTEGER, MAX_UINT64, SECP256K1_ORDER, - bufferToBigInt, + bytesToBigInt, + equalsBytes, + hexStringToBytes, privateToPublic, - toBuffer, + toBytes, + utf8ToBytes, } from '@ethereumjs/util' import * as tape from 'tape' @@ -41,7 +44,7 @@ tape('[BaseTransaction]', function (t) { eip1559Txs.push(FeeMarketEIP1559Transaction.fromTxData(tx.data, { common })) } - const zero = Buffer.alloc(0) + const zero = new Uint8Array(0) const txTypes = [ { class: Transaction, @@ -62,7 +65,7 @@ tape('[BaseTransaction]', function (t) { class: AccessListEIP2930Transaction, name: 'AccessListEIP2930Transaction', type: 1, - values: [Buffer.from([1])].concat(Array(7).fill(zero)), + values: [new Uint8Array([1])].concat(Array(7).fill(zero)), txs: eip2930Txs, fixtures: eip2930Fixtures, activeCapabilities: [Capability.EIP2718TypedTransaction, Capability.EIP2930AccessLists], @@ -72,7 +75,7 @@ tape('[BaseTransaction]', function (t) { class: FeeMarketEIP1559Transaction, name: 'FeeMarketEIP1559Transaction', type: 2, - values: [Buffer.from([1])].concat(Array(8).fill(zero)), + values: [new Uint8Array([1])].concat(Array(8).fill(zero)), txs: eip1559Txs, fixtures: eip1559Fixtures, activeCapabilities: [ @@ -152,7 +155,7 @@ tape('[BaseTransaction]', function (t) { t.test('fromValuesArray()', function (st) { let rlpData: any = legacyTxs[0].raw() - rlpData[0] = toBuffer('0x0') + rlpData[0] = toBytes('0x0') try { Transaction.fromValuesArray(rlpData) st.fail('should have thrown when nonce has leading zeroes') @@ -162,8 +165,8 @@ tape('[BaseTransaction]', function (t) { 'should throw with nonce with leading zeroes' ) } - rlpData[0] = toBuffer('0x') - rlpData[6] = toBuffer('0x0') + rlpData[0] = toBytes('0x') + rlpData[6] = toBytes('0x0') try { Transaction.fromValuesArray(rlpData) st.fail('should have thrown when v has leading zeroes') @@ -174,7 +177,7 @@ tape('[BaseTransaction]', function (t) { ) } rlpData = eip2930Txs[0].raw() - rlpData[3] = toBuffer('0x0') + rlpData[3] = toBytes('0x0') try { AccessListEIP2930Transaction.fromValuesArray(rlpData) st.fail('should have thrown when gasLimit has leading zeroes') @@ -185,7 +188,7 @@ tape('[BaseTransaction]', function (t) { ) } rlpData = eip1559Txs[0].raw() - rlpData[2] = toBuffer('0x0') + rlpData[2] = toBytes('0x0') try { FeeMarketEIP1559Transaction.fromValuesArray(rlpData) st.fail('should have thrown when maxPriorityFeePerGas has leading zeroes') @@ -277,11 +280,11 @@ tape('[BaseTransaction]', function (t) { for (const [i, tx] of txType.txs.entries()) { const { privateKey } = txType.fixtures[i] if (privateKey !== undefined) { - st.ok(tx.sign(Buffer.from(privateKey, 'hex')), `${txType.name}: should sign tx`) + st.ok(tx.sign(hexStringToBytes(privateKey)), `${txType.name}: should sign tx`) } st.throws( - () => tx.sign(Buffer.from('invalid')), + () => tx.sign(utf8ToBytes('invalid')), `${txType.name}: should fail with invalid PK` ) } @@ -319,7 +322,7 @@ tape('[BaseTransaction]', function (t) { for (const [i, tx] of txType.txs.entries()) { const { privateKey, sendersAddress } = txType.fixtures[i] if (privateKey !== undefined) { - const signedTx = tx.sign(Buffer.from(privateKey, 'hex')) + const signedTx = tx.sign(hexStringToBytes(privateKey)) st.equal( signedTx.getSenderAddress().toString(), `0x${sendersAddress}`, @@ -336,11 +339,11 @@ tape('[BaseTransaction]', function (t) { for (const [i, tx] of txType.txs.entries()) { const { privateKey } = txType.fixtures[i] if (privateKey !== undefined) { - const signedTx = tx.sign(Buffer.from(privateKey, 'hex')) + const signedTx = tx.sign(hexStringToBytes(privateKey)) const txPubKey = signedTx.getSenderPublicKey() - const pubKeyFromPriv = privateToPublic(Buffer.from(privateKey, 'hex')) + const pubKeyFromPriv = privateToPublic(hexStringToBytes(privateKey)) st.ok( - txPubKey.equals(pubKeyFromPriv), + equalsBytes(txPubKey, pubKeyFromPriv), `${txType.name}: should get sender's public key after signing it` ) } @@ -358,7 +361,7 @@ tape('[BaseTransaction]', function (t) { for (const [i, tx] of txType.txs.entries()) { const { privateKey } = txType.fixtures[i] if (privateKey !== undefined) { - let signedTx = tx.sign(Buffer.from(privateKey, 'hex')) + let signedTx = tx.sign(hexStringToBytes(privateKey)) signedTx = JSON.parse(JSON.stringify(signedTx)) // deep clone ;(signedTx as any).s = SECP256K1_ORDER + BigInt(1) st.throws(() => { @@ -376,7 +379,7 @@ tape('[BaseTransaction]', function (t) { for (const [i, tx] of txType.txs.entries()) { const { privateKey } = txType.fixtures[i] if (privateKey !== undefined) { - const signedTx = tx.sign(Buffer.from(privateKey, 'hex')) + const signedTx = tx.sign(hexStringToBytes(privateKey)) st.ok(signedTx.verifySignature(), `${txType.name}: should verify signing it`) } } @@ -385,7 +388,7 @@ tape('[BaseTransaction]', function (t) { }) t.test('initialization with defaults', function (st) { - const bufferZero = toBuffer('0x') + const bufferZero = toBytes('0x') const tx = Transaction.fromTxData({ nonce: '', gasLimit: '', @@ -401,11 +404,11 @@ tape('[BaseTransaction]', function (t) { st.equal(tx.r, undefined) st.equal(tx.s, undefined) st.isEquivalent(tx.to, undefined) - st.isEquivalent(tx.value, bufferToBigInt(bufferZero)) + st.isEquivalent(tx.value, bytesToBigInt(bufferZero)) st.isEquivalent(tx.data, bufferZero) - st.isEquivalent(tx.gasPrice, bufferToBigInt(bufferZero)) - st.isEquivalent(tx.gasLimit, bufferToBigInt(bufferZero)) - st.isEquivalent(tx.nonce, bufferToBigInt(bufferZero)) + st.isEquivalent(tx.gasPrice, bytesToBigInt(bufferZero)) + st.isEquivalent(tx.gasLimit, bytesToBigInt(bufferZero)) + st.isEquivalent(tx.nonce, bytesToBigInt(bufferZero)) st.end() }) diff --git a/packages/tx/test/eip1559.spec.ts b/packages/tx/test/eip1559.spec.ts index 458711cab8e..4312cf12cb5 100644 --- a/packages/tx/test/eip1559.spec.ts +++ b/packages/tx/test/eip1559.spec.ts @@ -1,6 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { TWO_POW256 } from '@ethereumjs/util' +import { TWO_POW256, equalsBytes, hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { FeeMarketEIP1559Transaction } from '../src' @@ -12,8 +12,8 @@ const common = new Common({ hardfork: Hardfork.London, }) -const validAddress = Buffer.from('01'.repeat(20), 'hex') -const validSlot = Buffer.from('01'.repeat(32), 'hex') +const validAddress = hexStringToBytes('01'.repeat(20)) +const validSlot = hexStringToBytes('01'.repeat(32)) const chainId = BigInt(4) tape('[FeeMarketEIP1559Transaction]', function (t) { @@ -92,12 +92,12 @@ tape('[FeeMarketEIP1559Transaction]', function (t) { t.test('sign()', function (st) { for (let index = 0; index < testdata.length; index++) { const data = testdata[index] - const pkey = Buffer.from(data.privateKey.slice(2), 'hex') + const pkey = hexStringToBytes(data.privateKey) const txn = FeeMarketEIP1559Transaction.fromTxData(data, { common }) const signed = txn.sign(pkey) - const rlpSerialized = Buffer.from(RLP.encode(Uint8Array.from(signed.serialize()))) + const rlpSerialized = RLP.encode(Uint8Array.from(signed.serialize())) st.ok( - rlpSerialized.equals(Buffer.from(data.signedTransactionRLP.slice(2), 'hex')), + equalsBytes(rlpSerialized, hexStringToBytes(data.signedTransactionRLP)), 'Should sign txs correctly' ) } @@ -106,23 +106,25 @@ tape('[FeeMarketEIP1559Transaction]', function (t) { t.test('hash()', function (st) { const data = testdata[0] - const pkey = Buffer.from(data.privateKey.slice(2), 'hex') + const pkey = hexStringToBytes(data.privateKey) let txn = FeeMarketEIP1559Transaction.fromTxData(data, { common }) let signed = txn.sign(pkey) - const expectedHash = Buffer.from( - '2e564c87eb4b40e7f469b2eec5aa5d18b0b46a24e8bf0919439cfb0e8fcae446', - 'hex' + const expectedHash = hexStringToBytes( + '2e564c87eb4b40e7f469b2eec5aa5d18b0b46a24e8bf0919439cfb0e8fcae446' ) - st.ok(signed.hash().equals(expectedHash), 'Should provide the correct hash when frozen') + st.ok(equalsBytes(signed.hash(), expectedHash), 'Should provide the correct hash when frozen') txn = FeeMarketEIP1559Transaction.fromTxData(data, { common, freeze: false }) signed = txn.sign(pkey) - st.ok(signed.hash().equals(expectedHash), 'Should provide the correct hash when not frozen') + st.ok( + equalsBytes(signed.hash(), expectedHash), + 'Should provide the correct hash when not frozen' + ) st.end() }) t.test('freeze property propagates from unsigned tx to signed tx', function (st) { const data = testdata[0] - const pkey = Buffer.from(data.privateKey.slice(2), 'hex') + const pkey = hexStringToBytes(data.privateKey) const txn = FeeMarketEIP1559Transaction.fromTxData(data, { common, freeze: false }) st.notOk(Object.isFrozen(txn), 'tx object is not frozen') const signedTxn = txn.sign(pkey) @@ -132,7 +134,7 @@ tape('[FeeMarketEIP1559Transaction]', function (t) { t.test('common propagates from the common of tx, not the common in TxOptions', function (st) { const data = testdata[0] - const pkey = Buffer.from(data.privateKey.slice(2), 'hex') + const pkey = hexStringToBytes(data.privateKey) const txn = FeeMarketEIP1559Transaction.fromTxData(data, { common, freeze: false }) const newCommon = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.London, eips: [2537] }) st.notDeepEqual(newCommon, common, 'new common is different than original common') @@ -149,22 +151,20 @@ tape('[FeeMarketEIP1559Transaction]', function (t) { t.test('unsigned tx -> getMessageToSign()', function (t) { const unsignedTx = FeeMarketEIP1559Transaction.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), to: validAddress, accessList: [[validAddress, [validSlot]]], chainId, }, { common } ) - const expectedHash = Buffer.from( - 'fa81814f7dd57bad435657a05eabdba2815f41e3f15ddd6139027e7db56b0dea', - 'hex' + const expectedHash = hexStringToBytes( + 'fa81814f7dd57bad435657a05eabdba2815f41e3f15ddd6139027e7db56b0dea' ) t.deepEqual(unsignedTx.getMessageToSign(true), expectedHash), 'correct hashed version' - const expectedSerialization = Buffer.from( - '02f85904808080809401010101010101010101010101010101010101018083010200f838f7940101010101010101010101010101010101010101e1a00101010101010101010101010101010101010101010101010101010101010101', - 'hex' + const expectedSerialization = hexStringToBytes( + '02f85904808080809401010101010101010101010101010101010101018083010200f838f7940101010101010101010101010101010101010101e1a00101010101010101010101010101010101010101010101010101010101010101' ) t.deepEqual( unsignedTx.getMessageToSign(false), @@ -177,7 +177,7 @@ tape('[FeeMarketEIP1559Transaction]', function (t) { t.test('toJSON()', function (st) { const data = testdata[0] - const pkey = Buffer.from(data.privateKey.slice(2), 'hex') + const pkey = hexStringToBytes(data.privateKey) const txn = FeeMarketEIP1559Transaction.fromTxData(data, { common }) const signed = txn.sign(pkey) diff --git a/packages/tx/test/eip3860.spec.ts b/packages/tx/test/eip3860.spec.ts index 6556d8c674c..b1cbaf92a68 100644 --- a/packages/tx/test/eip3860.spec.ts +++ b/packages/tx/test/eip3860.spec.ts @@ -16,7 +16,7 @@ const addressZero = Address.zero() tape('[EIP3860 tests]', function (t) { t.test('Should instantiate create txs with MAX_INITCODE_SIZE', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize)) + const data = new Uint8Array(Number(maxInitCodeSize)) for (const txType of txTypes) { try { TransactionFactory.fromTxData({ data, type: txType }, { common }) @@ -29,7 +29,7 @@ tape('[EIP3860 tests]', function (t) { }) t.test('Should instantiate txs with MAX_INITCODE_SIZE data', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize)) + const data = new Uint8Array(Number(maxInitCodeSize)) for (const txType of txTypes) { try { TransactionFactory.fromTxData({ data, type: txType, to: addressZero }, { common }) @@ -42,7 +42,7 @@ tape('[EIP3860 tests]', function (t) { }) t.test('Should not instantiate create txs with MAX_INITCODE_SIZE+1 data', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize) + 1) + const data = new Uint8Array(Number(maxInitCodeSize) + 1) for (const txType of txTypes) { try { TransactionFactory.fromTxData({ data, type: txType }, { common }) @@ -55,7 +55,7 @@ tape('[EIP3860 tests]', function (t) { }) t.test('Should instantiate txs with MAX_INITCODE_SIZE+1 data', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize) + 1) + const data = new Uint8Array(Number(maxInitCodeSize) + 1) for (const txType of txTypes) { try { TransactionFactory.fromTxData({ data, type: txType, to: addressZero }, { common }) @@ -70,7 +70,7 @@ tape('[EIP3860 tests]', function (t) { tape( 'Should allow txs with MAX_INITCODE_SIZE+1 data if allowUnlimitedInitCodeSize is active', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize) + 1) + const data = new Uint8Array(Number(maxInitCodeSize) + 1) for (const txType of txTypes) { try { TransactionFactory.fromTxData( @@ -87,7 +87,7 @@ tape('[EIP3860 tests]', function (t) { ) tape('Should charge initcode analysis gas is allowUnlimitedInitCodeSize is active', (st) => { - const data = Buffer.alloc(Number(maxInitCodeSize)) + const data = new Uint8Array(Number(maxInitCodeSize)) for (const txType of txTypes) { const eip3860ActiveTx = TransactionFactory.fromTxData( { data, type: txType }, diff --git a/packages/tx/test/eip4844.spec.ts b/packages/tx/test/eip4844.spec.ts index f5cb98bb3db..78138a8f77a 100644 --- a/packages/tx/test/eip4844.spec.ts +++ b/packages/tx/test/eip4844.spec.ts @@ -1,6 +1,12 @@ import { Common, Hardfork } from '@ethereumjs/common' +import { + bytesToHex, + concatBytes, + equalsBytes, + hexStringToBytes, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' -import { randomBytes } from 'crypto' import * as tape from 'tape' import { BlobEIP4844Transaction, TransactionFactory, initKZG } from '../src' @@ -28,7 +34,7 @@ tape('EIP4844 constructor tests - valid scenarios', (t) => { } else { const txData = { type: 0x05, - versionedHashes: [Buffer.concat([Buffer.from([1]), randomBytes(31)])], + versionedHashes: [concatBytes(new Uint8Array([1]), randomBytes(31))], maxFeePerDataGas: 1n, } const tx = BlobEIP4844Transaction.fromTxData(txData, { common }) @@ -90,9 +96,9 @@ tape('fromTxData using from a json', (t) => { t.pass('Should be able to parse a json data and hash it') t.equal(typeof tx.maxFeePerDataGas, 'bigint', 'should be able to parse correctly') - t.equal(tx.serialize().toString('hex'), txData.serialized, 'serialization should match') + t.equal(bytesToHex(tx.serialize()), txData.serialized, 'serialization should match') // TODO: fix the hash - t.equal(tx.hash().toString('hex'), txData.hash, 'hash should match') + t.equal(bytesToHex(tx.hash()), txData.hash, 'hash should match') } catch (e) { t.fail('failed to parse json data') } @@ -110,16 +116,16 @@ tape('EIP4844 constructor tests - invalid scenarios', (t) => { maxFeePerDataGas: 1n, } const shortVersionHash = { - versionedHashes: [Buffer.concat([Buffer.from([3]), randomBytes(3)])], + versionedHashes: [concatBytes(new Uint8Array([3]), randomBytes(3))], } const invalidVersionHash = { - versionedHashes: [Buffer.concat([Buffer.from([3]), randomBytes(31)])], + versionedHashes: [concatBytes(new Uint8Array([3]), randomBytes(31))], } const tooManyBlobs = { versionedHashes: [ - Buffer.concat([Buffer.from([1]), randomBytes(31)]), - Buffer.concat([Buffer.from([1]), randomBytes(31)]), - Buffer.concat([Buffer.from([1]), randomBytes(31)]), + concatBytes(new Uint8Array([1]), randomBytes(31)), + concatBytes(new Uint8Array([1]), randomBytes(31)), + concatBytes(new Uint8Array([1]), randomBytes(31)), ], } try { @@ -155,19 +161,19 @@ tape('Network wrapper tests', async (t) => { const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) const proof = kzg.computeAggregateKzgProof(blobs) - const bufferedHashes = versionedHashes.map((el) => Buffer.from(el)) const unsignedTx = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: Buffer.from(proof), + kzgProof: proof, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), }, { common } ) + const signedTx = unsignedTx.sign(pk) const sender = signedTx.getSenderAddress().toString() const wrapper = signedTx.serializeNetworkWrapper() @@ -189,13 +195,13 @@ tape('Network wrapper tests', async (t) => { const minimalTx = BlobEIP4844Transaction.minimalFromNetworkWrapper(deserializedTx, { common }) t.ok(minimalTx.blobs === undefined, 'minimal representation contains no blobs') t.ok( - minimalTx.hash().equals(deserializedTx.hash()), + equalsBytes(minimalTx.hash(), deserializedTx.hash()), 'has the same hash as the network wrapper version' ) const txWithMissingBlob = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs: blobs.slice(1), kzgCommitments: commitments, maxFeePerDataGas: 100000000n, @@ -221,7 +227,7 @@ tape('Network wrapper tests', async (t) => { commitments[0][0] = 154 const txWithInvalidCommitment = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, maxFeePerDataGas: 100000000n, @@ -241,15 +247,15 @@ tape('Network wrapper tests', async (t) => { 'throws when kzg proof cant be verified' ) - bufferedHashes[0][1] = 2 + versionedHashes[0][1] = 2 commitments[0][0] = mangledValue const txWithInvalidVersionedHashes = BlobEIP4844Transaction.fromTxData( { - versionedHashes: bufferedHashes, + versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: Buffer.from(proof), + kzgProof: proof, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -283,7 +289,7 @@ tape('hash() and signature verification', async (t) => { chainId: 1, nonce: 1, versionedHashes: [ - Buffer.from('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e', 'hex'), + hexStringToBytes('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e'), ], maxFeePerDataGas: 10000000n, gasLimit: 123457n, @@ -299,12 +305,12 @@ tape('hash() and signature verification', async (t) => { { common } ) t.equal( - unsignedTx.unsignedHash().toString('hex'), + bytesToHex(unsignedTx.unsignedHash()), '0fcee5b30088a9c96b4990a3914002736a50f42468209d65a93badd3d1cd0677', 'produced the correct transaction hash' ) const signedTx = unsignedTx.sign( - Buffer.from('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8', 'hex') + hexStringToBytes('45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8') ) t.equal( diff --git a/packages/tx/test/fromRpc.spec.ts b/packages/tx/test/fromRpc.spec.ts index 01cabc332b8..769169a82b3 100644 --- a/packages/tx/test/fromRpc.spec.ts +++ b/packages/tx/test/fromRpc.spec.ts @@ -1,4 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { bytesToHex, bytesToPrefixedHexString } from '@ethereumjs/util' import * as tape from 'tape' import { TransactionFactory } from '../src' @@ -12,7 +13,7 @@ tape('[fromEthersProvider]', async (t) => { const txHash = '0xed1960aa7d0d7b567c946d94331dddb37a1c67f51f30bf51f256ea40db88cfb0' const tx = await TransactionFactory.fromEthersProvider(provider, txHash, { common }) t.equal( - '0x' + tx.hash().toString('hex'), + bytesToPrefixedHexString(tx.hash()), txHash, 'generated correct tx from transaction RPC data' ) @@ -25,7 +26,7 @@ tape('[normalizeTxParams]', (t) => { const tx = TransactionFactory.fromTxData(normedTx) t.equal(normedTx.gasLimit, 21000n, 'correctly converted "gas" to "gasLimit"') t.equal( - tx.hash().toString('hex'), + bytesToHex(tx.hash()), rpcTx.hash.slice(2), 'converted normed tx data to transaction objec' ) diff --git a/packages/tx/test/inputValue.spec.ts b/packages/tx/test/inputValue.spec.ts index e591d3d5460..0433e2e6a3b 100644 --- a/packages/tx/test/inputValue.spec.ts +++ b/packages/tx/test/inputValue.spec.ts @@ -1,5 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, toBuffer } from '@ethereumjs/util' +import { Address, hexStringToBytes, toBytes } from '@ethereumjs/util' import * as tape from 'tape' import { @@ -14,21 +14,21 @@ import type { FeeMarketEIP1559ValuesArray, TxValuesArray, } from '../src' -import type { AddressLike, BigIntLike, BufferLike } from '@ethereumjs/util' +import type { AddressLike, BigIntLike, BytesLike } from '@ethereumjs/util' // @returns: Array with subtypes of the AddressLike type for a given address function generateAddressLikeValues(address: string): AddressLike[] { - return [address, toBuffer(address), new Address(toBuffer(address))] + return [address, toBytes(address), new Address(toBytes(address))] } // @returns: Array with subtypes of the BigIntLike type for a given number function generateBigIntLikeValues(value: number): BigIntLike[] { - return [value, BigInt(value), `0x${value.toString(16)}`, toBuffer(value)] + return [value, BigInt(value), `0x${value.toString(16)}`, toBytes(value)] } -// @returns: Array with subtypes of the BufferLike type for a given string -function generateBufferLikeValues(value: string): BufferLike[] { - return [value, toBuffer(value)] +// @returns: Array with subtypes of the BytesLike type for a given string +function generateBytesLikeValues(value: string): BytesLike[] { + return [value, toBytes(value)] } interface GenerateCombinationsArgs { @@ -89,7 +89,7 @@ function getRandomSubarray(array: TArrayItem[], size: number) { } const baseTxValues = { - data: generateBufferLikeValues('0x65'), + data: generateBytesLikeValues('0x65'), gasLimit: generateBigIntLikeValues(100000), nonce: generateBigIntLikeValues(0), to: generateAddressLikeValues('0x0000000000000000000000000000000000000000'), @@ -153,7 +153,7 @@ tape('[Invalid Array Input values]', (t) => { for (const txType of txTypes) { let tx = TransactionFactory.fromTxData({ type: txType }) if (signed) { - tx = tx.sign(Buffer.from('42'.repeat(32), 'hex')) + tx = tx.sign(hexStringToBytes('42'.repeat(32))) } const rawValues = tx.raw() for (let x = 0; x < rawValues.length; x++) { @@ -215,14 +215,14 @@ tape('[Invalid Access Lists]', (t) => { accessList: invalidAccessListItem, }) if (signed) { - tx = tx.sign(Buffer.from('42'.repeat(32), 'hex')) + tx = tx.sign(hexStringToBytes('42'.repeat(32))) } t.fail('did not fail on `fromTxData`') } catch (e: any) { t.pass('failed ok on decoding in `fromTxData`') tx = TransactionFactory.fromTxData({ type: txType }) if (signed) { - tx = tx.sign(Buffer.from('42'.repeat(32), 'hex')) + tx = tx.sign(hexStringToBytes('42'.repeat(32))) } } const rawValues = tx!.raw() diff --git a/packages/tx/test/legacy.spec.ts b/packages/tx/test/legacy.spec.ts index 7f9642c76f5..096313834da 100644 --- a/packages/tx/test/legacy.spec.ts +++ b/packages/tx/test/legacy.spec.ts @@ -1,14 +1,15 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { - arrToBufArr, - bufferToBigInt, - bufferToHex, - intToBuffer, - toBuffer, - unpadBuffer, + bytesToBigInt, + bytesToHex, + bytesToPrefixedHexString, + equalsBytes, + hexStringToBytes, + intToBytes, + toBytes, + unpadBytes, } from '@ethereumjs/util' -import { Buffer } from 'buffer' import * as tape from 'tape' import { Transaction } from '../src' @@ -62,22 +63,22 @@ tape('[Transaction]', function (t) { 'should initialize on a pre-Berlin Harfork (EIP-2930 not activated)' ) - const txData = txFixtures[3].raw.map(toBuffer) - txData[6] = intToBuffer(45) // v with 0-parity and chain ID 5 + const txData = txFixtures[3].raw.map(toBytes) + txData[6] = intToBytes(45) // v with 0-parity and chain ID 5 let tx = Transaction.fromValuesArray(txData) st.ok( tx.common.chainId() === BigInt(5), 'should initialize Common with chain ID (supported) derived from v value (v with 0-parity)' ) - txData[6] = intToBuffer(46) // v with 1-parity and chain ID 5 + txData[6] = intToBytes(46) // v with 1-parity and chain ID 5 tx = Transaction.fromValuesArray(txData) st.ok( tx.common.chainId() === BigInt(5), 'should initialize Common with chain ID (supported) derived from v value (v with 1-parity)' ) - txData[6] = intToBuffer(2033) // v with 0-parity and chain ID 999 + txData[6] = intToBytes(2033) // v with 0-parity and chain ID 999 tx = Transaction.fromValuesArray(txData) st.equal( tx.common.chainId(), @@ -85,7 +86,7 @@ tape('[Transaction]', function (t) { 'should initialize Common with chain ID (unsupported) derived from v value (v with 0-parity)' ) - txData[6] = intToBuffer(2034) // v with 1-parity and chain ID 999 + txData[6] = intToBytes(2034) // v with 1-parity and chain ID 999 tx = Transaction.fromValuesArray(txData) st.equal( tx.common.chainId(), @@ -97,18 +98,18 @@ tape('[Transaction]', function (t) { t.test('Initialization -> decode with fromValuesArray()', function (st) { for (const tx of txFixtures.slice(0, 4)) { - const txData = tx.raw.map(toBuffer) + const txData = tx.raw.map(toBytes) const pt = Transaction.fromValuesArray(txData) - st.equal(bufferToHex(unpadBuffer(toBuffer(pt.nonce))), tx.raw[0]) - st.equal(bufferToHex(toBuffer(pt.gasPrice)), tx.raw[1]) - st.equal(bufferToHex(toBuffer(pt.gasLimit)), tx.raw[2]) + st.equal(bytesToPrefixedHexString(unpadBytes(toBytes(pt.nonce))), tx.raw[0]) + st.equal(bytesToPrefixedHexString(toBytes(pt.gasPrice)), tx.raw[1]) + st.equal(bytesToPrefixedHexString(toBytes(pt.gasLimit)), tx.raw[2]) st.equal(pt.to?.toString(), tx.raw[3]) - st.equal(bufferToHex(unpadBuffer(toBuffer(pt.value))), tx.raw[4]) - st.equal('0x' + pt.data.toString('hex'), tx.raw[5]) - st.equal(bufferToHex(toBuffer(pt.v)), tx.raw[6]) - st.equal(bufferToHex(toBuffer(pt.r)), tx.raw[7]) - st.equal(bufferToHex(toBuffer(pt.s)), tx.raw[8]) + st.equal(bytesToPrefixedHexString(unpadBytes(toBytes(pt.value))), tx.raw[4]) + st.equal(bytesToPrefixedHexString(pt.data), tx.raw[5]) + st.equal(bytesToPrefixedHexString(toBytes(pt.v)), tx.raw[6]) + st.equal(bytesToPrefixedHexString(toBytes(pt.r)), tx.raw[7]) + st.equal(bytesToPrefixedHexString(toBytes(pt.s)), tx.raw[8]) transactions.push(pt) } @@ -116,7 +117,7 @@ tape('[Transaction]', function (t) { }) t.test('Initialization -> should accept lesser r values', function (st) { - const tx = Transaction.fromTxData({ r: bufferToBigInt(toBuffer('0x0005')) }) + const tx = Transaction.fromTxData({ r: bytesToBigInt(toBytes('0x0005')) }) st.equal(tx.r!.toString(16), '5') st.end() }) @@ -127,7 +128,7 @@ tape('[Transaction]', function (t) { let common = new Common({ chain: Chain.Goerli, hardfork: Hardfork.Petersburg }) let tx = Transaction.fromTxData({}, { common }) st.equal(tx.common.chainId(), BigInt(5)) - const privKey = Buffer.from(txFixtures[0].privateKey, 'hex') + const privKey = hexStringToBytes(txFixtures[0].privateKey) tx = tx.sign(privKey) const serialized = tx.serialize() common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Petersburg }) @@ -164,10 +165,10 @@ tape('[Transaction]', function (t) { let tx = Transaction.fromTxData({}) st.equal(tx.getDataFee(), BigInt(0)) - tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBuffer)) + tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBytes)) st.equal(tx.getDataFee(), BigInt(1716)) - tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBuffer), { freeze: false }) + tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBytes), { freeze: false }) st.equal(tx.getDataFee(), BigInt(1716)) st.end() @@ -178,7 +179,7 @@ tape('[Transaction]', function (t) { let tx = Transaction.fromTxData({}, { common }) st.equal(tx.getDataFee(), BigInt(0)) - tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBuffer), { + tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBytes), { common, }) st.equal(tx.getDataFee(), BigInt(1716)) @@ -188,7 +189,7 @@ tape('[Transaction]', function (t) { t.test('getDataFee() -> should invalidate cached value on hardfork change', function (st) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Byzantium }) - const tx = Transaction.fromValuesArray(txFixtures[0].raw.map(toBuffer), { + const tx = Transaction.fromValuesArray(txFixtures[0].raw.map(toBytes), { common, }) st.equal(tx.getDataFee(), BigInt(656)) @@ -210,8 +211,8 @@ tape('[Transaction]', function (t) { t.test('serialize()', function (st) { for (const [i, tx] of transactions.entries()) { const s1 = tx.serialize() - const s2 = Buffer.from(RLP.encode(txFixtures[i].raw)) - st.ok(s1.equals(s2)) + const s2 = RLP.encode(txFixtures[i].raw) + st.ok(equalsBytes(s1, s2)) } st.end() }) @@ -220,11 +221,10 @@ tape('[Transaction]', function (t) { const tx = Transaction.fromTxData({ value: 5000 }) const s1 = tx.serialize() - const s1Rlp = toBuffer('0x' + s1.toString('hex')) - const tx2 = Transaction.fromSerializedTx(s1Rlp) + const tx2 = Transaction.fromSerializedTx(s1) const s2 = tx2.serialize() - st.ok(s1.equals(s2)) + st.ok(equalsBytes(s1, s2)) st.end() }) @@ -234,43 +234,43 @@ tape('[Transaction]', function (t) { hardfork: Hardfork.TangerineWhistle, }) - let tx = Transaction.fromValuesArray(txFixtures[3].raw.slice(0, 6).map(toBuffer), { + let tx = Transaction.fromValuesArray(txFixtures[3].raw.slice(0, 6).map(toBytes), { common, }) st.throws(() => { tx.hash() }, 'should throw calling hash with unsigned tx') - tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBuffer), { + tx = Transaction.fromValuesArray(txFixtures[3].raw.map(toBytes), { common, }) st.deepEqual( tx.hash(), - Buffer.from('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa', 'hex') + hexStringToBytes('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa') ) st.deepEqual( tx.getMessageToSign(), - Buffer.from('61e1ec33764304dddb55348e7883d4437426f44ab3ef65e6da1e025734c03ff0', 'hex') + hexStringToBytes('61e1ec33764304dddb55348e7883d4437426f44ab3ef65e6da1e025734c03ff0') ) st.equal(tx.getMessageToSign(false).length, 6) st.deepEqual( tx.hash(), - Buffer.from('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa', 'hex') + hexStringToBytes('375a8983c9fc56d7cfd118254a80a8d7403d590a6c9e105532b67aca1efb97aa') ) st.end() }) t.test('hash() -> with defined chainId', function (st) { - const tx = Transaction.fromValuesArray(txFixtures[4].raw.map(toBuffer)) + const tx = Transaction.fromValuesArray(txFixtures[4].raw.map(toBytes)) st.equal( - tx.hash().toString('hex'), + bytesToHex(tx.hash()), '0f09dc98ea85b7872f4409131a790b91e7540953992886fc268b7ba5c96820e4' ) st.equal( - tx.hash().toString('hex'), + bytesToHex(tx.hash()), '0f09dc98ea85b7872f4409131a790b91e7540953992886fc268b7ba5c96820e4' ) st.equal( - tx.getMessageToSign().toString('hex'), + bytesToHex(tx.getMessageToSign()), 'f97c73fdca079da7652dbc61a46cd5aeef804008e057be3e712c43eac389aaf0' ) st.end() @@ -280,9 +280,9 @@ tape('[Transaction]', function (t) { "getMessageToSign(), getSenderPublicKey() (implicit call) -> verify EIP155 signature based on Vitalik's tests", function (st) { for (const tx of txFixturesEip155) { - const pt = Transaction.fromSerializedTx(toBuffer(tx.rlp)) - st.equal(pt.getMessageToSign().toString('hex'), tx.hash) - st.equal('0x' + pt.serialize().toString('hex'), tx.rlp) + const pt = Transaction.fromSerializedTx(toBytes(tx.rlp)) + st.equal(bytesToHex(pt.getMessageToSign()), tx.hash) + st.equal(bytesToPrefixedHexString(pt.serialize()), tx.rlp) st.equal(pt.getSenderAddress().toString(), '0x' + tx.sender) } st.end() @@ -301,26 +301,25 @@ tape('[Transaction]', function (t) { '0x0de0b6b3a7640000', '0x', ] - const privateKey = Buffer.from( - '4646464646464646464646464646464646464646464646464646464646464646', - 'hex' + const privateKey = hexStringToBytes( + '4646464646464646464646464646464646464646464646464646464646464646' ) - const pt = Transaction.fromValuesArray(txRaw.map(toBuffer)) + const pt = Transaction.fromValuesArray(txRaw.map(toBytes)) // Note that Vitalik's example has a very similar value denoted "signing data". // It's not the output of `serialize()`, but the pre-image of the hash returned by `tx.hash(false)`. // We don't have a getter for such a value in Transaction. st.equal( - pt.serialize().toString('hex'), + bytesToHex(pt.serialize()), 'ec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080808080' ) const signedTx = pt.sign(privateKey) st.equal( - signedTx.getMessageToSign().toString('hex'), + bytesToHex(signedTx.getMessageToSign()), 'daf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53' ) st.equal( - signedTx.serialize().toString('hex'), + bytesToHex(signedTx.serialize()), 'f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83' ) st.end() @@ -332,11 +331,11 @@ tape('[Transaction]', function (t) { function (st) { const common = new Common({ chain: 1, hardfork: Hardfork.Petersburg }) for (const txData of txFixtures.slice(0, 3)) { - const tx = Transaction.fromValuesArray(txData.raw.slice(0, 6).map(toBuffer), { + const tx = Transaction.fromValuesArray(txData.raw.slice(0, 6).map(toBytes), { common, }) - const privKey = Buffer.from(txData.privateKey, 'hex') + const privKey = hexStringToBytes(txData.privateKey) const txSigned = tx.sign(privKey) st.equal( @@ -361,15 +360,14 @@ tape('[Transaction]', function (t) { '0x0de0b6b3a7640000', '0x', ] - const privateKey = Buffer.from( - 'DE3128752F183E8930D7F00A2AAA302DCB5E700B2CBA2D8CA5795660F07DEFD5', - 'hex' + const privateKey = hexStringToBytes( + 'DE3128752F183E8930D7F00A2AAA302DCB5E700B2CBA2D8CA5795660F07DEFD5' ) const common = new Common({ chain: 3 }) - const tx = Transaction.fromValuesArray(txRaw.map(toBuffer), { common }) + const tx = Transaction.fromValuesArray(txRaw.map(toBytes), { common }) const signedTx = tx.sign(privateKey) st.equal( - signedTx.serialize().toString('hex'), + bytesToHex(signedTx.serialize()), 'f86c018502540be40082520894d7250824390ec5c8b71d856b5de895e271170d9d880de0b6b3a76400008029a0d3512c68099d184ccf54f44d9d6905bff303128574b663dcf10b4c726ddd8133a0628acc8f481dea593f13309dfc5f0340f83fdd40cf9fbe47f782668f6f3aec74' ) @@ -389,9 +387,8 @@ tape('[Transaction]', function (t) { value: '0x0', } - const privateKey = Buffer.from( - '4646464646464646464646464646464646464646464646464646464646464646', - 'hex' + const privateKey = hexStringToBytes( + '4646464646464646464646464646464646464646464646464646464646464646' ) const common = new Common({ @@ -436,7 +433,7 @@ tape('[Transaction]', function (t) { st.true( signedWithoutEIP155.v?.toString(16) === '1c' || signedWithoutEIP155.v?.toString(16) === '1b', - "v shouldn' be EIP155 encoded" + "v shouldn't be EIP155 encoded" ) st.end() @@ -469,7 +466,7 @@ tape('[Transaction]', function (t) { let tx = Transaction.fromTxData({}, { common }) st.equal(tx.common.chainId(), BigInt(5)) - const privKey = Buffer.from(txFixtures[0].privateKey, 'hex') + const privKey = hexStringToBytes(txFixtures[0].privateKey) tx = tx.sign(privKey) const serialized = tx.serialize() @@ -484,7 +481,7 @@ tape('[Transaction]', function (t) { t.test('freeze property propagates from unsigned tx to signed tx', function (st) { const tx = Transaction.fromTxData({}, { freeze: false }) st.notOk(Object.isFrozen(tx), 'tx object is not frozen') - const privKey = Buffer.from(txFixtures[0].privateKey, 'hex') + const privKey = hexStringToBytes(txFixtures[0].privateKey) const signedTxn = tx.sign(privKey) st.notOk(Object.isFrozen(signedTxn), 'tx object is not frozen') st.end() @@ -492,7 +489,7 @@ tape('[Transaction]', function (t) { t.test('common propagates from the common of tx, not the common in TxOptions', function (st) { const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.London }) - const pkey = Buffer.from(txFixtures[0].privateKey, 'hex') + const pkey = hexStringToBytes(txFixtures[0].privateKey) const txn = Transaction.fromTxData({}, { common, freeze: false }) const newCommon = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.London, eips: [2537] }) st.notDeepEqual(newCommon, common, 'new common is different than original common') @@ -518,9 +515,8 @@ tape('[Transaction]', function (t) { to: '0xd9024df085d09398ec76fbed18cac0e1149f50dc', value: '0x0', } - const privateKey = Buffer.from( - '4646464646464646464646464646464646464646464646464646464646464646', - 'hex' + const privateKey = hexStringToBytes( + '4646464646464646464646464646464646464646464646464646464646464646' ) tx = Transaction.fromTxData(txData) st.notOk(tx.isSigned()) @@ -541,7 +537,7 @@ tape('[Transaction]', function (t) { tx = Transaction.fromSerializedTx(rawSigned) st.ok(tx.isSigned()) - const signedValues = arrToBufArr(RLP.decode(Uint8Array.from(rawSigned))) as Buffer[] + const signedValues = RLP.decode(Uint8Array.from(rawSigned)) as Uint8Array[] tx = Transaction.fromValuesArray(signedValues) st.ok(tx.isSigned()) tx = Transaction.fromValuesArray(signedValues.slice(0, 6)) diff --git a/packages/tx/test/testLoader.ts b/packages/tx/test/testLoader.ts index b5b4bd4e831..62bcaa6adf7 100644 --- a/packages/tx/test/testLoader.ts +++ b/packages/tx/test/testLoader.ts @@ -1,3 +1,4 @@ +import { bytesToHex } from '@ethereumjs/util' import * as dir from 'node-dir' import * as path from 'path' @@ -38,7 +39,7 @@ export async function getTests( } const fileCallback = async ( err: Error | undefined, - content: string | Buffer, + content: string | Uint8Array, fileName: string, next: Function ) => { @@ -48,7 +49,7 @@ export async function getTests( } const subDir = fileName.substr(directory.length + 1) const parsedFileName = path.parse(fileName).name - content = Buffer.isBuffer(content) ? content.toString() : content + content = content instanceof Uint8Array ? bytesToHex(content) : content const testsByName = JSON.parse(content) const testNames = Object.keys(testsByName) for (const testName of testNames) { diff --git a/packages/tx/test/transactionFactory.spec.ts b/packages/tx/test/transactionFactory.spec.ts index 6ee4f6d4656..fd7d0e893a4 100644 --- a/packages/tx/test/transactionFactory.spec.ts +++ b/packages/tx/test/transactionFactory.spec.ts @@ -1,4 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { hexStringToBytes } from '@ethereumjs/util' import * as tape from 'tape' import { @@ -13,7 +14,7 @@ const common = new Common({ hardfork: Hardfork.London, }) -const pKey = Buffer.from('4646464646464646464646464646464646464646464646464646464646464646', 'hex') +const pKey = hexStringToBytes('4646464646464646464646464646464646464646464646464646464646464646') const unsignedTx = Transaction.fromTxData({}) const signedTx = unsignedTx.sign(pKey) @@ -90,11 +91,11 @@ tape('[TransactionFactory]: Basic functions', function (t) { t.test('fromBlockBodyData() -> success cases', function (st) { for (const txType of txTypes) { - let rawTx + let rawTx: Uint8Array | Uint8Array[] if (txType.eip2718) { - rawTx = txType.signed.serialize() as Buffer + rawTx = txType.signed.serialize() } else { - rawTx = txType.signed.raw() as Buffer[] + rawTx = txType.signed.raw() as Uint8Array[] } const tx = TransactionFactory.fromBlockBodyData(rawTx, { common }) st.equal(tx.constructor.name, txType.name, `should return the right type (${txType.name})`) diff --git a/packages/tx/test/transactionRunner.ts b/packages/tx/test/transactionRunner.ts index c19bf9bf599..8aff9bb76fd 100644 --- a/packages/tx/test/transactionRunner.ts +++ b/packages/tx/test/transactionRunner.ts @@ -1,5 +1,5 @@ import { Common } from '@ethereumjs/common' -import { toBuffer } from '@ethereumjs/util' +import { bytesToHex, toBytes } from '@ethereumjs/util' import * as minimist from 'minimist' import * as tape from 'tape' @@ -62,7 +62,7 @@ tape('TransactionTests', async (t) => { const shouldBeInvalid = forkTestData.exception !== undefined try { - const rawTx = toBuffer(testData.txbytes) + const rawTx = toBytes(testData.txbytes) const hardfork = forkNameMap[forkName] const common = new Common({ chain: 1, hardfork }) const activateEIPs = EIPs[forkName] @@ -71,7 +71,7 @@ tape('TransactionTests', async (t) => { } const tx = TransactionFactory.fromSerializedData(rawTx, { common }) const sender = tx.getSenderAddress().toString() - const hash = tx.hash().toString('hex') + const hash = bytesToHex(tx.hash()) const txIsValid = tx.validate() const senderIsCorrect = forkTestData.sender === sender const hashIsCorrect = forkTestData.hash?.slice(2) === hash diff --git a/packages/tx/test/typedTxsAndEIP2930.spec.ts b/packages/tx/test/typedTxsAndEIP2930.spec.ts index 0dd06c4d1cd..19e17fef989 100644 --- a/packages/tx/test/typedTxsAndEIP2930.spec.ts +++ b/packages/tx/test/typedTxsAndEIP2930.spec.ts @@ -4,17 +4,20 @@ import { MAX_INTEGER, MAX_UINT64, SECP256K1_ORDER_DIV_2, - bufferToBigInt, - bufferToHex, + bytesToBigInt, + bytesToPrefixedHexString, + concatBytes, + equalsBytes, + hexStringToBytes, privateToAddress, } from '@ethereumjs/util' import * as tape from 'tape' import { AccessListEIP2930Transaction, FeeMarketEIP1559Transaction } from '../src' -import type { AccessList, AccessListBufferItem } from '../src' +import type { AccessList, AccessListBytesItem } from '../src' -const pKey = Buffer.from('4646464646464646464646464646464646464646464646464646464646464646', 'hex') +const pKey = hexStringToBytes('4646464646464646464646464646464646464646464646464646464646464646') const address = privateToAddress(pKey) const common = new Common({ @@ -35,8 +38,8 @@ const txTypes = [ }, ] -const validAddress = Buffer.from('01'.repeat(20), 'hex') -const validSlot = Buffer.from('01'.repeat(32), 'hex') +const validAddress = hexStringToBytes('01'.repeat(20)) +const validSlot = hexStringToBytes('01'.repeat(32)) const chainId = BigInt(1) tape( @@ -132,7 +135,7 @@ tape( t.test('Initialization / Getter -> fromSerializedTx()', function (t) { for (const txType of txTypes) { try { - txType.class.fromSerializedTx(Buffer.from([99]), {}) + txType.class.fromSerializedTx(new Uint8Array([99]), {}) } catch (e: any) { t.ok( e.message.includes('wrong tx type'), @@ -142,7 +145,7 @@ tape( try { // Correct tx type + RLP-encoded 5 - const serialized = Buffer.concat([Buffer.from([txType.type]), Buffer.from([5])]) + const serialized = concatBytes(new Uint8Array([txType.type]), new Uint8Array([5])) txType.class.fromSerializedTx(serialized, {}) } catch (e: any) { t.ok( @@ -153,7 +156,7 @@ tape( try { // Correct tx type + RLP-encoded empty list - const serialized = Buffer.concat([Buffer.from([txType.type]), Buffer.from('c0', 'hex')]) + const serialized = concatBytes(new Uint8Array([txType.type]), hexStringToBytes('c0')) txType.class.fromSerializedTx(serialized, {}) } catch (e: any) { t.ok( @@ -169,8 +172,8 @@ tape( for (const txType of txTypes) { const access: AccessList = [ { - address: bufferToHex(validAddress), - storageKeys: [bufferToHex(validSlot)], + address: bytesToPrefixedHexString(validAddress), + storageKeys: [bytesToPrefixedHexString(validSlot)], }, ] const txn = txType.class.fromTxData( @@ -183,11 +186,11 @@ tape( // Check if everything is converted - const BufferArray = txn.accessList + const bytes = txn.accessList const JSON = txn.AccessListJSON - st.ok(BufferArray[0][0].equals(validAddress)) - st.ok(BufferArray[0][1][0].equals(validSlot)) + st.ok(equalsBytes(bytes[0][0], validAddress)) + st.ok(equalsBytes(bytes[0][1][0], validSlot)) st.deepEqual(JSON, access, `should allow json-typed access lists (${txType.name})`) @@ -195,7 +198,7 @@ tape( const txnRaw = txType.class.fromTxData( { - accessList: BufferArray, + accessList: bytes, chainId: 1, }, { common } @@ -212,7 +215,7 @@ tape( for (const txType of txTypes) { let accessList: any[] = [ [ - Buffer.from('01'.repeat(21), 'hex'), // Address of 21 bytes instead of 20 + hexStringToBytes('01'.repeat(21)), // Address of 21 bytes instead of 20 [], ], ] @@ -225,7 +228,7 @@ tape( [ validAddress, [ - Buffer.from('01'.repeat(31), 'hex'), // Slot of 31 bytes instead of 32 + hexStringToBytes('01'.repeat(31)), // Slot of 31 bytes instead of 32 ], ], ] @@ -265,7 +268,7 @@ tape( for (const txType of txTypes) { let tx = txType.class.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), to: validAddress, accessList: [[validAddress, [validSlot]]], chainId, @@ -274,7 +277,10 @@ tape( ) let signed = tx.sign(pKey) const signedAddress = signed.getSenderAddress() - t.ok(signedAddress.buf.equals(address), `should sign a transaction (${txType.name})`) + t.ok( + equalsBytes(signedAddress.bytes, address), + `should sign a transaction (${txType.name})` + ) signed.verifySignature() // If this throws, test will not end. tx = txType.class.fromTxData({}, { common }) @@ -333,13 +339,13 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { 'should initialize correctly from its own data' ) - const validAddress = Buffer.from('01'.repeat(20), 'hex') - const validSlot = Buffer.from('01'.repeat(32), 'hex') + const validAddress = hexStringToBytes('01'.repeat(20)) + const validSlot = hexStringToBytes('01'.repeat(32)) const chainId = BigInt(1) try { AccessListEIP2930Transaction.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), to: validAddress, accessList: [[validAddress, [validSlot]]], chainId, @@ -358,12 +364,12 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { }) t.throws(() => { - const buffer = Buffer.from([]) - const address = Buffer.from([]) - const storageKeys = [Buffer.from([]), Buffer.from([])] - const aclBuf: AccessListBufferItem = [address, storageKeys] + const bytes = new Uint8Array(0) + const address = new Uint8Array(0) + const storageKeys = [new Uint8Array(0), new Uint8Array(0)] + const aclBytes: AccessListBytesItem = [address, storageKeys] AccessListEIP2930Transaction.fromValuesArray( - [buffer, buffer, buffer, buffer, buffer, buffer, buffer, [aclBuf], buffer], + [bytes, bytes, bytes, bytes, bytes, bytes, bytes, [aclBytes], bytes], {} ) }, 'should throw with values array with length different than 8 or 11') @@ -371,7 +377,7 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { t.test('should return right upfront cost', (st) => { let tx = AccessListEIP2930Transaction.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), to: validAddress, accessList: [[validAddress, [validSlot]]], chainId, @@ -403,7 +409,7 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { // In this Tx, `to` is `undefined`, so we should charge homestead creation gas. tx = AccessListEIP2930Transaction.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), accessList: [[validAddress, [validSlot]]], chainId, }, @@ -458,22 +464,20 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { t.test('unsigned tx -> getMessageToSign()', function (t) { const unsignedTx = AccessListEIP2930Transaction.fromTxData( { - data: Buffer.from('010200', 'hex'), + data: hexStringToBytes('010200'), to: validAddress, accessList: [[validAddress, [validSlot]]], chainId, }, { common } ) - const expectedHash = Buffer.from( - '78528e2724aa359c58c13e43a7c467eb721ce8d410c2a12ee62943a3aaefb60b', - 'hex' + const expectedHash = hexStringToBytes( + '78528e2724aa359c58c13e43a7c467eb721ce8d410c2a12ee62943a3aaefb60b' ) t.deepEqual(unsignedTx.getMessageToSign(true), expectedHash), 'correct hashed version' - const expectedSerialization = Buffer.from( - '01f858018080809401010101010101010101010101010101010101018083010200f838f7940101010101010101010101010101010101010101e1a00101010101010101010101010101010101010101010101010101010101010101', - 'hex' + const expectedSerialization = hexStringToBytes( + '01f858018080809401010101010101010101010101010101010101018083010200f838f7940101010101010101010101010101010101010101e1a00101010101010101010101010101010101010101010101010101010101010101' ) t.deepEqual( unsignedTx.getMessageToSign(false), @@ -488,19 +492,18 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { // https://github.com/INFURA/go-ethlibs/blob/75b2a52a39d353ed8206cffaf68d09bd1b154aae/eth/transaction_signing_test.go#L87 t.test('should sign transaction correctly and return expected JSON', function (t) { - const address = Buffer.from('0000000000000000000000000000000000001337', 'hex') - const slot1 = Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000000', - 'hex' + const address = hexStringToBytes('0000000000000000000000000000000000001337') + const slot1 = hexStringToBytes( + '0000000000000000000000000000000000000000000000000000000000000000' ) const txData = { - data: Buffer.from('', 'hex'), + data: hexStringToBytes(''), gasLimit: 0x62d4, gasPrice: 0x3b9aca00, nonce: 0x00, - to: new Address(Buffer.from('df0a88b2b68c673713a8ec826003676f272e3573', 'hex')), + to: new Address(hexStringToBytes('df0a88b2b68c673713a8ec826003676f272e3573')), value: 0x01, - chainId: bufferToBigInt(Buffer.from('796f6c6f763378', 'hex')), + chainId: bytesToBigInt(hexStringToBytes('796f6c6f763378')), accessList: [[address, [slot1]]], } @@ -515,43 +518,42 @@ tape('[AccessListEIP2930Transaction] -> Class Specific Tests', function (t) { }) usedCommon.setEIPs([2718, 2929, 2930]) - const expectedUnsignedRaw = Buffer.from( - '01f86587796f6c6f76337880843b9aca008262d494df0a88b2b68c673713a8ec826003676f272e35730180f838f7940000000000000000000000000000000000001337e1a00000000000000000000000000000000000000000000000000000000000000000808080', - 'hex' + const expectedUnsignedRaw = hexStringToBytes( + '01f86587796f6c6f76337880843b9aca008262d494df0a88b2b68c673713a8ec826003676f272e35730180f838f7940000000000000000000000000000000000001337e1a00000000000000000000000000000000000000000000000000000000000000000808080' ) - const pkey = Buffer.from( - 'fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19', - 'hex' + const pkey = hexStringToBytes( + 'fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19' ) - const expectedSigned = Buffer.from( - '01f8a587796f6c6f76337880843b9aca008262d494df0a88b2b68c673713a8ec826003676f272e35730180f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0294ac94077b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938da00be950468ba1c25a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d', - 'hex' + const expectedSigned = hexStringToBytes( + '01f8a587796f6c6f76337880843b9aca008262d494df0a88b2b68c673713a8ec826003676f272e35730180f838f7940000000000000000000000000000000000001337e1a0000000000000000000000000000000000000000000000000000000000000000080a0294ac94077b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938da00be950468ba1c25a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d' ) - const expectedHash = Buffer.from( - 'bbd570a3c6acc9bb7da0d5c0322fe4ea2a300db80226f7df4fef39b2d6649eec', - 'hex' + const expectedHash = hexStringToBytes( + 'bbd570a3c6acc9bb7da0d5c0322fe4ea2a300db80226f7df4fef39b2d6649eec' ) const v = BigInt(0) - const r = bufferToBigInt( - Buffer.from('294ac94077b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938d', 'hex') + const r = bytesToBigInt( + hexStringToBytes('294ac94077b35057971e6b4b06dfdf55a6fbed819133a6c1d31e187f1bca938d') ) - const s = bufferToBigInt( - Buffer.from('0be950468ba1c25a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d', 'hex') + const s = bytesToBigInt( + hexStringToBytes('0be950468ba1c25a5cb50e9f6d8aa13c8cd21f24ba909402775b262ac76d374d') ) const unsignedTx = AccessListEIP2930Transaction.fromTxData(txData, { common: usedCommon }) const serializedMessageRaw = unsignedTx.serialize() - t.ok(expectedUnsignedRaw.equals(serializedMessageRaw), 'serialized unsigned message correct') + t.ok( + equalsBytes(expectedUnsignedRaw, serializedMessageRaw), + 'serialized unsigned message correct' + ) const signed = unsignedTx.sign(pkey) t.ok(v === signed.v!, 'v correct') t.ok(r === signed.r!, 'r correct') t.ok(s === signed.s!, 's correct') - t.ok(expectedSigned.equals(signed.serialize()), 'serialized signed message correct') - t.ok(expectedHash.equals(signed.hash()), 'hash correct') + t.ok(equalsBytes(expectedSigned, signed.serialize()), 'serialized signed message correct') + t.ok(equalsBytes(expectedHash, signed.hash()), 'hash correct') const expectedJSON = { chainId: '0x796f6c6f763378', diff --git a/packages/util/src/account.ts b/packages/util/src/account.ts index d181f3df24b..0e326c4a27d 100644 --- a/packages/util/src/account.ts +++ b/packages/util/src/account.ts @@ -1,53 +1,57 @@ import { RLP } from '@ethereumjs/rlp' import { keccak256 } from 'ethereum-cryptography/keccak' import { Point, utils } from 'ethereum-cryptography/secp256k1' -import { bytesToHex } from 'ethereum-cryptography/utils' +import { + bytesToHex, + concatBytes, + equalsBytes, + hexToBytes, + utf8ToBytes, +} from 'ethereum-cryptography/utils' import { - arrToBufArr, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, - bufferToHex, - toBuffer, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToPrefixedHexString, + toBytes, zeros, } from './bytes' import { KECCAK256_NULL, KECCAK256_RLP } from './constants' -import { assertIsBuffer, assertIsHexString, assertIsString } from './helpers' +import { assertIsBytes, assertIsHexString, assertIsString } from './helpers' import { stripHexPrefix } from './internal' -import type { BigIntLike, BufferLike } from './types' +import type { BigIntLike, BytesLike } from './types' const _0n = BigInt(0) export interface AccountData { nonce?: BigIntLike balance?: BigIntLike - storageRoot?: BufferLike - codeHash?: BufferLike + storageRoot?: BytesLike + codeHash?: BytesLike } -export type AccountBodyBuffer = [Buffer, Buffer, Buffer | Uint8Array, Buffer | Uint8Array] +export type AccountBodyBytes = [Uint8Array, Uint8Array, Uint8Array, Uint8Array] export class Account { nonce: bigint balance: bigint - storageRoot: Buffer - codeHash: Buffer + storageRoot: Uint8Array + codeHash: Uint8Array static fromAccountData(accountData: AccountData) { const { nonce, balance, storageRoot, codeHash } = accountData return new Account( - nonce !== undefined ? bufferToBigInt(toBuffer(nonce)) : undefined, - balance !== undefined ? bufferToBigInt(toBuffer(balance)) : undefined, - storageRoot !== undefined ? toBuffer(storageRoot) : undefined, - codeHash !== undefined ? toBuffer(codeHash) : undefined + nonce !== undefined ? bytesToBigInt(toBytes(nonce)) : undefined, + balance !== undefined ? bytesToBigInt(toBytes(balance)) : undefined, + storageRoot !== undefined ? toBytes(storageRoot) : undefined, + codeHash !== undefined ? toBytes(codeHash) : undefined ) } - public static fromRlpSerializedAccount(serialized: Buffer) { - const values = arrToBufArr(RLP.decode(Uint8Array.from(serialized)) as Uint8Array[]) as Buffer[] + public static fromRlpSerializedAccount(serialized: Uint8Array) { + const values = RLP.decode(serialized) as Uint8Array[] if (!Array.isArray(values)) { throw new Error('Invalid serialized account input. Must be array') @@ -56,10 +60,10 @@ export class Account { return this.fromValuesArray(values) } - public static fromValuesArray(values: Buffer[]) { + public static fromValuesArray(values: Uint8Array[]) { const [nonce, balance, storageRoot, codeHash] = values - return new Account(bufferToBigInt(nonce), bufferToBigInt(balance), storageRoot, codeHash) + return new Account(bytesToBigInt(nonce), bytesToBigInt(balance), storageRoot, codeHash) } /** @@ -91,29 +95,29 @@ export class Account { } /** - * Returns a Buffer Array of the raw Buffers for the account, in order. + * Returns an array of Uint8Arrays of the raw bytes for the account, in order. */ - raw(): Buffer[] { + raw(): Uint8Array[] { return [ - bigIntToUnpaddedBuffer(this.nonce), - bigIntToUnpaddedBuffer(this.balance), + bigIntToUnpaddedBytes(this.nonce), + bigIntToUnpaddedBytes(this.balance), this.storageRoot, this.codeHash, ] } /** - * Returns the RLP serialization of the account as a `Buffer`. + * Returns the RLP serialization of the account as a `Uint8Array`. */ - serialize(): Buffer { - return Buffer.from(RLP.encode(bufArrToArr(this.raw()))) + serialize(): Uint8Array { + return RLP.encode(this.raw()) } /** * Returns a `Boolean` determining if the account is a contract. */ isContract(): boolean { - return !this.codeHash.equals(KECCAK256_NULL) + return !equalsBytes(this.codeHash, KECCAK256_NULL) } /** @@ -122,7 +126,7 @@ export class Account { * "An account is considered empty when it has no code and zero nonce and zero balance." */ isEmpty(): boolean { - return this.balance === _0n && this.nonce === _0n && this.codeHash.equals(KECCAK256_NULL) + return this.balance === _0n && this.nonce === _0n && equalsBytes(this.codeHash, KECCAK256_NULL) } } @@ -160,12 +164,12 @@ export const toChecksumAddress = function ( let prefix = '' if (eip1191ChainId !== undefined) { - const chainId = bufferToBigInt(toBuffer(eip1191ChainId)) + const chainId = bytesToBigInt(toBytes(eip1191ChainId)) prefix = chainId.toString() + '0x' } - const buf = Buffer.from(prefix + address, 'utf8') - const hash = bytesToHex(keccak256(buf)) + const bytes = utf8ToBytes(prefix + address) + const hash = bytesToHex(keccak256(bytes)) let ret = '0x' for (let i = 0; i < address.length; i++) { @@ -196,18 +200,18 @@ export const isValidChecksumAddress = function ( * @param from The address which is creating this new address * @param nonce The nonce of the from account */ -export const generateAddress = function (from: Buffer, nonce: Buffer): Buffer { - assertIsBuffer(from) - assertIsBuffer(nonce) +export const generateAddress = function (from: Uint8Array, nonce: Uint8Array): Uint8Array { + assertIsBytes(from) + assertIsBytes(nonce) - if (bufferToBigInt(nonce) === BigInt(0)) { + if (bytesToBigInt(nonce) === BigInt(0)) { // in RLP we want to encode null in the case of zero nonce // read the RLP documentation for an answer if you dare - return Buffer.from(keccak256(RLP.encode(bufArrToArr([from, null] as any)))).slice(-20) + return keccak256(RLP.encode([from, Uint8Array.from([])])).subarray(-20) } // Only take the lower 160bits of the hash - return Buffer.from(keccak256(RLP.encode(bufArrToArr([from, nonce])))).slice(-20) + return keccak256(RLP.encode([from, nonce])).subarray(-20) } /** @@ -216,10 +220,14 @@ export const generateAddress = function (from: Buffer, nonce: Buffer): Buffer { * @param salt A salt * @param initCode The init code of the contract being created */ -export const generateAddress2 = function (from: Buffer, salt: Buffer, initCode: Buffer): Buffer { - assertIsBuffer(from) - assertIsBuffer(salt) - assertIsBuffer(initCode) +export const generateAddress2 = function ( + from: Uint8Array, + salt: Uint8Array, + initCode: Uint8Array +): Uint8Array { + assertIsBytes(from) + assertIsBytes(salt) + assertIsBytes(initCode) if (from.length !== 20) { throw new Error('Expected from to be of length 20') @@ -228,17 +236,15 @@ export const generateAddress2 = function (from: Buffer, salt: Buffer, initCode: throw new Error('Expected salt to be of length 32') } - const address = keccak256( - Buffer.concat([Buffer.from('ff', 'hex'), from, salt, keccak256(initCode)]) - ) + const address = keccak256(concatBytes(hexToBytes('ff'), from, salt, keccak256(initCode))) - return toBuffer(address).slice(-20) + return address.subarray(-20) } /** * Checks if the private key satisfies the rules of the curve secp256k1. */ -export const isValidPrivate = function (privateKey: Buffer): boolean { +export const isValidPrivate = function (privateKey: Uint8Array): boolean { return utils.isValidPrivateKey(privateKey) } @@ -248,13 +254,13 @@ export const isValidPrivate = function (privateKey: Buffer): boolean { * @param publicKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const isValidPublic = function (publicKey: Buffer, sanitize: boolean = false): boolean { - assertIsBuffer(publicKey) +export const isValidPublic = function (publicKey: Uint8Array, sanitize: boolean = false): boolean { + assertIsBytes(publicKey) if (publicKey.length === 64) { // Convert to SEC1 for secp256k1 // Automatically checks whether point is on curve try { - Point.fromHex(Buffer.concat([Buffer.from([4]), publicKey])) + Point.fromHex(concatBytes(Uint8Array.from([4]), publicKey)) return true } catch (e) { return false @@ -279,16 +285,16 @@ export const isValidPublic = function (publicKey: Buffer, sanitize: boolean = fa * @param pubKey The two points of an uncompressed key, unless sanitize is enabled * @param sanitize Accept public keys in other formats */ -export const pubToAddress = function (pubKey: Buffer, sanitize: boolean = false): Buffer { - assertIsBuffer(pubKey) +export const pubToAddress = function (pubKey: Uint8Array, sanitize: boolean = false): Uint8Array { + assertIsBytes(pubKey) if (sanitize && pubKey.length !== 64) { - pubKey = Buffer.from(Point.fromHex(pubKey).toRawBytes(false).slice(1)) + pubKey = Point.fromHex(pubKey).toRawBytes(false).subarray(1) } if (pubKey.length !== 64) { throw new Error('Expected pubKey to be of length 64') } // Only take the lower 160bits of the hash - return Buffer.from(keccak256(pubKey)).slice(-20) + return keccak256(pubKey).subarray(-20) } export const publicToAddress = pubToAddress @@ -296,27 +302,27 @@ export const publicToAddress = pubToAddress * Returns the ethereum public key of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToPublic = function (privateKey: Buffer): Buffer { - assertIsBuffer(privateKey) +export const privateToPublic = function (privateKey: Uint8Array): Uint8Array { + assertIsBytes(privateKey) // skip the type flag and use the X, Y points - return Buffer.from(Point.fromPrivateKey(privateKey).toRawBytes(false).slice(1)) + return Point.fromPrivateKey(privateKey).toRawBytes(false).subarray(1) } /** * Returns the ethereum address of a given private key. * @param privateKey A private key must be 256 bits wide */ -export const privateToAddress = function (privateKey: Buffer): Buffer { +export const privateToAddress = function (privateKey: Uint8Array): Uint8Array { return publicToAddress(privateToPublic(privateKey)) } /** * Converts a public key to the Ethereum format. */ -export const importPublic = function (publicKey: Buffer): Buffer { - assertIsBuffer(publicKey) +export const importPublic = function (publicKey: Uint8Array): Uint8Array { + assertIsBytes(publicKey) if (publicKey.length !== 64) { - publicKey = Buffer.from(Point.fromHex(publicKey).toRawBytes(false).slice(1)) + publicKey = Point.fromHex(publicKey).toRawBytes(false).subarray(1) } return publicKey } @@ -327,7 +333,7 @@ export const importPublic = function (publicKey: Buffer): Buffer { export const zeroAddress = function (): string { const addressLength = 20 const addr = zeros(addressLength) - return bufferToHex(addr) + return bytesToPrefixedHexString(addr) } /** @@ -344,33 +350,33 @@ export const isZeroAddress = function (hexAddress: string): boolean { return zeroAddr === hexAddress } -export function accountBodyFromSlim(body: AccountBodyBuffer) { +export function accountBodyFromSlim(body: AccountBodyBytes) { const [nonce, balance, storageRoot, codeHash] = body return [ nonce, balance, - arrToBufArr(storageRoot).length === 0 ? KECCAK256_RLP : storageRoot, - arrToBufArr(codeHash).length === 0 ? KECCAK256_NULL : codeHash, + storageRoot.length === 0 ? KECCAK256_RLP : storageRoot, + codeHash.length === 0 ? KECCAK256_NULL : codeHash, ] } const emptyUint8Arr = new Uint8Array(0) -export function accountBodyToSlim(body: AccountBodyBuffer) { +export function accountBodyToSlim(body: AccountBodyBytes) { const [nonce, balance, storageRoot, codeHash] = body return [ nonce, balance, - arrToBufArr(storageRoot).equals(KECCAK256_RLP) ? emptyUint8Arr : storageRoot, - arrToBufArr(codeHash).equals(KECCAK256_NULL) ? emptyUint8Arr : codeHash, + equalsBytes(storageRoot, KECCAK256_RLP) ? emptyUint8Arr : storageRoot, + equalsBytes(codeHash, KECCAK256_NULL) ? emptyUint8Arr : codeHash, ] } /** * Converts a slim account (per snap protocol spec) to the RLP encoded version of the account - * @param body Array of 4 Buffer-like items to represent the account + * @param body Array of 4 Uint8Array-like items to represent the account * @returns RLP encoded version of the account */ -export function accountBodyToRLP(body: AccountBodyBuffer, couldBeSlim = true) { +export function accountBodyToRLP(body: AccountBodyBytes, couldBeSlim = true) { const accountBody = couldBeSlim ? accountBodyFromSlim(body) : body - return arrToBufArr(RLP.encode(accountBody)) + return RLP.encode(accountBody) } diff --git a/packages/util/src/address.ts b/packages/util/src/address.ts index eaa26c42225..2d65597a9ed 100644 --- a/packages/util/src/address.ts +++ b/packages/util/src/address.ts @@ -1,3 +1,5 @@ +import { equalsBytes } from 'ethereum-cryptography/utils' + import { generateAddress, generateAddress2, @@ -5,19 +7,19 @@ import { privateToAddress, pubToAddress, } from './account' -import { bigIntToBuffer, bufferToBigInt, toBuffer, zeros } from './bytes' +import { bigIntToBytes, bytesToBigInt, bytesToPrefixedHexString, toBytes, zeros } from './bytes' /** * Handling and generating Ethereum addresses */ export class Address { - public readonly buf: Buffer + public readonly bytes: Uint8Array - constructor(buf: Buffer) { - if (buf.length !== 20) { + constructor(bytes: Uint8Array) { + if (bytes.length !== 20) { throw new Error('Invalid address length') } - this.buf = buf + this.bytes = bytes } /** @@ -35,31 +37,31 @@ export class Address { if (!isValidAddress(str)) { throw new Error('Invalid address') } - return new Address(toBuffer(str)) + return new Address(toBytes(str)) } /** * Returns an address for a given public key. * @param pubKey The two points of an uncompressed key */ - static fromPublicKey(pubKey: Buffer): Address { - if (!Buffer.isBuffer(pubKey)) { - throw new Error('Public key should be Buffer') + static fromPublicKey(pubKey: Uint8Array): Address { + if (!(pubKey instanceof Uint8Array)) { + throw new Error('Public key should be Uint8Array') } - const buf = pubToAddress(pubKey) - return new Address(buf) + const bytes = pubToAddress(pubKey) + return new Address(bytes) } /** * Returns an address for a given private key. * @param privateKey A private key must be 256 bits wide */ - static fromPrivateKey(privateKey: Buffer): Address { - if (!Buffer.isBuffer(privateKey)) { - throw new Error('Private key should be Buffer') + static fromPrivateKey(privateKey: Uint8Array): Address { + if (!(privateKey instanceof Uint8Array)) { + throw new Error('Private key should be Uint8Array') } - const buf = privateToAddress(privateKey) - return new Address(buf) + const bytes = privateToAddress(privateKey) + return new Address(bytes) } /** @@ -71,7 +73,7 @@ export class Address { if (typeof nonce !== 'bigint') { throw new Error('Expected nonce to be a bigint') } - return new Address(generateAddress(from.buf, bigIntToBuffer(nonce))) + return new Address(generateAddress(from.bytes, bigIntToBytes(nonce))) } /** @@ -80,21 +82,21 @@ export class Address { * @param salt A salt * @param initCode The init code of the contract being created */ - static generate2(from: Address, salt: Buffer, initCode: Buffer): Address { - if (!Buffer.isBuffer(salt)) { - throw new Error('Expected salt to be a Buffer') + static generate2(from: Address, salt: Uint8Array, initCode: Uint8Array): Address { + if (!(salt instanceof Uint8Array)) { + throw new Error('Expected salt to be a Uint8Array') } - if (!Buffer.isBuffer(initCode)) { - throw new Error('Expected initCode to be a Buffer') + if (!(initCode instanceof Uint8Array)) { + throw new Error('Expected initCode to be a Uint8Array') } - return new Address(generateAddress2(from.buf, salt, initCode)) + return new Address(generateAddress2(from.bytes, salt, initCode)) } /** * Is address equal to another. */ equals(address: Address): boolean { - return this.buf.equals(address.buf) + return equalsBytes(this.bytes, address.bytes) } /** @@ -109,7 +111,7 @@ export class Address { * by EIP-1352 */ isPrecompileOrSystemAddress(): boolean { - const address = bufferToBigInt(this.buf) + const address = bytesToBigInt(this.bytes) const rangeMin = BigInt(0) const rangeMax = BigInt('0xffff') return address >= rangeMin && address <= rangeMax @@ -119,13 +121,13 @@ export class Address { * Returns hex encoding of address. */ toString(): string { - return '0x' + this.buf.toString('hex') + return bytesToPrefixedHexString(this.bytes) } /** - * Returns Buffer representation of address. + * Returns a new Uint8Array representation of address. */ - toBuffer(): Buffer { - return Buffer.from(this.buf) + toBytes(): Uint8Array { + return new Uint8Array(this.bytes) } } diff --git a/packages/util/src/bytes.ts b/packages/util/src/bytes.ts index 086295f4193..e276b85b95e 100644 --- a/packages/util/src/bytes.ts +++ b/packages/util/src/bytes.ts @@ -1,13 +1,47 @@ -import { assertIsArray, assertIsBuffer, assertIsHexString } from './helpers' +import { getRandomBytesSync } from 'ethereum-cryptography/random' +import { bytesToHex, bytesToUtf8, hexToBytes } from 'ethereum-cryptography/utils' + +import { assertIsArray, assertIsBytes, assertIsHexString } from './helpers' import { isHexPrefixed, isHexString, padToEven, stripHexPrefix } from './internal' -import type { - NestedBufferArray, - NestedUint8Array, - PrefixedHexString, - TransformableToArray, - TransformableToBuffer, -} from './types' +import type { PrefixedHexString, TransformabletoBytes } from './types' + +/**************** Borrowed from @chainsafe/ssz */ +// Caching this info costs about ~1000 bytes and speeds up toHexString() by x6 +const hexByByte = Array.from({ length: 256 }, (v, i) => i.toString(16).padStart(2, '0')) + +export const bytesToPrefixedHexString = (bytes: Uint8Array): string => { + let hex = '0x' + if (bytes === undefined || bytes.length === 0) return hex + for (const byte of bytes) { + hex += hexByByte[byte] + } + return hex +} + +export const hexStringToBytes = (hex: string): Uint8Array => { + if (typeof hex !== 'string') { + throw new Error(`hex argument type ${typeof hex} must be of type string`) + } + + if (hex.startsWith('0x')) { + hex = hex.slice(2) + } + + if (hex.length % 2 !== 0) { + hex = padToEven(hex) + } + + const byteLen = hex.length / 2 + const bytes = new Uint8Array(byteLen) + for (let i = 0; i < byteLen; i++) { + const byte = parseInt(hex.slice(i * 2, (i + 1) * 2), 16) + bytes[i] = byte + } + return bytes +} + +/******************************************/ /** * Converts a `Number` into a hex `String` @@ -26,74 +60,71 @@ export const intToHex = function (i: number) { * @param {Number} i * @return {Buffer} */ -export const intToBuffer = function (i: number) { +export const intToBytes = function (i: number) { const hex = intToHex(i) - return Buffer.from(padToEven(hex.slice(2)), 'hex') + return hexToBytes(padToEven(hex.slice(2))) } /** * Returns a buffer filled with 0s. * @param bytes the number of bytes the buffer should be */ -export const zeros = function (bytes: number): Buffer { - return Buffer.allocUnsafe(bytes).fill(0) +export const zeros = function (bytes: number): Uint8Array { + return new Uint8Array(bytes) } /** - * Pads a `Buffer` with zeros till it has `length` bytes. + * Pads a `Uint8Array` with zeros till it has `length` bytes. * Truncates the beginning or end of input if its length exceeds `length`. - * @param msg the value to pad (Buffer) + * @param msg the value to pad (Uint8Array) * @param length the number of bytes the output should be * @param right whether to start padding form the left or right * @return (Buffer) */ -const setLength = function (msg: Buffer, length: number, right: boolean) { - const buf = zeros(length) +const setLength = function (msg: Uint8Array, length: number, right: boolean) { if (right) { if (msg.length < length) { - msg.copy(buf) - return buf + return new Uint8Array([...msg, ...zeros(length - msg.length)]) } - return msg.slice(0, length) + return msg.subarray(0, length) } else { if (msg.length < length) { - msg.copy(buf, length - msg.length) - return buf + return new Uint8Array([...zeros(length - msg.length), ...msg]) } - return msg.slice(-length) + return msg.subarray(-length) } } /** - * Left Pads a `Buffer` with leading zeros till it has `length` bytes. + * Left Pads a `Uint8Array` with leading zeros till it has `length` bytes. * Or it truncates the beginning if it exceeds. * @param msg the value to pad (Buffer) * @param length the number of bytes the output should be - * @return (Buffer) + * @return (Uint8Array) */ -export const setLengthLeft = function (msg: Buffer, length: number) { - assertIsBuffer(msg) +export const setLengthLeft = function (msg: Uint8Array, length: number) { + assertIsBytes(msg) return setLength(msg, length, false) } /** - * Right Pads a `Buffer` with trailing zeros till it has `length` bytes. + * Right Pads a `Uint8Array` with trailing zeros till it has `length` bytes. * it truncates the end if it exceeds. - * @param msg the value to pad (Buffer) + * @param msg the value to pad (Uint8Array) * @param length the number of bytes the output should be - * @return (Buffer) + * @return (Uint8Array) */ -export const setLengthRight = function (msg: Buffer, length: number) { - assertIsBuffer(msg) +export const setLengthRight = function (msg: Uint8Array, length: number) { + assertIsBytes(msg) return setLength(msg, length, true) } /** - * Trims leading zeros from a `Buffer`, `String` or `Number[]`. - * @param a (Buffer|Array|String) - * @return (Buffer|Array|String) + * Trims leading zeros from a `Uint8Array`, `String` or `Number[]`. + * @param a (Uint8Array|Array|String) + * @return (Uint8Array|Array|String) */ -const stripZeros = function (a: any): Buffer | number[] | string { +const stripZeros = function (a: any): Uint8Array | number[] | string { let first = a[0] while (a.length > 0 && first.toString() === '0') { a = a.slice(1) @@ -103,13 +134,13 @@ const stripZeros = function (a: any): Buffer | number[] | string { } /** - * Trims leading zeros from a `Buffer`. - * @param a (Buffer) - * @return (Buffer) + * Trims leading zeros from a `Uint8Array`. + * @param a (Uint8Array) + * @return (Uint8Array) */ -export const unpadBuffer = function (a: Buffer): Buffer { - assertIsBuffer(a) - return stripZeros(a) as Buffer +export const unpadBytes = function (a: Uint8Array): Uint8Array { + assertIsBytes(a) + return stripZeros(a) as Uint8Array } /** @@ -133,85 +164,71 @@ export const unpadHexString = function (a: string): string { return ('0x' + stripZeros(a)) as string } -export type ToBufferInputTypes = +export type ToBytesInputTypes = | PrefixedHexString | number | bigint - | Buffer | Uint8Array | number[] - | TransformableToArray - | TransformableToBuffer + | TransformabletoBytes | null | undefined /** - * Attempts to turn a value into a `Buffer`. - * Inputs supported: `Buffer`, `String` (hex-prefixed), `Number`, null/undefined, `BigInt` and other objects - * with a `toArray()` or `toBuffer()` method. + * Attempts to turn a value into a `Uint8Array`. + * Inputs supported: `Buffer`, `Uint8Array`, `String` (hex-prefixed), `Number`, null/undefined, `BigInt` and other objects + * with a `toArray()` or `toBytes()` method. * @param v the value */ -export const toBuffer = function (v: ToBufferInputTypes): Buffer { + +export const toBytes = function (v: ToBytesInputTypes): Uint8Array { if (v === null || v === undefined) { - return Buffer.allocUnsafe(0) + return new Uint8Array() } if (Buffer.isBuffer(v)) { - return Buffer.from(v) + return Uint8Array.from(v) } if (Array.isArray(v) || v instanceof Uint8Array) { - return Buffer.from(v as Uint8Array) + return Uint8Array.from(v) } if (typeof v === 'string') { if (!isHexString(v)) { throw new Error( - `Cannot convert string to buffer. toBuffer only supports 0x-prefixed hex strings and this string was given: ${v}` + `Cannot convert string to Uint8Array. toBytes only supports 0x-prefixed hex strings and this string was given: ${v}` ) } - return Buffer.from(padToEven(stripHexPrefix(v)), 'hex') + return hexToBytes(padToEven(v.slice(2))) } if (typeof v === 'number') { - return intToBuffer(v) + return intToBytes(v) } if (typeof v === 'bigint') { if (v < BigInt(0)) { - throw new Error(`Cannot convert negative bigint to buffer. Given: ${v}`) + throw new Error(`Cannot convert negative bigint to Uint8Array. Given: ${v}`) } let n = v.toString(16) if (n.length % 2) n = '0' + n - return Buffer.from(n, 'hex') - } - - if (v.toArray) { - // converts a BN to a Buffer - return Buffer.from(v.toArray()) + return hexToBytes(n) } - if (v.toBuffer) { - return Buffer.from(v.toBuffer()) + if (v.toBytes !== undefined) { + // converts a `TransformableToBytes` object to a Uint8Array + return v.toBytes() } throw new Error('invalid type') } /** - * Converts a `Buffer` into a `0x`-prefixed hex `String`. - * @param buf `Buffer` object to convert - */ -export const bufferToHex = function (buf: Buffer): string { - buf = toBuffer(buf) - return '0x' + buf.toString('hex') -} - -/** - * Converts a {@link Buffer} to a {@link bigint} + * Converts a {@link Uint8Array} to a {@link bigint} */ -export function bufferToBigInt(buf: Buffer) { - const hex = bufferToHex(buf) +export function bytesToBigInt(bytes: Uint8Array) { + const hex = bytesToPrefixedHexString(bytes) if (hex === '0x') { return BigInt(0) } @@ -219,37 +236,37 @@ export function bufferToBigInt(buf: Buffer) { } /** - * Converts a {@link bigint} to a {@link Buffer} + * Converts a {@link bigint} to a {@link Uint8Array} */ -export function bigIntToBuffer(num: bigint) { - return toBuffer('0x' + num.toString(16)) +export const bigIntToBytes = (num: bigint) => { + return toBytes('0x' + padToEven(num.toString(16))) } /** - * Converts a `Buffer` to a `Number`. - * @param buf `Buffer` object to convert + * Converts a `Uint8Array` to a `Number`. + * @param bytes `Uint8Array` object to convert * @throws If the input number exceeds 53 bits. */ -export const bufferToInt = function (buf: Buffer): number { - const res = Number(bufferToBigInt(buf)) +export const bytesToInt = function (bytes: Uint8Array): number { + const res = Number(bytesToBigInt(bytes)) if (!Number.isSafeInteger(res)) throw new Error('Number exceeds 53 bits') return res } /** - * Interprets a `Buffer` as a signed integer and returns a `BigInt`. Assumes 256-bit numbers. + * Interprets a `Uint8Array` as a signed integer and returns a `BigInt`. Assumes 256-bit numbers. * @param num Signed integer value */ -export const fromSigned = function (num: Buffer): bigint { - return BigInt.asIntN(256, bufferToBigInt(num)) +export const fromSigned = function (num: Uint8Array): bigint { + return BigInt.asIntN(256, bytesToBigInt(num)) } /** - * Converts a `BigInt` to an unsigned integer and returns it as a `Buffer`. Assumes 256-bit numbers. + * Converts a `BigInt` to an unsigned integer and returns it as a `Uint8Array`. Assumes 256-bit numbers. * @param num */ -export const toUnsigned = function (num: bigint): Buffer { - return bigIntToBuffer(BigInt.asUintN(256, num)) +export const toUnsigned = function (num: bigint): Uint8Array { + return bigIntToBytes(BigInt.asUintN(256, num)) } /** @@ -264,19 +281,19 @@ export const addHexPrefix = function (str: string): string { } /** - * Shortens a string or buffer's hex string representation to maxLength (default 50). + * Shortens a string or Uint8Array's hex string representation to maxLength (default 50). * * Examples: * * Input: '657468657265756d000000000000000000000000000000000000000000000000' * Output: '657468657265756d0000000000000000000000000000000000…' */ -export function short(buffer: Buffer | string, maxLength: number = 50): string { - const bufferStr = Buffer.isBuffer(buffer) ? buffer.toString('hex') : buffer - if (bufferStr.length <= maxLength) { - return bufferStr +export function short(bytes: Uint8Array | string, maxLength: number = 50): string { + const byteStr = bytes instanceof Uint8Array ? bytesToHex(bytes) : bytes + if (byteStr.length <= maxLength) { + return byteStr } - return bufferStr.slice(0, maxLength) + '…' + return byteStr.slice(0, maxLength) + '…' } /** @@ -302,30 +319,13 @@ export const toUtf8 = function (hex: string): string { if (hex.length % 2 !== 0) { throw new Error('Invalid non-even hex string input for toUtf8() provided') } - const bufferVal = Buffer.from(hex.replace(zerosRegexp, ''), 'hex') - - return bufferVal.toString('utf8') -} + const bytesVal = hexToBytes(hex.replace(zerosRegexp, '')) -/** - * Converts a `Buffer` or `Array` to JSON. - * @param ba (Buffer|Array) - * @return (Array|String|null) - */ -export const baToJSON = function (ba: any): any { - if (Buffer.isBuffer(ba)) { - return `0x${ba.toString('hex')}` - } else if (ba instanceof Array) { - const array = [] - for (let i = 0; i < ba.length; i++) { - array.push(baToJSON(ba[i])) - } - return array - } + return bytesToUtf8(bytesVal) } /** - * Checks provided Buffers for leading zeroes and throws if found. + * Checks provided Uint8Array for leading zeroes and throws if found. * * Examples: * @@ -337,56 +337,85 @@ export const baToJSON = function (ba: any): any { * @param values An object containing string keys and Buffer values * @throws if any provided value is found to have leading zero bytes */ -export const validateNoLeadingZeroes = function (values: { [key: string]: Buffer | undefined }) { +export const validateNoLeadingZeroes = function (values: { + [key: string]: Uint8Array | undefined +}) { for (const [k, v] of Object.entries(values)) { if (v !== undefined && v.length > 0 && v[0] === 0) { - throw new Error(`${k} cannot have leading zeroes, received: ${v.toString('hex')}`) + throw new Error(`${k} cannot have leading zeroes, received: ${bytesToHex(v)}`) } } } /** - * Converts a {@link Uint8Array} or {@link NestedUint8Array} to {@link Buffer} or {@link NestedBufferArray} + * Converts a {@link bigint} to a `0x` prefixed hex string */ -export function arrToBufArr(arr: Uint8Array): Buffer -export function arrToBufArr(arr: NestedUint8Array): NestedBufferArray -export function arrToBufArr(arr: Uint8Array | NestedUint8Array): Buffer | NestedBufferArray -export function arrToBufArr(arr: Uint8Array | NestedUint8Array): Buffer | NestedBufferArray { - if (!Array.isArray(arr)) { - return Buffer.from(arr) - } - return arr.map((a) => arrToBufArr(a)) +export const bigIntToHex = (num: bigint) => { + return '0x' + num.toString(16) } /** - * Converts a {@link Buffer} or {@link NestedBufferArray} to {@link Uint8Array} or {@link NestedUint8Array} + * Convert value from bigint to an unpadded Uint8Array + * (useful for RLP transport) + * @param value value to convert */ -export function bufArrToArr(arr: Buffer): Uint8Array -export function bufArrToArr(arr: NestedBufferArray): NestedUint8Array -export function bufArrToArr(arr: Buffer | NestedBufferArray): Uint8Array | NestedUint8Array -export function bufArrToArr(arr: Buffer | NestedBufferArray): Uint8Array | NestedUint8Array { - if (!Array.isArray(arr)) { - return Uint8Array.from(arr ?? []) - } - return arr.map((a) => bufArrToArr(a)) +export function bigIntToUnpaddedBytes(value: bigint): Uint8Array { + return unpadBytes(bigIntToBytes(value)) +} + +export function intToUnpaddedBytes(value: number): Uint8Array { + return unpadBytes(intToBytes(value)) } /** - * Converts a {@link bigint} to a `0x` prefixed hex string + * Compares two Uint8Arrays and returns a number indicating their order in a sorted array. + * + * @param {Uint8Array} value1 - The first Uint8Array to compare. + * @param {Uint8Array} value2 - The second Uint8Array to compare. + * @returns {number} A positive number if value1 is larger than value2, + * A negative number if value1 is smaller than value2, + * or 0 if value1 and value2 are equal. */ -export const bigIntToHex = (num: bigint) => { - return '0x' + num.toString(16) +export function compareBytes(value1: Uint8Array, value2: Uint8Array): number { + const bigIntValue1 = bytesToBigInt(value1) + const bigIntValue2 = bytesToBigInt(value2) + return bigIntValue1 > bigIntValue2 ? 1 : bigIntValue1 < bigIntValue2 ? -1 : 0 } /** - * Convert value from bigint to an unpadded Buffer - * (useful for RLP transport) - * @param value value to convert + * Generates a Uint8Array of random bytes of specified length. + * + * @param {number} length - The length of the Uint8Array. + * @returns {Uint8Array} A Uint8Array of random bytes of specified length. */ -export function bigIntToUnpaddedBuffer(value: bigint): Buffer { - return unpadBuffer(bigIntToBuffer(value)) +export function randomBytes(length: number): Uint8Array { + return getRandomBytesSync(length) } -export function intToUnpaddedBuffer(value: number): Buffer { - return unpadBuffer(intToBuffer(value)) +/** + * This mirrors the functionality of the `ethereum-cryptography` export except + * it skips the check to validate that every element of `arrays` is indead a `uint8Array` + * Can give small performance gains on large arrays + * @param arrays an array of Uint8Arrays + * @returns one Uint8Array with all the elements of the original set + * works like `Buffer.concat` + */ +export const concatBytesNoTypeCheck = (...arrays: Uint8Array[]) => { + if (arrays.length === 1) return arrays[0] + const length = arrays.reduce((a, arr) => a + arr.length, 0) + const result = new Uint8Array(length) + for (let i = 0, pad = 0; i < arrays.length; i++) { + const arr = arrays[i] + result.set(arr, pad) + pad += arr.length + } + return result } + +export { + bytesToHex, + bytesToUtf8, + concatBytes, + equalsBytes, + utf8ToBytes, +} from 'ethereum-cryptography/utils' diff --git a/packages/util/src/constants.ts b/packages/util/src/constants.ts index 6b3d660217c..5c2b174c1ea 100644 --- a/packages/util/src/constants.ts +++ b/packages/util/src/constants.ts @@ -1,5 +1,5 @@ -import { Buffer } from 'buffer' import { CURVE } from 'ethereum-cryptography/secp256k1' +import { hexToBytes } from 'ethereum-cryptography/utils' /** * 2^64-1 @@ -40,7 +40,7 @@ export const KECCAK256_NULL_S = 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273 /** * Keccak-256 hash of null */ -export const KECCAK256_NULL = Buffer.from(KECCAK256_NULL_S, 'hex') +export const KECCAK256_NULL = hexToBytes(KECCAK256_NULL_S) /** * Keccak-256 of an RLP of an empty array @@ -51,7 +51,7 @@ export const KECCAK256_RLP_ARRAY_S = /** * Keccak-256 of an RLP of an empty array */ -export const KECCAK256_RLP_ARRAY = Buffer.from(KECCAK256_RLP_ARRAY_S, 'hex') +export const KECCAK256_RLP_ARRAY = hexToBytes(KECCAK256_RLP_ARRAY_S) /** * Keccak-256 hash of the RLP of null @@ -61,11 +61,11 @@ export const KECCAK256_RLP_S = '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc0 /** * Keccak-256 hash of the RLP of null */ -export const KECCAK256_RLP = Buffer.from(KECCAK256_RLP_S, 'hex') +export const KECCAK256_RLP = hexToBytes(KECCAK256_RLP_S) /** * RLP encoded empty string */ -export const RLP_EMPTY_STRING = Buffer.from([0x80]) +export const RLP_EMPTY_STRING = Uint8Array.from([0x80]) export const MAX_WITHDRAWALS_PER_PAYLOAD = 16 diff --git a/packages/util/src/helpers.ts b/packages/util/src/helpers.ts index 297dbb0f8cf..284018e7889 100644 --- a/packages/util/src/helpers.ts +++ b/packages/util/src/helpers.ts @@ -15,9 +15,9 @@ export const assertIsHexString = function (input: string): void { * Throws if input is not a buffer * @param {Buffer} input value to check */ -export const assertIsBuffer = function (input: Buffer): void { - if (!Buffer.isBuffer(input)) { - const msg = `This method only supports Buffer but input was: ${input}` +export const assertIsBytes = function (input: Uint8Array): void { + if (!(input instanceof Uint8Array)) { + const msg = `This method only supports Uint8Array but input was: ${input}` throw new Error(msg) } } diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index 51bcfe86ed5..b277ee349a2 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -29,7 +29,7 @@ export * from './withdrawal' export * from './signature' /** - * Utilities for manipulating Buffers, byte arrays, etc. + * Utilities for manipulating bytes, Uint8Arrays, etc. */ export * from './bytes' diff --git a/packages/util/src/internal.ts b/packages/util/src/internal.ts index 9a9d04ad1cd..e55c58a33d1 100644 --- a/packages/util/src/internal.ts +++ b/packages/util/src/internal.ts @@ -22,6 +22,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE */ +import { bytesToHex, utf8ToBytes } from 'ethereum-cryptography/utils' + /** * Returns a `Boolean` on whether or not the a `String` starts with '0x' * @param str the string input value @@ -75,7 +77,7 @@ export function getBinarySize(str: string) { throw new Error(`[getBinarySize] method requires input type 'string', received ${typeof str}`) } - return Buffer.byteLength(str, 'utf8') + return utf8ToBytes(str).byteLength } /** @@ -134,9 +136,9 @@ export function toAscii(hex: string): string { * @returns hex representation of input string */ export function fromUtf8(stringValue: string) { - const str = Buffer.from(stringValue, 'utf8') + const str = utf8ToBytes(stringValue) - return `0x${padToEven(str.toString('hex')).replace(/^0+|0+$/g, '')}` + return `0x${padToEven(bytesToHex(str)).replace(/^0+|0+$/g, '')}` } /** diff --git a/packages/util/src/signature.ts b/packages/util/src/signature.ts index 6294b1656a4..6ee44a10750 100644 --- a/packages/util/src/signature.ts +++ b/packages/util/src/signature.ts @@ -1,14 +1,21 @@ import { keccak256 } from 'ethereum-cryptography/keccak' import { recoverPublicKey, signSync } from 'ethereum-cryptography/secp256k1' - -import { bufferToBigInt, bufferToHex, bufferToInt, setLengthLeft, toBuffer } from './bytes' +import { concatBytes, utf8ToBytes } from 'ethereum-cryptography/utils' + +import { + bytesToBigInt, + bytesToInt, + bytesToPrefixedHexString, + setLengthLeft, + toBytes, +} from './bytes' import { SECP256K1_ORDER, SECP256K1_ORDER_DIV_2 } from './constants' -import { assertIsBuffer } from './helpers' +import { assertIsBytes } from './helpers' export interface ECDSASignature { v: bigint - r: Buffer - s: Buffer + r: Uint8Array + s: Uint8Array } /** @@ -17,11 +24,15 @@ export interface ECDSASignature { * If `chainId` is provided assume an EIP-155-style signature and calculate the `v` value * accordingly, otherwise return a "static" `v` just derived from the `recovery` bit */ -export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: bigint): ECDSASignature { +export function ecsign( + msgHash: Uint8Array, + privateKey: Uint8Array, + chainId?: bigint +): ECDSASignature { const [signature, recovery] = signSync(msgHash, privateKey, { recovered: true, der: false }) - const r = Buffer.from(signature.slice(0, 32)) - const s = Buffer.from(signature.slice(32, 64)) + const r = signature.subarray(0, 32) + const s = signature.subarray(32, 64) const v = chainId === undefined @@ -50,20 +61,20 @@ function isValidSigRecovery(recovery: bigint): boolean { * @returns Recovered public key */ export const ecrecover = function ( - msgHash: Buffer, + msgHash: Uint8Array, v: bigint, - r: Buffer, - s: Buffer, + r: Uint8Array, + s: Uint8Array, chainId?: bigint -): Buffer { - const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64) +): Uint8Array { + const signature = concatBytes(setLengthLeft(r, 32), setLengthLeft(s, 32)) const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } const senderPubKey = recoverPublicKey(msgHash, signature, Number(recovery)) - return Buffer.from(senderPubKey.slice(1)) + return senderPubKey.subarray(1) } /** @@ -71,14 +82,22 @@ export const ecrecover = function ( * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions * @returns Signature */ -export const toRpcSig = function (v: bigint, r: Buffer, s: Buffer, chainId?: bigint): string { +export const toRpcSig = function ( + v: bigint, + r: Uint8Array, + s: Uint8Array, + chainId?: bigint +): string { const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } // geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin - return bufferToHex(Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32), toBuffer(v)])) + + return bytesToPrefixedHexString( + concatBytes(setLengthLeft(r, 32), setLengthLeft(s, 32), toBytes(v)) + ) } /** @@ -86,19 +105,23 @@ export const toRpcSig = function (v: bigint, r: Buffer, s: Buffer, chainId?: big * NOTE: Accepts `v === 0 | v === 1` for EIP1559 transactions * @returns Signature */ -export const toCompactSig = function (v: bigint, r: Buffer, s: Buffer, chainId?: bigint): string { +export const toCompactSig = function ( + v: bigint, + r: Uint8Array, + s: Uint8Array, + chainId?: bigint +): string { const recovery = calculateSigRecovery(v, chainId) if (!isValidSigRecovery(recovery)) { throw new Error('Invalid signature v value') } - let ss = s + const ss = Uint8Array.from([...s]) if ((v > BigInt(28) && v % BigInt(2) === BigInt(1)) || v === BigInt(1) || v === BigInt(28)) { - ss = Buffer.from(s) ss[0] |= 0x80 } - return bufferToHex(Buffer.concat([setLengthLeft(r, 32), setLengthLeft(ss, 32)])) + return bytesToPrefixedHexString(concatBytes(setLengthLeft(r, 32), setLengthLeft(ss, 32))) } /** @@ -110,20 +133,20 @@ export const toCompactSig = function (v: bigint, r: Buffer, s: Buffer, chainId?: * it's a signed message (EIP-191 or EIP-712) adding `27` at the end. Remove if needed. */ export const fromRpcSig = function (sig: string): ECDSASignature { - const buf: Buffer = toBuffer(sig) + const bytes: Uint8Array = toBytes(sig) - let r: Buffer - let s: Buffer + let r: Uint8Array + let s: Uint8Array let v: bigint - if (buf.length >= 65) { - r = buf.slice(0, 32) - s = buf.slice(32, 64) - v = bufferToBigInt(buf.slice(64)) - } else if (buf.length === 64) { + if (bytes.length >= 65) { + r = bytes.subarray(0, 32) + s = bytes.subarray(32, 64) + v = bytesToBigInt(bytes.subarray(64)) + } else if (bytes.length === 64) { // Compact Signature Representation (https://eips.ethereum.org/EIPS/eip-2098) - r = buf.slice(0, 32) - s = buf.slice(32, 64) - v = BigInt(bufferToInt(buf.slice(32, 33)) >> 7) + r = bytes.subarray(0, 32) + s = bytes.subarray(32, 64) + v = BigInt(bytesToInt(bytes.subarray(32, 33)) >> 7) s[0] &= 0x7f } else { throw new Error('Invalid signature length') @@ -148,8 +171,8 @@ export const fromRpcSig = function (sig: string): ECDSASignature { */ export const isValidSignature = function ( v: bigint, - r: Buffer, - s: Buffer, + r: Uint8Array, + s: Uint8Array, homesteadOrLater: boolean = true, chainId?: bigint ): boolean { @@ -161,8 +184,8 @@ export const isValidSignature = function ( return false } - const rBigInt = bufferToBigInt(r) - const sBigInt = bufferToBigInt(s) + const rBigInt = bytesToBigInt(r) + const sBigInt = bytesToBigInt(s) if ( rBigInt === BigInt(0) || @@ -186,8 +209,8 @@ export const isValidSignature = function ( * call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key * used to produce the signature. */ -export const hashPersonalMessage = function (message: Buffer): Buffer { - assertIsBuffer(message) - const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${message.length}`, 'utf-8') - return Buffer.from(keccak256(Buffer.concat([prefix, message]))) +export const hashPersonalMessage = function (message: Uint8Array): Uint8Array { + assertIsBytes(message) + const prefix = utf8ToBytes(`\u0019Ethereum Signed Message:\n${message.length}`) + return keccak256(concatBytes(prefix, message)) } diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index 6c007c0fe2d..1c4c6e4a4df 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -1,24 +1,25 @@ -import { bufferToBigInt, bufferToHex, toBuffer } from './bytes' +import { bytesToHex } from 'ethereum-cryptography/utils' + +import { bytesToBigInt, toBytes } from './bytes' import { isHexString } from './internal' import type { Address } from './address' -import type { ToBufferInputTypes } from './bytes' +import type { ToBytesInputTypes } from './bytes' /* * A type that represents an input that can be converted to a BigInt. */ -export type BigIntLike = bigint | PrefixedHexString | number | Buffer +export type BigIntLike = bigint | PrefixedHexString | number | Uint8Array /* * A type that represents an input that can be converted to a Buffer. */ -export type BufferLike = - | Buffer +export type BytesLike = | Uint8Array | number[] | number | bigint - | TransformableToBuffer + | TransformabletoBytes | PrefixedHexString /* @@ -29,22 +30,10 @@ export type PrefixedHexString = string /** * A type that represents an input that can be converted to an Address. */ -export type AddressLike = Address | Buffer | PrefixedHexString +export type AddressLike = Address | Uint8Array | PrefixedHexString -/* - * A type that represents an object that has a `toArray()` method. - */ -export interface TransformableToArray { - toArray(): Uint8Array - toBuffer?(): Buffer -} - -/* - * A type that represents an object that has a `toBuffer()` method. - */ -export interface TransformableToBuffer { - toBuffer(): Buffer - toArray?(): Uint8Array +export interface TransformabletoBytes { + toBytes?(): Uint8Array } export type NestedUint8Array = Array @@ -56,14 +45,14 @@ export type NestedBufferArray = Array export enum TypeOutput { Number, BigInt, - Buffer, + Uint8Array, PrefixedHexString, } export type TypeOutputReturnType = { [TypeOutput.Number]: number [TypeOutput.BigInt]: bigint - [TypeOutput.Buffer]: Buffer + [TypeOutput.Uint8Array]: Uint8Array [TypeOutput.PrefixedHexString]: PrefixedHexString } @@ -76,11 +65,11 @@ export type TypeOutputReturnType = { export function toType(input: null, outputType: T): null export function toType(input: undefined, outputType: T): undefined export function toType( - input: ToBufferInputTypes, + input: ToBytesInputTypes, outputType: T ): TypeOutputReturnType[T] export function toType( - input: ToBufferInputTypes, + input: ToBytesInputTypes, outputType: T ): TypeOutputReturnType[T] | undefined | null { if (input === null) { @@ -98,15 +87,15 @@ export function toType( ) } - const output = toBuffer(input) + const output = toBytes(input) switch (outputType) { - case TypeOutput.Buffer: + case TypeOutput.Uint8Array: return output as TypeOutputReturnType[T] case TypeOutput.BigInt: - return bufferToBigInt(output) as TypeOutputReturnType[T] + return bytesToBigInt(output) as TypeOutputReturnType[T] case TypeOutput.Number: { - const bigInt = bufferToBigInt(output) + const bigInt = bytesToBigInt(output) if (bigInt > BigInt(Number.MAX_SAFE_INTEGER)) { throw new Error( 'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)' @@ -115,7 +104,7 @@ export function toType( return Number(bigInt) as TypeOutputReturnType[T] } case TypeOutput.PrefixedHexString: - return bufferToHex(output) as TypeOutputReturnType[T] + return bytesToHex(output) as TypeOutputReturnType[T] default: throw new Error('unknown outputType') } diff --git a/packages/util/src/withdrawal.ts b/packages/util/src/withdrawal.ts index 88a9ef59180..53042f9f008 100644 --- a/packages/util/src/withdrawal.ts +++ b/packages/util/src/withdrawal.ts @@ -1,5 +1,5 @@ import { Address } from './address' -import { bigIntToHex } from './bytes' +import { bigIntToHex, bytesToPrefixedHexString, toBytes } from './bytes' import { TypeOutput, toType } from './types' import type { AddressLike, BigIntLike } from './types' @@ -26,7 +26,7 @@ export interface JsonRpcWithdrawal { amount: string // QUANTITY - bigint amount in Gwei 8 bytes } -export type WithdrawalBuffer = [Buffer, Buffer, Buffer, Buffer] +export type WithdrawalBytes = [Uint8Array, Uint8Array, Uint8Array, Uint8Array] /** * Representation of EIP-4895 withdrawal data @@ -56,13 +56,13 @@ export class Withdrawal { } = withdrawalData const index = toType(indexData, TypeOutput.BigInt) const validatorIndex = toType(validatorIndexData, TypeOutput.BigInt) - const address = new Address(toType(addressData, TypeOutput.Buffer)) + const address = addressData instanceof Address ? addressData : new Address(toBytes(addressData)) const amount = toType(amountData, TypeOutput.BigInt) return new Withdrawal(index, validatorIndex, address, amount) } - public static fromValuesArray(withdrawalArray: WithdrawalBuffer) { + public static fromValuesArray(withdrawalArray: WithdrawalBytes) { if (withdrawalArray.length !== 4) { throw Error(`Invalid withdrawalArray length expected=4 actual=${withdrawalArray.length}`) } @@ -75,39 +75,36 @@ export class Withdrawal { * @param withdrawal the withdrawal to convert * @returns buffer array of the withdrawal */ - public static toBufferArray(withdrawal: Withdrawal | WithdrawalData): WithdrawalBuffer { + public static toBytesArray(withdrawal: Withdrawal | WithdrawalData): WithdrawalBytes { const { index, validatorIndex, address, amount } = withdrawal - const indexBuffer = + const indexBytes = toType(index, TypeOutput.BigInt) === BigInt(0) - ? Buffer.alloc(0) - : toType(index, TypeOutput.Buffer) - const validatorIndexBuffer = + ? new Uint8Array() + : toType(index, TypeOutput.Uint8Array) + const validatorIndexBytes = toType(validatorIndex, TypeOutput.BigInt) === BigInt(0) - ? Buffer.alloc(0) - : toType(validatorIndex, TypeOutput.Buffer) - let addressBuffer - if (address instanceof Address) { - addressBuffer = (
address).buf - } else { - addressBuffer = toType(address, TypeOutput.Buffer) - } - const amountBuffer = + ? new Uint8Array() + : toType(validatorIndex, TypeOutput.Uint8Array) + const addressBytes = + address instanceof Address ? (
address).bytes : toType(address, TypeOutput.Uint8Array) + + const amountBytes = toType(amount, TypeOutput.BigInt) === BigInt(0) - ? Buffer.alloc(0) - : toType(amount, TypeOutput.Buffer) + ? new Uint8Array() + : toType(amount, TypeOutput.Uint8Array) - return [indexBuffer, validatorIndexBuffer, addressBuffer, amountBuffer] + return [indexBytes, validatorIndexBytes, addressBytes, amountBytes] } raw() { - return Withdrawal.toBufferArray(this) + return Withdrawal.toBytesArray(this) } toValue() { return { index: this.index, validatorIndex: this.validatorIndex, - address: this.address.buf, + address: this.address.bytes, amount: this.amount, } } @@ -116,7 +113,7 @@ export class Withdrawal { return { index: bigIntToHex(this.index), validatorIndex: bigIntToHex(this.validatorIndex), - address: '0x' + this.address.buf.toString('hex'), + address: bytesToPrefixedHexString(this.address.bytes), amount: bigIntToHex(this.amount), } } diff --git a/packages/util/test/account.spec.ts b/packages/util/test/account.spec.ts index a2f3b7f3429..082d3a477b5 100644 --- a/packages/util/test/account.spec.ts +++ b/packages/util/test/account.spec.ts @@ -1,20 +1,26 @@ import { RLP } from '@ethereumjs/rlp' +import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Account, - bufferToBigInt, + bytesToBigInt, + bytesToPrefixedHexString, generateAddress, generateAddress2, + hexStringToBytes, importPublic, + intToBytes, + intToHex, isValidAddress, isValidChecksumAddress, isValidPrivate, isValidPublic, + padToEven, privateToAddress, privateToPublic, publicToAddress, - toBuffer, + toBytes, toChecksumAddress, } from '../src' @@ -28,12 +34,12 @@ tape('Account', function (t) { st.equal(account.nonce, _0n, 'should have zero nonce') st.equal(account.balance, _0n, 'should have zero balance') st.equal( - account.storageRoot.toString('hex'), + bytesToHex(account.storageRoot), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', 'should have storageRoot equal to KECCAK256_RLP' ) st.equal( - account.codeHash.toString('hex'), + bytesToHex(account.codeHash), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'should have codeHash equal to KECCAK256_NULL' ) @@ -47,16 +53,17 @@ tape('Account', function (t) { '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', // storageRoot '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', // codeHash ] - const account = Account.fromValuesArray(raw.map(toBuffer)) + const account = Account.fromValuesArray(raw.map((el) => hexStringToBytes(el))) + st.equal(account.nonce, BigInt(2), 'should have correct nonce') st.equal(account.balance, BigInt(900), 'should have correct balance') st.equal( - account.storageRoot.toString('hex'), + bytesToHex(account.storageRoot), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', 'should have correct storageRoot' ) st.equal( - account.codeHash.toString('hex'), + bytesToHex(account.codeHash), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'should have correct codeHash' ) @@ -74,12 +81,12 @@ tape('Account', function (t) { st.equal(account.nonce, BigInt(2), 'should have correct nonce') st.equal(account.balance, BigInt(900), 'should have correct balance') st.equal( - account.storageRoot.toString('hex'), + bytesToHex(account.storageRoot), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', 'should have correct storageRoot' ) st.equal( - account.codeHash.toString('hex'), + bytesToHex(account.codeHash), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'should have correct codeHash' ) @@ -87,20 +94,19 @@ tape('Account', function (t) { }) t.test('from RLP data', function (st) { - const accountRlp = Buffer.from( - 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex' + const accountRlp = hexToBytes( + 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) const account = Account.fromRlpSerializedAccount(accountRlp) st.equal(account.nonce, BigInt(2), 'should have correct nonce') st.equal(account.balance, BigInt(900), 'should have correct balance') st.equal( - account.storageRoot.toString('hex'), + bytesToHex(account.storageRoot), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', 'should have correct storageRoot' ) st.equal( - account.codeHash.toString('hex'), + bytesToHex(account.codeHash), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', 'should have correct codeHash' ) @@ -115,17 +121,15 @@ tape('Account', function (t) { codeHash: '0xc5d2461236f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', } const account = Account.fromAccountData(raw) - const accountRlp = Buffer.from( - RLP.encode([raw.nonce, raw.balance, raw.storageRoot, raw.codeHash]) - ) - st.ok(account.serialize().equals(accountRlp), 'should serialize correctly') + const accountRlp = RLP.encode([raw.nonce, raw.balance, raw.storageRoot, raw.codeHash]) + + st.ok(equalsBytes(account.serialize(), accountRlp), 'should serialize correctly') st.end() }) t.test('isContract', function (st) { - const accountRlp = Buffer.from( - 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470', - 'hex' + const accountRlp = hexToBytes( + 'f84602820384a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) let account = Account.fromRlpSerializedAccount(accountRlp) st.notOk(account.isContract(), 'should return false for a non-contract account') @@ -158,11 +162,11 @@ tape('Account', function (t) { t.test('validation', function (st) { st.throws(() => { - new Account(undefined, undefined, Buffer.from('hey'), undefined) + new Account(undefined, undefined, hexToBytes('hey'), undefined) }, 'should only accept length 32 buffer for storageRoot') st.throws(() => { - new Account(undefined, undefined, undefined, Buffer.from('hey')) + new Account(undefined, undefined, undefined, hexToBytes('hey')) }, 'should only accept length 32 buffer for codeHash') const data = { balance: BigInt(5) } @@ -186,11 +190,11 @@ tape('Utility Functions', function (t) { const SECP256K1_N = BigInt('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141') let tmp = '0011223344' - st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on short input') + st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on short input') tmp = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on too big input') + st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on too big input') st.notOk( isValidPrivate(('WRONG_INPUT_TYPE') as Buffer), @@ -198,97 +202,82 @@ tape('Utility Functions', function (t) { ) tmp = '0000000000000000000000000000000000000000000000000000000000000000' - st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (zero)') + st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on invalid curve (zero)') tmp = SECP256K1_N.toString(16) - st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (== N)') + st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on invalid curve (== N)') tmp = (SECP256K1_N + BigInt(1)).toString(16) - st.notOk(isValidPrivate(Buffer.from(tmp, 'hex')), 'should fail on invalid curve (>= N)') + st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on invalid curve (>= N)') tmp = (SECP256K1_N - BigInt(1)).toString(16) - st.ok(isValidPrivate(Buffer.from(tmp, 'hex')), 'should work otherwise (< N)') + st.ok(isValidPrivate(hexToBytes(tmp)), 'should work otherwise (< N)') st.end() }) t.test('isValidPublic', function (st) { - let pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex' + let pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744' ) st.notOk(isValidPublic(pubKey), 'should fail on too short input') - pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d00', - 'hex' + pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d00' ) st.notOk(isValidPublic(pubKey), 'should fail on too big input') - pubKey = Buffer.from( - '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.notOk(isValidPublic(pubKey), 'should fail on SEC1 key') - pubKey = Buffer.from( - '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.ok(isValidPublic(pubKey, true), "shouldn't fail on SEC1 key wt.testh sant.testize enabled") - pubKey = Buffer.from( - '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.notOk(isValidPublic(pubKey), 'should fail wt.testh an invalid SEC1 public key') - pubKey = Buffer.from( - '03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', - 'hex' - ) + pubKey = hexToBytes('03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f') st.notOk(isValidPublic(pubKey), 'should fail an invalid 33-byte public key') - pubKey = Buffer.from( - 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000001', - 'hex' + pubKey = hexToBytes( + 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000001' ) st.notOk(isValidPublic(pubKey), 'should fail an invalid 64-byte public key') - pubKey = Buffer.from( - '04fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000001', - 'hex' + pubKey = hexToBytes( + '04fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0000000000000000000000000000000000000000000000000000000000000001' ) st.notOk(isValidPublic(pubKey, true), 'should fail an invalid 65-byte public key') - pubKey = Buffer.from( - '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a', - 'hex' - ) + pubKey = hexToBytes('033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a') st.ok( isValidPublic(pubKey, true), 'should work wt.testh compressed keys wt.testh sant.testize enabled' ) - pubKey = Buffer.from( - '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.ok(isValidPublic(pubKey, true), 'should work wt.testh sant.testize enabled') - pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.ok(isValidPublic(pubKey), 'should work otherwise') pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' as any try { - isValidPublic((pubKey) as Buffer) + isValidPublic((pubKey) as Uint8Array) } catch (err: any) { st.ok( - err.message.includes('This method only supports Buffer'), - 'should throw if input is not Buffer' + err.message.includes('This method only supports Uint8Array'), + 'should throw if input is not Uint8Array' ) } st.end() @@ -301,7 +290,7 @@ tape('Utility Functions', function (t) { let tmp = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' st.equal( - importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + bytesToHex(importPublic(hexToBytes(tmp))), pubKey, 'should work wt.testh an Ethereum public key' ) @@ -309,14 +298,14 @@ tape('Utility Functions', function (t) { tmp = '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' st.equal( - importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + bytesToHex(importPublic(hexToBytes(tmp))), pubKey, 'should work wt.testh uncompressed SEC1 keys' ) tmp = '033a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a' st.equal( - importPublic(Buffer.from(tmp, 'hex')).toString('hex'), + bytesToHex(importPublic(hexToBytes(tmp))), pubKey, 'should work wt.testh compressed SEC1 keys' ) @@ -328,33 +317,29 @@ tape('Utility Functions', function (t) { }) t.test('publicToAddress', function (st) { - let pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + let pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) let address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' let r = publicToAddress(pubKey) - st.equal(r.toString('hex'), address, 'should produce an address given a public key') + st.equal(bytesToHex(r), address, 'should produce an address given a public key') - pubKey = Buffer.from( - '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '043a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' r = publicToAddress(pubKey, true) - st.equal(r.toString('hex'), address, 'should produce an address given a SEC1 public key') + st.equal(bytesToHex(r), address, 'should produce an address given a SEC1 public key') - pubKey = Buffer.from( - '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + pubKey = hexToBytes( + '023a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) st.throws(function () { publicToAddress(pubKey, true) }, "shouldn't produce an address given an invalid SEC1 public key") - pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex' + pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744' ) st.throws(function () { publicToAddress(pubKey) @@ -364,45 +349,36 @@ tape('Utility Functions', function (t) { '0x3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' as any st.throws(function () { publicToAddress(pubKey) - }, 'should throw if input is not a buffer') + }, 'should throw if input is not a Uint8Array') st.end() }) t.test('privateToPublic', function (st) { const pubKey = '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' - let privateKey = Buffer.from( - 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', - 'hex' - ) + let privateKey = hexToBytes('ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f') const r = privateToPublic(privateKey) - st.equal(r.toString('hex'), pubKey, 'should produce a public key given a private key') + st.equal(bytesToHex(r), pubKey, 'should produce a public key given a private key') - privateKey = Buffer.from( - 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a', - 'hex' - ) + privateKey = hexToBytes('ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f2a') st.throws(function () { privateToPublic(privateKey) }, "shouldn't produce a public key given an invalid private key") - privateKey = Buffer.from( - 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c', - 'hex' - ) + privateKey = hexToBytes('ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c') st.throws(function () { privateToPublic(privateKey) }, "shouldn't produce a public key given an invalid private key") privateKey = '0xea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f' as any try { - privateToPublic((privateKey) as Buffer) + privateToPublic((privateKey) as Uint8Array) } catch (err: any) { st.ok( - err.message.includes('This method only supports Buffer'), - 'should throw if private key is not Buffer' + err.message.includes('This method only supports Uint8Array'), + 'should throw if private key is not Uint8Array' ) - st.ok(err.message.includes(privateKey), 'should throw if private key is not Buffer') + st.ok(err.message.includes(privateKey), 'should throw if private key is not Uint8Array') } st.end() }) @@ -410,35 +386,31 @@ tape('Utility Functions', function (t) { t.test('privateToAddress', function (st) { const address = '2f015c60e0be116b1f0cd534704db9c92118fb6a' // Our private key - const privateKey = Buffer.from( - 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f', - 'hex' + const privateKey = hexToBytes( + 'ea54bdc52d163f88c93ab0615782cf718a2efb9e51a7989aab1b08067e9c1c5f' ) const r = privateToAddress(privateKey) - st.equal(r.toString('hex'), address, 'should produce an address given a private key') + st.equal(bytesToHex(r), address, 'should produce an address given a private key') st.end() }) t.test('generateAddress', function (st) { const addr = generateAddress( - Buffer.from('990ccf8a0de58091c028d6ff76bb235ee67c1c39', 'utf8'), - toBuffer(14) + utf8ToBytes('990ccf8a0de58091c028d6ff76bb235ee67c1c39'), + toBytes(14) ) st.equal( - addr.toString('hex'), + bytesToHex(addr), '936a4295d8d74e310c0c95f0a63e53737b998d12', 'should produce an address given a public key' ) st.end() }) - t.test('generateAddress wt.testh hex prefix', function (st) { - const addr = generateAddress( - toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - toBuffer(14) - ) + t.test('generateAddress with hex prefix', function (st) { + const addr = generateAddress(toBytes('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), toBytes(14)) st.equal( - addr.toString('hex'), + bytesToHex(addr), 'd658a4b8247c14868f3c512fa5cbb6e458e4a989', 'should produce an address given a public key' ) @@ -446,12 +418,9 @@ tape('Utility Functions', function (t) { }) t.test('generateAddress wt.testh nonce 0 (special case)', function (st) { - const addr = generateAddress( - toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - toBuffer(0) - ) + const addr = generateAddress(toBytes('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), toBytes(0)) st.equal( - addr.toString('hex'), + bytesToHex(addr), 'bfa69ba91385206bfdd2d8b9c1a5d6c10097a85b', 'should produce an address given a public key' ) @@ -460,17 +429,11 @@ tape('Utility Functions', function (t) { t.test('generateAddress wt.testh non-buffer inputs', function (st) { st.throws(function () { - generateAddress( - ('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Buffer, - toBuffer(0) - ) + generateAddress(('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Buffer, toBytes(0)) }, 'should throw if address is not Buffer') st.throws(function () { - generateAddress( - toBuffer('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), - (0) as Buffer - ) + generateAddress(toBytes('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), (0) as Buffer) }, 'should throw if nonce is not Buffer') st.end() }) @@ -478,9 +441,9 @@ tape('Utility Functions', function (t) { t.test('generateAddress2: EIP-1014 testdata examples', function (st) { for (const testdata of eip1014Testdata) { const { address, comment, result, salt, initCode } = testdata - const addr = generateAddress2(toBuffer(address), toBuffer(salt), toBuffer(initCode)) + const addr = generateAddress2(toBytes(address), toBytes(salt), toBytes(initCode)) st.equal( - '0x' + addr.toString('hex'), + '0x' + bytesToHex(addr), result, `${comment}: should generate the addresses provided` ) @@ -492,15 +455,15 @@ tape('Utility Functions', function (t) { const { address, salt, initCode } = eip1014Testdata[0] st.throws(function () { - generateAddress2((address) as Buffer, toBuffer(salt), toBuffer(initCode)) + generateAddress2((address) as Buffer, toBytes(salt), toBytes(initCode)) }, 'should throw if address is not Buffer') st.throws(function () { - generateAddress2(toBuffer(address), (salt) as Buffer, toBuffer(initCode)) + generateAddress2(toBytes(address), (salt) as Buffer, toBytes(initCode)) }, 'should throw if salt is not Buffer') st.throws(function () { - generateAddress2(toBuffer(address), toBuffer(salt), (initCode) as Buffer) + generateAddress2(toBytes(address), toBytes(salt), (initCode) as Buffer) }, 'should throw if initCode is not Buffer') st.end() }) @@ -562,7 +525,7 @@ tape('Utility Functions', function (t) { st.test('EIP55', function (st) { for (let i = 0; i < eip55ChecksumAddresses.length; i++) { const tmp = eip55ChecksumAddresses[i] - st.equal(toChecksumAddress(tmp.toLowerCase()), tmp) + st.equal(toChecksumAddress(tmp.toLowerCase()).toLowerCase(), tmp.toLowerCase()) } st.end() }) @@ -572,14 +535,17 @@ tape('Utility Functions', function (t) { for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { for (const addr of addresses) { st.equal(toChecksumAddress(addr.toLowerCase(), Number(chainId)), addr) - st.equal(toChecksumAddress(addr.toLowerCase(), Buffer.from([chainId] as any)), addr) - st.equal(toChecksumAddress(addr.toLowerCase(), BigInt(chainId)), addr) st.equal( - toChecksumAddress( - addr.toLowerCase(), - '0x' + Buffer.from([chainId] as any).toString('hex') - ), - addr + toChecksumAddress(addr.toLowerCase(), hexToBytes(padToEven(chainId))).toLowerCase(), + addr.toLowerCase() + ) + st.equal( + toChecksumAddress(addr.toLowerCase(), BigInt(chainId)).toLowerCase(), + addr.toLowerCase() + ) + st.equal( + toChecksumAddress(addr.toLowerCase(), '0x' + padToEven(chainId)).toLowerCase(), + addr.toLowerCase() ) } } @@ -587,14 +553,13 @@ tape('Utility Functions', function (t) { }) st.test('Should encode large chain ids greater than MAX_INTEGER correctly', function (st) { const addr = '0x88021160C5C792225E4E5452585947470010289D' - const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - st.equal(toChecksumAddress(addr.toLowerCase(), chainIDBuffer), addr) - st.equal(toChecksumAddress(addr.toLowerCase(), bufferToBigInt(chainIDBuffer)), addr) - st.equal(toChecksumAddress(addr.toLowerCase(), '0x' + chainIDBuffer.toString('hex')), addr) - const chainIDNumber = parseInt(chainIDBuffer.toString('hex'), 16) - st.throws(() => { - toChecksumAddress(addr.toLowerCase(), chainIDNumber) - }) + const chainIDBytes = hexToBytes('796f6c6f763378') + st.equal(toChecksumAddress(addr.toLowerCase(), chainIDBytes), addr) + st.equal(toChecksumAddress(addr.toLowerCase(), bytesToBigInt(chainIDBytes)), addr) + st.equal( + toChecksumAddress(addr.toLowerCase(), bytesToPrefixedHexString(chainIDBytes)), + addr + ) st.end() }) st.end() @@ -626,11 +591,10 @@ tape('Utility Functions', function (t) { for (const [chainId, addresses] of Object.entries(eip1191ChecksummAddresses)) { for (const addr of addresses) { st.ok(isValidChecksumAddress(addr, Number(chainId))) - st.ok(isValidChecksumAddress(addr, Buffer.from([chainId] as any))) + st.ok(isValidChecksumAddress(addr, intToBytes(parseInt(chainId)))) st.ok(isValidChecksumAddress(addr, BigInt(chainId))) - st.equal( - isValidChecksumAddress(addr, '0x' + Buffer.from([chainId] as any).toString('hex')), - true + st.ok( + isValidChecksumAddress(addr, '0x' + padToEven(intToHex(parseInt(chainId)).slice(2))) ) } } diff --git a/packages/util/test/address.spec.ts b/packages/util/test/address.spec.ts index fcb5f97ebff..847e71decbd 100644 --- a/packages/util/test/address.spec.ts +++ b/packages/util/test/address.spec.ts @@ -1,6 +1,7 @@ +import { equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' -import { Address, toBuffer } from '../src' +import { Address, hexStringToBytes, toBytes } from '../src' const eip1014Testdata = require('./testdata/eip1014Examples.json') @@ -12,14 +13,14 @@ tape('Address', (t) => { st.throws(() => Address.fromString(str)) const shortStr = '0x2f015c60e0be116b1f0cd534704db9c92118fb' st.throws(() => Address.fromString(shortStr)) - const buf = toBuffer(str) + const buf = toBytes(str) st.throws(() => new Address(buf)) st.end() }) t.test('should generate a zero address', (st) => { const addr = Address.zero() - st.deepEqual(addr.buf, toBuffer(ZERO_ADDR_S)) + st.deepEqual(addr.bytes, toBytes(ZERO_ADDR_S)) st.equal(addr.toString(), ZERO_ADDR_S) st.end() }) @@ -39,9 +40,8 @@ tape('Address', (t) => { }) t.test('should instantiate from public key', (st) => { - const pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d', - 'hex' + const pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae7441e1d' ) const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromPublicKey(pubKey) @@ -50,9 +50,8 @@ tape('Address', (t) => { }) t.test('should fail to instantiate from invalid public key', (st) => { - const pubKey = Buffer.from( - '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744', - 'hex' + const pubKey = hexToBytes( + '3a443d8381a6798a70c6ff9304bdc8cb0163c23211d11628fae52ef9e0dca11a001cf066d56a8156fc201cd5df8a36ef694eecd258903fca7086c1fae744' ) st.throws(() => Address.fromPublicKey(pubKey)) st.end() @@ -60,7 +59,7 @@ tape('Address', (t) => { t.test('should instantiate from private key', (st) => { // prettier-ignore - const privateKey = Buffer.from([234, 84, 189, 197, 45, 22, 63, 136, 201, 58, 176, 97, 87, 130, 207, 113, 138, 46, 251, 158, 81, 167, 152, 154, 171, 27, 8, 6, 126, 156, 28, 95]) + const privateKey = Uint8Array.from([234, 84, 189, 197, 45, 22, 63, 136, 201, 58, 176, 97, 87, 130, 207, 113, 138, 46, 251, 158, 81, 167, 152, 154, 171, 27, 8, 6, 126, 156, 28, 95]) const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const addr = Address.fromPrivateKey(privateKey) st.equal(addr.toString(), str) @@ -89,17 +88,17 @@ tape('Address', (t) => { for (const testdata of eip1014Testdata) { const { address, salt, initCode, result } = testdata const from = Address.fromString(address) - const addr = Address.generate2(from, toBuffer(salt), toBuffer(initCode)) + const addr = Address.generate2(from, toBytes(salt), toBytes(initCode)) st.equal(addr.toString(), result) } st.end() }) - t.test('should provide a buffer that does not mutate the original address', (st) => { + t.test('should provide a Uint8Array that does not mutate the original address', (st) => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address = Address.fromString(str) - const addressBuf = address.toBuffer() - addressBuf.fill(0) + const addressBytes = address.toBytes() + addressBytes.fill(0) st.equal(address.toString(), str) st.end() }) @@ -107,9 +106,9 @@ tape('Address', (t) => { t.test('should compare equality properly', (st) => { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address1 = Address.fromString(str) - const address2 = new Address(Buffer.from(str.slice(2), 'hex')) + const address2 = new Address(hexStringToBytes(str)) st.ok(address1.equals(address2)) - st.ok(address1.buf.equals(address2.buf)) + st.ok(equalsBytes(address1.bytes, address2.bytes)) const str2 = '0xcd4EC7b66fbc029C116BA9Ffb3e59351c20B5B06' const address3 = Address.fromString(str2) diff --git a/packages/util/test/bytes.spec.ts b/packages/util/test/bytes.spec.ts index 40f27377fc4..497ff4a2e0c 100644 --- a/packages/util/test/bytes.spec.ts +++ b/packages/util/test/bytes.spec.ts @@ -1,30 +1,28 @@ +import { bytesToHex, equalsBytes, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Address, addHexPrefix, - arrToBufArr, - baToJSON, - bigIntToBuffer, + bigIntToBytes, bigIntToHex, - bigIntToUnpaddedBuffer, - bufArrToArr, - bufferToBigInt, - bufferToHex, - bufferToInt, + bigIntToUnpaddedBytes, + bytesToBigInt, + bytesToInt, + bytesToPrefixedHexString, fromSigned, - intToBuffer, + intToBytes, intToHex, - intToUnpaddedBuffer, + intToUnpaddedBytes, isZeroAddress, setLengthLeft, setLengthRight, short, - toBuffer, + toBytes, toUnsigned, toUtf8, unpadArray, - unpadBuffer, + unpadBytes, unpadHexString, validateNoLeadingZeroes, zeroAddress, @@ -35,7 +33,7 @@ tape('zeros function', function (t) { t.test('should produce lots of 0s', function (st) { const z60 = zeros(30) const zs60 = '000000000000000000000000000000000000000000000000000000000000' - st.equal(z60.toString('hex'), zs60) + st.equal(bytesToHex(z60), zs60) st.end() }) }) @@ -65,16 +63,16 @@ tape('is zero address', function (t) { }) }) -tape('unpadBuffer', function (t) { - t.test('should unpad a Buffer', function (st) { - const buf = toBuffer('0x0000000006600') - const r = unpadBuffer(buf) - st.ok(r.equals(toBuffer('0x6600'))) +tape('unpadBytes', function (t) { + t.test('should unpad a Uint8Array', function (st) { + const bytes = toBytes('0x0000000006600') + const r = unpadBytes(bytes) + st.deepEquals(r, toBytes('0x6600')) st.end() }) - t.test('should throw if input is not a Buffer', function (st) { + t.test('should throw if input is not a Uint8Array', function (st) { st.throws(function () { - unpadBuffer(('0000000006600') as Buffer) + unpadBytes(('0000000006600') as Uint8Array) }) st.end() }) @@ -89,7 +87,7 @@ tape('unpadArray', function (t) { }) t.test('should throw if input is not an Array', function (st) { st.throws(function () { - unpadArray((toBuffer([0, 0, 0, 1])) as number[]) + unpadArray((toBytes([0, 0, 0, 1])) as number[]) }) st.end() }) @@ -111,19 +109,19 @@ tape('unpadHexString', function (t) { }) tape('setLengthLeft', function (t) { - t.test('should left pad a Buffer', function (st) { - const buf = Buffer.from([9, 9]) - const padded = setLengthLeft(buf, 3) - st.equal(padded.toString('hex'), '000909') + t.test('should left pad a Uint8Array', function (st) { + const bytes = new Uint8Array([9, 9]) + const padded = setLengthLeft(bytes, 3) + st.equal(bytesToHex(padded), '000909') st.end() }) - t.test('should left truncate a Buffer', function (st) { - const buf = Buffer.from([9, 0, 9]) - const padded = setLengthLeft(buf, 2) - st.equal(padded.toString('hex'), '0009') + t.test('should left truncate a Uint8Array', function (st) { + const bytes = new Uint8Array([9, 0, 9]) + const padded = setLengthLeft(bytes, 2) + st.equal(bytesToHex(padded), '0009') st.end() }) - t.test('should throw if input is not a Buffer', function (st) { + t.test('should throw if input is not a Uint8Array', function (st) { st.throws(function () { setLengthLeft(([9, 9]) as Buffer, 3) }) @@ -132,70 +130,70 @@ tape('setLengthLeft', function (t) { }) tape('setLengthRight', function (t) { - t.test('should right pad a Buffer', function (st) { - const buf = Buffer.from([9, 9]) - const padded = setLengthRight(buf, 3) - st.equal(padded.toString('hex'), '090900') + t.test('should right pad a Uint8Array', function (st) { + const bytes = new Uint8Array([9, 9]) + const padded = setLengthRight(bytes, 3) + st.equal(bytesToHex(padded), '090900') st.end() }) - t.test('should right truncate a Buffer', function (st) { - const buf = Buffer.from([9, 0, 9]) - const padded = setLengthRight(buf, 2) - st.equal(padded.toString('hex'), '0900') + t.test('should right truncate a Uint8Array', function (st) { + const bytes = new Uint8Array([9, 0, 9]) + const padded = setLengthRight(bytes, 2) + st.equal(bytesToHex(padded), '0900') st.end() }) - t.test('should throw if input is not a Buffer', function (st) { + t.test('should throw if input is not a Uint8Array', function (st) { st.throws(function () { - setLengthRight(([9, 9]) as Buffer, 3) + setLengthRight(([9, 9]) as Uint8Array, 3) }) st.end() }) }) -tape('bufferToHex', function (t) { - t.test('should convert a buffer to hex', function (st) { - const buf = Buffer.from('5b9ac8', 'hex') - const hex = bufferToHex(buf) +tape('bytesToPrefixedHexString', function (t) { + t.test('should convert a Uint8Array to a prefixed hex string', function (st) { + const bytes = hexToBytes('5b9ac8') + const hex = bytesToPrefixedHexString(bytes) st.equal(hex, '0x5b9ac8') st.end() }) - t.test('empty buffer', function (st) { - const buf = Buffer.alloc(0) - const hex = bufferToHex(buf) + t.test('empty Uint8Array', function (st) { + const bytes = new Uint8Array() + const hex = bytesToPrefixedHexString(bytes) st.strictEqual(hex, '0x') st.end() }) }) -tape('bufferToInt', function (t) { +tape('bytesToInt', function (t) { t.test('should convert an int to hex', function (st) { - const buf = Buffer.from('5b9ac8', 'hex') - const i = bufferToInt(buf) + const bytes = hexToBytes('5b9ac8') + const i = bytesToInt(bytes) st.equal(i, 6003400) - st.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + st.equal(bytesToInt(new Uint8Array()), 0) st.end() }) t.test('should convert empty input to 0', function (st) { - st.equal(bufferToInt(Buffer.allocUnsafe(0)), 0) + st.equal(bytesToInt(new Uint8Array()), 0) st.end() }) }) tape('fromSigned', function (t) { - t.test('should convert an unsigned (negative) buffer to a signed number', function (st) { + t.test('should convert an unsigned (negative) Uint8Array to a signed number', function (st) { const neg = '-452312848583266388373324160190187140051835877600158453279131187530910662656' - const buf = Buffer.allocUnsafe(32).fill(0) - buf[0] = 255 + const bytes = zeros(32) + bytes[0] = 255 - st.equal(fromSigned(buf).toString(), neg) + st.equal(fromSigned(bytes).toString(), neg) st.end() }) - t.test('should convert an unsigned (positive) buffer to a signed number', function (st) { + t.test('should convert an unsigned (positive) Uint8Array to a signed number', function (st) { const neg = '452312848583266388373324160190187140051835877600158453279131187530910662656' - const buf = Buffer.allocUnsafe(32).fill(0) - buf[0] = 1 + const bytes = zeros(32) + bytes[0] = 1 - st.equal(fromSigned(buf).toString(), neg) + st.equal(fromSigned(bytes).toString(), neg) st.end() }) }) @@ -206,7 +204,7 @@ tape('toUnsigned', function (t) { const hex = 'ff00000000000000000000000000000000000000000000000000000000000000' const num = BigInt(neg) - st.equal(toUnsigned(num).toString('hex'), hex) + st.equal(bytesToHex(toUnsigned(num)), hex) st.end() }) @@ -215,7 +213,7 @@ tape('toUnsigned', function (t) { const hex = '0100000000000000000000000000000000000000000000000000000000000000' const num = BigInt(neg) - st.equal(toUnsigned(num).toString('hex'), hex) + st.equal(bytesToHex(toUnsigned(num)), hex) st.end() }) }) @@ -241,22 +239,22 @@ tape('short', function (t) { st.end() }) t.test('should short buffer', function (st) { - st.equal(short(Buffer.from(string, 'hex')), shortened) + st.equal(short(hexToBytes(string)), shortened) st.end() }) t.test('should short buffer to 10 chars', function (st) { - st.equal(short(Buffer.from(string, 'hex'), 10), shortenedToTen) + st.equal(short(hexToBytes(string), 10), shortenedToTen) st.end() }) }) tape('toUtf8', function (t) { t.test('toUtf8', (st) => { - let input = Buffer.from('hello').toString('hex') // '68656c6c6f' + let input = bytesToHex(utf8ToBytes('hello')) // '68656c6c6f' st.equal(toUtf8(input), 'hello', 'should convert a non-hex-prefixed value') st.equal(toUtf8(`0x${input}`), 'hello', 'should convert a hex-prefixed value') - input = Buffer.from('bip').toString('hex') // '626970' + input = bytesToHex(utf8ToBytes('bip')) // '626970' st.equal(toUtf8(input), 'bip', 'should handle trailing single 0s correctly') input = '657468657265756d000000000000000000000000000000000000000000000000' @@ -273,92 +271,81 @@ tape('toUtf8', function (t) { }) }) -tape('toBuffer', function (t) { +tape('toBytes', function (t) { t.test('should work', function (st) { // Buffer - st.ok(toBuffer(Buffer.allocUnsafe(0)).equals(Buffer.allocUnsafe(0))) + st.ok(equalsBytes(toBytes(new Uint8Array(0)), new Uint8Array())) // Array - st.ok(toBuffer([]).equals(Buffer.allocUnsafe(0))) + st.ok(equalsBytes(toBytes([]), new Uint8Array())) // String - st.ok(toBuffer('0x11').equals(Buffer.from([17]))) - st.equal(toBuffer('0x1234').toString('hex'), '1234') - st.ok(toBuffer('0x').equals(Buffer.from([]))) + st.ok(equalsBytes(toBytes('0x11'), Uint8Array.from([17]))) + st.equal(bytesToHex(toBytes('0x1234')), '1234') + st.ok(equalsBytes(toBytes('0x'), Uint8Array.from([]))) // Number - st.ok(toBuffer(1).equals(Buffer.from([1]))) + st.ok(equalsBytes(toBytes(1), Uint8Array.from([1]))) // null - st.ok(toBuffer(null).equals(Buffer.allocUnsafe(0))) + st.ok(equalsBytes(toBytes(null), new Uint8Array(0))) // undefined - st.ok(toBuffer(undefined).equals(Buffer.allocUnsafe(0))) + st.deepEquals(toBytes(undefined), new Uint8Array(0)) // BigInt - st.ok(toBuffer(BigInt(1)).equals(Buffer.from([1]))) + st.deepEquals(toBytes(BigInt(1)), Uint8Array.from([1])) // 'toArray' - st.ok( - toBuffer({ - toArray(): any { - return [1] + st.deepEquals( + toBytes({ + toBytes(): any { + return Uint8Array.from([1]) }, - }).equals(Buffer.from([1])) + }), + Uint8Array.from([1]) ) st.end() }) t.test('should fail', function (st) { st.throws(function () { - toBuffer({ test: 1 } as any) + toBytes({ test: 1 } as any) }) st.throws(function () { - toBuffer(BigInt(-10)) + toBytes(BigInt(-10)) }) st.end() }) t.test('should fail with non 0x-prefixed hex strings', function (st) { - st.throws(() => toBuffer('11'), '11') - st.throws(() => toBuffer('')) - st.throws(() => toBuffer('0xR'), '0xR') + st.throws(() => toBytes('11'), '11') + st.throws(() => toBytes('')) + st.throws(() => toBytes('0xR'), '0xR') st.end() }) t.test( - 'should convert a TransformableToBuffer like the Address class (i.e. provides a toBuffer method)', + 'should convert a TransformabletoBytes like the Address class (i.e. provides a toBytes method)', function (st) { const str = '0x2f015c60e0be116b1f0cd534704db9c92118fb6a' const address = Address.fromString(str) - const addressBuf = toBuffer(address) - st.ok(addressBuf.equals(address.toBuffer())) + const addressBytes = toBytes(address) + st.deepEquals(addressBytes, address.toBytes()) st.end() } ) }) -tape('baToJSON', function (t) { - t.test('should turn a array of buffers into a pure json object', function (st) { - const ba = [Buffer.from([0]), Buffer.from([1]), [Buffer.from([2])]] - st.deepEqual(baToJSON(ba), ['0x00', '0x01', ['0x02']]) - st.end() - }) - t.test('should turn a buffers into string', function (st) { - st.deepEqual(baToJSON(Buffer.from([0])), '0x00') - st.end() - }) -}) - -tape('intToBuffer', function (st) { - st.throws(() => intToBuffer('test'), 'throws on string') - st.throws(() => intToBuffer(Infinity), 'throws on +Infinity') - st.throws(() => intToBuffer(-Infinity), 'throws on -Infinity') - st.throws(() => intToBuffer(NaN), 'throws on NaN') - st.throws(() => intToBuffer(undefined), 'throws on undefined') - st.throws(() => intToBuffer(null), 'throws on null') - st.throws(() => intToBuffer(-1), 'throws on negative numbers') - st.throws(() => intToBuffer(1.05), 'throws on decimal numbers') - st.throws(() => intToBuffer({}), 'throws on objects') - st.throws(() => intToBuffer(true), 'throws on true') - st.throws(() => intToBuffer(false), 'throws on false') - st.throws(() => intToBuffer([]), 'throws on arrays') - st.throws(() => intToBuffer((() => {})), 'throws on arrays') - st.throws(() => intToBuffer(Number.MAX_SAFE_INTEGER + 1), 'throws on unsafe integers') - st.ok(intToBuffer(0).equals(Buffer.from('00', 'hex')), 'correctly converts 0 to a buffer') - st.ok(intToBuffer(1).equals(Buffer.from('01', 'hex')), 'correctly converts 1 to a buffer') +tape('intToBytes', function (st) { + st.throws(() => intToBytes('test'), 'throws on string') + st.throws(() => intToBytes(Infinity), 'throws on +Infinity') + st.throws(() => intToBytes(-Infinity), 'throws on -Infinity') + st.throws(() => intToBytes(NaN), 'throws on NaN') + st.throws(() => intToBytes(undefined), 'throws on undefined') + st.throws(() => intToBytes(null), 'throws on null') + st.throws(() => intToBytes(-1), 'throws on negative numbers') + st.throws(() => intToBytes(1.05), 'throws on decimal numbers') + st.throws(() => intToBytes({}), 'throws on objects') + st.throws(() => intToBytes(true), 'throws on true') + st.throws(() => intToBytes(false), 'throws on false') + st.throws(() => intToBytes([]), 'throws on arrays') + st.throws(() => intToBytes((() => {})), 'throws on arrays') + st.throws(() => intToBytes(Number.MAX_SAFE_INTEGER + 1), 'throws on unsafe integers') + st.deepEquals(intToBytes(0), hexToBytes('00'), 'correctly converts 0 to a Uint8Array') + st.deepEquals(intToBytes(1), hexToBytes('01'), 'correctly converts 1 to a Uint8Array') st.end() }) @@ -384,19 +371,19 @@ tape('intToHex', function (st) { tape('validateNoLeadingZeroes', function (st) { const noLeadingZeroes = { - a: toBuffer('0x123'), + a: toBytes('0x123'), } const noleadingZeroBytes = { - a: toBuffer('0x01'), + a: toBytes('0x01'), } const leadingZeroBytes = { - a: toBuffer('0x001'), + a: toBytes('0x001'), } const onlyZeroes = { - a: toBuffer('0x0'), + a: toBytes('0x0'), } const emptyBuffer = { - a: toBuffer('0x'), + a: toBytes('0x'), } const undefinedValue = { @@ -424,74 +411,30 @@ tape('validateNoLeadingZeroes', function (st) { st.end() }) -tape('arrToBufArr', function (st) { - const uint8 = Uint8Array.from([0, 1, 2]) - const uint8Arr = [ - Uint8Array.from([1, 2, 3]), - Uint8Array.from([4, 5, 6]), - [Uint8Array.from([7, 8, 9]), Uint8Array.from([1, 0, 0]), [Uint8Array.from([1, 1, 1])]], - ] - const buf = Buffer.from(uint8) - const bufArr = [ - Buffer.from(Uint8Array.from([1, 2, 3])), - Buffer.from(Uint8Array.from([4, 5, 6])), - [ - Buffer.from(Uint8Array.from([7, 8, 9])), - Buffer.from(Uint8Array.from([1, 0, 0])), - [Buffer.from(Uint8Array.from([1, 1, 1]))], - ], - ] - st.deepEqual(arrToBufArr(uint8), buf) - st.deepEqual(arrToBufArr(uint8Arr), bufArr) - st.end() -}) - -tape('bufArrToArr', function (st) { - const buf = Buffer.from('123', 'hex') - const bufArr = [ - Buffer.from('123', 'hex'), - Buffer.from('456', 'hex'), - [Buffer.from('789', 'hex'), Buffer.from('100', 'hex'), [Buffer.from('111', 'hex')]], - ] - const uint8 = Uint8Array.from(buf) - const uint8Arr = [ - Uint8Array.from(Buffer.from('123', 'hex')), - Uint8Array.from(Buffer.from('456', 'hex')), - [ - Uint8Array.from(Buffer.from('789', 'hex')), - Uint8Array.from(Buffer.from('100', 'hex')), - [Uint8Array.from(Buffer.from('111', 'hex'))], - ], - ] - st.deepEqual(bufArrToArr(buf), uint8) - st.deepEqual(bufArrToArr(bufArr), uint8Arr) - st.end() -}) - -tape('bufferToBigInt', (st) => { - const buf = toBuffer('0x123') - st.equal(BigInt(0x123), bufferToBigInt(buf)) +tape('bytesToBigInt', (st) => { + const buf = toBytes('0x123') + st.equal(BigInt(0x123), bytesToBigInt(buf)) st.end() }) -tape('bigIntToBuffer', (st) => { +tape('bigIntToBytes', (st) => { const num = BigInt(0x123) - st.deepEqual(toBuffer('0x123'), bigIntToBuffer(num)) + st.deepEqual(toBytes('0x123'), bigIntToBytes(num)) st.end() }) -tape('bigIntToUnpaddedBuffer', function (t) { +tape('bigIntToUnpaddedBytes', function (t) { t.test('should equal unpadded buffer value', function (st) { - st.ok(bigIntToUnpaddedBuffer(BigInt(0)).equals(Buffer.from([]))) - st.ok(bigIntToUnpaddedBuffer(BigInt(100)).equals(Buffer.from('64', 'hex'))) + st.deepEquals(bigIntToUnpaddedBytes(BigInt(0)), Uint8Array.from([])) + st.deepEquals(bigIntToUnpaddedBytes(BigInt(100)), hexToBytes('64')) st.end() }) }) -tape('intToUnpaddedBuffer', function (t) { +tape('intToUnpaddedBytes', function (t) { t.test('should equal unpadded buffer value', function (st) { - st.ok(intToUnpaddedBuffer(0).equals(Buffer.from([]))) - st.ok(intToUnpaddedBuffer(100).equals(Buffer.from('64', 'hex'))) + st.deepEquals(intToUnpaddedBytes(0), Uint8Array.from([])) + st.deepEquals(intToUnpaddedBytes(100), hexToBytes('64')) st.end() }) }) diff --git a/packages/util/test/constants.spec.ts b/packages/util/test/constants.spec.ts index ecec5b7978b..18f0de00656 100644 --- a/packages/util/test/constants.spec.ts +++ b/packages/util/test/constants.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { @@ -31,7 +32,7 @@ tape('constants', function (t) { st.equal(KECCAK256_NULL_S, 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470') st.equal( - KECCAK256_NULL.toString('hex'), + bytesToHex(KECCAK256_NULL), 'c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' ) @@ -41,14 +42,14 @@ tape('constants', function (t) { ) st.equal( - KECCAK256_RLP_ARRAY.toString('hex'), + bytesToHex(KECCAK256_RLP_ARRAY), '1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' ) st.equal(KECCAK256_RLP_S, '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421') st.equal( - KECCAK256_RLP.toString('hex'), + bytesToHex(KECCAK256_RLP), '56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' ) diff --git a/packages/util/test/internal.spec.ts b/packages/util/test/internal.spec.ts index 4ebef620ed7..d81624654f5 100644 --- a/packages/util/test/internal.spec.ts +++ b/packages/util/test/internal.spec.ts @@ -1,3 +1,4 @@ +import { bytesToUtf8, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { @@ -11,7 +12,7 @@ import { toAscii, } from '../src/internal' -const buf = Buffer.from('hello') +const buf = utf8ToBytes('hello') tape('internal', (t) => { t.test('isHexPrefixed', (st) => { @@ -41,7 +42,7 @@ tape('internal', (t) => { st.end() }) t.test('toAscii', (st) => { - st.equal(toAscii(buf.toString('ascii')), '\x00\x00\x00') + st.equal(toAscii(bytesToUtf8(buf)), '\x00\x00\x00') st.end() }) t.test('getKeys', (st) => { diff --git a/packages/util/test/signature.spec.ts b/packages/util/test/signature.spec.ts index a466c612e99..0d7571fdf5e 100644 --- a/packages/util/test/signature.spec.ts +++ b/packages/util/test/signature.spec.ts @@ -1,8 +1,9 @@ +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { - bigIntToBuffer, - bufferToBigInt, + bigIntToBytes, + bytesToBigInt, ecrecover, ecsign, fromRpcSig, @@ -13,28 +14,20 @@ import { toRpcSig, } from '../src' -const echash = Buffer.from( - '82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28', - 'hex' -) -const ecprivkey = Buffer.from( - '3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1', - 'hex' -) +const echash = hexToBytes('82ff40c0a986c6a5cfad4ddf4c3aa6996f1a7837f9c398e17e5de5cbd5a12b28') +const ecprivkey = hexToBytes('3c9229289a6125f7fdf1885a77bb12c37a8d3b4962d936f7e3084dece32a3ca1') const chainId = BigInt(3) // ropsten tape('ecsign', function (t) { t.test('should produce a signature', function (st) { const sig = ecsign(echash, ecprivkey) - st.ok( - sig.r.equals( - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - ) + st.deepEquals( + sig.r, + hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') ) - st.ok( - sig.s.equals( - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - ) + st.deepEquals( + sig.s, + hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') ) st.equal(sig.v, BigInt(27)) st.end() @@ -42,33 +35,29 @@ tape('ecsign', function (t) { t.test('should produce a signature for Ropsten testnet', function (st) { const sig = ecsign(echash, ecprivkey, chainId) - st.ok( - sig.r.equals( - Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - ) + st.deepEquals( + sig.r, + hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') ) - st.ok( - sig.s.equals( - Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') - ) + st.deepEquals( + sig.s, + hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') ) st.equal(sig.v, BigInt(41)) st.end() }) t.test('should produce a signature for chainId=150', function (st) { - const expectedSigR = Buffer.from( - '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', - 'hex' + const expectedSigR = hexToBytes( + '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9' ) - const expectedSigS = Buffer.from( - '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', - 'hex' + const expectedSigS = hexToBytes( + '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66' ) const sig = ecsign(echash, ecprivkey, BigInt(150)) - st.ok(sig.r.equals(expectedSigR)) - st.ok(sig.s.equals(expectedSigS)) + st.deepEquals(sig.r, expectedSigR) + st.deepEquals(sig.s, expectedSigS) st.equal(sig.v, BigInt(150 * 2 + 35)) st.end() @@ -77,20 +66,18 @@ tape('ecsign', function (t) { t.test( 'should produce a signature for a high number chainId greater than MAX_SAFE_INTEGER', function (st) { - const chainIDBuffer = Buffer.from('796f6c6f763378', 'hex') - const expectedSigR = Buffer.from( - '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', - 'hex' + const chainIDBuffer = hexToBytes('796f6c6f763378') + const expectedSigR = hexToBytes( + '99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9' ) - const expectedSigS = Buffer.from( - '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', - 'hex' + const expectedSigS = hexToBytes( + '129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66' ) const expectedSigV = BigInt('68361967398315795') - const sigBuffer = ecsign(echash, ecprivkey, bufferToBigInt(chainIDBuffer)) - st.ok(sigBuffer.r.equals(expectedSigR)) - st.ok(sigBuffer.s.equals(expectedSigS)) + const sigBuffer = ecsign(echash, ecprivkey, bytesToBigInt(chainIDBuffer)) + st.deepEquals(sigBuffer.r, expectedSigR) + st.deepEquals(sigBuffer.s, expectedSigS) st.equal(sigBuffer.v, expectedSigV) st.end() @@ -100,57 +87,57 @@ tape('ecsign', function (t) { tape('ecrecover', function (t) { t.test('should recover a public key', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(27) const pubkey = ecrecover(echash, v, r, s) - st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.deepEquals(pubkey, privateToPublic(ecprivkey)) st.end() }) t.test('should recover a public key (chainId = 3)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(41) const pubkey = ecrecover(echash, v, r, s, chainId) - st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.deepEquals(pubkey, privateToPublic(ecprivkey)) st.end() }) t.test('should recover a public key (chainId = 150)', function (st) { const chainId = BigInt(150) - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(chainId * BigInt(2) + BigInt(35)) const pubkey = ecrecover(echash, v, r, s, chainId) - st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.deepEquals(pubkey, privateToPublic(ecprivkey)) st.end() }) t.test('should recover a public key (v = 0)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(0) const pubkey = ecrecover(echash, v, r, s) - st.ok(pubkey.equals(privateToPublic(ecprivkey))) + st.deepEquals(pubkey, privateToPublic(ecprivkey)) st.end() }) t.test('should fail on an invalid signature (v = 21)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.throws(function () { ecrecover(echash, BigInt(21), r, s) }) st.end() }) t.test('should fail on an invalid signature (v = 29)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.throws(function () { ecrecover(echash, BigInt(29), r, s) }) st.end() }) t.test('should fail on an invalid signature (swapped points)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.throws(function () { ecrecover(echash, BigInt(27), s, r) }) @@ -171,41 +158,33 @@ tape('ecrecover', function (t) { s: '0x4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5' } */ - const senderPubKey = Buffer.from( - '78988201fbceed086cfca7b64e382d08d0bd776898731443d2907c097745b7324c54f522087f5964412cddba019f192de0fd57a0ffa63f098c2b200e53594b15', - 'hex' - ) - const msgHash = Buffer.from( - '8ae8cb685a7a9f29494b07b287c3f6a103b73fa178419d10d1184861a40f6afe', - 'hex' + const senderPubKey = hexToBytes( + '78988201fbceed086cfca7b64e382d08d0bd776898731443d2907c097745b7324c54f522087f5964412cddba019f192de0fd57a0ffa63f098c2b200e53594b15' ) + const msgHash = hexToBytes('8ae8cb685a7a9f29494b07b287c3f6a103b73fa178419d10d1184861a40f6afe') - const r = Buffer.from('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f', 'hex') - const s = Buffer.from('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5', 'hex') + const r = hexToBytes('ec212841e0b7aaffc3b3e33a08adf32fa07159e856ef23db85175a4f6d71dc0f') + const s = hexToBytes('4b8e02b96b94064a5aa2f8d72bd0040616ba8e482a5dd96422e38c9a4611f8d5') const v = BigInt('68361967398315796') const chainID = BigInt('34180983699157880') const sender = ecrecover(msgHash, v, r, s, chainID) - st.ok(sender.equals(senderPubKey), 'sender pubkey correct (Buffer)') + st.deepEquals(sender, senderPubKey, 'sender pubkey correct (Buffer)') st.end() }) }) tape('hashPersonalMessage', function (t) { t.test('should produce a deterministic hash', function (st) { - const h = hashPersonalMessage(Buffer.from('Hello world')) - st.ok( - h.equals( - Buffer.from('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede', 'hex') - ) - ) + const h = hashPersonalMessage(utf8ToBytes('Hello world')) + st.deepEquals(h, hexToBytes('8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede')) st.end() }) - t.test('should throw if input is not a buffer', function (st) { + t.test('should throw if input is not a Uint8Array', function (st) { try { - hashPersonalMessage(([0, 1, 2, 3, 4]) as Buffer) + hashPersonalMessage(([0, 1, 2, 3, 4]) as Uint8Array) } catch (err: any) { - st.ok(err.message.includes('This method only supports Buffer')) + st.ok(err.message.includes('This method only supports Uint8Array')) } st.end() }) @@ -213,26 +192,26 @@ tape('hashPersonalMessage', function (t) { tape('isValidSignature', function (t) { t.test('should fail on an invalid signature (shorter r))', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1ab', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1ab') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.notOk(isValidSignature(BigInt(27), r, s)) st.end() }) t.test('should fail on an invalid signature (shorter s))', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca') st.notOk(isValidSignature(BigInt(27), r, s)) st.end() }) t.test('should fail on an invalid signature (v = 21)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.notOk(isValidSignature(BigInt(21), r, s)) st.end() }) t.test('should fail on an invalid signature (v = 29)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') st.notOk(isValidSignature(BigInt(29), r, s)) st.end() }) @@ -241,8 +220,8 @@ tape('isValidSignature', function (t) { '0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0' ) - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = bigIntToBuffer(SECP256K1_N_DIV_2 + BigInt(1)) + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = bigIntToBytes(SECP256K1_N_DIV_2 + BigInt(1)) const v = BigInt(27) st.notOk(isValidSignature(v, r, s, true)) @@ -253,44 +232,44 @@ tape('isValidSignature', function (t) { '0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0' ) - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = bigIntToBuffer(SECP256K1_N_DIV_2 + BigInt(1)) + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = bigIntToBytes(SECP256K1_N_DIV_2 + BigInt(1)) const v = BigInt(27) st.ok(isValidSignature(v, r, s, false)) st.end() }) t.test('should work otherwise', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(27) st.ok(isValidSignature(v, r, s)) st.end() }) t.test('should work otherwise (v=0)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(0) st.ok(isValidSignature(v, r, s)) st.end() }) t.test('should work otherwise (v=1)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(1) st.ok(isValidSignature(v, r, s)) st.end() }) t.test('should work otherwise (chainId=3)', function (st) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(41) st.ok(isValidSignature(v, r, s, false, chainId)) st.end() }) t.test('should work otherwise (chainId=150)', function (st) { const chainId = BigInt(150) - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') const v = BigInt(chainId * BigInt(2) + BigInt(35)) st.ok(isValidSignature(v, r, s, false, chainId)) st.end() @@ -298,8 +277,8 @@ tape('isValidSignature', function (t) { }) tape('message sig', function (t) { - const r = Buffer.from('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9', 'hex') - const s = Buffer.from('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66', 'hex') + const r = hexToBytes('99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9') + const s = hexToBytes('129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66') t.test('should return hex strings that the RPC can use', function (st) { const sig = @@ -382,7 +361,7 @@ tape('message sig', function (t) { '0x99e71a99cb2270b8cac5254f9e99b6210c6c10224a1579cf389ef88b20a1abe9129ff05af364204442bdb53ab6f18a99ab48acc9326fa689f228040429e3ca66f2ded8deec6714' const chainID = BigInt('34180983699157880') const v = BigInt('68361967398315796') - st.equal(toRpcSig(v, r, s, chainID), sig) + st.deepEquals(toRpcSig(v, r, s, chainID), sig) st.end() } ) diff --git a/packages/util/test/ssz.spec.ts b/packages/util/test/ssz.spec.ts index 695e8000026..ebd8b233d36 100644 --- a/packages/util/test/ssz.spec.ts +++ b/packages/util/test/ssz.spec.ts @@ -1,3 +1,4 @@ +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Withdrawal, ssz } from '../src' @@ -5,49 +6,49 @@ const withdrawalsData = [ { index: BigInt(0), validatorIndex: BigInt(65535), - address: Buffer.from('0000000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0000000000000000000000000000000000000000'), amount: BigInt('0'), }, { index: BigInt(1), validatorIndex: BigInt(65536), - address: Buffer.from('0100000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0100000000000000000000000000000000000000'), amount: BigInt('04523128485832663883'), }, { index: BigInt(2), validatorIndex: BigInt(65537), - address: Buffer.from('0200000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0200000000000000000000000000000000000000'), amount: BigInt('09046256971665327767'), }, { index: BigInt(4), validatorIndex: BigInt(65538), - address: Buffer.from('0300000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0300000000000000000000000000000000000000'), amount: BigInt('13569385457497991651'), }, { index: BigInt(4), validatorIndex: BigInt(65539), - address: Buffer.from('0400000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0400000000000000000000000000000000000000'), amount: BigInt('18446744073709551615'), }, { index: BigInt(5), validatorIndex: BigInt(65540), - address: Buffer.from('0500000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0500000000000000000000000000000000000000'), amount: BigInt('02261564242916331941'), }, { index: BigInt(6), validatorIndex: BigInt(65541), - address: Buffer.from('0600000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0600000000000000000000000000000000000000'), amount: BigInt('02713877091499598330'), }, { index: BigInt(7), validatorIndex: BigInt(65542), - address: Buffer.from('0700000000000000000000000000000000000000', 'hex'), + address: hexToBytes('0700000000000000000000000000000000000000'), amount: BigInt('03166189940082864718'), }, ] @@ -56,16 +57,12 @@ tape('ssz', (t) => { t.test('withdrawals', (st) => { const withdrawals = withdrawalsData.map((wt) => Withdrawal.fromWithdrawalData(wt)) const withdrawalsValue = withdrawals.map((wt) => wt.toValue()) - const sszValues = ssz.Withdrawals.toViewDU(withdrawalsData) - .toValue() - .map((wt) => { - wt.address = Buffer.from(wt.address) - return wt - }) + const sszValues = ssz.Withdrawals.toViewDU(withdrawalsData).toValue() + st.deepEqual(sszValues, withdrawalsValue, 'sszValues should be same as withdrawalsValue') const withdrawalsRoot = ssz.Withdrawals.hashTreeRoot(withdrawalsValue) st.equal( - Buffer.from(withdrawalsRoot).toString('hex'), + bytesToHex(withdrawalsRoot), 'bd97f65e513f870484e85927510acb291fcfb3e593c05ab7f21f206921264946', 'ssz root should match' ) @@ -77,7 +74,7 @@ tape('ssz', (t) => { { index: BigInt('17107150653359250726'), validatorIndex: BigInt('1906681273455760070'), - address: Buffer.from('02ab1379b6334b58df82c85d50ff1214663cba20', 'hex'), + address: hexToBytes('02ab1379b6334b58df82c85d50ff1214663cba20'), amount: BigInt('5055030296454530815'), }, ] @@ -85,7 +82,7 @@ tape('ssz', (t) => { t.test('match spec v1.3.0-rc.1', (st) => { const withdrawalsRoot = ssz.Withdrawal.hashTreeRoot(specWithdrawals[0]) st.equal( - Buffer.from(withdrawalsRoot).toString('hex'), + bytesToHex(withdrawalsRoot), 'ed9cec6fb8ee22b146059d02c38940cca1dd22a00d0132b000999b983fceff95', 'ssz root should match' ) diff --git a/packages/util/test/types.spec.ts b/packages/util/test/types.spec.ts index fe1988586a0..cf8ef00ddbd 100644 --- a/packages/util/test/types.spec.ts +++ b/packages/util/test/types.spec.ts @@ -1,14 +1,14 @@ +import { bytesToHex } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { TypeOutput, - bigIntToBuffer, + bigIntToBytes, bigIntToHex, - bufferToBigInt, - bufferToHex, - intToBuffer, + bytesToBigInt, + intToBytes, intToHex, - toBuffer, + toBytes, toType, } from '../src' @@ -16,11 +16,11 @@ tape('toType', function (t) { t.test('from null and undefined', function (st) { st.equal(toType(null, TypeOutput.Number), null) st.equal(toType(null, TypeOutput.BigInt), null) - st.equal(toType(null, TypeOutput.Buffer), null) + st.equal(toType(null, TypeOutput.Uint8Array), null) st.equal(toType(null, TypeOutput.PrefixedHexString), null) st.equal(toType(undefined, TypeOutput.Number), undefined) st.equal(toType(undefined, TypeOutput.BigInt), undefined) - st.equal(toType(undefined, TypeOutput.Buffer), undefined) + st.equal(toType(undefined, TypeOutput.Uint8Array), undefined) st.equal(toType(undefined, TypeOutput.PrefixedHexString), undefined) st.end() }) @@ -36,14 +36,14 @@ tape('toType', function (t) { st.equal(result, BigInt(num)) st.end() }) - st.test('should convert to Buffer', function (st) { - const result = toType(num, TypeOutput.Buffer) - st.ok(result.equals(intToBuffer(num))) + st.test('should convert to Uint8Array', function (st) { + const result = toType(num, TypeOutput.Uint8Array) + st.deepEquals(result, intToBytes(num)) st.end() }) st.test('should convert to PrefixedHexString', function (st) { const result = toType(num, TypeOutput.PrefixedHexString) - st.strictEqual(result, bufferToHex(bigIntToBuffer(BigInt(num)))) + st.strictEqual(result, bytesToHex(bigIntToBytes(BigInt(num)))) st.end() }) st.test('should throw an error if greater than MAX_SAFE_INTEGER', function (st) { @@ -66,14 +66,14 @@ tape('toType', function (t) { st.equal(result, num) st.end() }) - st.test('should convert to Buffer', function (st) { - const result = toType(num, TypeOutput.Buffer) - st.ok(result.equals(bigIntToBuffer(num))) + st.test('should convert to Uint8Array', function (st) { + const result = toType(num, TypeOutput.Uint8Array) + st.deepEquals(result, bigIntToBytes(num)) st.end() }) st.test('should convert to PrefixedHexString', function (st) { const result = toType(num, TypeOutput.PrefixedHexString) - st.strictEqual(result, bufferToHex(bigIntToBuffer(num))) + st.strictEqual(result, bytesToHex(bigIntToBytes(num))) st.end() }) st.test( @@ -87,26 +87,26 @@ tape('toType', function (t) { } ) }) - t.test('from Buffer', function (st) { - const num = intToBuffer(1000) + t.test('from Uint8Array', function (st) { + const num = intToBytes(1000) st.test('should convert to Number', function (st) { const result = toType(num, TypeOutput.Number) - st.ok(intToBuffer(result).equals(num)) + st.deepEquals(intToBytes(result), num) st.end() }) st.test('should convert to BigInt', function (st) { const result = toType(num, TypeOutput.BigInt) - st.equal(result, bufferToBigInt(num)) + st.equal(result, bytesToBigInt(num)) st.end() }) - st.test('should convert to Buffer', function (st) { - const result = toType(num, TypeOutput.Buffer) - st.ok(result.equals(num)) + st.test('should convert to Uint8Array', function (st) { + const result = toType(num, TypeOutput.Uint8Array) + st.deepEquals(result, num) st.end() }) st.test('should convert to PrefixedHexString', function (st) { const result = toType(num, TypeOutput.PrefixedHexString) - st.strictEqual(result, bufferToHex(num)) + st.strictEqual(result, bytesToHex(num)) st.end() }) }) @@ -122,9 +122,9 @@ tape('toType', function (t) { st.strictEqual(bigIntToHex(result), num) st.end() }) - st.test('should convert to Buffer', function (st) { - const result = toType(num, TypeOutput.Buffer) - st.ok(result.equals(toBuffer(num))) + st.test('should convert to Uint8Array', function (st) { + const result = toType(num, TypeOutput.Uint8Array) + st.deepEquals(result, toBytes(num)) st.end() }) st.test('should throw an error if is not 0x-prefixed', function (st) { diff --git a/packages/util/test/withdrawal.spec.ts b/packages/util/test/withdrawal.spec.ts index a521255b877..ee8f6849c64 100644 --- a/packages/util/test/withdrawal.spec.ts +++ b/packages/util/test/withdrawal.spec.ts @@ -1,9 +1,10 @@ import { decode, encode } from '@ethereumjs/rlp' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' -import { Withdrawal, bigIntToHex, intToHex } from '../src' +import { Withdrawal, bigIntToHex, bytesToPrefixedHexString, intToHex } from '../src' -import type { WithdrawalBuffer } from '../src' +import type { WithdrawalBytes } from '../src' const withdrawalsVector = [ { @@ -66,33 +67,31 @@ const gethWithdrawals8BlockRlp = tape('Withdrawal', (t) => { // gethWithdrawals8Rlp is rlp encoded block with withdrawals in the 4th element of the top array - const gethWithdrawalsBuffer = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex'))[3]! - const gethWithdrawalsRlp = Buffer.from(encode(gethWithdrawalsBuffer)).toString('hex') - t.test('fromWithdrawalData and toBufferArray', (st) => { + const gethWithdrawalsBuffer = decode(hexToBytes(gethWithdrawals8BlockRlp))[3]! + const gethWithdrawalsRlp = bytesToHex(encode(gethWithdrawalsBuffer)) + t.test('fromWithdrawalData and toBytesArray', (st) => { const withdrawals = withdrawalsGethVector.map(Withdrawal.fromWithdrawalData) - const withdrawalsToBufferArr = withdrawals.map((wt) => wt.raw()) - const withdrawalsToRlp = Buffer.from(encode(withdrawalsToBufferArr)).toString('hex') + const withdrawalstoBytesArr = withdrawals.map((wt) => wt.raw()) + const withdrawalsToRlp = bytesToHex(encode(withdrawalstoBytesArr)) st.equal(gethWithdrawalsRlp, withdrawalsToRlp, 'The withdrawals to buffer should match') st.end() }) - t.test('toBufferArray from withdrawalData', (st) => { - const withdrawalsDataToBufferArr = withdrawalsGethVector.map(Withdrawal.toBufferArray) - const withdrawalsDataToRlp = Buffer.from(encode(withdrawalsDataToBufferArr)).toString('hex') + t.test('toBytesArray from withdrawalData', (st) => { + const withdrawalsDatatoBytesArr = withdrawalsGethVector.map(Withdrawal.toBytesArray) + const withdrawalsDataToRlp = bytesToHex(encode(withdrawalsDatatoBytesArr)) st.equal(gethWithdrawalsRlp, withdrawalsDataToRlp, 'The withdrawals to buffer should match') st.end() }) t.test('fromValuesArray, toJSON and toValue', (st) => { - const withdrawals = (gethWithdrawalsBuffer as WithdrawalBuffer[]).map( - Withdrawal.fromValuesArray - ) + const withdrawals = (gethWithdrawalsBuffer as WithdrawalBytes[]).map(Withdrawal.fromValuesArray) const withdrawalsJson = withdrawals.map((wt) => wt.toJSON()) st.deepEqual(withdrawalsGethVector, withdrawalsJson, 'Withdrawals json should match') const withdrawalsValue = withdrawals.map((wt) => wt.toValue()) st.deepEqual( - withdrawalsValue.map((wt) => `0x${wt.address.toString('hex')}`), + withdrawalsValue.map((wt) => bytesToPrefixedHexString(wt.address)), withdrawalsJson.map((wt) => wt.address) ) st.end() diff --git a/packages/vm/benchmarks/mockchain.ts b/packages/vm/benchmarks/mockchain.ts index 51340b5e1cc..fb413978d17 100644 --- a/packages/vm/benchmarks/mockchain.ts +++ b/packages/vm/benchmarks/mockchain.ts @@ -17,7 +17,7 @@ export class Mockchain { } } - putBlockHash(num: bigint, hash: Buffer): void { + putBlockHash(num: bigint, hash: Uint8Array): void { this._hashes[num.toString()] = hash } } diff --git a/packages/vm/benchmarks/util.ts b/packages/vm/benchmarks/util.ts index 923b1c7fccd..e52f04a6511 100644 --- a/packages/vm/benchmarks/util.ts +++ b/packages/vm/benchmarks/util.ts @@ -1,4 +1,4 @@ -import { Account, Address, toBuffer } from '@ethereumjs/util' +import { Account, Address, equalsBytes, toBytes } from '@ethereumjs/util' import { Common } from '@ethereumjs/common' import { Block } from '@ethereumjs/block' import { StateManager, DefaultStateManager } from '@ethereumjs/statemanager' @@ -29,18 +29,18 @@ export async function getPreState( const state = new DefaultStateManager() await state.checkpoint() for (const k in pre) { - const address = new Address(toBuffer(k)) + const address = new Address(toBytes(k)) const { nonce, balance, code, storage } = pre[k] const account = new Account(BigInt(nonce), BigInt(balance)) await state.putAccount(address, account) - await state.putContractCode(address, toBuffer(code)) + await state.putContractCode(address, toBytes(code)) for (const sk in storage) { const sv = storage[sk] - const valueBuffer = toBuffer(sv) + const valueBytes = toBytes(sv) // verify if this value buffer is not a zero buffer. if so, we should not write it... - const zeroBufferEquivalent = Buffer.alloc(valueBuffer.length, 0) - if (!zeroBufferEquivalent.equals(valueBuffer)) { - await state.putContractStorage(address, toBuffer(sk), toBuffer(sv)) + const zeroBytesEquivalent = new Uint8Array(valueBytes.length) + if (!equalsBytes(zeroBytesEquivalent, valueBytes)) { + await state.putContractStorage(address, toBytes(sk), toBytes(sv)) } } } @@ -52,7 +52,7 @@ export function getBlockchain(blockhashes: any): Mockchain { let mockchain = new Mockchain() for (const blockNum in blockhashes) { const hash = blockhashes[blockNum] - mockchain.putBlockHash(BigInt(blockNum), toBuffer(hash)) + mockchain.putBlockHash(BigInt(blockNum), toBytes(hash)) } return mockchain } @@ -60,7 +60,7 @@ export function getBlockchain(blockhashes: any): Mockchain { export const verifyResult = (block: Block, result: RunBlockResult) => { // verify the receipts root, the logs bloom and the gas used after block execution, // throw if any of these is not the expected value - if (result.receiptsRoot && !result.receiptsRoot.equals(block.header.receiptTrie)) { + if (result.receiptsRoot && !equalsBytes(result.receiptsRoot, block.header.receiptTrie)) { // there's something wrong here with the receipts trie. // if block has receipt data we can check against the expected result of the block // and the reported data of the VM in order to isolate the problem @@ -86,7 +86,7 @@ export const verifyResult = (block: Block, result: RunBlockResult) => { } throw new Error('invalid receiptTrie') } - if (!result.logsBloom.equals(block.header.logsBloom)) { + if (!equalsBytes(result.logsBloom, block.header.logsBloom)) { throw new Error('invalid logsBloom') } if (block.header.gasUsed !== result.gasUsed) { diff --git a/packages/vm/examples/helpers/account-utils.ts b/packages/vm/examples/helpers/account-utils.ts index ce0861be876..9d806f5746d 100644 --- a/packages/vm/examples/helpers/account-utils.ts +++ b/packages/vm/examples/helpers/account-utils.ts @@ -17,7 +17,7 @@ export const insertAccount = async (vm: VM, address: Address) => { await vm.stateManager.putAccount(address, account) } -export const getAccountNonce = async (vm: VM, accountPrivateKey: Buffer) => { +export const getAccountNonce = async (vm: VM, accountPrivateKey: Uint8Array) => { const address = Address.fromPrivateKey(accountPrivateKey) const account = await vm.stateManager.getAccount(address) return account.nonce diff --git a/packages/vm/examples/run-blockchain.ts b/packages/vm/examples/run-blockchain.ts index 90e8f9a2058..249d174897b 100644 --- a/packages/vm/examples/run-blockchain.ts +++ b/packages/vm/examples/run-blockchain.ts @@ -6,12 +6,19 @@ // 4. Puts the blocks from ../utils/blockchain-mock-data "blocks" attribute into the Blockchain // 5. Runs the Blockchain on the VM. -import { Account, Address, toBuffer, setLengthLeft } from '@ethereumjs/util' +import { + Account, + Address, + toBytes, + setLengthLeft, + bytesToPrefixedHexString, +} from '@ethereumjs/util' import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { Common, ConsensusType } from '@ethereumjs/common' import { VM } from '../' import { testData } from './helpers/blockchain-mock-data' +import { hexToBytes } from 'ethereum-cryptography/utils' async function main() { const common = new Common({ chain: 1, hardfork: testData.network.toLowerCase() }) @@ -43,7 +50,7 @@ async function main() { const blockchainHead = await vm.blockchain.getIteratorHead!() console.log('--- Finished processing the Blockchain ---') - console.log('New head:', '0x' + blockchainHead.hash().toString('hex')) + console.log('New head:', bytesToPrefixedHexString(blockchainHead.hash())) console.log('Expected:', testData.lastblockhash) } @@ -53,17 +60,17 @@ async function setupPreConditions(vm: VM, data: typeof testData) { for (const [addr, acct] of Object.entries(data.pre)) { const { nonce, balance, storage, code } = acct - const address = new Address(Buffer.from(addr.slice(2), 'hex')) + const address = new Address(hexToBytes(addr.slice(2))) const account = Account.fromAccountData({ nonce, balance }) await vm.stateManager.putAccount(address, account) for (const [key, val] of Object.entries(storage)) { - const storageKey = setLengthLeft(Buffer.from(key, 'hex'), 32) - const storageVal = Buffer.from(val as string, 'hex') + const storageKey = setLengthLeft(hexToBytes(key), 32) + const storageVal = hexToBytes(val as string) await vm.stateManager.putContractStorage(address, storageKey, storageVal) } - const codeBuf = Buffer.from(code.slice(2), 'hex') + const codeBuf = hexToBytes(code.slice(2)) await vm.stateManager.putContractCode(address, codeBuf) } @@ -72,7 +79,7 @@ async function setupPreConditions(vm: VM, data: typeof testData) { async function putBlocks(blockchain: Blockchain, common: Common, data: typeof testData) { for (const blockData of data.blocks) { - const blockRlp = toBuffer(blockData.rlp) + const blockRlp = toBytes(blockData.rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) await blockchain.putBlock(block) } diff --git a/packages/vm/examples/run-solidity-contract.ts b/packages/vm/examples/run-solidity-contract.ts index b33409dbbd5..1d45d84372b 100644 --- a/packages/vm/examples/run-solidity-contract.ts +++ b/packages/vm/examples/run-solidity-contract.ts @@ -4,17 +4,18 @@ import { defaultAbiCoder as AbiCoder, Interface } from '@ethersproject/abi' import { Address } from '@ethereumjs/util' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' -import { VM } from '..' +import { VM } from '@ethereumjs/vm' import { buildTransaction, encodeDeployment, encodeFunction } from './helpers/tx-builder' import { getAccountNonce, insertAccount } from './helpers/account-utils' import { Block } from '@ethereumjs/block' +import { bytesToHex, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' const solc = require('solc') const INITIAL_GREETING = 'Hello, World!' const SECOND_GREETING = 'Hola, Mundo!' const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Istanbul }) -const block = Block.fromBlockData({ header: { extraData: Buffer.alloc(97) } }, { common }) +const block = Block.fromBlockData({ header: { extraData: new Uint8Array(97) } }, { common }) /** * This function creates the input for the Solidity compiler. @@ -85,13 +86,14 @@ function getGreeterDeploymentBytecode(solcOutput: any): any { async function deployContract( vm: VM, - senderPrivateKey: Buffer, - deploymentBytecode: Buffer, + senderPrivateKey: Uint8Array, + deploymentBytecode: string, greeting: string ): Promise
{ // Contracts are deployed by sending their deployment bytecode to the address 0 // The contract params should be abi-encoded and appended to the deployment bytecode. - const data = encodeDeployment(deploymentBytecode.toString('hex'), { + + const data = encodeDeployment(deploymentBytecode, { types: ['string'], values: [greeting], }) @@ -113,7 +115,7 @@ async function deployContract( async function setGreeting( vm: VM, - senderPrivateKey: Buffer, + senderPrivateKey: Uint8Array, contractAddress: Address, greeting: string ) { @@ -144,7 +146,7 @@ async function getGreeting(vm: VM, contractAddress: Address, caller: Address) { to: contractAddress, caller: caller, origin: caller, // The tx.origin is also the caller here - data: Buffer.from(sigHash.slice(2), 'hex'), + data: hexToBytes(sigHash.slice(2)), block, }) @@ -158,10 +160,7 @@ async function getGreeting(vm: VM, contractAddress: Address, caller: Address) { } async function main() { - const accountPk = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' - ) + const accountPk = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const vm = await VM.create({ common }) const accountAddress = Address.fromPrivateKey(accountPk) @@ -215,8 +214,8 @@ async function main() { console.log('-------results-------') console.log('nonce: ' + createdAccount.nonce.toString()) console.log('balance in wei: ', createdAccount.balance.toString()) - console.log('storageRoot: 0x' + createdAccount.storageRoot.toString('hex')) - console.log('codeHash: 0x' + createdAccount.codeHash.toString('hex')) + console.log('storageRoot: 0x' + bytesToHex(createdAccount.storageRoot)) + console.log('codeHash: 0x' + bytesToHex(createdAccount.codeHash)) console.log('---------------------') console.log('Everything ran correctly!') diff --git a/packages/vm/src/bloom/index.ts b/packages/vm/src/bloom/index.ts index 78b8d529b8a..e79272c3b2f 100644 --- a/packages/vm/src/bloom/index.ts +++ b/packages/vm/src/bloom/index.ts @@ -4,12 +4,12 @@ import { keccak256 } from 'ethereum-cryptography/keccak' const BYTE_SIZE = 256 export class Bloom { - bitvector: Buffer + bitvector: Uint8Array /** * Represents a Bloom filter. */ - constructor(bitvector?: Buffer) { + constructor(bitvector?: Uint8Array) { if (!bitvector) { this.bitvector = zeros(BYTE_SIZE) } else { @@ -22,12 +22,12 @@ export class Bloom { * Adds an element to a bit vector of a 64 byte bloom filter. * @param e - The element to add */ - add(e: Buffer) { - e = Buffer.from(keccak256(e)) + add(e: Uint8Array) { + e = keccak256(e) const mask = 2047 // binary 11111111111 for (let i = 0; i < 3; i++) { - const first2bytes = e.readUInt16BE(i * 2) + const first2bytes = new DataView(e.buffer).getUint16(i * 2) const loc = mask & first2bytes const byteLoc = loc >> 3 const bitLoc = 1 << loc % 8 @@ -39,13 +39,13 @@ export class Bloom { * Checks if an element is in the bloom. * @param e - The element to check */ - check(e: Buffer): boolean { - e = Buffer.from(keccak256(e)) + check(e: Uint8Array): boolean { + e = keccak256(e) const mask = 2047 // binary 11111111111 let match = true for (let i = 0; i < 3 && match; i++) { - const first2bytes = e.readUInt16BE(i * 2) + const first2bytes = new DataView(e.buffer).getUint16(i * 2) const loc = mask & first2bytes const byteLoc = loc >> 3 const bitLoc = 1 << loc % 8 @@ -59,8 +59,8 @@ export class Bloom { * Checks if multiple topics are in a bloom. * @returns `true` if every topic is in the bloom */ - multiCheck(topics: Buffer[]): boolean { - return topics.every((t: Buffer) => this.check(t)) + multiCheck(topics: Uint8Array[]): boolean { + return topics.every((t: Uint8Array) => this.check(t)) } /** diff --git a/packages/vm/src/buildBlock.ts b/packages/vm/src/buildBlock.ts index b247a16a8f6..f2a0ef6e56d 100644 --- a/packages/vm/src/buildBlock.ts +++ b/packages/vm/src/buildBlock.ts @@ -3,7 +3,7 @@ import { ConsensusType } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Trie } from '@ethereumjs/trie' import { BlobEIP4844Transaction } from '@ethereumjs/tx' -import { Address, GWEI_TO_WEI, TypeOutput, Withdrawal, toBuffer, toType } from '@ethereumjs/util' +import { Address, GWEI_TO_WEI, TypeOutput, Withdrawal, toBytes, toType } from '@ethereumjs/util' import { Bloom } from './bloom' import { calculateMinerReward, encodeReceipt, rewardAccount } from './runBlock' @@ -118,7 +118,7 @@ export class BlockBuilder { for (const [i, txResult] of this.transactionResults.entries()) { const tx = this.transactions[i] const encodedReceipt = encodeReceipt(txResult.receipt, tx.type) - await receiptTrie.put(Buffer.from(RLP.encode(i)), encodedReceipt) + await receiptTrie.put(RLP.encode(i), encodedReceipt) } return receiptTrie.root() } @@ -131,7 +131,7 @@ export class BlockBuilder { const reward = calculateMinerReward(minerReward, 0) const coinbase = this.headerData.coinbase !== undefined - ? new Address(toBuffer(this.headerData.coinbase)) + ? new Address(toBytes(this.headerData.coinbase)) : Address.zero() await rewardAccount(this.vm.eei, coinbase, reward) } @@ -192,7 +192,9 @@ export class BlockBuilder { throw new Error('block data gas limit reached') } - const parentHeader = await this.vm.blockchain.getBlock(this.headerData.parentHash! as Buffer) + const parentHeader = await this.vm.blockchain.getBlock( + this.headerData.parentHash! as Uint8Array + ) excessDataGas = calcExcessDataGas( parentHeader!.header, (tx as BlobEIP4844Transaction).blobs?.length ?? 0 @@ -271,9 +273,9 @@ export class BlockBuilder { if (this.vm._common.isActivatedEIP(4844)) { let parentHeader = null if (this.headerData.parentHash !== undefined) { - parentHeader = await this.vm.blockchain.getBlock(toBuffer(this.headerData.parentHash)) + parentHeader = await this.vm.blockchain.getBlock(toBytes(this.headerData.parentHash)) } - if (parentHeader !== null && parentHeader.header._common.isActivatedEIP(4844)) { + if (parentHeader !== null && parentHeader.header._common.isActivatedEIP(4844) === true) { // Compute total number of blobs in block const blobTxns = this.transactions.filter((tx) => tx instanceof BlobEIP4844Transaction) let newBlobs = 0 diff --git a/packages/vm/src/eei/eei.ts b/packages/vm/src/eei/eei.ts index cfcf2a36072..4b0dba48fbd 100644 --- a/packages/vm/src/eei/eei.ts +++ b/packages/vm/src/eei/eei.ts @@ -1,4 +1,4 @@ -import { bufferToBigInt } from '@ethereumjs/util' +import { bytesToBigInt } from '@ethereumjs/util' import { VmState } from './vmState' @@ -8,7 +8,7 @@ import type { StateManager } from '@ethereumjs/statemanager' import type { Address } from '@ethereumjs/util' type Block = { - hash(): Buffer + hash(): Uint8Array } type Blockchain = { @@ -56,7 +56,7 @@ export class EEI extends VmState implements EEIInterface { * Returns code of an account. * @param address - Address of account */ - async getExternalCode(address: Address): Promise { + async getExternalCode(address: Address): Promise { return this.getContractCode(address) } @@ -66,7 +66,7 @@ export class EEI extends VmState implements EEIInterface { */ async getBlockHash(num: bigint): Promise { const block = await this._blockchain.getBlock(Number(num)) - return bufferToBigInt(block!.hash()) + return bytesToBigInt(block!.hash()) } /** @@ -75,7 +75,7 @@ export class EEI extends VmState implements EEIInterface { * @param key Storage key * @param value Storage value */ - async storageStore(address: Address, key: Buffer, value: Buffer): Promise { + async storageStore(address: Address, key: Uint8Array, value: Uint8Array): Promise { await this.putContractStorage(address, key, value) } @@ -85,7 +85,7 @@ export class EEI extends VmState implements EEIInterface { * @param key Storage key * @param original If true, return the original storage value (default: false) */ - async storageLoad(address: Address, key: Buffer, original = false): Promise { + async storageLoad(address: Address, key: Uint8Array, original = false): Promise { if (original) { return this.getOriginalContractStorage(address, key) } else { diff --git a/packages/vm/src/eei/vmState.ts b/packages/vm/src/eei/vmState.ts index 8e4f28293fc..98cbadf9e25 100644 --- a/packages/vm/src/eei/vmState.ts +++ b/packages/vm/src/eei/vmState.ts @@ -1,7 +1,8 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { ripemdPrecompileAddress } from '@ethereumjs/evm/dist/precompiles' -import { Account, Address, toBuffer } from '@ethereumjs/util' +import { Account, Address, toBytes } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { Journaling } from './journaling' @@ -37,7 +38,7 @@ export class VmState implements EVMStateAccess { // to also include on access list generation protected _accessedStorageReverted: Map>[] - protected _originalStorageCache: Map> + protected _originalStorageCache: Map> protected readonly touchedJournal: Journaling @@ -116,7 +117,6 @@ export class VmState implements EVMStateAccess { this._accessedStorageReverted.push(lastItem) } } - await this._stateManager.revert() this.touchedJournal.revert(ripemdPrecompileAddress) @@ -154,19 +154,19 @@ export class VmState implements EVMStateAccess { this.touchAccount(address) } - async getContractCode(address: Address): Promise { + async getContractCode(address: Address): Promise { return this._stateManager.getContractCode(address) } - async putContractCode(address: Address, value: Buffer): Promise { + async putContractCode(address: Address, value: Uint8Array): Promise { return this._stateManager.putContractCode(address, value) } - async getContractStorage(address: Address, key: Buffer): Promise { + async getContractStorage(address: Address, key: Uint8Array): Promise { return this._stateManager.getContractStorage(address, key) } - async putContractStorage(address: Address, key: Buffer, value: Buffer) { + async putContractStorage(address: Address, key: Uint8Array, value: Uint8Array) { await this._stateManager.putContractStorage(address, key, value) this.touchAccount(address) } @@ -180,18 +180,18 @@ export class VmState implements EVMStateAccess { return this._stateManager.accountExists(address) } - async setStateRoot(stateRoot: Buffer): Promise { + async setStateRoot(stateRoot: Uint8Array): Promise { if (this._checkpointCount !== 0) { throw new Error('Cannot set state root with uncommitted checkpoints') } return this._stateManager.setStateRoot(stateRoot) } - async getStateRoot(): Promise { + async getStateRoot(): Promise { return this._stateManager.getStateRoot() } - async hasStateRoot(root: Buffer): Promise { + async hasStateRoot(root: Uint8Array): Promise { return this._stateManager.hasStateRoot(root) } @@ -203,7 +203,7 @@ export class VmState implements EVMStateAccess { * at the end of the tx. */ touchAccount(address: Address): void { - this.touchedJournal.addJournalItem(address.buf.toString('hex')) + this.touchedJournal.addJournalItem(address.toString().slice(2)) } /** @@ -256,11 +256,11 @@ export class VmState implements EVMStateAccess { const account = Account.fromAccountData({ balance }) await this.putAccount(addr, account) if (code !== undefined) { - await this.putContractCode(addr, toBuffer(code)) + await this.putContractCode(addr, toBytes(code)) } if (storage !== undefined) { for (const [key, value] of storage) { - await this.putContractStorage(addr, toBuffer(key), toBuffer(value)) + await this.putContractStorage(addr, toBytes(key), toBytes(value)) } } } @@ -276,7 +276,7 @@ export class VmState implements EVMStateAccess { if (this._common.gteHardfork(Hardfork.SpuriousDragon) === true) { const touchedArray = Array.from(this.touchedJournal.journal) for (const addressHex of touchedArray) { - const address = new Address(Buffer.from(addressHex, 'hex')) + const address = new Address(hexToBytes(addressHex)) const empty = await this.accountIsEmpty(address) if (empty) { await this._stateManager.deleteAccount(address) @@ -297,15 +297,18 @@ export class VmState implements EVMStateAccess { * @param address - Address of the account to get the storage for * @param key - Key in the account's storage to get the value for. Must be 32 bytes long. */ - protected async getOriginalContractStorage(address: Address, key: Buffer): Promise { + protected async getOriginalContractStorage( + address: Address, + key: Uint8Array + ): Promise { if (key.length !== 32) { throw new Error('Storage key must be 32 bytes long') } - const addressHex = address.buf.toString('hex') - const keyHex = key.toString('hex') + const addressHex = address.toString() + const keyHex = bytesToHex(key) - let map: Map + let map: Map if (!this._originalStorageCache.has(addressHex)) { map = new Map() this._originalStorageCache.set(addressHex, map) @@ -344,12 +347,12 @@ export class VmState implements EVMStateAccess { /** * Returns true if the address is warm in the current context - * @param address - The address (as a Buffer) to check + * @param address - The address (as a Uint8Array) to check */ - isWarmedAddress(address: Buffer): boolean { + isWarmedAddress(address: Uint8Array): boolean { for (let i = this._accessedStorage.length - 1; i >= 0; i--) { const currentMap = this._accessedStorage[i] - if (currentMap.has(address.toString('hex'))) { + if (currentMap.has(bytesToHex(address))) { return true } } @@ -358,10 +361,10 @@ export class VmState implements EVMStateAccess { /** * Add a warm address in the current context - * @param address - The address (as a Buffer) to check + * @param address - The address (as a Uint8Array) to check */ - addWarmedAddress(address: Buffer): void { - const key = address.toString('hex') + addWarmedAddress(address: Uint8Array): void { + const key = bytesToHex(address) const storageSet = this._accessedStorage[this._accessedStorage.length - 1].get(key) if (!storageSet) { const emptyStorage = new Set() @@ -371,12 +374,12 @@ export class VmState implements EVMStateAccess { /** * Returns true if the slot of the address is warm - * @param address - The address (as a Buffer) to check - * @param slot - The slot (as a Buffer) to check + * @param address - The address (as a Uint8Array) to check + * @param slot - The slot (as a Uint8Array) to check */ - isWarmedStorage(address: Buffer, slot: Buffer): boolean { - const addressKey = address.toString('hex') - const storageKey = slot.toString('hex') + isWarmedStorage(address: Uint8Array, slot: Uint8Array): boolean { + const addressKey = bytesToHex(address) + const storageKey = bytesToHex(slot) for (let i = this._accessedStorage.length - 1; i >= 0; i--) { const currentMap = this._accessedStorage[i] @@ -390,17 +393,17 @@ export class VmState implements EVMStateAccess { /** * Mark the storage slot in the address as warm in the current context - * @param address - The address (as a Buffer) to check - * @param slot - The slot (as a Buffer) to check + * @param address - The address (as a Uint8Array) to check + * @param slot - The slot (as a Uint8Array) to check */ - addWarmedStorage(address: Buffer, slot: Buffer): void { - const addressKey = address.toString('hex') + addWarmedStorage(address: Uint8Array, slot: Uint8Array): void { + const addressKey = bytesToHex(address) let storageSet = this._accessedStorage[this._accessedStorage.length - 1].get(addressKey) if (!storageSet) { storageSet = new Set() this._accessedStorage[this._accessedStorage.length - 1].set(addressKey, storageSet!) } - storageSet!.add(slot.toString('hex')) + storageSet!.add(bytesToHex(slot)) } /** diff --git a/packages/vm/src/runBlock.ts b/packages/vm/src/runBlock.ts index 197a8364fb2..ed37120d3dd 100644 --- a/packages/vm/src/runBlock.ts +++ b/packages/vm/src/runBlock.ts @@ -6,12 +6,15 @@ import { Account, Address, GWEI_TO_WEI, - bigIntToBuffer, - bufArrToArr, - intToBuffer, + bigIntToBytes, + bytesToHex, + concatBytesNoTypeCheck, + equalsBytes, + intToBytes, short, } from '@ethereumjs/util' import { debug as createDebugLogger } from 'debug' +import { hexToBytes } from 'ethereum-cryptography/utils' import { Bloom } from './bloom' import * as DAOConfig from './config/dao_fork_accounts_config.json' @@ -66,7 +69,7 @@ export async function runBlock(this: VM, opts: RunBlockOpts): Promise { const castedTx = opts.tx for (const accessListItem of castedTx.AccessListJSON) { - const address = toBuffer(accessListItem.address) + const address = toBytes(accessListItem.address) state.addWarmedAddress(address) for (const storageKey of accessListItem.storageKeys) { - state.addWarmedStorage(address, toBuffer(storageKey)) + state.addWarmedStorage(address, toBytes(storageKey)) } } } @@ -209,7 +210,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { if (this.DEBUG) { debug( `New tx run hash=${ - opts.tx.isSigned() ? opts.tx.hash().toString('hex') : 'unsigned' + opts.tx.isSigned() ? bytesToHex(opts.tx.hash()) : 'unsigned' } sender=${caller}` ) } @@ -218,15 +219,15 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { // Add origin and precompiles to warm addresses const activePrecompiles = this.evm.precompiles for (const [addressStr] of activePrecompiles.entries()) { - state.addWarmedAddress(Buffer.from(addressStr, 'hex')) + state.addWarmedAddress(hexToBytes(addressStr)) } - state.addWarmedAddress(caller.buf) + state.addWarmedAddress(caller.bytes) if (tx.to) { // Note: in case we create a contract, we do this in EVMs `_executeCreate` (this is also correct in inner calls, per the EIP) - state.addWarmedAddress(tx.to.buf) + state.addWarmedAddress(tx.to.bytes) } if (this._common.isActivatedEIP(3651) === true) { - state.addWarmedAddress(block.header.coinbase.buf) + state.addWarmedAddress(block.header.coinbase.bytes) } } @@ -264,7 +265,10 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { const { nonce, balance } = fromAccount debug(`Sender's pre-tx balance is ${balance}`) // EIP-3607: Reject transactions from senders with deployed code - if (this._common.isActivatedEIP(3607) === true && !fromAccount.codeHash.equals(KECCAK256_NULL)) { + if ( + this._common.isActivatedEIP(3607) === true && + !equalsBytes(fromAccount.codeHash, KECCAK256_NULL) + ) { const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', this, block, tx) throw new Error(msg) } @@ -411,7 +415,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { if (this.DEBUG) { debug( `Running tx=0x${ - tx.isSigned() ? tx.hash().toString('hex') : 'unsigned' + tx.isSigned() ? bytesToHex(tx.hash()) : 'unsigned' } with caller=${caller} gasLimit=${gasLimit} to=${ to?.toString() ?? 'none' } value=${value} data=0x${short(data)}` @@ -438,7 +442,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { debug('-'.repeat(100)) debug( `Received tx execResult: [ executionGasUsed=${executionGasUsed} exceptionError=${ - exceptionError ? `'${exceptionError.error}'` : 'none' + exceptionError !== undefined ? `'${exceptionError.error}'` : 'none' } returnValue=0x${short(returnValue)} gasRefund=${results.gasRefund ?? 0} ]` ) } @@ -515,10 +519,10 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { /* * Cleanup accounts */ - if (results.execResult.selfdestruct) { + if (results.execResult.selfdestruct !== undefined) { const keys = Object.keys(results.execResult.selfdestruct) for (const k of keys) { - const address = new Address(Buffer.from(k, 'hex')) + const address = new Address(hexToBytes(k)) await state.deleteAccount(address) if (this.DEBUG) { debug(`tx selfdestruct on address=${address}`) @@ -546,7 +550,7 @@ async function _runTx(this: VM, opts: RunTxOpts): Promise { if (this.DEBUG) { debug( `tx run finished hash=${ - opts.tx.isSigned() ? opts.tx.hash().toString('hex') : 'unsigned' + opts.tx.isSigned() ? bytesToPrefixedHexString(opts.tx.hash()) : 'unsigned' } sender=${caller}` ) } @@ -610,7 +614,7 @@ export async function generateTxReceipt( if (this._common.gteHardfork(Hardfork.Byzantium) === true) { // Post-Byzantium receipt = { - status: txResult.execResult.exceptionError ? 0 : 1, // Receipts have a 0 as status on error + status: txResult.execResult.exceptionError !== undefined ? 0 : 1, // Receipts have a 0 as status on error ...baseReceipt, } as PostByzantiumTxReceipt } else { @@ -624,7 +628,7 @@ export async function generateTxReceipt( } else { // Typed EIP-2718 Transaction receipt = { - status: txResult.execResult.exceptionError ? 0 : 1, + status: txResult.execResult.exceptionError !== undefined ? 0 : 1, ...baseReceipt, } as PostByzantiumTxReceipt } diff --git a/packages/vm/src/types.ts b/packages/vm/src/types.ts index 4af717c055a..727f2be983d 100644 --- a/packages/vm/src/types.ts +++ b/packages/vm/src/types.ts @@ -19,7 +19,7 @@ export interface BaseTxReceipt { /** * Bloom bitvector */ - bitvector: Buffer + bitvector: Uint8Array /** * Logs emitted */ @@ -34,7 +34,7 @@ export interface PreByzantiumTxReceipt extends BaseTxReceipt { /** * Intermediary state root */ - stateRoot: Buffer + stateRoot: Uint8Array } /** @@ -187,13 +187,13 @@ export interface SealBlockOpts { * For PoW, the nonce. * Overrides the value passed in the constructor. */ - nonce?: Buffer + nonce?: Uint8Array /** * For PoW, the mixHash. * Overrides the value passed in the constructor. */ - mixHash?: Buffer + mixHash?: Uint8Array } /** @@ -207,7 +207,7 @@ export interface RunBlockOpts { /** * Root of the state trie */ - root?: Buffer + root?: Uint8Array /** * Whether to generate the stateRoot and other related fields. * If `true`, `runBlock` will set the fields `stateRoot`, `receiptTrie`, `gasUsed`, and `bloom` (logs bloom) after running the block. @@ -263,7 +263,7 @@ export interface RunBlockResult { /** * The stateRoot after executing the block */ - stateRoot: Buffer + stateRoot: Uint8Array /** * The gas used after executing the block */ @@ -271,11 +271,11 @@ export interface RunBlockResult { /** * The bloom filter of the LOGs (events) after executing the block */ - logsBloom: Buffer + logsBloom: Uint8Array /** * The receipt root after executing the block */ - receiptsRoot: Buffer + receiptsRoot: Uint8Array } export interface AfterBlockEvent extends RunBlockResult { diff --git a/packages/vm/src/vm.ts b/packages/vm/src/vm.ts index 5d7793abfdc..678e5b4ee81 100644 --- a/packages/vm/src/vm.ts +++ b/packages/vm/src/vm.ts @@ -3,6 +3,7 @@ import { Chain, Common } from '@ethereumjs/common' import { EVM, getActivePrecompiles } from '@ethereumjs/evm' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Account, Address, AsyncEventEmitter, TypeOutput, toType } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import { promisify } from 'util' import { buildBlock } from './buildBlock' @@ -113,13 +114,13 @@ export class VM { this.blockchain = opts.blockchain ?? new (Blockchain as any)({ common: this._common }) // TODO tests - if (opts.eei) { - if (opts.evm) { + if (opts.eei !== undefined) { + if (opts.evm !== undefined) { throw new Error('cannot specify EEI if EVM opt provided') } this.eei = opts.eei } else { - if (opts.evm) { + if (opts.evm !== undefined) { this.eei = opts.evm.eei } else { this.eei = new EEI(this.stateManager, this._common, this.blockchain) @@ -127,7 +128,7 @@ export class VM { } // TODO tests - if (opts.evm) { + if (opts.evm !== undefined) { this.evm = opts.evm } else { this.evm = new EVM({ @@ -177,11 +178,11 @@ export class VM { await this.eei.checkpoint() // put 1 wei in each of the precompiles in order to make the accounts non-empty and thus not have them deduct `callNewAccount` gas. for (const [addressStr] of getActivePrecompiles(this._common)) { - const address = new Address(Buffer.from(addressStr, 'hex')) + const address = new Address(hexToBytes(addressStr)) const account = await this.eei.getAccount(address) // Only do this if it is not overridden in genesis // Note: in the case that custom genesis has storage fields, this is preserved - if (account.isEmpty()) { + if (account.isEmpty() === true) { const newAccount = Account.fromAccountData({ balance: 1, storageRoot: account.storageRoot, diff --git a/packages/vm/test/api/EIPs/eip-1153.spec.ts b/packages/vm/test/api/EIPs/eip-1153.spec.ts index dabcb8cb886..25bb2dd60ae 100644 --- a/packages/vm/test/api/EIPs/eip-1153.spec.ts +++ b/packages/vm/test/api/EIPs/eip-1153.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' -import { Account, Address, bufferToInt, privateToAddress } from '@ethereumjs/util' +import { Account, Address, bytesToInt, privateToAddress } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -11,10 +12,7 @@ interface Test { transactions: Transaction[] } -const senderKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const senderKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') tape('EIP 1153: transient storage', (t) => { const initialGas = BigInt(0xffffffffff) @@ -54,7 +52,7 @@ tape('EIP 1153: transient storage', (t) => { }) for (const { code, address } of test.contracts) { - await vm.stateManager.putContractCode(address, Buffer.from(code, 'hex')) + await vm.stateManager.putContractCode(address, hexToBytes(code)) } const fromAddress = new Address(privateToAddress(senderKey)) @@ -70,10 +68,10 @@ tape('EIP 1153: transient storage', (t) => { t.test('should tload and tstore', async (st) => { const code = '60026001b46001b360005260206000F3' - const returndata = Buffer.alloc(32) + const returndata = new Uint8Array(32) returndata[31] = 0x02 - const address = new Address(Buffer.from('000000000000000000000000636F6E7472616374', 'hex')) + const address = new Address(hexToBytes('000000000000000000000000636F6E7472616374')) const tx = Transaction.fromTxData({ gasLimit: BigInt(21000 + 9000), to: address, @@ -112,7 +110,7 @@ tape('EIP 1153: transient storage', (t) => { // is 0, then the transient storage is cleared between // transactions const code = '36600014630000001c5760016300000012575b60ff6000b4600080f35b6000b360005260206000f3' - const address = new Address(Buffer.from('000000000000000000000000636F6E7472616374', 'hex')) + const address = new Address(hexToBytes('000000000000000000000000636F6E7472616374')) const test = { contracts: [{ address, code }], @@ -120,7 +118,7 @@ tape('EIP 1153: transient storage', (t) => { Transaction.fromTxData({ gasLimit: BigInt(15000000), to: address, - data: Buffer.alloc(32), + data: new Uint8Array(32), }).sign(senderKey), Transaction.fromTxData({ nonce: 1, @@ -164,15 +162,15 @@ tape('EIP 1153: transient storage', (t) => { const [result1, result2] = await runTest(test, st) st.equal(result1.execResult.exceptionError, undefined) - st.equal(bufferToInt(result2.execResult.returnValue), 0) + st.equal(bytesToInt(result2.execResult.returnValue), 0) st.end() }) t.test('tload should not keep reverted changes', async (st) => { // logic address has a contract with transient storage logic in it - const logicAddress = new Address(Buffer.from('EA674fdDe714fd979de3EdF0F56AA9716B898ec8', 'hex')) + const logicAddress = new Address(hexToBytes('EA674fdDe714fd979de3EdF0F56AA9716B898ec8')) // calling address is the address that calls the logic address - const callingAddress = new Address(Buffer.alloc(20, 0xff)) + const callingAddress = new Address(new Uint8Array(20).fill(0xff)) // Perform 3 calls: // - TSTORE, return @@ -659,7 +657,7 @@ tape('EIP 1153: transient storage', (t) => { } const [result] = await runTest(test, st) - st.equal(bufferToInt(result.execResult.returnValue), 0xaa) + st.equal(bytesToInt(result.execResult.returnValue), 0xaa) st.end() }) }) diff --git a/packages/vm/test/api/EIPs/eip-1283-net-gas-metering.spec.ts b/packages/vm/test/api/EIPs/eip-1283-net-gas-metering.spec.ts index 47d9d16fb05..e23656a4ed5 100644 --- a/packages/vm/test/api/EIPs/eip-1283-net-gas-metering.spec.ts +++ b/packages/vm/test/api/EIPs/eip-1283-net-gas-metering.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, bigIntToBuffer, setLengthLeft } from '@ethereumjs/util' +import { Address, bigIntToBytes, setLengthLeft } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -31,18 +32,18 @@ const testCases = [ tape('Constantinople: EIP-1283', async (t) => { t.test('net-metering SSTORE', async (st) => { - const caller = new Address(Buffer.from('0000000000000000000000000000000000000000', 'hex')) - const addr = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) - const key = setLengthLeft(bigIntToBuffer(BigInt(0)), 32) + const caller = new Address(hexToBytes('0000000000000000000000000000000000000000')) + const addr = new Address(hexToBytes('00000000000000000000000000000000000000ff')) + const key = setLengthLeft(bigIntToBytes(BigInt(0)), 32) for (const testCase of testCases) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Constantinople }) const vm = await VM.create({ common }) const account = createAccount(BigInt(0), BigInt(0)) await vm.stateManager.putAccount(addr, account) - await vm.stateManager.putContractCode(addr, Buffer.from(testCase.code, 'hex')) + await vm.stateManager.putContractCode(addr, hexToBytes(testCase.code)) if (testCase.original !== BigInt(0)) { - await vm.stateManager.putContractStorage(addr, key, bigIntToBuffer(testCase.original)) + await vm.stateManager.putContractStorage(addr, key, bigIntToBytes(testCase.original)) } const runCallArgs = { diff --git a/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts b/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts index e9d6383d287..71e513012ff 100644 --- a/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts +++ b/packages/vm/test/api/EIPs/eip-1559-FeeMarket.spec.ts @@ -5,7 +5,8 @@ import { FeeMarketEIP1559Transaction, Transaction, } from '@ethereumjs/tx' -import { Address, bigIntToBuffer, privateToAddress, setLengthLeft } from '@ethereumjs/util' +import { Address, bigIntToBytes, privateToAddress, setLengthLeft } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -32,8 +33,8 @@ common.hardforkBlock = function (hardfork: string | undefined) { return BigInt(0) } -const coinbase = new Address(Buffer.from('11'.repeat(20), 'hex')) -const pkey = Buffer.from('20'.repeat(32), 'hex') +const coinbase = new Address(hexToBytes('11'.repeat(20))) +const pkey = hexToBytes('20'.repeat(32)) const sender = new Address(privateToAddress(pkey)) /** @@ -162,7 +163,7 @@ tape('EIP1559 tests', (t) => { }) t.test('gasPrice uses the effective gas price', async (st) => { - const contractAddress = new Address(Buffer.from('20'.repeat(20), 'hex')) + const contractAddress = new Address(hexToBytes('20'.repeat(20))) const tx = new FeeMarketEIP1559Transaction( { maxFeePerGas: GWEI * BigInt(5), @@ -189,16 +190,16 @@ tape('EIP1559 tests', (t) => { */ // (This code returns the reported GASPRICE) - const code = Buffer.from('3A60005260206000F3', 'hex') + const code = hexToBytes('3A60005260206000F3') await vm.stateManager.putContractCode(contractAddress, code) const result = await vm.runTx({ tx: block.transactions[0], block }) const returnValue = result.execResult.returnValue const expectedCost = GWEI * BigInt(3) - const expectedReturn = setLengthLeft(bigIntToBuffer(expectedCost), 32) + const expectedReturn = setLengthLeft(bigIntToBytes(expectedCost), 32) - st.ok(returnValue.equals(expectedReturn)) + st.deepEquals(returnValue, expectedReturn) st.end() }) }) diff --git a/packages/vm/test/api/EIPs/eip-2315.spec.ts b/packages/vm/test/api/EIPs/eip-2315.spec.ts index b9fbfc72865..2585682aafe 100644 --- a/packages/vm/test/api/EIPs/eip-2315.spec.ts +++ b/packages/vm/test/api/EIPs/eip-2315.spec.ts @@ -1,4 +1,5 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -18,7 +19,7 @@ tape('Berlin: EIP 2315 tests', (t) => { }) const result = await vm.evm.runCode!({ - code: Buffer.from(test.code, 'hex'), + code: hexToBytes(test.code), gasLimit: BigInt(0xffffffffff), }) diff --git a/packages/vm/test/api/EIPs/eip-2565-modexp-gas-cost.spec.ts b/packages/vm/test/api/EIPs/eip-2565-modexp-gas-cost.spec.ts index 371427058cf..303ed7879d7 100644 --- a/packages/vm/test/api/EIPs/eip-2565-modexp-gas-cost.spec.ts +++ b/packages/vm/test/api/EIPs/eip-2565-modexp-gas-cost.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Address } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -14,13 +15,13 @@ tape('EIP-2565 ModExp gas cost tests', (t) => { for (const test of testData) { const testName = test.Name - const to = new Address(Buffer.from('0000000000000000000000000000000000000005', 'hex')) + const to = new Address(hexToBytes('0000000000000000000000000000000000000005')) const result = await vm.evm.runCall({ caller: Address.zero(), gasLimit: BigInt(0xffffffffff), to, value: BigInt(0), - data: Buffer.from(test.Input, 'hex'), + data: hexToBytes(test.Input), }) if (result.execResult.executionGasUsed !== BigInt(test.Gas)) { @@ -30,16 +31,16 @@ tape('EIP-2565 ModExp gas cost tests', (t) => { continue } - if (result.execResult.exceptionError) { + if (result.execResult.exceptionError !== undefined) { st.fail(`[${testName}]: Call should not fail`) continue } - if (!result.execResult.returnValue.equals(Buffer.from(test.Expected, 'hex'))) { + if (!equalsBytes(result.execResult.returnValue, hexToBytes(test.Expected))) { st.fail( `[${testName}]: Return value not the expected value (expected: ${ test.Expected - }, received: ${result.execResult.returnValue.toString('hex')})` + }, received: ${bytesToHex(result.execResult.returnValue)})` ) continue } diff --git a/packages/vm/test/api/EIPs/eip-2929.spec.ts b/packages/vm/test/api/EIPs/eip-2929.spec.ts index 4ff50d31818..a446949f668 100644 --- a/packages/vm/test/api/EIPs/eip-2929.spec.ts +++ b/packages/vm/test/api/EIPs/eip-2929.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' import { Account, Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -8,11 +9,8 @@ import { VM } from '../../../src/vm' // Test cases source: https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a tape('EIP 2929: gas cost tests', (t) => { const initialGas = BigInt(0xffffffffff) - const address = new Address(Buffer.from('000000000000000000000000636F6E7472616374', 'hex')) - const senderKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' - ) + const address = new Address(hexToBytes('000000000000000000000000636F6E7472616374')) + const senderKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin, eips: [2929] }) const runTest = async function (test: any, st: tape.Test) { @@ -48,7 +46,7 @@ tape('EIP 2929: gas cost tests', (t) => { i++ }) - await vm.stateManager.putContractCode(address, Buffer.from(test.code, 'hex')) + await vm.stateManager.putContractCode(address, hexToBytes(test.code)) const unsignedTx = Transaction.fromTxData({ gasLimit: initialGas, // ensure we pass a lot of gas, so we do not run out of gas @@ -66,18 +64,15 @@ tape('EIP 2929: gas cost tests', (t) => { const runCodeTest = async function (code: string, expectedGasUsed: bigint, st: tape.Test) { // setup the accounts for this test - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' - ) - const contractAddress = new Address( - Buffer.from('00000000000000000000000000000000000000ff', 'hex') + const privateKey = hexToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) + const contractAddress = new Address(hexToBytes('00000000000000000000000000000000000000ff')) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin, eips: [2929] }) const vm = await VM.create({ common }) - await vm.stateManager.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await vm.stateManager.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code // setup the call arguments const unsignedTx = Transaction.fromTxData({ diff --git a/packages/vm/test/api/EIPs/eip-2930-accesslists.spec.ts b/packages/vm/test/api/EIPs/eip-2930-accesslists.spec.ts index 4a28cb1f928..59af2f9f5e3 100644 --- a/packages/vm/test/api/EIPs/eip-2930-accesslists.spec.ts +++ b/packages/vm/test/api/EIPs/eip-2930-accesslists.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { AccessListEIP2930Transaction } from '@ethereumjs/tx' -import { Account, Address, bufferToHex } from '@ethereumjs/util' +import { Account, Address, bytesToHex } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -11,22 +12,19 @@ const common = new Common({ hardfork: Hardfork.Berlin, }) -const validAddress = Buffer.from('00000000000000000000000000000000000000ff', 'hex') -const validSlot = Buffer.from('00'.repeat(32), 'hex') +const validAddress = hexToBytes('00000000000000000000000000000000000000ff') +const validSlot = hexToBytes('00'.repeat(32)) // setup the accounts for this test -const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const privateKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const contractAddress = new Address(validAddress) tape('EIP-2930 Optional Access Lists tests', (t) => { t.test('VM should charge the right gas when using access list transactions', async (st) => { const access = [ { - address: bufferToHex(validAddress), - storageKeys: [bufferToHex(validSlot)], + address: bytesToHex(validAddress), + storageKeys: [bytesToHex(validSlot)], }, ] const txnWithAccessList = AccessListEIP2930Transaction.fromTxData( @@ -51,7 +49,7 @@ tape('EIP-2930 Optional Access Lists tests', (t) => { const vm = await VM.create({ common }) // contract code PUSH1 0x00 SLOAD STOP - await vm.stateManager.putContractCode(contractAddress, Buffer.from('60005400', 'hex')) + await vm.stateManager.putContractCode(contractAddress, hexToBytes('60005400')) const address = Address.fromPrivateKey(privateKey) const initialBalance = BigInt(10) ** BigInt(18) diff --git a/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts b/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts index 9ff636f9b4a..33de7089637 100644 --- a/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3074-authcall.spec.ts @@ -4,15 +4,17 @@ import { ERROR } from '@ethereumjs/evm/dist/exceptions' import { Transaction } from '@ethereumjs/tx' import { Address, - bigIntToBuffer, - bufferToBigInt, + bigIntToBytes, + bytesToBigInt, + concatBytesNoTypeCheck, ecsign, privateToAddress, setLengthLeft, - toBuffer, + toBytes, zeros, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -27,10 +29,7 @@ const common = new Common({ }) // setup the accounts for this test -const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const privateKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const authAddress = new Address(privateToAddress(privateKey)) const block = Block.fromBlockData( @@ -42,21 +41,21 @@ const block = Block.fromBlockData( { common } ) -const callerPrivateKey = Buffer.from('44'.repeat(32), 'hex') +const callerPrivateKey = hexToBytes('44'.repeat(32)) const callerAddress = new Address(privateToAddress(callerPrivateKey)) const PREBALANCE = BigInt(10000000) const address = new Address(privateToAddress(privateKey)) -const contractAddress = new Address(Buffer.from('ff'.repeat(20), 'hex')) -const contractStorageAddress = new Address(Buffer.from('ee'.repeat(20), 'hex')) +const contractAddress = new Address(hexToBytes('ff'.repeat(20))) +const contractStorageAddress = new Address(hexToBytes('ee'.repeat(20))) // Bytecode to exit call frame and return the topmost stack item -const RETURNTOP = Buffer.from('60005260206000F3', 'hex') +const RETURNTOP = hexToBytes('60005260206000F3') //Bytecode to exit call frame and return the current memory size -const RETURNMEMSIZE = Buffer.from('5960005260206000F3', 'hex') +const RETURNMEMSIZE = hexToBytes('5960005260206000F3') // Bytecode to store CALLER in slot 0 and GAS in slot 1 and the first 32 bytes of the input in slot 2 // Returns the entire input as output -const STORECALLER = Buffer.from('5A60015533600055600035600255366000600037366000F3', 'hex') +const STORECALLER = hexToBytes('5A60015533600055600035600255366000600037366000F3') /** * This signs a message to be used for AUTH opcodes @@ -65,12 +64,12 @@ const STORECALLER = Buffer.from('5A60015533600055600035600255366000600037366000F * @param privateKey - The private key of the account to sign * @returns The signed message */ -function signMessage(commitUnpadded: Buffer, address: Address, privateKey: Buffer) { +function signMessage(commitUnpadded: Uint8Array, address: Address, privateKey: Uint8Array) { const commit = setLengthLeft(commitUnpadded, 32) - const paddedInvokerAddress = setLengthLeft(address.buf, 32) - const chainId = setLengthLeft(bigIntToBuffer(common.chainId()), 32) - const message = Buffer.concat([Buffer.from('03', 'hex'), chainId, paddedInvokerAddress, commit]) - const msgHash = Buffer.from(keccak256(message)) + const paddedInvokerAddress = setLengthLeft(address.bytes, 32) + const chainId = setLengthLeft(bigIntToBytes(common.chainId()), 32) + const message = concatBytesNoTypeCheck(hexToBytes('03'), chainId, paddedInvokerAddress, commit) + const msgHash = keccak256(message) return ecsign(msgHash, privateKey) } @@ -79,34 +78,34 @@ function signMessage(commitUnpadded: Buffer, address: Address, privateKey: Buffe * @param commitUnpadded - The commit * @param signature - The signature as obtained by `signMessage` * @param address - The address which signed the commit - * @param msizeBuffer - Optional: memory size buffer, defaults to `0x80` (128 bytes) + * @param msizeBytes - Optional: memory size buffUint8Arrayer, defaults to `0x80` (128 bytes) */ function getAuthCode( - commitUnpadded: Buffer, + commitUnpadded: Uint8Array, signature: ECDSASignature, address: Address, - msizeBuffer?: Buffer + msizeBuffer?: Uint8Array ) { const commit = setLengthLeft(commitUnpadded, 32) - let v: Buffer + let v: Uint8Array if (signature.v === BigInt(27)) { - v = setLengthLeft(Buffer.from('00', 'hex'), 32) + v = setLengthLeft(hexToBytes('00'), 32) } else if (signature.v === BigInt(28)) { - v = setLengthLeft(Buffer.from('01', 'hex'), 32) + v = setLengthLeft(hexToBytes('01'), 32) } else { - v = setLengthLeft(toBuffer(signature.v), 32) + v = setLengthLeft(toBytes(signature.v), 32) } - const PUSH32 = Buffer.from('7F', 'hex') - const AUTH = Buffer.from('F6', 'hex') - const MSTORE = Buffer.from('52', 'hex') + const PUSH32 = hexToBytes('7F') + const AUTH = hexToBytes('F6') + const MSTORE = hexToBytes('52') const mslot0 = zeros(32) - const mslot1 = Buffer.concat([zeros(31), Buffer.from('20', 'hex')]) - const mslot2 = Buffer.concat([zeros(31), Buffer.from('40', 'hex')]) - const mslot3 = Buffer.concat([zeros(31), Buffer.from('60', 'hex')]) - const addressBuffer = setLengthLeft(address.buf, 32) + const mslot1 = concatBytesNoTypeCheck(zeros(31), hexToBytes('20')) + const mslot2 = concatBytesNoTypeCheck(zeros(31), hexToBytes('40')) + const mslot3 = concatBytesNoTypeCheck(zeros(31), hexToBytes('60')) + const addressBuffer = setLengthLeft(address.bytes, 32) // This bytecode setups the stack to be used for AUTH - return Buffer.concat([ + return concatBytesNoTypeCheck( PUSH32, signature.s, PUSH32, @@ -127,13 +126,13 @@ function getAuthCode( PUSH32, mslot3, MSTORE, - Buffer.from('60', 'hex'), - msizeBuffer ?? Buffer.from('80', 'hex'), - Buffer.from('6000', 'hex'), + hexToBytes('60'), + msizeBuffer ?? hexToBytes('80'), + hexToBytes('6000'), PUSH32, addressBuffer, - AUTH, - ]) + AUTH + ) } // This type has all arguments to be used on AUTHCALL @@ -153,14 +152,14 @@ type AuthcallData = { * @param position * @param value */ -function MSTORE(position: Buffer, value: Buffer) { - return Buffer.concat([ - Buffer.from('7F', 'hex'), +function MSTORE(position: Uint8Array, value: Uint8Array) { + return concatBytesNoTypeCheck( + hexToBytes('7F'), setLengthLeft(value, 32), - Buffer.from('7F', 'hex'), + hexToBytes('7F'), setLengthLeft(position, 32), - Buffer.from('52', 'hex'), - ]) + hexToBytes('52') + ) } /** @@ -169,16 +168,16 @@ function MSTORE(position: Buffer, value: Buffer) { * @returns - The bytecode to execute AUTHCALL */ function getAuthCallCode(data: AuthcallData) { - const gasLimitBuffer = setLengthLeft(bigIntToBuffer(data.gasLimit ?? BigInt(0)), 32) - const addressBuffer = setLengthLeft(data.address.buf, 32) - const valueBuffer = setLengthLeft(bigIntToBuffer(data.value ?? BigInt(0)), 32) - const valueExtBuffer = setLengthLeft(bigIntToBuffer(data.valueExt ?? BigInt(0)), 32) - const argsOffsetBuffer = setLengthLeft(bigIntToBuffer(data.argsOffset ?? BigInt(0)), 32) - const argsLengthBuffer = setLengthLeft(bigIntToBuffer(data.argsLength ?? BigInt(0)), 32) - const retOffsetBuffer = setLengthLeft(bigIntToBuffer(data.retOffset ?? BigInt(0)), 32) - const retLengthBuffer = setLengthLeft(bigIntToBuffer(data.retLength ?? BigInt(0)), 32) - const PUSH32 = Buffer.from('7f', 'hex') - const AUTHCALL = Buffer.from('f7', 'hex') + const gasLimitBuffer = setLengthLeft(bigIntToBytes(data.gasLimit ?? BigInt(0)), 32) + const addressBuffer = setLengthLeft(data.address.bytes, 32) + const valueBuffer = setLengthLeft(bigIntToBytes(data.value ?? BigInt(0)), 32) + const valueExtBuffer = setLengthLeft(bigIntToBytes(data.valueExt ?? BigInt(0)), 32) + const argsOffsetBuffer = setLengthLeft(bigIntToBytes(data.argsOffset ?? BigInt(0)), 32) + const argsLengthBuffer = setLengthLeft(bigIntToBytes(data.argsLength ?? BigInt(0)), 32) + const retOffsetBuffer = setLengthLeft(bigIntToBytes(data.retOffset ?? BigInt(0)), 32) + const retLengthBuffer = setLengthLeft(bigIntToBytes(data.retLength ?? BigInt(0)), 32) + const PUSH32 = hexToBytes('7f') + const AUTHCALL = hexToBytes('f7') const order = [ retLengthBuffer, retOffsetBuffer, @@ -190,18 +189,18 @@ function getAuthCallCode(data: AuthcallData) { gasLimitBuffer, ] const bufferList = [] - order.map((e: Buffer) => { + order.map((e: Uint8Array) => { bufferList.push(PUSH32) bufferList.push(e) }) bufferList.push(AUTHCALL) - return Buffer.concat(bufferList) + return concatBytesNoTypeCheck(...bufferList) } // This flips the signature: the result is a signature which has the same public key upon key recovery, // But the s-value is now > N_DIV_2 function flipSignature(signature: any) { - const s = bufferToBigInt(signature.s) + const s = bytesToBigInt(signature.s) const flipped = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n - s if (signature.v === 27) { @@ -209,16 +208,16 @@ function flipSignature(signature: any) { } else { signature.v = 27 } - signature.s = setLengthLeft(bigIntToBuffer(flipped), 32) + signature.s = setLengthLeft(bigIntToBytes(flipped), 32) return signature } tape('EIP-3074 AUTH', (t) => { t.test('Should execute AUTH correctly', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([getAuthCode(message, signature, authAddress), RETURNTOP]) + const code = concatBytesNoTypeCheck(getAuthCode(message, signature, authAddress), RETURNTOP) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -233,15 +232,15 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue.slice(31) - st.ok(buf.equals(Buffer.from('01', 'hex')), 'auth should return 1') + st.deepEquals(buf, hexToBytes('01'), 'auth should return 1') }) t.test('Should not set AUTH if signature is invalid', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) signature.r = signature.s - const code = Buffer.concat([getAuthCode(message, signature, authAddress), RETURNTOP]) + const code = concatBytesNoTypeCheck(getAuthCode(message, signature, authAddress), RETURNTOP) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -256,16 +255,16 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue - st.ok(buf.equals(zeros(32)), 'auth puts 0 on stack on invalid signature') + st.deepEquals(buf, zeros(32), 'auth puts 0 on stack on invalid signature') }) t.test('Should not set AUTH if reported address is invalid', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) signature.r = signature.s // use the contractAddress instead of authAddress for the expected address (this should fail) - const code = Buffer.concat([getAuthCode(message, signature, contractAddress), RETURNTOP]) + const code = concatBytesNoTypeCheck(getAuthCode(message, signature, contractAddress), RETURNTOP) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -280,14 +279,14 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue - st.ok(buf.equals(zeros(32)), 'auth puts 0') + st.deepEquals(buf, zeros(32), 'auth puts 0') }) t.test('Should throw if signature s > N_DIV_2', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = flipSignature(signMessage(message, contractAddress, privateKey)) - const code = Buffer.concat([getAuthCode(message, signature, authAddress), RETURNTOP]) + const code = concatBytesNoTypeCheck(getAuthCode(message, signature, authAddress), RETURNTOP) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -306,14 +305,14 @@ tape('EIP-3074 AUTH', (t) => { t.test('Should be able to call AUTH multiple times', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) const signature2 = signMessage(message, contractAddress, callerPrivateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCode(message, signature2, callerAddress), - RETURNTOP, - ]) + RETURNTOP + ) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -328,17 +327,17 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue.slice(31) - st.ok(buf.equals(Buffer.from('01', 'hex')), 'auth returned right address') + st.deepEquals(buf, hexToBytes('01'), 'auth returned right address') }) t.test('Should use zeros in case that memory size < 128', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('00', 'hex') + const message = hexToBytes('00') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ - getAuthCode(message, signature, authAddress, Buffer.from('60', 'hex')), - RETURNTOP, - ]) + const code = concatBytesNoTypeCheck( + getAuthCode(message, signature, authAddress, hexToBytes('60')), + RETURNTOP + ) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -353,14 +352,14 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue.slice(31) - st.ok(buf.equals(Buffer.from('01', 'hex')), 'auth returned right address') + st.deepEquals(buf, hexToBytes('01'), 'auth returned right address') }) t.test('Should charge memory expansion gas if the memory size > 128', async (st) => { const vm = await VM.create({ common }) - const message = Buffer.from('00', 'hex') + const message = hexToBytes('00') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([getAuthCode(message, signature, authAddress), RETURNMEMSIZE]) + const code = concatBytesNoTypeCheck(getAuthCode(message, signature, authAddress), RETURNMEMSIZE) await vm.stateManager.putContractCode(contractAddress, code) const tx = Transaction.fromTxData({ @@ -375,16 +374,17 @@ tape('EIP-3074 AUTH', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) - st.ok( - result.execResult.returnValue.slice(31).equals(Buffer.from('80', 'hex')), + st.deepEquals( + result.execResult.returnValue.slice(31), + hexToBytes('80'), 'reported msize is correct' ) const gas = result.execResult.executionGasUsed - const code2 = Buffer.concat([ - getAuthCode(message, signature, authAddress, Buffer.from('90', 'hex')), - RETURNMEMSIZE, - ]) + const code2 = concatBytesNoTypeCheck( + getAuthCode(message, signature, authAddress, hexToBytes('90')), + RETURNMEMSIZE + ) await vm.stateManager.putContractCode(contractAddress, code2) const tx2 = Transaction.fromTxData({ @@ -398,8 +398,9 @@ tape('EIP-3074 AUTH', (t) => { // the memory size in AUTH is 0x90 (so extra 16 bytes), but memory expands with words (32 bytes) // so the correct amount of msize is 0xa0, not 0x90 - st.ok( - result2.execResult.returnValue.slice(31).equals(Buffer.from('a0', 'hex')), + st.deepEquals( + result2.execResult.returnValue.slice(31), + hexToBytes('a0'), 'reported msize is correct' ) st.ok(result2.execResult.executionGasUsed > gas, 'charged more gas for memory expansion') @@ -408,7 +409,7 @@ tape('EIP-3074 AUTH', (t) => { }) // Setups the environment for the VM, puts `code` at contractAddress and also puts the STORECALLER bytecode at the contractStorageAddress -async function setupVM(code: Buffer) { +async function setupVM(code: Uint8Array) { const vm = await VM.create({ common: common.copy() }) await vm.stateManager.putContractCode(contractAddress, code) await vm.stateManager.putContractCode(contractStorageAddress, STORECALLER) @@ -420,15 +421,15 @@ async function setupVM(code: Buffer) { tape('EIP-3074 AUTHCALL', (t) => { t.test('Should execute AUTHCALL correctly', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -440,22 +441,22 @@ tape('EIP-3074 AUTHCALL', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const buf = result.execResult.returnValue.slice(31) - st.ok(buf.equals(Buffer.from('01', 'hex')), 'authcall success') + st.deepEquals(buf, hexToBytes('01'), 'authcall success') const storage = await vm.stateManager.getContractStorage(contractStorageAddress, zeros(32)) - st.ok(storage.equals(address.buf), 'caller set correctly') + st.deepEquals(storage, address.bytes, 'caller set correctly') }) t.test('Should forward max call gas when gas set to 0', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) let gas: bigint @@ -475,9 +476,9 @@ tape('EIP-3074 AUTHCALL', (t) => { const gasUsed = await vm.stateManager.getContractStorage( contractStorageAddress, - Buffer.from('00'.repeat(31) + '01', 'hex') + hexToBytes('00'.repeat(31) + '01') ) - const gasBigInt = bufferToBigInt(gasUsed) + const gasBigInt = bytesToBigInt(gasUsed) const preGas = gas! - common.param('gasPrices', 'warmstorageread')! - @@ -487,9 +488,9 @@ tape('EIP-3074 AUTHCALL', (t) => { }) t.test('Should forward max call gas when gas set to 0 - warm account', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, @@ -497,8 +498,8 @@ tape('EIP-3074 AUTHCALL', (t) => { getAuthCallCode({ address: contractStorageAddress, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) let gas: bigint @@ -518,9 +519,9 @@ tape('EIP-3074 AUTHCALL', (t) => { const gasUsed = await vm.stateManager.getContractStorage( contractStorageAddress, - Buffer.from('00'.repeat(31) + '01', 'hex') + hexToBytes('00'.repeat(31) + '01') ) - const gasBigInt = bufferToBigInt(gasUsed) + const gasBigInt = bytesToBigInt(gasUsed) const preGas = gas! - common.param('gasPrices', 'warmstorageread')! const expected = preGas - preGas / 64n - 2n st.equal(gasBigInt, expected, 'forwarded max call gas') @@ -529,16 +530,16 @@ tape('EIP-3074 AUTHCALL', (t) => { t.test( 'Should forward max call gas when gas set to 0 - cold account, nonzero transfer, create new account', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ - address: new Address(Buffer.from('cc'.repeat(20), 'hex')), + address: new Address(hexToBytes('cc'.repeat(20))), value: 1n, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) let gas: bigint @@ -575,16 +576,16 @@ tape('EIP-3074 AUTHCALL', (t) => { t.test( 'Should charge value transfer gas when transferring and transfer from contract, not authcall address', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, value: 1n, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) let gas: bigint @@ -608,9 +609,9 @@ tape('EIP-3074 AUTHCALL', (t) => { const gasUsed = await vm.stateManager.getContractStorage( contractStorageAddress, - Buffer.from('00'.repeat(31) + '01', 'hex') + hexToBytes('00'.repeat(31) + '01') ) - const gasBigInt = bufferToBigInt(gasUsed) + const gasBigInt = bytesToBigInt(gasUsed) const preGas = gas! - common.param('gasPrices', 'warmstorageread')! - @@ -633,12 +634,12 @@ tape('EIP-3074 AUTHCALL', (t) => { ) t.test('Should throw if AUTH not set', async (st) => { - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCallCode({ address: contractStorageAddress, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -657,14 +658,14 @@ tape('EIP-3074 AUTHCALL', (t) => { }) t.test('Should unset AUTH in case of invalid signature', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) const signature2 = { v: signature.v, r: signature.s, s: signature.s, } - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, @@ -673,8 +674,8 @@ tape('EIP-3074 AUTHCALL', (t) => { getAuthCallCode({ address: contractStorageAddress, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -693,16 +694,16 @@ tape('EIP-3074 AUTHCALL', (t) => { }) t.test('Should throw if not enough gas is available', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, gasLimit: 10000000n, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -717,16 +718,16 @@ tape('EIP-3074 AUTHCALL', (t) => { }) t.test('Should throw if valueExt is nonzero', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, valueExt: 1n, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -745,16 +746,16 @@ tape('EIP-3074 AUTHCALL', (t) => { }) t.test('Should forward the right amount of gas', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const code = Buffer.concat([ + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), getAuthCallCode({ address: contractStorageAddress, gasLimit: 700000n, }), - RETURNTOP, - ]) + RETURNTOP + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -766,19 +767,19 @@ tape('EIP-3074 AUTHCALL', (t) => { await vm.runTx({ tx, block, skipHardForkValidation: true }) const gas = await vm.stateManager.getContractStorage( contractStorageAddress, - Buffer.from('00'.repeat(31) + '01', 'hex') + hexToBytes('00'.repeat(31) + '01') ) - const gasBigInt = bufferToBigInt(gas) + const gasBigInt = bytesToBigInt(gas) st.equals(gasBigInt, BigInt(700000 - 2), 'forwarded the right amount of gas') // The 2 is subtracted due to the GAS opcode base fee }) t.test('Should set input and output correctly', async (st) => { - const message = Buffer.from('01', 'hex') + const message = hexToBytes('01') const signature = signMessage(message, contractAddress, privateKey) - const input = Buffer.from('aa'.repeat(32), 'hex') - const code = Buffer.concat([ + const input = hexToBytes('aa'.repeat(32)) + const code = concatBytesNoTypeCheck( getAuthCode(message, signature, authAddress), - MSTORE(Buffer.from('20', 'hex'), input), + MSTORE(hexToBytes('20'), input), getAuthCallCode({ address: contractStorageAddress, argsOffset: 32n, @@ -786,8 +787,8 @@ tape('EIP-3074 AUTHCALL', (t) => { retOffset: 64n, retLength: 32n, }), - Buffer.from('60206040F3', 'hex'), // PUSH 32 PUSH 64 RETURN -> This returns the 32 bytes at memory position 64 - ]) + hexToBytes('60206040F3') // PUSH 32 PUSH 64 RETURN -> This returns the 32 bytes at memory position 64 + ) const vm = await setupVM(code) const tx = Transaction.fromTxData({ @@ -799,9 +800,9 @@ tape('EIP-3074 AUTHCALL', (t) => { const result = await vm.runTx({ tx, block, skipHardForkValidation: true }) const callInput = await vm.stateManager.getContractStorage( contractStorageAddress, - Buffer.from('00'.repeat(31) + '02', 'hex') + hexToBytes('00'.repeat(31) + '02') ) - st.ok(callInput.equals(input), 'authcall input ok') - st.ok(result.execResult.returnValue.equals(input), 'authcall output ok') + st.deepEquals(callInput, input, 'authcall input ok') + st.deepEquals(result.execResult.returnValue, input, 'authcall output ok') }) }) diff --git a/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts b/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts index 15327409ccb..2cc2ef86118 100644 --- a/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3198-BaseFee.spec.ts @@ -2,6 +2,7 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, privateToAddress } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -30,8 +31,8 @@ common.hardforkBlock = function (hardfork: string | undefined) { return BigInt(0) } -const coinbase = new Address(Buffer.from('11'.repeat(20), 'hex')) -const pkey = Buffer.from('20'.repeat(32), 'hex') +const coinbase = new Address(hexToBytes('11'.repeat(20))) +const pkey = hexToBytes('20'.repeat(32)) const sender = new Address(privateToAddress(pkey)) /** diff --git a/packages/vm/test/api/EIPs/eip-3529.spec.ts b/packages/vm/test/api/EIPs/eip-3529.spec.ts index 17a17638875..2e221a0c5c0 100644 --- a/packages/vm/test/api/EIPs/eip-3529.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3529.spec.ts @@ -1,14 +1,15 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' import { Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' import type { InterpreterStep } from '@ethereumjs/evm/dist/interpreter' -const address = new Address(Buffer.from('11'.repeat(20), 'hex')) -const pkey = Buffer.from('20'.repeat(32), 'hex') +const address = new Address(hexToBytes('11'.repeat(20))) +const pkey = hexToBytes('20'.repeat(32)) const testCases = [ { @@ -125,19 +126,19 @@ tape('EIP-3529 tests', (t) => { }) const gasLimit = BigInt(100000) - const key = Buffer.from('00'.repeat(32), 'hex') + const key = hexToBytes('00'.repeat(32)) for (const testCase of testCases) { - const code = Buffer.from((testCase.code + '00').slice(2), 'hex') // add a STOP opcode (0 gas) so we can find the gas used / effective gas + const code = hexToBytes((testCase.code + '00').slice(2)) // add a STOP opcode (0 gas) so we can find the gas used / effective gas await vm.stateManager.putContractStorage( address, key, - Buffer.from(testCase.original.toString().padStart(64, '0'), 'hex') + hexToBytes(testCase.original.toString().padStart(64, '0')) ) await vm.stateManager.getContractStorage(address, key) - vm.eei.addWarmedStorage(address.toBuffer(), key) + vm.eei.addWarmedStorage(address.toBytes(), key) await vm.evm.runCode!({ code, @@ -194,14 +195,14 @@ tape('EIP-3529 tests', (t) => { } }) - const address = new Address(Buffer.from('20'.repeat(20), 'hex')) + const address = new Address(hexToBytes('20'.repeat(20))) - const value = Buffer.from('01'.repeat(32), 'hex') + const value = hexToBytes('01'.repeat(32)) let code = '' for (let i = 0; i < 100; i++) { - const key = Buffer.from(i.toString(16).padStart(64, '0'), 'hex') + const key = hexToBytes(i.toString(16).padStart(64, '0')) await vm.stateManager.putContractStorage(address, key, value) const hex = i.toString(16).padStart(2, '0') // push 0 push sstore @@ -210,7 +211,7 @@ tape('EIP-3529 tests', (t) => { code += '00' - await vm.stateManager.putContractCode(address, Buffer.from(code, 'hex')) + await vm.stateManager.putContractCode(address, hexToBytes(code)) const tx = Transaction.fromTxData({ to: address, diff --git a/packages/vm/test/api/EIPs/eip-3540-evm-object-format.spec.ts b/packages/vm/test/api/EIPs/eip-3540-evm-object-format.spec.ts index 10ca8e17099..1bfc0a075ee 100644 --- a/packages/vm/test/api/EIPs/eip-3540-evm-object-format.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3540-evm-object-format.spec.ts @@ -1,12 +1,13 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EOF } from '@ethereumjs/evm/dist/eof' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Address, privateToAddress } from '@ethereumjs/util' +import { Address, concatBytesNoTypeCheck, privateToAddress } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const GWEI = BigInt('1000000000') const sender = new Address(privateToAddress(pkey)) @@ -31,29 +32,36 @@ tape('EIP 3540 tests', (t) => { }) t.test('EOF > codeAnalysis() tests', async (st) => { - const eofHeader = Buffer.from([EOF.FORMAT, EOF.MAGIC, EOF.VERSION]) + const eofHeader = Uint8Array.from([EOF.FORMAT, EOF.MAGIC, EOF.VERSION]) st.ok( - EOF.codeAnalysis(Buffer.concat([eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00, 0x00])])) - ?.code! > 0, + EOF.codeAnalysis( + concatBytesNoTypeCheck(eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00, 0x00])) + )?.code! > 0, 'valid code section' ) st.ok( EOF.codeAnalysis( - Buffer.concat([ + concatBytesNoTypeCheck( eofHeader, - Uint8Array.from([0x01, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0xaa]), - ]) + Uint8Array.from([0x01, 0x00, 0x01, 0x02, 0x00, 0x01, 0x00, 0x00, 0xaa]) + ) )?.data! > 0, 'valid data section' ) st.ok( - !EOF.codeAnalysis( - Buffer.concat([eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00, 0x00, 0x00])]) + !( + EOF.codeAnalysis( + concatBytesNoTypeCheck(eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00, 0x00, 0x00])) + ) !== undefined ), 'invalid container length (too long)' ) st.ok( - !EOF.codeAnalysis(Buffer.concat([eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00])])), + !( + EOF.codeAnalysis( + concatBytesNoTypeCheck(eofHeader, Uint8Array.from([0x01, 0x00, 0x01, 0x00])) + ) !== undefined + ), 'invalid container length (too short)' ) st.end() diff --git a/packages/vm/test/api/EIPs/eip-3541.spec.ts b/packages/vm/test/api/EIPs/eip-3541.spec.ts index eeee5760721..d66e2b8d2a6 100644 --- a/packages/vm/test/api/EIPs/eip-3541.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3541.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -7,7 +8,7 @@ import { VM } from '../../../src/vm' import type { InterpreterStep } from '@ethereumjs/evm/dist/interpreter' import type { Address } from '@ethereumjs/util' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) tape('EIP 3541 tests', (t) => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin, eips: [3541] }) diff --git a/packages/vm/test/api/EIPs/eip-3607.spec.ts b/packages/vm/test/api/EIPs/eip-3607.spec.ts index b7907ab7197..accea3dadc5 100644 --- a/packages/vm/test/api/EIPs/eip-3607.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3607.spec.ts @@ -12,7 +12,7 @@ tape('EIP-3607 tests', (t) => { t.test('should reject txs from senders with deployed code when EIP is enabled', async (st) => { const vm = await VM.create({ common }) - await vm.stateManager.putContractCode(precompileAddr, Buffer.alloc(32, 1)) + await vm.stateManager.putContractCode(precompileAddr, new Uint8Array(32).fill(1)) const tx = Transaction.fromTxData({ gasLimit: 100000 }, { freeze: false }) tx.getSenderAddress = () => precompileAddr try { @@ -32,7 +32,7 @@ tape('EIP-3607 tests', (t) => { 'should not reject txs from senders with deployed code when EIP is not enabled', async (st) => { const vm = await VM.create({ common: commonNoEIP3607 }) - await vm.stateManager.putContractCode(precompileAddr, Buffer.alloc(32, 1)) + await vm.stateManager.putContractCode(precompileAddr, new Uint8Array(32).fill(1)) const tx = Transaction.fromTxData({ gasLimit: 100000 }, { freeze: false }) tx.getSenderAddress = () => precompileAddr try { diff --git a/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts b/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts index 33ebd2711c0..cfb6338094a 100644 --- a/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3651-warm-coinbase.spec.ts @@ -2,14 +2,15 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { Transaction } from '@ethereumjs/tx' import { Address, privateToAddress } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const GWEI = BigInt(1000000000) const sender = new Address(privateToAddress(pkey)) -const coinbase = new Address(Buffer.from('ff'.repeat(20), 'hex')) +const coinbase = new Address(hexToBytes('ff'.repeat(20))) const common = new Common({ chain: Chain.Mainnet, @@ -27,8 +28,8 @@ const block = Block.fromBlockData( { common } ) -const code = Buffer.from('60008080806001415AF100', 'hex') -const contractAddress = new Address(Buffer.from('ee'.repeat(20), 'hex')) +const code = hexToBytes('60008080806001415AF100') +const contractAddress = new Address(hexToBytes('ee'.repeat(20))) async function getVM(common: Common) { const vm = await VM.create({ common }) diff --git a/packages/vm/test/api/EIPs/eip-3670-eof-code-validation.spec.ts b/packages/vm/test/api/EIPs/eip-3670-eof-code-validation.spec.ts index 01565c4cba3..04ecdb4631c 100644 --- a/packages/vm/test/api/EIPs/eip-3670-eof-code-validation.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3670-eof-code-validation.spec.ts @@ -2,10 +2,11 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EOF } from '@ethereumjs/evm/dist/eof' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, privateToAddress } from '@ethereumjs/util' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const GWEI = BigInt('1000000000') const sender = new Address(privateToAddress(pkey)) @@ -30,28 +31,28 @@ tape('EIP 3670 tests', (t) => { }) t.test('EOF > validOpcodes() tests', (st) => { - st.ok(EOF.validOpcodes(Buffer.from([0])), 'valid -- STOP ') - st.ok(EOF.validOpcodes(Buffer.from([0xfe])), 'valid -- INVALID opcode') - st.ok(EOF.validOpcodes(Buffer.from([0x60, 0xaa, 0])), 'valid - PUSH1 AA STOP') + st.ok(EOF.validOpcodes(Uint8Array.from([0])), 'valid -- STOP ') + st.ok(EOF.validOpcodes(Uint8Array.from([0xfe])), 'valid -- INVALID opcode') + st.ok(EOF.validOpcodes(Uint8Array.from([0x60, 0xaa, 0])), 'valid - PUSH1 AA STOP') for (const opcode of [0x00, 0xf3, 0xfd, 0xfe, 0xff]) { st.ok( - EOF.validOpcodes(Buffer.from([0x60, 0xaa, opcode])), + EOF.validOpcodes(Uint8Array.from([0x60, 0xaa, opcode])), `code ends with valid terminating instruction 0x${opcode.toString(16)}` ) } - st.notOk(EOF.validOpcodes(Buffer.from([0xaa])), 'invalid -- AA -- undefined opcode') + st.notOk(EOF.validOpcodes(Uint8Array.from([0xaa])), 'invalid -- AA -- undefined opcode') st.notOk( - EOF.validOpcodes(Buffer.from([0x7f, 0xaa, 0])), + EOF.validOpcodes(Uint8Array.from([0x7f, 0xaa, 0])), 'invalid -- PUSH32 AA STOP -- truncated push' ) st.notOk( - EOF.validOpcodes(Buffer.from([0x61, 0xaa, 0])), + EOF.validOpcodes(Uint8Array.from([0x61, 0xaa, 0])), 'invalid -- PUSH2 AA STOP -- truncated push' ) st.notOk( - EOF.validOpcodes(Buffer.from([0x60, 0xaa, 0x30])), + EOF.validOpcodes(Uint8Array.from([0x60, 0xaa, 0x30])), 'invalid -- PUSH1 AA ADDRESS -- invalid terminal opcode' ) st.end() @@ -98,31 +99,29 @@ tape('EIP 3670 tests', (t) => { const vm = await VM.create({ common }) // Valid EOF code - const codeValid = Buffer.from( - 'ef000101008102000c006080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b6000602a905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b92915050560048656c6c6f20576f726c6421', - 'hex' + const codeValid = hexToBytes( + 'ef000101008102000c006080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b6000602a905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b92915050560048656c6c6f20576f726c6421' ) // Invalid EOF code: code is exactly the same except the byte at the zero-index is not the FORMAT magic // This thus runs into opcode 0xED which is unassigned and thus invalid - const codeInvalid = Buffer.from( - 'ed000101008102000c006080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b6000602a905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b92915050560048656c6c6f20576f726c6421', - 'hex' + const codeInvalid = hexToBytes( + 'ed000101008102000c006080604052348015600f57600080fd5b506004361060285760003560e01c8063f8a8fd6d14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b6000602a905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b92915050560048656c6c6f20576f726c6421' ) const codes = [codeValid, codeInvalid] const returnValues = [ - Buffer.from('000000000000000000000000000000000000000000000000000000000000002a', 'hex'), - Buffer.from(''), + hexToBytes('000000000000000000000000000000000000000000000000000000000000002a'), + utf8ToBytes(''), ] const expectedErrors = [false, true] let nonce = 0n for (let i = 0; i < codes.length; i++) { - const calldata = Buffer.from('f8a8fd6d', 'hex') + const calldata = hexToBytes('f8a8fd6d') - const addr = new Address(Buffer.from('20'.repeat(20), 'hex')) - const pkey = Buffer.from('42'.repeat(32), 'hex') + const addr = new Address(hexToBytes('20'.repeat(20))) + const pkey = hexToBytes('42'.repeat(32)) const code = codes[i] @@ -150,7 +149,7 @@ tape('EIP 3670 tests', (t) => { const expectReturn = returnValues[i] const expectError = expectedErrors[i] - st.ok(ret.execResult.returnValue.equals(expectReturn), 'return value ok') + st.deepEquals(ret.execResult.returnValue, expectReturn, 'return value ok') if (expectError) { st.ok(ret.execResult.exceptionError !== undefined, 'threw error') } else { diff --git a/packages/vm/test/api/EIPs/eip-3855.spec.ts b/packages/vm/test/api/EIPs/eip-3855.spec.ts index 92ab9a94d61..872ada800a8 100644 --- a/packages/vm/test/api/EIPs/eip-3855.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3855.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { ERROR } from '@ethereumjs/evm/dist/exceptions' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -25,7 +26,7 @@ tape('EIP 3541 tests', (t) => { }) const result = await vm.evm.runCode!({ - code: Buffer.from('5F', 'hex'), + code: hexToBytes('5F'), gasLimit: BigInt(10), }) @@ -45,7 +46,7 @@ tape('EIP 3541 tests', (t) => { const depth = Number(common.param('vm', 'stackLimit')) const result = await vm.evm.runCode!({ - code: Buffer.from('5F'.repeat(depth), 'hex'), + code: hexToBytes('5F'.repeat(depth)), gasLimit: BigInt(10000), }) @@ -65,7 +66,7 @@ tape('EIP 3541 tests', (t) => { const depth = Number(common.param('vm', 'stackLimit')!) + 1 const result = await vm.evm.runCode!({ - code: Buffer.from('5F'.repeat(depth), 'hex'), + code: hexToBytes('5F'.repeat(depth)), gasLimit: BigInt(10000), }) @@ -77,7 +78,7 @@ tape('EIP 3541 tests', (t) => { const vm = await VM.create({ common: commonNoEIP3855 }) const result = await vm.evm.runCode!({ - code: Buffer.from('5F', 'hex'), + code: hexToBytes('5F'), gasLimit: BigInt(10000), }) diff --git a/packages/vm/test/api/EIPs/eip-3860.spec.ts b/packages/vm/test/api/EIPs/eip-3860.spec.ts index f5fb84f88ef..12d138381b8 100644 --- a/packages/vm/test/api/EIPs/eip-3860.spec.ts +++ b/packages/vm/test/api/EIPs/eip-3860.spec.ts @@ -1,10 +1,11 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, privateToAddress } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const GWEI = BigInt('1000000000') const sender = new Address(privateToAddress(pkey)) @@ -22,11 +23,11 @@ tape('EIP 3860 tests', (t) => { account.balance = balance await vm.stateManager.putAccount(sender, account) - const buffer = Buffer.allocUnsafe(1000000).fill(0x60) + const bytes = new Uint8Array(1000000).fill(0x60) const tx = FeeMarketEIP1559Transaction.fromTxData({ data: '0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3' + - buffer.toString('hex'), + bytesToHex(bytes), gasLimit: 100000000000, maxFeePerGas: 7, nonce: 0, diff --git a/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts b/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts index a0ef7ce9c2b..6e47a039b09 100644 --- a/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4399-supplant-difficulty-opcode-with-prevrando.spec.ts @@ -1,6 +1,7 @@ import { Block } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { bufferToBigInt } from '@ethereumjs/util' +import { bytesToBigInt } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -33,14 +34,14 @@ tape('EIP-4399 -> 0x44 (DIFFICULTY) should return PREVRANDAO', (t) => { }) const runCodeArgs = { - code: Buffer.from('4400', 'hex'), + code: hexToBytes('4400'), gasLimit: BigInt(0xffff), } await vm.evm.runCode!({ ...runCodeArgs, block }) st.equal(stack[0], block.header.difficulty, '0x44 returns DIFFICULTY (London)') common.setHardfork(Hardfork.Merge) - const prevRandao = bufferToBigInt(Buffer.alloc(32, 1)) + const prevRandao = bytesToBigInt(new Uint8Array(32).fill(1)) block = Block.fromBlockData( { header: { diff --git a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts index 72b7ef30d8a..06ce01077eb 100644 --- a/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts +++ b/packages/vm/test/api/EIPs/eip-4895-withdrawals.spec.ts @@ -4,12 +4,13 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { decode } from '@ethereumjs/rlp' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { Address, GWEI_TO_WEI, KECCAK256_RLP, Withdrawal, zeros } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import genesisJSON = require('../../../../client/test/testdata/geth-genesis/withdrawals.json') import { VM } from '../../../src/vm' -import type { WithdrawalBuffer, WithdrawalData } from '@ethereumjs/util' +import type { WithdrawalBytes, WithdrawalData } from '@ethereumjs/util' const common = new Common({ chain: Chain.Mainnet, @@ -17,7 +18,7 @@ const common = new Common({ eips: [4895], }) -const pkey = Buffer.from('20'.repeat(32), 'hex') +const pkey = hexToBytes('20'.repeat(32)) const gethWithdrawals8BlockRlp = 'f903e1f90213a0fe950635b1bd2a416ff6283b0bbd30176e1b1125ad06fa729da9f3f4c1c61710a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794aa00000000000000000000000000000000000000a07f7510a0cb6203f456e34ec3e2ce30d6c5590ded42c10a9cf3f24784119c5afba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080018401c9c380802f80a0ff0000000000000000000000000000000000000000000000000000000000000088000000000000000007a0b695b29ec7ee934ef6a68838b13729f2d49fffe26718de16a1a9ed94a4d7d06dc0c0f901c6da8082ffff94000000000000000000000000000000000000000080f83b0183010000940100000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f83b0283010001940200000000000000000000000000000000000000a00200000000000000000000000000000000000000000000000000000000000000f83b0383010002940300000000000000000000000000000000000000a00300000000000000000000000000000000000000000000000000000000000000f83b0483010003940400000000000000000000000000000000000000a00400000000000000000000000000000000000000000000000000000000000000f83b0583010004940500000000000000000000000000000000000000a00500000000000000000000000000000000000000000000000000000000000000f83b0683010005940600000000000000000000000000000000000000a00600000000000000000000000000000000000000000000000000000000000000f83b0783010006940700000000000000000000000000000000000000a00700000000000000000000000000000000000000000000000000000000000000' @@ -35,12 +36,12 @@ tape('EIP4895 tests', (t) => { SSTORE If code is ran, this stores "2" at slot "0". Check if withdrawal operations do not invoke this code */ - const withdrawalCheckAddress = new Address(Buffer.from('fe'.repeat(20), 'hex')) - const withdrawalCode = Buffer.from('6002600055') + const withdrawalCheckAddress = new Address(hexToBytes('fe'.repeat(20))) + const withdrawalCode = hexToBytes('6002600055') await vm.stateManager.putContractCode(withdrawalCheckAddress, withdrawalCode) - const contractAddress = new Address(Buffer.from('ff'.repeat(20), 'hex')) + const contractAddress = new Address(hexToBytes('ff'.repeat(20))) /* PUSH @@ -52,7 +53,7 @@ tape('EIP4895 tests', (t) => { RETURN // Return the balance */ const contract = '73' + addresses[0] + '3160005260206000F3' - await vm.stateManager.putContractCode(contractAddress, Buffer.from(contract, 'hex')) + await vm.stateManager.putContractCode(contractAddress, hexToBytes(contract)) const transaction = FeeMarketEIP1559Transaction.fromTxData({ to: contractAddress, @@ -71,7 +72,7 @@ tape('EIP4895 tests', (t) => { withdrawals.push({ index, validatorIndex: index, - address: new Address(Buffer.from(addresses[i], 'hex')), + address: new Address(hexToBytes(addresses[i])), amount: amounts[i], }) index++ @@ -80,13 +81,11 @@ tape('EIP4895 tests', (t) => { { header: { baseFeePerGas: BigInt(7), - withdrawalsRoot: Buffer.from( - '267414525d22e2be123b619719b92c561f31e0cdd40959148230f5713aecd6b8', - 'hex' + withdrawalsRoot: hexToBytes( + '267414525d22e2be123b619719b92c561f31e0cdd40959148230f5713aecd6b8' ), - transactionsTrie: Buffer.from( - '9a744e8acc2886e5809ff013e3b71bf8ec97f9941cafbd7730834fc8f76391ba', - 'hex' + transactionsTrie: hexToBytes( + '9a744e8acc2886e5809ff013e3b71bf8ec97f9941cafbd7730834fc8f76391ba' ), }, transactions: [transaction], @@ -95,7 +94,7 @@ tape('EIP4895 tests', (t) => { { common: vm._common } ) - let result: Buffer + let result: Uint8Array vm.events.on('afterTx', (e) => { result = e.execResult.returnValue }) @@ -103,31 +102,31 @@ tape('EIP4895 tests', (t) => { await vm.runBlock({ block, generate: true }) for (let i = 0; i < addresses.length; i++) { - const address = new Address(Buffer.from(addresses[i], 'hex')) + const address = new Address(hexToBytes(addresses[i])) const amount = amounts[i] const balance = (await vm.stateManager.getAccount(address)).balance st.equals(BigInt(amount) * GWEI_TO_WEI, balance, 'balance ok') } - st.ok(zeros(32).equals(result!), 'withdrawals happen after transactions') + st.deepEquals(zeros(32), result!, 'withdrawals happen after transactions') const slotValue = await vm.stateManager.getContractStorage(withdrawalCheckAddress, zeros(32)) - st.ok(zeros(0).equals(slotValue), 'withdrawals do not invoke code') + st.deepEquals(zeros(0), slotValue, 'withdrawals do not invoke code') }) t.test('EIP4895: state updation should exclude 0 amount updates', async (st) => { const vm = await VM.create({ common }) await vm.eei.generateCanonicalGenesis(parseGethGenesisState(genesisJSON)) - const preState = (await vm.eei.getStateRoot()).toString('hex') + const preState = bytesToHex(await vm.eei.getStateRoot()) st.equal( preState, 'ca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45', 'preState should be correct' ) - const gethBlockBufferArray = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex')) - const withdrawals = (gethBlockBufferArray[3] as WithdrawalBuffer[]).map((wa) => + const gethBlockBufferArray = decode(hexToBytes(gethWithdrawals8BlockRlp)) + const withdrawals = (gethBlockBufferArray[3] as WithdrawalBytes[]).map((wa) => Withdrawal.fromValuesArray(wa) ) st.equal(withdrawals[0].amount, BigInt(0), 'withdrawal 0 should have 0 amount') @@ -147,7 +146,7 @@ tape('EIP4895 tests', (t) => { }, { common: vm._common } ) - postState = (await vm.eei.getStateRoot()).toString('hex') + postState = bytesToHex(await vm.eei.getStateRoot()) await vm.runBlock({ block, generate: true }) st.equal( @@ -170,7 +169,7 @@ tape('EIP4895 tests', (t) => { { common: vm._common } ) await vm.runBlock({ block, generate: true }) - postState = (await vm.eei.getStateRoot()).toString('hex') + postState = bytesToHex(await vm.eei.getStateRoot()) st.equal( postState, '23eadd91fca55c0e14034e4d63b2b3ed43f2e807b6bf4d276b784ac245e7fa3f', @@ -192,7 +191,7 @@ tape('EIP4895 tests', (t) => { }) const genesisBlock = blockchain.genesisBlock st.equal( - genesisBlock.header.stateRoot.toString('hex'), + bytesToHex(genesisBlock.header.stateRoot), 'ca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45', 'correct state root should be generated' ) @@ -200,8 +199,8 @@ tape('EIP4895 tests', (t) => { await vm.eei.generateCanonicalGenesis(parseGethGenesisState(genesisJSON)) const vmCopy = await vm.copy() - const gethBlockBufferArray = decode(Buffer.from(gethWithdrawals8BlockRlp, 'hex')) - const withdrawals = (gethBlockBufferArray[3] as WithdrawalBuffer[]).map((wa) => + const gethBlockBufferArray = decode(hexToBytes(gethWithdrawals8BlockRlp)) + const withdrawals = (gethBlockBufferArray[3] as WithdrawalBytes[]).map((wa) => Withdrawal.fromValuesArray(wa) ) const td = await blockchain.getTotalDifficulty(genesisBlock.hash()) @@ -219,7 +218,7 @@ tape('EIP4895 tests', (t) => { const block = await blockBuilder.build() st.equal( - block.header.stateRoot.toString('hex'), + bytesToHex(block.header.stateRoot), '23eadd91fca55c0e14034e4d63b2b3ed43f2e807b6bf4d276b784ac245e7fa3f', 'correct state root should be generated' ) @@ -227,9 +226,9 @@ tape('EIP4895 tests', (t) => { // block should successfully execute with VM.runBlock and have same outputs const result = await vmCopy.runBlock({ block }) st.equal(result.gasUsed, block.header.gasUsed) - st.ok(result.receiptsRoot.equals(block.header.receiptTrie)) - st.ok(result.stateRoot.equals(block.header.stateRoot)) - st.ok(result.logsBloom.equals(block.header.logsBloom)) + st.deepEquals(result.receiptsRoot, block.header.receiptTrie) + st.deepEquals(result.stateRoot, block.header.stateRoot) + st.deepEquals(result.logsBloom, block.header.logsBloom) st.end() }) }) diff --git a/packages/vm/test/api/bloom.spec.ts b/packages/vm/test/api/bloom.spec.ts index cc00e597083..32fd2a1186d 100644 --- a/packages/vm/test/api/bloom.spec.ts +++ b/packages/vm/test/api/bloom.spec.ts @@ -1,4 +1,5 @@ import * as utils from '@ethereumjs/util' +import { bytesToHex, hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { Bloom } from '../../src/bloom' @@ -24,65 +25,56 @@ tape('bloom', (t: tape.Test) => { t.test('should contain values of hardcoded bitvector', (st) => { const hex = '00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000' - const vector = Buffer.from(hex, 'hex') + const vector = hexToBytes(hex) const b = new Bloom(vector) - st.true(b.check(Buffer.from('value 1', 'utf8')), 'should contain string "value 1"') - st.true(b.check(Buffer.from('value 2', 'utf8')), 'should contain string "value 2"') + st.true(b.check(utf8ToBytes('value 1')), 'should contain string "value 1"') + st.true(b.check(utf8ToBytes('value 2')), 'should contain string "value 2"') st.end() }) t.test('check shouldnt be tautology', (st) => { const b = new Bloom() - st.false( - b.check(Buffer.from('random value', 'utf8')), - 'should not contain string "random value"' - ) + st.false(b.check(utf8ToBytes('random value')), 'should not contain string "random value"') st.end() }) t.test('should correctly add value', (st) => { const b = new Bloom() - b.add(Buffer.from('value', 'utf8')) - const found = b.check(Buffer.from('value', 'utf8')) + b.add(utf8ToBytes('value')) + const found = b.check(utf8ToBytes('value')) st.true(found, 'should contain added value') st.end() }) t.test('should check multiple values', (st) => { const b = new Bloom() - b.add(Buffer.from('value 1', 'utf8')) - b.add(Buffer.from('value 2', 'utf8')) - const found = b.multiCheck([Buffer.from('value 1'), Buffer.from('value 2')]) + b.add(utf8ToBytes('value 1')) + b.add(utf8ToBytes('value 2')) + const found = b.multiCheck([utf8ToBytes('value 1'), utf8ToBytes('value 2')]) st.true(found, 'should contain both values') st.end() }) t.test('should or two filters', (st) => { const b1 = new Bloom() - b1.add(Buffer.from('value 1', 'utf8')) + b1.add(utf8ToBytes('value 1')) const b2 = new Bloom() - b2.add(Buffer.from('value 2', 'utf8')) + b2.add(utf8ToBytes('value 2')) b1.or(b2) - st.true(b1.check(Buffer.from('value 2', 'utf-8')), 'should contain "value 2" after or') + st.true(b1.check(utf8ToBytes('value 2')), 'should contain "value 2" after or') st.end() }) t.test('should generate the correct bloom filter value', (st) => { const bloom = new Bloom() - bloom.add(Buffer.from('1d7022f5b17d2f8b695918fb48fa1089c9f85401', 'hex')) - bloom.add( - Buffer.from('8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925', 'hex') - ) - bloom.add( - Buffer.from('0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631', 'hex') - ) - bloom.add( - Buffer.from('0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48', 'hex') - ) + bloom.add(hexToBytes('1d7022f5b17d2f8b695918fb48fa1089c9f85401')) + bloom.add(hexToBytes('8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925')) + bloom.add(hexToBytes('0000000000000000000000005409ed021d9299bf6814279a6a1411a7e866a631')) + bloom.add(hexToBytes('0000000000000000000000001dc4c1cefef38a777b15aa20260a54e584b16c48')) st.equal( - bloom.bitvector.toString('hex'), + bytesToHex(bloom.bitvector), '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000081100200000000000000000000000000000000000000000000000000000000008000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000002000000000000000004000000000000000000000' ) st.end() diff --git a/packages/vm/test/api/buildBlock.spec.ts b/packages/vm/test/api/buildBlock.spec.ts index 0c45d30fdb2..aa2420a4526 100644 --- a/packages/vm/test/api/buildBlock.spec.ts +++ b/packages/vm/test/api/buildBlock.spec.ts @@ -2,7 +2,8 @@ import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' -import { Account, Address } from '@ethereumjs/util' +import { Account, Address, concatBytesNoTypeCheck } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../src/vm' @@ -50,9 +51,9 @@ tape('BlockBuilder', async (t) => { } const result = await vmCopy.runBlock({ block }) st.equal(result.gasUsed, block.header.gasUsed) - st.ok(result.receiptsRoot.equals(block.header.receiptTrie)) - st.ok(result.stateRoot.equals(block.header.stateRoot)) - st.ok(result.logsBloom.equals(block.header.logsBloom)) + st.deepEquals(result.receiptsRoot, block.header.receiptTrie) + st.deepEquals(result.stateRoot, block.header.stateRoot) + st.deepEquals(result.logsBloom, block.header.logsBloom) st.end() }) @@ -112,33 +113,33 @@ tape('BlockBuilder', async (t) => { await blockBuilder.addTransaction(tx) const sealOpts = { - mixHash: Buffer.alloc(32), - nonce: Buffer.alloc(8), + mixHash: new Uint8Array(32), + nonce: new Uint8Array(8), } const block = await blockBuilder.build(sealOpts) - st.ok(block.header.mixHash.equals(sealOpts.mixHash)) - st.ok(block.header.nonce.equals(sealOpts.nonce)) + st.deepEquals(block.header.mixHash, sealOpts.mixHash) + st.deepEquals(block.header.nonce, sealOpts.nonce) st.doesNotThrow(async () => vm.blockchain.consensus.validateDifficulty(block.header)) st.end() }) t.test('should correctly seal a PoA block', async (st) => { const signer = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' - ), - publicKey: Buffer.from( - '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195', - 'hex' + address: new Address(hexToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), + publicKey: hexToBytes( + '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195' ), } const common = new Common({ chain: Chain.Rinkeby, hardfork: Hardfork.Istanbul }) // extraData: [vanity, activeSigner, seal] - const extraData = Buffer.concat([Buffer.alloc(32), signer.address.toBuffer(), Buffer.alloc(65)]) + const extraData = concatBytesNoTypeCheck( + new Uint8Array(32), + signer.address.toBytes(), + new Uint8Array(65) + ) const cliqueSigner = signer.privateKey const genesisBlock = Block.fromBlockData( { header: { gasLimit: 50000, extraData } }, @@ -152,7 +153,7 @@ tape('BlockBuilder', async (t) => { const blockBuilder = await vm.buildBlock({ parentBlock: genesisBlock, - headerData: { difficulty: 2, extraData: Buffer.alloc(97) }, + headerData: { difficulty: 2, extraData: new Uint8Array(97) }, blockOpts: { cliqueSigner, freeze: false }, }) @@ -167,8 +168,9 @@ tape('BlockBuilder', async (t) => { const block = await blockBuilder.build() st.ok(block.header.cliqueVerifySignature([signer.address]), 'should verify signature') - st.ok( - block.header.cliqueSigner().equals(signer.address), + st.deepEquals( + block.header.cliqueSigner(), + signer.address, 'should recover the correct signer address' ) st.end() @@ -246,9 +248,9 @@ tape('BlockBuilder', async (t) => { // block should successfully execute with VM.runBlock and have same outputs const result = await vmCopy.runBlock({ block }) st.equal(result.gasUsed, block.header.gasUsed) - st.ok(result.receiptsRoot.equals(block.header.receiptTrie)) - st.ok(result.stateRoot.equals(block.header.stateRoot)) - st.ok(result.logsBloom.equals(block.header.logsBloom)) + st.deepEquals(result.receiptsRoot, block.header.receiptTrie) + st.deepEquals(result.stateRoot, block.header.stateRoot) + st.deepEquals(result.logsBloom, block.header.logsBloom) st.end() }) @@ -342,9 +344,9 @@ tape('BlockBuilder', async (t) => { } const result = await vmCopy.runBlock({ block }) st.equal(result.gasUsed, block.header.gasUsed) - st.ok(result.receiptsRoot.equals(block.header.receiptTrie)) - st.ok(result.stateRoot.equals(block.header.stateRoot)) - st.ok(result.logsBloom.equals(block.header.logsBloom)) + st.deepEquals(result.receiptsRoot, block.header.receiptTrie) + st.deepEquals(result.stateRoot, block.header.stateRoot) + st.deepEquals(result.logsBloom, block.header.logsBloom) st.end() }) }) diff --git a/packages/vm/test/api/customChain.spec.ts b/packages/vm/test/api/customChain.spec.ts index 4188003a583..f95a689a96a 100644 --- a/packages/vm/test/api/customChain.spec.ts +++ b/packages/vm/test/api/customChain.spec.ts @@ -4,6 +4,7 @@ import { Common, Hardfork } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { Address } from '@ethereumjs/util' import { Interface } from '@ethersproject/abi' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../src/vm' @@ -57,10 +58,7 @@ const block = Block.fromBlockData( common, } ) -const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' -) +const privateKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') tape('VM initialized with custom state', (t) => { t.test('should transfer eth from already existent account', async (t) => { @@ -99,14 +97,14 @@ tape('VM initialized with custom state', (t) => { const callResult = await vm.evm.runCall({ to: Address.fromString(contractAddress), - data: Buffer.from(sigHash.slice(2), 'hex'), + data: hexToBytes(sigHash.slice(2)), caller: Address.fromPrivateKey(privateKey), }) const storage = genesisState[contractAddress][2] // Returned value should be 4, because we are trying to trigger the method `retrieve` // in the contract, which returns the variable stored in slot 0x00..00 - t.equal(callResult.execResult.returnValue.toString('hex'), storage[0][1].slice(2)) + t.equal(bytesToHex(callResult.execResult.returnValue), storage[0][1].slice(2)) t.end() }) diff --git a/packages/vm/test/api/eei.spec.ts b/packages/vm/test/api/eei.spec.ts index 2fbbc803e5c..c8cb1184ed8 100644 --- a/packages/vm/test/api/eei.spec.ts +++ b/packages/vm/test/api/eei.spec.ts @@ -3,6 +3,7 @@ import { Common } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { DefaultStateManager as StateManager } from '@ethereumjs/statemanager' import { Account, Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../src' @@ -100,7 +101,7 @@ tape('EEI', (t) => { ) } - const address = new Address(Buffer.from('02E815899482f27C899fB266319dE7cc97F72E87', 'hex')) + const address = new Address(hexToBytes('02E815899482f27C899fB266319dE7cc97F72E87')) void eei.putAccount(address, Account.fromAccountData({ nonce: 5, balance: '0x123' })) const vm = await VM.create({ evm }) const accountFromEEI = await vm.eei.getAccount(address) diff --git a/packages/vm/test/api/events.spec.ts b/packages/vm/test/api/events.spec.ts index 139daab38e2..831f5d8920d 100644 --- a/packages/vm/test/api/events.spec.ts +++ b/packages/vm/test/api/events.spec.ts @@ -1,12 +1,12 @@ import { Block } from '@ethereumjs/block' import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' -import { Account, Address, bufferToHex, toBuffer } from '@ethereumjs/util' +import { Account, Address, bytesToPrefixedHexString, toBytes } from '@ethereumjs/util' import * as tape from 'tape' import { VM } from '../../src/vm' tape('VM events', (t) => { - const privKey = toBuffer('0xa5737ecdc1b89ca0091647e727ba082ed8953f29182e94adc397210dda643b07') + const privKey = toBytes('0xa5737ecdc1b89ca0091647e727ba082ed8953f29182e94adc397210dda643b07') t.test('should emit the Block before running it', async (st) => { const vm = await VM.create() @@ -90,7 +90,7 @@ tape('VM events', (t) => { await vm.runTx({ tx, skipBalance: true, skipHardForkValidation: true }) - st.equal(bufferToHex(emitted.execResult.returnValue), '0x') + st.equal(bytesToPrefixedHexString(emitted.execResult.returnValue), '0x') st.end() }) @@ -113,8 +113,8 @@ tape('VM events', (t) => { await vm.runTx({ tx, skipBalance: true, skipHardForkValidation: true }) - st.equal(bufferToHex(emitted.to), '0x1111111111111111111111111111111111111111') - st.equal(bufferToHex(emitted.code), '0x') + st.equal(emitted.to.toString(), '0x1111111111111111111111111111111111111111') + st.equal(bytesToPrefixedHexString(emitted.code), '0x') st.end() }) @@ -137,7 +137,7 @@ tape('VM events', (t) => { await vm.runTx({ tx, skipBalance: true, skipHardForkValidation: true }) - st.equal(bufferToHex(emitted.createdAddress), '0x') + st.equal(bytesToPrefixedHexString(emitted.createdAddress), '0x') st.end() }) @@ -186,7 +186,7 @@ tape('VM events', (t) => { await vm.runTx({ tx, skipBalance: true, skipHardForkValidation: true }) st.equal( - bufferToHex(emitted.code), + bytesToPrefixedHexString(emitted.code), '0x7f410000000000000000000000000000000000000000000000000000000000000060005260016000f3' ) diff --git a/packages/vm/test/api/index.spec.ts b/packages/vm/test/api/index.spec.ts index a4bb1c1bbac..61f7b7cb728 100644 --- a/packages/vm/test/api/index.spec.ts +++ b/packages/vm/test/api/index.spec.ts @@ -3,7 +3,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { EVM } from '@ethereumjs/evm' import { Account, Address, KECCAK256_RLP } from '@ethereumjs/util' -import { Buffer } from 'buffer' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import * as util from 'util' // eslint-disable-line @typescript-eslint/no-unused-vars @@ -255,10 +255,8 @@ tape('VM -> hardforkByBlockNumber, hardforkByTTD, state (deprecated), blockchain }) tape('Ensure that precompile activation creates non-empty accounts', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('00000000000000000000000000000000000000ee', 'hex')) // caller address - const contractAddress = new Address( - Buffer.from('00000000000000000000000000000000000000ff', 'hex') - ) // contract address + const caller = new Address(hexToBytes('00000000000000000000000000000000000000ee')) // caller address + const contractAddress = new Address(hexToBytes('00000000000000000000000000000000000000ff')) // contract address // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const vmNotActivated = await VM.create({ common }) @@ -279,9 +277,9 @@ tape('VM -> hardforkByBlockNumber, hardforkByTTD, state (deprecated), blockchain STOP */ - await vmNotActivated.stateManager.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await vmNotActivated.stateManager.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code await vmNotActivated.stateManager.putAccount(caller, new Account(BigInt(0), BigInt(0x111))) // give calling account a positive balance - await vmActivated.stateManager.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await vmActivated.stateManager.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code await vmActivated.stateManager.putAccount(caller, new Account(BigInt(0), BigInt(0x111))) // give calling account a positive balance // setup the call arguments const runCallArgs = { diff --git a/packages/vm/test/api/istanbul/eip-1108.spec.ts b/packages/vm/test/api/istanbul/eip-1108.spec.ts index 459f1a0c5cd..9d235228e40 100644 --- a/packages/vm/test/api/istanbul/eip-1108.spec.ts +++ b/packages/vm/test/api/istanbul/eip-1108.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { getActivePrecompiles } from '@ethereumjs/evm' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -12,7 +13,7 @@ tape('Istanbul: EIP-1108 tests', (t) => { const ECADD = getActivePrecompiles(common).get(address)! const result = await ECADD({ - data: Buffer.alloc(0), + data: new Uint8Array(0), gasLimit: BigInt(0xffff), _common: common, _EVM: vm.evm, @@ -29,7 +30,7 @@ tape('Istanbul: EIP-1108 tests', (t) => { const ECMUL = getActivePrecompiles(common).get(address)! const result = await ECMUL({ - data: Buffer.alloc(0), + data: new Uint8Array(0), gasLimit: BigInt(0xffff), _common: common, _EVM: vm.evm, @@ -46,9 +47,8 @@ tape('Istanbul: EIP-1108 tests', (t) => { const ECPAIRING = getActivePrecompiles(common).get(address)! const result = await ECPAIRING({ - data: Buffer.from( - '00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa', - 'hex' + data: hexToBytes( + '00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa' ), gasLimit: BigInt(0xffffff), _common: common, diff --git a/packages/vm/test/api/istanbul/eip-1344.spec.ts b/packages/vm/test/api/istanbul/eip-1344.spec.ts index 817ea351541..43ce3df2ab1 100644 --- a/packages/vm/test/api/istanbul/eip-1344.spec.ts +++ b/packages/vm/test/api/istanbul/eip-1344.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { ERROR } from '@ethereumjs/evm/dist/exceptions' -import { bufferToBigInt } from '@ethereumjs/util' +import { bytesToBigInt } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -17,7 +18,7 @@ const code = ['46', '60', '00', '53', '60', '01', '60', '00', 'f3'] tape('Istanbul: EIP-1344', async (t) => { t.test('CHAINID', async (st) => { const runCodeArgs = { - code: Buffer.from(code.join(''), 'hex'), + code: hexToBytes(code.join('')), gasLimit: BigInt(0xffff), } @@ -27,11 +28,11 @@ tape('Istanbul: EIP-1344', async (t) => { const vm = await VM.create({ common }) try { const res = await vm.evm.runCode!(runCodeArgs) - if (testCase.err) { + if (testCase.err !== undefined) { st.equal(res.exceptionError?.error, testCase.err) } else { st.assert(res.exceptionError === undefined) - st.equal(testCase.chainId, bufferToBigInt(res.returnValue)) + st.equal(testCase.chainId, bytesToBigInt(res.returnValue)) } } catch (e: any) { st.fail(e.message) diff --git a/packages/vm/test/api/istanbul/eip-152.spec.ts b/packages/vm/test/api/istanbul/eip-152.spec.ts index e568d272551..b2ed8d265b0 100644 --- a/packages/vm/test/api/istanbul/eip-152.spec.ts +++ b/packages/vm/test/api/istanbul/eip-152.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { ERROR } from '@ethereumjs/evm/dist/exceptions' import { F, precompile09 } from '@ethereumjs/evm/dist/precompiles/09-blake2f' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -91,7 +92,7 @@ tape('Istanbul: EIP-152', (t) => { for (const testCase of failingTestCases) { st.comment(testCase.name) const res = precompile09({ - data: Buffer.from(testCase.input, 'hex'), + data: hexToBytes(testCase.input), gasLimit: BigInt(20), _common: common, _EVM: vm.evm, @@ -102,12 +103,12 @@ tape('Istanbul: EIP-152', (t) => { for (const testCase of testCases) { st.comment(testCase.name) const res = precompile09({ - data: Buffer.from(testCase.input, 'hex'), + data: hexToBytes(testCase.input), gasLimit: BigInt(10000000), _common: common, _EVM: vm.evm, }) - st.equal(res.returnValue.toString('hex'), testCase.expected) + st.equal(bytesToHex(res.returnValue), testCase.expected) } st.end() diff --git a/packages/vm/test/api/istanbul/eip-1884.spec.ts b/packages/vm/test/api/istanbul/eip-1884.spec.ts index c0d2e7f2968..6d27f86b55a 100644 --- a/packages/vm/test/api/istanbul/eip-1884.spec.ts +++ b/packages/vm/test/api/istanbul/eip-1884.spec.ts @@ -1,6 +1,7 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' import { ERROR } from '@ethereumjs/evm/dist/exceptions' -import { Address, bufferToBigInt } from '@ethereumjs/util' +import { Address, bytesToBigInt } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -15,9 +16,9 @@ const testCases = [ const code = ['47', '60', '00', '53', '60', '01', '60', '00', 'f3'] tape('Istanbul: EIP-1884', async (t) => { t.test('SELFBALANCE', async (st) => { - const addr = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const addr = new Address(hexToBytes('00000000000000000000000000000000000000ff')) const runCodeArgs = { - code: Buffer.from(code.join(''), 'hex'), + code: hexToBytes(code.join('')), gasLimit: BigInt(0xffff), address: addr, } @@ -34,11 +35,11 @@ tape('Istanbul: EIP-1884', async (t) => { try { const res = await vm.evm.runCode!(runCodeArgs) - if (testCase.err) { + if (testCase.err !== undefined) { st.equal(res.exceptionError?.error, testCase.err) } else { st.assert(res.exceptionError === undefined) - st.assert(BigInt(testCase.selfbalance) === bufferToBigInt(res.returnValue)) + st.assert(BigInt(testCase.selfbalance!) === bytesToBigInt(res.returnValue)) } } catch (e: any) { st.fail(e.message) diff --git a/packages/vm/test/api/istanbul/eip-2200.spec.ts b/packages/vm/test/api/istanbul/eip-2200.spec.ts index 39469661142..e6351a252cd 100644 --- a/packages/vm/test/api/istanbul/eip-2200.spec.ts +++ b/packages/vm/test/api/istanbul/eip-2200.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, setLengthLeft, toBuffer } from '@ethereumjs/util' +import { Address, setLengthLeft, toBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -43,9 +44,9 @@ const testCases = [ tape('Istanbul: EIP-2200', async (t) => { t.test('net-metering SSTORE', async (st) => { - const caller = new Address(Buffer.from('0000000000000000000000000000000000000000', 'hex')) - const addr = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) - const key = setLengthLeft(toBuffer('0x' + BigInt(0).toString(16)), 32) + const caller = new Address(hexToBytes('0000000000000000000000000000000000000000')) + const addr = new Address(hexToBytes('00000000000000000000000000000000000000ff')) + const key = setLengthLeft(toBytes('0x' + BigInt(0).toString(16)), 32) for (const testCase of testCases) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) @@ -53,12 +54,12 @@ tape('Istanbul: EIP-2200', async (t) => { const account = createAccount(BigInt(0), BigInt(0)) await vm.stateManager.putAccount(addr, account) - await vm.stateManager.putContractCode(addr, Buffer.from(testCase.code, 'hex')) + await vm.stateManager.putContractCode(addr, hexToBytes(testCase.code)) if (testCase.original !== BigInt(0)) { await vm.stateManager.putContractStorage( addr, key, - toBuffer('0x' + testCase.original.toString(16)) + toBytes('0x' + testCase.original.toString(16)) ) } diff --git a/packages/vm/test/api/runBlock.spec.ts b/packages/vm/test/api/runBlock.spec.ts index 572380e44c7..fd0edbf23d8 100644 --- a/packages/vm/test/api/runBlock.spec.ts +++ b/packages/vm/test/api/runBlock.spec.ts @@ -7,7 +7,8 @@ import { FeeMarketEIP1559Transaction, Transaction, } from '@ethereumjs/tx' -import { Account, Address, KECCAK256_RLP, toBuffer } from '@ethereumjs/util' +import { Account, Address, KECCAK256_RLP, toBytes } from '@ethereumjs/util' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../src/vm' @@ -30,18 +31,19 @@ const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) tape('runBlock() -> successful API parameter usage', async (t) => { async function simpleRun(vm: VM, st: tape.Test) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const genesisRlp = toBuffer(testData.genesisRLP) + const genesisRlp = toBytes(testData.genesisRLP) const genesis = Block.fromRLPSerializedBlock(genesisRlp, { common }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) //@ts-ignore await setupPreConditions(vm.eei, testData) - st.ok( + st.deepEquals( //@ts-ignore - vm.stateManager._trie.root().equals(genesis.header.stateRoot), + vm.stateManager._trie.root(), + genesis.header.stateRoot, 'genesis state root should match calculated state root' ) @@ -67,7 +69,7 @@ tape('runBlock() -> successful API parameter usage', async (t) => { await setupPreConditions(vm.eei, testData) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const block1Rlp = toBuffer(testData.blocks[0].rlp) + const block1Rlp = toBytes(testData.blocks[0].rlp) const block1 = Block.fromRLPSerializedBlock(block1Rlp, { common }) await vm.runBlock({ block: block1, @@ -77,7 +79,7 @@ tape('runBlock() -> successful API parameter usage', async (t) => { skipHardForkValidation: true, }) - const block2Rlp = toBuffer(testData.blocks[1].rlp) + const block2Rlp = toBytes(testData.blocks[1].rlp) const block2 = Block.fromRLPSerializedBlock(block2Rlp, { common }) await vm.runBlock({ block: block2, @@ -87,7 +89,7 @@ tape('runBlock() -> successful API parameter usage', async (t) => { skipHardForkValidation: true, }) - const block3Rlp = toBuffer(testData.blocks[2].rlp) + const block3Rlp = toBytes(testData.blocks[2].rlp) const block3 = Block.fromRLPSerializedBlock(block3Rlp, { common }) await vm.runBlock({ block: block3, @@ -146,9 +148,8 @@ tape('runBlock() -> successful API parameter usage', async (t) => { hardfork: Hardfork.Chainstart, }) - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const privateKey = hexToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) function getBlock(common: Common): Block { @@ -202,7 +203,7 @@ tape('runBlock() -> API parameter usage/data errors', async (t) => { t.test('should fail when runTx fails', async (t) => { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) // The mocked VM uses a mocked runTx @@ -219,7 +220,7 @@ tape('runBlock() -> API parameter usage/data errors', async (t) => { const block = Block.fromBlockData({ header: { ...testData.blocks[0].header, - gasLimit: Buffer.from('8000000000000000', 'hex'), + gasLimit: hexToBytes('8000000000000000'), }, }) await vm @@ -231,7 +232,7 @@ tape('runBlock() -> API parameter usage/data errors', async (t) => { t.test('should fail when block validation fails', async (t) => { const vm = await VM.create({ common }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) await vm @@ -244,7 +245,7 @@ tape('runBlock() -> API parameter usage/data errors', async (t) => { t.test('should fail when no `validateHeader` method exists on blockchain class', async (t) => { const vm = await VM.create({ common }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) ;(vm.blockchain as any).validateHeader = undefined try { @@ -261,7 +262,7 @@ tape('runBlock() -> API parameter usage/data errors', async (t) => { t.test('should fail when tx gas limit higher than block gas limit', async (t) => { const vm = await VM.create({ common }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Object.create(Block.fromRLPSerializedBlock(blockRlp, { common })) // modify first tx's gasLimit const { nonce, gasPrice, to, value, data, v, r, s } = block.transactions[0] @@ -289,7 +290,7 @@ tape('runBlock() -> runtime behavior', async (t) => { const block1: any = RLP.decode(testData.blocks[0].rlp) // edit extra data of this block to "dao-hard-fork" - block1[0][12] = Buffer.from('dao-hard-fork') + block1[0][12] = utf8ToBytes('dao-hard-fork') const block = Block.fromValuesArray(block1, { common }) // @ts-ignore await setupPreConditions(vm.eei, testData) @@ -298,20 +299,18 @@ tape('runBlock() -> runtime behavior', async (t) => { const fundBalance1 = BigInt('0x1111') const accountFunded1 = createAccount(BigInt(0), fundBalance1) const DAOFundedContractAddress1 = new Address( - Buffer.from('d4fe7bc31cedb7bfb8a345f31e668033056b2728', 'hex') + hexToBytes('d4fe7bc31cedb7bfb8a345f31e668033056b2728') ) await vm.stateManager.putAccount(DAOFundedContractAddress1, accountFunded1) const fundBalance2 = BigInt('0x2222') const accountFunded2 = createAccount(BigInt(0), fundBalance2) const DAOFundedContractAddress2 = new Address( - Buffer.from('b3fb0e5aba0e20e5c49d252dfd30e102b171a425', 'hex') + hexToBytes('b3fb0e5aba0e20e5c49d252dfd30e102b171a425') ) await vm.stateManager.putAccount(DAOFundedContractAddress2, accountFunded2) - const DAORefundAddress = new Address( - Buffer.from('bf4ed7b27f1d666546e30d74d50d173d20bca754', 'hex') - ) + const DAORefundAddress = new Address(hexToBytes('bf4ed7b27f1d666546e30d74d50d173d20bca754')) const fundBalanceRefund = BigInt('0x4444') const accountRefund = createAccount(BigInt(0), fundBalanceRefund) await vm.stateManager.putAccount(DAORefundAddress, accountRefund) @@ -339,26 +338,18 @@ tape('runBlock() -> runtime behavior', async (t) => { const vm = await setupVM({ common }) const signer = { - address: new Address(Buffer.from('0b90087d864e82a284dca15923f3776de6bb016f', 'hex')), - privateKey: Buffer.from( - '64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993', - 'hex' - ), - publicKey: Buffer.from( - '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195', - 'hex' + address: new Address(hexToBytes('0b90087d864e82a284dca15923f3776de6bb016f')), + privateKey: hexToBytes('64bf9cc30328b0e42387b3c82c614e6386259136235e20c1357bd11cdee86993'), + publicKey: hexToBytes( + '40b2ebdf4b53206d2d3d3d59e7e2f13b1ea68305aec71d5d24cefe7f24ecae886d241f9267f04702d7f693655eb7b4aa23f30dcd0c3c5f2b970aad7c8a828195' ), } const otherUser = { - address: new Address(Buffer.from('6f62d8382bf2587361db73ceca28be91b2acb6df', 'hex')), - privateKey: Buffer.from( - '2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6', - 'hex' - ), - publicKey: Buffer.from( - 'ca0a55f6e81cb897aee6a1c390aa83435c41048faa0564b226cfc9f3df48b73e846377fb0fd606df073addc7bd851f22547afbbdd5c3b028c91399df802083a2', - 'hex' + address: new Address(hexToBytes('6f62d8382bf2587361db73ceca28be91b2acb6df')), + privateKey: hexToBytes('2a6e9ad5a6a8e4f17149b8bc7128bf090566a11dbd63c30e5a0ee9f161309cd6'), + publicKey: hexToBytes( + 'ca0a55f6e81cb897aee6a1c390aa83435c41048faa0564b226cfc9f3df48b73e846377fb0fd606df073addc7bd851f22547afbbdd5c3b028c91399df802083a2' ), } @@ -371,7 +362,7 @@ tape('runBlock() -> runtime behavior', async (t) => { // create block with the signer and txs const block = Block.fromBlockData( - { header: { extraData: Buffer.alloc(97) }, transactions: [tx, tx] }, + { header: { extraData: new Uint8Array(97) }, transactions: [tx, tx] }, { common, cliqueSigner: signer.privateKey } ) @@ -409,7 +400,7 @@ tape('should correctly reflect generated fields', async (t) => { // filled with 0s and no txs. Once we run it we should // get a receipt trie root of for the empty receipts set, // which is a well known constant. - const buffer32Zeros = Buffer.alloc(32, 0) + const buffer32Zeros = new Uint8Array(32) const block = Block.fromBlockData({ header: { receiptTrie: buffer32Zeros, transactionsTrie: buffer32Zeros, gasUsed: BigInt(1) }, }) @@ -420,8 +411,8 @@ tape('should correctly reflect generated fields', async (t) => { skipBlockValidation: true, }) - t.ok(results.block.header.receiptTrie.equals(KECCAK256_RLP)) - t.ok(results.block.header.transactionsTrie.equals(KECCAK256_RLP)) + t.deepEquals(results.block.header.receiptTrie, KECCAK256_RLP) + t.deepEquals(results.block.header.transactionsTrie, KECCAK256_RLP) t.equal(results.block.header.gasUsed, BigInt(0)) }) @@ -429,7 +420,7 @@ async function runWithHf(hardfork: string) { const common = new Common({ chain: Chain.Mainnet, hardfork }) const vm = await setupVM({ common }) - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common }) // @ts-ignore @@ -456,7 +447,7 @@ tape('runBlock() -> API return values', async (t) => { res = await runWithHf('spuriousDragon') t.deepEqual( (res.receipts[0] as PreByzantiumTxReceipt).stateRoot, - Buffer.from('4477e2cfaf9fd2eed4f74426798b55d140f6a9612da33413c4745f57d7a97fcc', 'hex'), + hexToBytes('4477e2cfaf9fd2eed4f74426798b55d140f6a9612da33413c4745f57d7a97fcc'), 'should return correct pre-Byzantium receipt format' ) }) @@ -466,7 +457,7 @@ tape('runBlock() -> tx types', async (t) => { async function simpleRun(vm: VM, transactions: TypedTransaction[], st: tape.Test) { const common = vm._common - const blockRlp = toBuffer(testData.blocks[0].rlp) + const blockRlp = toBytes(testData.blocks[0].rlp) const block = Block.fromRLPSerializedBlock(blockRlp, { common, freeze: false }) //@ts-ignore overwrite transactions diff --git a/packages/vm/test/api/runTx.spec.ts b/packages/vm/test/api/runTx.spec.ts index 9c7c76e4b93..0276dc48d9d 100644 --- a/packages/vm/test/api/runTx.spec.ts +++ b/packages/vm/test/api/runTx.spec.ts @@ -8,6 +8,7 @@ import { TransactionFactory, } from '@ethereumjs/tx' import { Account, Address, KECCAK256_NULL, MAX_INTEGER } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../src/vm' @@ -46,7 +47,7 @@ tape('runTx() -> successful API parameter usage', async (t) => { if (vm._common.consensusType() === 'poa') { // Setup block with correct extraData for POA block = Block.fromBlockData( - { header: { extraData: Buffer.alloc(97) } }, + { header: { extraData: new Uint8Array(97) } }, { common: vm._common } ) } @@ -201,9 +202,8 @@ tape('runTx() -> successful API parameter usage', async (t) => { for (const txType of TRANSACTION_TYPES) { const vm = await VM.create({ common }) - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const privateKey = hexToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) const address = Address.fromPrivateKey(privateKey) const initialBalance = BigInt(10) ** BigInt(18) @@ -229,7 +229,7 @@ tape('runTx() -> successful API parameter usage', async (t) => { ) const tx = unsignedTx.sign(privateKey) - const coinbase = Buffer.from('00000000000000000000000000000000000000ff', 'hex') + const coinbase = hexToBytes('00000000000000000000000000000000000000ff') const block = Block.fromBlockData( { header: { @@ -436,9 +436,8 @@ tape('runTx() -> runtime behavior', async (t) => { for (const txType of TRANSACTION_TYPES) { const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) const vm = await VM.create({ common }) - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const privateKey = hexToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) /* Code which is deployed here: PUSH1 01 @@ -446,13 +445,13 @@ tape('runTx() -> runtime behavior', async (t) => { SSTORE INVALID */ - const code = Buffer.from('6001600055FE', 'hex') - const address = new Address(Buffer.from('00000000000000000000000000000000000000ff', 'hex')) + const code = hexToBytes('6001600055FE') + const address = new Address(hexToBytes('00000000000000000000000000000000000000ff')) await vm.eei.putContractCode(address, code) await vm.eei.putContractStorage( address, - Buffer.from('00'.repeat(32), 'hex'), - Buffer.from('00'.repeat(31) + '01', 'hex') + hexToBytes('00'.repeat(32)), + hexToBytes('00'.repeat(31) + '01') ) const txParams: any = { nonce: '0x00', @@ -515,9 +514,7 @@ tape('runTx() -> runtime errors', async (t) => { const from = createAccount() await vm.eei.putAccount(caller, from) - const contractAddress = new Address( - Buffer.from('61de9dc6f6cff1df2809480882cfd3c2364b28f7', 'hex') - ) + const contractAddress = new Address(hexToBytes('61de9dc6f6cff1df2809480882cfd3c2364b28f7')) const to = createAccount(BigInt(0), MAX_INTEGER) await vm.eei.putAccount(contractAddress, to) @@ -558,8 +555,8 @@ tape('runTx() -> API return values', async (t) => { ) t.deepEqual( res.execResult.returnValue, - Buffer.from([]), - `execution result -> return value -> empty Buffer (${txType.name})` + Uint8Array.from([]), + `execution result -> return value -> empty Uint8Array (${txType.name})` ) t.equal(res.gasRefund, BigInt(0), `gasRefund -> 0 (${txType.name})`) } @@ -604,7 +601,7 @@ tape('runTx() -> API return values', async (t) => { t.deepEqual( res.bloom.bitvector, - Buffer.from('00'.repeat(256), 'hex'), + hexToBytes('00'.repeat(256)), `runTx result -> bloom.bitvector -> should be empty (${txType.name})` ) t.equal( @@ -673,7 +670,7 @@ tape('runTx() -> consensus bugs', async (t) => { REVERT puts an "error message" in the RETURNDATA buffer. This buffer would contain the contract code to deploy if the message would not fail. In this case, REVERT puts a message in the RETURNDATA buffer which is larger than the `maxCodeSize` This should not consume all gas: it should only consume the gas spent by the attempt to create the contract */ - const pkey = Buffer.alloc(32, 1) + const pkey = new Uint8Array(32).fill(1) const txData: FeeMarketEIP1559TxData = { gasLimit: 100000, maxPriorityFeePerGas: 1000, @@ -743,10 +740,7 @@ tape('runTx() -> skipBalance behavior', async (t) => { t.plan(6) const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) const vm = await VM.create({ common }) - const senderKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' - ) + const senderKey = hexToBytes('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109') const sender = Address.fromPrivateKey(senderKey) for (const balance of [undefined, BigInt(5)]) { @@ -777,11 +771,11 @@ tape( const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) const vm = await VM.create({ common }) - const pkey = Buffer.alloc(32, 1) + const pkey = new Uint8Array(32).fill(1) // CALLER EXTCODEHASH PUSH 0 SSTORE STOP // Puts EXTCODEHASH of CALLER into slot 0 - const code = Buffer.from('333F60005500', 'hex') + const code = hexToBytes('333F60005500') const codeAddr = Address.fromString('0x' + '20'.repeat(20)) await vm.stateManager.putContractCode(codeAddr, code) @@ -797,11 +791,8 @@ tape( await vm.eei.putAccount(addr, acc) await vm.runTx({ tx, skipHardForkValidation: true }) - const hash = await vm.stateManager.getContractStorage( - codeAddr, - Buffer.from('00'.repeat(32), 'hex') - ) - t.ok(hash.equals(KECCAK256_NULL), 'hash ok') + const hash = await vm.stateManager.getContractStorage(codeAddr, hexToBytes('00'.repeat(32))) + t.deepEquals(hash, KECCAK256_NULL, 'hash ok') t.end() } @@ -813,7 +804,7 @@ tape( const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) const vm = await VM.create({ common }) - const pkey = Buffer.alloc(32, 1) + const pkey = new Uint8Array(32).fill(1) // PUSH 0 DUP DUP DUP // CALLVALUE CALLER GAS @@ -821,7 +812,7 @@ tape( // STOP // Calls CALLER and sends back the ETH just sent with the transaction - const code = Buffer.from('600080808034335AF100', 'hex') + const code = hexToBytes('600080808034335AF100') const codeAddr = Address.fromString('0x' + '20'.repeat(20)) await vm.stateManager.putContractCode(codeAddr, code) @@ -852,11 +843,11 @@ tape( const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) const vm = await VM.create({ common }) - const pkey = Buffer.alloc(32, 1) + const pkey = new Uint8Array(32).fill(1) // CALLER EXTCODEHASH PUSH 0 SSTORE STOP // Puts EXTCODEHASH of CALLER into slot 0 - const code = Buffer.from('33FF', 'hex') + const code = hexToBytes('33FF') const codeAddr = Address.fromString('0x' + '20'.repeat(20)) await vm.stateManager.putContractCode(codeAddr, code) diff --git a/packages/vm/test/api/state/accountExists.spec.ts b/packages/vm/test/api/state/accountExists.spec.ts index c9877259c44..9cfa94447a8 100644 --- a/packages/vm/test/api/state/accountExists.spec.ts +++ b/packages/vm/test/api/state/accountExists.spec.ts @@ -1,5 +1,6 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { Address, toBuffer } from '@ethereumjs/util' +import { Address, toBytes } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VM } from '../../../src/vm' @@ -10,10 +11,8 @@ tape('correctly apply new account gas fee on pre-Spurious Dragon hardforks', asy // This test verifies that issue is now resolved // setup the accounts for this test - const caller = new Address(Buffer.from('1747de68ae74afa4e00f8ef79b9c875a339cda70', 'hex')) // caller address - const contractAddress = new Address( - Buffer.from('02E815899482f27C899fB266319dE7cc97F72E87', 'hex') - ) // contract address + const caller = new Address(hexToBytes('1747de68ae74afa4e00f8ef79b9c875a339cda70')) // caller address + const contractAddress = new Address(hexToBytes('02E815899482f27C899fB266319dE7cc97F72E87')) // contract address // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Homestead }) const vm = await VM.create({ common }) @@ -24,19 +23,18 @@ tape('correctly apply new account gas fee on pre-Spurious Dragon hardforks', asy const existingAccount = await vm.stateManager.getAccount(existingAddress) existingAccount.balance = BigInt(1) await vm.stateManager.putAccount(existingAddress, existingAccount) - await vm.stateManager.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + await vm.stateManager.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code await vm.stateManager.putContractStorage( contractAddress, - Buffer.from('d08f588b94e47566eea77acec87441cecca23f61aea9ed8eb086c062d3837605', 'hex'), - Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') + hexToBytes('d08f588b94e47566eea77acec87441cecca23f61aea9ed8eb086c062d3837605'), + hexToBytes('0000000000000000000000000000000000000000000000000000000000000001') ) // setup the call arguments const runCallArgs = { caller, // call address gasLimit: BigInt(174146 - 22872), // tx gas limit minus the tx fee (21000) and data fee (1872) to represent correct gas costs - data: Buffer.from( - 'a9059cbb000000000000000000000000f48a1bdc65d9ccb4b569ffd4bffff415b90783d60000000000000000000000000000000000000000000000000000000000000001', - 'hex' + data: hexToBytes( + 'a9059cbb000000000000000000000000f48a1bdc65d9ccb4b569ffd4bffff415b90783d60000000000000000000000000000000000000000000000000000000000000001' ), to: contractAddress, // call to the contract address value: BigInt(0), @@ -55,10 +53,8 @@ tape( 'do not apply new account gas fee for empty account in DB on pre-Spurious Dragon hardforks', async (t) => { // setup the accounts for this test - const caller = new Address(Buffer.from('1747de68ae74afa4e00f8ef79b9c875a339cda70', 'hex')) // caller address - const contractAddress = new Address( - Buffer.from('02E815899482f27C899fB266319dE7cc97F72E87', 'hex') - ) // contract address + const caller = new Address(hexToBytes('1747de68ae74afa4e00f8ef79b9c875a339cda70')) // caller address + const contractAddress = new Address(hexToBytes('02E815899482f27C899fB266319dE7cc97F72E87')) // contract address // setup the vm const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Homestead }) const vm = await VM.create({ common }) @@ -69,23 +65,22 @@ tape( existingAccount.balance = BigInt(1) await vm.stateManager.putAccount(existingAddress, existingAccount) // add empty account to DB - const emptyAddress = new Address(Buffer.from('f48a1bdc65d9ccb4b569ffd4bffff415b90783d6', 'hex')) + const emptyAddress = new Address(hexToBytes('f48a1bdc65d9ccb4b569ffd4bffff415b90783d6')) const emptyAccount = await vm.stateManager.getAccount(emptyAddress) //@ts-ignore - vm.stateManager._trie.put(toBuffer(emptyAddress), emptyAccount.serialize()) - await vm.stateManager.putContractCode(contractAddress, Buffer.from(code, 'hex')) // setup the contract code + vm.stateManager._trie.put(toBytes(emptyAddress), emptyAccount.serialize()) + await vm.stateManager.putContractCode(contractAddress, hexToBytes(code)) // setup the contract code await vm.stateManager.putContractStorage( contractAddress, - Buffer.from('d08f588b94e47566eea77acec87441cecca23f61aea9ed8eb086c062d3837605', 'hex'), - Buffer.from('0000000000000000000000000000000000000000000000000000000000000001', 'hex') + hexToBytes('d08f588b94e47566eea77acec87441cecca23f61aea9ed8eb086c062d3837605'), + hexToBytes('0000000000000000000000000000000000000000000000000000000000000001') ) // setup the call arguments const runCallArgs = { caller, // call address gasLimit: BigInt(174146 - 22872), // tx gas limit minus the tx fee (21000) and data fee (1872) to represent correct gas costs - data: Buffer.from( - 'a9059cbb000000000000000000000000f48a1bdc65d9ccb4b569ffd4bffff415b90783d60000000000000000000000000000000000000000000000000000000000000001', - 'hex' + data: hexToBytes( + 'a9059cbb000000000000000000000000f48a1bdc65d9ccb4b569ffd4bffff415b90783d60000000000000000000000000000000000000000000000000000000000000001' ), to: contractAddress, // call to the contract address value: BigInt(0), diff --git a/packages/vm/test/api/utils.ts b/packages/vm/test/api/utils.ts index 843793bedab..d6abd21eacf 100644 --- a/packages/vm/test/api/utils.ts +++ b/packages/vm/test/api/utils.ts @@ -1,6 +1,7 @@ import { Blockchain } from '@ethereumjs/blockchain' import { TransactionFactory } from '@ethereumjs/tx' import { Account } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' import { MemoryLevel } from 'memory-level' import { VM } from '../../src/vm' @@ -24,7 +25,7 @@ export async function setBalance(vm: VM, address: Address, balance = BigInt(1000 export async function setupVM(opts: VMOpts & { genesisBlock?: Block } = {}) { const db: any = new MemoryLevel() const { common, genesisBlock } = opts - if (!opts.blockchain) { + if (opts.blockchain === undefined) { opts.blockchain = await Blockchain.create({ db, validateBlocks: false, @@ -98,9 +99,8 @@ export function getTransaction( const tx = TransactionFactory.fromTxData(txParams, { common, freeze: false }) if (sign) { - const privateKey = Buffer.from( - 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', - 'hex' + const privateKey = hexToBytes( + 'e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109' ) return tx.sign(privateKey) } diff --git a/packages/vm/test/api/vmState.spec.ts b/packages/vm/test/api/vmState.spec.ts index 2396e77c675..2ff115ede96 100644 --- a/packages/vm/test/api/vmState.spec.ts +++ b/packages/vm/test/api/vmState.spec.ts @@ -2,6 +2,7 @@ import { Blockchain } from '@ethereumjs/blockchain' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Address } from '@ethereumjs/util' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import { VmState } from '../../src/eei/vmState' @@ -26,7 +27,7 @@ tape('vmState', (t) => { await vmState.generateCanonicalGenesis(blockchain.genesisState()) const stateRoot = await vmState.getStateRoot() st.equal( - stateRoot.toString('hex'), + bytesToHex(stateRoot), genesisData.genesis_state_root, 'generateCanonicalGenesis should produce correct state root for mainnet from ethereum/tests data' ) @@ -40,9 +41,8 @@ tape('vmState', (t) => { return st.end() } const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Petersburg }) - const expectedStateRoot = Buffer.from( - 'd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544', - 'hex' + const expectedStateRoot = hexToBytes( + 'd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544' ) const stateManager = new StateManager({}) @@ -51,30 +51,31 @@ tape('vmState', (t) => { await vmState.generateCanonicalGenesis(blockchain.genesisState()) const stateRoot = await vmState.getStateRoot() - st.true( - stateRoot.equals(expectedStateRoot), + st.deepEquals( + stateRoot, + expectedStateRoot, `generateCanonicalGenesis should produce correct state root for mainnet from common` ) st.end() }) t.test('should generate the genesis state root correctly for all other chains', async (st) => { - const chains: [Chain, Buffer][] = [ + const chains: [Chain, Uint8Array][] = [ [ Chain.Ropsten, - Buffer.from('217b0bbcfb72e2d57e28f33cb361b9983513177755dc3f33ce3e7022ed62b77b', 'hex'), + hexToBytes('217b0bbcfb72e2d57e28f33cb361b9983513177755dc3f33ce3e7022ed62b77b'), ], [ Chain.Rinkeby, - Buffer.from('53580584816f617295ea26c0e17641e0120cab2f0a8ffb53a866fd53aa8e8c2d', 'hex'), + hexToBytes('53580584816f617295ea26c0e17641e0120cab2f0a8ffb53a866fd53aa8e8c2d'), ], [ Chain.Goerli, - Buffer.from('5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008', 'hex'), + hexToBytes('5d6cded585e73c4e322c30c2f782a336316f17dd85a4863b9d838d2d4b8b3008'), ], [ Chain.Sepolia, - Buffer.from('5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494', 'hex'), + hexToBytes('5eb6e371a698b8d68f665192350ffcecbbbf322916f4b51bd79bb6887da3f494'), ], ] @@ -87,8 +88,9 @@ tape('vmState', (t) => { await vmState.generateCanonicalGenesis(blockchain.genesisState()) const stateRoot = await vmState.getStateRoot() - st.true( - stateRoot.equals(expectedStateRoot), + st.deepEquals( + stateRoot, + expectedStateRoot, `generateCanonicalGenesis should produce correct state root for ${Chain[chain]}` ) } @@ -100,20 +102,20 @@ tape('Original storage cache', async (t) => { const stateManager = new DefaultStateManager() const vmState = new VmState({ stateManager }) - const address = new Address(Buffer.from('a94f5374fce5edbc8e2a8697c15331677e6ebf0b', 'hex')) + const address = new Address(hexToBytes('a94f5374fce5edbc8e2a8697c15331677e6ebf0b')) const account = createAccount() await vmState.putAccount(address, account) - const key = Buffer.from('1234567890123456789012345678901234567890123456789012345678901234', 'hex') - const value = Buffer.from('1234', 'hex') + const key = hexToBytes('1234567890123456789012345678901234567890123456789012345678901234') + const value = hexToBytes('1234') t.test('should initially have empty storage value', async (st) => { await vmState.checkpoint() const res = await vmState.getContractStorage(address, key) - st.deepEqual(res, Buffer.alloc(0)) + st.deepEqual(res, new Uint8Array(0)) const origRes = await (vmState).getOriginalContractStorage(address, key) - st.deepEqual(origRes, Buffer.alloc(0)) + st.deepEqual(origRes, new Uint8Array(0)) await vmState.commit() @@ -135,7 +137,7 @@ tape('Original storage cache', async (t) => { }) t.test('should return correct original value after modification', async (st) => { - const newValue = Buffer.from('1235', 'hex') + const newValue = hexToBytes('1235') await vmState.putContractStorage(address, key, newValue) const res = await vmState.getContractStorage(address, key) st.deepEqual(res, newValue) @@ -146,12 +148,9 @@ tape('Original storage cache', async (t) => { }) t.test('should cache keys separately', async (st) => { - const key2 = Buffer.from( - '0000000000000000000000000000000000000000000000000000000000000012', - 'hex' - ) - const value2 = Buffer.from('12', 'hex') - const value3 = Buffer.from('123', 'hex') + const key2 = hexToBytes('0000000000000000000000000000000000000000000000000000000000000012') + const value2 = utf8ToBytes('12') + const value3 = utf8ToBytes('123') await vmState.putContractStorage(address, key2, value2) let res = await vmState.getContractStorage(address, key2) @@ -168,7 +167,7 @@ tape('Original storage cache', async (t) => { // Check previous key res = await vmState.getContractStorage(address, key) - st.deepEqual(res, Buffer.from('1235', 'hex')) + st.deepEqual(res, hexToBytes('1235')) origRes = await (vmState).getOriginalContractStorage(address, key) st.deepEqual(origRes, value) @@ -177,7 +176,7 @@ tape('Original storage cache', async (t) => { t.test("getOriginalContractStorage should validate the key's length", async (st) => { try { - await (vmState).getOriginalContractStorage(address, Buffer.alloc(12)) + await (vmState).getOriginalContractStorage(address, new Uint8Array(12)) } catch (e: any) { st.equal(e.message, 'Storage key must be 32 bytes long') st.end() @@ -194,12 +193,12 @@ tape('StateManager - generateAccessList', (tester) => { // Only use 0..9 function a(n: number) { - return Buffer.from(`ff${'00'.repeat(18)}0${n}`, 'hex') + return hexToBytes(`ff${'00'.repeat(18)}0${n}`) } // Only use 0..9 function s(n: number) { - return Buffer.from(`${'00'.repeat(31)}0${n}`, 'hex') + return hexToBytes(`${'00'.repeat(31)}0${n}`) } function getStateManagerAliases() { diff --git a/packages/vm/test/retesteth/transition-child.ts b/packages/vm/test/retesteth/transition-child.ts index 9abd89ba448..a5a0c407713 100644 --- a/packages/vm/test/retesteth/transition-child.ts +++ b/packages/vm/test/retesteth/transition-child.ts @@ -2,8 +2,9 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { RLP } from '@ethereumjs/rlp' import { Transaction, TransactionFactory } from '@ethereumjs/tx' -import { arrToBufArr } from '@ethereumjs/util' +import { bytesToPrefixedHexString } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' +import { hexToBytes } from 'ethereum-cryptography/utils' import { readFileSync, writeFileSync } from 'fs' import { join } from 'path' @@ -14,7 +15,7 @@ import { makeBlockFromEnv, setupPreConditions } from '../util' import type { PostByzantiumTxReceipt } from '../../src' import type { TypedTransaction } from '@ethereumjs/tx' -import type { NestedBufferArray } from '@ethereumjs/util' +import type { NestedUint8Array } from '@ethereumjs/util' const yargs = require('yargs/yargs') @@ -58,12 +59,13 @@ async function runTransition(argsIn: any) { const genesis = Block.fromBlockData({ header: BlockHeader.fromHeaderData(genesisBlockData) }) blockchain = await Blockchain.create({ common, genesisBlock: genesis }) } - const vm = blockchain ? await VM.create({ common, blockchain }) : await VM.create({ common }) + const vm = + blockchain !== undefined ? await VM.create({ common, blockchain }) : await VM.create({ common }) await setupPreConditions(vm.eei, { pre: alloc }) const block = makeBlockFromEnv(inputEnv, { common }) - const txsData = arrToBufArr(RLP.decode(Buffer.from(rlpTxs.slice(2), 'hex'))) + const txsData = RLP.decode(hexToBytes(rlpTxs.slice(2))) const headerData = block.header.toJSON() headerData.difficulty = inputEnv.parentDifficulty @@ -84,9 +86,9 @@ async function runTransition(argsIn: any) { root: '0x', status: receipt.status === 0 ? '0x' : '0x1', cumulativeGasUsed: '0x' + receipt.cumulativeBlockGasUsed.toString(16), - logsBloom: '0x' + receipt.bitvector.toString('hex'), + logsBloom: bytesToPrefixedHexString(receipt.bitvector), logs: null, - transactionHash: '0x' + afterTx.transaction.hash().toString('hex'), + transactionHash: bytesToPrefixedHexString(afterTx.transaction.hash()), contractAddress: '0x0000000000000000000000000000000000000000', gasUsed: '0x' + afterTx.totalGasSpent.toString(16), blockHash: '0x0000000000000000000000000000000000000000000000000000000000000000', @@ -100,13 +102,13 @@ async function runTransition(argsIn: any) { const rejected = [] let index = 0 - for (const txData of txsData) { + for (const txData of txsData) { try { let tx: TypedTransaction - if (Buffer.isBuffer(txData)) { - tx = TransactionFactory.fromSerializedData(txData as Buffer, { common }) + if (txData instanceof Uint8Array) { + tx = TransactionFactory.fromSerializedData(txData as Uint8Array, { common }) } else { - tx = Transaction.fromValuesArray(txData as Buffer[], { common }) + tx = Transaction.fromValuesArray(txData as Uint8Array[], { common }) } await builder.addTransaction(tx) } catch (e: any) { @@ -119,14 +121,14 @@ async function runTransition(argsIn: any) { } const logsBloom = builder.logsBloom() - const logsHash = Buffer.from(keccak256(logsBloom)) + const logsHash = keccak256(logsBloom) const output = { - stateRoot: '0x' + (await vm.eei.getStateRoot()).toString('hex'), - txRoot: '0x' + (await builder.transactionsTrie()).toString('hex'), - receiptsRoot: '0x' + (await builder.receiptTrie()).toString('hex'), - logsHash: '0x' + logsHash.toString('hex'), - logsBloom: '0x' + logsBloom.toString('hex'), + stateRoot: bytesToPrefixedHexString(await vm.eei.getStateRoot()), + txRoot: bytesToPrefixedHexString(await builder.transactionsTrie()), + receiptsRoot: bytesToPrefixedHexString(await builder.receiptTrie()), + logsHash: bytesToPrefixedHexString(logsHash), + logsBloom: bytesToPrefixedHexString(logsBloom), currentDifficulty: '0x20000', receipts, // TODO fixme } diff --git a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts index f76db2d2d43..6ea6577ec80 100644 --- a/packages/vm/test/tester/runners/BlockchainTestsRunner.ts +++ b/packages/vm/test/tester/runners/BlockchainTestsRunner.ts @@ -4,7 +4,8 @@ import { ConsensusAlgorithm } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' import { Trie } from '@ethereumjs/trie' import { TransactionFactory } from '@ethereumjs/tx' -import { bufferToBigInt, isHexPrefixed, stripHexPrefix, toBuffer } from '@ethereumjs/util' +import { bytesToBigInt, isHexPrefixed, stripHexPrefix, toBytes } from '@ethereumjs/util' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { Level } from 'level' import { MemoryLevel } from 'memory-level' @@ -55,8 +56,8 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes const genesisBlock = Block.fromBlockData(blockData, { common }) if (typeof testData.genesisRLP === 'string') { - const rlp = toBuffer(testData.genesisRLP) - t.ok(genesisBlock.serialize().equals(rlp), 'correct genesis RLP') + const rlp = toBytes(testData.genesisRLP) + t.deepEquals(genesisBlock.serialize(), rlp, 'correct genesis RLP') } const blockchain = await Blockchain.create({ @@ -90,7 +91,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes // set up pre-state await setupPreConditions(vm.eei, testData) - t.ok(vm.stateManager._trie.root().equals(genesisBlock.header.stateRoot), 'correct pre stateRoot') + t.deepEquals(vm.stateManager._trie.root(), genesisBlock.header.stateRoot, 'correct pre stateRoot') async function handleError(error: string | undefined, expectException: string | boolean) { if (expectException !== false) { @@ -115,16 +116,16 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes // Here we decode the rlp to extract the block number // The block library cannot be used, as this throws on certain EIP1559 blocks when trying to convert try { - const blockRlp = Buffer.from((raw.rlp as string).slice(2), 'hex') + const blockRlp = hexToBytes((raw.rlp as string).slice(2)) const decodedRLP: any = RLP.decode(Uint8Array.from(blockRlp)) - currentBlock = bufferToBigInt(decodedRLP[0][8]) + currentBlock = bytesToBigInt(decodedRLP[0][8]) } catch (e: any) { await handleError(e, expectException) continue } try { - const blockRlp = Buffer.from((raw.rlp as string).slice(2), 'hex') + const blockRlp = hexToBytes((raw.rlp as string).slice(2)) // Update common HF let TD: bigint | undefined = undefined let timestamp: bigint | undefined = undefined @@ -132,7 +133,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes const decoded: any = RLP.decode(blockRlp) const parentHash = decoded[0][0] TD = await blockchain.getTotalDifficulty(parentHash) - timestamp = bufferToBigInt(decoded[0][11]) + timestamp = bytesToBigInt(decoded[0][11]) // eslint-disable-next-line no-empty } catch (e) {} @@ -153,7 +154,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes >[]) { const shouldFail = txData.valid === 'false' try { - const txRLP = Buffer.from(txData.rawBytes.slice(2), 'hex') + const txRLP = hexToBytes(txData.rawBytes.slice(2)) const tx = TransactionFactory.fromSerializedData(txRLP, { common }) await blockBuilder.addTransaction(tx) if (shouldFail) { @@ -222,7 +223,7 @@ export async function runBlockchainTest(options: any, testData: any, t: tape.Tes } } t.equal( - (blockchain as any)._headHeaderHash.toString('hex'), + bytesToHex((blockchain as any)._headHeaderHash), testData.lastblockhash, 'correct last header block' ) diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index d83c3f4fac0..b6779f82ee2 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -2,7 +2,7 @@ import { Block } from '@ethereumjs/block' import { Blockchain } from '@ethereumjs/blockchain' import { DefaultStateManager } from '@ethereumjs/statemanager' import { Trie } from '@ethereumjs/trie' -import { toBuffer } from '@ethereumjs/util' +import { bytesToHex, equalsBytes, toBytes } from '@ethereumjs/util' import { EVM } from '../../../../evm/src' import { EEI } from '../../../src' @@ -123,7 +123,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { }) vm.events.on('afterTx', async () => { const stateRoot = { - stateRoot: vm.stateManager._trie.root.toString('hex'), + stateRoot: bytesToHex(vm.stateManager._trie.root), } t.comment(JSON.stringify(stateRoot)) }) @@ -140,8 +140,8 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { } const stateManagerStateRoot = vm.stateManager._trie.root() - const testDataPostStateRoot = toBuffer(testData.postStateRoot) - const stateRootsAreEqual = stateManagerStateRoot.equals(testDataPostStateRoot) + const testDataPostStateRoot = toBytes(testData.postStateRoot) + const stateRootsAreEqual = equalsBytes(stateManagerStateRoot, testDataPostStateRoot) const end = Date.now() const timeSpent = `${(end - begin) / 1000} secs` diff --git a/packages/vm/test/tester/testLoader.ts b/packages/vm/test/tester/testLoader.ts index c5bda693802..35d96be47f7 100644 --- a/packages/vm/test/tester/testLoader.ts +++ b/packages/vm/test/tester/testLoader.ts @@ -36,7 +36,7 @@ export async function getTests( } const fileCallback = async ( err: Error | undefined, - content: string | Buffer, + content: string | Uint8Array, fileName: string, next: Function ) => { @@ -46,7 +46,7 @@ export async function getTests( } const subDir = fileName.substr(directory.length + 1) const parsedFileName = path.parse(fileName).name - content = Buffer.isBuffer(content) ? content.toString() : content + content = content instanceof Uint8Array ? content.toString() : content const testsByName = JSON.parse(content) const testNames = Object.keys(testsByName) for (const testName of testNames) { diff --git a/packages/vm/test/util.ts b/packages/vm/test/util.ts index 06c53e920f2..ec678efdfc5 100644 --- a/packages/vm/test/util.ts +++ b/packages/vm/test/util.ts @@ -9,16 +9,16 @@ import { import { Account, Address, - bigIntToBuffer, - bufferToBigInt, - bufferToHex, + bigIntToBytes, + bytesToBigInt, + bytesToPrefixedHexString, isHexPrefixed, setLengthLeft, stripHexPrefix, - toBuffer, + toBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' -import { bytesToHex } from 'ethereum-cryptography/utils' +import { bytesToHex, equalsBytes, hexToBytes } from 'ethereum-cryptography/utils' import type { VmState } from '../src/eei/vmState' import type { BlockOptions } from '@ethereumjs/block' @@ -49,7 +49,7 @@ export function dumpState(state: any, cb: Function) { const storageRS = storageTrie.createReadStream() storageRS.on('data', function (data: any) { - storage[data.key.toString('hex')] = data.value.toString('hex') + storage[bytesToHex(data.key)] = bytesToHex(data.value) }) storageRS.on('end', function () { @@ -65,8 +65,8 @@ export function dumpState(state: any, cb: Function) { results.push(result) } for (let i = 0; i < results.length; i++) { - console.log("SHA3'd address: " + bufferToHex(results[i].address)) - console.log('\tstorage root: ' + bufferToHex(results[i].storageRoot)) + console.log("SHA3'd address: " + bytesToHex(results[i].address)) + console.log('\tstorage root: ' + bytesToHex(results[i].storageRoot)) console.log('\tstorage: ') for (const storageKey in results[i].storage) { console.log('\t\t' + storageKey + ': ' + results[i].storage[storageKey]) @@ -78,28 +78,28 @@ export function dumpState(state: any, cb: Function) { }) } -export function format(a: any, toZero: boolean = false, isHex: boolean = false): Buffer { +export function format(a: any, toZero: boolean = false, isHex: boolean = false): Uint8Array { if (a === '') { - return Buffer.alloc(0) + return new Uint8Array() } if (typeof a === 'string' && isHexPrefixed(a)) { a = a.slice(2) if (a.length % 2) a = '0' + a - a = Buffer.from(a, 'hex') + a = hexToBytes(a) } else if (!isHex) { try { - a = bigIntToBuffer(BigInt(a)) + a = bigIntToBytes(BigInt(a)) } catch { // pass } } else { if (a.length % 2) a = '0' + a - a = Buffer.from(a, 'hex') + a = hexToBytes(a) } - if (toZero && a.toString('hex') === '') { - a = Buffer.from([0]) + if (toZero && bytesToHex(a) === '') { + a = Uint8Array.from([0]) } return a @@ -125,7 +125,7 @@ export function makeTx( } if (txData.secretKey !== undefined) { - const privKey = toBuffer(txData.secretKey) + const privKey = toBytes(txData.secretKey) return tx.sign(privKey) } @@ -138,7 +138,7 @@ export async function verifyPostConditions(state: any, testData: any, t: tape.Te const keyMap: any = {} for (const key in testData) { - const hash = bytesToHex(keccak256(Buffer.from(stripHexPrefix(key), 'hex'))) + const hash = bytesToHex(keccak256(hexToBytes(stripHexPrefix(key)))) hashedAccounts[hash] = testData[key] keyMap[hash] = key } @@ -150,7 +150,7 @@ export async function verifyPostConditions(state: any, testData: any, t: tape.Te stream.on('data', function (data: any) { const rlp = data.value const account = Account.fromRlpSerializedAccount(rlp) - const key = data.key.toString('hex') + const key = bytesToHex(data.key) const testData = hashedAccounts[key] const address = keyMap[key] delete keyMap[key] @@ -189,18 +189,16 @@ export function verifyAccountPostConditions( ) { return new Promise((resolve) => { t.comment('Account: ' + address) - if (!format(account.balance, true).equals(format(acctData.balance, true))) { + if (!equalsBytes(format(account.balance, true), format(acctData.balance, true))) { t.comment( - `Expected balance of ${bufferToBigInt(format(acctData.balance, true))}, but got ${ + `Expected balance of ${bytesToBigInt(format(acctData.balance, true))}, but got ${ account.balance }` ) } - if (!format(account.nonce, true).equals(format(acctData.nonce, true))) { + if (!equalsBytes(format(account.nonce, true), format(acctData.nonce, true))) { t.comment( - `Expected nonce of ${bufferToBigInt(format(acctData.nonce, true))}, but got ${ - account.nonce - }` + `Expected nonce of ${bytesToBigInt(format(acctData.nonce, true))}, but got ${account.nonce}` ) } @@ -209,15 +207,15 @@ export function verifyAccountPostConditions( const hashedStorage: any = {} for (const key in acctData.storage) { - hashedStorage[bytesToHex(keccak256(setLengthLeft(Buffer.from(key.slice(2), 'hex'), 32)))] = + hashedStorage[bytesToHex(keccak256(setLengthLeft(hexToBytes(key.slice(2)), 32)))] = acctData.storage[key] } state.root(account.storageRoot) const rs = state.createReadStream() rs.on('data', function (data: any) { - let key = data.key.toString('hex') - const val = '0x' + Buffer.from(RLP.decode(data.value) as Uint8Array).toString('hex') + let key = bytesToHex(data.key) + const val = bytesToPrefixedHexString(RLP.decode(data.value) as Uint8Array) if (key === '0x') { key = '0x00' @@ -227,7 +225,7 @@ export function verifyAccountPostConditions( if (val !== hashedStorage[key]) { t.comment( - `Expected storage key 0x${data.key.toString('hex')} at address ${address} to have value ${ + `Expected storage key 0x${bytesToHex(data.key)} at address ${address} to have value ${ hashedStorage[key] ?? '0x' }, but got ${val}}` ) @@ -330,7 +328,7 @@ export async function setupPreConditions(state: VmState, testData: any) { // Set contract storage for (const storageKey of Object.keys(storage)) { const val = format(storage[storageKey]) - if (['', '00'].includes(val.toString('hex'))) { + if (['', '00'].includes(bytesToHex(val))) { continue } const key = setLengthLeft(format(storageKey), 32) From d9c020c3fe52d2952cebc6824b1dd2c9af41beeb Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 28 Mar 2023 16:13:04 -0400 Subject: [PATCH 02/32] Devp2p status fix --- packages/devp2p/examples/simple.ts | 2 +- packages/devp2p/src/protocol/eth.ts | 2 +- packages/devp2p/src/rlpx/ecies.ts | 2 +- packages/devp2p/src/rlpx/peer.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/devp2p/examples/simple.ts b/packages/devp2p/examples/simple.ts index 8cf80e3c1c7..8a4c2fa870c 100644 --- a/packages/devp2p/examples/simple.ts +++ b/packages/devp2p/examples/simple.ts @@ -1,6 +1,6 @@ import { Chain, Common } from '@ethereumjs/common' import chalk from 'chalk' -import { hexToBytes } from 'ethereum-cryptography/utils' +import { bytesToHex, hexToBytes } from 'ethereum-cryptography/utils' import { DPT } from '../src/index' diff --git a/packages/devp2p/src/protocol/eth.ts b/packages/devp2p/src/protocol/eth.ts index 1b88a27b041..74ffc3e8d57 100644 --- a/packages/devp2p/src/protocol/eth.ts +++ b/packages/devp2p/src/protocol/eth.ts @@ -227,7 +227,7 @@ export class ETH extends Protocol { _getStatusString(status: ETH.StatusMsg) { let sStr = `[V:${bytesToInt(status[0] as Uint8Array)}, NID:${bytesToInt( status[1] as Uint8Array - )}, TD:${status[2].length === 0 ? 0 : bytesToInt(status[2] as Uint8Array)}` + )}, TD:${status[2].length === 0 ? 0 : bytesToBigInt(status[2] as Uint8Array).toString()}` sStr += `, BestH:${formatLogId( bytesToHex(status[3] as Uint8Array), this._verbose diff --git a/packages/devp2p/src/rlpx/ecies.ts b/packages/devp2p/src/rlpx/ecies.ts index 5c7e540042a..2151af932dd 100644 --- a/packages/devp2p/src/rlpx/ecies.ts +++ b/packages/devp2p/src/rlpx/ecies.ts @@ -394,7 +394,7 @@ export class ECIES { if (!this._ingressMac) return this._ingressMac.updateBody(body) - const _mac = Uint8Array.from(this._ingressMac.digest()) + const _mac = this._ingressMac.digest() assertEq(_mac, mac, 'Invalid MAC', debug) const size = this._bodySize diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 06adf22e37d..1df28c1689c 100644 --- a/packages/devp2p/src/rlpx/peer.ts +++ b/packages/devp2p/src/rlpx/peer.ts @@ -587,11 +587,11 @@ export class Peer extends EventEmitter { // if (protocolName === 'Peer') { try { - payload = RLP.decode(Uint8Array.from(payload)) + payload = RLP.decode(payload) } catch (e: any) { if (msgCode === PREFIXES.DISCONNECT) { if (compressed) { - payload = RLP.decode(Uint8Array.from(origPayload)) + payload = RLP.decode(origPayload) } else { payload = RLP.decode(snappy.uncompress(payload)) } From da78efebd8bbe675ae02646b3887fd232c82e09b Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 28 Mar 2023 20:57:03 -0400 Subject: [PATCH 03/32] Remove buffer detritus --- .../client/lib/sync/fetcher/accountfetcher.ts | 6 +- .../test/sync/fetcher/storagefetcher.spec.ts | 116 ++++++++---------- packages/devp2p/src/rlpx/peer.ts | 1 + packages/evm/test/eips/eip-3860.spec.ts | 10 +- packages/evm/test/runCall.spec.ts | 2 +- 5 files changed, 59 insertions(+), 76 deletions(-) diff --git a/packages/client/lib/sync/fetcher/accountfetcher.ts b/packages/client/lib/sync/fetcher/accountfetcher.ts index 73d284ba0f2..39179ad6709 100644 --- a/packages/client/lib/sync/fetcher/accountfetcher.ts +++ b/packages/client/lib/sync/fetcher/accountfetcher.ts @@ -320,9 +320,9 @@ export class AccountFetcher extends Fetcher await this.accountTrie.put(account.hash, accountBodyToRLP(account.body)) // build record of accounts that need storage slots to be fetched - const storageRoot: Buffer = - account.body[2] instanceof Buffer ? account.body[2] : Buffer.from(account.body[2]) - if (storageRoot.compare(KECCAK256_RLP) !== 0) { + const storageRoot: Uint8Array = + account.body[2] instanceof Uint8Array ? account.body[2] : Uint8Array.from(account.body[2]) + if (!equalsBytes(storageRoot, KECCAK256_RLP)) { storageFetchRequests.push({ accountHash: account.hash, storageRoot, diff --git a/packages/client/test/sync/fetcher/storagefetcher.spec.ts b/packages/client/test/sync/fetcher/storagefetcher.spec.ts index 72a66fb04ca..168b2fe0ed6 100644 --- a/packages/client/test/sync/fetcher/storagefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/storagefetcher.spec.ts @@ -1,4 +1,5 @@ import { RLP } from '@ethereumjs/rlp' +import { hexToBytes, utf8ToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' import * as td from 'testdouble' @@ -28,16 +29,14 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from('e794e45a596856bcd5412788f46752a559a4aa89fe556ab26a8c2cf0fc24cb5e', 'hex'), + root: hexToBytes('e794e45a596856bcd5412788f46752a559a4aa89fe556ab26a8c2cf0fc24cb5e'), storageRequests: [ { - accountHash: Buffer.from( - '352a47fc6863b89a6b51890ef3c1550d560886c027141d2058ba1e2d4c66d99a', - 'hex' + accountHash: hexToBytes( + '352a47fc6863b89a6b51890ef3c1550d560886c027141d2058ba1e2d4c66d99a' ), - storageRoot: Buffer.from( - '556a482068355939c95a3412bdb21213a301483edb1b64402fb66ac9f3583599', - 'hex' + storageRoot: hexToBytes( + '556a482068355939c95a3412bdb21213a301483edb1b64402fb66ac9f3583599' ), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), @@ -50,14 +49,8 @@ tape('[StorageFetcher]', async (t) => { t.equal((fetcher as any).storageRequests.length, 1, 'one storageRequests have been added') fetcher.enqueueByStorageRequestList([ { - accountHash: Buffer.from( - 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1', - 'hex' - ), - storageRoot: Buffer.from( - '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92', - 'hex' - ), + accountHash: hexToBytes('e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1'), + storageRoot: hexToBytes('69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92'), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), }, @@ -82,33 +75,31 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from(''), + root: utf8ToBytes(''), first: BigInt(1), count: BigInt(10), }) const fullResult: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] const StorageDataResponse: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] StorageDataResponse.completed = true const task = { storageRequests: [ { - accountHash: Buffer.from( - 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1', - 'hex' + accountHash: hexToBytes( + 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1' ), - storageRoot: Buffer.from( - '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92', - 'hex' + storageRoot: hexToBytes( + '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92' ), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), @@ -133,25 +124,23 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from(''), + root: utf8ToBytes(''), }) const StorageDataResponse: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] StorageDataResponse.completed = false const task = { storageRequests: [ { - accountHash: Buffer.from( - 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1', - 'hex' + accountHash: hexToBytes( + 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1' ), - storageRoot: Buffer.from( - '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92', - 'hex' + storageRoot: hexToBytes( + '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92' ), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), @@ -167,9 +156,9 @@ tape('[StorageFetcher]', async (t) => { t.equal(results, undefined, 'Process should not return full results yet') const remainingStorageData: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] remainingStorageData.completed = true @@ -186,32 +175,30 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from(''), + root: utf8ToBytes(''), }) const partialResult: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] const task = { storageRequests: [ { - accountHash: Buffer.from( - 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1', - 'hex' + accountHash: hexToBytes( + 'e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1' ), - storageRoot: Buffer.from( - '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92', - 'hex' + storageRoot: hexToBytes( + '69522138e4770e642ec8d7bd5e2b71a23fb732bb447cd4faf838b45cfe3b2a92' ), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), }, ], } - const resData = RLP.decode(Buffer.from(_storageRangesRLP, 'hex')) as unknown + const resData = RLP.decode(hexToBytes(_storageRangesRLP)) as unknown const res = p.decode( p.messages.filter((message) => message.name === 'StorageRanges')[0], resData @@ -232,10 +219,8 @@ tape('[StorageFetcher]', async (t) => { await fetcher.request(job as any) td.verify( job.peer.snap.getStorageRanges({ - root: Buffer.from(''), - accounts: [ - Buffer.from('e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1', 'hex'), - ], + root: utf8ToBytes(''), + accounts: [hexToBytes('e9a5016cb1a53dbc750d06e725514ac164231d71853cafdcbff42f5adb6ca6f1')], origin: td.matchers.anything(), limit: td.matchers.anything(), bytes: BigInt(50000), @@ -252,32 +237,30 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from(''), + root: utf8ToBytes(''), }) const partialResult: any = [ [ - [{ hash: Buffer.from(''), body: Buffer.from('') }], - [{ hash: Buffer.from(''), body: Buffer.from('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], + [{ hash: utf8ToBytes(''), body: utf8ToBytes('') }], ], ] const task = { storageRequests: [ { - accountHash: Buffer.from( - '00009e5969eba9656d7e4dad5b0596241deb87c29bbab71c23b602c2b88a7276', - 'hex' + accountHash: hexToBytes( + '00009e5969eba9656d7e4dad5b0596241deb87c29bbab71c23b602c2b88a7276' ), - storageRoot: Buffer.from( - '4431bd7d69241190bb930b74485c1e31ff75552f67d758d0b6612e7bd9226121', - 'hex' + storageRoot: hexToBytes( + '4431bd7d69241190bb930b74485c1e31ff75552f67d758d0b6612e7bd9226121' ), first: BigInt(0), count: BigInt(2) ** BigInt(256) - BigInt(1), }, ], } - const resData = RLP.decode(Buffer.from(_storageRangesRLP, 'hex')) as unknown + const resData = RLP.decode(hexToBytes(_storageRangesRLP)) as unknown const res = p.decode( p.messages.filter((message) => message.name === 'StorageRanges')[0], resData @@ -315,14 +298,13 @@ tape('[StorageFetcher]', async (t) => { // We have not been able to captured valid storage proof yet but we can try invalid // proof for coverage. A valid proof test can be added later - const accResData = RLP.decode(Buffer.from(_accountRangeRLP, 'hex')) as unknown + const accResData = RLP.decode(hexToBytes(_accountRangeRLP)) as unknown const { proof: proofInvalid } = p.decode( p.messages.filter((message) => message.name === 'AccountRange')[0], accResData ) - const dummyStorageRoot = Buffer.from( - '39ed8daab7679c0b1b7cf3667c50108185d4d9d1431c24a1c35f696a58277f8f', - 'hex' + const dummyStorageRoot = hexToBytes( + '39ed8daab7679c0b1b7cf3667c50108185d4d9d1431c24a1c35f696a58277f8f' ) const dummyOrigin = Buffer.alloc(32) try { @@ -349,7 +331,7 @@ tape('[StorageFetcher]', async (t) => { const fetcher = new StorageFetcher({ config, pool, - root: Buffer.from(''), + root: utf8ToBytes(''), first: BigInt(1), count: BigInt(10), }) diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 1df28c1689c..27af19a506c 100644 --- a/packages/devp2p/src/rlpx/peer.ts +++ b/packages/devp2p/src/rlpx/peer.ts @@ -602,6 +602,7 @@ export class Peer extends EventEmitter { } protocolObj.protocol._handleMessage(msgCode, payload) } catch (err: any) { + console.log(err) this.disconnect(DISCONNECT_REASONS.SUBPROTOCOL_ERROR) this._logger(`Error on peer subprotocol message handling: ${err}`) this.emit('error', err) diff --git a/packages/evm/test/eips/eip-3860.spec.ts b/packages/evm/test/eips/eip-3860.spec.ts index e4a0b43efd8..154ad5f41f2 100644 --- a/packages/evm/test/eips/eip-3860.spec.ts +++ b/packages/evm/test/eips/eip-3860.spec.ts @@ -145,7 +145,7 @@ tape('EIP 3860 tests', (t) => { // It tries to deploy a contract too large, where the code is all zeros // (since memory which is not allocated/resized to yet is always defaulted to 0) data: Buffer.concat([ - Buffer.from('00'.repeat(Number(common.param('vm', 'maxInitCodeSize')) + 1), 'hex'), + hexToBytes('00'.repeat(Number(common.param('vm', 'maxInitCodeSize')) + 1)), buffer, ]), } @@ -185,7 +185,7 @@ tape('EIP 3860 tests', (t) => { // (the initcode of this contract is just zeros, so STOP opcode // It stores the topmost stack item of this CREATE(2) at slot 0 // This is either the contract address if it was succesful, or 0 in case of error - const factoryCode = Buffer.from('600060003560006000' + code + '600055', 'hex') + const factoryCode = hexToBytes('600060003560006000' + code + '600055') await evm.eei.putContractCode(contractFactory, factoryCode) await evmDisabled.eei.putContractCode(contractFactory, factoryCode) @@ -194,13 +194,13 @@ tape('EIP 3860 tests', (t) => { from: caller, to: contractFactory, gasLimit: BigInt(0xfffffffff), - data: Buffer.from('00'.repeat(30) + 'C001', 'hex'), + data: hexToBytes('00'.repeat(30) + 'C001'), } await evm.runCall(runCallArgs) await evmDisabled.runCall(runCallArgs) - const key0 = Buffer.from('00'.repeat(32), 'hex') + const key0 = hexToBytes('00'.repeat(32)) const storageActive = await evm.eei.getContractStorage(contractFactory, key0) const storageInactive = await evmDisabled.eei.getContractStorage(contractFactory, key0) @@ -219,7 +219,7 @@ tape('EIP 3860 tests', (t) => { from: caller, to: contractFactory, gasLimit: BigInt(0xfffffffff), - data: Buffer.from('00'.repeat(30) + 'C000', 'hex'), + data: hexToBytes('00'.repeat(30) + 'C000'), } // Test: diff --git a/packages/evm/test/runCall.spec.ts b/packages/evm/test/runCall.spec.ts index 1f5c75d046f..98d4abe245f 100644 --- a/packages/evm/test/runCall.spec.ts +++ b/packages/evm/test/runCall.spec.ts @@ -621,7 +621,7 @@ tape('step event: ensure EVM memory and not internal memory gets reported', asyn const eei = await getEEI() const evm = await EVM.create({ common, eei }) - const contractCode = Buffer.from('600060405200', 'hex') // PUSH 0 PUSH 40 MSTORE STOP + const contractCode = hexToBytes('600060405200') // PUSH 0 PUSH 40 MSTORE STOP const contractAddress = Address.fromString('0x000000000000000000000000636F6E7472616374') await eei.putContractCode(contractAddress, contractCode) From 71e3a3a398bd19ca8d52ecb4e1ea3891f3d80e57 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 28 Mar 2023 21:01:48 -0400 Subject: [PATCH 04/32] Switch db to view --- packages/client/lib/config.ts | 2 +- packages/client/lib/execution/level.ts | 2 +- packages/client/lib/util/metaDBManager.ts | 2 +- packages/trie/benchmarks/engines/level.ts | 2 +- packages/trie/recipes/level.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client/lib/config.ts b/packages/client/lib/config.ts index 571f64667f8..13b2c0e582a 100644 --- a/packages/client/lib/config.ts +++ b/packages/client/lib/config.ts @@ -539,7 +539,7 @@ export class Config { static async getClientKey(datadir: string, common: Common) { const networkDir = `${datadir}/${common.chainName()}` const db = this.getConfigDB(networkDir) - const encodingOpts = { keyEncoding: 'utf8', valueEncoding: 'buffer' } + const encodingOpts = { keyEncoding: 'utf8', valueEncoding: 'view' } const dbKey = 'config:client_key' let key try { diff --git a/packages/client/lib/execution/level.ts b/packages/client/lib/execution/level.ts index 08398f739d5..78f88a5ad6a 100644 --- a/packages/client/lib/execution/level.ts +++ b/packages/client/lib/execution/level.ts @@ -3,7 +3,7 @@ import { MemoryLevel } from 'memory-level' import type { BatchDBOp, DB } from '@ethereumjs/trie' import type { AbstractLevel } from 'abstract-level' -export const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } +export const ENCODING_OPTS = { keyEncoding: 'view', valueEncoding: 'view' } /** * LevelDB is a thin wrapper around the underlying levelup db, diff --git a/packages/client/lib/util/metaDBManager.ts b/packages/client/lib/util/metaDBManager.ts index c20feb68bd2..dc067a65478 100644 --- a/packages/client/lib/util/metaDBManager.ts +++ b/packages/client/lib/util/metaDBManager.ts @@ -4,7 +4,7 @@ import type { Chain } from '../blockchain' import type { Config } from '../config' import type { AbstractLevel } from 'abstract-level' -const encodingOpts = { keyEncoding: 'buffer', valueEncoding: 'buffer' } +const encodingOpts = { keyEncoding: 'view', valueEncoding: 'view' } /** * Number prepended to the db key to avoid collisions diff --git a/packages/trie/benchmarks/engines/level.ts b/packages/trie/benchmarks/engines/level.ts index e172c9adfee..0064b15e2cd 100644 --- a/packages/trie/benchmarks/engines/level.ts +++ b/packages/trie/benchmarks/engines/level.ts @@ -4,7 +4,7 @@ import { MemoryLevel } from 'memory-level' import type { BatchDBOp, DB } from '../../src/types' import type { AbstractLevel } from 'abstract-level' -export const ENCODING_OPTS = { keyEncoding: 'Uint8Array', valueEncoding: 'Uint8Array' } +export const ENCODING_OPTS = { keyEncoding: 'view', valueEncoding: 'view' } /** * LevelDB is a thin wrapper around the underlying levelup db, diff --git a/packages/trie/recipes/level.ts b/packages/trie/recipes/level.ts index 6a7744a441a..6d6814d70db 100644 --- a/packages/trie/recipes/level.ts +++ b/packages/trie/recipes/level.ts @@ -3,7 +3,7 @@ import { MemoryLevel } from 'memory-level' import type { BatchDBOp, DB } from '@ethereumjs/trie' import type { AbstractLevel } from 'abstract-level' -const ENCODING_OPTS = { keyEncoding: 'buffer', valueEncoding: 'buffer' } +const ENCODING_OPTS = { keyEncoding: 'view', valueEncoding: 'view' } export class LevelDB implements DB { readonly _leveldb: AbstractLevel< From f35554d9662c308fd9d3494bfde4a493e5cc862c Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 28 Mar 2023 21:06:24 -0400 Subject: [PATCH 05/32] buffer cleanup --- .../test/sync/fetcher/storagefetcher.spec.ts | 2 +- packages/devp2p/src/util.ts | 2 +- packages/evm/src/memory.ts | 2 +- .../evm/src/precompiles/0a-bls12-g1add.ts | 2 +- .../evm/src/precompiles/0b-bls12-g1mul.ts | 2 +- .../evm/src/precompiles/0d-bls12-g2add.ts | 2 +- .../evm/src/precompiles/0e-bls12-g2mul.ts | 2 +- .../evm/src/precompiles/util/bls12_381.ts | 14 ++++---- packages/evm/test/eips/eip-3860.spec.ts | 8 ++--- packages/tx/src/types.ts | 6 ++-- packages/util/src/bytes.ts | 2 +- packages/util/src/types.ts | 2 +- packages/util/test/account.spec.ts | 32 +++++++++++-------- packages/util/test/bytes.spec.ts | 4 +-- 14 files changed, 44 insertions(+), 38 deletions(-) diff --git a/packages/client/test/sync/fetcher/storagefetcher.spec.ts b/packages/client/test/sync/fetcher/storagefetcher.spec.ts index 168b2fe0ed6..6cc76198c1b 100644 --- a/packages/client/test/sync/fetcher/storagefetcher.spec.ts +++ b/packages/client/test/sync/fetcher/storagefetcher.spec.ts @@ -306,7 +306,7 @@ tape('[StorageFetcher]', async (t) => { const dummyStorageRoot = hexToBytes( '39ed8daab7679c0b1b7cf3667c50108185d4d9d1431c24a1c35f696a58277f8f' ) - const dummyOrigin = Buffer.alloc(32) + const dummyOrigin = new Uint8Array(32) try { await fetcher['verifyRangeProof'](dummyStorageRoot, dummyOrigin, { slots, diff --git a/packages/devp2p/src/util.ts b/packages/devp2p/src/util.ts index fa0afc447c0..a744dd96fdc 100644 --- a/packages/devp2p/src/util.ts +++ b/packages/devp2p/src/util.ts @@ -126,7 +126,7 @@ export function toNewUint8Array(buf: Uint8Array): Uint8Array { /*************************** ************************************************************/ // Methods borrowed from `node-ip` by Fedor Indutny (https://github.com/indutny/node-ip) -// and modified to use Uint8Arrays instead of Buffers +// and modified to use Uint8Arrays instead of Uint8Arrays export const ipToString = (bytes: Uint8Array, offset?: number, length?: number) => { offset = offset !== undefined ? ~~offset : 0 length = length ?? bytes.length - offset diff --git a/packages/evm/src/memory.ts b/packages/evm/src/memory.ts index 85eafd06041..de06282c5d7 100644 --- a/packages/evm/src/memory.ts +++ b/packages/evm/src/memory.ts @@ -75,7 +75,7 @@ export class Memory { return loaded } const returnBytes = new Uint8Array(size) - // Copy the stored "buffer" from memory into the return Buffer + // Copy the stored "buffer" from memory into the return Uint8Array returnBytes.set(loaded) return returnBytes diff --git a/packages/evm/src/precompiles/0a-bls12-g1add.ts b/packages/evm/src/precompiles/0a-bls12-g1add.ts index edbd32748c9..1388851876f 100644 --- a/packages/evm/src/precompiles/0a-bls12-g1add.ts +++ b/packages/evm/src/precompiles/0a-bls12-g1add.ts @@ -57,7 +57,7 @@ export async function precompile0a(opts: PrecompileInput): Promise { } } - // convert input to mcl G1 points, add them, and convert the output to a Buffer. + // convert input to mcl G1 points, add them, and convert the output to a Uint8Array. let mclPoint1 let mclPoint2 try { diff --git a/packages/evm/src/precompiles/0b-bls12-g1mul.ts b/packages/evm/src/precompiles/0b-bls12-g1mul.ts index b6253c48dad..b006d88ea12 100644 --- a/packages/evm/src/precompiles/0b-bls12-g1mul.ts +++ b/packages/evm/src/precompiles/0b-bls12-g1mul.ts @@ -59,7 +59,7 @@ export async function precompile0b(opts: PrecompileInput): Promise { } } - // convert input to mcl G1 points, add them, and convert the output to a Buffer. + // convert input to mcl G1 points, add them, and convert the output to a Uint8Array. let mclPoint try { diff --git a/packages/evm/src/precompiles/0d-bls12-g2add.ts b/packages/evm/src/precompiles/0d-bls12-g2add.ts index 3fdb64351ce..620bf616732 100644 --- a/packages/evm/src/precompiles/0d-bls12-g2add.ts +++ b/packages/evm/src/precompiles/0d-bls12-g2add.ts @@ -63,7 +63,7 @@ export async function precompile0d(opts: PrecompileInput): Promise { // TODO: verify that point is on G2 - // convert input to mcl G2 points, add them, and convert the output to a Buffer. + // convert input to mcl G2 points, add them, and convert the output to a Uint8Array. let mclPoint1 let mclPoint2 diff --git a/packages/evm/src/precompiles/0e-bls12-g2mul.ts b/packages/evm/src/precompiles/0e-bls12-g2mul.ts index 1b25251d658..546e1964a4c 100644 --- a/packages/evm/src/precompiles/0e-bls12-g2mul.ts +++ b/packages/evm/src/precompiles/0e-bls12-g2mul.ts @@ -63,7 +63,7 @@ export async function precompile0e(opts: PrecompileInput): Promise { // TODO: verify that point is on G2 - // convert input to mcl G2 point/Fr point, add them, and convert the output to a Buffer. + // convert input to mcl G2 point/Fr point, add them, and convert the output to a Uint8Array. let mclPoint try { mclPoint = BLS12_381_ToG2Point(opts.data.subarray(0, 256), mcl) diff --git a/packages/evm/src/precompiles/util/bls12_381.ts b/packages/evm/src/precompiles/util/bls12_381.ts index 0fe8b79814b..b307a51b6ca 100644 --- a/packages/evm/src/precompiles/util/bls12_381.ts +++ b/packages/evm/src/precompiles/util/bls12_381.ts @@ -139,8 +139,8 @@ export const gasDiscountPairs = [ [127, 175], [128, 174], ] -// convert an input Buffer to a mcl G1 point -// this does /NOT/ do any input checks. the input Buffer needs to be of length 128 +// convert an input Uint8Array to a mcl G1 point +// this does /NOT/ do any input checks. the input Uint8Array needs to be of length 128 // it does raise an error if the point is not on the curve. function BLS12_381_ToG1Point(input: Uint8Array, mcl: any): any { const p_x = bytesToHex(input.subarray(16, 64)) @@ -178,7 +178,7 @@ function BLS12_381_ToG1Point(input: Uint8Array, mcl: any): any { } // input: a mcl G1 point -// output: a 128-byte Buffer +// output: a 128-byte Uint8Array function BLS12_381_FromG1Point(input: any): Uint8Array { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 " @@ -200,8 +200,8 @@ function BLS12_381_FromG1Point(input: any): Uint8Array { return concatBytesNoTypeCheck(xBuffer, yBuffer) } -// convert an input Buffer to a mcl G2 point -// this does /NOT/ do any input checks. the input Buffer needs to be of length 256 +// convert an input Uint8Array to a mcl G2 point +// this does /NOT/ do any input checks. the input Uint8Array needs to be of length 256 function BLS12_381_ToG2Point(input: Uint8Array, mcl: any): any { const p_x_1 = input.subarray(0, 64) const p_x_2 = input.subarray(64, 128) @@ -251,7 +251,7 @@ function BLS12_381_ToG2Point(input: Uint8Array, mcl: any): any { } // input: a mcl G2 point -// output: a 256-byte Buffer +// output: a 256-byte Uint8Array function BLS12_381_FromG2Point(input: any): Uint8Array { // TODO: figure out if there is a better way to decode these values. const decodeStr = input.getStr(16) //return a string of pattern "1 " @@ -276,7 +276,7 @@ function BLS12_381_FromG2Point(input: any): Uint8Array { return concatBytesNoTypeCheck(xBuffer1, xBuffer2, yBuffer1, yBuffer2) } -// input: a 32-byte hex scalar Buffer +// input: a 32-byte hex scalar Uint8Array // output: a mcl Fr point function BLS12_381_ToFrPoint(input: Uint8Array, mcl: any): any { diff --git a/packages/evm/test/eips/eip-3860.spec.ts b/packages/evm/test/eips/eip-3860.spec.ts index 154ad5f41f2..1e53d6e6ed4 100644 --- a/packages/evm/test/eips/eip-3860.spec.ts +++ b/packages/evm/test/eips/eip-3860.spec.ts @@ -135,7 +135,7 @@ tape('EIP 3860 tests', (t) => { const eei = await getEEI() const evm = await EVM.create({ common, eei, allowUnlimitedInitCodeSize: true }) - const buffer = Buffer.allocUnsafe(1000000).fill(0x60) + const buffer = new Uint8Array(1000000).fill(0x60) // setup the call arguments const runCallArgs = { @@ -144,10 +144,10 @@ tape('EIP 3860 tests', (t) => { // Simple test, PUSH PUSH 0 RETURN // It tries to deploy a contract too large, where the code is all zeros // (since memory which is not allocated/resized to yet is always defaulted to 0) - data: Buffer.concat([ + data: concatBytesNoTypeCheck( hexToBytes('00'.repeat(Number(common.param('vm', 'maxInitCodeSize')) + 1)), - buffer, - ]), + buffer + ), } const result = await evm.runCall(runCallArgs) st.ok( diff --git a/packages/tx/src/types.ts b/packages/tx/src/types.ts index ebc95b241ff..ff1187624fe 100644 --- a/packages/tx/src/types.ts +++ b/packages/tx/src/types.ts @@ -258,12 +258,12 @@ export interface BlobEIP4844TxData extends FeeMarketEIP1559TxData { } /** - * Buffer values array for a legacy {@link Transaction} + * Bytes values array for a legacy {@link Transaction} */ export type TxValuesArray = Uint8Array[] /** - * Buffer values array for an {@link AccessListEIP2930Transaction} + * Bytes values array for an {@link AccessListEIP2930Transaction} */ export type AccessListEIP2930ValuesArray = [ Uint8Array, @@ -280,7 +280,7 @@ export type AccessListEIP2930ValuesArray = [ ] /** - * Buffer values array for a {@link FeeMarketEIP1559Transaction} + * Bytes values array for a {@link FeeMarketEIP1559Transaction} */ export type FeeMarketEIP1559ValuesArray = [ Uint8Array, diff --git a/packages/util/src/bytes.ts b/packages/util/src/bytes.ts index e276b85b95e..faaec5d480a 100644 --- a/packages/util/src/bytes.ts +++ b/packages/util/src/bytes.ts @@ -334,7 +334,7 @@ export const toUtf8 = function (hex: string): string { * * Note: This method is useful for validating that RLP encoded integers comply with the rule that all * integer values encoded to RLP must be in the most compact form and contain no leading zero bytes - * @param values An object containing string keys and Buffer values + * @param values An object containing string keys and Uint8Array values * @throws if any provided value is found to have leading zero bytes */ export const validateNoLeadingZeroes = function (values: { diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index 1c4c6e4a4df..144672eb3c9 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -12,7 +12,7 @@ import type { ToBytesInputTypes } from './bytes' export type BigIntLike = bigint | PrefixedHexString | number | Uint8Array /* - * A type that represents an input that can be converted to a Buffer. + * A type that represents an input that can be converted to a Uint8Array. */ export type BytesLike = | Uint8Array diff --git a/packages/util/test/account.spec.ts b/packages/util/test/account.spec.ts index 082d3a477b5..d2856eb2fab 100644 --- a/packages/util/test/account.spec.ts +++ b/packages/util/test/account.spec.ts @@ -197,7 +197,7 @@ tape('Utility Functions', function (t) { st.notOk(isValidPrivate(hexToBytes(tmp)), 'should fail on too big input') st.notOk( - isValidPrivate(('WRONG_INPUT_TYPE') as Buffer), + isValidPrivate(('WRONG_INPUT_TYPE') as Uint8Array), 'should fail on wrong input type' ) @@ -311,8 +311,8 @@ tape('Utility Functions', function (t) { ) st.throws(function () { - importPublic((pubKey) as Buffer) - }, 'should throw if input is not Buffer') + importPublic((pubKey) as Uint8Array) + }, 'should throw if input is not Uint8Array') st.end() }) @@ -429,12 +429,18 @@ tape('Utility Functions', function (t) { t.test('generateAddress wt.testh non-buffer inputs', function (st) { st.throws(function () { - generateAddress(('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Buffer, toBytes(0)) - }, 'should throw if address is not Buffer') + generateAddress( + ('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39') as Uint8Array, + toBytes(0) + ) + }, 'should throw if address is not Uint8Array') st.throws(function () { - generateAddress(toBytes('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), (0) as Buffer) - }, 'should throw if nonce is not Buffer') + generateAddress( + toBytes('0x990ccf8a0de58091c028d6ff76bb235ee67c1c39'), + (0) as Uint8Array + ) + }, 'should throw if nonce is not Uint8Array') st.end() }) @@ -455,16 +461,16 @@ tape('Utility Functions', function (t) { const { address, salt, initCode } = eip1014Testdata[0] st.throws(function () { - generateAddress2((address) as Buffer, toBytes(salt), toBytes(initCode)) - }, 'should throw if address is not Buffer') + generateAddress2((address) as Uint8Array, toBytes(salt), toBytes(initCode)) + }, 'should throw if address is not Uint8Array') st.throws(function () { - generateAddress2(toBytes(address), (salt) as Buffer, toBytes(initCode)) - }, 'should throw if salt is not Buffer') + generateAddress2(toBytes(address), (salt) as Uint8Array, toBytes(initCode)) + }, 'should throw if salt is not Uint8Array') st.throws(function () { - generateAddress2(toBytes(address), toBytes(salt), (initCode) as Buffer) - }, 'should throw if initCode is not Buffer') + generateAddress2(toBytes(address), toBytes(salt), (initCode) as Uint8Array) + }, 'should throw if initCode is not Uint8Array') st.end() }) diff --git a/packages/util/test/bytes.spec.ts b/packages/util/test/bytes.spec.ts index 497ff4a2e0c..ec7070f50cd 100644 --- a/packages/util/test/bytes.spec.ts +++ b/packages/util/test/bytes.spec.ts @@ -123,7 +123,7 @@ tape('setLengthLeft', function (t) { }) t.test('should throw if input is not a Uint8Array', function (st) { st.throws(function () { - setLengthLeft(([9, 9]) as Buffer, 3) + setLengthLeft(([9, 9]) as Uint8Array, 3) }) st.end() }) @@ -273,7 +273,7 @@ tape('toUtf8', function (t) { tape('toBytes', function (t) { t.test('should work', function (st) { - // Buffer + // Uint8Array st.ok(equalsBytes(toBytes(new Uint8Array(0)), new Uint8Array())) // Array st.ok(equalsBytes(toBytes([]), new Uint8Array())) From f16ebde3efb62a3ed0187c73bacd61f1321628ae Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 29 Mar 2023 11:18:47 -0400 Subject: [PATCH 06/32] Correctly parse heads from DB --- packages/blockchain/src/db/manager.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/blockchain/src/db/manager.ts b/packages/blockchain/src/db/manager.ts index d84911091af..5ce46bfe988 100644 --- a/packages/blockchain/src/db/manager.ts +++ b/packages/blockchain/src/db/manager.ts @@ -65,7 +65,11 @@ export class DBManager { async getHeads(): Promise<{ [key: string]: Uint8Array }> { const heads = await this.get(DBTarget.Heads) for (const key of Object.keys(heads)) { - heads[key] = Uint8Array.from(heads[key]) + // DB incorrectly stores the `uint8Array` representation of each head hash + // as a JSON object of key value pairs where the key is the array index + // and the value is the uint8 from that index of the original array + // so we convert it back to a Uint8Array before storing the heads + heads[key] = Uint8Array.from(Object.values(heads[key])) } return heads } @@ -236,7 +240,6 @@ export class DBManager { const convertedOps: DBOpData[] = ops.map((op) => op.baseDBOp) // update the current cache for each operation ops.map((op) => op.updateCache(this._cache)) - return this._db.batch(convertedOps as any) } } From 20f41b1de7f3253ffe66439aa89eebcefea9f385 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 29 Mar 2023 11:19:11 -0400 Subject: [PATCH 07/32] Fix encodings --- packages/blockchain/src/db/operation.ts | 2 +- packages/trie/recipes/level-legacy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/blockchain/src/db/operation.ts b/packages/blockchain/src/db/operation.ts index 4a6cfa9c1ae..84730ba853e 100644 --- a/packages/blockchain/src/db/operation.ts +++ b/packages/blockchain/src/db/operation.ts @@ -119,7 +119,7 @@ export class DBOp { if (operationTarget === DBTarget.Heads) { dbOperation.baseDBOp.valueEncoding = 'json' } else { - dbOperation.baseDBOp.valueEncoding = 'binary' + dbOperation.baseDBOp.valueEncoding = 'view' } return dbOperation diff --git a/packages/trie/recipes/level-legacy.ts b/packages/trie/recipes/level-legacy.ts index 0040f872693..ff37097c8b4 100644 --- a/packages/trie/recipes/level-legacy.ts +++ b/packages/trie/recipes/level-legacy.ts @@ -3,7 +3,7 @@ import level from 'level-mem' import type { BatchDBOp, DB } from '@ethereumjs/trie' import type { LevelUp } from 'levelup' -const ENCODING_OPTS = { keyEncoding: 'binary', valueEncoding: 'binary' } +const ENCODING_OPTS = { keyEncoding: 'view', valueEncoding: 'view' } export class LevelDB implements DB { readonly _leveldb: LevelUp From 61894c6d40d5f29e818a0ee100865b44eb0f3577 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 29 Mar 2023 11:59:29 -0400 Subject: [PATCH 08/32] Cast db values to uint8array --- packages/blockchain/src/db/manager.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/blockchain/src/db/manager.ts b/packages/blockchain/src/db/manager.ts index 5ce46bfe988..ec099111ba3 100644 --- a/packages/blockchain/src/db/manager.ts +++ b/packages/blockchain/src/db/manager.ts @@ -217,13 +217,13 @@ export class DBManager { if (this._cache[cacheString] === undefined) { throw new Error(`Invalid cache: ${cacheString}`) } - let value = this._cache[cacheString].get(dbKey) if (!value) { value = await this._db.get(dbKey, dbOpts) - if (value) { - this._cache[cacheString].set(dbKey, value) + if (value !== undefined) { + // Always cast values to Uint8Array since db sometimes returns values as `Buffer` + this._cache[cacheString].set(dbKey, Uint8Array.from(value)) } } From b43ab5148ce023b6bbb09659941fdfd68b16b277 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Wed, 29 Mar 2023 15:19:50 -0400 Subject: [PATCH 09/32] Fix db bug in ethash --- packages/ethash/src/index.ts | 3 +++ packages/ethash/test/miner.spec.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/ethash/src/index.ts b/packages/ethash/src/index.ts index 88c78852141..70741010050 100644 --- a/packages/ethash/src/index.ts +++ b/packages/ethash/src/index.ts @@ -318,6 +318,9 @@ export class Ethash { let data try { data = await this.cacheDB!.get(epoc, this.dbOpts) + // Fix uint8Arrays that get stored in DB as JSON dictionary of array indices and values + data.seed = Uint8Array.from(Object.values(data.seed)) + data.cache = data.cache.map((el) => Uint8Array.from(Object.values(el))) } catch (error: any) { if (error.code !== 'LEVEL_NOT_FOUND') { throw error diff --git a/packages/ethash/test/miner.spec.ts b/packages/ethash/test/miner.spec.ts index 5bd1e035cc5..2d01dd51c36 100644 --- a/packages/ethash/test/miner.spec.ts +++ b/packages/ethash/test/miner.spec.ts @@ -6,11 +6,11 @@ import * as tape from 'tape' import { Ethash } from '../src' import type { BlockHeader } from '@ethereumjs/block' - +const cacheDb = new MemoryLevel() const common = new Common({ chain: Chain.Ropsten, hardfork: Hardfork.Petersburg }) tape('Check if miner works as expected', async function (t) { - const e = new Ethash(new MemoryLevel()) + const e = new Ethash(cacheDb as any) const block = Block.fromBlockData( { @@ -54,7 +54,7 @@ tape('Check if miner works as expected', async function (t) { }) tape('Check if it is possible to mine Blocks and BlockHeaders', async function (t) { - const e = new Ethash(new MemoryLevel()) + const e = new Ethash(cacheDb as any) const block = Block.fromBlockData( { @@ -82,7 +82,7 @@ tape('Check if it is possible to mine Blocks and BlockHeaders', async function ( }) tape('Check if it is possible to stop the miner', async function (t) { - const e = new Ethash(new MemoryLevel()) + const e = new Ethash(cacheDb as any) const block = Block.fromBlockData( { @@ -104,7 +104,7 @@ tape('Check if it is possible to stop the miner', async function (t) { }) tape('Check if it is possible to stop the miner', async function (t) { - const e = new Ethash(new MemoryLevel()) + const e = new Ethash(cacheDb as any) const block: any = {} @@ -116,7 +116,7 @@ tape('Check if it is possible to stop the miner', async function (t) { }) tape('Should keep common when mining blocks or headers', async function (t) { - const e = new Ethash(new MemoryLevel()) + const e = new Ethash(cacheDb as any) const block = Block.fromBlockData( { From 3e70fd79a2685a5a4456d21c142614d3db8b3e8b Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Thu, 30 Mar 2023 09:37:23 -0400 Subject: [PATCH 10/32] Remove unused peerId check --- packages/client/lib/net/server/rlpxserver.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/client/lib/net/server/rlpxserver.ts b/packages/client/lib/net/server/rlpxserver.ts index 9d5f90eb6fb..7180f2e0d82 100644 --- a/packages/client/lib/net/server/rlpxserver.ts +++ b/packages/client/lib/net/server/rlpxserver.ts @@ -282,13 +282,7 @@ export class RlpxServer extends Server { } }) - this.rlpx.on('peer:error', (rlpxPeer: Devp2pRLPxPeer, error: Error) => { - const peerId = bytesToHex(rlpxPeer.getId() as Uint8Array) - if (peerId === null) { - return this.error(error) - } - this.error(error) - }) + this.rlpx.on('peer:error', (rlpxPeer: Devp2pRLPxPeer, error: Error) => this.error(error)) this.rlpx.on('error', (e: Error) => this.error(e)) From 047e916b0d862a2b2387760d6a6e5a340a9ebef7 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 31 Mar 2023 11:00:59 -0400 Subject: [PATCH 11/32] Add pow miner test --- packages/client/test/miner/ethash.spec.ts | 116 ++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 packages/client/test/miner/ethash.spec.ts diff --git a/packages/client/test/miner/ethash.spec.ts b/packages/client/test/miner/ethash.spec.ts new file mode 100644 index 00000000000..b2e85cc0d70 --- /dev/null +++ b/packages/client/test/miner/ethash.spec.ts @@ -0,0 +1,116 @@ +import { parseGethGenesisState } from '@ethereumjs/blockchain' +import { Common, Hardfork } from '@ethereumjs/common' +import { Address } from '@ethereumjs/util' +import { hexToBytes } from 'ethereum-cryptography/utils' +import { removeSync } from 'fs-extra' +import * as tape from 'tape' + +import { Config } from '../../lib' +import { createInlineClient } from '../sim/simutils' + +import type { EthereumClient } from '../../lib' + +const pk = hexToBytes('95a602ff1ae30a2243f400dcf002561b9743b2ae9827b1008e3714a5cc1c0cfe') +const minerAddress = Address.fromPrivateKey(pk) + +async function setupPowDevnet(prefundAddress: Address, cleanStart: boolean) { + if (cleanStart) { + removeSync(`datadir/devnet`) + } + const addr = prefundAddress.toString().slice(2) + const consensusConfig = { ethash: true } + + const defaultChainData = { + config: { + chainId: 123456, + homesteadBlock: 0, + eip150Block: 0, + eip150Hash: '0x0000000000000000000000000000000000000000000000000000000000000000', + eip155Block: 0, + eip158Block: 0, + byzantiumBlock: 0, + constantinopleBlock: 0, + petersburgBlock: 0, + istanbulBlock: 0, + berlinBlock: 0, + londonBlock: 0, + ...consensusConfig, + }, + nonce: '0x0', + timestamp: '0x614b3731', + gasLimit: '0x47b760', + difficulty: '0x1', + mixHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + coinbase: '0x0000000000000000000000000000000000000000', + number: '0x0', + gasUsed: '0x0', + parentHash: '0x0000000000000000000000000000000000000000000000000000000000000000', + baseFeePerGas: 7, + } + const extraData = '0x' + '0'.repeat(32) + const chainData = { + ...defaultChainData, + extraData, + alloc: { [addr]: { balance: '0x10000000000000000000' } }, + } + const common = Common.fromGethGenesis(chainData, { chain: 'devnet', hardfork: Hardfork.London }) + const customGenesisState = parseGethGenesisState(chainData) + + const config = new Config({ + common, + transports: ['rlpx'], + bootnodes: [], + multiaddrs: [], + discDns: false, + discV4: false, + port: 30304, + maxAccountRange: (BigInt(2) ** BigInt(256) - BigInt(1)) / BigInt(10), + maxFetcherJobs: 10, + datadir: 'devnet', + accounts: [[minerAddress, pk]], + mine: true, + }) + + try { + const client = await createInlineClient(config, common, customGenesisState) + return client + } catch (err) { + console.log(err) + throw err + } +} + +const stopClient = async (client: EthereumClient, t: tape.Test) => { + await new Promise((resolve) => { + client.config.logger.on('data', (data) => { + if (data.message.includes('Miner: Found PoW solution') === true && client.started) { + void client.stop().then(() => { + t.ok(!client.started, 'client stopped successfully') + resolve(undefined) + }) + } + }) + }) +} + +const restartClient = async (client: EthereumClient, t: tape.Test) => { + await client.start() + await new Promise((resolve) => { + client.config.logger.on('data', (data) => { + if (data.message.includes('Miner: Found PoW solution') === true && client.started) { + void client.stop().then(() => { + t.ok(!client.started, 'client found a new solution successfully') + resolve(undefined) + }) + } + }) + }) +} +tape('start client', async (t) => { + const client = await setupPowDevnet(minerAddress, true) + t.ok(client.started, 'client started successfully') + await stopClient(client, t) + const restartedClient = await setupPowDevnet(minerAddress, false) + await restartClient(restartedClient, t) + t.end() +}) From 9bcd6dcc7f520356d71249d9385af2071b76d4ee Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 31 Mar 2023 11:25:10 -0400 Subject: [PATCH 12/32] Finish test --- packages/client/test/miner/ethash.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/client/test/miner/ethash.spec.ts b/packages/client/test/miner/ethash.spec.ts index b2e85cc0d70..e9e8ef69487 100644 --- a/packages/client/test/miner/ethash.spec.ts +++ b/packages/client/test/miner/ethash.spec.ts @@ -9,6 +9,7 @@ import { Config } from '../../lib' import { createInlineClient } from '../sim/simutils' import type { EthereumClient } from '../../lib' +import type { FullEthereumService } from '../../lib/service' const pk = hexToBytes('95a602ff1ae30a2243f400dcf002561b9743b2ae9827b1008e3714a5cc1c0cfe') const minerAddress = Address.fromPrivateKey(pk) @@ -94,7 +95,6 @@ const stopClient = async (client: EthereumClient, t: tape.Test) => { } const restartClient = async (client: EthereumClient, t: tape.Test) => { - await client.start() await new Promise((resolve) => { client.config.logger.on('data', (data) => { if (data.message.includes('Miner: Found PoW solution') === true && client.started) { @@ -110,7 +110,10 @@ tape('start client', async (t) => { const client = await setupPowDevnet(minerAddress, true) t.ok(client.started, 'client started successfully') await stopClient(client, t) - const restartedClient = await setupPowDevnet(minerAddress, false) - await restartClient(restartedClient, t) + await client.open() + await ((client.service('eth') as FullEthereumService).execution as any).stateDB!.open() + await client.chain.blockchain.db.open() + await client.start() + await restartClient(client, t) t.end() }) From a23e6facdd4a0bec291143c20977599795bc79ea Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 31 Mar 2023 12:07:32 -0400 Subject: [PATCH 13/32] Move pow test to integration tests --- .../pow.spec.ts} | 31 +++---------------- 1 file changed, 5 insertions(+), 26 deletions(-) rename packages/client/test/{miner/ethash.spec.ts => integration/pow.spec.ts} (77%) diff --git a/packages/client/test/miner/ethash.spec.ts b/packages/client/test/integration/pow.spec.ts similarity index 77% rename from packages/client/test/miner/ethash.spec.ts rename to packages/client/test/integration/pow.spec.ts index e9e8ef69487..2700cff0f7d 100644 --- a/packages/client/test/miner/ethash.spec.ts +++ b/packages/client/test/integration/pow.spec.ts @@ -72,19 +72,15 @@ async function setupPowDevnet(prefundAddress: Address, cleanStart: boolean) { mine: true, }) - try { - const client = await createInlineClient(config, common, customGenesisState) - return client - } catch (err) { - console.log(err) - throw err - } + const client = await createInlineClient(config, common, customGenesisState) + return client } const stopClient = async (client: EthereumClient, t: tape.Test) => { await new Promise((resolve) => { client.config.logger.on('data', (data) => { if (data.message.includes('Miner: Found PoW solution') === true && client.started) { + t.pass('found a PoW solution') void client.stop().then(() => { t.ok(!client.started, 'client stopped successfully') resolve(undefined) @@ -94,26 +90,9 @@ const stopClient = async (client: EthereumClient, t: tape.Test) => { }) } -const restartClient = async (client: EthereumClient, t: tape.Test) => { - await new Promise((resolve) => { - client.config.logger.on('data', (data) => { - if (data.message.includes('Miner: Found PoW solution') === true && client.started) { - void client.stop().then(() => { - t.ok(!client.started, 'client found a new solution successfully') - resolve(undefined) - }) - } - }) - }) -} -tape('start client', async (t) => { +tape('PoW client test', async (t) => { + t.plan(3) const client = await setupPowDevnet(minerAddress, true) t.ok(client.started, 'client started successfully') await stopClient(client, t) - await client.open() - await ((client.service('eth') as FullEthereumService).execution as any).stateDB!.open() - await client.chain.blockchain.db.open() - await client.start() - await restartClient(client, t) - t.end() }) From 20d8d8c7fcd1a4ec142724af0b961ae39ea577a7 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Fri, 31 Mar 2023 18:34:04 +0200 Subject: [PATCH 14/32] client: lint --- packages/client/test/integration/pow.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/client/test/integration/pow.spec.ts b/packages/client/test/integration/pow.spec.ts index 2700cff0f7d..00d61f24c5e 100644 --- a/packages/client/test/integration/pow.spec.ts +++ b/packages/client/test/integration/pow.spec.ts @@ -9,7 +9,6 @@ import { Config } from '../../lib' import { createInlineClient } from '../sim/simutils' import type { EthereumClient } from '../../lib' -import type { FullEthereumService } from '../../lib/service' const pk = hexToBytes('95a602ff1ae30a2243f400dcf002561b9743b2ae9827b1008e3714a5cc1c0cfe') const minerAddress = Address.fromPrivateKey(pk) From fdf8a79d746aac837a08026ca53f875b7460d54f Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:46:09 -0400 Subject: [PATCH 15/32] rename test helper --- packages/client/test/integration/pow.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/test/integration/pow.spec.ts b/packages/client/test/integration/pow.spec.ts index 00d61f24c5e..7b05e9f7bc9 100644 --- a/packages/client/test/integration/pow.spec.ts +++ b/packages/client/test/integration/pow.spec.ts @@ -75,7 +75,7 @@ async function setupPowDevnet(prefundAddress: Address, cleanStart: boolean) { return client } -const stopClient = async (client: EthereumClient, t: tape.Test) => { +const mineBlockAndstopClient = async (client: EthereumClient, t: tape.Test) => { await new Promise((resolve) => { client.config.logger.on('data', (data) => { if (data.message.includes('Miner: Found PoW solution') === true && client.started) { @@ -93,5 +93,5 @@ tape('PoW client test', async (t) => { t.plan(3) const client = await setupPowDevnet(minerAddress, true) t.ok(client.started, 'client started successfully') - await stopClient(client, t) + await mineBlockAndstopClient(client, t) }) From cd6f43115a81c45885e7f42be1443d228270e61f Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Fri, 31 Mar 2023 13:50:36 -0400 Subject: [PATCH 16/32] Add timeout to PoW test --- packages/client/test/integration/pow.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/test/integration/pow.spec.ts b/packages/client/test/integration/pow.spec.ts index 7b05e9f7bc9..5ff93dda3ca 100644 --- a/packages/client/test/integration/pow.spec.ts +++ b/packages/client/test/integration/pow.spec.ts @@ -89,7 +89,7 @@ const mineBlockAndstopClient = async (client: EthereumClient, t: tape.Test) => { }) } -tape('PoW client test', async (t) => { +tape('PoW client test', { timeout: 60000 }, async (t) => { t.plan(3) const client = await setupPowDevnet(minerAddress, true) t.ok(client.started, 'client started successfully') From f1578985089228876e88ce1bf3c0389b92e51c92 Mon Sep 17 00:00:00 2001 From: harkamal Date: Sat, 4 Mar 2023 15:31:42 +0530 Subject: [PATCH 17/32] Update eip4844 txs to decoupled blobs spec --- package-lock.json | 26 +++++++++---------- packages/block/package.json | 2 +- packages/client/package.json | 2 +- .../test/rpc/eth/sendRawTransaction.spec.ts | 6 ++--- packages/evm/package.json | 2 +- packages/tx/package.json | 2 +- packages/tx/src/depInterfaces.ts | 6 ++--- packages/tx/src/eip4844Transaction.ts | 22 ++++++++-------- packages/tx/src/kzg/kzg.ts | 4 +-- packages/tx/src/types.ts | 6 ++--- packages/tx/src/utils/blobHelpers.ts | 8 ++++++ packages/tx/test/eip4844.spec.ts | 9 ++++--- 12 files changed, 53 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 01a1d98d679..039b7458db6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4814,9 +4814,9 @@ } }, "node_modules/c-kzg": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-1.0.9.tgz", - "integrity": "sha512-5shQs7k/f7cN0Ya7g1bTgCX7CO2emh/2mkPKrjxqkC7Y+tM9YN88MWkop9ftMMZXadvVMrxWfZ/RCqBR8jRQOQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.0.tgz", + "integrity": "sha512-HNNkHQNJsqAiAx8kRvD4yfcZAZEPxBnivG4fmSdKQFx3BEvkTiaWAdj1FVQ/IfIWyplq4AslfuqkVqlv1Kzedg==", "hasInstallScript": true, "dependencies": { "node-addon-api": "^5.0.0" @@ -17806,7 +17806,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.0" }, "engines": { "node": ">=14" @@ -17861,7 +17861,7 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", @@ -18116,7 +18116,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", @@ -18233,7 +18233,7 @@ "node": ">=14" }, "peerDependencies": { - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.0" }, "peerDependenciesMeta": { "c-kzg": { @@ -19510,7 +19510,7 @@ "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "ethereum-cryptography": "^1.1.2", "ethers": "^5.7.1" } @@ -19558,7 +19558,7 @@ "@types/jwt-simple": "^0.5.33", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "chalk": "^4.1.2", "connect": "^3.7.0", "constants-browserify": "^1.0.0", @@ -19742,7 +19742,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "level": "^8.0.0", @@ -21817,9 +21817,9 @@ "version": "3.1.2" }, "c-kzg": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-1.0.9.tgz", - "integrity": "sha512-5shQs7k/f7cN0Ya7g1bTgCX7CO2emh/2mkPKrjxqkC7Y+tM9YN88MWkop9ftMMZXadvVMrxWfZ/RCqBR8jRQOQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.0.tgz", + "integrity": "sha512-HNNkHQNJsqAiAx8kRvD4yfcZAZEPxBnivG4fmSdKQFx3BEvkTiaWAdj1FVQ/IfIWyplq4AslfuqkVqlv1Kzedg==", "requires": { "node-addon-api": "^5.0.0" }, diff --git a/packages/block/package.json b/packages/block/package.json index aea98c469b7..315fc932b9f 100644 --- a/packages/block/package.json +++ b/packages/block/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.0" }, "engines": { "node": ">=14" diff --git a/packages/client/package.json b/packages/client/package.json index 9fd6496abb1..3c1eab5a452 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -73,7 +73,7 @@ "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "fs-extra": "^10.1.0", diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index 4bbfd31ed99..be16a4655b1 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -232,14 +232,14 @@ tape('blob EIP 4844 transaction', async (t) => { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proof = kzg.computeAggregateKzgProof(blobs.map((blob) => Uint8Array.from(blob))) + const proofs = blobs.map((blob) => kzg.computeBlobKzgProof(blob)) const pk = randomBytes(32) const tx = BlobEIP4844Transaction.fromTxData( { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 1000000n, gasLimit: 0xffffn, maxFeePerGas: 10000000n, @@ -254,7 +254,7 @@ tape('blob EIP 4844 transaction', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 1000000n, gasLimit: 0xfffffn, maxFeePerGas: 100000000n, diff --git a/packages/evm/package.json b/packages/evm/package.json index cffcd0d0138..d766df154ba 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -65,7 +65,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^1.0.8", + "c-kzg": "^2.0.0", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", diff --git a/packages/tx/package.json b/packages/tx/package.json index 92087337b0c..cd7364a9d82 100644 --- a/packages/tx/package.json +++ b/packages/tx/package.json @@ -59,7 +59,7 @@ "@ethersproject/providers": "^5.7.2" }, "peerDependencies": { - "c-kzg": "^1.0.8" + "c-kzg": "^2.0.0" }, "peerDependenciesMeta": { "c-kzg": { diff --git a/packages/tx/src/depInterfaces.ts b/packages/tx/src/depInterfaces.ts index 6d3e3e5659c..79cad4a0d00 100644 --- a/packages/tx/src/depInterfaces.ts +++ b/packages/tx/src/depInterfaces.ts @@ -5,16 +5,16 @@ export interface Kzg { loadTrustedSetup(filePath: string): void freeTrustedSetup(): void blobToKzgCommitment(blob: Uint8Array): Uint8Array - computeAggregateKzgProof(blobs: Uint8Array[]): Uint8Array + computeBlobKzgProof(blob: Uint8Array): Uint8Array verifyKzgProof( polynomialKzg: Uint8Array, z: Uint8Array, y: Uint8Array, kzgProof: Uint8Array ): boolean - verifyAggregateKzgProof( + verifyBlobKzgProofBatch( blobs: Uint8Array[], expectedKzgCommitments: Uint8Array[], - kzgAggregatedProof: Uint8Array + kzgProofs: Uint8Array[] ): boolean } diff --git a/packages/tx/src/eip4844Transaction.ts b/packages/tx/src/eip4844Transaction.ts index c40a4048f3f..53b7d112c24 100644 --- a/packages/tx/src/eip4844Transaction.ts +++ b/packages/tx/src/eip4844Transaction.ts @@ -44,14 +44,14 @@ const validateBlobTransactionNetworkWrapper = ( versionedHashes: Uint8Array[], blobs: Uint8Array[], commitments: Uint8Array[], - kzgProof: Uint8Array, + kzgProofs: Uint8Array[], version: number ) => { if (!(versionedHashes.length === blobs.length && blobs.length === commitments.length)) { throw new Error('Number of versionedHashes, blobs, and commitments not all equal') } try { - kzg.verifyAggregateKzgProof(blobs, commitments, kzgProof) + kzg.verifyBlobKzgProofBatch(blobs, commitments, kzgProofs) } catch (e) { throw new Error('KZG proof cannot be verified from blobs/commitments') } @@ -82,7 +82,7 @@ export class BlobEIP4844Transaction extends BaseTransaction toBytes(blob)) this.kzgCommitments = txData.kzgCommitments?.map((commitment) => toBytes(commitment)) - this.aggregateKzgProof = toBytes(txData.kzgProof) + this.kzgProofs = txData.kzgProofs?.map((proof) => toBytes(proof)) const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) @@ -216,7 +216,7 @@ export class BlobEIP4844Transaction extends BaseTransaction Uint8Array.from(commitment)) ?? [], tx: { ...blobTxToNetworkWrapperDataFormat(this), ...to }, - kzgAggregatedProof: Uint8Array.from(this.aggregateKzgProof ?? []), + blobKzgProofs: this.kzgProofs?.map((proof) => Uint8Array.from(proof)) ?? [], }) return concatBytes(new Uint8Array([0x05]), serializedTxWrapper) } @@ -459,7 +459,7 @@ export class BlobEIP4844Transaction extends BaseTransaction { return commitments } +export const blobsToProofs = (blobs: Uint8Array[]) => { + const proofs = [] + for (const blob of blobs) { + proofs.push(kzg.computeBlobKzgProof(blob)) + } + return proofs +} + /** * Converts a vector commitment for a given data blob to its versioned hash. For 4844, this version * number will be 0x01 for KZG vector commitments but could be different if future vector commitment diff --git a/packages/tx/test/eip4844.spec.ts b/packages/tx/test/eip4844.spec.ts index 78138a8f77a..efb22159776 100644 --- a/packages/tx/test/eip4844.spec.ts +++ b/packages/tx/test/eip4844.spec.ts @@ -12,6 +12,7 @@ import * as tape from 'tape' import { BlobEIP4844Transaction, TransactionFactory, initKZG } from '../src' import { blobsToCommitments, + blobsToProofs, commitmentsToVersionedHashes, getBlobs, } from '../src/utils/blobHelpers' @@ -160,13 +161,13 @@ tape('Network wrapper tests', async (t) => { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proof = kzg.computeAggregateKzgProof(blobs) + const proofs = blobsToProofs(blobs) const unsignedTx = BlobEIP4844Transaction.fromTxData( { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -204,6 +205,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs: blobs.slice(1), kzgCommitments: commitments, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -230,6 +232,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), @@ -255,7 +258,7 @@ tape('Network wrapper tests', async (t) => { versionedHashes, blobs, kzgCommitments: commitments, - kzgProof: proof, + kzgProofs: proofs, maxFeePerDataGas: 100000000n, gasLimit: 0xffffffn, to: randomBytes(20), From add6591e44eeb6e2374bd251ab1c5d097b63ee1b Mon Sep 17 00:00:00 2001 From: harkamal Date: Tue, 7 Mar 2023 17:11:35 +0530 Subject: [PATCH 18/32] fix sharding spec and blobtx --- packages/client/test/sim/sharding.spec.ts | 7 ++++++- packages/client/test/sim/simutils.ts | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 7393bf018a4..0643e3d0dcc 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -28,6 +28,9 @@ export async function runTx(data: string, to?: string, value?: bigint) { } tape('sharding/eip4844 hardfork tests', async (t) => { + if (process.env.EXTRA_CL_PARAMS === undefined) { + process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0' + } const { teardownCallBack, result } = await startNetwork(network, client, { filterKeywords, filterOutWords, @@ -55,7 +58,9 @@ tape('sharding/eip4844 hardfork tests', async (t) => { client, 2 ** 14, pkey, - '0x3dA33B9A0894b908DdBb00d96399e506515A1009' + '0x3dA33B9A0894b908DdBb00d96399e506515A1009', + undefined, + { common } ) const eth2res = await (await fetch('http://127.0.0.1:9596/eth/v1/beacon/headers')).json() diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 4efe3d3d7a1..eaba72bee24 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -2,6 +2,7 @@ import { Blockchain } from '@ethereumjs/blockchain' import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, initKZG } from '@ethereumjs/tx' import { blobsToCommitments, + blobsToProofs, commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' @@ -22,6 +23,7 @@ import { EthereumClient } from '../../lib/client' import { Config } from '../../lib/config' import type { Common } from '@ethereumjs/common' +import type { TxOptions } from '@ethereumjs/tx' import type { ChildProcessWithoutNullStreams } from 'child_process' import type { Client } from 'jayson/promise' @@ -297,10 +299,12 @@ export const runBlobTx = async ( blobSize: number, pkey: Uint8Array, to?: string, - value?: bigint + value?: bigint, + opts?: TxOptions ) => { const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) + const proofs = blobsToProofs(blobs) const hashes = commitmentsToVersionedHashes(commitments) const sender = Address.fromPrivateKey(pkey) @@ -311,6 +315,7 @@ export const runBlobTx = async ( chainId: '0x1', blobs, kzgCommitments: commitments, + kzgProofs: proofs, versionedHashes: hashes, gas: undefined, maxFeePerDataGas: undefined, @@ -327,7 +332,7 @@ export const runBlobTx = async ( txData['gasLimit'] = BigInt(1000000) as any const nonce = await client.request('eth_getTransactionCount', [sender.toString(), 'latest'], 2.0) txData['nonce'] = BigInt(nonce.result) as any - const blobTx = BlobEIP4844Transaction.fromTxData(txData).sign(pkey) + const blobTx = BlobEIP4844Transaction.fromTxData(txData, opts).sign(pkey) const serializedWrapper = blobTx.serializeNetworkWrapper() @@ -359,12 +364,14 @@ export const createBlobTxs = async ( blobSize = 2 ** 17 - 1, pkey: Uint8Array, to?: string, - value?: bigint + value?: bigint, + opts?: TxOptions ) => { const txHashes: any = [] const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) + const proofs = blobsToProofs(blobs) const hashes = commitmentsToVersionedHashes(commitments) for (let x = 1; x <= numTxs; x++) { @@ -376,6 +383,7 @@ export const createBlobTxs = async ( chainId: '0x1', blobs, kzgCommitments: commitments, + kzgProofs: proofs, versionedHashes: hashes, gas: undefined, maxFeePerDataGas: undefined, @@ -391,7 +399,7 @@ export const createBlobTxs = async ( txData['maxFeePerDataGas'] = BigInt(1000) as any txData['gasLimit'] = BigInt(1000000) as any - const blobTx = BlobEIP4844Transaction.fromTxData(txData).sign(pkey) + const blobTx = BlobEIP4844Transaction.fromTxData(txData, opts).sign(pkey) const serializedWrapper = blobTx.serializeNetworkWrapper() await fs.appendFile('./blobs.txt', bytesToPrefixedHexString(serializedWrapper) + '\n') From b25335370a54035b30a0a16034ebc981fc6b6975 Mon Sep 17 00:00:00 2001 From: harkamal Date: Wed, 8 Mar 2023 19:43:47 +0530 Subject: [PATCH 19/32] fix the sharding muli client run --- packages/client/test/sim/sharding.spec.ts | 37 +++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 0643e3d0dcc..6bea8839688 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -2,6 +2,7 @@ import { Common } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' +import * as fs from 'node:fs' import * as tape from 'tape' import { @@ -100,24 +101,28 @@ tape('sharding/eip4844 hardfork tests', async (t) => { st.end() }) - t.test('data gas fee market tests', async (st) => { - const res = await runBlobTxsFromFile(client, './test/sim/configs/blobs.txt') - let done = false - let txReceipt - while (!done) { - txReceipt = await client.request('eth_getTransactionReceipt', [res[0]], 2.0) - if (txReceipt.result !== null) { - done = true + t.test( + 'data gas fee market tests', + { skip: !fs.existsSync('./test/sim/configs/blobs.txt') }, + async (st) => { + const res = await runBlobTxsFromFile(client, './test/sim/configs/blobs.txt') + let done = false + let txReceipt + while (!done) { + txReceipt = await client.request('eth_getTransactionReceipt', [res[0]], 2.0) + if (txReceipt.result !== null) { + done = true + } + await sleep(2000) } - await sleep(2000) + const block1 = await client.request( + 'eth_getBlockByHash', + [txReceipt.result.blockHash, false], + 2.0 + ) + st.ok(BigInt(block1.result.excessDataGas) > 0n, 'block1 has excess data gas > 0') } - const block1 = await client.request( - 'eth_getBlockByHash', - [txReceipt.result.blockHash, false], - 2.0 - ) - st.ok(BigInt(block1.result.excessDataGas) > 0n, 'block1 has excess data gas > 0') - }) + ) t.test('point precompile contract test', async (st) => { const nonce = await client.request( From ea57e81c8281a2078fdccfbda27b048fdc4af173 Mon Sep 17 00:00:00 2001 From: harkamal Date: Sat, 18 Mar 2023 17:02:18 +0530 Subject: [PATCH 20/32] fix args --- packages/client/test/sim/single-run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/test/sim/single-run.sh b/packages/client/test/sim/single-run.sh index 19a2e3bf635..d73bc183a60 100755 --- a/packages/client/test/sim/single-run.sh +++ b/packages/client/test/sim/single-run.sh @@ -87,7 +87,7 @@ case $MULTIPEER in echo "ELCLIENT=$ELCLIENT not implemented" esac - CL_PORT_ARGS="--genesisValidators 8 --startValidators 4..7 --enr.tcp 9001 --port 9001 --execution.urls http://localhost:8552 --rest.port 9597 --server http://localhost:9597 --network.connectToDiscv5Bootnodes true --bootnodes $bootEnrs" + CL_PORT_ARGS="--genesisValidators 8 --startValidators 4..7 --enr.tcp 9001 --port 9001 --execution.urls http://localhost:8552 --rest.port 9597 --server http://127.0.0.1:9597 --network.connectToDiscv5Bootnodes true --bootnodes $bootEnrs" ;; * ) @@ -105,7 +105,7 @@ case $MULTIPEER in echo "ELCLIENT=$ELCLIENT not implemented" esac - CL_PORT_ARGS="--enr.ip 127.0.0.1 --enr.tcp 9000 --enr.udp 9000" + CL_PORT_ARGS="--sync.isSingleNode --enr.ip 127.0.0.1 --enr.tcp 9000 --enr.udp 9000" if [ ! -n "$MULTIPEER" ] then echo "setting up to run as a solo node..." From 1f611784511e60e5a27e1a3654fed9f37bc845b2 Mon Sep 17 00:00:00 2001 From: harkamal Date: Sun, 19 Mar 2023 00:47:37 +0530 Subject: [PATCH 21/32] fix compatibility with latest lodestar branch --- packages/client/test/sim/4844.md | 4 ++-- packages/client/test/sim/sharding.spec.ts | 2 +- packages/client/test/sim/single-run.sh | 13 +++++++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/client/test/sim/4844.md b/packages/client/test/sim/4844.md index 5eaa42b1366..d587d9e25c0 100644 --- a/packages/client/test/sim/4844.md +++ b/packages/client/test/sim/4844.md @@ -8,7 +8,7 @@ To run a single EthereumJS client <> Lodestar CL client for testing, run the fol `NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` To run a second EthereumJS <> Lodestar pair, use this command: -`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` +`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` To send a single blob transaction to the network, you can use the `txGenerator.ts` script as follows: @@ -20,6 +20,6 @@ This script was adapted from the [interop repo blob script](https://github.com/I To run the 4844 spec tests contained in `test/sim/sharding.spec.ts`, use the following command: -`EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=/absolute/path/to/your/data/dir npm run tape -- test/sim/sharding.spec.ts` +`EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=/absolute/path/to/your/data/dir npm run tape -- test/sim/sharding.spec.ts` Note, these tests are adapted from the specification tests contained in the [EIP-4844 Interop repo](https://github.com/Inphi/eip4844-interop) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 6bea8839688..e3705616367 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -30,7 +30,7 @@ export async function runTx(data: string, to?: string, value?: bigint) { tape('sharding/eip4844 hardfork tests', async (t) => { if (process.env.EXTRA_CL_PARAMS === undefined) { - process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0' + process.env.EXTRA_CL_PARAMS = '--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0' } const { teardownCallBack, result } = await startNetwork(network, client, { filterKeywords, diff --git a/packages/client/test/sim/single-run.sh b/packages/client/test/sim/single-run.sh index d73bc183a60..0222cb298f7 100755 --- a/packages/client/test/sim/single-run.sh +++ b/packages/client/test/sim/single-run.sh @@ -247,8 +247,17 @@ else responseCmd="curl --location --request GET 'http://localhost:9596/eth/v1/beacon/headers/genesis' --header 'Content-Type: application/json' 2>/dev/null | jq \".data.root\"" CL_GENESIS_HASH=$(eval "$responseCmd") done; - # since peer1 is setup get their enr and enode - bootEnrs=$(sudo cat "$origDataDir/peer1/lodestar/enr") + + # We should curl and get boot enr + while [ ! -n "$bootEnrs" ] + do + sleep 3 + echo "Fetching bootEnrs block from peer1/bootnode ..." + ejsId=$(( ejsId +1 )) + responseCmd="curl --location --request GET 'http://localhost:9596/eth/v1/node/identity' --header 'Content-Type: application/json' 2>/dev/null | jq \".data.enr\"" + bootEnrs=$(eval "$responseCmd") + done; + elBootnode=$(cat "$origDataDir/peer1/ethereumjs/$NETWORK/rlpx"); EL_PORT_ARGS="$EL_PORT_ARGS --bootnodes $elBootnode" CL_PORT_ARGS="$CL_PORT_ARGS --bootnodes $bootEnrs" From 1f8be0374e26faf51adbe716a6fa5a8a822e529b Mon Sep 17 00:00:00 2001 From: harkamal Date: Sat, 25 Mar 2023 18:19:30 +0530 Subject: [PATCH 22/32] update test help --- packages/client/test/sim/4844.md | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/client/test/sim/4844.md b/packages/client/test/sim/4844.md index d587d9e25c0..dd004b69be2 100644 --- a/packages/client/test/sim/4844.md +++ b/packages/client/test/sim/4844.md @@ -4,22 +4,36 @@ Note: All commands should be run from the `client` package directory root (so so ## Running a local devnet -To run a single EthereumJS client <> Lodestar CL client for testing, run the following command: -`NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.EIP4844_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` +Step 1. To run a single EthereumJS client <> Lodestar CL client for testing, run the following command: +`NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory test/sim/./single-run.sh` -To run a second EthereumJS <> Lodestar pair, use this command: -`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=path/to/your/data/directory test/sim/./single-run.sh` +Step 2. (Optional) To run a second EthereumJS <> Lodestar pair, use this command: +`MULTIPEER=syncpeer NETWORK=sharding EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory test/sim/./single-run.sh` -To send a single blob transaction to the network, you can use the `txGenerator.ts` script as follows: +Step 3. To send a single blob transaction to the network, you may just run spec test: +`EXTERNAL_RUN=true npm run tape -- test/sim/sharding.spec.ts` + +OR, you can use the `txGenerator.ts` script as follows: `ts-node test/sim/txGenerator 8545 'hello'`. The first argument is the port number of the EthereumJS client you which to submit the transaction to and the second is any data to include in the blob. This script was adapted from the [interop repo blob script](https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js) + ## EIP-4844 spec tests -To run the 4844 spec tests contained in `test/sim/sharding.spec.ts`, use the following command: +You don't need to externally start the nodes, the sim tests will do all that for you as well as run the tests against it. + +Run Step 1 & 3 together: + +`LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory npm run tape -- + test/sim/sharding.spec.ts` + + +### Run Step 1, 2 & 3 together + -`EXTRA_CL_PARAMS="--params.CAPELLA_FORK_EPOCH 0 --params.DENEB_FORK_EPOCH 0" LODE_IMAGE=g11tech/lodestar:4844-ae177e DATADIR=/absolute/path/to/your/data/dir npm run tape -- test/sim/sharding.spec.ts` +`WITH_PEER=syncpeer LODE_IMAGE=g11tech/lodestar:decoupled DATADIR=path/to/your/data/directory npm run tape -- + test/sim/sharding.spec.ts` Note, these tests are adapted from the specification tests contained in the [EIP-4844 Interop repo](https://github.com/Inphi/eip4844-interop) From 622cef4bc53d33d5f05b9f4d62d797de19d1008b Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:15:49 -0400 Subject: [PATCH 23/32] Fix fee market test --- packages/client/test/sim/sharding.spec.ts | 52 ++++++++++++++--------- packages/client/test/sim/simutils.ts | 2 +- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index e3705616367..12960804e3a 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -2,10 +2,12 @@ import { Common } from '@ethereumjs/common' import { TransactionFactory } from '@ethereumjs/tx' import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' +import { randomBytes } from 'node:crypto' import * as fs from 'node:fs' import * as tape from 'tape' import { + createBlobTxs, filterKeywords, filterOutWords, runBlobTx, @@ -34,7 +36,7 @@ tape('sharding/eip4844 hardfork tests', async (t) => { } const { teardownCallBack, result } = await startNetwork(network, client, { filterKeywords, - filterOutWords, + filterOutWords: [], externalRun: process.env.EXTERNAL_RUN, withPeer: process.env.WITH_PEER, }) @@ -101,28 +103,36 @@ tape('sharding/eip4844 hardfork tests', async (t) => { st.end() }) - t.test( - 'data gas fee market tests', - { skip: !fs.existsSync('./test/sim/configs/blobs.txt') }, - async (st) => { - const res = await runBlobTxsFromFile(client, './test/sim/configs/blobs.txt') - let done = false - let txReceipt - while (!done) { - txReceipt = await client.request('eth_getTransactionReceipt', [res[0]], 2.0) - if (txReceipt.result !== null) { - done = true - } - await sleep(2000) + t.test('data gas fee market tests', async (st) => { + const txns = await createBlobTxs( + 4, + 4096, + pkey, + '0x' + randomBytes(20).toString('hex'), + undefined, + { common } + ) + const txHashes = [] + for (const txn of txns) { + const res = await client.request('eth_sendRawTransaction', [txn], 2.0) + txHashes.push(res.result) + } + let done = false + let txReceipt + while (!done) { + txReceipt = await client.request('eth_getTransactionReceipt', [txHashes[0]], 2.0) + if (txReceipt.result !== null) { + done = true } - const block1 = await client.request( - 'eth_getBlockByHash', - [txReceipt.result.blockHash, false], - 2.0 - ) - st.ok(BigInt(block1.result.excessDataGas) > 0n, 'block1 has excess data gas > 0') + await sleep(2000) } - ) + const block1 = await client.request( + 'eth_getBlockByHash', + [txReceipt.result.blockHash, false], + 2.0 + ) + st.ok(BigInt(block1.result.excessDataGas) > 0n, 'block1 has excess data gas > 0') + }) t.test('point precompile contract test', async (st) => { const nonce = await client.request( diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index eaba72bee24..334e5a9ad50 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -367,7 +367,7 @@ export const createBlobTxs = async ( value?: bigint, opts?: TxOptions ) => { - const txHashes: any = [] + const txHashes: string[] = [] const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) From 964a678c79c2575828d658e275b82d84f1df9606 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:57:42 -0400 Subject: [PATCH 24/32] lint --- packages/client/test/sim/sharding.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/client/test/sim/sharding.spec.ts b/packages/client/test/sim/sharding.spec.ts index 12960804e3a..59a5a5624f6 100644 --- a/packages/client/test/sim/sharding.spec.ts +++ b/packages/client/test/sim/sharding.spec.ts @@ -3,7 +3,6 @@ import { TransactionFactory } from '@ethereumjs/tx' import { bytesToPrefixedHexString, hexStringToBytes, privateToAddress } from '@ethereumjs/util' import { Client } from 'jayson/promise' import { randomBytes } from 'node:crypto' -import * as fs from 'node:fs' import * as tape from 'tape' import { @@ -11,7 +10,6 @@ import { filterKeywords, filterOutWords, runBlobTx, - runBlobTxsFromFile, runTxHelper, sleep, startNetwork, @@ -36,7 +34,7 @@ tape('sharding/eip4844 hardfork tests', async (t) => { } const { teardownCallBack, result } = await startNetwork(network, client, { filterKeywords, - filterOutWords: [], + filterOutWords, externalRun: process.env.EXTERNAL_RUN, withPeer: process.env.WITH_PEER, }) From 4fd3a616768c09d5efc19d56443050703b1ba5b7 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 27 Mar 2023 21:35:30 -0400 Subject: [PATCH 25/32] Move all kzg stuff to util --- package-lock.json | 10 +++++++-- packages/block/test/eip4844block.spec.ts | 7 ++++--- packages/client/bin/cli.ts | 2 +- .../devnets/4844-interop/tools/txGenerator.ts | 10 ++++++--- .../client/test/miner/pendingBlock.spec.ts | 11 +++++----- .../test/rpc/engine/getBlobsBundleV1.spec.ts | 4 ++-- .../test/rpc/eth/sendRawTransaction.spec.ts | 9 ++------ packages/client/test/sim/simutils.ts | 10 ++++----- packages/client/test/sim/txGenerator.ts | 10 ++++++--- packages/evm/package.json | 1 - .../precompiles/14-kzg-point-evaluation.ts | 3 ++- .../precompiles/14-pointevaluation.spec.ts | 10 +++++++-- packages/tx/src/depInterfaces.ts | 20 ------------------ packages/tx/src/eip4844Transaction.ts | 4 ++-- packages/tx/src/index.ts | 2 -- packages/tx/test/eip4844.spec.ts | 18 ++++++++-------- packages/util/package.json | 8 +++++++ .../{tx/src/utils => util/src}/blobHelpers.ts | 2 +- packages/util/src/index.ts | 2 ++ packages/{tx/src/kzg => util/src}/kzg.ts | 21 ++++++++++++++++++- 20 files changed, 92 insertions(+), 72 deletions(-) delete mode 100644 packages/tx/src/depInterfaces.ts rename packages/{tx/src/utils => util/src}/blobHelpers.ts (98%) rename packages/{tx/src/kzg => util/src}/kzg.ts (54%) diff --git a/package-lock.json b/package-lock.json index 039b7458db6..af46517dd94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18099,7 +18099,6 @@ "license": "MPL-2.0", "dependencies": { "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/providers": "^5.7.1", "debug": "^4.3.3", @@ -18256,6 +18255,14 @@ }, "engines": { "node": ">=14" + }, + "peerDependencies": { + "c-kzg": "^2.0.0" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } } }, "packages/vm": { @@ -19732,7 +19739,6 @@ "requires": { "@ethereumjs/common": "^3.1.1", "@ethereumjs/statemanager": "^1.0.4", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/abi": "^5.0.12", "@ethersproject/providers": "^5.7.1", diff --git a/packages/block/test/eip4844block.spec.ts b/packages/block/test/eip4844block.spec.ts index c38fe887380..9813e03254c 100644 --- a/packages/block/test/eip4844block.spec.ts +++ b/packages/block/test/eip4844block.spec.ts @@ -1,11 +1,12 @@ import { Chain, Common, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { blobsToCommitments, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { randomBytes } from '@ethereumjs/util' + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' diff --git a/packages/client/bin/cli.ts b/packages/client/bin/cli.ts index 54ba7087959..f3f97be7c09 100755 --- a/packages/client/bin/cli.ts +++ b/packages/client/bin/cli.ts @@ -4,12 +4,12 @@ import { Block } from '@ethereumjs/block' import { Blockchain, parseGethGenesisState } from '@ethereumjs/blockchain' import { Chain, Common, ConsensusAlgorithm, Hardfork } from '@ethereumjs/common' import { RLP } from '@ethereumjs/rlp' -import { initKZG } from '@ethereumjs/tx' import { Address, bytesToHex, bytesToPrefixedHexString, hexStringToBytes, + initKZG, randomBytes, short, toBytes, diff --git a/packages/client/devnets/4844-interop/tools/txGenerator.ts b/packages/client/devnets/4844-interop/tools/txGenerator.ts index 16dd356693d..53bd429a0cf 100644 --- a/packages/client/devnets/4844-interop/tools/txGenerator.ts +++ b/packages/client/devnets/4844-interop/tools/txGenerator.ts @@ -1,12 +1,16 @@ // Adapted from - https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js import { Common, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { + Address, + initKZG, blobsToCommitments, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { Address, bytesToPrefixedHexString, hexStringToBytes } from '@ethereumjs/util' + bytesToPrefixedHexString, + hexStringToBytes, +} from '@ethereumjs/util' + import * as kzg from 'c-kzg' import { randomBytes } from '@ethereumjs/util' import { Client } from 'jayson/promise' diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index cc8f56e820e..0c5b63abfe8 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -1,18 +1,17 @@ import { Block, BlockHeader } from '@ethereumjs/block' import { Common, Chain as CommonChain, Hardfork } from '@ethereumjs/common' -import { BlobEIP4844Transaction, Transaction, initKZG } from '@ethereumjs/tx' -import { - blobsToCommitments, - commitmentsToVersionedHashes, - getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' +import { BlobEIP4844Transaction, Transaction } from '@ethereumjs/tx' import { Account, Address, + blobsToCommitments, bytesToHex, bytesToPrefixedHexString, + commitmentsToVersionedHashes, equalsBytes, + getBlobs, hexStringToBytes, + initKZG, randomBytes, } from '@ethereumjs/util' import { VM } from '@ethereumjs/vm' diff --git a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts index ce608ca7a8b..0b976a85807 100644 --- a/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts +++ b/packages/client/test/rpc/engine/getBlobsBundleV1.spec.ts @@ -1,7 +1,7 @@ import { Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' -import { TransactionFactory, initKZG } from '@ethereumjs/tx' -import { Address, hexStringToBytes } from '@ethereumjs/util' +import { TransactionFactory } from '@ethereumjs/tx' +import { Address, hexStringToBytes, initKZG } from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index be16a4655b1..fedf0e4657e 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -1,18 +1,13 @@ import { BlockHeader } from '@ethereumjs/block' import { Chain, Common, Hardfork } from '@ethereumjs/common' import { DefaultStateManager } from '@ethereumjs/statemanager' -import { - BlobEIP4844Transaction, - FeeMarketEIP1559Transaction, - Transaction, - initKZG, -} from '@ethereumjs/tx' +import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' import { blobsToCommitments, commitmentsToVersionedHashes, getBlobs, } from '@ethereumjs/tx/dist/utils/blobHelpers' -import { bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' +import { bytesToPrefixedHexString, hexStringToBytes, initKZG, randomBytes } from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index 334e5a9ad50..aa697658797 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -1,16 +1,14 @@ import { Blockchain } from '@ethereumjs/blockchain' -import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx' import { + Address, blobsToCommitments, blobsToProofs, - commitmentsToVersionedHashes, - getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { - Address, bytesToHex, bytesToPrefixedHexString, bytesToUtf8, + commitmentsToVersionedHashes, + getBlobs, randomBytes, } from '@ethereumjs/util' import * as kzg from 'c-kzg' diff --git a/packages/client/test/sim/txGenerator.ts b/packages/client/test/sim/txGenerator.ts index 9f2985c6485..ca245b42463 100644 --- a/packages/client/test/sim/txGenerator.ts +++ b/packages/client/test/sim/txGenerator.ts @@ -1,10 +1,14 @@ // Adapted from - https://github.com/Inphi/eip4844-interop/blob/master/blob_tx_generator/blob.js -import { BlobEIP4844Transaction, initKZG } from '@ethereumjs/tx' +import { BlobEIP4844Transaction } from '@ethereumjs/tx' import { + Address, blobsToCommitments, + bytesToPrefixedHexString, commitmentsToVersionedHashes, -} from '@ethereumjs/tx/test/utils/blobHelpers' -import { Address, bytesToPrefixedHexString, hexStringToBytes, randomBytes } from '@ethereumjs/util' + hexStringToBytes, + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import { Client } from 'jayson/promise' const clientPort = process.argv[2] diff --git a/packages/evm/package.json b/packages/evm/package.json index d766df154ba..b73d71b099d 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -48,7 +48,6 @@ }, "dependencies": { "@ethereumjs/common": "^3.1.1", - "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@ethersproject/providers": "^5.7.1", "debug": "^4.3.3", diff --git a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts index 5db899a67b6..e257afee432 100644 --- a/packages/evm/src/precompiles/14-kzg-point-evaluation.ts +++ b/packages/evm/src/precompiles/14-kzg-point-evaluation.ts @@ -1,9 +1,10 @@ -import { computeVersionedHash, kzg } from '@ethereumjs/tx' import { bigIntToBytes, bytesToBigInt, bytesToHex, + computeVersionedHash, concatBytesNoTypeCheck, + kzg, setLengthLeft, short, } from '@ethereumjs/util' diff --git a/packages/evm/test/precompiles/14-pointevaluation.spec.ts b/packages/evm/test/precompiles/14-pointevaluation.spec.ts index b6a4ca18632..3e1ab41b7b7 100644 --- a/packages/evm/test/precompiles/14-pointevaluation.spec.ts +++ b/packages/evm/test/precompiles/14-pointevaluation.spec.ts @@ -1,6 +1,12 @@ import { Common, Hardfork } from '@ethereumjs/common' -import { computeVersionedHash, initKZG } from '@ethereumjs/tx' -import { bigIntToBytes, bytesToBigInt, concatBytesNoTypeCheck, unpadBytes } from '@ethereumjs/util' +import { + bigIntToBytes, + bytesToBigInt, + computeVersionedHash, + concatBytesNoTypeCheck, + initKZG, + unpadBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' diff --git a/packages/tx/src/depInterfaces.ts b/packages/tx/src/depInterfaces.ts deleted file mode 100644 index 79cad4a0d00..00000000000 --- a/packages/tx/src/depInterfaces.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Interface for an externally provided kzg library used when creating blob transactions - */ -export interface Kzg { - loadTrustedSetup(filePath: string): void - freeTrustedSetup(): void - blobToKzgCommitment(blob: Uint8Array): Uint8Array - computeBlobKzgProof(blob: Uint8Array): Uint8Array - verifyKzgProof( - polynomialKzg: Uint8Array, - z: Uint8Array, - y: Uint8Array, - kzgProof: Uint8Array - ): boolean - verifyBlobKzgProofBatch( - blobs: Uint8Array[], - expectedKzgCommitments: Uint8Array[], - kzgProofs: Uint8Array[] - ): boolean -} diff --git a/packages/tx/src/eip4844Transaction.ts b/packages/tx/src/eip4844Transaction.ts index 53b7d112c24..6e270bf8f4f 100644 --- a/packages/tx/src/eip4844Transaction.ts +++ b/packages/tx/src/eip4844Transaction.ts @@ -7,23 +7,23 @@ import { bytesToBigInt, bytesToHex, bytesToPrefixedHexString, + computeVersionedHash, concatBytes, ecrecover, hexStringToBytes, + kzg, toBytes, } from '@ethereumjs/util' import { keccak256 } from 'ethereum-cryptography/keccak' import { BaseTransaction } from './baseTransaction' import { LIMIT_BLOBS_PER_TX } from './constants' -import { kzg } from './kzg/kzg' import { BlobNetworkTransactionWrapper, BlobTransactionType, SignedBlobTransactionType, } from './types' import { AccessLists, blobTxToNetworkWrapperDataFormat } from './util' -import { computeVersionedHash } from './utils/blobHelpers' import type { AccessList, diff --git a/packages/tx/src/index.ts b/packages/tx/src/index.ts index 05522a3b341..90667ec674a 100644 --- a/packages/tx/src/index.ts +++ b/packages/tx/src/index.ts @@ -1,8 +1,6 @@ export { FeeMarketEIP1559Transaction } from './eip1559Transaction' export { AccessListEIP2930Transaction } from './eip2930Transaction' export { BlobEIP4844Transaction } from './eip4844Transaction' -export { initKZG, kzg } from './kzg/kzg' export { Transaction } from './legacyTransaction' export { TransactionFactory } from './transactionFactory' export * from './types' -export { computeVersionedHash } from './utils/blobHelpers' diff --git a/packages/tx/test/eip4844.spec.ts b/packages/tx/test/eip4844.spec.ts index efb22159776..2bab2a7046b 100644 --- a/packages/tx/test/eip4844.spec.ts +++ b/packages/tx/test/eip4844.spec.ts @@ -1,21 +1,21 @@ import { Common, Hardfork } from '@ethereumjs/common' import { + blobsToCommitments, + blobsToProofs, bytesToHex, + commitmentsToVersionedHashes, concatBytes, equalsBytes, + getBlobs, hexStringToBytes, - randomBytes, + initKZG, } from '@ethereumjs/util' import * as kzg from 'c-kzg' +import { randomBytes } from 'crypto' +import { hexToBytes } from 'ethereum-cryptography/utils' import * as tape from 'tape' -import { BlobEIP4844Transaction, TransactionFactory, initKZG } from '../src' -import { - blobsToCommitments, - blobsToProofs, - commitmentsToVersionedHashes, - getBlobs, -} from '../src/utils/blobHelpers' +import { BlobEIP4844Transaction, TransactionFactory } from '../src' // Hack to detect if running in browser or not const isBrowser = new Function('try {return this===window;}catch(e){ return false;}') @@ -292,7 +292,7 @@ tape('hash() and signature verification', async (t) => { chainId: 1, nonce: 1, versionedHashes: [ - hexStringToBytes('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e'), + hexToBytes('01624652859a6e98ffc1608e2af0147ca4e86e1ce27672d8d3f3c9d4ffd6ef7e'), ], maxFeePerDataGas: 10000000n, gasLimit: 123457n, diff --git a/packages/util/package.json b/packages/util/package.json index 4ba531a21be..f012b1bcf0e 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -91,6 +91,14 @@ "@types/bn.js": "^5.1.0", "@types/secp256k1": "^4.0.1" }, + "peerDependencies": { + "c-kzg": "^2.0.0" + }, + "peerDependenciesMeta": { + "c-kzg": { + "optional": true + } + }, "engines": { "node": ">=14" } diff --git a/packages/tx/src/utils/blobHelpers.ts b/packages/util/src/blobHelpers.ts similarity index 98% rename from packages/tx/src/utils/blobHelpers.ts rename to packages/util/src/blobHelpers.ts index 5fec0e89932..8b5c703c312 100644 --- a/packages/tx/src/utils/blobHelpers.ts +++ b/packages/util/src/blobHelpers.ts @@ -1,7 +1,7 @@ import { sha256 } from 'ethereum-cryptography/sha256' import { utf8ToBytes } from 'ethereum-cryptography/utils' -import { kzg } from '../kzg/kzg' +import { kzg } from './kzg' /** * These utilities for constructing blobs are borrowed from https://github.com/Inphi/eip4844-interop.git diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index df57cde6547..9a05428d06e 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -52,6 +52,7 @@ export * from './encoding' * Export ethjs-util methods */ export * from './asyncEventEmitter' +export * from './blobHelpers' export { arrayContainsArray, fromAscii, @@ -64,4 +65,5 @@ export { stripHexPrefix, toAscii, } from './internal' +export * from './kzg' export * from './lock' diff --git a/packages/tx/src/kzg/kzg.ts b/packages/util/src/kzg.ts similarity index 54% rename from packages/tx/src/kzg/kzg.ts rename to packages/util/src/kzg.ts index 15c176639c5..7e20ee69fe3 100644 --- a/packages/tx/src/kzg/kzg.ts +++ b/packages/util/src/kzg.ts @@ -1,4 +1,23 @@ -import type { Kzg } from '../depInterfaces' +/** + * Interface for an externally provided kzg library used when creating blob transactions + */ +export interface Kzg { + loadTrustedSetup(filePath: string): void + freeTrustedSetup(): void + blobToKzgCommitment(blob: Uint8Array): Uint8Array + computeBlobKzgProof(blob: Uint8Array): Uint8Array + verifyKzgProof( + polynomialKzg: Uint8Array, + z: Uint8Array, + y: Uint8Array, + kzgProof: Uint8Array + ): boolean + verifyBlobKzgProofBatch( + blobs: Uint8Array[], + expectedKzgCommitments: Uint8Array[], + kzgProofs: Uint8Array[] + ): boolean +} function kzgNotLoaded(): never { throw Error('kzg library not loaded') From c46436280775b43d02c984876d2729ad963359a9 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 3 Apr 2023 10:42:49 -0400 Subject: [PATCH 26/32] Various cleanup --- packages/util/src/kzg.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/util/src/kzg.ts b/packages/util/src/kzg.ts index 7e20ee69fe3..14f69414d91 100644 --- a/packages/util/src/kzg.ts +++ b/packages/util/src/kzg.ts @@ -39,5 +39,10 @@ export let kzg: Kzg = { */ export function initKZG(kzgLib: Kzg, trustedSetupPath: string) { kzg = kzgLib + try { + // Always try to free trusted setup before loading (in case loaded by different module) + kzg.freeTrustedSetup() + // eslint-disable-next-line + } catch {} kzg.loadTrustedSetup(trustedSetupPath) } From 81bebd024f1ce48ec0ade57fa9d74f4965165680 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:13:00 -0400 Subject: [PATCH 27/32] Update to latest c-kzg --- package-lock.json | 16 +++++++++------- packages/client/package.json | 2 +- .../test/rpc/eth/sendRawTransaction.spec.ts | 15 +++++++-------- packages/devp2p/src/rlpx/peer.ts | 1 - packages/tx/test/eip4844.spec.ts | 2 +- packages/util/src/blobHelpers.ts | 8 +++----- packages/util/src/kzg.ts | 10 ++-------- 7 files changed, 23 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index af46517dd94..edc3f86573c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4814,11 +4814,12 @@ } }, "node_modules/c-kzg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.0.tgz", - "integrity": "sha512-HNNkHQNJsqAiAx8kRvD4yfcZAZEPxBnivG4fmSdKQFx3BEvkTiaWAdj1FVQ/IfIWyplq4AslfuqkVqlv1Kzedg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.2.tgz", + "integrity": "sha512-enFknANTmTFe/GqMEcnH1eKSSuMgpT3u7TXqVmOrv3PYprWaW8k827fgBuVzFDvcOnuRqw1Cp1myHQfZpKuibw==", "hasInstallScript": true, "dependencies": { + "bindings": "^1.5.0", "node-addon-api": "^5.0.0" } }, @@ -17861,7 +17862,7 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.2", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", @@ -21823,10 +21824,11 @@ "version": "3.1.2" }, "c-kzg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.0.tgz", - "integrity": "sha512-HNNkHQNJsqAiAx8kRvD4yfcZAZEPxBnivG4fmSdKQFx3BEvkTiaWAdj1FVQ/IfIWyplq4AslfuqkVqlv1Kzedg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.2.tgz", + "integrity": "sha512-enFknANTmTFe/GqMEcnH1eKSSuMgpT3u7TXqVmOrv3PYprWaW8k827fgBuVzFDvcOnuRqw1Cp1myHQfZpKuibw==", "requires": { + "bindings": "^1.5.0", "node-addon-api": "^5.0.0" }, "dependencies": { diff --git a/packages/client/package.json b/packages/client/package.json index 3c1eab5a452..81f027eb72e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -70,10 +70,10 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", + "c-kzg": "^2.0.2", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", - "c-kzg": "^2.0.0", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "fs-extra": "^10.1.0", diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index fedf0e4657e..6e120dfa133 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -4,10 +4,13 @@ import { DefaultStateManager } from '@ethereumjs/statemanager' import { BlobEIP4844Transaction, FeeMarketEIP1559Transaction, Transaction } from '@ethereumjs/tx' import { blobsToCommitments, + bytesToPrefixedHexString, commitmentsToVersionedHashes, getBlobs, -} from '@ethereumjs/tx/dist/utils/blobHelpers' -import { bytesToPrefixedHexString, hexStringToBytes, initKZG, randomBytes } from '@ethereumjs/util' + hexStringToBytes, + initKZG, + randomBytes, +} from '@ethereumjs/util' import * as kzg from 'c-kzg' import * as tape from 'tape' @@ -207,11 +210,7 @@ tape('blob EIP 4844 transaction', async (t) => { // Disable block header consensus format validation const consensusFormatValidation = BlockHeader.prototype._consensusFormatValidation BlockHeader.prototype._consensusFormatValidation = (): any => {} - try { - kzg.freeTrustedSetup() - } catch { - // NOOP - just verifying KZG is ready if not already - } + initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') const gethGenesis = require('../../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { @@ -227,7 +226,7 @@ tape('blob EIP 4844 transaction', async (t) => { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proofs = blobs.map((blob) => kzg.computeBlobKzgProof(blob)) + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) const pk = randomBytes(32) const tx = BlobEIP4844Transaction.fromTxData( { diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 27af19a506c..1df28c1689c 100644 --- a/packages/devp2p/src/rlpx/peer.ts +++ b/packages/devp2p/src/rlpx/peer.ts @@ -602,7 +602,6 @@ export class Peer extends EventEmitter { } protocolObj.protocol._handleMessage(msgCode, payload) } catch (err: any) { - console.log(err) this.disconnect(DISCONNECT_REASONS.SUBPROTOCOL_ERROR) this._logger(`Error on peer subprotocol message handling: ${err}`) this.emit('error', err) diff --git a/packages/tx/test/eip4844.spec.ts b/packages/tx/test/eip4844.spec.ts index 2bab2a7046b..33feeaf384e 100644 --- a/packages/tx/test/eip4844.spec.ts +++ b/packages/tx/test/eip4844.spec.ts @@ -161,7 +161,7 @@ tape('Network wrapper tests', async (t) => { const blobs = getBlobs('hello world') const commitments = blobsToCommitments(blobs) const versionedHashes = commitmentsToVersionedHashes(commitments) - const proofs = blobsToProofs(blobs) + const proofs = blobsToProofs(blobs, commitments) const unsignedTx = BlobEIP4844Transaction.fromTxData( { versionedHashes, diff --git a/packages/util/src/blobHelpers.ts b/packages/util/src/blobHelpers.ts index 8b5c703c312..d14924cdba5 100644 --- a/packages/util/src/blobHelpers.ts +++ b/packages/util/src/blobHelpers.ts @@ -63,11 +63,9 @@ export const blobsToCommitments = (blobs: Uint8Array[]) => { return commitments } -export const blobsToProofs = (blobs: Uint8Array[]) => { - const proofs = [] - for (const blob of blobs) { - proofs.push(kzg.computeBlobKzgProof(blob)) - } +export const blobsToProofs = (blobs: Uint8Array[], commitments: Uint8Array[]) => { + const proofs = blobs.map((blob, ctx) => kzg.computeBlobKzgProof(blob, commitments[ctx])) + return proofs } diff --git a/packages/util/src/kzg.ts b/packages/util/src/kzg.ts index 14f69414d91..05c48fd5f9e 100644 --- a/packages/util/src/kzg.ts +++ b/packages/util/src/kzg.ts @@ -3,9 +3,8 @@ */ export interface Kzg { loadTrustedSetup(filePath: string): void - freeTrustedSetup(): void blobToKzgCommitment(blob: Uint8Array): Uint8Array - computeBlobKzgProof(blob: Uint8Array): Uint8Array + computeBlobKzgProof(blob: Uint8Array, commitment: Uint8Array): Uint8Array verifyKzgProof( polynomialKzg: Uint8Array, z: Uint8Array, @@ -25,7 +24,6 @@ function kzgNotLoaded(): never { // eslint-disable-next-line import/no-mutable-exports export let kzg: Kzg = { - freeTrustedSetup: kzgNotLoaded, loadTrustedSetup: kzgNotLoaded, blobToKzgCommitment: kzgNotLoaded, computeBlobKzgProof: kzgNotLoaded, @@ -39,10 +37,6 @@ export let kzg: Kzg = { */ export function initKZG(kzgLib: Kzg, trustedSetupPath: string) { kzg = kzgLib - try { - // Always try to free trusted setup before loading (in case loaded by different module) - kzg.freeTrustedSetup() - // eslint-disable-next-line - } catch {} + kzg.loadTrustedSetup(trustedSetupPath) } From 25d43bb1d4a6ed256aac71b5abf757b8c4451334 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:16:58 -0400 Subject: [PATCH 28/32] Fix utils --- packages/client/test/sim/simutils.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/client/test/sim/simutils.ts b/packages/client/test/sim/simutils.ts index aa697658797..e410e14f213 100644 --- a/packages/client/test/sim/simutils.ts +++ b/packages/client/test/sim/simutils.ts @@ -9,6 +9,7 @@ import { bytesToUtf8, commitmentsToVersionedHashes, getBlobs, + initKZG, randomBytes, } from '@ethereumjs/util' import * as kzg from 'c-kzg' @@ -302,7 +303,7 @@ export const runBlobTx = async ( ) => { const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) - const proofs = blobsToProofs(blobs) + const proofs = blobsToProofs(blobs, commitments) const hashes = commitmentsToVersionedHashes(commitments) const sender = Address.fromPrivateKey(pkey) @@ -369,7 +370,7 @@ export const createBlobTxs = async ( const blobs = getBlobs(bytesToHex(randomBytes(blobSize))) const commitments = blobsToCommitments(blobs) - const proofs = blobsToProofs(blobs) + const proofs = blobsToProofs(blobs, commitments) const hashes = commitmentsToVersionedHashes(commitments) for (let x = 1; x <= numTxs; x++) { From a3c4871f20a52eca8a23ccaf178cc5877e8786ed Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 13:52:08 -0400 Subject: [PATCH 29/32] Remove outdated kzg references --- packages/client/test/miner/pendingBlock.spec.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index 0c5b63abfe8..b44514f7c44 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -258,11 +258,6 @@ tape('[PendingBlock]', async (t) => { }) t.test('construct blob bundles', async (st) => { - try { - kzg.freeTrustedSetup() - } catch { - /** ensure kzg is setup */ - } initKZG(kzg, __dirname + '/../../lib/trustedSetups/devnet4.txt') const gethGenesis = require('../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { @@ -296,7 +291,6 @@ tape('[PendingBlock]', async (t) => { await pendingBlock.build(payloadId) const pendingBlob = pendingBlock.blobBundles.get(bytesToPrefixedHexString(payloadId))?.blobs[0] st.ok(pendingBlob !== undefined && equalsBytes(pendingBlob, blobs[0])) - kzg.freeTrustedSetup() st.end() }) t.test('should reset td', (st) => { From 7bfe2e5ed9f88f6614b604b4b6792c8080d0f277 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 15:18:15 -0400 Subject: [PATCH 30/32] Fix client tests --- packages/client/test/miner/pendingBlock.spec.ts | 5 ++++- packages/client/test/rpc/eth/sendRawTransaction.spec.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/client/test/miner/pendingBlock.spec.ts b/packages/client/test/miner/pendingBlock.spec.ts index b44514f7c44..a6cc28c83d6 100644 --- a/packages/client/test/miner/pendingBlock.spec.ts +++ b/packages/client/test/miner/pendingBlock.spec.ts @@ -258,7 +258,10 @@ tape('[PendingBlock]', async (t) => { }) t.test('construct blob bundles', async (st) => { - initKZG(kzg, __dirname + '/../../lib/trustedSetups/devnet4.txt') + try { + initKZG(kzg, __dirname + '/../../lib/trustedSetups/devnet4.txt') + // eslint-disable-next-line + } catch {} const gethGenesis = require('../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { chain: 'customChain', diff --git a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts index 6e120dfa133..a8c9614c656 100644 --- a/packages/client/test/rpc/eth/sendRawTransaction.spec.ts +++ b/packages/client/test/rpc/eth/sendRawTransaction.spec.ts @@ -210,8 +210,10 @@ tape('blob EIP 4844 transaction', async (t) => { // Disable block header consensus format validation const consensusFormatValidation = BlockHeader.prototype._consensusFormatValidation BlockHeader.prototype._consensusFormatValidation = (): any => {} - - initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') + try { + initKZG(kzg, __dirname + '/../../../lib/trustedSetups/devnet4.txt') + // eslint-disable-next-line + } catch {} const gethGenesis = require('../../../../block/test/testdata/4844-hardfork.json') const common = Common.fromGethGenesis(gethGenesis, { chain: 'customChain', From 2fb8d069062ec143c5f10b760caebc2eb6edb0b3 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 10 Apr 2023 08:02:41 -0400 Subject: [PATCH 31/32] Update c-kzg dep --- package-lock.json | 28 ++++++++++++++-------------- packages/block/package.json | 2 +- packages/client/package.json | 2 +- packages/evm/package.json | 2 +- packages/tx/package.json | 2 +- packages/util/package.json | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index edc3f86573c..f2b15da9b0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4814,9 +4814,9 @@ } }, "node_modules/c-kzg": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.2.tgz", - "integrity": "sha512-enFknANTmTFe/GqMEcnH1eKSSuMgpT3u7TXqVmOrv3PYprWaW8k827fgBuVzFDvcOnuRqw1Cp1myHQfZpKuibw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.4.tgz", + "integrity": "sha512-DsHrUSUIvC/k8TuHDTLddMGYBTYfcleyoIjv9k5iv4kJTI4J6gkntEocjKbKDCmohrwms0N4QYqx1ugp3RY3FQ==", "hasInstallScript": true, "dependencies": { "bindings": "^1.5.0", @@ -17807,7 +17807,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "engines": { "node": ">=14" @@ -17862,7 +17862,7 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^2.0.2", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", @@ -18116,7 +18116,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.4", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", @@ -18233,7 +18233,7 @@ "node": ">=14" }, "peerDependencies": { - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { @@ -18258,7 +18258,7 @@ "node": ">=14" }, "peerDependencies": { - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { @@ -19518,7 +19518,7 @@ "@ethereumjs/tx": "^4.1.1", "@ethereumjs/util": "^8.0.5", "@types/lru-cache": "^5.1.0", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.4", "ethereum-cryptography": "^1.1.2", "ethers": "^5.7.1" } @@ -19566,7 +19566,7 @@ "@types/jwt-simple": "^0.5.33", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "constants-browserify": "^1.0.0", @@ -19749,7 +19749,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.4", "debug": "^4.3.3", "ethereum-cryptography": "^1.1.2", "level": "^8.0.0", @@ -21824,9 +21824,9 @@ "version": "3.1.2" }, "c-kzg": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.2.tgz", - "integrity": "sha512-enFknANTmTFe/GqMEcnH1eKSSuMgpT3u7TXqVmOrv3PYprWaW8k827fgBuVzFDvcOnuRqw1Cp1myHQfZpKuibw==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/c-kzg/-/c-kzg-2.0.4.tgz", + "integrity": "sha512-DsHrUSUIvC/k8TuHDTLddMGYBTYfcleyoIjv9k5iv4kJTI4J6gkntEocjKbKDCmohrwms0N4QYqx1ugp3RY3FQ==", "requires": { "bindings": "^1.5.0", "node-addon-api": "^5.0.0" diff --git a/packages/block/package.json b/packages/block/package.json index 315fc932b9f..cca736dd58e 100644 --- a/packages/block/package.json +++ b/packages/block/package.json @@ -48,7 +48,7 @@ }, "devDependencies": { "@types/lru-cache": "^5.1.0", - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "engines": { "node": ">=14" diff --git a/packages/client/package.json b/packages/client/package.json index 81f027eb72e..6de352750b5 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -70,7 +70,7 @@ "@ethereumjs/vm": "6.4.1", "abstract-level": "^1.0.3", "body-parser": "^1.19.2", - "c-kzg": "^2.0.2", + "c-kzg": "^2.0.4", "chalk": "^4.1.2", "connect": "^3.7.0", "cors": "^2.8.5", diff --git a/packages/evm/package.json b/packages/evm/package.json index b73d71b099d..4e27c4f2fff 100644 --- a/packages/evm/package.json +++ b/packages/evm/package.json @@ -64,7 +64,7 @@ "@types/minimist": "^1.2.2", "@types/node-dir": "^0.0.34", "benchmark": "^2.1.4", - "c-kzg": "^2.0.0", + "c-kzg": "^2.0.4", "level": "^8.0.0", "memory-level": "^1.0.0", "minimist": "^1.2.5", diff --git a/packages/tx/package.json b/packages/tx/package.json index cd7364a9d82..f5257a71ee3 100644 --- a/packages/tx/package.json +++ b/packages/tx/package.json @@ -59,7 +59,7 @@ "@ethersproject/providers": "^5.7.2" }, "peerDependencies": { - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { diff --git a/packages/util/package.json b/packages/util/package.json index f012b1bcf0e..05da274902e 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -92,7 +92,7 @@ "@types/secp256k1": "^4.0.1" }, "peerDependencies": { - "c-kzg": "^2.0.0" + "c-kzg": "^2.0.4" }, "peerDependenciesMeta": { "c-kzg": { From f26ad8186c15667039840e1f9bb210a2a514d42a Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 11 Apr 2023 08:45:03 -0400 Subject: [PATCH 32/32] Fix karma, remove Buffer references --- packages/block/karma.conf.js | 1 + packages/tx/karma.conf.js | 1 + packages/util/src/bytes.ts | 4 ---- packages/util/src/types.ts | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/block/karma.conf.js b/packages/block/karma.conf.js index 3cdcf9821e4..f1ad097975f 100644 --- a/packages/block/karma.conf.js +++ b/packages/block/karma.conf.js @@ -13,6 +13,7 @@ module.exports = function (config) { acornOptions: { ecmaVersion: 12, }, + ignore: ['c-kzg'], }, }, concurrency: 1, diff --git a/packages/tx/karma.conf.js b/packages/tx/karma.conf.js index 5f921f9078a..837089c9068 100644 --- a/packages/tx/karma.conf.js +++ b/packages/tx/karma.conf.js @@ -15,6 +15,7 @@ module.exports = function (config) { acornOptions: { ecmaVersion: 12, }, + ignore: ['c-kzg', 'safer-buffer'], }, }, browsers: ['FirefoxHeadless', 'ChromeHeadless'], diff --git a/packages/util/src/bytes.ts b/packages/util/src/bytes.ts index faaec5d480a..904bab18943 100644 --- a/packages/util/src/bytes.ts +++ b/packages/util/src/bytes.ts @@ -186,10 +186,6 @@ export const toBytes = function (v: ToBytesInputTypes): Uint8Array { return new Uint8Array() } - if (Buffer.isBuffer(v)) { - return Uint8Array.from(v) - } - if (Array.isArray(v) || v instanceof Uint8Array) { return Uint8Array.from(v) } diff --git a/packages/util/src/types.ts b/packages/util/src/types.ts index 144672eb3c9..dd6020addb8 100644 --- a/packages/util/src/types.ts +++ b/packages/util/src/types.ts @@ -37,7 +37,6 @@ export interface TransformabletoBytes { } export type NestedUint8Array = Array -export type NestedBufferArray = Array /** * Type output options