diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 16c8808360..7de1eb6949 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -304,7 +304,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash(), vmContext.BlockNumber.Uint64(), blockHash) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.Bloom = types.CreateBloom(receipt) + // These three are non-consensus fields: //receipt.BlockHash //receipt.BlockNumber @@ -376,7 +377,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, StateRoot: root, TxRoot: types.DeriveSha(includedTxs, trie.NewStackTrie(nil)), ReceiptRoot: types.DeriveSha(receipts, trie.NewStackTrie(nil)), - Bloom: types.CreateBloom(receipts), + Bloom: types.MergeBloom(receipts), LogsHash: rlpHash(statedb.Logs()), Receipts: receipts, Rejected: rejectedTxs, diff --git a/cmd/evm/testdata/evmrun/3.out.1.txt b/cmd/evm/testdata/evmrun/3.out.1.txt index 44956f54f6..25dd8da5a1 100644 --- a/cmd/evm/testdata/evmrun/3.out.1.txt +++ b/cmd/evm/testdata/evmrun/3.out.1.txt @@ -1,12 +1,13 @@ { "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", "accounts": { - "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "0x0000000000000000000000007265636569766572": { "balance": "0", "nonce": 0, "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", "code": "0x6040", + "address": "0x0000000000000000000000007265636569766572", "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" } } diff --git a/cmd/evm/testdata/evmrun/4.out.1.txt b/cmd/evm/testdata/evmrun/4.out.1.txt index 44956f54f6..25dd8da5a1 100644 --- a/cmd/evm/testdata/evmrun/4.out.1.txt +++ b/cmd/evm/testdata/evmrun/4.out.1.txt @@ -1,12 +1,13 @@ { "root": "b444481d1367188172f8c6091e948aaa68bae763fd26d6b9e994306a66bf69f9", "accounts": { - "pre(0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142)": { + "0x0000000000000000000000007265636569766572": { "balance": "0", "nonce": 0, "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "codeHash": "0x3e48ef54b89079a075f3b8fc253c657a86b110a7aed3568c1517b10edf2c3eb6", "code": "0x6040", + "address": "0x0000000000000000000000007265636569766572", "key": "0x30d7a0694cb29af31b982480e11d7ebb003a3fca4026939149071f014689b142" } } diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index bdfddfff06..cc9f44e460 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -61,8 +61,7 @@ var ( // is only used for necessary consensus checks. The legacy consensus engine can be any // engine implements the consensus interface (except the beacon itself). type Beacon struct { - ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique - ttdblock *uint64 // Merge block-number for testchain generation without TTDs + ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique } // New creates a consensus engine with the given embedded eth1 engine. @@ -73,16 +72,14 @@ func New(ethone consensus.Engine) *Beacon { return &Beacon{ethone: ethone} } -// TestingTTDBlock is a replacement mechanism for TTD-based pre-/post-merge -// splitting. With chain history deletion, TD calculations become impossible. -// This is fine for progressing the live chain, but to be able to generate test -// chains, we do need a split point. This method supports setting an explicit -// block number to use as the splitter *for testing*, instead of having to keep -// the notion of TDs in the client just for testing. -// -// The block with supplied number is regarded as the last pre-merge block. -func (beacon *Beacon) TestingTTDBlock(number uint64) { - beacon.ttdblock = &number +// isPostMerge reports whether the given block number is assumed to be post-merge. +// Here we check the MergeNetsplitBlock to allow configuring networks with a PoW or +// PoA chain for unit testing purposes. +func isPostMerge(config *params.ChainConfig, blockNum uint64, timestamp uint64) bool { + mergedAtGenesis := config.TerminalTotalDifficulty != nil && config.TerminalTotalDifficulty.Sign() == 0 + return mergedAtGenesis || + config.MergeNetsplitBlock != nil && blockNum >= config.MergeNetsplitBlock.Uint64() || + config.ShanghaiTime != nil && timestamp >= *config.ShanghaiTime } // Author implements consensus.Engine, returning the verified author of the block. @@ -332,15 +329,7 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [ // Prepare implements consensus.Engine, initializing the difficulty field of a // header to conform to the beacon protocol. The changes are done inline. func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - // The beacon engine requires access to total difficulties to be able to - // seal pre-merge and post-merge blocks. With the transition to removing - // old blocks, TDs become unaccessible, thus making TTD based pre-/post- - // merge decisions impossible. - // - // We do not need to seal non-merge blocks anymore live, but we do need - // to be able to generate test chains, thus we're reverting to a testing- - // settable field to direct that. - if beacon.ttdblock != nil && *beacon.ttdblock >= header.Number.Uint64() { + if !isPostMerge(chain.Config(), header.Number.Uint64(), header.Time) { return beacon.ethone.Prepare(chain, header) } header.Difficulty = beaconDifficulty @@ -450,15 +439,7 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash { // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - // The beacon engine requires access to total difficulties to be able to - // seal pre-merge and post-merge blocks. With the transition to removing - // old blocks, TDs become unaccessible, thus making TTD based pre-/post- - // merge decisions impossible. - // - // We do not need to seal non-merge blocks anymore live, but we do need - // to be able to generate test chains, thus we're reverting to a testing- - // settable field to direct that. - if beacon.ttdblock != nil && *beacon.ttdblock > parent.Number.Uint64() { + if !isPostMerge(chain.Config(), parent.Number.Uint64()+1, time) { return beacon.ethone.CalcDifficulty(chain, time, parent) } return beaconDifficulty diff --git a/core/block_validator.go b/core/block_validator.go index 5885df9ee2..591e472bc1 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -129,7 +129,11 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. - rbloom := types.CreateBloom(res.Receipts) + // + // Receipts must go through MakeReceipt to calculate the receipt's bloom + // already. Merge the receipt's bloom together instead of recalculating + // everything. + rbloom := types.MergeBloom(res.Receipts) if rbloom != header.Bloom { return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) } diff --git a/core/genesis.go b/core/genesis.go index 8546f4e37e..4dd6c9ed24 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -364,6 +364,11 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g } newCfg := genesis.chainConfigOrDefault(ghash, storedCfg) + // Sanity-check the new configuration. + if err := newCfg.CheckConfigForkOrder(); err != nil { + return nil, common.Hash{}, nil, err + } + // TODO(rjl493456442) better to define the comparator of chain config // and short circuit if the chain config is not changed. compatErr := storedCfg.CheckCompatible(newCfg, head.Number.Uint64(), head.Time) diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go index b9684f8e17..efd16d5fa7 100644 --- a/core/rawdb/accessors_chain_test.go +++ b/core/rawdb/accessors_chain_test.go @@ -338,7 +338,7 @@ func TestBlockReceiptStorage(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), GasUsed: 111111, } - receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1}) + receipt1.Bloom = types.CreateBloom(receipt1) receipt2 := &types.Receipt{ PostState: common.Hash{2}.Bytes(), @@ -351,7 +351,7 @@ func TestBlockReceiptStorage(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), GasUsed: 222222, } - receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2}) + receipt2.Bloom = types.CreateBloom(receipt2) receipts := []*types.Receipt{receipt1, receipt2} // Check that no receipt entries are in a pristine database @@ -679,7 +679,7 @@ func TestReadLogs(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), GasUsed: 111111, } - receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1}) + receipt1.Bloom = types.CreateBloom(receipt1) receipt2 := &types.Receipt{ PostState: common.Hash{2}.Bytes(), @@ -692,7 +692,7 @@ func TestReadLogs(t *testing.T) { ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), GasUsed: 222222, } - receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2}) + receipt2.Bloom = types.CreateBloom(receipt2) receipts := []*types.Receipt{receipt1, receipt2} hash := common.BytesToHash([]byte{0x03, 0x14}) diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go index 1ba8cf639f..e553df7029 100644 --- a/core/rawdb/freezer_table.go +++ b/core/rawdb/freezer_table.go @@ -405,6 +405,13 @@ func (t *freezerTable) repairIndex() error { // If legacy metadata is detected, attempt to recover the offset from the // index file to avoid clearing the entire table. if t.metadata.version == freezerTableV1 { + // Skip truncation if the legacy metadata is opened in read-only mode. + // Since all items in the legacy index file were forcibly synchronized, + // data integrity is guaranteed. Therefore, it's safe to leave any extra + // items untruncated in this special scenario. + if t.readonly { + return nil + } t.logger.Info("Recovering freezer flushOffset for legacy table", "offset", size) return t.metadata.setFlushOffset(size, true) } diff --git a/core/state_processor.go b/core/state_processor.go index 902ff582e8..2c74f6f407 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -189,7 +189,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b // Set the receipt logs and create the bloom filter. receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.Bloom = types.CreateBloom(receipt) receipt.BlockHash = blockHash receipt.BlockNumber = blockNumber receipt.TransactionIndex = uint(statedb.TxIndex()) diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go index f7c9e4844b..8589e5c01d 100644 --- a/core/txpool/blobpool/blobpool_test.go +++ b/core/txpool/blobpool/blobpool_test.go @@ -142,6 +142,7 @@ func (bc *testBlockChain) CurrentBlock() *types.Header { GasLimit: gasLimit, BaseFee: baseFee, ExcessBlobGas: &excessBlobGas, + Difficulty: common.Big0, } } @@ -1565,8 +1566,9 @@ func TestAdd(t *testing.T) { if tt.block != nil { // Fake a header for the new set of transactions header := &types.Header{ - Number: big.NewInt(int64(chain.CurrentBlock().Number.Uint64() + 1)), - BaseFee: chain.CurrentBlock().BaseFee, // invalid, but nothing checks it, yolo + Number: big.NewInt(int64(chain.CurrentBlock().Number.Uint64() + 1)), + Difficulty: common.Big0, + BaseFee: chain.CurrentBlock().BaseFee, // invalid, but nothing checks it, yolo } // Inject the fake block into the chain txs := make([]*types.Transaction, len(tt.block)) diff --git a/core/txpool/errors.go b/core/txpool/errors.go index 3a6a913976..32e49db87c 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -60,4 +60,13 @@ var ( // input transaction of non-blob type when a blob transaction from this sender // remains pending (and vice-versa). ErrAlreadyReserved = errors.New("address already reserved") + + // ErrAuthorityReserved is returned if a transaction has an authorization + // signed by an address which already has in-flight transactions known to the + // pool. + ErrAuthorityReserved = errors.New("authority already reserved") + + // ErrAuthorityNonce is returned if a transaction has an authorization with + // a nonce that is not currently valid for the authority. + ErrAuthorityNonceTooLow = errors.New("authority nonce too low") ) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 1440af5440..c7ff954a49 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -21,6 +21,7 @@ import ( "errors" "math" "math/big" + "slices" "sort" "sync" "sync/atomic" @@ -196,6 +197,20 @@ func (config *Config) sanitize() Config { // The pool separates processable transactions (which can be applied to the // current state) and future transactions. Transactions move between those // two states over time as they are received and processed. +// +// In addition to tracking transactions, the pool also tracks a set of pending SetCode +// authorizations (EIP7702). This helps minimize number of transactions that can be +// trivially churned in the pool. As a standard rule, any account with a deployed +// delegation or an in-flight authorization to deploy a delegation will only be allowed a +// single transaction slot instead of the standard number. This is due to the possibility +// of the account being sweeped by an unrelated account. +// +// Because SetCode transactions can have many authorizations included, we avoid explicitly +// checking their validity to save the state lookup. So long as the encompassing +// transaction is valid, the authorization will be accepted and tracked by the pool. In +// case the pool is tracking a pending / queued transaction from a specific account, it +// will reject new transactions with delegations from that account with standard in-flight +// transactions. type LegacyPool struct { config Config chainconfig *params.ChainConfig @@ -263,7 +278,7 @@ func New(config Config, chain BlockChain) *LegacyPool { // pool, specifically, whether it is a Legacy, AccessList or Dynamic transaction. func (pool *LegacyPool) Filter(tx *types.Transaction) bool { switch tx.Type() { - case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType: + case types.LegacyTxType, types.AccessListTxType, types.DynamicFeeTxType, types.SetCodeTxType: return true default: return false @@ -540,7 +555,8 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction) error { Accept: 0 | 1<= 0 { + list = append(list[:i], list[i+1:]...) + } else { + log.Error("Authority with untracked tx", "addr", addr, "hash", hash) + } + if len(list) == 0 { + // If list is newly empty, delete it entirely. + delete(t.auths, addr) + continue + } + t.auths[addr] = list + } +} + // numSlots calculates the number of slots needed for a single transaction. func numSlots(tx *types.Transaction) int { return int((tx.Size() + txSlotSize - 1) / txSlotSize) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index fdbcad3d4f..da28e8a75b 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -34,6 +34,7 @@ import ( "github.com/ethereum/go-ethereum/core/tracing" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" @@ -79,8 +80,9 @@ func (bc *testBlockChain) Config() *params.ChainConfig { func (bc *testBlockChain) CurrentBlock() *types.Header { return &types.Header{ - Number: new(big.Int), - GasLimit: bc.gasLimit.Load(), + Number: new(big.Int), + Difficulty: common.Big0, + GasLimit: bc.gasLimit.Load(), } } @@ -128,6 +130,39 @@ func dynamicFeeTx(nonce uint64, gaslimit uint64, gasFee *big.Int, tip *big.Int, return tx } +type unsignedAuth struct { + nonce uint64 + key *ecdsa.PrivateKey +} + +func setCodeTx(nonce uint64, key *ecdsa.PrivateKey, unsigned []unsignedAuth) *types.Transaction { + return pricedSetCodeTx(nonce, 250000, uint256.NewInt(1000), uint256.NewInt(1), key, unsigned) +} + +func pricedSetCodeTx(nonce uint64, gaslimit uint64, gasFee, tip *uint256.Int, key *ecdsa.PrivateKey, unsigned []unsignedAuth) *types.Transaction { + var authList []types.SetCodeAuthorization + for _, u := range unsigned { + auth, _ := types.SignSetCode(u.key, types.SetCodeAuthorization{ + ChainID: *uint256.MustFromBig(params.TestChainConfig.ChainID), + Address: common.Address{0x42}, + Nonce: u.nonce, + }) + authList = append(authList, auth) + } + return types.MustSignNewTx(key, types.LatestSignerForChainID(params.TestChainConfig.ChainID), &types.SetCodeTx{ + ChainID: uint256.MustFromBig(params.TestChainConfig.ChainID), + Nonce: nonce, + GasTipCap: tip, + GasFeeCap: gasFee, + Gas: gaslimit, + To: common.Address{}, + Value: uint256.NewInt(100), + Data: nil, + AccessList: nil, + AuthList: authList, + }) +} + func makeAddressReserver() txpool.AddressReserver { var ( reserved = make(map[common.Address]struct{}) @@ -2163,6 +2198,201 @@ func TestSlotCount(t *testing.T) { } } +// TestSetCodeTransactions tests a few scenarios regarding the EIP-7702 +// SetCodeTx. +func TestSetCodeTransactions(t *testing.T) { + t.Parallel() + + // Create the pool to test the status retrievals with + statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting()) + blockchain := newTestBlockChain(params.MergedTestChainConfig, 1000000, statedb, new(event.Feed)) + + pool := New(testTxPoolConfig, blockchain) + pool.Init(testTxPoolConfig.PriceLimit, blockchain.CurrentBlock(), makeAddressReserver()) + defer pool.Close() + + // Create the test accounts + var ( + keyA, _ = crypto.GenerateKey() + keyB, _ = crypto.GenerateKey() + keyC, _ = crypto.GenerateKey() + addrA = crypto.PubkeyToAddress(keyA.PublicKey) + addrB = crypto.PubkeyToAddress(keyB.PublicKey) + addrC = crypto.PubkeyToAddress(keyC.PublicKey) + ) + testAddBalance(pool, addrA, big.NewInt(params.Ether)) + testAddBalance(pool, addrB, big.NewInt(params.Ether)) + testAddBalance(pool, addrC, big.NewInt(params.Ether)) + + for _, tt := range []struct { + name string + pending int + queued int + run func(string) + }{ + { + // Check that only one in-flight transaction is allowed for accounts + // with delegation set. Also verify the accepted transaction can be + // replaced by fee. + name: "only-one-in-flight", + pending: 1, + run: func(name string) { + aa := common.Address{0xaa, 0xaa} + statedb.SetCode(addrA, append(types.DelegationPrefix, aa.Bytes()...)) + statedb.SetCode(aa, []byte{byte(vm.ADDRESS), byte(vm.PUSH0), byte(vm.SSTORE)}) + // Send transactions. First is accepted, second is rejected. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyA)); err != nil { + t.Fatalf("%s: failed to add remote transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + } + // Also check gapped transaction. + if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), keyA)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + } + // Replace by fee. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyA)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + }, + }, + { + name: "allow-setcode-tx-with-pending-authority-tx", + pending: 2, + run: func(name string) { + // Send two transactions where the first has no conflicting delegations and + // the second should be allowed despite conflicting with the authorities in 1). + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { + t.Fatalf("%s: failed to add conflicting delegation: %v", name, err) + } + }, + }, + { + name: "allow-one-tx-from-pooled-delegation", + pending: 2, + run: func(name string) { + // Verify C cannot originate another transaction when it has a pooled delegation. + if err := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{0, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1), keyC)); err != nil { + t.Fatalf("%s: failed to add with pending delegatio: %v", name, err) + } + // Also check gapped transaction is rejected. + if err := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1), keyC)); !errors.Is(err, txpool.ErrAccountLimitExceeded) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, txpool.ErrAccountLimitExceeded, err) + } + }, + }, + { + name: "replace-by-fee-setcode-tx", + pending: 1, + run: func(name string) { + // 4. Fee bump the setcode tx send. + if err := pool.addRemoteSync(setCodeTx(0, keyB, []unsignedAuth{{1, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(2000), uint256.NewInt(2), keyB, []unsignedAuth{{0, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + }, + }, + { + name: "allow-tx-from-replaced-authority", + pending: 2, + run: func(name string) { + // Fee bump with a different auth list. Make sure that unlocks the authorities. + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyB}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(3000), uint256.NewInt(300), keyA, []unsignedAuth{{0, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + // Now send a regular tx from B. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(10), keyB)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + }, + }, + { + name: "allow-tx-from-replaced-self-sponsor-authority", + pending: 2, + run: func(name string) { + // + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyA}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(30), uint256.NewInt(30), keyA, []unsignedAuth{{0, keyB}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + // Now send a regular tx from keyA. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + // Make sure we can still send from keyB. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyB)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + }, + }, + { + name: "track-multiple-conflicting-delegations", + pending: 3, + run: func(name string) { + // Send two setcode txs both with C as an authority. + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(10), uint256.NewInt(3), keyA, []unsignedAuth{{0, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err := pool.addRemoteSync(pricedSetCodeTx(0, 250000, uint256.NewInt(30), uint256.NewInt(30), keyB, []unsignedAuth{{0, keyC}})); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + // Replace the tx from A with a non-setcode tx. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyA)); err != nil { + t.Fatalf("%s: failed to replace with remote transaction: %v", name, err) + } + // Make sure we can only pool one tx from keyC since it is still a + // pending authority. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { + t.Fatalf("%s: failed to added single pooled for account with pending delegation: %v", name, err) + } + if err, want := pool.addRemoteSync(pricedTransaction(1, 100000, big.NewInt(1000), keyC)), txpool.ErrAccountLimitExceeded; !errors.Is(err, want) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) + } + }, + }, + { + name: "reject-delegation-from-pending-account", + pending: 1, + run: func(name string) { + // Attempt to submit a delegation from an account with a pending tx. + if err := pool.addRemoteSync(pricedTransaction(0, 100000, big.NewInt(1000), keyC)); err != nil { + t.Fatalf("%s: failed to add with remote setcode transaction: %v", name, err) + } + if err, want := pool.addRemoteSync(setCodeTx(0, keyA, []unsignedAuth{{1, keyC}})), txpool.ErrAuthorityReserved; !errors.Is(err, want) { + t.Fatalf("%s: error mismatch: want %v, have %v", name, want, err) + } + }, + }, + } { + tt.run(tt.name) + pending, queued := pool.Stats() + if pending != tt.pending { + t.Fatalf("%s: pending transactions mismatched: have %d, want %d", tt.name, pending, tt.pending) + } + if queued != tt.queued { + t.Fatalf("%s: queued transactions mismatched: have %d, want %d", tt.name, queued, tt.queued) + } + if err := validatePoolInternals(pool); err != nil { + t.Fatalf("%s: pool internal state corrupted: %v", tt.name, err) + } + pool.Clear() + } +} + // Benchmarks the speed of validating the contents of the pending queue of the // transaction pool. func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 8cd13776a6..4d53c386b6 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -70,17 +70,21 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types return fmt.Errorf("%w: transaction size %v, limit %v", ErrOversizedData, tx.Size(), opts.MaxSize) } // Ensure only transactions that have been enabled are accepted - if !opts.Config.IsBerlin(head.Number) && tx.Type() != types.LegacyTxType { + rules := opts.Config.Rules(head.Number, head.Difficulty.Sign() == 0, head.Time) + if !rules.IsBerlin && tx.Type() != types.LegacyTxType { return fmt.Errorf("%w: type %d rejected, pool not yet in Berlin", core.ErrTxTypeNotSupported, tx.Type()) } - if !opts.Config.IsLondon(head.Number) && tx.Type() == types.DynamicFeeTxType { + if !rules.IsLondon && tx.Type() == types.DynamicFeeTxType { return fmt.Errorf("%w: type %d rejected, pool not yet in London", core.ErrTxTypeNotSupported, tx.Type()) } - if !opts.Config.IsCancun(head.Number, head.Time) && tx.Type() == types.BlobTxType { + if !rules.IsCancun && tx.Type() == types.BlobTxType { return fmt.Errorf("%w: type %d rejected, pool not yet in Cancun", core.ErrTxTypeNotSupported, tx.Type()) } + if !rules.IsPrague && tx.Type() == types.SetCodeTxType { + return fmt.Errorf("%w: type %d rejected, pool not yet in Prague", core.ErrTxTypeNotSupported, tx.Type()) + } // Check whether the init code size has been exceeded - if opts.Config.IsShanghai(head.Number, head.Time) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { + if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) } // Transactions can't be negative. This may never happen using RLP decoded @@ -109,7 +113,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time)) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) if err != nil { return err } @@ -154,6 +158,11 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types return err } } + if tx.Type() == types.SetCodeTxType { + if len(tx.SetCodeAuthorizations()) == 0 { + return fmt.Errorf("set code tx must have at least one authorization tuple") + } + } return nil } @@ -209,6 +218,11 @@ type ValidationOptionsWithState struct { // ExistingCost is a mandatory callback to retrieve an already pooled // transaction's cost with the given nonce to check for overdrafts. ExistingCost func(addr common.Address, nonce uint64) *big.Int + + // KnownConflicts is an optional callback which iterates over the list of + // addresses and returns all addresses known to the pool with in-flight + // transactions. + KnownConflicts func(sender common.Address, authorizers []common.Address) []common.Address } // ValidateTransactionWithState is a helper method to check whether a transaction @@ -262,6 +276,14 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op if used, left := opts.UsedAndLeftSlots(from); left <= 0 { return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used) } + + // Verify no authorizations will invalidate existing transactions known to + // the pool. + if opts.KnownConflicts != nil { + if conflicts := opts.KnownConflicts(from, tx.SetCodeAuthorities()); len(conflicts) > 0 { + return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved) + } + } } return nil } diff --git a/core/types/block.go b/core/types/block.go index c3db4d89e8..b284fb3b16 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -237,6 +237,9 @@ type extblock struct { // // The body elements and the receipts are used to recompute and overwrite the // relevant portions of the header. +// +// The receipt's bloom must already calculated for the block's bloom to be +// correctly calculated. func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block { if body == nil { body = &Body{} @@ -260,7 +263,10 @@ func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher b.header.ReceiptHash = EmptyReceiptsHash } else { b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher) - b.header.Bloom = CreateBloom(receipts) + // Receipts must go through MakeReceipt to calculate the receipt's bloom + // already. Merge the receipt's bloom together instead of recalculating + // everything. + b.header.Bloom = MergeBloom(receipts) } if len(uncles) == 0 { diff --git a/core/types/bloom9.go b/core/types/bloom9.go index a560a20724..962ba46d47 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -100,32 +100,35 @@ func (b *Bloom) UnmarshalText(input []byte) error { return hexutil.UnmarshalFixedText("Bloom", input, b[:]) } -// CreateBloom creates a bloom filter out of the give Receipts (+Logs) -func CreateBloom(receipts Receipts) Bloom { - buf := make([]byte, 6) - var bin Bloom - for _, receipt := range receipts { - for _, log := range receipt.Logs { - bin.add(log.Address.Bytes(), buf) - for _, b := range log.Topics { - bin.add(b[:], buf) - } - } - } - return bin -} - -// LogsBloom returns the bloom bytes for the given logs -func LogsBloom(logs []*Log) []byte { - buf := make([]byte, 6) - var bin Bloom - for _, log := range logs { +// CreateBloom creates a bloom filter out of the give Receipt (+Logs) +func CreateBloom(receipt *Receipt) Bloom { + var ( + bin Bloom + buf = make([]byte, 6) + ) + for _, log := range receipt.Logs { bin.add(log.Address.Bytes(), buf) for _, b := range log.Topics { bin.add(b[:], buf) } } - return bin[:] + return bin +} + +// MergeBloom merges the precomputed bloom filters in the Receipts without +// recalculating them. It assumes that each receipt’s Bloom field is already +// correctly populated. +func MergeBloom(receipts Receipts) Bloom { + var bin Bloom + for _, receipt := range receipts { + if len(receipt.Logs) != 0 { + bl := receipt.Bloom.Bytes() + for i := range bin { + bin[i] |= bl[i] + } + } + } + return bin } // Bloom9 returns the bloom filter for the given data diff --git a/core/types/bloom9_test.go b/core/types/bloom9_test.go index d3178d112e..07f6446a97 100644 --- a/core/types/bloom9_test.go +++ b/core/types/bloom9_test.go @@ -126,26 +126,70 @@ func BenchmarkCreateBloom(b *testing.B) { for i := 0; i < 200; i += 2 { copy(rLarge[i:], rSmall) } - b.Run("small", func(b *testing.B) { + b.Run("small-createbloom", func(b *testing.B) { b.ReportAllocs() - var bl Bloom for i := 0; i < b.N; i++ { - bl = CreateBloom(rSmall) + for _, receipt := range rSmall { + receipt.Bloom = CreateBloom(receipt) + } } b.StopTimer() + + bl := MergeBloom(rSmall) var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) if got != exp { b.Errorf("Got %x, exp %x", got, exp) } }) - b.Run("large", func(b *testing.B) { + b.Run("large-createbloom", func(b *testing.B) { b.ReportAllocs() - var bl Bloom for i := 0; i < b.N; i++ { - bl = CreateBloom(rLarge) + for _, receipt := range rLarge { + receipt.Bloom = CreateBloom(receipt) + } } b.StopTimer() + + bl := MergeBloom(rLarge) + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) + b.Run("small-mergebloom", func(b *testing.B) { + for _, receipt := range rSmall { + receipt.Bloom = CreateBloom(receipt) + } + b.ReportAllocs() + b.ResetTimer() + + var bl Bloom + for i := 0; i < b.N; i++ { + bl = MergeBloom(rSmall) + } + b.StopTimer() + + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") + got := crypto.Keccak256Hash(bl.Bytes()) + if got != exp { + b.Errorf("Got %x, exp %x", got, exp) + } + }) + b.Run("large-mergebloom", func(b *testing.B) { + for _, receipt := range rLarge { + receipt.Bloom = CreateBloom(receipt) + } + b.ReportAllocs() + b.ResetTimer() + + var bl Bloom + for i := 0; i < b.N; i++ { + bl = MergeBloom(rLarge) + } + b.StopTimer() + var exp = common.HexToHash("c384c56ece49458a427c67b90fefe979ebf7104795be65dc398b280f24104949") got := crypto.Keccak256Hash(bl.Bytes()) if got != exp { diff --git a/core/types/receipt.go b/core/types/receipt.go index 47f16d950a..e52a0c6477 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -291,7 +291,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { } r.CumulativeGasUsed = stored.CumulativeGasUsed r.Logs = stored.Logs - r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + r.Bloom = CreateBloom((*Receipt)(r)) return nil } diff --git a/core/types/receipt_test.go b/core/types/receipt_test.go index fc51eb11a5..78b43c7e49 100644 --- a/core/types/receipt_test.go +++ b/core/types/receipt_test.go @@ -394,7 +394,7 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) { func TestReceiptMarshalBinary(t *testing.T) { // Legacy Receipt - legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt}) + legacyReceipt.Bloom = CreateBloom(legacyReceipt) have, err := legacyReceipt.MarshalBinary() if err != nil { t.Fatalf("marshal binary error: %v", err) @@ -421,7 +421,7 @@ func TestReceiptMarshalBinary(t *testing.T) { // 2930 Receipt buf.Reset() - accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt}) + accessListReceipt.Bloom = CreateBloom(accessListReceipt) have, err = accessListReceipt.MarshalBinary() if err != nil { t.Fatalf("marshal binary error: %v", err) @@ -439,7 +439,7 @@ func TestReceiptMarshalBinary(t *testing.T) { // 1559 Receipt buf.Reset() - eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt}) + eip1559Receipt.Bloom = CreateBloom(eip1559Receipt) have, err = eip1559Receipt.MarshalBinary() if err != nil { t.Fatalf("marshal binary error: %v", err) @@ -463,7 +463,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) { if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil { t.Fatalf("unmarshal binary error: %v", err) } - legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt}) + legacyReceipt.Bloom = CreateBloom(legacyReceipt) if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) { t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt) } @@ -474,7 +474,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) { if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil { t.Fatalf("unmarshal binary error: %v", err) } - accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt}) + accessListReceipt.Bloom = CreateBloom(accessListReceipt) if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) { t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt) } @@ -485,7 +485,7 @@ func TestReceiptUnmarshalBinary(t *testing.T) { if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil { t.Fatalf("unmarshal binary error: %v", err) } - eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt}) + eip1559Receipt.Bloom = CreateBloom(eip1559Receipt) if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) { t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt) } diff --git a/core/types/transaction.go b/core/types/transaction.go index 35dc1ea3f9..7df13e04bb 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -483,6 +483,21 @@ func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization { return setcodetx.AuthList } +// SetCodeAuthorities returns a list of each authorization's corresponding authority. +func (tx *Transaction) SetCodeAuthorities() []common.Address { + setcodetx, ok := tx.inner.(*SetCodeTx) + if !ok { + return nil + } + auths := make([]common.Address, 0, len(setcodetx.AuthList)) + for _, auth := range setcodetx.AuthList { + if addr, err := auth.Authority(); err == nil { + auths = append(auths, addr) + } + } + return auths +} + // SetTime sets the decoding time of a transaction. This is used by tests to set // arbitrary times and by persistent transaction pools when loading old txs from // disk. diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index 8fc3361192..e91e07d05d 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -64,9 +64,12 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { engine := beacon.New(ethash.NewFaker()) if merged { config.TerminalTotalDifficulty = common.Big0 + config.MergeNetsplitBlock = common.Big0 } else { - engine.TestingTTDBlock(uint64(n)) + // When !merged, the tests expect the next block after the generated chain to be in PoS. + config.MergeNetsplitBlock = big.NewInt(int64(n + 1)) } + genesis := &core.Genesis{ Config: &config, Alloc: types.GenesisAlloc{ diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index 6a3057326d..e2790d91eb 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -42,7 +42,7 @@ func makeReceipt(addr common.Address) *types.Receipt { receipt.Logs = []*types.Log{ {Address: addr}, } - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) + receipt.Bloom = types.CreateBloom(receipt) return receipt } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index 0a32c278cb..8e6524446f 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -147,11 +147,11 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe config.LondonBlock = londonBlock config.ArrowGlacierBlock = londonBlock config.GrayGlacierBlock = londonBlock - + if cancunBlock != nil { + // Enable the merge with cancun fork. + config.MergeNetsplitBlock = cancunBlock + } engine := beacon.New(ethash.NewFaker()) - engine.TestingTTDBlock(testHead + 1) - - td := params.GenesisDifficulty.Uint64() if cancunBlock != nil { ts := gspec.Timestamp + cancunBlock.Uint64()*10 // fixed 10 sec block time in blockgen @@ -209,10 +209,9 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe b.AddTx(types.MustSignNewTx(key, signer, blobTx)) } } - td += b.Difficulty().Uint64() }) + // Construct testing chain - gspec.Config.TerminalTotalDifficulty = new(big.Int).SetUint64(td) chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil) if err != nil { t.Fatalf("Failed to create local chain, %v", err) diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 5b35a2f935..f92599dba7 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -74,9 +74,7 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, generator func(int, config = params.TestChainConfig engine = beacon.New(ethash.NewFaker()) ) - if !shanghai { - engine.TestingTTDBlock(math.MaxUint64) - } else { + if shanghai { config = ¶ms.ChainConfig{ ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 3d54ab1868..57ba628b78 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -75,8 +75,6 @@ func TestSupplyOmittedFields(t *testing.T) { } ) - gspec.Config.TerminalTotalDifficulty = big.NewInt(0) - out, _, err := testSupplyTracer(t, gspec, func(b *core.BlockGen) { b.SetPoS() }) @@ -546,7 +544,6 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { engine := beacon.New(ethash.NewFaker()) - engine.TestingTTDBlock(1) traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") diff --git a/go.mod b/go.mod index fc768460c7..cf9fda50c8 100644 --- a/go.mod +++ b/go.mod @@ -59,7 +59,7 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/status-im/keycard-go v0.2.0 github.com/stretchr/testify v1.9.0 - github.com/supranational/blst v0.3.13 + github.com/supranational/blst v0.3.14 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/urfave/cli/v2 v2.25.7 go.uber.org/automaxprocs v1.5.2 diff --git a/go.sum b/go.sum index 82836a5d8c..9812cbbab5 100644 --- a/go.sum +++ b/go.sum @@ -508,8 +508,8 @@ github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= -github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo= +github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go index 3e6a042dbb..0f6ba10b90 100644 --- a/graphql/graphql_test.go +++ b/graphql/graphql_test.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "io" - "math" "math/big" "net/http" "strings" @@ -459,15 +458,13 @@ func newGQLService(t *testing.T, stack *node.Node, shanghai bool, gspec *core.Ge var engine = beacon.New(ethash.NewFaker()) if shanghai { gspec.Config.TerminalTotalDifficulty = common.Big0 + gspec.Config.MergeNetsplitBlock = common.Big0 // GenerateChain will increment timestamps by 10. // Shanghai upgrade at block 1. shanghaiTime := uint64(5) gspec.Config.ShanghaiTime = &shanghaiTime - } else { - // set an arbitrary large ttd as chains are required to be known to be merged - gspec.Config.TerminalTotalDifficulty = big.NewInt(math.MaxInt64) - engine.TestingTTDBlock(math.MaxUint64) } + ethBackend, err := eth.New(stack, ethConf) if err != nil { t.Fatalf("could not create eth backend: %v", err) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c0b37c516b..92249f7829 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1675,7 +1675,7 @@ func (api *DebugAPI) GetRawHeader(ctx context.Context, blockNrOrHash rpc.BlockNu hash = h } else { block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash) - if err != nil { + if block == nil || err != nil { return nil, err } hash = block.Hash() @@ -1694,7 +1694,7 @@ func (api *DebugAPI) GetRawBlock(ctx context.Context, blockNrOrHash rpc.BlockNum hash = h } else { block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash) - if err != nil { + if block == nil || err != nil { return nil, err } hash = block.Hash() @@ -1713,7 +1713,7 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block hash = h } else { block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash) - if err != nil { + if block == nil || err != nil { return nil, err } hash = block.Hash() diff --git a/p2p/discover/common.go b/p2p/discover/common.go index cabc5c7962..767cc23b92 100644 --- a/p2p/discover/common.go +++ b/p2p/discover/common.go @@ -49,8 +49,9 @@ type Config struct { // All remaining settings are optional. // Packet handling configuration: - NetRestrict *netutil.Netlist // list of allowed IP networks - Unhandled chan<- ReadPacket // unhandled packets are sent on this channel + NetRestrict *netutil.Netlist // list of allowed IP networks + Unhandled chan<- ReadPacket // unhandled packets are sent on this channel + V5RespTimeout time.Duration // timeout for v5 queries // Node table configuration: Bootnodes []*enode.Node // list of bootstrap nodes @@ -73,6 +74,9 @@ func (cfg Config) withDefaults() Config { if cfg.RefreshInterval == 0 { cfg.RefreshInterval = 30 * time.Minute } + if cfg.V5RespTimeout == 0 { + cfg.V5RespTimeout = 700 * time.Millisecond + } // Debug/test settings: if cfg.Log == nil { diff --git a/p2p/discover/table_reval.go b/p2p/discover/table_reval.go index 2465fee906..1519313d19 100644 --- a/p2p/discover/table_reval.go +++ b/p2p/discover/table_reval.go @@ -79,7 +79,7 @@ func (tr *tableRevalidation) nodeEndpointChanged(tab *Table, n *tableNode) { func (tr *tableRevalidation) run(tab *Table, now mclock.AbsTime) (nextTime mclock.AbsTime) { reval := func(list *revalidationList) { if list.nextTime <= now { - if n := list.get(now, &tab.rand, tr.activeReq); n != nil { + if n := list.get(&tab.rand, tr.activeReq); n != nil { tr.startRequest(tab, n) } // Update nextTime regardless if any requests were started because @@ -203,7 +203,7 @@ type revalidationList struct { } // get returns a random node from the queue. Nodes in the 'exclude' map are not returned. -func (list *revalidationList) get(now mclock.AbsTime, rand randomSource, exclude map[enode.ID]struct{}) *tableNode { +func (list *revalidationList) get(rand randomSource, exclude map[enode.ID]struct{}) *tableNode { if len(list.nodes) == 0 { return nil } diff --git a/p2p/discover/v5_udp.go b/p2p/discover/v5_udp.go index c2d418545a..f840c983f1 100644 --- a/p2p/discover/v5_udp.go +++ b/p2p/discover/v5_udp.go @@ -43,9 +43,6 @@ const ( lookupRequestLimit = 3 // max requests against a single node during lookup findnodeResultLimit = 16 // applies in FINDNODE handler totalNodesResponseLimit = 5 // applies in waitForNodes - - // change to a configurable value - respTimeoutV5 = 3 * time.Second ) // codecV5 is implemented by v5wire.Codec (and testCodec). @@ -74,6 +71,7 @@ type UDPv5 struct { log log.Logger clock mclock.Clock validSchemes enr.IdentityScheme + respTimeout time.Duration // misc buffers used during message handling logcontext []interface{} @@ -163,6 +161,7 @@ func newUDPv5(conn UDPConn, ln *enode.LocalNode, cfg Config) (*UDPv5, error) { log: cfg.Log, validSchemes: cfg.ValidSchemes, clock: cfg.Clock, + respTimeout: cfg.V5RespTimeout, // channels into dispatch packetInCh: make(chan ReadPacket, 1), readNextCh: make(chan struct{}, 1), @@ -656,7 +655,7 @@ func (t *UDPv5) startResponseTimeout(c *callV5) { timer mclock.Timer done = make(chan struct{}) ) - timer = t.clock.AfterFunc(respTimeoutV5, func() { + timer = t.clock.AfterFunc(t.respTimeout, func() { <-done select { case t.respTimeoutCh <- &callTimeout{c, timer}: diff --git a/params/config.go b/params/config.go index cb12098bc9..5a2e10a943 100644 --- a/params/config.go +++ b/params/config.go @@ -735,13 +735,13 @@ func (c *ChainConfig) CheckConfigForkOrder() error { } { if cur.config != nil { if err := cur.config.validate(); err != nil { - return fmt.Errorf("invalid blob configuration for fork %s: %v", cur.name, err) + return fmt.Errorf("invalid chain configuration in blobSchedule for fork %q: %v", cur.name, err) } } if cur.timestamp != nil { // If the fork is configured, a blob schedule must be defined for it. if cur.config == nil { - return fmt.Errorf("unsupported fork configuration: missing blob configuration entry for %v in schedule", cur.name) + return fmt.Errorf("invalid chain configuration: missing entry for fork %q in blobSchedule", cur.name) } } } diff --git a/trie/secure_trie.go b/trie/secure_trie.go index f53b10758f..45d5fd63e7 100644 --- a/trie/secure_trie.go +++ b/trie/secure_trie.go @@ -275,6 +275,7 @@ func (t *StateTrie) Copy() *StateTrie { trie: *t.trie.Copy(), db: t.db, secKeyCache: t.secKeyCache, + preimages: t.preimages, } } diff --git a/version/version.go b/version/version.go index f700f9e973..374522e438 100644 --- a/version/version.go +++ b/version/version.go @@ -19,6 +19,6 @@ package version const ( Major = 1 // Major version component of the current release Minor = 15 // Minor version component of the current release - Patch = 1 // Patch version component of the current release + Patch = 2 // Patch version component of the current release Meta = "unstable" // Version metadata to append to the version string )