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/18] `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 3f3595f681..01a1d98d67 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 6f7753042e..915901b95a 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 3169df0497..6891aa0492 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 7f99a9200d..35905e0960 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 a89a35cc5d..8c3fcd4dab 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 1fa494f2bf..3fe11f549c 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 e7e6a3a73e..9857da8272 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 947a2c8c21..8ca4bdb09b 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 05cf8afdfa..c3b4fab6ac 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 6942bc0a75..2384eb74d2 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 2d048de225..c38fe88738 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 bbda3f6f55..1289c1b0c7 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 e17d1beb7f..e5d17cbde8 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 b966b2a082..c15e653f69 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 e9bd5620a9..3761501ef6 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 7f4f0c9b91..bf6c6f6aef 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 7704a7bafd..c3c366f59f 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 e7ec9b503e..b28760f788 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 d84a3459c8..9df9511420 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 341b338bae..7bb6aa3bdf 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 c7146b050f..c55a19716c 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 98a806c1fc..d84911091a 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 53c70fef20..4a6cfa9c1a 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 9a00414fa8..838daf9fd0 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 cdbfb13859..a1396722af 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 014e8e790a..af2d5bca64 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 23a673fdda..4cd8801f93 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 479c179720..d385eee7f6 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 a1fb958af7..7238639bc9 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 58bb2fbf44..b13f81bf08 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 9c3daa75db..af4581efe9 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 fdc642c702..3b141c9c0f 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 b8d7286c80..3c66a11a77 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 364df4cc8c..47a9f85938 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 55d2026028..54ba708795 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 fa6bab0f80..2f6ba9c77f 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 1b218ebac7..71744f2191 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 dac3e915d1..3a28e37796 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 d92da6c03f..16dd356693 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 a5fbe9388b..134293ea87 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 d114c88bff..817da52231 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 a1485804a1..571f64667f 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 7045782a0f..c0c43c1ddd 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 a336f4513e..08398f739d 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 c5fb45b503..583f9568b1 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 40bc65305d..45114c38f0 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 24e87381e2..ad4ee93637 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 542477f46e..802d354ec6 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 b37f7d2407..014a30255a 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 dc9b2463da..1a63af33d9 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 50acc2de53..62e3eb27c7 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 5df71836a5..b1da0b9753 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 2f8d8b8641..6a172f31e0 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 59917a953b..aa3ec103ac 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 89e813317a..9d5f90eb6f 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 a745674ab7..b6ce4b942e 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 563377cbb3..6f01670724 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 37263f9ccc..443114c6c4 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 c36e2c3d90..680b530617 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 41f12fe909..a9810b8e38 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 0ad46d3f0a..a8b16d7cde 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 6212413d86..31f5a03131 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 a1c39ea525..f783156441 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 838cce0d81..0c48f37aec 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 48fa899c9b..475f779efb 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 a1c727e32b..c67705c06a 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 70995be552..73d284ba0f 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 14141eadcd..5b7cdba2bc 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 512ed0fd54..672c125144 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 34589ca4f4..94a7060d0f 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 845e44ce71..b0b2cd7118 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 bf56c3c84f..051d16b7d8 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 6428cc5772..00d9702ed3 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 f537c87f49..755880a66b 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 c56251acd5..c20feb68bd 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 8916168bdd..5694dcf97f 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 e029ffc793..54216b8c9b 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 20283a089e..e5a388f9db 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 d9e11e4dce..3f2996085a 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 a2febee35a..68722a4dee 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 372fedf55c..7bd5f11896 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 82d35a6b49..5cea1159b9 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 2c2ba5c7ae..1c24e89bfe 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 47ef4efb24..cc8f56e820 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 813daaf2ad..c66d7fe09f 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 2d5c413902..c3e195395c 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 156f0a3e1f..99cdbf8ce4 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 b5d3aec64e..e206557198 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 1685b8075c..ea5c249c43 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 6bfd7fbd5e..f34ef52380 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 88eca2a3d1..48ef13e181 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 69471e4cc0..feacb433fb 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 d3aaab349f..ce608ca7a8 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 f776bed5f9..32fb207324 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 ed63d819c1..e83ba7d41e 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 73477dd1d0..0f98f83415 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 03458e31d2..425d485af6 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 39b1879056..efa1e8696b 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 830ffa90e0..11fc32311d 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 9c13c9b429..205f66607e 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 9751fa3ac5..7bf3ed7c30 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 1a3811e26c..27f67441b6 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 ea541afafe..a4374714f5 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 92afcc3184..cea1aedfae 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 f1b182870d..405cf98f3c 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 47005c2adc..a1f6c68073 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 f0c0f39903..4bbfd31ed9 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 ec48e15abb..53325fac28 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 ea6849fafd..40b04c05c0 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 f99b1585e1..8fb6e688a8 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 4b5ad0619d..4585eaf8bf 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 88523e57d5..2c562d6923 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 52d3c5bd4c..0c361d734c 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 96de836797..0bbe155f7a 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 155a3cc223..c2dea486bf 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 feba6dd97c..390e19060a 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 7664da5459..7393bf018a 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 24e885132f..4efe3d3d7a 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 3a032b23c5..aa6f8f6278 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 60b32585d4..9f2985c648 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 85f8517277..c9be394c3f 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 37803efd7a..bdb9447634 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 26c2ace473..eb135fd22a 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 0038390db9..9f5b4f5760 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 456ac9c411..c8480b3f6a 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 0e05dc0d7f..51d3892f6c 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 13116a2fa4..7d46da2b27 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 c8727d4503..b58c707c01 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 57cff62c9f..5c690a05e4 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 5c6088b8a9..a3e5e437bc 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 b8c4152cfa..5841faf6bf 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 f92ff3120b..510ddf8472 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 9e25824397..fd7111c39f 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 40d0fc884e..f6ee474f97 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 64a79a1077..f71f3ce0c2 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 6105551209..8cf80e3c1c 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 722b94192f..72befc7883 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 c2de2eaa6e..18fdd64f82 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 2a77dae624..2968938de8 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 d5813edb62..e741de4038 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 25243ecac7..fe130f50d8 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 90840f83a3..224730c6ff 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 c9a99e262e..ece927dc61 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 01326b8fbc..995d7e08c6 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 1fd4abf41f..8c70048fa2 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 ecaa5a4908..1b88a27b04 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 cdfe303adb..0247f9bb8b 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 5f239ed963..08e7624075 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 a3c03c11c7..accead154d 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 0df8f1f7c2..5c7e540042 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 a19c7a3b08..8a50c89c8f 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 152f8f8e1d..06adf22e37 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 7a5eae9136..030fdc75f1 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 de3f94761d..fa0afc447c 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 c1a4bd496f..f714216684 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 b17e40713b..c829579b24 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 30545fdbf7..8543d7a41c 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 09e8d629a1..c65ea644db 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 7ebeeb9da3..6190ad4a76 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 078772a3d7..4e6f1c70eb 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 5d4ea3b86b..4c11feec3f 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 5ba45fd6f5..3539d23852 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 a2a769bb0d..d35e8fa124 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 441a6d6923..ad1aa738fe 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 8e198482b5..213312c399 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 ade619568c..17f11016b3 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 6fabc2d65d..88c7885214 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 f4247cc28e..1804780c9d 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 bdcc973518..11aa520e7a 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 d38e7053e7..c72b7b34fe 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 4327e54043..5bd1e035cc 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 2e6ae419d3..d2cc372dd6 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 91a1c75f35..96a843a8c3 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 fb64a886f3..cffcd0d013 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 10217b11c6..1bbc738091 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 5cd91707c0..f822b54fb9 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 d38489c010..dbc3afe86f 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 c063df3738..85eafd0604 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 5928bb7385..e8d43af877 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 b4341ae201..8682811eb1 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 3b6e9efe46..5b548fde03 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 8e6ee5c3d2..bb702ff6e6 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 09256f7971..0fe763996f 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 321bb76853..3b10ea773d 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 a886bc3148..0d48b11901 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 7e74dfa03e..7e62b82511 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 88d6baad51..79ed9e9130 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 eddfb3c953..0e50083c95 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 70d8cd0bbd..9398e98f1f 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 5bd67a3991..aee4b5efb1 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 7670b3bc11..8591fe3f1f 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 ac45df8b0f..9f9d16793f 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 1dcb4981cb..9556700089 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 aa3853eaba..edbd32748c 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 704255835a..b6253c48da 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 cee17ba973..884b23d6a1 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 963c740c01..3fdb64351c 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 ca72e35386..1b25251d65 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 791c462d6a..d1f4939f2f 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 606e0f351f..ac3de1f165 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 f7ca9ec4df..721e8b89ad 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 006b724910..7c573a97d5 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 a41fdfc8d0..5db899a67b 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 843ed394f6..866a9f7f34 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 8bd66ec91e..d9c1c5f2db 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 522ae0536c..0fe8b79814 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 8c641cad92..4331e8dc58 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 df8310955e..339e4fc7fa 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 2d0735d87b..d6033e0a5b 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 2d4155ba25..6439c6dac3 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 5492c12fde..ba6af03d9e 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 5bb8000318..e4a0b43efd 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 8764f5c057..8af17a1980 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 6a0d493467..746e87d551 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 bb1e56a8d9..113d5db14c 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 a48fd1f854..73f57ee4de 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 57550d8f84..95e96c4d0e 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 2d658a469b..b6a4ca1863 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 9c4da676d5..dc8b3c5030 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 42e1a3ab3c..0ec4ce680a 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 320f6f6754..1f5c75d046 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 5e195545f0..93502fe1fd 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 580f6fb36b..4ebc724bf3 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 2d49cb8639..60e390c259 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 8bc471fcab..778be20e1c 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 fc56f8140e..8cd0607d3e 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 a9d7c32a3e..80c825bf9d 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 28614d1431..b7353515cd 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 8d454ead86..3a63166317 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 20a58fadf6..ff761023d4 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 c629f739ce..45ab64a462 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 caae1cbc11..51ddc44ba2 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 bc9c1004ee..23480495dd 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 73623019d6..e172c9adfe 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 86d7451a38..15e0ff52e5 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 2704feb525..6bd60c4af5 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 97c4619e92..e6789a084d 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 c06c008e0a..0040f87269 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 62ac888cdb..6a7744a441 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 91fc693b51..07d2ba4290 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 4a7a0b2218..26c13e101f 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 c272264320..1a45b2879b 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 8e734e49d7..9e370a07f2 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 c0b50679db..f9945d17e4 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 75615868b3..8cfbaf1020 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 198c3ee4e2..64b3c53a4a 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 a24623bcd6..e5b3ea5bcf 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 8a9ac0946d..10f471655d 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 dd2bf0e949..dafa5b6159 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 5a73e08b58..68cb4cd0c0 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 fd7fbefc5d..944909d17b 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 76d31f4986..70ea90cf2c 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 9d0301f534..12b566e1ec 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 d164de487b..8514d2078b 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 5798e9dedd..0f3f1969e5 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 a55b8ea874..84c3d19b18 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 c4f97e3d14..65b7ab0544 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 5d93fc4c57..141c315a46 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 fd20d4795a..34cef14b28 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 214c55317a..f1c2118f63 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 54daf12f6a..c08606befa 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 87472caad0..ebd737adfe 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 f80bb17bda..d89950edad 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 1bd5ba52a5..30645e3fe2 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 564530a99c..8ffcf94607 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 1e60047058..ecb8cf6017 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 0b1f50c823..4f4803dc77 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 dc3a8dc1c9..67a00c554f 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 06ba3a8246..5cb2ffb13f 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 a1532f6f0b..a98af66b00 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 fa67af06bb..3933de928d 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 c0537a93e3..7809d0bcff 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 d4aaff01fd..ebc95b241f 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 958002c2d5..bded6cba96 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 c3080da2c5..3e5808b609 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 ed8a89e587..4717acccde 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 458711cab8..4312cf12cb 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 6556d8c674..b1cbaf92a6 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 f5cb98bb3d..78138a8f77 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 01cabc332b..769169a82b 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 e591d3d546..0433e2e6a3 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 7f9642c76f..096313834d 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 b5b4bd4e83..62bcaa6adf 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 6ee4f6d465..fd7d0e893a 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 c19bf9bf59..8aff9bb76f 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 0dd06c4d1c..19e17fef98 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 d181f3df24..0e326c4a27 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 eaa26c4222..2d65597a9e 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 086295f419..e276b85b95 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 6b3d660217..5c2b174c1e 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 297dbb0f8c..284018e788 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 51bcfe86ed..b277ee349a 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 9a9d04ad1c..e55c58a33d 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 6294b1656a..6ee44a1075 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 6c007c0fe2..1c4c6e4a4d 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 88a9ef5918..53042f9f00 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 a2f3b7f342..082d3a477b 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 fcb5f97ebf..847e71decb 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 40f27377fc..497ff4a2e0 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 ecec5b7978..18f0de0065 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 4ebef620ed..d81624654f 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 a466c612e9..0d7571fdf5 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 695e800002..ebd8b233d3 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 fe1988586a..cf8ef00ddb 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 a521255b87..ee8f6849c6 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 51340b5e1c..fb413978d1 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 923b1c7fcc..e52f04a651 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 ce0861be87..9d806f5746 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 90e8f9a205..249d174897 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 b33409dbbd..1d45d84372 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 78b8d529b8..e79272c3b2 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 b247a16a8f..f2a0ef6e56 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 cfcf2a3607..4b0dba48fb 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 8e4f28293f..98cbadf9e2 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 197a8364fb..ed37120d3d 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 4af717c055..727f2be983 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 5d7793abfd..678e5b4ee8 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 dabcb8cb88..25bb2dd60a 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 47d9d16fb0..e23656a4ed 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 e9d6383d28..71e513012f 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 b9fbfc7286..2585682aaf 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 371427058c..303ed7879d 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 4ff50d3181..a446949f66 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 4a28cb1f92..59af2f9f5e 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 9ff636f9b4..33de708963 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 15327409cc..2cc2ef8611 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 17a1763887..2e221a0c5c 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 10ca8e1709..1bfc0a075e 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 eeee576072..d66e2b8d2a 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 b7907ab719..accea3dadc 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 33ebd2711c..cfb6338094 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 01565c4cba..04ecdb4631 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 92ab9a94d6..872ada800a 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 f5fb84f88e..12d138381b 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 a0ef7ce9c2..6e47a039b0 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 72b7ef30d8..06ce01077e 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 cc00e59708..32fd2a1186 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 0c45d30fdb..aa2420a452 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 4188003a58..f95a689a96 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 2fbbc803e5..c8cb1184ed 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 139daab38e..831f5d8920 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 a4bb1c1bba..61f7b7cb72 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 459f1a0c5c..9d235228e4 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 817ea35154..43ce3df2ab 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 e568d27255..b2ed8d265b 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 c0d2e7f296..6d27f86b55 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 3946966114..e6351a252c 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 572380e44c..fd0edbf23d 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 9c7c76e4b9..0276dc48d9 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 c9877259c4..9cfa94447a 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 843793beda..d6abd21eac 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 2396e77c67..2ff115ede9 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 9abd89ba44..a5a0c40771 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 f76db2d2d4..6ea6577ec8 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 d83c3f4fac..b6779f82ee 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 c5bda69380..35d96be47f 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 06c53e920f..ec678efdfc 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/18] 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 8cf80e3c1c..8a4c2fa870 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 1b88a27b04..74ffc3e8d5 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 5c7e540042..2151af932d 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 06adf22e37..1df28c1689 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/18] 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 73d284ba0f..39179ad670 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 72a66fb04c..168b2fe0ed 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 1df28c1689..27af19a506 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 e4a0b43efd..154ad5f41f 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 1f5c75d046..98d4abe245 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/18] 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 571f64667f..13b2c0e582 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 08398f739d..78f88a5ad6 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 c20feb68bd..dc067a6547 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 e172c9adfe..0064b15e2c 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 6a7744a441..6d6814d70d 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/18] 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 168b2fe0ed..6cc76198c1 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 fa0afc447c..a744dd96fd 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 85eafd0604..de06282c5d 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 edbd32748c..1388851876 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 b6253c48da..b006d88ea1 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 3fdb64351c..620bf61673 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 1b25251d65..546e1964a4 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 0fe8b79814..b307a51b6c 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 154ad5f41f..1e53d6e6ed 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 ebc95b241f..ff1187624f 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 e276b85b95..faaec5d480 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 1c4c6e4a4d..144672eb3c 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 082d3a477b..d2856eb2fa 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 497ff4a2e0..ec7070f50c 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/18] 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 d84911091a..5ce46bfe98 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/18] 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 4a6cfa9c1a..84730ba853 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 0040f87269..ff37097c8b 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/18] 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 5ce46bfe98..ec099111ba 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/18] 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 88c7885214..7074101005 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 5bd1e035cc..2d01dd51c3 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/18] 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 9d5f90eb6f..7180f2e0d8 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/18] 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 0000000000..b2e85cc0d7 --- /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/18] 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 b2e85cc0d7..e9e8ef6948 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/18] 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 e9e8ef6948..2700cff0f7 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/18] 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 2700cff0f7..00d61f24c5 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/18] 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 00d61f24c5..7b05e9f7bc 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/18] 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 7b05e9f7bc..5ff93dda3c 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 affa49b4ef08cff4b5c7c3f9021beb059f887593 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 10:27:48 -0400 Subject: [PATCH 17/18] Fix test runner --- packages/vm/test/tester/runners/GeneralStateTestsRunner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts index d86fe02f53..00ba0b97f0 100644 --- a/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts +++ b/packages/vm/test/tester/runners/GeneralStateTestsRunner.ts @@ -100,7 +100,7 @@ async function runTestCase(options: any, testData: any, t: tape.Test) { } // Even if no txs are ran, coinbase should always be created - const coinbaseAddress = new Address(testData.env.currentCoinbase.slice(2)) + const coinbaseAddress = Address.fromString(testData.env.currentCoinbase) const account = await (vm).eei.getAccount(coinbaseAddress) await (vm).eei.putAccount(coinbaseAddress, account) From 75ce24b92b742d06f87e258432bd77c535025b2e Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Tue, 4 Apr 2023 10:38:11 -0400 Subject: [PATCH 18/18] remove console log --- packages/devp2p/src/rlpx/peer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/devp2p/src/rlpx/peer.ts b/packages/devp2p/src/rlpx/peer.ts index 27af19a506..1df28c1689 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)