mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-01 09:33:46 +00:00
* accounts/usbwallet: support dynamic tx (#30180) Adds support non-legacy transaction-signing using ledger --------- Co-authored-by: Martin Holst Swende <martin@swende.se> * signer/core: extended support for EIP-712 array types (#30620) This change updates the EIP-712 implementation to resolve [#30619](https://github.com/ethereum/go-ethereum/issues/30619). The test cases have been repurposed from the ethers.js [repository](https://github.com/ethers-io/ethers.js/blob/main/testcases/typed-data.json.gz), but have been updated to remove tests that don't have a valid domain separator; EIP-712 messages without a domain separator are not supported by geth. --------- Co-authored-by: Martin Holst Swende <martin@swende.se> * cmd/evm: benchmarking via `statetest` command + filter by name, index and fork (#30442) When `evm statetest --bench` is specified, benchmark the execution similarly to `evm run`. Also adds the ability to filter tests by name, index and fork. --------- Co-authored-by: Martin Holst Swende <martin@swende.se> * beacon/blsync: remove cli dependencies (#30720) This PR moves chain config related code (config file processing, fork logic, network defaults) from `beacon/types` and `beacon/blsync` into `beacon/params` while the command line flag logic of the chain config is moved into `cmd/utils`, thereby removing the cli dependencies from package `beacon` and its sub-packages. * core/state: invoke OnCodeChange-hook on selfdestruct (#30686) This change invokes the OnCodeChange hook when selfdestruct operation is performed, and a contract is removed. This is an event which can be consumed by tracers. * trie/utils: remove unneeded initialization (#30472) * travis: build and upload RISC-V docker images too (#30739) Requested by @barnabasbusa * core/state, triedb/database: refactor state reader (#30712) Co-authored-by: Martin HS <martin@swende.se> * eth/protocols/eth: add ETH68 protocol handler fuzzers (#30417) Adds a protocol handler fuzzer to fuzz the ETH68 protocol handlers * tests: fix test panic (#30741) Fix panic in tests * p2p/netutil: unittests for addrutil (#30439) add unit tests for `p2p/addrutil` --------- Co-authored-by: Martin HS <martin@swende.se> * docs: fix typo (#30740) fixes a typo on one of the postmortems * core/state: tests on the binary iterator (#30754) Fixes an error in the binary iterator, adds additional testcases --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * cmd/geth: remove unlock commandline flag (#30737) This is one further step towards removing account management from `geth`. This PR deprecates the flag `unlock`, and makes the flag moot: unlock via geth is no longer possible. * build: upgrade -dlgo version to Go 1.23.3 (#30742) New release: https://groups.google.com/g/golang-announce/c/X5KodEJYuqI * core: fix typos (#30767) * all: remove kilic dependency from bls12381 fuzzers (#30296) The [kilic](https://github.com/kilic/bls12-381) bls12381 implementation has been archived. It shouldn't be necessary to include it as a fuzzing target any longer. This also adds fuzzers for G1/G2 mul that use inputs that are guaranteed to be valid. Previously, we just did random input fuzzing for these precompiles. * core/txpool, eth/catalyst: clear transaction pool in Rollback (#30534) This adds an API method `DropTransactions` to legacy pool, blob pool and txpool interface. This method removes all txs currently tracked in the pools. It modifies the simulated beacon to use the new method in `Rollback` which removes previous hacky implementation that also erroneously reset the gas tip to 1 gwei. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * rpc: run tests in parallel (#30384) Continuation of https://github.com/ethereum/go-ethereum/pull/30381 * version: go-ethereum v1.14.12 stable * version: begin v1.14.13 release cycle * version: fix typo in v1.14.13 release cycle name * core/vm/program: evm bytecode-building utility (#30725) In many cases, there is a need to create somewhat nontrivial bytecode. A recent example is the verkle statetests, where we want a `CREATE2`- op to create a contract, which can then be invoked, and when invoked does a selfdestruct-to-self. It is overkill to go full solidity, but it is also a bit tricky do assemble this by concatenating bytes. This PR takes an approach that has been used in in goevmlab for several years. Using this utility, the case can be expressed as: ```golang // Some runtime code runtime := program.New().Ops(vm.ADDRESS, vm.SELFDESTRUCT).Bytecode() // A constructor returning the runtime code initcode := program.New().ReturnData(runtime).Bytecode() // A factory invoking the constructor outer := program.New().Create2AndCall(initcode, nil).Bytecode() ``` We have a lot of places in the codebase where we concatenate bytes, cast from `vm.OpCode` . By taking tihs approach instead, thos places can be made a bit more maintainable/robust. * core, eth, internal, cmd: rework EVM constructor (#30745) This pull request refactors the EVM constructor by removing the TxContext parameter. The EVM object is frequently overused. Ideally, only a single EVM instance should be created and reused throughout the entire state transition of a block, with the transaction context switched as needed by calling evm.SetTxContext. Unfortunately, in some parts of the code, the EVM object is repeatedly created, resulting in unnecessary complexity. This pull request is the first step towards gradually improving and simplifying this setup. --------- Co-authored-by: Martin Holst Swende <martin@swende.se> * core, eth, internal, miner: remove unnecessary parameters (#30776) Follow-up to #30745 , this change removes some unnecessary parameters. * internal/ethapi: remove double map-clone (#30788) `ActivePrecompiledContracts()` clones the precompiled contract map, thus its callsite does not need to clone it * all: typos in comments (#30779) fixes some typos * trie: replace custom logic with bytes.HasPrefix (#30771) in `trie` - Replace custom logic with `bytes.HasPrefix` - Remove unnecessary code in `GetNode` * core, triedb: remove destruct flag in state snapshot (#30752) This pull request removes the destruct flag from the state snapshot to simplify the code. Previously, this flag indicated that an account was removed during a state transition, making all associated storage slots inaccessible. Because storage deletion can involve a large number of slots, the actual deletion is deferred until the end of the process, where it is handled in batches. With the deprecation of self-destruct in the Cancun fork, storage deletions are no longer expected. Historically, the largest storage deletion event in Ethereum was around 15 megabytes—manageable in memory. In this pull request, the single destruct flag is replaced by a set of deletion markers for individual storage slots. Each deleted storage slot will now appear in the Storage set with a nil value. This change will simplify a lot logics, such as storage accessing, storage flushing, storage iteration and so on. * internal/flags: fix "flag redefined" bug for alias on custom flags (#30796) This change fixes a bug on the `DirectoryFlag` and the `BigFlag`, which would trigger a `panic` with the message "flag redefined" in case an alias was added to such a flag. * eth/tracers/logger: fix json-logger output missing (#30804) Fixes a flaw introduced in https://github.com/ethereum/go-ethereum/pull/29795 , discovered while reviewing https://github.com/ethereum/go-ethereum/pull/30633 . * eth/tracers/logger: improve markdown logger (#30805) This PR improves the output of the markdown logger a bit. - Remove `RStack` field, - Move `Stack` last, since it may have very large vertical expansion - Make the pre- and post-exec metadata structured into a bullet-list * internal/ethapi: remove double map-clone (#30803) Similar to https://github.com/ethereum/go-ethereum/pull/30788 * accounts/abi: fix MakeTopics mutation of big.Int inputs (#30785) #28764 updated `func MakeTopics` to support negative `*big.Int`s. However, it also changed the behavior of the function from just _reading_ the input `*big.Int` via `Bytes()`, to leveraging `big.U256Bytes` which is documented as being _destructive_: This change updates `MakeTopics` to not mutate the original, and also applies the same change in signer/core/apitypes. * core/state/snapshot: simplify snapshot rebuild (#30772) This PR is purely for improved readability; I was doing work involving the file and think this may help others who are trying to understand what's going on. 1. `snapshot.Tree.Rebuild()` now returns a function that blocks until regeneration is complete, allowing `Tree.waitBuild()` to be removed entirely as all it did was search for the `done` channel behind this new function. 2. Its usage inside `New()` is also simplified by (a) only waiting if `!AsyncBuild`; and (b) avoiding the double negative of `if !NoBuild`. --------- Co-authored-by: Martin HS <martin@swende.se> * eth/ethconfig: improve error message if TTD missing (#30807) This updates the message you get when trying to initialize Geth with genesis.json that doesn't have `terminalTotalDifficulty`. The previous message was a bit obscure, I had to check the code to find out what the problem was. * core/tracing: add GetCodeHash to StateDB (#30784) This PR extends the tracing.StateDB interface by adding a GetCodeHash function. * Revert "core/state/snapshot: simplify snapshot rebuild (#30772)" (#30810) This reverts commit23800122b3. The original pull request introduces a bug and some flaky tests are detected because of this flaw. ``` --- FAIL: TestRecoverSnapshotFromWipingCrash (0.27s) blockchain_snapshot_test.go:158: The disk layer is not integrated snapshot is not constructed {"pc":0,"op":88,"gas":"0x7148","gasCost":"0x2","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PC"} {"pc":1,"op":255,"gas":"0x7146","gasCost":"0x1db0","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"SELFDESTRUCT"} {"output":"","gasUsed":"0x0"} {"output":"","gasUsed":"0x1db2"} {"pc":0,"op":116,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH21"} ``` Before the original PR, the snapshot would block the function until the disk layer was fully generated under the following conditions: (a) explicitly required by users with `AsyncBuild = false`. (b) the snapshot was being fully rebuilt or *the disk layer generation had resumed*. Unfortunately, with the changes introduced in that PR, the snapshot no longer waits for disk layer generation to complete if the generation is resumed. It brings lots of uncertainty and breaks this tiny debug feature. * cmd/evm: don't reuse state between iterations, show errors (#30780) Reusing state between benchmark iterations resulted in inconsistent results across runs, which surfaced in https://github.com/ethereum/go-ethereum/issues/30778 . If these errors are triggered again, they will now trigger panic. --------- Co-authored-by: Martin HS <martin@swende.se> * core: better document reason for dropping error on return (#30811) Add a comment for error return of nil Signed-off-by: wangjingcun <wangjingcun@aliyun.com> * core/state/snapshot: handle legacy journal (#30802) This workaround is meant to minimize the possibility for snapshot generation once the geth node upgrades to new version (specifically #30752 ) In #30752, the journal format in state snapshot is modified by removing the destruct set. Therefore, the existing old format (version = 0) will be discarded and all in-memory layers will be lost. Unfortunately, the lost in-memory layers can't be recovered by some other approaches, and the entire state snapshot will be regenerated (it will last about 2.5 hours). This pull request introduces a workaround to adopt the legacy journal if the destruct set contained is empty. Since self-destruction has been deprecated following the cancun fork, the destruct set is expected to be nil for layers above the fork block. However, an exception occurs during contract deployment: pre-funded accounts may self-destruct, causing accounts with non-zero balances to be removed from the state. For example, https://etherscan.io/tx/0xa087333d83f0cd63b96bdafb686462e1622ce25f40bd499e03efb1051f31fe49). For nodes with a fully synced state, the legacy journal is likely compatible with the updated definition, eliminating the need for regeneration. Unfortunately, nodes performing a full sync of historical chain segments or encountering pre-funded account deletions may face incompatibilities, leading to automatic snapshot regeneration. * trie: combine validation loops in VerifyRangeProof (#30823) Small optimization. It's guaranteed that `len(keys)` == `len(values)`, so we can combine the checks in a single loop rather than 2 separate loops. * all: exclude empty outputs in requests commitment (#30670) Implements changes from these spec PRs: - https://github.com/ethereum/EIPs/pull/8989 - https://github.com/ethereum/execution-apis/pull/599 * cmd/bootnode: remove bootnode utility (#30813) Since we don't really support custom networks anymore, we don't need the bootnode utility. In case a discovery-only node is wanted, it can still be run using cmd/devp2p. * core/types: add length check in CalcRequestsHash (#30829) The existing implementation is correct when building and verifying blocks, since we will only collect non-empty requests into the block requests list. But it isn't correct for cases where a requests list containing empty items is sent by the consensus layer on the engine API. We want to ensure that empty requests do not cause a difference in validation there, so the commitment computation should explicitly skip them. * triedb/pathdb: track flat state changes in pathdb (snapshot integration pt 2) (#30643) This pull request ports some changes from the main state snapshot integration one, specifically introducing the flat state tracking in pathdb. Note, the tracked flat state changes are only held in memory and won't be persisted in the disk. Meanwhile, the correspoding state retrieval in persistent state is also not supported yet. The states management in disk is more complicated and will be implemented in a separate pull request. Part 1: https://github.com/ethereum/go-ethereum/pull/30752 * core/state: introduce code reader interface (#30816) This PR introduces a `ContractCodeReader` interface with functions defined: type ContractCodeReader interface { Code(addr common.Address, codeHash common.Hash) ([]byte, error) CodeSize(addr common.Address, codeHash common.Hash) (int, error) } This interface can be implemented in various ways. Although the codebase currently includes only one implementation, additional implementations could be created for different purposes and scenarios, such as a code reader designed for the Verkle tree approach or one that reads code from the witness. *Notably, this interface modifies the function’s semantics. If the contract code is not found, no error will be returned. An error should only be returned in the event of an unexpected issue, primarily for future implementations.* The original state.Reader interface is extended with ContractCodeReader methods, it gives us more flexibility to manipulate the reader with additional logic on top, e.g. Hooks. type Reader interface { ContractCodeReader StateReader } --------- Co-authored-by: Felix Lange <fjl@twurst.com> * core: switch EVM tx context in ApplyMessage (#30809) This change relocates the EVM tx context switching to the ApplyMessage function. With this change, we can remove a lot of EVM.SetTxContext calls before message execution. ### Tracing API changes - This PR replaces the `GasPrice` field of the `VMContext` struct with `BaseFee`. Users may instead take the effective gas price from `tx.EffectiveGasTipValue(env.BaseFee)`. --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> * eth/tracers: fix state hooks in API (#30830) When a tx/block was being traced through the API the state hooks weren't being called as they should. This is due to #30745 moving the hooked statedb one level up in the state processor. This PR fixes that. --------- Co-authored-by: Martin HS <martin@swende.se> Co-authored-by: Gary Rong <garyrong0905@gmail.com> * cmd/evm: improve block/state test runner (#30633) * unify `staterunner` and `blockrunner` CLI flags, especially around tracing * added support for struct logger or json logging (although having issue #30658) * new --cross-check flag to validate the stateless witness collection / execution matches stateful * adds support for tracing the stateless execution when a tracer is set (to more easily debug differences) * --human for more readable test summary * directory or file input, so if you pass tests/spec-tests/fixtures/blockchain_tests it will execute all blockchain tests * fuzzing: fix oss-fuzz fuzzer (#30845) The fuzzer added recenly to fuzz the eth handler doesn't build on oss-fuzz, because it also has dependencies in the peer_test.go. This change fixes it, I hope, by adding that file also for preprocessing. * internal/debug: rename --trace to --go-execution-trace (#30846) This flag is very rarely needed, so it's OK for it to have a verbose name. The name --trace also conflicts with the concept of EVM tracing, which is much more heavily used. * eth/downloader: move SyncMode to package eth/ethconfig (#30847) Lots of packages depend on eth/downloader just for the SyncMode type. Since we have a dedicated package for eth protocol configuration, it makes more sense to define SyncMode there, turning eth/downloader into more of a leaf package. * CODEOWNERS: add some more entries for auto assignment (#30851) * cmd/evm, eth/tracers: refactor structlogger and make it streaming (#30806) This PR refactors the structlog a bit, making it so that it can be used in a streaming mode. ------------- OBS: this PR makes a change in the input `config` config, the third input-parem field to `debug.traceCall`. Previously, seteting it to e.g. ` {"enableMemory": true, "limit": 1024}` would mean that the response was limited to `1024` items. Since an 'item' may include both memory and storage, the actual size of the response was undertermined. After this change, the response will be limited to `1024` __`bytes`__ (or thereabouts). ----------- The commandline usage of structlog now uses the streaming mode, leaving the non-streaming mode of operation for the eth_Call. There are two benefits of streaming mode 1. Not have to maintain a long list of operations, 2. Not have to duplicate / n-plicate data, e.g. memory / stack / returndata so that each entry has their own private slice. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * core/tracing: extends tracing.Hooks with OnSystemCallStartV2 (#30786) This PR extends the Hooks interface with a new method, `OnSystemCallStartV2`, which takes `VMContext` as its parameter. Motivation By including `VMContext` as a parameter, the `OnSystemCallStartV2` hook achieves parity with the `OnTxStart` hook in terms of provided insights. This alignment simplifies the inner tracer logic, enabling consistent handling of state changes and internal calls within the same framework. --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> * trie/utils: ensure master can generate a correct genesis for kaustinen7 (#30856) This imports the following fixes: - update gnark to 1.1.0 - update go-verkle to 0.2.2 - fix: main storage offset bug (gballet/go-ethereum#329) - fix: tree key generation (gballet/go-ethereum#401) --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com> * core/txpool: remove unused parameter `local` (#30871) * core/state: enable partial-functional reader (snapshot integration pt 3) (#30650) It's a pull request based on https://github.com/ethereum/go-ethereum/pull/30643 In this pull request, the partial functional state reader is enabled if **legacy snapshot is not enabled**. The tracked flat states in pathdb will be used to serve the state retrievals, as the second implementation to fasten the state access. This pull request should be a noop change in normal cases. * cmd/evm: consolidate evm output switches (#30849) This PR attempts to clean up some ambiguities and quirks from recent changes to evm flag handling. This PR mainly focuses on `evm run` subcommand, to use the same flags for configuring tracing/output options as `statetest/blocktest` does. Additionally, it adds quite a lot of tests for expected outputs of the various subcommands, to avoid accidental changes. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * core/vm: remove unnecessary comment (#30887) * metrics, cmd/geth: change init-process of metrics (#30814) This PR modifies how the metrics library handles `Enabled`: previously, the package `init` decided whether to serve real metrics or just dummy-types. This has several drawbacks: - During pkg init, we need to determine whether metrics are enabled or not. So we first hacked in a check if certain geth-specific commandline-flags were enabled. Then we added a similar check for geth-env-vars. Then we almost added a very elaborate check for toml-config-file, plus toml parsing. - Using "real" types and dummy types interchangeably means that everything is hidden behind interfaces. This has a performance penalty, and also it just adds a lot of code. This PR removes the interface stuff, uses concrete types, and allows for the setting of Enabled to happen later. It is still assumed that `metrics.Enable()` is invoked early on. The somewhat 'heavy' operations, such as ticking meters and exp-decay, now checks the enable-flag to prevent resource leak. The change may be large, but it's mostly pretty trivial, and from the last time I gutted the metrics, I ensured that we have fairly good test coverage. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * build: update to Go 1.23.4 (#30872) * accounts/abi: support unpacking solidity errors (#30738) This PR adds the error fragments to `func (abi ABI) getArguments` which allows typed decoding of errors. * core/state: remove pointless wrapper functions (#30891) * p2p: fix DiscReason encoding/decoding (#30855) This fixes an issue where the disconnect message was not wrapped in a list. The specification requires it to be a list like any other message. In order to remain compatible with legacy geth versions, we now accept both encodings when parsing a disconnect message. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * internal/ethapi: add block override to estimateGas (#30695) Add block overrides to `eth_estimateGas` to align consistency with `eth_call`. https://github.com/ethereum/go-ethereum/issues/27800#issuecomment-1658186166 Fixes https://github.com/ethereum/go-ethereum/issues/28175 --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> * p2p: DNS resolution for static nodes (#30822) Closes #23210 # Context When deploying Geth in Kubernetes with ReplicaSets, we encountered two DNS-related issues affecting node connectivity. First, during startup, Geth tries to resolve DNS names for static nodes too early in the config unmarshaling phase. If peer nodes aren't ready yet (which is common in Kubernetes rolling deployments), this causes an immediate failure: ``` INFO [11-26|10:03:42.816] Starting Geth on Ethereum mainnet... INFO [11-26|10:03:42.817] Bumping default cache on mainnet provided=1024 updated=4096 Fatal: config.toml, line 81: (p2p.Config.StaticNodes) lookup idontexist.geth.node: no such host ``` The second issue comes up when pods get rescheduled to different nodes - their IPs change but peers keep using the initially resolved IP, never updating the DNS mapping. This PR adds proper DNS support for enode:// URLs by deferring resolution to connection time. It also handles DNS failures gracefully instead of failing fatally during startup, making it work better in container environments where IPs are dynamic and peers come and go during rollouts. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * all: implement eip-7702 set code tx (#30078) This PR implements EIP-7702: "Set EOA account code". Specification: https://eips.ethereum.org/EIPS/eip-7702 > Add a new transaction type that adds a list of `[chain_id, address, nonce, y_parity, r, s]` authorization tuples. For each tuple, write a delegation designator `(0xef0100 ++ address)` to the signing account’s code. All code reading operations must load the code pointed to by the designator. --------- Co-authored-by: Mario Vega <marioevz@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Felix Lange <fjl@twurst.com> * trie/pathdb: state iterator (snapshot integration pt 4) (#30654) In this pull request, the state iterator is implemented. It's mostly a copy-paste from the original state snapshot package, but still has some important changes to highlight here: (a) The iterator for the disk layer consists of a diff iterator and a disk iterator. Originally, the disk layer in the state snapshot was a wrapper around the disk, and its corresponding iterator was also a wrapper around the disk iterator. However, due to structural differences, the disk layer iterator is divided into two parts: - The disk iterator, which traverses the content stored on disk. - The diff iterator, which traverses the aggregated state buffer. Checkout `BinaryIterator` and `FastIterator` for more details. (b) The staleness management is improved in the diffAccountIterator and diffStorageIterator Originally, in the `diffAccountIterator`, the layer’s staleness had to be checked within the Next function to ensure the iterator remained usable. Additionally, a read lock on the associated diff layer was required to first retrieve the account blob. This read lock protection is essential to prevent concurrent map read/write. Afterward, a staleness check was performed to ensure the retrieved data was not outdated. The entire logic can be simplified as follows: a loadAccount callback is provided to retrieve account data. If the corresponding state is immutable (e.g., diff layers in the path database), the staleness check can be skipped, and a single account data retrieval is sufficient. However, if the corresponding state is mutable (e.g., the disk layer in the path database), the callback can operate as follows: ```go func(hash common.Hash) ([]byte, error) { dl.lock.RLock() defer dl.lock.RUnlock() if dl.stale { return nil, errSnapshotStale } return dl.buffer.states.mustAccount(hash) } ``` The callback solution can eliminate the complexity for managing concurrency with the read lock for atomic operation. * core/vm, go.mod: update uint256 and use faster method to write to memory (#30868) Updates geth to use the latest uint256, and use faster memory-writer * accounts/abi/bind: make it possible to wait for tx hash (#30079) This change adds methods which makes it possible for to wait for a transaction with a specific hash when deploying contracts during abi bind interaction. --------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * core: fixes for Prague fork in GenerateChain (#30924) Adding some missing functionality I noticed while updating the hivechain tool for the Prague fork: - we forgot to process the parent block hash - added `ConsensusLayerRequests` to get the requests list of the block * build(deps): bump golang.org/x/crypto from 0.26.0 to 0.31.0 (#30921) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.31.0. Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * core/vm: make all opcodes proper type (#30925) Noticed this omission while doing some work on goevmlab. We don't properly type some of the opcodes, but apparently implicit casting works in all the internal usecases. * core/types, internal/ethapi: fixes for prague RPC encoding (#30926) Fixing some issues I found while regenerating RPC tests for Prague: - Authorization signature values were not encoded as hex - `requestsRoot` in block should be `requestsHash` - `authorizationList` should work for `eth_call` * cmd/evm: make evm statetest accept non-json files (#30927) This fixes a regression introduced recently. Without this fix, it's not possible to use statetests without `.json` suffix. This is problematic for goevmlab `minimizer`, which appends the suffix `.min` during processing. * core/types: updates for EIP-7702 API functions (#30933) Here I am proposing two small changes to the exported API for EIP-7702: (1) `Authorization` has a very generic name, but it is in fact only used for one niche use case: authorizing code in a `SetCodeTx`. So I propose calling it `SetCodeAuthorization` instead. The signing function is renamed to `SignSetCode` instead of `SignAuth`. (2) The signing function for authorizations should take key as the first parameter, and the authorization second. The key will almost always be in a variable, while the authorization can be given as a literal. * core/types: rename SetCodeAuthorization 'v' to 'yParity' The API spec requires the name yParity. * cmd/evm: update tests for SetCodeAuthorization JSON encoding change (#30936) Fixing a regression introduced by73a4ecf675, which I accidentally pushed to the master branch directly. * core, core/types: rename AuthList to SetCodeAuthorizations (#30935) As a follow-up to #30933, I propose to also use the SetCode prefix in our internal APIs for the authorization list. * params: update system contracts for prague devnet-5 (#30938) * internal/flags: update copyright year to 2025 (#30976) * crypto/bn256: fix MulScalar (#30974) The `a` parameter should be used in the `MulScalar` function. The upstream cloudflare and google repos have already merged fixes. Reference: *8d7daa0c54* https://github.com/cloudflare/bn256/pull/33 * all: use cmp.Compare (#30958) * eth/tracers/logger: skip system calls (#30923) This commit makes it so that the struct logger will not emit logs while system calls are being executed. This will make it consistent with the JSON and MD loggers. It is as it stands hard to distinguish when system calls are being processed vs when a tx is being processed. --------- Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> * internal/ethapi: update default simulation timestamp increment to 12 (#30981) Update the default timestamp increment to 12s for `eth_simulate` endpoint * core/rawdb: fix panic in freezer (#30973) Fixes an issue where the node panics when an LStat fails with something other than os.ErrNotExist closes https://github.com/ethereum/go-ethereum/issues/30968 * core/types: improve printList in DeriveSha test (#30969) * eth/protocols/eth: prevent hanging dispatch (#30918) This PR attempts to fix a strange test-failure (timeout) observed on a windows-32 platform. https://ci.appveyor.com/project/ethereum/go-ethereum/builds/51174391/job/d8ascanwwltrlqd5 A goroutine is stuck trying to deliver a response: ``` goroutine 9632 [select, 29 minutes]: github.com/ethereum/go-ethereum/eth/protocols/eth.(*Peer).dispatchResponse(0x314f100, 0x3e5f6d0, 0x3acbb84) C:/projects/go-ethereum/eth/protocols/eth/dispatcher.go:172 +0x2a5 github.com/ethereum/go-ethereum/eth/protocols/eth.handleBlockHeaders({0x12abe68, 0x30021b8}, {0x12a815c, 0x40b41c0}, 0x314f100) C:/projects/go-ethereum/eth/protocols/eth/handlers.go:301 +0x173 github.com/ethereum/go-ethereum/eth/protocols/eth.handleMessage({0x12abe68, 0x30021b8}, 0x314f100) C:/projects/go-ethereum/eth/protocols/eth/handler.go:205 +0x4f6 github.com/ethereum/go-ethereum/eth/protocols/eth.Handle({0x12abe68, 0x30021b8}, 0x314f100) C:/projects/go-ethereum/eth/protocols/eth/handler.go:149 +0x33 github.com/ethereum/go-ethereum/eth.testSnapSyncDisabling.func1(0x314f100) C:/projects/go-ethereum/eth/sync_test.go:65 +0x33 github.com/ethereum/go-ethereum/eth.(*handler).runEthPeer(0x30021b8, 0x314f100, 0x427f648) C:/projects/go-ethereum/eth/handler.go:355 +0xe65 created by github.com/ethereum/go-ethereum/eth.testSnapSyncDisabling in goroutine 11 C:/projects/go-ethereum/eth/sync_test.go:64 +0x54f FAIL github.com/ethereum/go-ethereum/eth 1800.138s ``` --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * cmd/clef: fix JS issues in documentation (#30980) Fixes a couple of js-flaws in the docs * triedb/pathdb: fix tester generator (#30972) This change fixes is a rare bug in test generator: If the run is very unlucky it can use `modifyAccountOp` / `deleteAccountOp` without creating any account, leading to have a trie root same as the parent. This change makes the first operation always be a creation. * README: remove private network section from readme (#31005) * triedb/pathdb: configure different node hasher in pathdb (#31008) As the node hash scheme in verkle and merkle are totally different, the original default node hasher in pathdb is no longer suitable. Therefore, this pull request configures different node hasher respectively. * build: upgrade golangci-lint to v1.63.4 (#31019) This PR upgrades `golangci-lint` to v1.63.4 and fixes a warn message which is reported by v1.63.4: ```text WARN [config_reader] The configuration option `run.skip-dirs-use-default` is deprecated, please use `issues.exclude-dirs-use-default`. ``` Also fixes 2 warnings which are reported by v1.63.4: ```text core/txpool/blobpool/blobpool.go:1754:12: S1005: unnecessary assignment to the blank identifier (gosimple) for acct, _ := range p.index { ^ core/txpool/legacypool/legacypool.go:1989:19: S1005: unnecessary assignment to the blank identifier (gosimple) for localSender, _ := range pool.locals.accounts { ^ ``` * eth/tracers/logger: return revert reason (#31013) Fix the error comparison in tracer to prevent dropping revert reason data --------- Co-authored-by: Martin <mrscdevel@gmail.com> Co-authored-by: rjl493456442 <garyrong0905@gmail.com> * cmd/devp2p/internal/ethtest: using slices.SortFunc to simplify the code (#31012) Co-authored-by: Felix Lange <fjl@twurst.com> * core/txpool/legacypool: fix flaky test TestAllowedTxSize (#30975) - it was failing because the maximum data length (previously `dataSize`) was set to `txMaxSize - 213` but should had been `txMaxSize - 103` and the last call `dataSize+1+uint64(rand.Intn(10*txMaxSize)))` would sometimes fail depending on rand.Intn. - Maximal transaction data size comment (invalid) replaced by code logic to find the maximum tx length without its data length - comments and variable naming improved for clarity - 3rd pool add test replaced to add just 1 above the maximum length, which is important to ensure the logic is correct * core: remove unused function parameters (#31001) * cmd, core, miner: rework genesis setup (#30907) This pull request refactors the genesis setup function, the major changes are highlighted here: **(a) Triedb is opened in verkle mode if `EnableVerkleAtGenesis` is configured in chainConfig or the database has been initialized previously with `EnableVerkleAtGenesis` configured**. A new config field `EnableVerkleAtGenesis` has been added in the chainConfig. This field must be configured with True if Geth wants to initialize the genesis in Verkle mode. In the verkle devnet-7, the verkle transition is activated at genesis. Therefore, the verkle rules should be used since the genesis. In production networks (mainnet and public testnets), verkle activation always occurs after the genesis block. Therefore, this flag is only made for devnet and should be deprecated later. Besides, verkle transition at non-genesis block hasn't been implemented yet, it should be done in the following PRs. **(b) The genesis initialization condition has been simplified** There is a special mode supported by the Geth is that: Geth can be initialized with an existing chain segment, which can fasten the node sync process by retaining the chain freezer folder. Originally, if the triedb is regarded as uninitialized and the genesis block can be found in the chain freezer, the genesis block along with genesis state will be committed. This condition has been simplified to checking the presence of chain config in key-value store. The existence of chain config can represent the genesis has been committed. * all: fix some typos in comments and names (#31023) * core/types: change SetCodeTx.ChainID to uint256 (#30982) We still need to decide how to handle non-specfic `chainId` in the JSON encoding of authorizations. With `chainId` being a uint64, the previous implementation just used value zero. However, it might actually be more correct to use the value `null` for this case. * eth/gasprice: ensure cache purging goroutine terminates with subscription (#31025) * beacon/engine: check for empty requests (#31010) According to https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#engine_newpayloadv4: > Elements of the list MUST be ordered by request_type in ascending order. Elements with empty request_data MUST be excluded from the list. --------- Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> * core: use sync.Once for SenderCacher initialization (#31029) This changes the SenderCacher so its goroutines will only be started on first use. Avoids starting them when package core is just imported but core.BlockChain isn't used. * core/txpool/legacypool: ensure pending nonces are reset by SubPool.Clear (#31020) closes https://github.com/ethereum/go-ethereum/issues/30842 * core/tracing: document `OnCodeChange` now being called from SelfDestruct (#31007) Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> * all: implement state history v2 (#30107) This pull request delivers the new version of the state history, where the raw storage key is used instead of the hash. Before the cancun fork, it's supported by protocol to destruct a specific account and therefore, all the storage slot owned by it should be wiped in the same transition. Technically, storage wiping should be performed through storage iteration, and only the storage key hash will be available for traversal if the state snapshot is not available. Therefore, the storage key hash is chosen as the identifier in the old version state history. Fortunately, account self-destruction has been deprecated by the protocol since the Cancun fork, and there are no empty accounts eligible for deletion under EIP-158. Therefore, we can conclude that no storage wiping should occur after the Cancun fork. In this case, it makes no sense to keep using hash. Besides, another big reason for making this change is the current format state history is unusable if verkle is activated. Verkle tree has a different key derivation scheme (merkle uses keccak256), the preimage of key hash must be provided in order to make verkle rollback functional. This pull request is a prerequisite for landing verkle. Additionally, the raw storage key is more human-friendly for those who want to manually check the history, even though Solidity already performs some hashing to derive the storage location. --- This pull request doesn't bump the database version, as I believe the database should still be compatible if users degrade from the new geth version to old one, the only side effect is the persistent new version state history will be unusable. --------- Co-authored-by: Zsolt Felfoldi <zsfelfoldi@gmail.com> * ethdb/memorydb: faster DeleteRange (#31038) This PR replaces the iterator based DeleteRange implementation of memorydb with a simpler and much faster loop that directly deletes keys in the order of iteration instead of unnecessarily collecting keys in memory and sorting them. --------- Co-authored-by: Martin HS <martin@swende.se> * cmd/abigen: require either `--abi` or `--combined-json` (#31045) This PR addresses issue #30768 , which highlights that running cmd/abigen/abigen --pkg my_package example.json (erroneously omitting the --abi flag) generates an empty binding, when it should fail explicitly. --------- Co-authored-by: jwasinger <j-wasinger@hotmail.com> * core/types: correct chainId check for pragueSigner (#31032) Use zero value check for the pragueSigner This aligns with cancunSigner and londonSigner as well. * build: upgrade -dlgo version to Go 1.23.5 (#31037) * core/types: initialize ChainID in SetCodeTx copy method (#31054) * core/txpool: terminate subpool reset goroutine if pool was closed (#31030) if the pool terminates before `resetDone` can be read, then the go-routine will hang. * cmd/evm: refactor handling output-files for `t8n` (#30854) As part of trying to make the inputs and outputs of the evm subcommands more streamlined and aligned, this PR modifies how `evm t8n` manages output-files. Previously, we do a kind of wonky thing where between each transaction, we invoke a `getTracer` closure. In that closure, we create a new output-file, a tracer, and then make the tracer stream output to the file. We also fiddle a bit to ensure that the file becomes properly closed. It is a kind of hacky solution we have in place. This PR changes it, so that from the execution-pipeline point of view, we have just a regular tracer. No fiddling with re-setting it or closing files. That particular tracer, however, is a bit special: it takes care of creating new files per transaction (in the tx-start-hook) and closing (on tx-end-hook). Also instantiating the right type of underlying tracer, which can be a json-logger or a custom tracer. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * eth/filters: ensure API timeoutLoop terminates with event system (#31056) Discovered from failing test introduced https://github.com/ethereum/go-ethereum/pull/31033 . We should ensure `timeoutLoop` terminates if the filter event system is terminated. * go.mod: remove toolchain line (#31057) We have our own system for downloading the toolchain, and really don't want Go's to get in the way of that. We may switch to Go's builtin toolchain support, but not now. * cmd/evm: restore --bench flag to evm statetest (#31055) Refactoring of the `evm` command moved where some commands were valid. One command, `--bench`, used to work in `evm statetest`. The pluming is still in place. This PR puts the `--bench` flag in the place the trace flags were moved, and adds tests to validate the bench flag operates in `run` and `statetest` --------- Co-authored-by: Felix Lange <fjl@twurst.com> * p2p: support configuring NAT in TOML file (#31041) This is an alternative for #27407 with a solution based on gencodec. With the PR, one can now configure like this: ``` # config.toml [Node.P2P] NAT = "extip:33.33.33.33" ``` ```shell $ geth --config config.toml ... INFO [01-17|16:37:31.436] Started P2P networking self=enode://2290...ab@33.33.33.33:30303 ``` * go.mod: gencodec stable v0.1.0 (#31062) * triedb/pathdb: fix state revert on v2 history (#31060) State history v2 has been shipped and will take effect after the Cancun fork. However, the state revert function does not differentiate between v1 and v2, instead blindly using the storage map key for state reversion. This mismatch between the keys of the live state set and the state history can trigger a panic: `non-existent storage slot for reverting`. This flaw has been fixed in this PR. * trie: reduce allocations in stacktrie (#30743) This PR uses various tweaks and tricks to make the stacktrie near alloc-free. ``` [user@work go-ethereum]$ benchstat stacktrie.1 stacktrie.7 goos: linux goarch: amd64 pkg: github.com/ethereum/go-ethereum/trie cpu: 12th Gen Intel(R) Core(TM) i7-1270P │ stacktrie.1 │ stacktrie.7 │ │ sec/op │ sec/op vs base │ Insert100K-8 106.97m ± 8% 88.21m ± 34% -17.54% (p=0.000 n=10) │ stacktrie.1 │ stacktrie.7 │ │ B/op │ B/op vs base │ Insert100K-8 13199.608Ki ± 0% 3.424Ki ± 3% -99.97% (p=0.000 n=10) │ stacktrie.1 │ stacktrie.7 │ │ allocs/op │ allocs/op vs base │ Insert100K-8 553428.50 ± 0% 22.00 ± 5% -100.00% (p=0.000 n=10) ``` Also improves derivesha: ``` goos: linux goarch: amd64 pkg: github.com/ethereum/go-ethereum/core/types cpu: 12th Gen Intel(R) Core(TM) i7-1270P │ derivesha.1 │ derivesha.2 │ │ sec/op │ sec/op vs base │ DeriveSha200/stack_trie-8 477.8µ ± 2% 430.0µ ± 12% -10.00% (p=0.000 n=10) │ derivesha.1 │ derivesha.2 │ │ B/op │ B/op vs base │ DeriveSha200/stack_trie-8 45.17Ki ± 0% 25.65Ki ± 0% -43.21% (p=0.000 n=10) │ derivesha.1 │ derivesha.2 │ │ allocs/op │ allocs/op vs base │ DeriveSha200/stack_trie-8 1259.0 ± 0% 232.0 ± 0% -81.57% (p=0.000 n=10) ``` --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * eth/catalyst: fail on duplicate request types (#31071) Refer to: https://github.com/ethereum/execution-apis/pull/623 * accounts/usbwallet: fix ledger access for latest firmware and add Ledger Flex (#31004) The latest firmware for Ledger Nano S Plus now returns `0x5000` for it's product ID, which doesn't match any of the product IDs enumerated in `hub.go`. This PR removes the assumption about the interfaces exposed, and simply checks the upper byte for a match. Also adds support for the `0x0007` / `0x7000` product ID (Ledger Flex). * core/vm: implement EIP-2537 spec updates (#30978) Reference: - Remove MUL precompiles: https://github.com/ethereum/EIPs/pull/8945 - Pricing change for pairing operation: https://github.com/ethereum/EIPs/pull/9098 - Pricing change for add, mapping and mul operations: https://github.com/ethereum/EIPs/pull/9097 - Pricing change for MSM operations: https://github.com/ethereum/EIPs/pull/9116 --------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * p2p/nat: add stun protocol (#31064) This implements a basic mechanism to query the node's external IP using a STUN server. There is a built-in list of public STUN servers for convenience. The new detection mechanism must be selected explicitly using `--nat=stun` and is not enabled by default in Geth. Fixes #30881 --------- Co-authored-by: Felix Lange <fjl@twurst.com> * fix README.md (#31076) Hi I fixed 2 minor spelling issues. --------- Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> * chore: fix various comments (#31082) * all: nuke total difficulty (#30744) The total difficulty is the sum of all block difficulties from genesis to a certain block. This value was used in PoW for deciding which chain is heavier, and thus which chain to select. Since PoS has a different fork selection algorithm, all blocks since the merge have a difficulty of 0, and all total difficulties are the same for the past 2 years. Whilst the TDs are mostly useless nowadays, there was never really a reason to mess around removing them since they are so tiny. This reasoning changes when we go down the path of pruned chain history. In order to reconstruct any TD, we **must** retrieve all the headers from chain head to genesis and then iterate all the difficulties to compute the TD. In a world where we completely prune past chain segments (bodies, receipts, headers), it is not possible to reconstruct the TD at all. In a world where we still keep chain headers and prune only the rest, reconstructing it possible as long as we process (or download) the chain forward from genesis, but trying to snap sync the head first and backfill later hits the same issue, the TD becomes impossible to calculate until genesis is backfilled. All in all, the TD is a messy out-of-state, out-of-consensus computed field that is overall useless nowadays, but code relying on it forces the client into certain modes of operation and prevents other modes or other optimizations. This PR completely nukes out the TD from the node. It doesn't compute it, it doesn't operate on it, it's as if it didn't even exist. Caveats: - Whenever we have APIs that return TD (devp2p handshake, tracer, etc.) we return a TD of 0. - For era files, we recompute the TD during export time (fairly quick) to retain the format content. - It is not possible to "verify" the merge point (i.e. with TD gone, TTD is useless). Since we're not verifying PoW any more, just blindly trust it, not verifying but blindly trusting the many year old merge point seems just the same trust model. - Our tests still need to be able to generate pre and post merge blocks, so they need a new way to split the merge without TTD. The PR introduces a settable ttdBlock field on the consensus object which is used by tests as the block where originally the TTD happened. This is not needed for live nodes, we never want to generate old blocks. - One merge transition consensus test was disabled. With a non-operational TD, testing how the client reacts to TTD is useless, it cannot react. Questions: - Should we also drop total terminal difficulty from the genesis json? It's a number we cannot react on any more, so maybe it would be cleaner to get rid of even more concepts. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * .github: add lint step (#31068) * core/{.,state,vm},miner,eth/tracers,tests: implement 7709 with a syscall flag (#31036) Same as #31015 but requires the contract to exist. Not compatible with any verkle testnet up to now. This adds a `isSytemCall` flag so that it is possible to detect when a system call is executed, so that the code execution and other locations are not added to the witness. --------- Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com> Co-authored-by: Felix Lange <fjl@twurst.com> * build: bump test timeout (#31095) Travis often fails because the test times out. * .travis.yml: change arch for Docker build to arm64 (#31096) This is an attempt to work around a gcc issue in the Docker build. * Revert ".travis.yml: change arch for Docker build to arm64 (#31096)" This reverts commit7b96ec4dae. * build: retry PPA upload up to three times (#31099) * crypto: add IsOnCurve check (#31100) * build: provide a flag to disable publishing in dockerx build (#31098) This changes the `-upload` flag to just toggle the upload. The remote image name is now configured using the `-hub` flag. * version: begin v1.15.0 release cycle * all: add build tags for wasip1 (#31090) * core: implement eip-7623 floor data gas (#30946) This PR builds on #29040 and updates it to the new version of the spec. I filled the EEST tests and they pass. Link to spec: https://eips.ethereum.org/EIPS/eip-7623 --------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> Co-authored-by: lightclient <lightclient@protonmail.com> * core/vm: EXTCODE* return delegation designator for 7702 (#31089) Implements https://github.com/ethereum/EIPs/pull/9248 * params: update system contract addresses for devnet-6 (#31102) Finalize Prague system contract addresses. Reference: * https://github.com/ethereum/EIPs/pull/9287 * https://github.com/ethereum/EIPs/pull/9288 * https://github.com/ethereum/EIPs/pull/9289 * eth/catalyst: fix validation of type 0 request (#31103) I caught this error on Hive. It was introduced by https://github.com/ethereum/go-ethereum/pull/31071 because after adding the equality check the request type 0 will be rejected. * core/vm: simplify tracer hook invocation in interpreter loop (#31074) Removes duplicate code in the interpreter loop. * tests/fuzzers/bls12381: fix error message in fuzzCrossG2Add (#31113) Fixes a typo in the error message within the `fuzzCrossG2Add` function. The panic message incorrectly references "G1 point addition mismatch" when it should be "G2 point addition mismatch," as the function deals with G2 points. This doesn't affect functionality but could cause confusion during debugging. I've updated the message to reflect the correct curve. * core/rawdb: introduce flush offset in freezer (#30392) This is a follow-up PR to #29792 to get rid of the data file sync. **This is a non-backward compatible change, which increments the database version from 8 to 9**. We introduce a flushOffset for each freezer table, which tracks the position of the most recently fsync’d item in the index file. When this offset moves forward, it indicates that all index entries below it, along with their corresponding data items, have been properly persisted to disk. The offset can also be moved backward when truncating from either the head or tail of the file. Previously, the data file required an explicit fsync after every mutation, which was highly inefficient. With the introduction of the flush offset, the synchronization strategy becomes more flexible, allowing the freezer to sync every 30 seconds instead. The data items above the flush offset are regarded volatile and callers must ensure they are recoverable after the unclean shutdown, or explicitly sync the freezer before any proceeding operations. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * core: copy genesis before modifying (#31097) This PR fixes a data race in SetupGenesisWithOverride. * params: start osaka fork (#31125) This PR defines the Osaka fork. An easy first step to start our work on the next hardfork (This is needed for EOF testing as well) --------- Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> * params,core: add max and target value to chain config (#31002) Implements [EIP-7840](https://github.com/ethereum/EIPs/pull/9129) and [EIP-7691](d96625a4dc/EIPS/eip-7691.md). --------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Felix Lange <fjl@twurst.com> * core: assign default difficulty to zero for chain without ethash (#31067) I hit this case while trying something with the simulated backend. The EVM only enables instruction set forks after the merge when 'Random' is set. In the simulated backend, the random value will be set via the engine API for all blocks after genesis. But for the genesis block itself, the random value will not be assigned in the vm.BlockContext because the genesis has a non-zero difficulty. For my case, this meant that estimateGas did not work for the first transaction sent on the simulated chain, since the contract contained a PUSH0 instruction. This could also be fixed by explicitly configuring a zero difficulty in the simulated backend. However, I think that zero difficulty is a better default these days. --------- Co-authored-by: lightclient <lightclient@protonmail.com> * core/txpool: remove locals-tracking from txpools (#30559) Replaces #29297, descendant from #27535 --------- This PR removes `locals` as a concept from transaction pools. Therefore, the pool now acts as very a good simulation/approximation of how our peers' pools behave. What this PR does instead, is implement a locals-tracker, which basically is a little thing which, from time to time, asks the pool "did you forget this transaction?". If it did, the tracker resubmits it. If the txpool _had_ forgotten it, chances are that the peers had also forgotten it. It will be propagated again. Doing this change means that we can simplify the pool internals, quite a lot. ### The semantics of `local` Historically, there has been two features, or usecases, that has been combined into the concept of `locals`. 1. "I want my local node to remember this transaction indefinitely, and resubmit to the network occasionally" 2. "I want this (valid) transaction included to be top-prio for my miner" This PR splits these features up, let's call it `1: local` and `2: prio`. The `prio` is not actually individual transaction, but rather a set of `address`es to prioritize. The attribute `local` means it will be tracked, and `prio` means it will be prioritized by miner. For `local`: anything transaction received via the RPC is marked as `local`, and tracked by the tracker. For `prio`: any transactions from this sender is included first, when building a block. The existing commandline-flag `--txpool.locals` sets the set of `prio` addresses. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * core/txpool/blobpool: fix incorrect arguments in test (#31127) Fixes the linter on master which was broken by https://github.com/ethereum/go-ethereum/pull/30559 * consensus/misc/eip4844: use head's target blobs, not parent (#31101) A clarification was made to EIP-7691 stating that at the fork boundary it is required to use the target blob count associated with the head block, rather than the parent as implemented here. See for more: https://github.com/ethereum/EIPs/pull/9249 * consensus/misc/eip4844: more changes for blob gas calculation (#31128) This PR changes the signature of `CalcExcessBlobGas` to take in just the header timestamp instead of the whole object. It also adds a sanity check for the parent->child block order to `VerifyEIP4844Header`. * core/tracing: state journal wrapper (#30441) Here we add some more changes for live tracing API v1.1: - Hook `OnSystemCallStartV2` was introduced with `VMContext` as parameter. - Hook `OnBlockHashRead` was introduced. - `GetCodeHash` was added to the state interface - The new `WrapWithJournal` construction helps with tracking EVM reverts in the tracer. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * all: update license comments and AUTHORS (#31133) * build: update to Go 1.23.6 (#31130) Co-authored-by: Felix Lange <fjl@twurst.com> * build: update EEST fixtures to prague devnet-6 (#31088) Co-authored-by: lightclient <lightclient@protonmail.com> * version: release go-ethereum v1.15.0 * version: begin v1.15.1 release cycle * cmd/devp2p/internal/ethtest: remove TD from status validation (#31137) After recent changes in Geth (removing TD):39638c81c5 (diff-d70a44d4b7a0e84fe9dcca25d368f626ae6c9bc0b8fe9690074ba92d298bcc0d)Non-Geth clients are failing many devp2p tests with an error: `peering failed: status exchange failed: wrong TD in status: have 1 want 0` Right now only Geth is passing it - all other clients are affected by this change. I think there should be no validation of TD when checking `Status` message in hive tests. Now Geth has 0 (and hive tests requires 0) and all other clients have actual TD. And on real networks there is no validation of TD when peering * params,core/forkid: enable prague on holesky and sepolia (#31139) Agreed to the following fork dates for Holesky and Sepolia on ACDC 150 Holesky slot: 3710976 (Mon, Feb 24 at 21:55:12 UTC) Sepolia slot: 7118848 (Wed, Mar 5 at 07:29:36 UTC) * consensus/beacon: remove TestingTTDBlock (#31153) This removes the method `TestingTTDBlock` introduced by #30744. It was added to make the beacon consensus engine aware of the merge block in tests without relying on the total difficulty. However, tracking the merge block this way is very annoying. We usually configure forks in the `ChainConfig`, but the method is on the consensus engine, which isn't always created in the same place. By sidestepping the `ChainConfig` we don't get the usual fork-order checking, so it's possible to enable the merge before the London fork, for example. This in turn can lead to very hard-to-debug outputs and validation errors. So here I'm changing the consensus engine to check the `MergeNetsplitBlock` instead. Alternatively, we assume a network is merged if it has a `TerminalTotalDifficulty` of zero, which is a very common configuration in tests. * p2p/discover: remove unused parameter in revalidationList.get (#31155) * p2p/discover: make discv5 response timeout configurable (#31119) * core/txpool/legacypool: add support for SetCode transactions (#31073) The new SetCode transaction type introduces some additional complexity when handling the transaction pool. This complexity stems from two new account behaviors: 1. The balance and nonce of an account can change during regular transaction execution *when they have a deployed delegation*. 2. The nonce and code of an account can change without any EVM execution at all. This is the "set code" mechanism introduced by EIP-7702. The first issue has already been considered extensively during the design of ERC-4337, and we're relatively confident in the solution of simply limiting the number of in-flight pending transactions an account can have to one. This puts a reasonable bound on transaction cancellation. Normally to cancel, you would need to spend 21,000 gas. Now it's possible to cancel for around the cost of warming the account and sending value (`2,600+9,000=11,600`). So 50% cheaper. The second issue is more novel and needs further consideration. Since authorizations are not bound to a specific transaction, we cannot drop transactions with conflicting authorizations. Otherwise, it might be possible to cherry-pick authorizations from txs and front run them with different txs at much lower fee amounts, effectively DoSing the authority. Fortunately, conflicting authorizations do not affect the underlying validity of the transaction so we can just accept both. --------- Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: Felix Lange <fjl@twurst.com> * internal/ethapi: fix panic in debug methods (#31157) Fixes an error when the block is not found in debug methods. * trie: copy preimage store pointer in StateTrie.Copy (#31158) This fixes an error where executing `evm run --dump ...` omits preimages from the dump (because the statedb used for execution is a copy of another instance). * go.mod: update blst to v0.3.14 (#31165) closes https://github.com/ethereum/go-ethereum/issues/31072 BLST released their newest version which includes a fix for go v.1.24: https://github.com/supranational/blst/releases/tag/v0.3.14 I went through all commits between 0.3.14 and 0.3.13 for a sanity check * core: sanity-check fork configuration in genesis (#31171) This is to prevent a crash on startup with a custom genesis configuration. With this change in place, upgrading a chain created by geth v1.14.x and below will now print an error instead of crashing: Fatal: Failed to register the Ethereum service: invalid chain configuration: missing entry for fork "cancun" in blobSchedule Arguably this is not great, and it should just auto-upgrade the config. We'll address this in a follow-up PR for geth v1.15.2 * core/rawdb: skip setting flushOffset in read-only mode (#31173) This PR addresses a flaw in the freezer table upgrade path. In v1.15.0, freezer table v2 was introduced, including an additional field (`flushOffset`) maintained in the metadata file. To ensure backward compatibility, an upgrade path was implemented for legacy freezer tables by setting `flushOffset` to the size of the index file. However, if the freezer table is opened in read-only mode, this file write operation is rejected, causing Geth to shut down entirely. Given that invalid items in the freezer index file can be detected and truncated, all items in freezer v0 index files are guaranteed to be complete. Therefore, when operating in read-only mode, it is safe to use the freezer data without performing an upgrade. * version: release go-ethereum v1.15.1 stable * version: begin v1.15.2 release cycle * core/types: create block's bloom by merging receipts' bloom (#31129) Currently, when calculating block's bloom, we loop through all the receipt logs to calculate the hash value. However, normally, after going through applyTransaction, the receipt's bloom is already calculated based on the receipt log, so the block's bloom can be calculated by just ORing these receipt's blooms. ``` goos: darwin goarch: arm64 pkg: github.com/ethereum/go-ethereum/core/types cpu: Apple M1 Pro BenchmarkCreateBloom BenchmarkCreateBloom/small BenchmarkCreateBloom/small-10 810922 1481 ns/op 104 B/op 5 allocs/op BenchmarkCreateBloom/large BenchmarkCreateBloom/large-10 8173 143764 ns/op 9614 B/op 401 allocs/op BenchmarkCreateBloom/small-mergebloom BenchmarkCreateBloom/small-mergebloom-10 5178918 232.0 ns/op 0 B/op 0 allocs/op BenchmarkCreateBloom/large-mergebloom BenchmarkCreateBloom/large-mergebloom-10 54110 22207 ns/op 0 B/op 0 allocs/op ``` --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> Co-authored-by: Zsolt Felfoldi <zsfelfoldi@gmail.com> * consensus/beacon: fix isPostMerge for mainnet (#31191) This fixes a regression introduced in #31153 where we didn't consider mainnet to be in PoS, causing #31190. The problem is, `params.MainnetChainConfig` does not have a defined `MergeNetsplitBlock`, so it isn't considered to be in PoS in `CalcDifficulty`. * p2p: fix marshaling of NAT in TOML (#31192) This fixes an issue where a nat.Interface unmarshaled from the TOML config file could not be re-marshaled to TOML correctly. Fixes #31183 * eth/protocols/eth: add discovery iterator to protocol (#31185) We somehow forgot to add this in #30302, so discv5 and DNS have actually been disabled since then. Fixes #31168 * version: release go-ethereum v1.15.2 stable * version: begin v1.15.3 release cycle * trie: do not expect ordering in stacktrie during fuzzing (#31170) This PR removes the assumption of the stacktrie and trie to have the same ordering. This was hit by the fuzzers on oss-fuzz --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * utils: clarify description for history.state flag (#31164) * ethclient: add comment describing block number tags (#30984) Adds a comment on how to use rpc.*BlockNumber and the explanation of the block number tags --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * core/vm: clean up EVM environmental structure (#31061) This PR does a few things including: - Remove `ContractRef` interface - Remove `vm.AccountRef` which implements `ContractRef` interface - Maintain the `jumpDests` struct in EVM for sharing between call frames - Simplify the delegateCall context initialization * params: add osaka blob schedule (#31174) Prevents crashes when running execution spec tests for osaka * eth/catalyst: support earlier forks in SimulatedBeacon (#31084) Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> * build: upgrade -dlgo version to Go 1.24.0 (#31159) Co-authored-by: Felix Lange <fjl@twurst.com> * core/asm: delete assembler/disassembler (#31211) I maintain an improved version of the go-ethereum assembler at https://github.com/fjl/geas. We don't really use core/asm in our tests, and it has some bugs that prevent it from being useful, so I'm removing the package. * .github: downgrade go for lint step (#31217) * core/txpool/legacypool: add setCodeTx reorg test (#31206) This PR adds a test that makes sure that a node can send multiple transactions again once a authorization is removed * internal/ethapi: handle prague system calls in eth_simulate (#31176) eth_simulate was not processing prague system calls for history contract and EL requests resulting in inaccurate stateRoot and requestsRoot fields in the block. * eth/tracers: refactor block context in test runner (#29450) This commit contains a minor refactoring of the block context used within the test runners. --------- Signed-off-by: jsvisa <delweng@gmail.com> * oss-fuzz: remove deprecated targets (#31224) Fixes https://github.com/ethereum/go-ethereum/issues/31223 (sorry, I thought the fork fork would be created on my repo, not upstream, when I used the GH editor) * p2p/nat: remove test with default servers (#31225) The test occasionally fails when network connectivity is bad or if it hits the wrong server. We usually don't add tests with external network dependency so I'm removing them. Fixes #31220 * core/types: remove unneeded todo marker (#31179) * signer/core: fix encoding of `bytes` nested within array (#31049) Fixes an incorrect encoding of recursive bytes types. closes https://github.com/ethereum/go-ethereum/issues/30979 * internal/ethapi: fix prev hashes in eth_simulate (#31122) Shout-out to @Gabriel-Trintinalia for discovering this issue. The gist of it as follows: When processing a block, we should provide the parent block as well as the last 256 block hashes. Some of these parents data (specifically the hash) was incorrect because even though during the processing of the parent block we have updated the header, that header was not updating the TransactionsRoot and ReceiptsRoot fields (types.NewBlock makes a new copy of the header and changes it only on that instance). --------- Co-authored-by: lightclient <lightclient@protonmail.com> * cmd/clef: improve documentation in readme (#31218) Fixed broken or outdated links and improved documentation formatting to ensure consistency and correct references. --------- Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> * build: replace `tenv` linter with `usetesting` (#31172) * eth: report error from setupDiscovery at startup (#31233) I ran into this while trying to debug a discv5 thing. I tried to disable DNS discovery using `--discovery.dns=false`, which doesn't work. Annoyingly, geth started anyway and discarded the error silently. I eventually found my mistake, but it took way longer than it should have. Also including a small change to the error message for invalid DNS URLs here. The user actually needs to see the URL to make sense of the error. * go.mod: update cloudflare-go (#31240) Updates cloudflare-go from v0.79.0 to v0.114.0 which also gets rid of a dependency to `github.com/hashicorp/go-retryablehttp` which had a security flaw. Diff: https://github.com/cloudflare/cloudflare-go/compare/v0.79.0...v0.114.0 I did a quick sanity check on the diff on all methods that we use and went through the release notes, there was nothing related to how we use it afaict * crypto: add comment to FromECDSAPub (#31241) closes https://github.com/ethereum/go-ethereum/issues/26240 * core/txpool: move setcode tx validation into legacyPool (#31209) In this PR, several improvements have been made: Authorization-related validations have been moved to legacyPool. Previously, these checks were part of the standard validation procedure, which applies common validations across different pools. Since these checks are specific to SetCode transactions, relocating them to legacyPool is a more reasonable choice. Additionally, authorization conflict checks are now performed regardless of whether the transaction is a replacement or not. --------- Co-authored-by: lightclient <lightclient@protonmail.com> * params: add deposit contract addresses (#31247) We forgot to add the deposit contract address for holesky, causing deposits to not be flagged correctly --------- Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> * ethclient/simulated: add goroutine leak test (#31033) Adds a basic sanity test case to catch any go-routines leaked from instantiation/closing of a simulated backend. * eth/protocols/eth: fix loading "eth" ENR key in dial filter (#31251) This fixes an issue where dial candidates from discv5 would be ignored because the "eth" ENR entry was not loaded correctly. * version: release go-ethereum v1.15.3 stable * version: begin v1.15.4 release cycle * build: filter out .git folder for go generate check (#31265) Fixes lint issue >>> /home/appveyor/.gvm/gos/go1.24.0/bin/go generate ./... ci.go:404: File changed: .git/index ci.go:407: One or more generated files were updated by running 'go generate ./...' exit status 1 * eth/gasprice: fix eth_feeHistory blobGasRatio (#31246) This change divides BlobGasUsed by MaxBlobGasPerBlock instead of MaxBlobsPerBlock. Dividing by MaxBlobsPerBlock meant the blobGasUsedRatio was an incorrect large number. This bug was introduced by a typo [here](e6f3ce7b16 (diff-3357b2399699d7cf954c543cbfb02ff442eb24491e55f5e813e3cc85829b3e8dR110)) Fixes https://github.com/ethereum/go-ethereum/issues/31245 * cmd/workload: RPC workload tests for filters and history (#31189) Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> * eth/gasprice: sanity check ratio values (#31270) Follow on to #31246. Adds a sanity check in the test to make sure the ratio value never goes over 1. Would have avoided the issue in #31245. * core/txpool: fix error logs flood caused by removeAuthorities (#31249) when remove an non-SetCodeTxType transaction, error logs flood ``` t=2025-02-25T03:11:06+0000 lvl=error msg="Authority with untracked tx" addr=0xD5bf9221fCB1C31Cd1EE477a60c148d40dD63DC1 hash=0x626fdf205a5b1619deb2f9e51fed567353f80acbd522265b455daa0821c571d9 ``` in this PR, only try to removeAuthorities for txs with SetCodeTxType in addition, the performance of removeAuthorities improved a lot, because no need range all `t.auths` now. --------- Co-authored-by: lightclient <lightclient@protonmail.com> * build: update PPA Go bootstrap version to 1.23 (#31282) This is for fixing the PPA build, which has been failing since the update to Go 1.24. In Go 1.24, the required Go version for bootstrapping was updated to 1.22. In general, they are following through with always depending on the Go version two releases ago for bootstrapping. Since we still support Ubuntu Xenial (16.04) until its EOL date of 04/2026, and Xenial only has golang 1.10 as a package, we now need to build Go a total of four times to get the most recent version. I'm adding a step for Go 1.23 here. This should last us until Go 1.25, which should be out around 04/2026, and we can hopefully drop the first bootstrapping step at that time. * build: simplify go mod tidy check (#31266) This changes the go mod tidy check to use the go mod tidy -diff command, removing the custom diffing for go.mod. The check for go.mod/go.sum is now performed in the check_generate action. Also included is a change where check_generate and check_baddeps will now run on the GitHub Actions lint step. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * all: drop x/exp direct dependency (#30558) This is a not-particularly-important "cleanliness" PR. It removes the last remnants of the `x/exp` package, where we used the `maps.Keys` function. The original returned the keys in a slice, but when it became 'native' the signature changed to return an iterator, so the new idiom is `slices.Collect(maps.Keys(theMap))`, unless of course the raw iterator can be used instead. In some cases, where we previously collect into slice and then sort, we can now instead do `slices.SortXX` on the iterator instead, making the code a bit more concise. This PR might be _slighly_ less optimal, because the original `x/exp` implementation allocated the slice at the correct size off the bat, which I suppose the new code won't. Putting it up for discussion. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * build/deb: add step for new Go bootstrap to debian rules (#31283) Next attempt at fixing the build on launchpad.net * core/tracing: stringer for gas and nonce change reasons (#31234) * eth: remove EventMux accessors (#30017) Hi, it seems these methods in the `backend.go` and `api_backend.go` files are not used that expose the eventMux, but that is not needed. * eth: do not add failed tx to localTxTracker (#31202) In transaction-sending APIs such as `eth_sendRawTransaction`, a submitted transaction failing the configured txpool validation rules (i.e. fee too low) would cause an error to be returned, even though the transaction was successfully added into the locals tracker. Once added there, the transaction may even be included into the chain at a later time, when fee market conditions change. This change improves on this by performing the validation in the locals tracker, basically skipping some of the validation rules for local transactions. We still try to add the tx to the main pool immediately, but an error will only be returned for transactions which are fundamentally invalid. --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com> * eth: check blob transaction validity on the peer goroutine when received (#31219) This ensures that if we receive a blob transaction announcement where we cannot link the tx to the sidecar commitments, we will drop the sending peer. This check is added in the protocol handler for the PooledTransactions message. Tests for this have also been added in the cross-client "eth" protocol test suite. --------- Co-authored-by: Felix Lange <fjl@twurst.com> * ethclient: add BlobBaseFee method (#31290) * version: release go-ethereum v1.15.4 stable * version: begin v1.15.5 release cycle * eth/tracers: fix omitempty for memory and storage (#31289) This fixes a regression in the opcode tracer API where we would log empty memory and storage fields. * build: upgrade to Go 1.24.1 and golangci-lint 1.64.4 (#31313) - upgrade -dlgo version to Go 1.24.1 - upgrade golangci-lint version to 1.64.6 * core: match on deposit contract log topic (#31317) This resolves a situation on the Sepolia testnet, which has a different deposit contract. The contract on that network emits two kinds of logs, instead of only deposit events like the deposit contract on mainnet. So we need to skip events with mismatched topics. * version: release v1.15.5 stable * fix: fix compiler errors * feat: merge 1.15.5 * fix: fix lint errors * feat: fix some diffs * feat: update types * feat: update comments * feat: fix some diffs * feat: fix some diffs * test: update tests * feat: update some jsons * test: update core/state_transition.go * feat: fix a test issue * chore: update ci * chore: update ci * feat: update config.go * feat: update core/state_processor.go * feat: update core/state_processor.go * feat: more changes * feat: update * feat: update state * feat: update ci --------- Signed-off-by: wangjingcun <wangjingcun@aliyun.com> Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: jsvisa <delweng@gmail.com> Co-authored-by: Madhur Shrimal <madhur.shrimal@gmail.com> Co-authored-by: Martin Holst Swende <martin@swende.se> Co-authored-by: Naveen <116692862+naveen-imtb@users.noreply.github.com> Co-authored-by: jwasinger <j-wasinger@hotmail.com> Co-authored-by: Felföldi Zsolt <zsfelfoldi@gmail.com> Co-authored-by: Karol Chojnowski <karolchojnowski95@gmail.com> Co-authored-by: zhiqiangxu <652732310@qq.com> Co-authored-by: Péter Szilágyi <peterke@gmail.com> Co-authored-by: rjl493456442 <garyrong0905@gmail.com> Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de> Co-authored-by: tianyeyouyou <tianyeyouyou@gmail.com> Co-authored-by: witty <131909329+0xwitty@users.noreply.github.com> Co-authored-by: Marius Kjærstad <sandakersmann@users.noreply.github.com> Co-authored-by: bitcoin-lightning <153181187+AtomicInnovation321@users.noreply.github.com> Co-authored-by: Felix Lange <fjl@twurst.com> Co-authored-by: Håvard Anda Estensen <haavard.ae@gmail.com> Co-authored-by: Hyunsoo Shin (Lake) <hyunsooda@kaist.ac.kr> Co-authored-by: wangjingcun <wangjingcun@aliyun.com> Co-authored-by: j2gg0s <j2gg0s@gmail.com> Co-authored-by: Daniel Liu <139250065@qq.com> Co-authored-by: Jordan Krage <jmank88@gmail.com> Co-authored-by: Arran Schlosberg <519948+ARR4N@users.noreply.github.com> Co-authored-by: Nebojsa Urosevic <nebojsa94@users.noreply.github.com> Co-authored-by: Ng Wei Han <47109095+weiihann@users.noreply.github.com> Co-authored-by: Sina Mahmoodi <itz.s1na@gmail.com> Co-authored-by: Sina M <1591639+s1na@users.noreply.github.com> Co-authored-by: lightclient <14004106+lightclient@users.noreply.github.com> Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Co-authored-by: Ignacio Hagopian <jsign.uy@gmail.com> Co-authored-by: steven <wangpeculiar@gmail.com> Co-authored-by: Zheyuan He <ecjgvmhc@gmail.com> Co-authored-by: Hteev Oli <gethorz@proton.me> Co-authored-by: Darren Kelly <107671032+darrenvechain@users.noreply.github.com> Co-authored-by: gitglorythegreat <t4juu3@proton.me> Co-authored-by: lorenzo <31852651+lorenzo-dev1@users.noreply.github.com> Co-authored-by: Antony Denyer <email@antonydenyer.co.uk> Co-authored-by: Lucas <lucaslg360@gmail.com> Co-authored-by: Mario Vega <marioevz@gmail.com> Co-authored-by: maskpp <maskpp266@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: かげ <47621124+ronething-bot@users.noreply.github.com> Co-authored-by: georgehao <haohongfan@gmail.com> Co-authored-by: Savely <136869149+savvar9991@users.noreply.github.com> Co-authored-by: Ceyhun Onur <ceyhun.onur@avalabs.org> Co-authored-by: Daniel Liu <liudaniel@qq.com> Co-authored-by: Martin Redmond <21436+reds@users.noreply.github.com> Co-authored-by: Martin <mrscdevel@gmail.com> Co-authored-by: dashangcun <907225865@qq.com> Co-authored-by: Quentin McGaw <quentin.mcgaw@gmail.com> Co-authored-by: Paul Lange <palango@users.noreply.github.com> Co-authored-by: Matthieu Vachon <matt@streamingfast.io> Co-authored-by: Cedrick <Cedrickentrep@gmail.com> Co-authored-by: Shude Li <islishude@gmail.com> Co-authored-by: levisyin <lilassherl@gmail.com> Co-authored-by: Danno Ferrin <danno@numisight.com> Co-authored-by: ucwong <ucwong@126.com> Co-authored-by: Michael de Hoog <michael.dehoog@coinbase.com> Co-authored-by: zhen peng <505380967@qq.com> Co-authored-by: Christina <156356273+cratiu222@users.noreply.github.com> Co-authored-by: Ryan Tinianov <tinianov@live.com> Co-authored-by: lightclient <lightclient@protonmail.com> Co-authored-by: kazak <alright-epsilon8h@icloud.com> Co-authored-by: ericxtheodore <ericxtheodore@outlook.com> Co-authored-by: Marcin Sobczak <77129288+marcindsobczak@users.noreply.github.com> Co-authored-by: Harry Ngo <17699212+huyngopt1994@users.noreply.github.com> Co-authored-by: Chen Kai <281165273grape@gmail.com> Co-authored-by: minh-bq <minh.bui@skymavis.com> Co-authored-by: piersy <pierspowlesland@gmail.com> Co-authored-by: EdisonSR <61781882@qq.com> Co-authored-by: nethoxa <135072738+nethoxa@users.noreply.github.com> Co-authored-by: Delweng <delweng@gmail.com> Co-authored-by: rrhlrmrr <39875249+rrhlrmrr@users.noreply.github.com> Co-authored-by: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Co-authored-by: James <jamesstanleystewart@gmail.com> Co-authored-by: buddho <galaxystroller@gmail.com> Co-authored-by: Darioush Jalali <darioush.jalali@avalabs.org> Co-authored-by: Kuwon Sebastian Na <laggu91@gmail.com>
4267 lines
157 KiB
Go
4267 lines
157 KiB
Go
// Copyright 2014 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
gomath "math"
|
|
"math/big"
|
|
"math/rand"
|
|
"os"
|
|
"path"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/consensus"
|
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
|
"github.com/ethereum/go-ethereum/core/state"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
|
"github.com/ethereum/go-ethereum/core/vm/program"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
|
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
"github.com/ethereum/go-ethereum/trie"
|
|
"github.com/holiman/uint256"
|
|
)
|
|
|
|
// So we can deterministically seed different blockchains
|
|
var (
|
|
canonicalSeed = 1
|
|
forkSeed = 2
|
|
)
|
|
|
|
// newCanonical creates a chain database, and injects a deterministic canonical
|
|
// chain. Depending on the full flag, it creates either a full block chain or a
|
|
// header only chain. The database and genesis specification for block generation
|
|
// are also returned in case more test blocks are needed later.
|
|
func newCanonical(engine consensus.Engine, n int, full bool, scheme string) (ethdb.Database, *Genesis, *BlockChain, error) {
|
|
var (
|
|
genesis = &Genesis{
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
Config: params.AllEthashProtocolChanges,
|
|
}
|
|
)
|
|
// Initialize a fresh chain with only a genesis block
|
|
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
|
|
// Create and inject the requested chain
|
|
if n == 0 {
|
|
return rawdb.NewMemoryDatabase(), genesis, blockchain, nil
|
|
}
|
|
if full {
|
|
// Full block-chain requested
|
|
genDb, blocks := makeBlockChainWithGenesis(genesis, n, engine, canonicalSeed)
|
|
_, err := blockchain.InsertChain(blocks)
|
|
return genDb, genesis, blockchain, err
|
|
}
|
|
// Header-only chain requested
|
|
genDb, headers := makeHeaderChainWithGenesis(genesis, n, engine, canonicalSeed)
|
|
_, err := blockchain.InsertHeaderChain(headers)
|
|
return genDb, genesis, blockchain, err
|
|
}
|
|
|
|
func newGwei(n int64) *big.Int {
|
|
return new(big.Int).Mul(big.NewInt(n), big.NewInt(params.GWei))
|
|
}
|
|
|
|
// Test fork of length N starting from block i
|
|
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, scheme string) {
|
|
// Copy old chain up to #i into a new db
|
|
genDb, _, blockchain2, err := newCanonical(ethash.NewFaker(), i, full, scheme)
|
|
if err != nil {
|
|
t.Fatal("could not make new canonical in testFork", err)
|
|
}
|
|
defer blockchain2.Stop()
|
|
|
|
// Assert the chains have the same header/block at #i
|
|
var hash1, hash2 common.Hash
|
|
if full {
|
|
hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
|
|
hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
|
|
} else {
|
|
hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
|
|
hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
|
|
}
|
|
if hash1 != hash2 {
|
|
t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
|
|
}
|
|
// Extend the newly created chain
|
|
var (
|
|
blockChainB []*types.Block
|
|
headerChainB []*types.Header
|
|
)
|
|
if full {
|
|
blockChainB = makeBlockChain(blockchain2.chainConfig, blockchain2.GetBlockByHash(blockchain2.CurrentBlock().Hash()), n, ethash.NewFaker(), genDb, forkSeed)
|
|
if _, err := blockchain2.InsertChain(blockChainB); err != nil {
|
|
t.Fatalf("failed to insert forking chain: %v", err)
|
|
}
|
|
} else {
|
|
headerChainB = makeHeaderChain(blockchain2.chainConfig, blockchain2.CurrentHeader(), n, ethash.NewFaker(), genDb, forkSeed)
|
|
if _, err := blockchain2.InsertHeaderChain(headerChainB); err != nil {
|
|
t.Fatalf("failed to insert forking chain: %v", err)
|
|
}
|
|
}
|
|
// Sanity check that the forked chain can be imported into the original
|
|
if full {
|
|
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
|
|
t.Fatalf("failed to import forked block chain: %v", err)
|
|
}
|
|
} else {
|
|
if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
|
|
t.Fatalf("failed to import forked header chain: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// testBlockChainImport tries to process a chain of blocks, writing them into
|
|
// the database if successful.
|
|
func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|
for _, block := range chain {
|
|
// Try and process the block
|
|
err := blockchain.engine.VerifyHeader(blockchain, block.Header())
|
|
if err == nil {
|
|
err = blockchain.validator.ValidateBody(block)
|
|
}
|
|
if err != nil {
|
|
if err == ErrKnownBlock {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
|
if err != nil {
|
|
blockchain.reportBlock(block, res, err)
|
|
return err
|
|
}
|
|
err = blockchain.validator.ValidateState(block, statedb, res, false)
|
|
if err != nil {
|
|
blockchain.reportBlock(block, res, err)
|
|
return err
|
|
}
|
|
|
|
blockchain.chainmu.MustLock()
|
|
rawdb.WriteBlock(blockchain.db, block)
|
|
statedb.Commit(block.NumberU64(), false, false)
|
|
blockchain.chainmu.Unlock()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// testHeaderChainImport tries to process a chain of header, writing them into
|
|
// the database if successful.
|
|
func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
|
|
for _, header := range chain {
|
|
// Try and validate the header
|
|
if err := blockchain.engine.VerifyHeader(blockchain, header); err != nil {
|
|
return err
|
|
}
|
|
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
|
blockchain.chainmu.MustLock()
|
|
rawdb.WriteHeader(blockchain.db, header)
|
|
blockchain.chainmu.Unlock()
|
|
}
|
|
return nil
|
|
}
|
|
func TestLastBlock(t *testing.T) {
|
|
testLastBlock(t, rawdb.HashScheme)
|
|
testLastBlock(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLastBlock(t *testing.T, scheme string) {
|
|
genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 0, true, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to create pristine chain: %v", err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
blocks := makeBlockChain(blockchain.chainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), 1, ethash.NewFullFaker(), genDb, 0)
|
|
if _, err := blockchain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("Failed to insert block: %v", err)
|
|
}
|
|
if blocks[len(blocks)-1].Hash() != rawdb.ReadHeadBlockHash(blockchain.db) {
|
|
t.Fatalf("Write/Get HeadBlockHash failed")
|
|
}
|
|
}
|
|
|
|
// Test inserts the blocks/headers after the fork choice rule is changed.
|
|
// The chain is reorged to whatever specified.
|
|
func testInsertAfterMerge(t *testing.T, blockchain *BlockChain, i, n int, full bool, scheme string) {
|
|
// Copy old chain up to #i into a new db
|
|
genDb, _, blockchain2, err := newCanonical(ethash.NewFaker(), i, full, scheme)
|
|
if err != nil {
|
|
t.Fatal("could not make new canonical in testFork", err)
|
|
}
|
|
defer blockchain2.Stop()
|
|
|
|
// Assert the chains have the same header/block at #i
|
|
var hash1, hash2 common.Hash
|
|
if full {
|
|
hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
|
|
hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
|
|
} else {
|
|
hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
|
|
hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
|
|
}
|
|
if hash1 != hash2 {
|
|
t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
|
|
}
|
|
|
|
// Extend the newly created chain
|
|
if full {
|
|
blockChainB := makeBlockChain(blockchain2.chainConfig, blockchain2.GetBlockByHash(blockchain2.CurrentBlock().Hash()), n, ethash.NewFaker(), genDb, forkSeed)
|
|
if _, err := blockchain2.InsertChain(blockChainB); err != nil {
|
|
t.Fatalf("failed to insert forking chain: %v", err)
|
|
}
|
|
if blockchain2.CurrentBlock().Number.Uint64() != blockChainB[len(blockChainB)-1].NumberU64() {
|
|
t.Fatalf("failed to reorg to the given chain")
|
|
}
|
|
if blockchain2.CurrentBlock().Hash() != blockChainB[len(blockChainB)-1].Hash() {
|
|
t.Fatalf("failed to reorg to the given chain")
|
|
}
|
|
} else {
|
|
headerChainB := makeHeaderChain(blockchain2.chainConfig, blockchain2.CurrentHeader(), n, ethash.NewFaker(), genDb, forkSeed)
|
|
if _, err := blockchain2.InsertHeaderChain(headerChainB); err != nil {
|
|
t.Fatalf("failed to insert forking chain: %v", err)
|
|
}
|
|
if blockchain2.CurrentHeader().Number.Uint64() != headerChainB[len(headerChainB)-1].Number.Uint64() {
|
|
t.Fatalf("failed to reorg to the given chain")
|
|
}
|
|
if blockchain2.CurrentHeader().Hash() != headerChainB[len(headerChainB)-1].Hash() {
|
|
t.Fatalf("failed to reorg to the given chain")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, it can be extended
|
|
// with various length chains.
|
|
func TestExtendCanonicalHeaders(t *testing.T) {
|
|
testExtendCanonical(t, false, rawdb.HashScheme)
|
|
testExtendCanonical(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestExtendCanonicalBlocks(t *testing.T) {
|
|
testExtendCanonical(t, true, rawdb.HashScheme)
|
|
testExtendCanonical(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testExtendCanonical(t *testing.T, full bool, scheme string) {
|
|
length := 5
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
// Start fork from current height
|
|
testFork(t, processor, length, 1, full, scheme)
|
|
testFork(t, processor, length, 2, full, scheme)
|
|
testFork(t, processor, length, 5, full, scheme)
|
|
testFork(t, processor, length, 10, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, it can be extended
|
|
// with various length chains.
|
|
func TestExtendCanonicalHeadersAfterMerge(t *testing.T) {
|
|
testExtendCanonicalAfterMerge(t, false, rawdb.HashScheme)
|
|
testExtendCanonicalAfterMerge(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestExtendCanonicalBlocksAfterMerge(t *testing.T) {
|
|
testExtendCanonicalAfterMerge(t, true, rawdb.HashScheme)
|
|
testExtendCanonicalAfterMerge(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testExtendCanonicalAfterMerge(t *testing.T, full bool, scheme string) {
|
|
length := 5
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
testInsertAfterMerge(t, processor, length, 1, full, scheme)
|
|
testInsertAfterMerge(t, processor, length, 10, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating shorter
|
|
// forks do not take canonical ownership.
|
|
func TestShorterForkHeaders(t *testing.T) {
|
|
testShorterFork(t, false, rawdb.HashScheme)
|
|
testShorterFork(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestShorterForkBlocks(t *testing.T) {
|
|
testShorterFork(t, true, rawdb.HashScheme)
|
|
testShorterFork(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testShorterFork(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
// Sum of numbers must be less than `length` for this to be a shorter fork
|
|
testFork(t, processor, 0, 3, full, scheme)
|
|
testFork(t, processor, 0, 7, full, scheme)
|
|
testFork(t, processor, 1, 1, full, scheme)
|
|
testFork(t, processor, 1, 7, full, scheme)
|
|
testFork(t, processor, 5, 3, full, scheme)
|
|
testFork(t, processor, 5, 4, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating shorter
|
|
// forks do not take canonical ownership.
|
|
func TestShorterForkHeadersAfterMerge(t *testing.T) {
|
|
testShorterForkAfterMerge(t, false, rawdb.HashScheme)
|
|
testShorterForkAfterMerge(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestShorterForkBlocksAfterMerge(t *testing.T) {
|
|
testShorterForkAfterMerge(t, true, rawdb.HashScheme)
|
|
testShorterForkAfterMerge(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testShorterForkAfterMerge(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
testInsertAfterMerge(t, processor, 0, 3, full, scheme)
|
|
testInsertAfterMerge(t, processor, 0, 7, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 1, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 7, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 3, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 4, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating longer
|
|
// forks do take canonical ownership.
|
|
func TestLongerForkHeaders(t *testing.T) {
|
|
testLongerFork(t, false, rawdb.HashScheme)
|
|
testLongerFork(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestLongerForkBlocks(t *testing.T) {
|
|
testLongerFork(t, true, rawdb.HashScheme)
|
|
testLongerFork(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLongerFork(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
testInsertAfterMerge(t, processor, 0, 11, full, scheme)
|
|
testInsertAfterMerge(t, processor, 0, 15, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 10, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 12, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 6, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 8, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating longer
|
|
// forks do take canonical ownership.
|
|
func TestLongerForkHeadersAfterMerge(t *testing.T) {
|
|
testLongerForkAfterMerge(t, false, rawdb.HashScheme)
|
|
testLongerForkAfterMerge(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestLongerForkBlocksAfterMerge(t *testing.T) {
|
|
testLongerForkAfterMerge(t, true, rawdb.HashScheme)
|
|
testLongerForkAfterMerge(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLongerForkAfterMerge(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
testInsertAfterMerge(t, processor, 0, 11, full, scheme)
|
|
testInsertAfterMerge(t, processor, 0, 15, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 10, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 12, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 6, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 8, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating equal
|
|
// forks do take canonical ownership.
|
|
func TestEqualForkHeaders(t *testing.T) {
|
|
testEqualFork(t, false, rawdb.HashScheme)
|
|
testEqualFork(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestEqualForkBlocks(t *testing.T) {
|
|
testEqualFork(t, true, rawdb.HashScheme)
|
|
testEqualFork(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEqualFork(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
// Sum of numbers must be equal to `length` for this to be an equal fork
|
|
testFork(t, processor, 0, 10, full, scheme)
|
|
testFork(t, processor, 1, 9, full, scheme)
|
|
testFork(t, processor, 2, 8, full, scheme)
|
|
testFork(t, processor, 5, 5, full, scheme)
|
|
testFork(t, processor, 6, 4, full, scheme)
|
|
testFork(t, processor, 9, 1, full, scheme)
|
|
}
|
|
|
|
// Tests that given a starting canonical chain of a given size, creating equal
|
|
// forks do take canonical ownership.
|
|
func TestEqualForkHeadersAfterMerge(t *testing.T) {
|
|
testEqualForkAfterMerge(t, false, rawdb.HashScheme)
|
|
testEqualForkAfterMerge(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestEqualForkBlocksAfterMerge(t *testing.T) {
|
|
testEqualForkAfterMerge(t, true, rawdb.HashScheme)
|
|
testEqualForkAfterMerge(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEqualForkAfterMerge(t *testing.T, full bool, scheme string) {
|
|
length := 10
|
|
|
|
// Make first chain starting from genesis
|
|
_, _, processor, err := newCanonical(ethash.NewFaker(), length, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer processor.Stop()
|
|
|
|
testInsertAfterMerge(t, processor, 0, 10, full, scheme)
|
|
testInsertAfterMerge(t, processor, 1, 9, full, scheme)
|
|
testInsertAfterMerge(t, processor, 2, 8, full, scheme)
|
|
testInsertAfterMerge(t, processor, 5, 5, full, scheme)
|
|
testInsertAfterMerge(t, processor, 6, 4, full, scheme)
|
|
testInsertAfterMerge(t, processor, 9, 1, full, scheme)
|
|
}
|
|
|
|
// Tests that chains missing links do not get accepted by the processor.
|
|
func TestBrokenHeaderChain(t *testing.T) {
|
|
testBrokenChain(t, false, rawdb.HashScheme)
|
|
testBrokenChain(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestBrokenBlockChain(t *testing.T) {
|
|
testBrokenChain(t, true, rawdb.HashScheme)
|
|
testBrokenChain(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testBrokenChain(t *testing.T, full bool, scheme string) {
|
|
// Make chain starting from genesis
|
|
genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 10, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to make new canonical chain: %v", err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
// Create a forked chain, and try to insert with a missing link
|
|
if full {
|
|
chain := makeBlockChain(blockchain.chainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), 5, ethash.NewFaker(), genDb, forkSeed)[1:]
|
|
if err := testBlockChainImport(chain, blockchain); err == nil {
|
|
t.Errorf("broken block chain not reported")
|
|
}
|
|
} else {
|
|
chain := makeHeaderChain(blockchain.chainConfig, blockchain.CurrentHeader(), 5, ethash.NewFaker(), genDb, forkSeed)[1:]
|
|
if err := testHeaderChainImport(chain, blockchain); err == nil {
|
|
t.Errorf("broken header chain not reported")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that reorganising a long difficult chain after a short easy one
|
|
// overwrites the canonical numbers and links in the database.
|
|
func TestReorgLongHeaders(t *testing.T) {
|
|
testReorgLong(t, false, rawdb.HashScheme)
|
|
testReorgLong(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestReorgLongBlocks(t *testing.T) {
|
|
testReorgLong(t, true, rawdb.HashScheme)
|
|
testReorgLong(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testReorgLong(t *testing.T, full bool, scheme string) {
|
|
testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280+params.GenesisDifficulty.Int64(), full, scheme)
|
|
}
|
|
|
|
// Tests that reorganising a short difficult chain after a long easy one
|
|
// overwrites the canonical numbers and links in the database.
|
|
func TestReorgShortHeaders(t *testing.T) {
|
|
testReorgShort(t, false, rawdb.HashScheme)
|
|
testReorgShort(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestReorgShortBlocks(t *testing.T) {
|
|
testReorgShort(t, true, rawdb.HashScheme)
|
|
testReorgShort(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testReorgShort(t *testing.T, full bool, scheme string) {
|
|
// Create a long easy chain vs. a short heavy one. Due to difficulty adjustment
|
|
// we need a fairly long chain of blocks with different difficulties for a short
|
|
// one to become heavier than a long one. The 96 is an empirical value.
|
|
easy := make([]int64, 96)
|
|
for i := 0; i < len(easy); i++ {
|
|
easy[i] = 60
|
|
}
|
|
diff := make([]int64, len(easy)-1)
|
|
for i := 0; i < len(diff); i++ {
|
|
diff[i] = -9
|
|
}
|
|
testReorg(t, easy, diff, 12615120+params.GenesisDifficulty.Int64(), full, scheme)
|
|
}
|
|
|
|
func testReorg(t *testing.T, first, second []int64, td int64, full bool, scheme string) {
|
|
// Create a pristine chain and database
|
|
genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to create pristine chain: %v", err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
// Insert an easy and a difficult chain afterwards
|
|
easyBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), ethash.NewFaker(), genDb, len(first), func(i int, b *BlockGen) {
|
|
b.OffsetTime(first[i])
|
|
})
|
|
diffBlocks, _ := GenerateChain(params.TestChainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), ethash.NewFaker(), genDb, len(second), func(i int, b *BlockGen) {
|
|
b.OffsetTime(second[i])
|
|
})
|
|
if full {
|
|
if _, err := blockchain.InsertChain(easyBlocks); err != nil {
|
|
t.Fatalf("failed to insert easy chain: %v", err)
|
|
}
|
|
if _, err := blockchain.InsertChain(diffBlocks); err != nil {
|
|
t.Fatalf("failed to insert difficult chain: %v", err)
|
|
}
|
|
} else {
|
|
easyHeaders := make([]*types.Header, len(easyBlocks))
|
|
for i, block := range easyBlocks {
|
|
easyHeaders[i] = block.Header()
|
|
}
|
|
diffHeaders := make([]*types.Header, len(diffBlocks))
|
|
for i, block := range diffBlocks {
|
|
diffHeaders[i] = block.Header()
|
|
}
|
|
if _, err := blockchain.InsertHeaderChain(easyHeaders); err != nil {
|
|
t.Fatalf("failed to insert easy chain: %v", err)
|
|
}
|
|
if _, err := blockchain.InsertHeaderChain(diffHeaders); err != nil {
|
|
t.Fatalf("failed to insert difficult chain: %v", err)
|
|
}
|
|
}
|
|
// Check that the chain is valid number and link wise
|
|
if full {
|
|
prev := blockchain.CurrentBlock()
|
|
for block := blockchain.GetBlockByNumber(blockchain.CurrentBlock().Number.Uint64() - 1); block.NumberU64() != 0; prev, block = block.Header(), blockchain.GetBlockByNumber(block.NumberU64()-1) {
|
|
if prev.ParentHash != block.Hash() {
|
|
t.Errorf("parent block hash mismatch: have %x, want %x", prev.ParentHash, block.Hash())
|
|
}
|
|
}
|
|
} else {
|
|
prev := blockchain.CurrentHeader()
|
|
for header := blockchain.GetHeaderByNumber(blockchain.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, blockchain.GetHeaderByNumber(header.Number.Uint64()-1) {
|
|
if prev.ParentHash != header.Hash() {
|
|
t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests chain insertions in the face of one entity containing an invalid nonce.
|
|
func TestHeadersInsertNonceError(t *testing.T) {
|
|
testInsertNonceError(t, false, rawdb.HashScheme)
|
|
testInsertNonceError(t, false, rawdb.PathScheme)
|
|
}
|
|
func TestBlocksInsertNonceError(t *testing.T) {
|
|
testInsertNonceError(t, true, rawdb.HashScheme)
|
|
testInsertNonceError(t, true, rawdb.PathScheme)
|
|
}
|
|
|
|
func testInsertNonceError(t *testing.T, full bool, scheme string) {
|
|
doTest := func(i int) {
|
|
// Create a pristine chain and database
|
|
genDb, _, blockchain, err := newCanonical(ethash.NewFaker(), 0, full, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to create pristine chain: %v", err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
// Create and insert a chain with a failing nonce
|
|
var (
|
|
failAt int
|
|
failRes int
|
|
failNum uint64
|
|
)
|
|
if full {
|
|
blocks := makeBlockChain(blockchain.chainConfig, blockchain.GetBlockByHash(blockchain.CurrentBlock().Hash()), i, ethash.NewFaker(), genDb, 0)
|
|
|
|
failAt = rand.Int() % len(blocks)
|
|
failNum = blocks[failAt].NumberU64()
|
|
|
|
blockchain.engine = ethash.NewFakeFailer(failNum)
|
|
failRes, err = blockchain.InsertChain(blocks)
|
|
} else {
|
|
headers := makeHeaderChain(blockchain.chainConfig, blockchain.CurrentHeader(), i, ethash.NewFaker(), genDb, 0)
|
|
|
|
failAt = rand.Int() % len(headers)
|
|
failNum = headers[failAt].Number.Uint64()
|
|
|
|
blockchain.engine = ethash.NewFakeFailer(failNum)
|
|
blockchain.hc.engine = blockchain.engine
|
|
failRes, err = blockchain.InsertHeaderChain(headers)
|
|
}
|
|
// Check that the returned error indicates the failure
|
|
if failRes != failAt {
|
|
t.Errorf("test %d: failure (%v) index mismatch: have %d, want %d", i, err, failRes, failAt)
|
|
}
|
|
// Check that all blocks after the failing block have been inserted
|
|
for j := 0; j < i-failAt; j++ {
|
|
if full {
|
|
if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil {
|
|
t.Errorf("test %d: invalid block in chain: %v", i, block)
|
|
}
|
|
} else {
|
|
if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil {
|
|
t.Errorf("test %d: invalid header in chain: %v", i, header)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for i := 1; i < 25 && !t.Failed(); i++ {
|
|
doTest(i)
|
|
}
|
|
}
|
|
|
|
// Tests that fast importing a block chain produces the same chain data as the
|
|
// classical full block processing.
|
|
func TestFastVsFullChains(t *testing.T) {
|
|
testFastVsFullChains(t, rawdb.HashScheme)
|
|
testFastVsFullChains(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testFastVsFullChains(t *testing.T, scheme string) {
|
|
// Configure and generate a sample block chain
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
)
|
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 1024, func(i int, block *BlockGen) {
|
|
block.SetCoinbase(common.Address{0x00})
|
|
|
|
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
|
if i%3 == 2 {
|
|
for j := 0; j < i%4+1; j++ {
|
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, block.header.BaseFee, nil), signer, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
block.AddTx(tx)
|
|
}
|
|
}
|
|
// If the block number is a multiple of 5, add an uncle to the block
|
|
if i%5 == 4 {
|
|
block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 2).Hash(), Number: big.NewInt(int64(i))})
|
|
}
|
|
})
|
|
// Import the chain as an archive node for the comparison baseline
|
|
archiveDb := rawdb.NewMemoryDatabase()
|
|
archive, _ := NewBlockChain(archiveDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer archive.Stop()
|
|
|
|
if n, err := archive.InsertChain(blocks); err != nil {
|
|
t.Fatalf("failed to process block %d: %v", n, err)
|
|
}
|
|
// Fast import the chain as a non-archive node to test
|
|
fastDb := rawdb.NewMemoryDatabase()
|
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer fast.Stop()
|
|
|
|
headers := make([]*types.Header, len(blocks))
|
|
for i, block := range blocks {
|
|
headers[i] = block.Header()
|
|
}
|
|
if n, err := fast.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
|
}
|
|
// Freezer style fast import the chain.
|
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
defer ancientDb.Close()
|
|
|
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer ancient.Stop()
|
|
|
|
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
|
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
|
}
|
|
|
|
// Iterate over all chain data components, and cross reference
|
|
for i := 0; i < len(blocks); i++ {
|
|
num, hash, time := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
|
|
|
|
if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
|
|
t.Errorf("block #%d [%x]: header mismatch: fastdb %v, archivedb %v", num, hash, fheader, aheader)
|
|
}
|
|
if anheader, arheader := ancient.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); anheader.Hash() != arheader.Hash() {
|
|
t.Errorf("block #%d [%x]: header mismatch: ancientdb %v, archivedb %v", num, hash, anheader, arheader)
|
|
}
|
|
if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() {
|
|
t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock)
|
|
} else if types.DeriveSha(fblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) || types.DeriveSha(anblock.Transactions(), trie.NewStackTrie(nil)) != types.DeriveSha(arblock.Transactions(), trie.NewStackTrie(nil)) {
|
|
t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions())
|
|
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) {
|
|
t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles())
|
|
}
|
|
|
|
// Check receipts.
|
|
freceipts := rawdb.ReadReceipts(fastDb, hash, num, time, fast.Config())
|
|
anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, time, fast.Config())
|
|
areceipts := rawdb.ReadReceipts(archiveDb, hash, num, time, fast.Config())
|
|
if types.DeriveSha(freceipts, trie.NewStackTrie(nil)) != types.DeriveSha(areceipts, trie.NewStackTrie(nil)) {
|
|
t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
|
|
}
|
|
|
|
// Check that hash-to-number mappings are present in all databases.
|
|
if m := rawdb.ReadHeaderNumber(fastDb, hash); m == nil || *m != num {
|
|
t.Errorf("block #%d [%x]: wrong hash-to-number mapping in fastdb: %v", num, hash, m)
|
|
}
|
|
if m := rawdb.ReadHeaderNumber(ancientDb, hash); m == nil || *m != num {
|
|
t.Errorf("block #%d [%x]: wrong hash-to-number mapping in ancientdb: %v", num, hash, m)
|
|
}
|
|
if m := rawdb.ReadHeaderNumber(archiveDb, hash); m == nil || *m != num {
|
|
t.Errorf("block #%d [%x]: wrong hash-to-number mapping in archivedb: %v", num, hash, m)
|
|
}
|
|
}
|
|
|
|
// Check that the canonical chains are the same between the databases
|
|
for i := 0; i < len(blocks)+1; i++ {
|
|
if fhash, ahash := rawdb.ReadCanonicalHash(fastDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); fhash != ahash {
|
|
t.Errorf("block #%d: canonical hash mismatch: fastdb %v, archivedb %v", i, fhash, ahash)
|
|
}
|
|
if anhash, arhash := rawdb.ReadCanonicalHash(ancientDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); anhash != arhash {
|
|
t.Errorf("block #%d: canonical hash mismatch: ancientdb %v, archivedb %v", i, anhash, arhash)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that various import methods move the chain head pointers to the correct
|
|
// positions.
|
|
func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
|
testLightVsFastVsFullChainHeads(t, rawdb.HashScheme)
|
|
testLightVsFastVsFullChainHeads(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
|
// Configure and generate a sample block chain
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
)
|
|
height := uint64(64)
|
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
|
|
|
// makeDb creates a db instance for testing.
|
|
makeDb := func() ethdb.Database {
|
|
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
return db
|
|
}
|
|
// Configure a subchain to roll back
|
|
remove := blocks[height/2].NumberU64()
|
|
|
|
// Create a small assertion method to check the three heads
|
|
assert := func(t *testing.T, kind string, chain *BlockChain, header uint64, fast uint64, block uint64) {
|
|
t.Helper()
|
|
|
|
if num := chain.CurrentBlock().Number.Uint64(); num != block {
|
|
t.Errorf("%s head block mismatch: have #%v, want #%v", kind, num, block)
|
|
}
|
|
if num := chain.CurrentSnapBlock().Number.Uint64(); num != fast {
|
|
t.Errorf("%s head snap-block mismatch: have #%v, want #%v", kind, num, fast)
|
|
}
|
|
if num := chain.CurrentHeader().Number.Uint64(); num != header {
|
|
t.Errorf("%s head header mismatch: have #%v, want #%v", kind, num, header)
|
|
}
|
|
}
|
|
// Import the chain as an archive node and ensure all pointers are updated
|
|
archiveDb := makeDb()
|
|
defer archiveDb.Close()
|
|
|
|
archiveCaching := *defaultCacheConfig
|
|
archiveCaching.TrieDirtyDisabled = true
|
|
archiveCaching.StateScheme = scheme
|
|
|
|
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
if n, err := archive.InsertChain(blocks); err != nil {
|
|
t.Fatalf("failed to process block %d: %v", n, err)
|
|
}
|
|
defer archive.Stop()
|
|
|
|
assert(t, "archive", archive, height, height, height)
|
|
archive.SetHead(remove - 1)
|
|
assert(t, "archive", archive, height/2, height/2, height/2)
|
|
|
|
// Import the chain as a non-archive node and ensure all pointers are updated
|
|
fastDb := makeDb()
|
|
defer fastDb.Close()
|
|
fast, _ := NewBlockChain(fastDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer fast.Stop()
|
|
|
|
headers := make([]*types.Header, len(blocks))
|
|
for i, block := range blocks {
|
|
headers[i] = block.Header()
|
|
}
|
|
if n, err := fast.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
if n, err := fast.InsertReceiptChain(blocks, receipts, 0); err != nil {
|
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
|
}
|
|
assert(t, "fast", fast, height, height, 0)
|
|
fast.SetHead(remove - 1)
|
|
assert(t, "fast", fast, height/2, height/2, 0)
|
|
|
|
// Import the chain as a ancient-first node and ensure all pointers are updated
|
|
ancientDb := makeDb()
|
|
defer ancientDb.Close()
|
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer ancient.Stop()
|
|
|
|
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
|
}
|
|
assert(t, "ancient", ancient, height, height, 0)
|
|
ancient.SetHead(remove - 1)
|
|
assert(t, "ancient", ancient, 0, 0, 0)
|
|
|
|
if frozen, err := ancientDb.Ancients(); err != nil || frozen != 1 {
|
|
t.Fatalf("failed to truncate ancient store, want %v, have %v", 1, frozen)
|
|
}
|
|
// Import the chain as a light node and ensure all pointers are updated
|
|
lightDb := makeDb()
|
|
defer lightDb.Close()
|
|
light, _ := NewBlockChain(lightDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
if n, err := light.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
defer light.Stop()
|
|
|
|
assert(t, "light", light, height, 0, 0)
|
|
light.SetHead(remove - 1)
|
|
assert(t, "light", light, height/2, 0, 0)
|
|
}
|
|
|
|
// Tests that chain reorganisations handle transaction removals and reinsertions.
|
|
func TestChainTxReorgs(t *testing.T) {
|
|
testChainTxReorgs(t, rawdb.HashScheme)
|
|
testChainTxReorgs(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testChainTxReorgs(t *testing.T, scheme string) {
|
|
var (
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
|
key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
|
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
GasLimit: 3141592,
|
|
Alloc: types.GenesisAlloc{
|
|
addr1: {Balance: big.NewInt(1000000000000000)},
|
|
addr2: {Balance: big.NewInt(1000000000000000)},
|
|
addr3: {Balance: big.NewInt(1000000000000000)},
|
|
},
|
|
}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
)
|
|
|
|
// Create two transactions shared between the chains:
|
|
// - postponed: transaction included at a later block in the forked chain
|
|
// - swapped: transaction included at the same block number in the forked chain
|
|
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
|
|
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, key1)
|
|
|
|
// Create two transactions that will be dropped by the forked chain:
|
|
// - pastDrop: transaction dropped retroactively from a past block
|
|
// - freshDrop: transaction dropped exactly at the block where the reorg is detected
|
|
var pastDrop, freshDrop *types.Transaction
|
|
|
|
// Create three transactions that will be added in the forked chain:
|
|
// - pastAdd: transaction added before the reorganization is detected
|
|
// - freshAdd: transaction added at the exact block the reorg is detected
|
|
// - futureAdd: transaction added after the reorg has already finished
|
|
var pastAdd, freshAdd, futureAdd *types.Transaction
|
|
|
|
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, gen *BlockGen) {
|
|
switch i {
|
|
case 0:
|
|
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
|
|
|
|
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
|
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
|
|
|
case 2:
|
|
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key2)
|
|
|
|
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
|
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
|
|
|
gen.OffsetTime(9) // Lower the block difficulty to simulate a weaker chain
|
|
}
|
|
})
|
|
// Import the chain. This runs all block validation rules.
|
|
db := rawdb.NewMemoryDatabase()
|
|
blockchain, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
// overwrite the old chain
|
|
_, chain, _ = GenerateChainWithGenesis(gspec, ethash.NewFaker(), 5, func(i int, gen *BlockGen) {
|
|
switch i {
|
|
case 0:
|
|
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
|
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
|
|
|
case 2:
|
|
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
|
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
|
|
|
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
|
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
|
|
|
case 3:
|
|
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key3)
|
|
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
|
}
|
|
})
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
|
|
// removed tx
|
|
for i, tx := range (types.Transactions{pastDrop, freshDrop}) {
|
|
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn != nil {
|
|
t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn)
|
|
}
|
|
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt != nil {
|
|
t.Errorf("drop %d: receipt %v found while shouldn't have been", i, rcpt)
|
|
}
|
|
}
|
|
// added tx
|
|
for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) {
|
|
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
|
t.Errorf("add %d: expected tx to be found", i)
|
|
}
|
|
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
|
t.Errorf("add %d: expected receipt to be found", i)
|
|
}
|
|
}
|
|
// shared tx
|
|
for i, tx := range (types.Transactions{postponed, swapped}) {
|
|
if txn, _, _, _ := rawdb.ReadTransaction(db, tx.Hash()); txn == nil {
|
|
t.Errorf("share %d: expected tx to be found", i)
|
|
}
|
|
if rcpt, _, _, _ := rawdb.ReadReceipt(db, tx.Hash(), blockchain.Config()); rcpt == nil {
|
|
t.Errorf("share %d: expected receipt to be found", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLogReorgs(t *testing.T) {
|
|
testLogReorgs(t, rawdb.HashScheme)
|
|
testLogReorgs(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLogReorgs(t *testing.T, scheme string) {
|
|
var (
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
|
|
// this code generates a log
|
|
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
)
|
|
|
|
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer blockchain.Stop()
|
|
|
|
rmLogsCh := make(chan RemovedLogsEvent)
|
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
|
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *BlockGen) {
|
|
if i == 1 {
|
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, code), signer, key1)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
}
|
|
gen.AddTx(tx)
|
|
}
|
|
})
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert chain: %v", err)
|
|
}
|
|
|
|
_, chain, _ = GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, gen *BlockGen) {})
|
|
done := make(chan struct{})
|
|
go func() {
|
|
ev := <-rmLogsCh
|
|
if len(ev.Logs) == 0 {
|
|
t.Error("expected logs")
|
|
}
|
|
close(done)
|
|
}()
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
timeout := time.NewTimer(1 * time.Second)
|
|
defer timeout.Stop()
|
|
select {
|
|
case <-done:
|
|
case <-timeout.C:
|
|
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
|
|
}
|
|
}
|
|
|
|
// This EVM code generates a log when the contract is created.
|
|
var logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
|
|
// This test checks that log events and RemovedLogsEvent are sent
|
|
// when the chain reorganizes.
|
|
func TestLogRebirth(t *testing.T) {
|
|
testLogRebirth(t, rawdb.HashScheme)
|
|
testLogRebirth(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLogRebirth(t *testing.T, scheme string) {
|
|
var (
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
engine = ethash.NewFaker()
|
|
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
)
|
|
defer blockchain.Stop()
|
|
|
|
// The event channels.
|
|
newLogCh := make(chan []*types.Log, 10)
|
|
rmLogsCh := make(chan RemovedLogsEvent, 10)
|
|
blockchain.SubscribeLogsEvent(newLogCh)
|
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
|
|
|
// This chain contains 10 logs.
|
|
genDb, chain, _ := GenerateChainWithGenesis(gspec, engine, 3, func(i int, gen *BlockGen) {
|
|
if i < 2 {
|
|
for ii := 0; ii < 5; ii++ {
|
|
tx, err := types.SignNewTx(key1, signer, &types.LegacyTx{
|
|
Nonce: gen.TxNonce(addr1),
|
|
GasPrice: gen.header.BaseFee,
|
|
Gas: uint64(1000001),
|
|
Data: logCode,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
}
|
|
gen.AddTx(tx)
|
|
}
|
|
}
|
|
})
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 10, 0)
|
|
|
|
// Generate long reorg chain containing more logs. Inserting the
|
|
// chain removes one log and adds four.
|
|
_, forkChain, _ := GenerateChainWithGenesis(gspec, engine, 3, func(i int, gen *BlockGen) {
|
|
if i == 2 {
|
|
// The last (head) block is not part of the reorg-chain, we can ignore it
|
|
return
|
|
}
|
|
for ii := 0; ii < 5; ii++ {
|
|
tx, err := types.SignNewTx(key1, signer, &types.LegacyTx{
|
|
Nonce: gen.TxNonce(addr1),
|
|
GasPrice: gen.header.BaseFee,
|
|
Gas: uint64(1000000),
|
|
Data: logCode,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
}
|
|
gen.AddTx(tx)
|
|
}
|
|
gen.OffsetTime(-9) // higher block difficulty
|
|
})
|
|
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 10, 10)
|
|
|
|
// This chain segment is rooted in the original chain, but doesn't contain any logs.
|
|
// When inserting it, the canonical chain switches away from forkChain and re-emits
|
|
// the log event for the old chain, as well as a RemovedLogsEvent for forkChain.
|
|
newBlocks, _ := GenerateChain(gspec.Config, chain[len(chain)-1], engine, genDb, 1, func(i int, gen *BlockGen) {})
|
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 10, 10)
|
|
}
|
|
|
|
// This test is a variation of TestLogRebirth. It verifies that log events are emitted
|
|
// when a side chain containing log events overtakes the canonical chain.
|
|
func TestSideLogRebirth(t *testing.T) {
|
|
testSideLogRebirth(t, rawdb.HashScheme)
|
|
testSideLogRebirth(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testSideLogRebirth(t *testing.T, scheme string) {
|
|
var (
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
)
|
|
defer blockchain.Stop()
|
|
|
|
newLogCh := make(chan []*types.Log, 10)
|
|
rmLogsCh := make(chan RemovedLogsEvent, 10)
|
|
blockchain.SubscribeLogsEvent(newLogCh)
|
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
|
|
|
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *BlockGen) {
|
|
if i == 1 {
|
|
gen.OffsetTime(-9) // higher block difficulty
|
|
}
|
|
})
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
|
|
|
// Generate side chain with lower difficulty, after the merge, the chain will be accepted even if it is lower difficulty
|
|
genDb, sideChain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 2, func(i int, gen *BlockGen) {
|
|
if i == 1 {
|
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, logCode), signer, key1)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
}
|
|
gen.AddTx(tx)
|
|
}
|
|
})
|
|
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
|
|
|
// Generate a new block based on side chain. Should not emit any events anymore.
|
|
newBlocks, _ := GenerateChain(gspec.Config, sideChain[len(sideChain)-1], ethash.NewFaker(), genDb, 1, func(i int, gen *BlockGen) {})
|
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
}
|
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
|
}
|
|
|
|
func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) {
|
|
t.Helper()
|
|
var (
|
|
countNew int
|
|
countRm int
|
|
prev int
|
|
)
|
|
// Drain events.
|
|
for len(logsCh) > 0 {
|
|
x := <-logsCh
|
|
countNew += len(x)
|
|
for _, log := range x {
|
|
// We expect added logs to be in ascending order: 0:0, 0:1, 1:0 ...
|
|
have := 100*int(log.BlockNumber) + int(log.TxIndex)
|
|
if have < prev {
|
|
t.Fatalf("Expected new logs to arrive in ascending order (%d < %d)", have, prev)
|
|
}
|
|
prev = have
|
|
}
|
|
}
|
|
prev = 0
|
|
for len(rmLogsCh) > 0 {
|
|
x := <-rmLogsCh
|
|
countRm += len(x.Logs)
|
|
for _, log := range x.Logs {
|
|
// We expect removed logs to be in ascending order: 0:0, 0:1, 1:0 ...
|
|
have := 100*int(log.BlockNumber) + int(log.TxIndex)
|
|
if have < prev {
|
|
t.Fatalf("Expected removed logs to arrive in ascending order (%d < %d)", have, prev)
|
|
}
|
|
prev = have
|
|
}
|
|
}
|
|
|
|
if countNew != wantNew {
|
|
t.Fatalf("wrong number of log events: got %d, want %d", countNew, wantNew)
|
|
}
|
|
if countRm != wantRemoved {
|
|
t.Fatalf("wrong number of removed log events: got %d, want %d", countRm, wantRemoved)
|
|
}
|
|
}
|
|
|
|
// Tests if the canonical block can be fetched from the database during chain insertion.
|
|
func TestCanonicalBlockRetrieval(t *testing.T) {
|
|
testCanonicalBlockRetrieval(t, rawdb.HashScheme)
|
|
testCanonicalBlockRetrieval(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testCanonicalBlockRetrieval(t *testing.T, scheme string) {
|
|
_, gspec, blockchain, err := newCanonical(ethash.NewFaker(), 0, true, scheme)
|
|
if err != nil {
|
|
t.Fatalf("failed to create pristine chain: %v", err)
|
|
}
|
|
defer blockchain.Stop()
|
|
|
|
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 10, func(i int, gen *BlockGen) {})
|
|
|
|
var pend sync.WaitGroup
|
|
pend.Add(len(chain))
|
|
|
|
for i := range chain {
|
|
go func(block *types.Block) {
|
|
defer pend.Done()
|
|
|
|
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
|
for {
|
|
ch := rawdb.ReadCanonicalHash(blockchain.db, block.NumberU64())
|
|
if ch == (common.Hash{}) {
|
|
continue // busy wait for canonical hash to be written
|
|
}
|
|
if ch != block.Hash() {
|
|
t.Errorf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
|
return
|
|
}
|
|
fb := rawdb.ReadBlock(blockchain.db, ch, block.NumberU64())
|
|
if fb == nil {
|
|
t.Errorf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
|
return
|
|
}
|
|
if fb.Hash() != block.Hash() {
|
|
t.Errorf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
|
|
return
|
|
}
|
|
return
|
|
}
|
|
}(chain[i])
|
|
|
|
if _, err := blockchain.InsertChain(types.Blocks{chain[i]}); err != nil {
|
|
t.Fatalf("failed to insert block %d: %v", i, err)
|
|
}
|
|
}
|
|
pend.Wait()
|
|
}
|
|
func TestEIP155Transition(t *testing.T) {
|
|
testEIP155Transition(t, rawdb.HashScheme)
|
|
testEIP155Transition(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEIP155Transition(t *testing.T, scheme string) {
|
|
// Configure and generate a sample block chain
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000)
|
|
deleteAddr = common.Address{1}
|
|
gspec = &Genesis{
|
|
Config: ¶ms.ChainConfig{
|
|
ChainID: big.NewInt(1),
|
|
EIP150Block: big.NewInt(0),
|
|
EIP155Block: big.NewInt(2),
|
|
HomesteadBlock: new(big.Int),
|
|
},
|
|
Alloc: types.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
|
}
|
|
)
|
|
genDb, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, func(i int, block *BlockGen) {
|
|
var (
|
|
tx *types.Transaction
|
|
err error
|
|
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
|
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
|
}
|
|
)
|
|
switch i {
|
|
case 0:
|
|
tx, err = basicTx(types.HomesteadSigner{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
case 2:
|
|
tx, err = basicTx(types.HomesteadSigner{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
|
|
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
case 3:
|
|
tx, err = basicTx(types.HomesteadSigner{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
|
|
tx, err = basicTx(types.LatestSigner(gspec.Config))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
}
|
|
})
|
|
|
|
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer blockchain.Stop()
|
|
|
|
if _, err := blockchain.InsertChain(blocks); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block := blockchain.GetBlockByNumber(1)
|
|
if block.Transactions()[0].Protected() {
|
|
t.Error("Expected block[0].txs[0] to not be replay protected")
|
|
}
|
|
|
|
block = blockchain.GetBlockByNumber(3)
|
|
if block.Transactions()[0].Protected() {
|
|
t.Error("Expected block[3].txs[0] to not be replay protected")
|
|
}
|
|
if !block.Transactions()[1].Protected() {
|
|
t.Error("Expected block[3].txs[1] to be replay protected")
|
|
}
|
|
if _, err := blockchain.InsertChain(blocks[4:]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// generate an invalid chain id transaction
|
|
config := ¶ms.ChainConfig{
|
|
ChainID: big.NewInt(2),
|
|
EIP150Block: big.NewInt(0),
|
|
EIP155Block: big.NewInt(2),
|
|
HomesteadBlock: new(big.Int),
|
|
}
|
|
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], ethash.NewFaker(), genDb, 4, func(i int, block *BlockGen) {
|
|
var (
|
|
tx *types.Transaction
|
|
err error
|
|
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
|
return types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
|
}
|
|
)
|
|
if i == 0 {
|
|
tx, err = basicTx(types.LatestSigner(config))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
}
|
|
})
|
|
_, err := blockchain.InsertChain(blocks)
|
|
if have, want := err, types.ErrInvalidChainId; !errors.Is(have, want) {
|
|
t.Errorf("have %v, want %v", have, want)
|
|
}
|
|
}
|
|
func TestEIP161AccountRemoval(t *testing.T) {
|
|
testEIP161AccountRemoval(t, rawdb.HashScheme)
|
|
testEIP161AccountRemoval(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEIP161AccountRemoval(t *testing.T, scheme string) {
|
|
// Configure and generate a sample block chain
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000)
|
|
theAddr = common.Address{1}
|
|
gspec = &Genesis{
|
|
Config: ¶ms.ChainConfig{
|
|
ChainID: big.NewInt(1),
|
|
HomesteadBlock: new(big.Int),
|
|
EIP155Block: new(big.Int),
|
|
EIP150Block: new(big.Int),
|
|
EIP158Block: big.NewInt(2),
|
|
},
|
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
|
}
|
|
)
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, block *BlockGen) {
|
|
var (
|
|
tx *types.Transaction
|
|
err error
|
|
signer = types.LatestSigner(gspec.Config)
|
|
)
|
|
switch i {
|
|
case 0:
|
|
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
|
case 1:
|
|
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
|
case 2:
|
|
tx, err = types.SignTx(types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), 21000, new(big.Int), nil), signer, key)
|
|
}
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
block.AddTx(tx)
|
|
})
|
|
// account must exist pre eip 161
|
|
blockchain, _ := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer blockchain.Stop()
|
|
|
|
if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st, _ := blockchain.State(); !st.Exist(theAddr) {
|
|
t.Error("expected account to exist")
|
|
}
|
|
|
|
// account needs to be deleted post eip 161
|
|
if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st, _ := blockchain.State(); st.Exist(theAddr) {
|
|
t.Error("account should not exist")
|
|
}
|
|
|
|
// account mustn't be created post eip 161
|
|
if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if st, _ := blockchain.State(); st.Exist(theAddr) {
|
|
t.Error("account should not exist")
|
|
}
|
|
}
|
|
|
|
// This is a regression test (i.e. as weird as it is, don't delete it ever), which
|
|
// tests that under weird reorg conditions the blockchain and its internal header-
|
|
// chain return the same latest block/header.
|
|
//
|
|
// https://github.com/ethereum/go-ethereum/pull/15941
|
|
func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
|
testBlockchainHeaderchainReorgConsistency(t, rawdb.HashScheme)
|
|
testBlockchainHeaderchainReorgConsistency(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testBlockchainHeaderchainReorgConsistency(t *testing.T, scheme string) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
|
|
|
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
|
forks := make([]*types.Block, len(blocks))
|
|
for i := 0; i < len(forks); i++ {
|
|
parent := genesis.ToBlock()
|
|
if i > 0 {
|
|
parent = blocks[i-1]
|
|
}
|
|
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
|
forks[i] = fork[0]
|
|
}
|
|
// Import the canonical and fork chain side by side, verifying the current block
|
|
// and current header consistency
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
for i := 0; i < len(blocks); i++ {
|
|
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
|
}
|
|
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
|
t.Errorf("block %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number, chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
|
}
|
|
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
|
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
|
|
}
|
|
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
|
|
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x..], header #%d [%x..]", i, chain.CurrentBlock().Number, chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
|
|
}
|
|
}
|
|
}
|
|
|
|
// Tests that importing small side forks doesn't leave junk in the trie database
|
|
// cache (which would eventually cause memory issues).
|
|
func TestTrieForkGC(t *testing.T) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
|
|
|
// Generate a bunch of fork blocks, each side forking from the canonical chain
|
|
forks := make([]*types.Block, len(blocks))
|
|
for i := 0; i < len(forks); i++ {
|
|
parent := genesis.ToBlock()
|
|
if i > 0 {
|
|
parent = blocks[i-1]
|
|
}
|
|
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
|
forks[i] = fork[0]
|
|
}
|
|
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
for i := 0; i < len(blocks); i++ {
|
|
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
|
}
|
|
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
|
|
t.Fatalf("fork %d: failed to insert into chain: %v", i, err)
|
|
}
|
|
}
|
|
// Dereference all the recent tries and ensure no past trie is left in
|
|
for i := 0; i < state.TriesInMemory; i++ {
|
|
chain.TrieDB().Dereference(blocks[len(blocks)-1-i].Root())
|
|
chain.TrieDB().Dereference(forks[len(blocks)-1-i].Root())
|
|
}
|
|
if _, nodes, _ := chain.TrieDB().Size(); nodes > 0 { // all memory is returned in the nodes return for hashdb
|
|
t.Fatalf("stale tries still alive after garbase collection")
|
|
}
|
|
}
|
|
|
|
// Tests that doing large reorgs works even if the state associated with the
|
|
// forking point is not available any more.
|
|
func TestLargeReorgTrieGC(t *testing.T) {
|
|
testLargeReorgTrieGC(t, rawdb.HashScheme)
|
|
testLargeReorgTrieGC(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
|
// Generate the original common chain segment and the two competing forks
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
genDb, shared, _ := GenerateChainWithGenesis(genesis, engine, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
|
original, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
|
|
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*state.TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
|
|
|
// Import the shared chain and the original canonical one
|
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
defer db.Close()
|
|
|
|
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if _, err := chain.InsertChain(shared); err != nil {
|
|
t.Fatalf("failed to insert shared chain: %v", err)
|
|
}
|
|
if _, err := chain.InsertChain(original); err != nil {
|
|
t.Fatalf("failed to insert original chain: %v", err)
|
|
}
|
|
// Ensure that the state associated with the forking point is pruned away
|
|
if chain.HasState(shared[len(shared)-1].Root()) {
|
|
t.Fatalf("common-but-old ancestor still cache")
|
|
}
|
|
// Import the competitor chain without exceeding the canonical's TD.
|
|
// Post-merge the side chain should be executed
|
|
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
|
|
t.Fatalf("failed to insert competitor chain: %v", err)
|
|
}
|
|
if !chain.HasState(competitor[len(competitor)-3].Root()) {
|
|
t.Fatalf("failed to insert low-TD chain")
|
|
}
|
|
// Import the head of the competitor chain.
|
|
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
|
|
t.Fatalf("failed to finalize competitor chain: %v", err)
|
|
}
|
|
// In path-based trie database implementation, it will keep 128 diff + 1 disk
|
|
// layers, totally 129 latest states available. In hash-based it's 128.
|
|
states := state.TriesInMemory
|
|
if scheme == rawdb.PathScheme {
|
|
states = states + 1
|
|
}
|
|
for i, block := range competitor[:len(competitor)-states] {
|
|
if chain.HasState(block.Root()) {
|
|
t.Fatalf("competitor %d: unexpected competing chain state", i)
|
|
}
|
|
}
|
|
for i, block := range competitor[len(competitor)-states:] {
|
|
if !chain.HasState(block.Root()) {
|
|
t.Fatalf("competitor %d: competing chain state missing", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBlockchainRecovery(t *testing.T) {
|
|
testBlockchainRecovery(t, rawdb.HashScheme)
|
|
testBlockchainRecovery(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testBlockchainRecovery(t *testing.T, scheme string) {
|
|
// Configure and generate a sample block chain
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000)
|
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: funds}}}
|
|
)
|
|
height := uint64(64)
|
|
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
|
|
|
// Import the chain as a ancient-first node and ensure all pointers are updated
|
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
defer ancientDb.Close()
|
|
ancient, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
|
|
headers := make([]*types.Header, len(blocks))
|
|
for i, block := range blocks {
|
|
headers[i] = block.Header()
|
|
}
|
|
if n, err := ancient.InsertHeaderChain(headers); err != nil {
|
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
|
}
|
|
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
|
|
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
|
}
|
|
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
|
|
ancient.Stop()
|
|
|
|
// Destroy head fast block manually
|
|
midBlock := blocks[len(blocks)/2]
|
|
rawdb.WriteHeadFastBlockHash(ancientDb, midBlock.Hash())
|
|
|
|
// Reopen broken blockchain again
|
|
ancient, _ = NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer ancient.Stop()
|
|
if num := ancient.CurrentBlock().Number.Uint64(); num != 0 {
|
|
t.Errorf("head block mismatch: have #%v, want #%v", num, 0)
|
|
}
|
|
if num := ancient.CurrentSnapBlock().Number.Uint64(); num != midBlock.NumberU64() {
|
|
t.Errorf("head snap-block mismatch: have #%v, want #%v", num, midBlock.NumberU64())
|
|
}
|
|
if num := ancient.CurrentHeader().Number.Uint64(); num != midBlock.NumberU64() {
|
|
t.Errorf("head header mismatch: have #%v, want #%v", num, midBlock.NumberU64())
|
|
}
|
|
}
|
|
|
|
// This test checks that InsertReceiptChain will roll back correctly when attempting to insert a side chain.
|
|
func TestInsertReceiptChainRollback(t *testing.T) {
|
|
testInsertReceiptChainRollback(t, rawdb.HashScheme)
|
|
testInsertReceiptChainRollback(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
|
// Generate forked chain. The returned BlockChain object is used to process the side chain blocks.
|
|
tmpChain, sideblocks, canonblocks, gspec, err := getLongAndShortChains(scheme)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer tmpChain.Stop()
|
|
// Get the side chain receipts.
|
|
if _, err := tmpChain.InsertChain(sideblocks); err != nil {
|
|
t.Fatal("processing side chain failed:", err)
|
|
}
|
|
t.Log("sidechain head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash())
|
|
sidechainReceipts := make([]types.Receipts, len(sideblocks))
|
|
for i, block := range sideblocks {
|
|
sidechainReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
|
|
}
|
|
// Get the canon chain receipts.
|
|
if _, err := tmpChain.InsertChain(canonblocks); err != nil {
|
|
t.Fatal("processing canon chain failed:", err)
|
|
}
|
|
t.Log("canon head:", tmpChain.CurrentBlock().Number, tmpChain.CurrentBlock().Hash())
|
|
canonReceipts := make([]types.Receipts, len(canonblocks))
|
|
for i, block := range canonblocks {
|
|
canonReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
|
|
}
|
|
|
|
// Set up a BlockChain that uses the ancient store.
|
|
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
defer ancientDb.Close()
|
|
|
|
ancientChain, _ := NewBlockChain(ancientDb, DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil)
|
|
defer ancientChain.Stop()
|
|
|
|
// Import the canonical header chain.
|
|
canonHeaders := make([]*types.Header, len(canonblocks))
|
|
for i, block := range canonblocks {
|
|
canonHeaders[i] = block.Header()
|
|
}
|
|
if _, err = ancientChain.InsertHeaderChain(canonHeaders); err != nil {
|
|
t.Fatal("can't import canon headers:", err)
|
|
}
|
|
|
|
// Try to insert blocks/receipts of the side chain.
|
|
_, err = ancientChain.InsertReceiptChain(sideblocks, sidechainReceipts, uint64(len(sideblocks)))
|
|
if err == nil {
|
|
t.Fatal("expected error from InsertReceiptChain.")
|
|
}
|
|
if ancientChain.CurrentSnapBlock().Number.Uint64() != 0 {
|
|
t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentSnapBlock().Number)
|
|
}
|
|
if frozen, err := ancientChain.db.Ancients(); err != nil || frozen != 1 {
|
|
t.Fatalf("failed to truncate ancient data, frozen index is %d", frozen)
|
|
}
|
|
|
|
// Insert blocks/receipts of the canonical chain.
|
|
_, err = ancientChain.InsertReceiptChain(canonblocks, canonReceipts, uint64(len(canonblocks)))
|
|
if err != nil {
|
|
t.Fatalf("can't import canon chain receipts: %v", err)
|
|
}
|
|
if ancientChain.CurrentSnapBlock().Number.Uint64() != canonblocks[len(canonblocks)-1].NumberU64() {
|
|
t.Fatalf("failed to insert ancient recept chain after rollback")
|
|
}
|
|
if frozen, _ := ancientChain.db.Ancients(); frozen != uint64(len(canonblocks))+1 {
|
|
t.Fatalf("wrong ancients count %d", frozen)
|
|
}
|
|
}
|
|
|
|
// Tests that importing a very large side fork, which is larger than the canon chain,
|
|
// but where the difficulty per block is kept low: this means that it will not
|
|
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
|
//
|
|
// Details at:
|
|
// - https://github.com/ethereum/go-ethereum/issues/18977
|
|
// - https://github.com/ethereum/go-ethereum/pull/18988
|
|
func TestLowDiffLongChain(t *testing.T) {
|
|
testLowDiffLongChain(t, rawdb.HashScheme)
|
|
testLowDiffLongChain(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testLowDiffLongChain(t *testing.T, scheme string) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
|
// until after at least 128 blocks post tip
|
|
genDb, blocks, _ := GenerateChainWithGenesis(genesis, engine, 6*state.TriesInMemory, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
b.OffsetTime(-9)
|
|
})
|
|
|
|
// Import the canonical chain
|
|
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
defer diskdb.Close()
|
|
|
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
// Generate fork chain, starting from an early block
|
|
parent := blocks[10]
|
|
fork, _ := GenerateChain(genesis.Config, parent, engine, genDb, 8*state.TriesInMemory, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{2})
|
|
})
|
|
|
|
// And now import the fork
|
|
if i, err := chain.InsertChain(fork); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
|
}
|
|
head := chain.CurrentBlock()
|
|
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
|
|
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
|
}
|
|
// Sanity check that all the canonical numbers are present
|
|
header := chain.CurrentHeader()
|
|
for number := head.Number.Uint64(); number > 0; number-- {
|
|
if hash := chain.GetHeaderByNumber(number).Hash(); hash != header.Hash() {
|
|
t.Fatalf("header %d: canonical hash mismatch: have %x, want %x", number, hash, header.Hash())
|
|
}
|
|
header = chain.GetHeader(header.ParentHash, number-1)
|
|
}
|
|
}
|
|
|
|
// Tests that importing a sidechain (S), where
|
|
// - S is sidechain, containing blocks [Sn...Sm]
|
|
// - C is canon chain, containing blocks [G..Cn..Cm]
|
|
// - A common ancestor is placed at prune-point + blocksBetweenCommonAncestorAndPruneblock
|
|
// - The sidechain S is prepended with numCanonBlocksInSidechain blocks from the canon chain
|
|
//
|
|
// The mergePoint can be these values:
|
|
// -1: the transition won't happen
|
|
// 0: the transition happens since genesis
|
|
// 1: the transition happens after some chain segments
|
|
func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommonAncestorAndPruneblock int, mergePoint int) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
chainConfig := *params.TestChainConfig
|
|
var (
|
|
engine = beacon.New(ethash.NewFaker())
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr = crypto.PubkeyToAddress(key.PublicKey)
|
|
nonce = uint64(0)
|
|
|
|
gspec = &Genesis{
|
|
Config: &chainConfig,
|
|
Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(gomath.MaxInt64)}},
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
mergeBlock = gomath.MaxInt32
|
|
)
|
|
// Generate and import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
// Activate the transition since genesis if required
|
|
if mergePoint == 0 {
|
|
mergeBlock = 0
|
|
|
|
// Set the terminal total difficulty in the config
|
|
gspec.Config.TerminalTotalDifficulty = big.NewInt(0)
|
|
}
|
|
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2*state.TriesInMemory, func(i int, gen *BlockGen) {
|
|
tx, err := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("deadbeef"), big.NewInt(100), 21000, big.NewInt(int64(i+1)*params.GWei), nil), signer, key)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
}
|
|
gen.AddTx(tx)
|
|
if int(gen.header.Number.Uint64()) >= mergeBlock {
|
|
gen.SetPoS()
|
|
}
|
|
nonce++
|
|
})
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
lastPrunedIndex := len(blocks) - state.TriesInMemory - 1
|
|
lastPrunedBlock := blocks[lastPrunedIndex]
|
|
firstNonPrunedBlock := blocks[len(blocks)-state.TriesInMemory]
|
|
|
|
// Verify pruning of lastPrunedBlock
|
|
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
|
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
|
}
|
|
// Verify firstNonPrunedBlock is not pruned
|
|
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
|
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
|
}
|
|
|
|
// Activate the transition in the middle of the chain
|
|
if mergePoint == 1 {
|
|
// Set the terminal total difficulty in the config
|
|
ttd := big.NewInt(int64(len(blocks)))
|
|
ttd.Mul(ttd, params.GenesisDifficulty)
|
|
gspec.Config.TerminalTotalDifficulty = ttd
|
|
mergeBlock = len(blocks)
|
|
}
|
|
|
|
// Generate the sidechain
|
|
// First block should be a known block, block after should be a pruned block. So
|
|
// canon(pruned), side, side...
|
|
|
|
// Generate fork chain, make it longer than canon
|
|
parentIndex := lastPrunedIndex + blocksBetweenCommonAncestorAndPruneblock
|
|
parent := blocks[parentIndex]
|
|
fork, _ := GenerateChain(gspec.Config, parent, engine, genDb, 2*state.TriesInMemory, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{2})
|
|
if int(b.header.Number.Uint64()) >= mergeBlock {
|
|
b.SetPoS()
|
|
}
|
|
})
|
|
// Prepend the parent(s)
|
|
var sidechain []*types.Block
|
|
for i := numCanonBlocksInSidechain; i > 0; i-- {
|
|
sidechain = append(sidechain, blocks[parentIndex+1-i])
|
|
}
|
|
sidechain = append(sidechain, fork...)
|
|
n, err := chain.InsertChain(sidechain)
|
|
if err != nil {
|
|
t.Errorf("Got error, %v number %d - %d", err, sidechain[n].NumberU64(), n)
|
|
}
|
|
head := chain.CurrentBlock()
|
|
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
|
|
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
|
}
|
|
}
|
|
|
|
// Tests that importing a sidechain (S), where
|
|
// - S is sidechain, containing blocks [Sn...Sm]
|
|
// - C is canon chain, containing blocks [G..Cn..Cm]
|
|
// - The common ancestor Cc is pruned
|
|
// - The first block in S: Sn, is == Cn
|
|
//
|
|
// That is: the sidechain for import contains some blocks already present in canon chain.
|
|
// So the blocks are:
|
|
//
|
|
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
|
// ^ ^ ^ pruned
|
|
func TestPrunedImportSide(t *testing.T) {
|
|
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
|
// glogger.Verbosity(3)
|
|
// log.SetDefault(log.NewLogger(glogger))
|
|
testSideImport(t, 3, 3, -1)
|
|
testSideImport(t, 3, -3, -1)
|
|
testSideImport(t, 10, 0, -1)
|
|
testSideImport(t, 1, 10, -1)
|
|
testSideImport(t, 1, -10, -1)
|
|
}
|
|
|
|
func TestPrunedImportSideWithMerging(t *testing.T) {
|
|
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
|
// glogger.Verbosity(3)
|
|
// log.SetDefault(log.NewLogger(glogger))
|
|
testSideImport(t, 3, 3, 0)
|
|
testSideImport(t, 3, -3, 0)
|
|
testSideImport(t, 10, 0, 0)
|
|
testSideImport(t, 1, 10, 0)
|
|
testSideImport(t, 1, -10, 0)
|
|
|
|
testSideImport(t, 3, 3, 1)
|
|
testSideImport(t, 3, -3, 1)
|
|
testSideImport(t, 10, 0, 1)
|
|
testSideImport(t, 1, 10, 1)
|
|
testSideImport(t, 1, -10, 1)
|
|
}
|
|
|
|
func TestInsertKnownHeaders(t *testing.T) {
|
|
testInsertKnownChainData(t, "headers", rawdb.HashScheme)
|
|
testInsertKnownChainData(t, "headers", rawdb.PathScheme)
|
|
}
|
|
func TestInsertKnownReceiptChain(t *testing.T) {
|
|
testInsertKnownChainData(t, "receipts", rawdb.HashScheme)
|
|
testInsertKnownChainData(t, "receipts", rawdb.PathScheme)
|
|
}
|
|
func TestInsertKnownBlocks(t *testing.T) {
|
|
testInsertKnownChainData(t, "blocks", rawdb.HashScheme)
|
|
testInsertKnownChainData(t, "blocks", rawdb.PathScheme)
|
|
}
|
|
|
|
func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
genDb, blocks, receipts := GenerateChainWithGenesis(genesis, engine, 32, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
|
|
|
// A longer chain but total difficulty is lower.
|
|
blocks2, receipts2 := GenerateChain(genesis.Config, blocks[len(blocks)-1], engine, genDb, 65, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
|
|
|
|
// A shorter chain but total difficulty is higher.
|
|
blocks3, receipts3 := GenerateChain(genesis.Config, blocks[len(blocks)-1], engine, genDb, 64, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
b.OffsetTime(-9) // A higher difficulty
|
|
})
|
|
// Import the shared chain and the original canonical one
|
|
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
defer chaindb.Close()
|
|
|
|
chain, err := NewBlockChain(chaindb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
var (
|
|
inserter func(blocks []*types.Block, receipts []types.Receipts) error
|
|
asserter func(t *testing.T, block *types.Block)
|
|
)
|
|
if typ == "headers" {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
headers := make([]*types.Header, 0, len(blocks))
|
|
for _, block := range blocks {
|
|
headers = append(headers, block.Header())
|
|
}
|
|
_, err := chain.InsertHeaderChain(headers)
|
|
return err
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentHeader().Hash() != block.Hash() {
|
|
t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
} else if typ == "receipts" {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
headers := make([]*types.Header, 0, len(blocks))
|
|
for _, block := range blocks {
|
|
headers = append(headers, block.Header())
|
|
}
|
|
_, err := chain.InsertHeaderChain(headers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
|
return err
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentSnapBlock().Hash() != block.Hash() {
|
|
t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentSnapBlock().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
} else {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
_, err := chain.InsertChain(blocks)
|
|
return err
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentBlock().Hash() != block.Hash() {
|
|
t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := inserter(blocks, receipts); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
|
|
// Reimport the chain data again. All the imported
|
|
// chain data are regarded "known" data.
|
|
if err := inserter(blocks, receipts); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks[len(blocks)-1])
|
|
|
|
// Import a long canonical chain with some known data as prefix.
|
|
rollback := blocks[len(blocks)/2].NumberU64()
|
|
|
|
chain.SetHead(rollback - 1)
|
|
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks2[len(blocks2)-1])
|
|
|
|
// Import a heavier shorter but higher total difficulty chain with some known data as prefix.
|
|
if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks3[len(blocks3)-1])
|
|
|
|
// Import a longer but lower total difficulty chain with some known data as prefix.
|
|
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
// Post-merge the chain should change even if td is lower.
|
|
asserter(t, blocks2[len(blocks2)-1])
|
|
|
|
// Rollback the heavier chain and re-insert the longer chain again.
|
|
chain.SetHead(rollback - 1)
|
|
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks2[len(blocks2)-1])
|
|
}
|
|
|
|
func TestInsertKnownHeadersWithMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "headers", 0)
|
|
}
|
|
func TestInsertKnownReceiptChainWithMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "receipts", 0)
|
|
}
|
|
func TestInsertKnownBlocksWithMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "blocks", 0)
|
|
}
|
|
func TestInsertKnownHeadersAfterMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "headers", 1)
|
|
}
|
|
func TestInsertKnownReceiptChainAfterMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "receipts", 1)
|
|
}
|
|
func TestInsertKnownBlocksAfterMerging(t *testing.T) {
|
|
testInsertKnownChainDataWithMerging(t, "blocks", 1)
|
|
}
|
|
|
|
// mergeHeight can be assigned in these values:
|
|
// 0: means the merging is applied since genesis
|
|
// 1: means the merging is applied after the first segment
|
|
func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight int) {
|
|
// Copy the TestChainConfig so we can modify it during tests
|
|
chainConfig := *params.TestChainConfig
|
|
var (
|
|
genesis = &Genesis{
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
Config: &chainConfig,
|
|
}
|
|
engine = beacon.New(ethash.NewFaker())
|
|
mergeBlock = uint64(gomath.MaxUint64)
|
|
)
|
|
// Apply merging since genesis
|
|
if mergeHeight == 0 {
|
|
genesis.Config.TerminalTotalDifficulty = big.NewInt(0)
|
|
mergeBlock = uint64(0)
|
|
}
|
|
|
|
genDb, blocks, receipts := GenerateChainWithGenesis(genesis, engine, 32,
|
|
func(i int, b *BlockGen) {
|
|
if b.header.Number.Uint64() >= mergeBlock {
|
|
b.SetPoS()
|
|
}
|
|
b.SetCoinbase(common.Address{1})
|
|
})
|
|
|
|
// Apply merging after the first segment
|
|
if mergeHeight == 1 {
|
|
// TTD is genesis diff + blocks
|
|
ttd := big.NewInt(1 + int64(len(blocks)))
|
|
ttd.Mul(ttd, params.GenesisDifficulty)
|
|
genesis.Config.TerminalTotalDifficulty = ttd
|
|
mergeBlock = uint64(len(blocks))
|
|
}
|
|
// Longer chain and shorter chain
|
|
blocks2, receipts2 := GenerateChain(genesis.Config, blocks[len(blocks)-1], engine, genDb, 65, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
if b.header.Number.Uint64() >= mergeBlock {
|
|
b.SetPoS()
|
|
}
|
|
})
|
|
blocks3, receipts3 := GenerateChain(genesis.Config, blocks[len(blocks)-1], engine, genDb, 64, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
b.OffsetTime(-9) // Time shifted, difficulty shouldn't be changed
|
|
if b.header.Number.Uint64() >= mergeBlock {
|
|
b.SetPoS()
|
|
}
|
|
})
|
|
// Import the shared chain and the original canonical one
|
|
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
if err != nil {
|
|
t.Fatalf("failed to create temp freezer db: %v", err)
|
|
}
|
|
defer chaindb.Close()
|
|
|
|
chain, err := NewBlockChain(chaindb, nil, genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
var (
|
|
inserter func(blocks []*types.Block, receipts []types.Receipts) error
|
|
asserter func(t *testing.T, block *types.Block)
|
|
)
|
|
if typ == "headers" {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
headers := make([]*types.Header, 0, len(blocks))
|
|
for _, block := range blocks {
|
|
headers = append(headers, block.Header())
|
|
}
|
|
i, err := chain.InsertHeaderChain(headers)
|
|
if err != nil {
|
|
return fmt.Errorf("index %d, number %d: %w", i, headers[i].Number, err)
|
|
}
|
|
return err
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentHeader().Hash() != block.Hash() {
|
|
t.Fatalf("current head header mismatch, have %v, want %v", chain.CurrentHeader().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
} else if typ == "receipts" {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
headers := make([]*types.Header, 0, len(blocks))
|
|
for _, block := range blocks {
|
|
headers = append(headers, block.Header())
|
|
}
|
|
i, err := chain.InsertHeaderChain(headers)
|
|
if err != nil {
|
|
return fmt.Errorf("index %d: %w", i, err)
|
|
}
|
|
_, err = chain.InsertReceiptChain(blocks, receipts, 0)
|
|
return err
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentSnapBlock().Hash() != block.Hash() {
|
|
t.Fatalf("current head fast block mismatch, have %v, want %v", chain.CurrentSnapBlock().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
} else {
|
|
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
|
|
i, err := chain.InsertChain(blocks)
|
|
if err != nil {
|
|
return fmt.Errorf("index %d: %w", i, err)
|
|
}
|
|
return nil
|
|
}
|
|
asserter = func(t *testing.T, block *types.Block) {
|
|
if chain.CurrentBlock().Hash() != block.Hash() {
|
|
t.Fatalf("current head block mismatch, have %v, want %v", chain.CurrentBlock().Hash().Hex(), block.Hash().Hex())
|
|
}
|
|
}
|
|
}
|
|
if err := inserter(blocks, receipts); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
|
|
// Reimport the chain data again. All the imported
|
|
// chain data are regarded "known" data.
|
|
if err := inserter(blocks, receipts); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks[len(blocks)-1])
|
|
|
|
// Import a long canonical chain with some known data as prefix.
|
|
rollback := blocks[len(blocks)/2].NumberU64()
|
|
chain.SetHead(rollback - 1)
|
|
if err := inserter(blocks, receipts); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks[len(blocks)-1])
|
|
|
|
// Import a longer chain with some known data as prefix.
|
|
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks2[len(blocks2)-1])
|
|
|
|
// Import a shorter chain with some known data as prefix.
|
|
// The reorg is expected since the fork choice rule is
|
|
// already changed.
|
|
if err := inserter(append(blocks, blocks3...), append(receipts, receipts3...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
// The head shouldn't change.
|
|
asserter(t, blocks3[len(blocks3)-1])
|
|
|
|
// Reimport the longer chain again, the reorg is still expected
|
|
chain.SetHead(rollback - 1)
|
|
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
|
|
t.Fatalf("failed to insert chain data: %v", err)
|
|
}
|
|
asserter(t, blocks2[len(blocks2)-1])
|
|
}
|
|
|
|
// getLongAndShortChains returns two chains: A is longer, B is heavier.
|
|
func getLongAndShortChains(scheme string) (*BlockChain, []*types.Block, []*types.Block, *Genesis, error) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
// Generate and import the canonical chain,
|
|
// Offset the time, to keep the difficulty low
|
|
genDb, longChain, _ := GenerateChainWithGenesis(genesis, engine, 80, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
})
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
return nil, nil, nil, nil, fmt.Errorf("failed to create tester chain: %v", err)
|
|
}
|
|
// Generate fork chain, make it shorter than canon, with common ancestor pretty early
|
|
parentIndex := 3
|
|
parent := longChain[parentIndex]
|
|
heavyChainExt, _ := GenerateChain(genesis.Config, parent, engine, genDb, 75, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{2})
|
|
b.OffsetTime(-9)
|
|
})
|
|
var heavyChain []*types.Block
|
|
heavyChain = append(heavyChain, longChain[:parentIndex+1]...)
|
|
heavyChain = append(heavyChain, heavyChainExt...)
|
|
|
|
// Verify that the test is sane
|
|
var (
|
|
longerTd = new(big.Int)
|
|
shorterTd = new(big.Int)
|
|
)
|
|
for index, b := range longChain {
|
|
longerTd.Add(longerTd, b.Difficulty())
|
|
if index <= parentIndex {
|
|
shorterTd.Add(shorterTd, b.Difficulty())
|
|
}
|
|
}
|
|
for _, b := range heavyChain {
|
|
shorterTd.Add(shorterTd, b.Difficulty())
|
|
}
|
|
if shorterTd.Cmp(longerTd) <= 0 {
|
|
return nil, nil, nil, nil, fmt.Errorf("test is moot, heavyChain td (%v) must be larger than canon td (%v)", shorterTd, longerTd)
|
|
}
|
|
longerNum := longChain[len(longChain)-1].NumberU64()
|
|
shorterNum := heavyChain[len(heavyChain)-1].NumberU64()
|
|
if shorterNum >= longerNum {
|
|
return nil, nil, nil, nil, fmt.Errorf("test is moot, heavyChain num (%v) must be lower than canon num (%v)", shorterNum, longerNum)
|
|
}
|
|
return chain, longChain, heavyChain, genesis, nil
|
|
}
|
|
|
|
// TestReorgToShorterRemovesCanonMapping tests that if we
|
|
// 1. Have a chain [0 ... N .. X]
|
|
// 2. Reorg to shorter but heavier chain [0 ... N ... Y]
|
|
// 3. Then there should be no canon mapping for the block at height X
|
|
// 4. The forked block should still be retrievable by hash
|
|
func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
|
|
testReorgToShorterRemovesCanonMapping(t, rawdb.HashScheme)
|
|
testReorgToShorterRemovesCanonMapping(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testReorgToShorterRemovesCanonMapping(t *testing.T, scheme string) {
|
|
chain, canonblocks, sideblocks, _, err := getLongAndShortChains(scheme)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(canonblocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
canonNum := chain.CurrentBlock().Number.Uint64()
|
|
canonHash := chain.CurrentBlock().Hash()
|
|
_, err = chain.InsertChain(sideblocks)
|
|
if err != nil {
|
|
t.Errorf("Got error, %v", err)
|
|
}
|
|
head := chain.CurrentBlock()
|
|
if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() {
|
|
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
|
}
|
|
// We have now inserted a sidechain.
|
|
if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil {
|
|
t.Errorf("expected block to be gone: %v", blockByNum.NumberU64())
|
|
}
|
|
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
|
|
t.Errorf("expected header to be gone: %v", headerByNum.Number)
|
|
}
|
|
if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
|
|
t.Errorf("expected block to be present: %x", blockByHash.Hash())
|
|
}
|
|
if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
|
|
t.Errorf("expected header to be present: %x", headerByHash.Hash())
|
|
}
|
|
}
|
|
|
|
// TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario
|
|
// as TestReorgToShorterRemovesCanonMapping, but applied on headerchain
|
|
// imports -- that is, for fast sync
|
|
func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
|
|
testReorgToShorterRemovesCanonMappingHeaderChain(t, rawdb.HashScheme)
|
|
testReorgToShorterRemovesCanonMappingHeaderChain(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T, scheme string) {
|
|
chain, canonblocks, sideblocks, _, err := getLongAndShortChains(scheme)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
// Convert into headers
|
|
canonHeaders := make([]*types.Header, len(canonblocks))
|
|
for i, block := range canonblocks {
|
|
canonHeaders[i] = block.Header()
|
|
}
|
|
if n, err := chain.InsertHeaderChain(canonHeaders); err != nil {
|
|
t.Fatalf("header %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
canonNum := chain.CurrentHeader().Number.Uint64()
|
|
canonHash := chain.CurrentBlock().Hash()
|
|
sideHeaders := make([]*types.Header, len(sideblocks))
|
|
for i, block := range sideblocks {
|
|
sideHeaders[i] = block.Header()
|
|
}
|
|
if n, err := chain.InsertHeaderChain(sideHeaders); err != nil {
|
|
t.Fatalf("header %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
head := chain.CurrentHeader()
|
|
if got := sideblocks[len(sideblocks)-1].Hash(); got != head.Hash() {
|
|
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
|
}
|
|
// We have now inserted a sidechain.
|
|
if blockByNum := chain.GetBlockByNumber(canonNum); blockByNum != nil {
|
|
t.Errorf("expected block to be gone: %v", blockByNum.NumberU64())
|
|
}
|
|
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
|
|
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
|
|
}
|
|
if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
|
|
t.Errorf("expected block to be present: %x", blockByHash.Hash())
|
|
}
|
|
if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
|
|
t.Errorf("expected header to be present: %x", headerByHash.Hash())
|
|
}
|
|
}
|
|
|
|
// Benchmarks large blocks with value transfers to non-existing accounts
|
|
func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks int, recipientFn func(uint64) common.Address) {
|
|
var (
|
|
signer = types.HomesteadSigner{}
|
|
testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey)
|
|
bankFunds = big.NewInt(100000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
testBankAddress: {Balance: bankFunds},
|
|
common.HexToAddress("0xc0de"): {
|
|
Code: []byte{0x60, 0x01, 0x50},
|
|
Balance: big.NewInt(0),
|
|
}, // push 1, pop
|
|
},
|
|
GasLimit: 100e6, // 100 M
|
|
}
|
|
)
|
|
// Generate the original common chain segment and the two competing forks
|
|
engine := ethash.NewFaker()
|
|
|
|
blockGenerator := func(i int, block *BlockGen) {
|
|
block.SetCoinbase(common.Address{1})
|
|
for txi := 0; txi < numTxs; txi++ {
|
|
uniq := uint64(i*numTxs + txi)
|
|
recipient := recipientFn(uniq)
|
|
tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, block.header.BaseFee, nil), signer, testBankKey)
|
|
if err != nil {
|
|
b.Error(err)
|
|
}
|
|
block.AddTx(tx)
|
|
}
|
|
}
|
|
|
|
_, shared, _ := GenerateChainWithGenesis(gspec, engine, numBlocks, blockGenerator)
|
|
b.StopTimer()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Import the shared chain and the original canonical one
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
b.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
b.StartTimer()
|
|
if _, err := chain.InsertChain(shared); err != nil {
|
|
b.Fatalf("failed to insert shared chain: %v", err)
|
|
}
|
|
b.StopTimer()
|
|
block := chain.GetBlockByHash(chain.CurrentBlock().Hash())
|
|
if got := block.Transactions().Len(); got != numTxs*numBlocks {
|
|
b.Fatalf("Transactions were not included, expected %d, got %d", numTxs*numBlocks, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkBlockChain_1x1000ValueTransferToNonexisting(b *testing.B) {
|
|
var (
|
|
numTxs = 1000
|
|
numBlocks = 1
|
|
)
|
|
recipientFn := func(nonce uint64) common.Address {
|
|
return common.BigToAddress(new(big.Int).SetUint64(1337 + nonce))
|
|
}
|
|
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn)
|
|
}
|
|
|
|
func BenchmarkBlockChain_1x1000ValueTransferToExisting(b *testing.B) {
|
|
var (
|
|
numTxs = 1000
|
|
numBlocks = 1
|
|
)
|
|
b.StopTimer()
|
|
b.ResetTimer()
|
|
|
|
recipientFn := func(nonce uint64) common.Address {
|
|
return common.BigToAddress(new(big.Int).SetUint64(1337))
|
|
}
|
|
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn)
|
|
}
|
|
|
|
func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
|
var (
|
|
numTxs = 1000
|
|
numBlocks = 1
|
|
)
|
|
b.StopTimer()
|
|
b.ResetTimer()
|
|
|
|
recipientFn := func(nonce uint64) common.Address {
|
|
return common.BigToAddress(new(big.Int).SetUint64(0xc0de))
|
|
}
|
|
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn)
|
|
}
|
|
|
|
// Tests that importing a some old blocks, where all blocks are before the
|
|
// pruning point.
|
|
// This internally leads to a sidechain import, since the blocks trigger an
|
|
// ErrPrunedAncestor error.
|
|
// This may e.g. happen if
|
|
// 1. Downloader rollbacks a batch of inserted blocks and exits
|
|
// 2. Downloader starts to sync again
|
|
// 3. The blocks fetched are all known and canonical blocks
|
|
func TestSideImportPrunedBlocks(t *testing.T) {
|
|
testSideImportPrunedBlocks(t, rawdb.HashScheme)
|
|
testSideImportPrunedBlocks(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
|
// Generate a canonical chain to act as the main dataset
|
|
engine := ethash.NewFaker()
|
|
genesis := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
// Generate and import the canonical chain
|
|
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil)
|
|
|
|
// Construct a database with freezer enabled
|
|
datadir := t.TempDir()
|
|
ancient := path.Join(datadir, "ancient")
|
|
|
|
pdb, err := pebble.New(datadir, 0, 0, "", false)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create persistent key-value database: %v", err)
|
|
}
|
|
db, err := rawdb.NewDatabaseWithFreezer(pdb, ancient, "", false)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create persistent freezer database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
// In path-based trie database implementation, it will keep 128 diff + 1 disk
|
|
// layers, totally 129 latest states available. In hash-based it's 128.
|
|
states := state.TriesInMemory
|
|
if scheme == rawdb.PathScheme {
|
|
states = state.TriesInMemory + 1
|
|
}
|
|
lastPrunedIndex := len(blocks) - states - 1
|
|
lastPrunedBlock := blocks[lastPrunedIndex]
|
|
|
|
// Verify pruning of lastPrunedBlock
|
|
if chain.HasBlockAndState(lastPrunedBlock.Hash(), lastPrunedBlock.NumberU64()) {
|
|
t.Errorf("Block %d not pruned", lastPrunedBlock.NumberU64())
|
|
}
|
|
firstNonPrunedBlock := blocks[len(blocks)-states]
|
|
// Verify firstNonPrunedBlock is not pruned
|
|
if !chain.HasBlockAndState(firstNonPrunedBlock.Hash(), firstNonPrunedBlock.NumberU64()) {
|
|
t.Errorf("Block %d pruned", firstNonPrunedBlock.NumberU64())
|
|
}
|
|
blockToReimport := blocks[5:8]
|
|
_, err = chain.InsertChain(blockToReimport)
|
|
if err != nil {
|
|
t.Errorf("Got error, %v", err)
|
|
}
|
|
}
|
|
|
|
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
|
// while changing the internals of statedb. The workflow is that a contract is
|
|
// self destructed, then in a followup transaction (but same block) it's created
|
|
// again and the transaction reverted.
|
|
//
|
|
// The original statedb implementation flushed dirty objects to the tries after
|
|
// each transaction, so this works ok. The rework accumulated writes in memory
|
|
// first, but the journal wiped the entire state object on create-revert.
|
|
func TestDeleteCreateRevert(t *testing.T) {
|
|
testDeleteCreateRevert(t, rawdb.HashScheme)
|
|
testDeleteCreateRevert(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testDeleteCreateRevert(t *testing.T, scheme string) {
|
|
var (
|
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(100000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address 0xAAAAA selfdestructs if called
|
|
aa: {
|
|
// Code needs to just selfdestruct
|
|
Code: []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)},
|
|
Nonce: 1,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
// The address 0xBBBB send 1 wei to 0xAAAA, then reverts
|
|
bb: {
|
|
Code: []byte{
|
|
byte(vm.PC), // [0]
|
|
byte(vm.DUP1), // [0,0]
|
|
byte(vm.DUP1), // [0,0,0]
|
|
byte(vm.DUP1), // [0,0,0,0]
|
|
byte(vm.PUSH1), 0x01, // [0,0,0,0,1] (value)
|
|
byte(vm.PUSH2), 0xaa, 0xaa, // [0,0,0,0,1, 0xaaaa]
|
|
byte(vm.GAS),
|
|
byte(vm.CALL),
|
|
byte(vm.REVERT),
|
|
},
|
|
Balance: big.NewInt(1),
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
// One transaction to AAAA
|
|
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
// One transaction to BBBB
|
|
tx, _ = types.SignTx(types.NewTransaction(1, bb,
|
|
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
}
|
|
|
|
// TestDeleteRecreateSlots tests a state-transition that contains both deletion
|
|
// and recreation of contract state.
|
|
// Contract A exists, has slots 1 and 2 set
|
|
// Tx 1: Selfdestruct A
|
|
// Tx 2: Re-create A, set slots 3 and 4
|
|
// Expected outcome is that _all_ slots are cleared from A, due to the selfdestruct,
|
|
// and then the new slots exist
|
|
func TestDeleteRecreateSlots(t *testing.T) {
|
|
testDeleteRecreateSlots(t, rawdb.HashScheme)
|
|
testDeleteRecreateSlots(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testDeleteRecreateSlots(t *testing.T, scheme string) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
)
|
|
// Populate two slots
|
|
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
|
|
// The bb-code needs to CREATE2 the aa contract. It consists of
|
|
// both initcode and deployment code
|
|
// initcode:
|
|
// 1. Set slots 3=3, 4=4,
|
|
// 2. Return aaCode
|
|
|
|
initCode := []byte{
|
|
byte(vm.PUSH1), 0x3, // value
|
|
byte(vm.PUSH1), 0x3, // location
|
|
byte(vm.SSTORE), // Set slot[3] = 3
|
|
byte(vm.PUSH1), 0x4, // value
|
|
byte(vm.PUSH1), 0x4, // location
|
|
byte(vm.SSTORE), // Set slot[4] = 4
|
|
// Slots are set, now return the code
|
|
byte(vm.PUSH2), byte(vm.PC), byte(vm.SELFDESTRUCT), // Push code on stack
|
|
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
byte(vm.MSTORE),
|
|
// Code is now in memory.
|
|
byte(vm.PUSH1), 0x2, // size
|
|
byte(vm.PUSH1), byte(32 - 2), // offset
|
|
byte(vm.RETURN),
|
|
}
|
|
if l := len(initCode); l > 32 {
|
|
t.Fatalf("init code is too long for a pushx, need a more elaborate deployer")
|
|
}
|
|
bbCode := []byte{
|
|
// Push initcode onto stack
|
|
byte(vm.PUSH1) + byte(len(initCode)-1)}
|
|
bbCode = append(bbCode, initCode...)
|
|
bbCode = append(bbCode, []byte{
|
|
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
byte(vm.MSTORE),
|
|
byte(vm.PUSH1), 0x00, // salt
|
|
byte(vm.PUSH1), byte(len(initCode)), // size
|
|
byte(vm.PUSH1), byte(32 - len(initCode)), // offset
|
|
byte(vm.PUSH1), 0x00, // endowment
|
|
byte(vm.CREATE2),
|
|
}...)
|
|
|
|
initHash := crypto.Keccak256Hash(initCode)
|
|
aa := crypto.CreateAddress2(bb, [32]byte{}, initHash[:])
|
|
t.Logf("Destination address: %x\n", aa)
|
|
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address 0xAAAAA selfdestructs if called
|
|
aa: {
|
|
// Code needs to just selfdestruct
|
|
Code: aaCode,
|
|
Nonce: 1,
|
|
Balance: big.NewInt(0),
|
|
Storage: aaStorage,
|
|
},
|
|
// The contract BB recreates AA
|
|
bb: {
|
|
Code: bbCode,
|
|
Balance: big.NewInt(1),
|
|
},
|
|
},
|
|
}
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
// One transaction to AA, to kill it
|
|
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
// One transaction to BB, to recreate AA
|
|
tx, _ = types.SignTx(types.NewTransaction(1, bb,
|
|
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
|
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
statedb, _ := chain.State()
|
|
|
|
// If all is correct, then slot 1 and 2 are zero
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
t.Errorf("got %x exp %x", got, exp)
|
|
}
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
t.Errorf("got %x exp %x", got, exp)
|
|
}
|
|
// Also, 3 and 4 should be set
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("03")), common.HexToHash("03"); got != exp {
|
|
t.Fatalf("got %x exp %x", got, exp)
|
|
}
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("04")), common.HexToHash("04"); got != exp {
|
|
t.Fatalf("got %x exp %x", got, exp)
|
|
}
|
|
}
|
|
|
|
// TestDeleteRecreateAccount tests a state-transition that contains deletion of a
|
|
// contract with storage, and a recreate of the same contract via a
|
|
// regular value-transfer
|
|
// Expected outcome is that _all_ slots are cleared from A
|
|
func TestDeleteRecreateAccount(t *testing.T) {
|
|
testDeleteRecreateAccount(t, rawdb.HashScheme)
|
|
testDeleteRecreateAccount(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testDeleteRecreateAccount(t *testing.T, scheme string) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
|
|
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
|
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
)
|
|
// Populate two slots
|
|
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address 0xAAAAA selfdestructs if called
|
|
aa: {
|
|
// Code needs to just selfdestruct
|
|
Code: aaCode,
|
|
Nonce: 1,
|
|
Balance: big.NewInt(0),
|
|
Storage: aaStorage,
|
|
},
|
|
},
|
|
}
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
// One transaction to AA, to kill it
|
|
tx, _ := types.SignTx(types.NewTransaction(0, aa,
|
|
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
// One transaction to AA, to recreate it (but without storage
|
|
tx, _ = types.SignTx(types.NewTransaction(1, aa,
|
|
big.NewInt(1), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
|
Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
statedb, _ := chain.State()
|
|
|
|
// If all is correct, then both slots are zero
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
t.Errorf("got %x exp %x", got, exp)
|
|
}
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
t.Errorf("got %x exp %x", got, exp)
|
|
}
|
|
}
|
|
|
|
// TestDeleteRecreateSlotsAcrossManyBlocks tests multiple state-transition that contains both deletion
|
|
// and recreation of contract state.
|
|
// Contract A exists, has slots 1 and 2 set
|
|
// Tx 1: Selfdestruct A
|
|
// Tx 2: Re-create A, set slots 3 and 4
|
|
// Expected outcome is that _all_ slots are cleared from A, due to the selfdestruct,
|
|
// and then the new slots exist
|
|
func TestDeleteRecreateSlotsAcrossManyBlocks(t *testing.T) {
|
|
testDeleteRecreateSlotsAcrossManyBlocks(t, rawdb.HashScheme)
|
|
testDeleteRecreateSlotsAcrossManyBlocks(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testDeleteRecreateSlotsAcrossManyBlocks(t *testing.T, scheme string) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
aaStorage = make(map[common.Hash]common.Hash) // Initial storage in AA
|
|
aaCode = []byte{byte(vm.PC), byte(vm.SELFDESTRUCT)} // Code for AA (simple selfdestruct)
|
|
)
|
|
// Populate two slots
|
|
aaStorage[common.HexToHash("01")] = common.HexToHash("01")
|
|
aaStorage[common.HexToHash("02")] = common.HexToHash("02")
|
|
|
|
// The bb-code needs to CREATE2 the aa contract. It consists of
|
|
// both initcode and deployment code
|
|
// initcode:
|
|
// 1. Set slots 3=blocknum+1, 4=4,
|
|
// 2. Return aaCode
|
|
|
|
initCode := []byte{
|
|
byte(vm.PUSH1), 0x1, //
|
|
byte(vm.NUMBER), // value = number + 1
|
|
byte(vm.ADD), //
|
|
byte(vm.PUSH1), 0x3, // location
|
|
byte(vm.SSTORE), // Set slot[3] = number + 1
|
|
byte(vm.PUSH1), 0x4, // value
|
|
byte(vm.PUSH1), 0x4, // location
|
|
byte(vm.SSTORE), // Set slot[4] = 4
|
|
// Slots are set, now return the code
|
|
byte(vm.PUSH2), byte(vm.PC), byte(vm.SELFDESTRUCT), // Push code on stack
|
|
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
byte(vm.MSTORE),
|
|
// Code is now in memory.
|
|
byte(vm.PUSH1), 0x2, // size
|
|
byte(vm.PUSH1), byte(32 - 2), // offset
|
|
byte(vm.RETURN),
|
|
}
|
|
if l := len(initCode); l > 32 {
|
|
t.Fatalf("init code is too long for a pushx, need a more elaborate deployer")
|
|
}
|
|
bbCode := []byte{
|
|
// Push initcode onto stack
|
|
byte(vm.PUSH1) + byte(len(initCode)-1)}
|
|
bbCode = append(bbCode, initCode...)
|
|
bbCode = append(bbCode, []byte{
|
|
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
byte(vm.MSTORE),
|
|
byte(vm.PUSH1), 0x00, // salt
|
|
byte(vm.PUSH1), byte(len(initCode)), // size
|
|
byte(vm.PUSH1), byte(32 - len(initCode)), // offset
|
|
byte(vm.PUSH1), 0x00, // endowment
|
|
byte(vm.CREATE2),
|
|
}...)
|
|
|
|
initHash := crypto.Keccak256Hash(initCode)
|
|
aa := crypto.CreateAddress2(bb, [32]byte{}, initHash[:])
|
|
t.Logf("Destination address: %x\n", aa)
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address 0xAAAAA selfdestructs if called
|
|
aa: {
|
|
// Code needs to just selfdestruct
|
|
Code: aaCode,
|
|
Nonce: 1,
|
|
Balance: big.NewInt(0),
|
|
Storage: aaStorage,
|
|
},
|
|
// The contract BB recreates AA
|
|
bb: {
|
|
Code: bbCode,
|
|
Balance: big.NewInt(1),
|
|
},
|
|
},
|
|
}
|
|
var nonce uint64
|
|
|
|
type expectation struct {
|
|
exist bool
|
|
blocknum int
|
|
values map[int]int
|
|
}
|
|
var current = &expectation{
|
|
exist: true, // exists in genesis
|
|
blocknum: 0,
|
|
values: map[int]int{1: 1, 2: 2},
|
|
}
|
|
var expectations []*expectation
|
|
var newDestruct = func(e *expectation, b *BlockGen) *types.Transaction {
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, aa,
|
|
big.NewInt(0), 50000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
nonce++
|
|
if e.exist {
|
|
e.exist = false
|
|
e.values = nil
|
|
}
|
|
//t.Logf("block %d; adding destruct\n", e.blocknum)
|
|
return tx
|
|
}
|
|
var newResurrect = func(e *expectation, b *BlockGen) *types.Transaction {
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, bb,
|
|
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
nonce++
|
|
if !e.exist {
|
|
e.exist = true
|
|
e.values = map[int]int{3: e.blocknum + 1, 4: 4}
|
|
}
|
|
//t.Logf("block %d; adding resurrect\n", e.blocknum)
|
|
return tx
|
|
}
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 150, func(i int, b *BlockGen) {
|
|
var exp = new(expectation)
|
|
exp.blocknum = i + 1
|
|
exp.values = make(map[int]int)
|
|
for k, v := range current.values {
|
|
exp.values[k] = v
|
|
}
|
|
exp.exist = current.exist
|
|
|
|
b.SetCoinbase(common.Address{1})
|
|
if i%2 == 0 {
|
|
b.AddTx(newDestruct(exp, b))
|
|
}
|
|
if i%3 == 0 {
|
|
b.AddTx(newResurrect(exp, b))
|
|
}
|
|
if i%5 == 0 {
|
|
b.AddTx(newDestruct(exp, b))
|
|
}
|
|
if i%7 == 0 {
|
|
b.AddTx(newResurrect(exp, b))
|
|
}
|
|
expectations = append(expectations, exp)
|
|
current = exp
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
|
//Debug: true,
|
|
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
var asHash = func(num int) common.Hash {
|
|
return common.BytesToHash([]byte{byte(num)})
|
|
}
|
|
for i, block := range blocks {
|
|
blockNum := i + 1
|
|
if n, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
statedb, _ := chain.State()
|
|
// If all is correct, then slot 1 and 2 are zero
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("01")), (common.Hash{}); got != exp {
|
|
t.Errorf("block %d, got %x exp %x", blockNum, got, exp)
|
|
}
|
|
if got, exp := statedb.GetState(aa, common.HexToHash("02")), (common.Hash{}); got != exp {
|
|
t.Errorf("block %d, got %x exp %x", blockNum, got, exp)
|
|
}
|
|
exp := expectations[i]
|
|
if exp.exist {
|
|
if !statedb.Exist(aa) {
|
|
t.Fatalf("block %d, expected %v to exist, it did not", blockNum, aa)
|
|
}
|
|
for slot, val := range exp.values {
|
|
if gotValue, expValue := statedb.GetState(aa, asHash(slot)), asHash(val); gotValue != expValue {
|
|
t.Fatalf("block %d, slot %d, got %x exp %x", blockNum, slot, gotValue, expValue)
|
|
}
|
|
}
|
|
} else {
|
|
if statedb.Exist(aa) {
|
|
t.Fatalf("block %d, expected %v to not exist, it did", blockNum, aa)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInitThenFailCreateContract tests a pretty notorious case that happened
|
|
// on mainnet over blocks 7338108, 7338110 and 7338115.
|
|
// - Block 7338108: address e771789f5cccac282f23bb7add5690e1f6ca467c is initiated
|
|
// with 0.001 ether (thus created but no code)
|
|
// - Block 7338110: a CREATE2 is attempted. The CREATE2 would deploy code on
|
|
// the same address e771789f5cccac282f23bb7add5690e1f6ca467c. However, the
|
|
// deployment fails due to OOG during initcode execution
|
|
// - Block 7338115: another tx checks the balance of
|
|
// e771789f5cccac282f23bb7add5690e1f6ca467c, and the snapshotter returned it as
|
|
// zero.
|
|
//
|
|
// The problem being that the snapshotter maintains a destructset, and adds items
|
|
// to the destructset in case something is created "onto" an existing item.
|
|
// We need to either roll back the snapDestructs, or not place it into snapDestructs
|
|
// in the first place.
|
|
//
|
|
|
|
func TestInitThenFailCreateContract(t *testing.T) {
|
|
testInitThenFailCreateContract(t, rawdb.HashScheme)
|
|
testInitThenFailCreateContract(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
)
|
|
|
|
// The bb-code needs to CREATE2 the aa contract. It consists of
|
|
// both initcode and deployment code
|
|
// initcode:
|
|
// 1. If blocknum < 1, error out (e.g invalid opcode)
|
|
// 2. else, return a snippet of code
|
|
initCode := []byte{
|
|
byte(vm.PUSH1), 0x1, // y (2)
|
|
byte(vm.NUMBER), // x (number)
|
|
byte(vm.GT), // x > y?
|
|
byte(vm.PUSH1), byte(0x8),
|
|
byte(vm.JUMPI), // jump to label if number > 2
|
|
byte(0xFE), // illegal opcode
|
|
byte(vm.JUMPDEST),
|
|
byte(vm.PUSH1), 0x2, // size
|
|
byte(vm.PUSH1), 0x0, // offset
|
|
byte(vm.RETURN), // return 2 bytes of zero-code
|
|
}
|
|
if l := len(initCode); l > 32 {
|
|
t.Fatalf("init code is too long for a pushx, need a more elaborate deployer")
|
|
}
|
|
bbCode := []byte{
|
|
// Push initcode onto stack
|
|
byte(vm.PUSH1) + byte(len(initCode)-1)}
|
|
bbCode = append(bbCode, initCode...)
|
|
bbCode = append(bbCode, []byte{
|
|
byte(vm.PUSH1), 0x0, // memory start on stack
|
|
byte(vm.MSTORE),
|
|
byte(vm.PUSH1), 0x00, // salt
|
|
byte(vm.PUSH1), byte(len(initCode)), // size
|
|
byte(vm.PUSH1), byte(32 - len(initCode)), // offset
|
|
byte(vm.PUSH1), 0x00, // endowment
|
|
byte(vm.CREATE2),
|
|
}...)
|
|
|
|
initHash := crypto.Keccak256Hash(initCode)
|
|
aa := crypto.CreateAddress2(bb, [32]byte{}, initHash[:])
|
|
t.Logf("Destination address: %x\n", aa)
|
|
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address aa has some funds
|
|
aa: {Balance: big.NewInt(100000)},
|
|
// The contract BB tries to create code onto AA
|
|
bb: {
|
|
Code: bbCode,
|
|
Balance: big.NewInt(1),
|
|
},
|
|
},
|
|
}
|
|
nonce := uint64(0)
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 4, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
// One transaction to BB
|
|
tx, _ := types.SignTx(types.NewTransaction(nonce, bb,
|
|
big.NewInt(0), 100000, b.header.BaseFee, nil), types.HomesteadSigner{}, key)
|
|
b.AddTx(tx)
|
|
nonce++
|
|
})
|
|
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{
|
|
//Debug: true,
|
|
//Tracer: vm.NewJSONLogger(nil, os.Stdout),
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
statedb, _ := chain.State()
|
|
if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
|
|
t.Fatalf("Genesis err, got %v exp %v", got, exp)
|
|
}
|
|
// First block tries to create, but fails
|
|
{
|
|
block := blocks[0]
|
|
if _, err := chain.InsertChain([]*types.Block{blocks[0]}); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
|
|
}
|
|
statedb, _ = chain.State()
|
|
if got, exp := statedb.GetBalance(aa), uint256.NewInt(100000); got.Cmp(exp) != 0 {
|
|
t.Fatalf("block %d: got %v exp %v", block.NumberU64(), got, exp)
|
|
}
|
|
}
|
|
// Import the rest of the blocks
|
|
for _, block := range blocks[1:] {
|
|
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestEIP2718Transition tests that an EIP-2718 transaction will be accepted
|
|
// after the fork block has passed. This is verified by sending an EIP-2930
|
|
// access list transaction, which specifies a single slot access, and then
|
|
// checking that the gas usage of a hot SLOAD and a cold SLOAD are calculated
|
|
// correctly.
|
|
func TestEIP2718Transition(t *testing.T) {
|
|
testEIP2718Transition(t, rawdb.HashScheme)
|
|
testEIP2718Transition(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEIP2718Transition(t *testing.T, scheme string) {
|
|
var (
|
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(1000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
// The address 0xAAAA sloads 0x00 and 0x01
|
|
aa: {
|
|
Code: []byte{
|
|
byte(vm.PC),
|
|
byte(vm.PC),
|
|
byte(vm.SLOAD),
|
|
byte(vm.SLOAD),
|
|
},
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
},
|
|
}
|
|
)
|
|
// Generate blocks
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
|
|
// One transaction to 0xAAAA
|
|
signer := types.LatestSigner(gspec.Config)
|
|
tx, _ := types.SignNewTx(key, signer, &types.AccessListTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 0,
|
|
To: &aa,
|
|
Gas: 30000,
|
|
GasPrice: b.header.BaseFee,
|
|
AccessList: types.AccessList{{
|
|
Address: aa,
|
|
StorageKeys: []common.Hash{{0}},
|
|
}},
|
|
})
|
|
b.AddTx(tx)
|
|
})
|
|
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
block := chain.GetBlockByNumber(1)
|
|
|
|
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
|
|
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
|
|
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
|
|
if block.GasUsed() != expected {
|
|
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
|
|
}
|
|
}
|
|
|
|
// TestEIP1559Transition tests the following:
|
|
//
|
|
// 1. A transaction whose gasFeeCap is greater than the baseFee is valid.
|
|
// 2. Gas accounting for access lists on EIP-1559 transactions is correct.
|
|
// 3. Only the transaction's tip will be received by the coinbase.
|
|
// 4. The transaction sender pays for both the tip and baseFee.
|
|
// 5. The coinbase receives only the partially realized tip when
|
|
// gasFeeCap - gasTipCap < baseFee.
|
|
// 6. Legacy transaction behave as expected (e.g. gasPrice = gasFeeCap = gasTipCap).
|
|
func TestEIP1559Transition(t *testing.T) {
|
|
testEIP1559Transition(t, rawdb.HashScheme)
|
|
testEIP1559Transition(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testEIP1559Transition(t *testing.T, scheme string) {
|
|
var (
|
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
engine = ethash.NewFaker()
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
|
config = *params.AllEthashProtocolChanges
|
|
gspec = &Genesis{
|
|
Config: &config,
|
|
Alloc: types.GenesisAlloc{
|
|
addr1: {Balance: funds},
|
|
addr2: {Balance: funds},
|
|
// The address 0xAAAA sloads 0x00 and 0x01
|
|
aa: {
|
|
Code: []byte{
|
|
byte(vm.PC),
|
|
byte(vm.PC),
|
|
byte(vm.SLOAD),
|
|
byte(vm.SLOAD),
|
|
},
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
gspec.Config.BerlinBlock = common.Big0
|
|
gspec.Config.LondonBlock = common.Big0
|
|
signer := types.LatestSigner(gspec.Config)
|
|
|
|
genDb, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{1})
|
|
|
|
// One transaction to 0xAAAA
|
|
accesses := types.AccessList{types.AccessTuple{
|
|
Address: aa,
|
|
StorageKeys: []common.Hash{{0}},
|
|
}}
|
|
|
|
txdata := &types.DynamicFeeTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 0,
|
|
To: &aa,
|
|
Gas: 30000,
|
|
GasFeeCap: newGwei(5),
|
|
GasTipCap: big.NewInt(2),
|
|
AccessList: accesses,
|
|
Data: []byte{},
|
|
}
|
|
tx := types.NewTx(txdata)
|
|
tx, _ = types.SignTx(tx, signer, key1)
|
|
|
|
b.AddTx(tx)
|
|
})
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
block := chain.GetBlockByNumber(1)
|
|
|
|
// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
|
|
expectedGas := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
|
|
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
|
|
if block.GasUsed() != expectedGas {
|
|
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
|
|
}
|
|
|
|
state, _ := chain.State()
|
|
|
|
// 3: Ensure that miner received only the tx's tip.
|
|
actual := state.GetBalance(block.Coinbase()).ToBig()
|
|
expected := new(big.Int).Add(
|
|
new(big.Int).SetUint64(block.GasUsed()*block.Transactions()[0].GasTipCap().Uint64()),
|
|
ethash.ConstantinopleBlockReward.ToBig(),
|
|
)
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
|
|
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
|
|
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
|
|
blocks, _ = GenerateChain(gspec.Config, block, engine, genDb, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(common.Address{2})
|
|
|
|
txdata := &types.LegacyTx{
|
|
Nonce: 0,
|
|
To: &aa,
|
|
Gas: 30000,
|
|
GasPrice: newGwei(5),
|
|
}
|
|
tx := types.NewTx(txdata)
|
|
tx, _ = types.SignTx(tx, signer, key2)
|
|
|
|
b.AddTx(tx)
|
|
})
|
|
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
block = chain.GetBlockByNumber(2)
|
|
state, _ = chain.State()
|
|
effectiveTip := block.Transactions()[0].GasTipCap().Uint64() - block.BaseFee().Uint64()
|
|
|
|
// 6+5: Ensure that miner received only the tx's effective tip.
|
|
actual = state.GetBalance(block.Coinbase()).ToBig()
|
|
expected = new(big.Int).Add(
|
|
new(big.Int).SetUint64(block.GasUsed()*effectiveTip),
|
|
ethash.ConstantinopleBlockReward.ToBig(),
|
|
)
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
|
|
// 4: Ensure the tx sender paid for the gasUsed * (effectiveTip + block baseFee).
|
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr2).ToBig())
|
|
expected = new(big.Int).SetUint64(block.GasUsed() * (effectiveTip + block.BaseFee().Uint64()))
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
}
|
|
|
|
// Tests the scenario the chain is requested to another point with the missing state.
|
|
// It expects the state is recovered and all relevant chain markers are set correctly.
|
|
func TestSetCanonical(t *testing.T) {
|
|
testSetCanonical(t, rawdb.HashScheme)
|
|
testSetCanonical(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testSetCanonical(t *testing.T, scheme string) {
|
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
|
|
|
var (
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
funds = big.NewInt(100000000000000000)
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
signer = types.LatestSigner(gspec.Config)
|
|
engine = ethash.NewFaker()
|
|
chainLength = 10
|
|
)
|
|
// Generate and import the canonical chain
|
|
_, canon, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) {
|
|
tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, gen.header.BaseFee, nil), signer, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
gen.AddTx(tx)
|
|
})
|
|
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
|
defer diskdb.Close()
|
|
|
|
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
|
|
if n, err := chain.InsertChain(canon); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
// Generate the side chain and import them
|
|
_, side, _ := GenerateChainWithGenesis(gspec, engine, chainLength, func(i int, gen *BlockGen) {
|
|
tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.header.BaseFee, nil), signer, key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
gen.AddTx(tx)
|
|
})
|
|
for _, block := range side {
|
|
_, err := chain.InsertBlockWithoutSetHead(block, false)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert into chain: %v", err)
|
|
}
|
|
}
|
|
for _, block := range side {
|
|
got := chain.GetBlockByHash(block.Hash())
|
|
if got == nil {
|
|
t.Fatalf("Lost the inserted block")
|
|
}
|
|
}
|
|
|
|
// Set the chain head to the side chain, ensure all the relevant markers are updated.
|
|
verify := func(head *types.Block) {
|
|
if chain.CurrentBlock().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
|
|
}
|
|
if chain.CurrentSnapBlock().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentSnapBlock().Hash())
|
|
}
|
|
if chain.CurrentHeader().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
|
|
}
|
|
if !chain.HasState(head.Root()) {
|
|
t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
|
|
}
|
|
}
|
|
chain.SetCanonical(side[len(side)-1])
|
|
verify(side[len(side)-1])
|
|
|
|
// Reset the chain head to original chain
|
|
chain.SetCanonical(canon[chainLength-1])
|
|
verify(canon[chainLength-1])
|
|
}
|
|
|
|
// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
|
|
// correctly in case reorg is called.
|
|
func TestCanonicalHashMarker(t *testing.T) {
|
|
testCanonicalHashMarker(t, rawdb.HashScheme)
|
|
testCanonicalHashMarker(t, rawdb.PathScheme)
|
|
}
|
|
|
|
func testCanonicalHashMarker(t *testing.T, scheme string) {
|
|
var cases = []struct {
|
|
forkA int
|
|
forkB int
|
|
}{
|
|
// ForkA: 10 blocks
|
|
// ForkB: 1 blocks
|
|
//
|
|
// reorged:
|
|
// markers [2, 10] should be deleted
|
|
// markers [1] should be updated
|
|
{10, 1},
|
|
|
|
// ForkA: 10 blocks
|
|
// ForkB: 2 blocks
|
|
//
|
|
// reorged:
|
|
// markers [3, 10] should be deleted
|
|
// markers [1, 2] should be updated
|
|
{10, 2},
|
|
|
|
// ForkA: 10 blocks
|
|
// ForkB: 10 blocks
|
|
//
|
|
// reorged:
|
|
// markers [1, 10] should be updated
|
|
{10, 10},
|
|
|
|
// ForkA: 10 blocks
|
|
// ForkB: 11 blocks
|
|
//
|
|
// reorged:
|
|
// markers [1, 11] should be updated
|
|
{10, 11},
|
|
}
|
|
for _, c := range cases {
|
|
var (
|
|
gspec = &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{},
|
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
|
}
|
|
engine = ethash.NewFaker()
|
|
)
|
|
_, forkA, _ := GenerateChainWithGenesis(gspec, engine, c.forkA, func(i int, gen *BlockGen) {})
|
|
_, forkB, _ := GenerateChainWithGenesis(gspec, engine, c.forkB, func(i int, gen *BlockGen) {})
|
|
|
|
// Initialize test chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
// Insert forkA and forkB, the canonical should on forkA still
|
|
if n, err := chain.InsertChain(forkA); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
if n, err := chain.InsertChain(forkB); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
verify := func(head *types.Block) {
|
|
if chain.CurrentBlock().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected block hash, want %x, got %x", head.Hash(), chain.CurrentBlock().Hash())
|
|
}
|
|
if chain.CurrentSnapBlock().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected fast block hash, want %x, got %x", head.Hash(), chain.CurrentSnapBlock().Hash())
|
|
}
|
|
if chain.CurrentHeader().Hash() != head.Hash() {
|
|
t.Fatalf("Unexpected head header, want %x, got %x", head.Hash(), chain.CurrentHeader().Hash())
|
|
}
|
|
if !chain.HasState(head.Root()) {
|
|
t.Fatalf("Lost block state %v %x", head.Number(), head.Hash())
|
|
}
|
|
}
|
|
|
|
// Switch canonical chain to forkB if necessary
|
|
if len(forkA) < len(forkB) {
|
|
verify(forkB[len(forkB)-1])
|
|
} else {
|
|
verify(forkA[len(forkA)-1])
|
|
chain.SetCanonical(forkB[len(forkB)-1])
|
|
verify(forkB[len(forkB)-1])
|
|
}
|
|
|
|
// Ensure all hash markers are updated correctly
|
|
for i := 0; i < len(forkB); i++ {
|
|
block := forkB[i]
|
|
hash := chain.GetCanonicalHash(block.NumberU64())
|
|
if hash != block.Hash() {
|
|
t.Fatalf("Unexpected canonical hash %d", block.NumberU64())
|
|
}
|
|
}
|
|
if c.forkA > c.forkB {
|
|
for i := uint64(c.forkB) + 1; i <= uint64(c.forkA); i++ {
|
|
hash := chain.GetCanonicalHash(i)
|
|
if hash != (common.Hash{}) {
|
|
t.Fatalf("Unexpected canonical hash %d", i)
|
|
}
|
|
}
|
|
}
|
|
chain.Stop()
|
|
}
|
|
}
|
|
|
|
func TestCreateThenDeletePreByzantium(t *testing.T) {
|
|
// We use Ropsten chain config instead of Testchain config, this is
|
|
// deliberate: we want to use pre-byz rules where we have intermediate state roots
|
|
// between transactions.
|
|
testCreateThenDelete(t, ¶ms.ChainConfig{
|
|
ChainID: big.NewInt(3),
|
|
HomesteadBlock: big.NewInt(0),
|
|
EIP150Block: big.NewInt(0),
|
|
EIP155Block: big.NewInt(10),
|
|
EIP158Block: big.NewInt(10),
|
|
ByzantiumBlock: big.NewInt(1_700_000),
|
|
})
|
|
}
|
|
func TestCreateThenDeletePostByzantium(t *testing.T) {
|
|
testCreateThenDelete(t, params.TestChainConfig)
|
|
}
|
|
|
|
// testCreateThenDelete tests a creation and subsequent deletion of a contract, happening
|
|
// within the same block.
|
|
func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
// A sender who makes transactions, has some funds
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
destAddress = crypto.CreateAddress(address, 0)
|
|
funds = big.NewInt(1000000000000000)
|
|
)
|
|
|
|
// runtime code is 0x60ffff : PUSH1 0xFF SELFDESTRUCT, a.k.a SELFDESTRUCT(0xFF)
|
|
code := append([]byte{0x60, 0xff, 0xff}, make([]byte, 32-3)...)
|
|
initCode := []byte{
|
|
// SSTORE 1:1
|
|
byte(vm.PUSH1), 0x1,
|
|
byte(vm.PUSH1), 0x1,
|
|
byte(vm.SSTORE),
|
|
// Get the runtime-code on the stack
|
|
byte(vm.PUSH32)}
|
|
initCode = append(initCode, code...)
|
|
initCode = append(initCode, []byte{
|
|
byte(vm.PUSH1), 0x0, // offset
|
|
byte(vm.MSTORE),
|
|
byte(vm.PUSH1), 0x3, // size
|
|
byte(vm.PUSH1), 0x0, // offset
|
|
byte(vm.RETURN), // return 3 bytes of zero-code
|
|
}...)
|
|
gspec := &Genesis{
|
|
Config: config,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
},
|
|
}
|
|
nonce := uint64(0)
|
|
signer := types.HomesteadSigner{}
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2, func(i int, b *BlockGen) {
|
|
fee := big.NewInt(1)
|
|
if b.header.BaseFee != nil {
|
|
fee = b.header.BaseFee
|
|
}
|
|
b.SetCoinbase(common.Address{1})
|
|
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 100000,
|
|
Data: initCode,
|
|
})
|
|
nonce++
|
|
b.AddTx(tx)
|
|
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 100000,
|
|
To: &destAddress,
|
|
})
|
|
b.AddTx(tx)
|
|
nonce++
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{
|
|
//Debug: true,
|
|
//Tracer: logger.NewJSONLogger(nil, os.Stdout),
|
|
}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
// Import the blocks
|
|
for _, block := range blocks {
|
|
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDeleteThenCreate(t *testing.T) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
factoryAddr = crypto.CreateAddress(address, 0)
|
|
funds = big.NewInt(1000000000000000)
|
|
)
|
|
/*
|
|
contract Factory {
|
|
function deploy(bytes memory code) public {
|
|
address addr;
|
|
assembly {
|
|
addr := create2(0, add(code, 0x20), mload(code), 0)
|
|
if iszero(extcodesize(addr)) {
|
|
revert(0, 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
factoryBIN := common.Hex2Bytes("608060405234801561001057600080fd5b50610241806100206000396000f3fe608060405234801561001057600080fd5b506004361061002a5760003560e01c80627743601461002f575b600080fd5b610049600480360381019061004491906100d8565b61004b565b005b6000808251602084016000f59050803b61006457600080fd5b5050565b600061007b61007684610146565b610121565b905082815260208101848484011115610097576100966101eb565b5b6100a2848285610177565b509392505050565b600082601f8301126100bf576100be6101e6565b5b81356100cf848260208601610068565b91505092915050565b6000602082840312156100ee576100ed6101f5565b5b600082013567ffffffffffffffff81111561010c5761010b6101f0565b5b610118848285016100aa565b91505092915050565b600061012b61013c565b90506101378282610186565b919050565b6000604051905090565b600067ffffffffffffffff821115610161576101606101b7565b5b61016a826101fa565b9050602081019050919050565b82818337600083830152505050565b61018f826101fa565b810181811067ffffffffffffffff821117156101ae576101ad6101b7565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f830116905091905056fea2646970667358221220ea8b35ed310d03b6b3deef166941140b4d9e90ea2c92f6b41eb441daf49a59c364736f6c63430008070033")
|
|
|
|
/*
|
|
contract C {
|
|
uint256 value;
|
|
constructor() {
|
|
value = 100;
|
|
}
|
|
function destruct() public payable {
|
|
selfdestruct(payable(msg.sender));
|
|
}
|
|
receive() payable external {}
|
|
}
|
|
*/
|
|
contractABI := common.Hex2Bytes("6080604052348015600f57600080fd5b5060646000819055506081806100266000396000f3fe608060405260043610601f5760003560e01c80632b68b9c614602a576025565b36602557005b600080fd5b60306032565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220ab749f5ed1fcb87bda03a74d476af3f074bba24d57cb5a355e8162062ad9a4e664736f6c63430008070033")
|
|
contractAddr := crypto.CreateAddress2(factoryAddr, [32]byte{}, crypto.Keccak256(contractABI))
|
|
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
},
|
|
}
|
|
nonce := uint64(0)
|
|
signer := types.HomesteadSigner{}
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 2, func(i int, b *BlockGen) {
|
|
fee := big.NewInt(1)
|
|
if b.header.BaseFee != nil {
|
|
fee = b.header.BaseFee
|
|
}
|
|
b.SetCoinbase(common.Address{1})
|
|
|
|
// Block 1
|
|
if i == 0 {
|
|
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 500000,
|
|
Data: factoryBIN,
|
|
})
|
|
nonce++
|
|
b.AddTx(tx)
|
|
|
|
data := common.Hex2Bytes("00774360000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a76080604052348015600f57600080fd5b5060646000819055506081806100266000396000f3fe608060405260043610601f5760003560e01c80632b68b9c614602a576025565b36602557005b600080fd5b60306032565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220ab749f5ed1fcb87bda03a74d476af3f074bba24d57cb5a355e8162062ad9a4e664736f6c6343000807003300000000000000000000000000000000000000000000000000")
|
|
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 500000,
|
|
To: &factoryAddr,
|
|
Data: data,
|
|
})
|
|
b.AddTx(tx)
|
|
nonce++
|
|
} else {
|
|
// Block 2
|
|
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 500000,
|
|
To: &contractAddr,
|
|
Data: common.Hex2Bytes("2b68b9c6"), // destruct
|
|
})
|
|
nonce++
|
|
b.AddTx(tx)
|
|
|
|
data := common.Hex2Bytes("00774360000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a76080604052348015600f57600080fd5b5060646000819055506081806100266000396000f3fe608060405260043610601f5760003560e01c80632b68b9c614602a576025565b36602557005b600080fd5b60306032565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220ab749f5ed1fcb87bda03a74d476af3f074bba24d57cb5a355e8162062ad9a4e664736f6c6343000807003300000000000000000000000000000000000000000000000000")
|
|
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 500000,
|
|
To: &factoryAddr, // re-creation
|
|
Data: data,
|
|
})
|
|
b.AddTx(tx)
|
|
nonce++
|
|
}
|
|
})
|
|
// Import the canonical chain
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
for _, block := range blocks {
|
|
if _, err := chain.InsertChain([]*types.Block{block}); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", block.NumberU64(), err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestTransientStorageReset ensures the transient storage is wiped correctly
|
|
// between transactions.
|
|
func TestTransientStorageReset(t *testing.T) {
|
|
var (
|
|
engine = ethash.NewFaker()
|
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
|
destAddress = crypto.CreateAddress(address, 0)
|
|
funds = big.NewInt(1000000000000000)
|
|
vmConfig = vm.Config{
|
|
ExtraEips: []int{1153}, // Enable transient storage EIP
|
|
}
|
|
)
|
|
code := append([]byte{
|
|
// TLoad value with location 1
|
|
byte(vm.PUSH1), 0x1,
|
|
byte(vm.TLOAD),
|
|
|
|
// PUSH location
|
|
byte(vm.PUSH1), 0x1,
|
|
|
|
// SStore location:value
|
|
byte(vm.SSTORE),
|
|
}, make([]byte, 32-6)...)
|
|
initCode := []byte{
|
|
// TSTORE 1:1
|
|
byte(vm.PUSH1), 0x1,
|
|
byte(vm.PUSH1), 0x1,
|
|
byte(vm.TSTORE),
|
|
|
|
// Get the runtime-code on the stack
|
|
byte(vm.PUSH32)}
|
|
initCode = append(initCode, code...)
|
|
initCode = append(initCode, []byte{
|
|
byte(vm.PUSH1), 0x0, // offset
|
|
byte(vm.MSTORE),
|
|
byte(vm.PUSH1), 0x6, // size
|
|
byte(vm.PUSH1), 0x0, // offset
|
|
byte(vm.RETURN), // return 6 bytes of zero-code
|
|
}...)
|
|
gspec := &Genesis{
|
|
Config: params.TestChainConfig,
|
|
Alloc: types.GenesisAlloc{
|
|
address: {Balance: funds},
|
|
},
|
|
}
|
|
nonce := uint64(0)
|
|
signer := types.HomesteadSigner{}
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
fee := big.NewInt(1)
|
|
if b.header.BaseFee != nil {
|
|
fee = b.header.BaseFee
|
|
}
|
|
b.SetCoinbase(common.Address{1})
|
|
tx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 100000,
|
|
Data: initCode,
|
|
})
|
|
nonce++
|
|
b.AddTxWithVMConfig(tx, vmConfig)
|
|
|
|
tx, _ = types.SignNewTx(key, signer, &types.LegacyTx{
|
|
Nonce: nonce,
|
|
GasPrice: new(big.Int).Set(fee),
|
|
Gas: 100000,
|
|
To: &destAddress,
|
|
})
|
|
b.AddTxWithVMConfig(tx, vmConfig)
|
|
nonce++
|
|
})
|
|
|
|
// Initialize the blockchain with 1153 enabled.
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vmConfig, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
// Import the blocks
|
|
if _, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("failed to insert into chain: %v", err)
|
|
}
|
|
// Check the storage
|
|
state, err := chain.StateAt(chain.CurrentHeader().Root)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load state %v", err)
|
|
}
|
|
loc := common.BytesToHash([]byte{1})
|
|
slot := state.GetState(destAddress, loc)
|
|
if slot != (common.Hash{}) {
|
|
t.Fatalf("Unexpected dirty storage slot")
|
|
}
|
|
}
|
|
|
|
func TestEIP3651(t *testing.T) {
|
|
var (
|
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
engine = beacon.New(ethash.NewFaker())
|
|
|
|
// A sender who makes transactions, has some funds
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
|
config = *params.AllEthashProtocolChanges
|
|
gspec = &Genesis{
|
|
Config: &config,
|
|
Alloc: types.GenesisAlloc{
|
|
addr1: {Balance: funds},
|
|
addr2: {Balance: funds},
|
|
// The address 0xAAAA sloads 0x00 and 0x01
|
|
aa: {
|
|
Code: []byte{
|
|
byte(vm.PC),
|
|
byte(vm.PC),
|
|
byte(vm.SLOAD),
|
|
byte(vm.SLOAD),
|
|
},
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
// The address 0xBBBB calls 0xAAAA
|
|
bb: {
|
|
Code: []byte{
|
|
byte(vm.PUSH1), 0, // out size
|
|
byte(vm.DUP1), // out offset
|
|
byte(vm.DUP1), // out insize
|
|
byte(vm.DUP1), // in offset
|
|
byte(vm.PUSH2), // address
|
|
byte(0xaa),
|
|
byte(0xaa),
|
|
byte(vm.GAS), // gas
|
|
byte(vm.DELEGATECALL),
|
|
},
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
gspec.Config.BerlinBlock = common.Big0
|
|
gspec.Config.LondonBlock = common.Big0
|
|
gspec.Config.TerminalTotalDifficulty = common.Big0
|
|
gspec.Config.ShanghaiTime = u64(0)
|
|
signer := types.LatestSigner(gspec.Config)
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(aa)
|
|
// One transaction to Coinbase
|
|
txdata := &types.DynamicFeeTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 0,
|
|
To: &bb,
|
|
Gas: 500000,
|
|
GasFeeCap: newGwei(5),
|
|
GasTipCap: big.NewInt(2),
|
|
AccessList: nil,
|
|
Data: []byte{},
|
|
}
|
|
tx := types.NewTx(txdata)
|
|
tx, _ = types.SignTx(tx, signer, key1)
|
|
|
|
b.AddTx(tx)
|
|
})
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
block := chain.GetBlockByNumber(1)
|
|
|
|
// 1+2: Ensure EIP-1559 access lists are accounted for via gas usage.
|
|
innerGas := vm.GasQuickStep*2 + params.ColdSloadCostEIP2929*2
|
|
expectedGas := params.TxGas + 5*vm.GasFastestStep + vm.GasQuickStep + 100 + innerGas // 100 because 0xaaaa is in access list
|
|
if block.GasUsed() != expectedGas {
|
|
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expectedGas, block.GasUsed())
|
|
}
|
|
|
|
state, _ := chain.State()
|
|
|
|
// 3: Ensure that miner received only the tx's tip.
|
|
actual := state.GetBalance(block.Coinbase()).ToBig()
|
|
expected := new(big.Int).SetUint64(block.GasUsed() * block.Transactions()[0].GasTipCap().Uint64())
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("miner balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
|
|
// 4: Ensure the tx sender paid for the gasUsed * (tip + block baseFee).
|
|
actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
|
|
expected = new(big.Int).SetUint64(block.GasUsed() * (block.Transactions()[0].GasTipCap().Uint64() + block.BaseFee().Uint64()))
|
|
if actual.Cmp(expected) != 0 {
|
|
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
|
}
|
|
}
|
|
|
|
// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7
|
|
var depositsGeneratorCode = common.FromHex("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033")
|
|
|
|
// This is a smoke test for EIP-7685 requests added in the Prague fork. The test first
|
|
// creates a block containing requests, and then inserts it into the chain to run
|
|
// validation.
|
|
func TestPragueRequests(t *testing.T) {
|
|
var (
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
config = *params.MergedTestChainConfig
|
|
signer = types.LatestSigner(&config)
|
|
engine = beacon.New(ethash.NewFaker())
|
|
)
|
|
gspec := &Genesis{
|
|
Config: &config,
|
|
Alloc: types.GenesisAlloc{
|
|
addr1: {Balance: big.NewInt(9999900000000000)},
|
|
config.DepositContractAddress: {Code: depositsGeneratorCode},
|
|
params.WithdrawalQueueAddress: {Code: params.WithdrawalQueueCode},
|
|
params.ConsolidationQueueAddress: {Code: params.ConsolidationQueueCode},
|
|
},
|
|
}
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
// create deposit
|
|
depositTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 0,
|
|
To: &config.DepositContractAddress,
|
|
Gas: 500_000,
|
|
GasFeeCap: newGwei(5),
|
|
GasTipCap: big.NewInt(2),
|
|
})
|
|
b.AddTx(depositTx)
|
|
|
|
// create withdrawal request
|
|
withdrawalTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 1,
|
|
To: ¶ms.WithdrawalQueueAddress,
|
|
Gas: 500_000,
|
|
GasFeeCap: newGwei(5),
|
|
GasTipCap: big.NewInt(2),
|
|
Value: newGwei(1),
|
|
Data: common.FromHex("b917cfdc0d25b72d55cf94db328e1629b7f4fde2c30cdacf873b664416f76a0c7f7cc50c9f72a3cb84be88144cde91250000000000000d80"),
|
|
})
|
|
b.AddTx(withdrawalTx)
|
|
|
|
// create consolidation request
|
|
consolidationTx := types.MustSignNewTx(key1, signer, &types.DynamicFeeTx{
|
|
ChainID: gspec.Config.ChainID,
|
|
Nonce: 2,
|
|
To: ¶ms.ConsolidationQueueAddress,
|
|
Gas: 500_000,
|
|
GasFeeCap: newGwei(5),
|
|
GasTipCap: big.NewInt(2),
|
|
Value: newGwei(1),
|
|
Data: common.FromHex("b917cfdc0d25b72d55cf94db328e1629b7f4fde2c30cdacf873b664416f76a0c7f7cc50c9f72a3cb84be88144cde9125b9812f7d0b1f2f969b52bbb2d316b0c2fa7c9dba85c428c5e6c27766bcc4b0c6e874702ff1eb1c7024b08524a9771601"),
|
|
})
|
|
b.AddTx(consolidationTx)
|
|
})
|
|
|
|
// Check block has the correct requests hash.
|
|
rh := blocks[0].RequestsHash()
|
|
if rh == nil {
|
|
t.Fatal("block has nil requests hash")
|
|
}
|
|
expectedRequestsHash := common.HexToHash("0x06ffb72b9f0823510b128bca6cd4f96f59b745de6791e9fc350b596e7605101e")
|
|
if *rh != expectedRequestsHash {
|
|
t.Fatalf("block has wrong requestsHash %v, want %v", *rh, expectedRequestsHash)
|
|
}
|
|
|
|
// Insert block to check validation.
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
}
|
|
|
|
// TestEIP7702 deploys two delegation designations and calls them. It writes one
|
|
// value to storage which is verified after.
|
|
func TestEIP7702(t *testing.T) {
|
|
var (
|
|
config = *params.MergedTestChainConfig
|
|
signer = types.LatestSigner(&config)
|
|
engine = beacon.New(ethash.NewFaker())
|
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
|
)
|
|
gspec := &Genesis{
|
|
Config: &config,
|
|
Alloc: types.GenesisAlloc{
|
|
addr1: {Balance: funds},
|
|
addr2: {Balance: funds},
|
|
aa: { // The address 0xAAAA calls into addr2
|
|
Code: program.New().Call(nil, addr2, 1, 0, 0, 0, 0).Bytes(),
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
bb: { // The address 0xBBBB sstores 42 into slot 42.
|
|
Code: program.New().Sstore(0x42, 0x42).Bytes(),
|
|
Nonce: 0,
|
|
Balance: big.NewInt(0),
|
|
},
|
|
},
|
|
}
|
|
|
|
// Sign authorization tuples.
|
|
// The way the auths are combined, it becomes
|
|
// 1. tx -> addr1 which is delegated to 0xaaaa
|
|
// 2. addr1:0xaaaa calls into addr2:0xbbbb
|
|
// 3. addr2:0xbbbb writes to storage
|
|
auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
|
|
ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
|
|
Address: aa,
|
|
Nonce: 1,
|
|
})
|
|
auth2, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
|
|
Address: bb,
|
|
Nonce: 0,
|
|
})
|
|
|
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
|
b.SetCoinbase(aa)
|
|
txdata := &types.SetCodeTx{
|
|
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
|
|
Nonce: 0,
|
|
To: addr1,
|
|
Gas: 500000,
|
|
GasFeeCap: uint256.MustFromBig(newGwei(5)),
|
|
GasTipCap: uint256.NewInt(2),
|
|
AuthList: []types.SetCodeAuthorization{auth1, auth2},
|
|
}
|
|
tx := types.MustSignNewTx(key1, signer, txdata)
|
|
b.AddTx(tx)
|
|
})
|
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{}, nil)
|
|
if err != nil {
|
|
t.Fatalf("failed to create tester chain: %v", err)
|
|
}
|
|
defer chain.Stop()
|
|
if n, err := chain.InsertChain(blocks); err != nil {
|
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
|
}
|
|
|
|
// Verify delegation designations were deployed.
|
|
state, _ := chain.State()
|
|
code, want := state.GetCode(addr1), types.AddressToDelegation(auth1.Address)
|
|
if !bytes.Equal(code, want) {
|
|
t.Fatalf("addr1 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
|
}
|
|
code, want = state.GetCode(addr2), types.AddressToDelegation(auth2.Address)
|
|
if !bytes.Equal(code, want) {
|
|
t.Fatalf("addr2 code incorrect: got %s, want %s", common.Bytes2Hex(code), common.Bytes2Hex(want))
|
|
}
|
|
// Verify delegation executed the correct code.
|
|
var (
|
|
fortyTwo = common.BytesToHash([]byte{0x42})
|
|
actual = state.GetState(addr2, fortyTwo)
|
|
)
|
|
if actual.Cmp(fortyTwo) != 0 {
|
|
t.Fatalf("addr2 storage wrong: expected %d, got %d", fortyTwo, actual)
|
|
}
|
|
}
|