Merge branch 'master' into enhance-simulation-api

This commit is contained in:
Rez 2025-03-19 07:35:54 +11:00
commit ea7217b5dd
No known key found for this signature in database
21 changed files with 280 additions and 32 deletions

View file

@ -55,4 +55,17 @@ var (
AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}). AddFork("CAPELLA", 256, []byte{4, 1, 112, 0}).
AddFork("DENEB", 29696, []byte{5, 1, 112, 0}). AddFork("DENEB", 29696, []byte{5, 1, 112, 0}).
AddFork("ELECTRA", 115968, []byte{6, 1, 112, 0}) AddFork("ELECTRA", 115968, []byte{6, 1, 112, 0})
HoodiLightConfig = (&ChainConfig{
GenesisValidatorsRoot: common.HexToHash("0x212f13fc4df078b6cb7db228f1c8307566dcecf900867401a92023d7ba99cb5f"),
GenesisTime: 1742212800,
Checkpoint: common.HexToHash(""),
}).
AddFork("GENESIS", 0, common.FromHex("0x10000910")).
AddFork("ALTAIR", 0, common.FromHex("0x20000910")).
AddFork("BELLATRIX", 0, common.FromHex("0x30000910")).
AddFork("CAPELLA", 0, common.FromHex("0x40000910")).
AddFork("DENEB", 0, common.FromHex("0x50000910")).
AddFork("ELECTRA", 2048, common.FromHex("0x60000910")).
AddFork("FULU", 18446744073709551615, common.FromHex("0x70000910"))
) )

View file

@ -47,6 +47,7 @@ func main() {
utils.MainnetFlag, utils.MainnetFlag,
utils.SepoliaFlag, utils.SepoliaFlag,
utils.HoleskyFlag, utils.HoleskyFlag,
utils.HoodiFlag,
utils.BlsyncApiFlag, utils.BlsyncApiFlag,
utils.BlsyncJWTSecretFlag, utils.BlsyncJWTSecretFlag,
}, },

View file

@ -234,6 +234,8 @@ func ethFilter(args []string) (nodeFilter, error) {
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock()) filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
case "holesky": case "holesky":
filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock()) filter = forkid.NewStaticFilter(params.HoleskyChainConfig, core.DefaultHoleskyGenesisBlock().ToBlock())
case "hoodi":
filter = forkid.NewStaticFilter(params.HoodiChainConfig, core.DefaultHoodiGenesisBlock().ToBlock())
default: default:
return nil, fmt.Errorf("unknown network %q", args[0]) return nil, fmt.Errorf("unknown network %q", args[0])
} }

View file

@ -296,6 +296,9 @@ func prepare(ctx *cli.Context) {
case ctx.IsSet(utils.HoleskyFlag.Name): case ctx.IsSet(utils.HoleskyFlag.Name):
log.Info("Starting Geth on Holesky testnet...") log.Info("Starting Geth on Holesky testnet...")
case ctx.IsSet(utils.HoodiFlag.Name):
log.Info("Starting Geth on Hoodi testnet...")
case ctx.IsSet(utils.DeveloperFlag.Name): case ctx.IsSet(utils.DeveloperFlag.Name):
log.Info("Starting Geth in ephemeral dev mode...") log.Info("Starting Geth in ephemeral dev mode...")
log.Warn(`You are running Geth in --dev mode. Please note the following: log.Warn(`You are running Geth in --dev mode. Please note the following:
@ -322,6 +325,7 @@ func prepare(ctx *cli.Context) {
// Make sure we're not on any supported preconfigured testnet either // Make sure we're not on any supported preconfigured testnet either
if !ctx.IsSet(utils.HoleskyFlag.Name) && if !ctx.IsSet(utils.HoleskyFlag.Name) &&
!ctx.IsSet(utils.SepoliaFlag.Name) && !ctx.IsSet(utils.SepoliaFlag.Name) &&
!ctx.IsSet(utils.HoodiFlag.Name) &&
!ctx.IsSet(utils.DeveloperFlag.Name) { !ctx.IsSet(utils.DeveloperFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up! // Nope, we're really on mainnet. Bump that cache up!
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096) log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)

View file

@ -134,7 +134,7 @@ var (
} }
NetworkIdFlag = &cli.Uint64Flag{ NetworkIdFlag = &cli.Uint64Flag{
Name: "networkid", Name: "networkid",
Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky instead)", Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky, --hoodi instead)",
Value: ethconfig.Defaults.NetworkId, Value: ethconfig.Defaults.NetworkId,
Category: flags.EthCategory, Category: flags.EthCategory,
} }
@ -153,6 +153,11 @@ var (
Usage: "Holesky network: pre-configured proof-of-stake test network", Usage: "Holesky network: pre-configured proof-of-stake test network",
Category: flags.EthCategory, Category: flags.EthCategory,
} }
HoodiFlag = &cli.BoolFlag{
Name: "hoodi",
Usage: "Hoodi network: pre-configured proof-of-stake test network",
Category: flags.EthCategory,
}
// Dev mode // Dev mode
DeveloperFlag = &cli.BoolFlag{ DeveloperFlag = &cli.BoolFlag{
Name: "dev", Name: "dev",
@ -958,6 +963,7 @@ var (
TestnetFlags = []cli.Flag{ TestnetFlags = []cli.Flag{
SepoliaFlag, SepoliaFlag,
HoleskyFlag, HoleskyFlag,
HoodiFlag,
} }
// NetworkFlags is the flag group of all built-in supported networks. // NetworkFlags is the flag group of all built-in supported networks.
NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...) NetworkFlags = append([]cli.Flag{MainnetFlag}, TestnetFlags...)
@ -984,6 +990,9 @@ func MakeDataDir(ctx *cli.Context) string {
if ctx.Bool(HoleskyFlag.Name) { if ctx.Bool(HoleskyFlag.Name) {
return filepath.Join(path, "holesky") return filepath.Join(path, "holesky")
} }
if ctx.Bool(HoodiFlag.Name) {
return filepath.Join(path, "hoodi")
}
return path return path
} }
Fatalf("Cannot determine default data directory, please set manually (--datadir)") Fatalf("Cannot determine default data directory, please set manually (--datadir)")
@ -1044,6 +1053,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls = params.HoleskyBootnodes urls = params.HoleskyBootnodes
case ctx.Bool(SepoliaFlag.Name): case ctx.Bool(SepoliaFlag.Name):
urls = params.SepoliaBootnodes urls = params.SepoliaBootnodes
case ctx.Bool(HoodiFlag.Name):
urls = params.HoodiBootnodes
} }
} }
cfg.BootstrapNodes = mustParseBootnodes(urls) cfg.BootstrapNodes = mustParseBootnodes(urls)
@ -1428,6 +1439,8 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir(): case ctx.Bool(HoleskyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "holesky") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "holesky")
case ctx.Bool(HoodiFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "hoodi")
} }
} }
@ -1554,7 +1567,7 @@ func setRequiredBlocks(ctx *cli.Context, cfg *ethconfig.Config) {
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) { func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
// Avoid conflicting network flags // Avoid conflicting network flags
flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag) flags.CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag, HoodiFlag)
flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer flags.CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
// Set configurations from CLI flags // Set configurations from CLI flags
@ -1738,6 +1751,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} }
cfg.Genesis = core.DefaultSepoliaGenesisBlock() cfg.Genesis = core.DefaultSepoliaGenesisBlock()
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash) SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
case ctx.Bool(HoodiFlag.Name):
if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 560048
}
cfg.Genesis = core.DefaultHoodiGenesisBlock()
SetDNSDiscoveryDefaults(cfg, params.HoodiGenesisHash)
case ctx.Bool(DeveloperFlag.Name): case ctx.Bool(DeveloperFlag.Name):
if !ctx.IsSet(NetworkIdFlag.Name) { if !ctx.IsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337 cfg.NetworkId = 1337
@ -1852,6 +1871,8 @@ func MakeBeaconLightConfig(ctx *cli.Context) bparams.ClientConfig {
config.ChainConfig = *bparams.SepoliaLightConfig config.ChainConfig = *bparams.SepoliaLightConfig
case ctx.Bool(HoleskyFlag.Name): case ctx.Bool(HoleskyFlag.Name):
config.ChainConfig = *bparams.HoleskyLightConfig config.ChainConfig = *bparams.HoleskyLightConfig
case ctx.Bool(HoodiFlag.Name):
config.ChainConfig = *bparams.HoodiLightConfig
default: default:
if !customConfig { if !customConfig {
config.ChainConfig = *bparams.MainnetLightConfig config.ChainConfig = *bparams.MainnetLightConfig
@ -2055,7 +2076,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly bool) ethdb.
} }
chainDb = remotedb.New(client) chainDb = remotedb.New(client)
default: default:
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly) chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "eth/db/chaindata/", readonly)
} }
if err != nil { if err != nil {
Fatalf("Could not open database: %v", err) Fatalf("Could not open database: %v", err)
@ -2118,6 +2139,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
genesis = core.DefaultHoleskyGenesisBlock() genesis = core.DefaultHoleskyGenesisBlock()
case ctx.Bool(SepoliaFlag.Name): case ctx.Bool(SepoliaFlag.Name):
genesis = core.DefaultSepoliaGenesisBlock() genesis = core.DefaultSepoliaGenesisBlock()
case ctx.Bool(HoodiFlag.Name):
genesis = core.DefaultHoodiGenesisBlock()
case ctx.Bool(DeveloperFlag.Name): case ctx.Bool(DeveloperFlag.Name):
Fatalf("Developer chains are ephemeral") Fatalf("Developer chains are ephemeral")
} }

View file

@ -46,6 +46,9 @@ var checkpointsSepoliaJSON []byte
//go:embed checkpoints_holesky.json //go:embed checkpoints_holesky.json
var checkpointsHoleskyJSON []byte var checkpointsHoleskyJSON []byte
//go:embed checkpoints_hoodi.json
var checkpointsHoodiJSON []byte
// checkpoints lists sets of checkpoints for multiple chains. The matching // checkpoints lists sets of checkpoints for multiple chains. The matching
// checkpoint set is autodetected by the indexer once the canonical chain is // checkpoint set is autodetected by the indexer once the canonical chain is
// known. // known.
@ -53,6 +56,7 @@ var checkpoints = []checkpointList{
decodeCheckpoints(checkpointsMainnetJSON), decodeCheckpoints(checkpointsMainnetJSON),
decodeCheckpoints(checkpointsSepoliaJSON), decodeCheckpoints(checkpointsSepoliaJSON),
decodeCheckpoints(checkpointsHoleskyJSON), decodeCheckpoints(checkpointsHoleskyJSON),
decodeCheckpoints(checkpointsHoodiJSON),
} }
func decodeCheckpoints(encoded []byte) (result checkpointList) { func decodeCheckpoints(encoded []byte) (result checkpointList) {

View file

@ -0,0 +1 @@
[]

View file

@ -346,6 +346,9 @@ func (f *FilterMaps) needTailEpoch(epoch uint32) bool {
if epoch > firstEpoch { if epoch > firstEpoch {
return true return true
} }
if (epoch+1)<<f.logMapsPerEpoch >= f.indexedRange.maps.AfterLast() {
return true
}
if epoch+1 < firstEpoch { if epoch+1 < firstEpoch {
return false return false
} }

View file

@ -185,27 +185,30 @@ func (f *FilterMaps) lastCanonicalMapBoundaryBefore(renderBefore uint32) (nextMa
// lastMapBoundaryBefore returns the latest map boundary before the specified // lastMapBoundaryBefore returns the latest map boundary before the specified
// map index. // map index.
func (f *FilterMaps) lastMapBoundaryBefore(mapIndex uint32) (uint32, bool) { func (f *FilterMaps) lastMapBoundaryBefore(renderBefore uint32) (uint32, bool) {
if !f.indexedRange.initialized || f.indexedRange.maps.AfterLast() == 0 { if !f.indexedRange.initialized || f.indexedRange.maps.AfterLast() == 0 || renderBefore == 0 {
return 0, false return 0, false
} }
if mapIndex > f.indexedRange.maps.AfterLast() { afterLastFullMap := f.indexedRange.maps.AfterLast()
mapIndex = f.indexedRange.maps.AfterLast() if afterLastFullMap > 0 && f.indexedRange.headIndexed {
afterLastFullMap-- // last map is not full
} }
if mapIndex > f.indexedRange.maps.First() { firstRendered := min(renderBefore-1, afterLastFullMap)
return mapIndex - 1, true if firstRendered == 0 {
return 0, false
} }
if mapIndex+f.mapsPerEpoch > f.indexedRange.maps.First() { if firstRendered >= f.indexedRange.maps.First() {
if mapIndex > f.indexedRange.maps.First()-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch { return firstRendered - 1, true
mapIndex = f.indexedRange.maps.First() - f.mapsPerEpoch + f.indexedRange.tailPartialEpoch }
} if firstRendered+f.mapsPerEpoch > f.indexedRange.maps.First() {
firstRendered = min(firstRendered, f.indexedRange.maps.First()-f.mapsPerEpoch+f.indexedRange.tailPartialEpoch)
} else { } else {
mapIndex = (mapIndex >> f.logMapsPerEpoch) << f.logMapsPerEpoch firstRendered = (firstRendered >> f.logMapsPerEpoch) << f.logMapsPerEpoch
} }
if mapIndex == 0 { if firstRendered == 0 {
return 0, false return 0, false
} }
return mapIndex - 1, true return firstRendered - 1, true
} }
// emptyFilterMap returns an empty filter map. // emptyFilterMap returns an empty filter map.

View file

@ -112,6 +112,17 @@ func TestCreation(t *testing.T) {
{123, 2740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 0}}, // Future Prague block {123, 2740434112, ID{Hash: checksumToBytes(0xdfbd9bed), Next: 0}}, // Future Prague block
}, },
}, },
// Hoodi test cases
{
params.HoodiChainConfig,
core.DefaultHoodiGenesisBlock().ToBlock(),
[]testcase{
{0, 0, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople, Petersburg, Istanbul, Berlin, London, Paris, Shanghai, Cancun block
{123, 1742999831, ID{Hash: checksumToBytes(0xbef71d30), Next: 1742999832}}, // Last Cancun block
{123, 1742999832, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // First Prague block
{123, 2740434112, ID{Hash: checksumToBytes(0x0929e24e), Next: 0}}, // Future Prague block
},
},
} }
for i, tt := range tests { for i, tt := range tests {
for j, ttt := range tt.cases { for j, ttt := range tt.cases {

View file

@ -221,6 +221,8 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene
genesis = DefaultSepoliaGenesisBlock() genesis = DefaultSepoliaGenesisBlock()
case params.HoleskyGenesisHash: case params.HoleskyGenesisHash:
genesis = DefaultHoleskyGenesisBlock() genesis = DefaultHoleskyGenesisBlock()
case params.HoodiGenesisHash:
genesis = DefaultHoodiGenesisBlock()
} }
if genesis != nil { if genesis != nil {
return genesis.Alloc, nil return genesis.Alloc, nil
@ -431,6 +433,8 @@ func (g *Genesis) chainConfigOrDefault(ghash common.Hash, stored *params.ChainCo
return params.HoleskyChainConfig return params.HoleskyChainConfig
case ghash == params.SepoliaGenesisHash: case ghash == params.SepoliaGenesisHash:
return params.SepoliaChainConfig return params.SepoliaChainConfig
case ghash == params.HoodiGenesisHash:
return params.HoodiChainConfig
default: default:
return stored return stored
} }
@ -625,6 +629,18 @@ func DefaultHoleskyGenesisBlock() *Genesis {
} }
} }
// DefaultHoodiGenesisBlock returns the Hoodi network genesis block.
func DefaultHoodiGenesisBlock() *Genesis {
return &Genesis{
Config: params.HoodiChainConfig,
Nonce: 0x1234,
GasLimit: 0x2255100,
Difficulty: big.NewInt(0x01),
Timestamp: 1742212800,
Alloc: decodePrealloc(hoodiAllocData),
}
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. // DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis { func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
// Override the default period to the user requested one // Override the default period to the user requested one

File diff suppressed because one or more lines are too long

View file

@ -104,6 +104,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
}, },
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash}, wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
}, },
{
name: "custom block in DB, genesis == hoodi",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
customg.Commit(db, tdb)
return SetupGenesisBlock(db, tdb, DefaultHoodiGenesisBlock())
},
wantErr: &GenesisMismatchError{Stored: customghash, New: params.HoodiGenesisHash},
},
{ {
name: "compatible config in DB", name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) { fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, *params.ConfigCompatError, error) {
@ -178,6 +187,8 @@ func TestGenesisHashes(t *testing.T) {
}{ }{
{DefaultGenesisBlock(), params.MainnetGenesisHash}, {DefaultGenesisBlock(), params.MainnetGenesisHash},
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash}, {DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
{DefaultHoleskyGenesisBlock(), params.HoleskyGenesisHash},
{DefaultHoodiGenesisBlock(), params.HoodiGenesisHash},
} { } {
// Test via MustCommit // Test via MustCommit
db := rawdb.NewMemoryDatabase() db := rawdb.NewMemoryDatabase()

View file

@ -119,6 +119,7 @@ func TestDeleteBloomBits(t *testing.T) {
for s := uint64(0); s < 2; s++ { for s := uint64(0); s < 2; s++ {
WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02}) WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02})
WriteBloomBits(db, i, s, params.SepoliaGenesisHash, []byte{0x01, 0x02}) WriteBloomBits(db, i, s, params.SepoliaGenesisHash, []byte{0x01, 0x02})
WriteBloomBits(db, i, s, params.HoodiGenesisHash, []byte{0x01, 0x02})
} }
} }
check := func(bit uint, section uint64, head common.Hash, exist bool) { check := func(bit uint, section uint64, head common.Hash, exist bool) {
@ -133,24 +134,31 @@ func TestDeleteBloomBits(t *testing.T) {
// Check the existence of written data. // Check the existence of written data.
check(0, 0, params.MainnetGenesisHash, true) check(0, 0, params.MainnetGenesisHash, true)
check(0, 0, params.SepoliaGenesisHash, true) check(0, 0, params.SepoliaGenesisHash, true)
check(0, 0, params.HoodiGenesisHash, true)
// Check the existence of deleted data. // Check the existence of deleted data.
DeleteBloombits(db, 0, 0, 1) DeleteBloombits(db, 0, 0, 1)
check(0, 0, params.MainnetGenesisHash, false) check(0, 0, params.MainnetGenesisHash, false)
check(0, 0, params.SepoliaGenesisHash, false) check(0, 0, params.SepoliaGenesisHash, false)
check(0, 0, params.HoodiGenesisHash, false)
check(0, 1, params.MainnetGenesisHash, true) check(0, 1, params.MainnetGenesisHash, true)
check(0, 1, params.SepoliaGenesisHash, true) check(0, 1, params.SepoliaGenesisHash, true)
check(0, 1, params.HoodiGenesisHash, true)
// Check the existence of deleted data. // Check the existence of deleted data.
DeleteBloombits(db, 0, 0, 2) DeleteBloombits(db, 0, 0, 2)
check(0, 0, params.MainnetGenesisHash, false) check(0, 0, params.MainnetGenesisHash, false)
check(0, 0, params.SepoliaGenesisHash, false) check(0, 0, params.SepoliaGenesisHash, false)
check(0, 0, params.HoodiGenesisHash, false)
check(0, 1, params.MainnetGenesisHash, false) check(0, 1, params.MainnetGenesisHash, false)
check(0, 1, params.SepoliaGenesisHash, false) check(0, 1, params.SepoliaGenesisHash, false)
check(0, 1, params.HoodiGenesisHash, false)
// Bit1 shouldn't be affect. // Bit1 shouldn't be affect.
check(1, 0, params.MainnetGenesisHash, true) check(1, 0, params.MainnetGenesisHash, true)
check(1, 0, params.SepoliaGenesisHash, true) check(1, 0, params.SepoliaGenesisHash, true)
check(1, 0, params.HoodiGenesisHash, true)
check(1, 1, params.MainnetGenesisHash, true) check(1, 1, params.MainnetGenesisHash, true)
check(1, 1, params.SepoliaGenesisHash, true) check(1, 1, params.SepoliaGenesisHash, true)
check(1, 1, params.HoodiGenesisHash, true)
} }

View file

@ -279,7 +279,7 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed
TxIndex: i, TxIndex: i,
TxHash: tx.Hash(), TxHash: tx.Hash(),
} }
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config) res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, task.statedb, config, nil)
if err != nil { if err != nil {
task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()} task.results[i] = &txTraceResult{TxHash: tx.Hash(), Error: err.Error()}
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err) log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
@ -632,7 +632,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac
TxIndex: i, TxIndex: i,
TxHash: tx.Hash(), TxHash: tx.Hash(),
} }
res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config) res, err := api.traceTx(ctx, tx, msg, txctx, blockCtx, statedb, config, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -676,7 +676,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat
// concurrent use. // concurrent use.
// See: https://github.com/ethereum/go-ethereum/issues/29114 // See: https://github.com/ethereum/go-ethereum/issues/29114
blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config) res, err := api.traceTx(ctx, txs[task.index], msg, txctx, blockCtx, task.statedb, config, nil)
if err != nil { if err != nil {
results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()} results[task.index] = &txTraceResult{TxHash: txs[task.index].Hash(), Error: err.Error()}
continue continue
@ -894,7 +894,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
TxIndex: int(index), TxIndex: int(index),
TxHash: hash, TxHash: hash,
} }
return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config) return api.traceTx(ctx, tx, msg, txctx, vmctx, statedb, config, nil)
} }
// TraceCall lets you trace a given eth_call. It collects the structured logs // TraceCall lets you trace a given eth_call. It collects the structured logs
@ -907,10 +907,11 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *
func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) { func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, config *TraceCallConfig) (interface{}, error) {
// Try to retrieve the specified block // Try to retrieve the specified block
var ( var (
err error err error
block *types.Block block *types.Block
statedb *state.StateDB statedb *state.StateDB
release StateReleaseFunc release StateReleaseFunc
precompiles vm.PrecompiledContracts
) )
if hash, ok := blockNrOrHash.Hash(); ok { if hash, ok := blockNrOrHash.Hash(); ok {
block, err = api.blockByHash(ctx, hash) block, err = api.blockByHash(ctx, hash)
@ -952,7 +953,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
config.BlockOverrides.Apply(&vmctx) config.BlockOverrides.Apply(&vmctx)
rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time) rules := api.backend.ChainConfig().Rules(vmctx.BlockNumber, vmctx.Random != nil, vmctx.Time)
precompiles := vm.ActivePrecompiledContracts(rules) precompiles = vm.ActivePrecompiledContracts(rules)
if err := config.StateOverrides.Apply(statedb, precompiles); err != nil { if err := config.StateOverrides.Apply(statedb, precompiles); err != nil {
return nil, err return nil, err
} }
@ -977,13 +978,13 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if config != nil { if config != nil {
traceConfig = &config.TraceConfig traceConfig = &config.TraceConfig
} }
return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig) return api.traceTx(ctx, tx, msg, new(Context), vmctx, statedb, traceConfig, precompiles)
} }
// traceTx configures a new tracer according to the provided configuration, and // traceTx configures a new tracer according to the provided configuration, and
// executes the given message in the provided environment. The return value will // executes the given message in the provided environment. The return value will
// be tracer dependent. // be tracer dependent.
func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig) (interface{}, error) { func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *core.Message, txctx *Context, vmctx vm.BlockContext, statedb *state.StateDB, config *TraceConfig, precompiles vm.PrecompiledContracts) (interface{}, error) {
var ( var (
tracer *Tracer tracer *Tracer
err error err error
@ -1009,6 +1010,9 @@ func (api *API) traceTx(ctx context.Context, tx *types.Transaction, message *cor
} }
tracingStateDB := state.NewHookedState(statedb, tracer.Hooks) tracingStateDB := state.NewHookedState(statedb, tracer.Hooks)
evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true}) evm := vm.NewEVM(vmctx, tracingStateDB, api.backend.ChainConfig(), vm.Config{Tracer: tracer.Hooks, NoBaseFee: true})
if precompiles != nil {
evm.SetPrecompiles(precompiles)
}
// Define a meaningful timeout of a single transaction trace // Define a meaningful timeout of a single transaction trace
if config.Timeout != nil { if config.Timeout != nil {

View file

@ -642,6 +642,7 @@ func TestTracingWithOverrides(t *testing.T) {
t.Parallel() t.Parallel()
// Initialize test accounts // Initialize test accounts
accounts := newAccounts(3) accounts := newAccounts(3)
ecRecoverAddress := common.HexToAddress("0x0000000000000000000000000000000000000001")
storageAccount := common.Address{0x13, 37} storageAccount := common.Address{0x13, 37}
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: params.TestChainConfig, Config: params.TestChainConfig,
@ -940,6 +941,48 @@ func TestTracingWithOverrides(t *testing.T) {
}, },
want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`, want: `{"gas":25288,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000055"}`,
}, },
{ // Call to precompile ECREC (0x01), but code was modified to add 1 to input
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &randomAccounts[0].addr,
To: &ecRecoverAddress,
Data: newRPCBytes(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")),
},
config: &TraceCallConfig{
StateOverrides: &override.StateOverride{
randomAccounts[0].addr: override.OverrideAccount{
Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))),
},
ecRecoverAddress: override.OverrideAccount{
// The code below adds one to input
Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")),
MovePrecompileTo: &randomAccounts[2].addr,
},
},
},
want: `{"gas":21167,"failed":false,"returnValue":"0000000000000000000000000000000000000000000000000000000000000002"}`,
},
{ // Call to ECREC Precompiled on a different address, expect the original behaviour of ECREC precompile
blockNumber: rpc.LatestBlockNumber,
call: ethapi.TransactionArgs{
From: &randomAccounts[0].addr,
To: &randomAccounts[2].addr, // Moved EcRecover
Data: newRPCBytes(common.Hex2Bytes("82f3df49d3645876de6313df2bbe9fbce593f21341a7b03acdb9423bc171fcc9000000000000000000000000000000000000000000000000000000000000001cba13918f50da910f2d55a7ea64cf716ba31dad91856f45908dde900530377d8a112d60f36900d18eb8f9d3b4f85a697b545085614509e3520e4b762e35d0d6bd")),
},
config: &TraceCallConfig{
StateOverrides: &override.StateOverride{
randomAccounts[0].addr: override.OverrideAccount{
Balance: newRPCBalance(new(big.Int).Mul(big.NewInt(1), big.NewInt(params.Ether))),
},
ecRecoverAddress: override.OverrideAccount{
// The code below adds one to input
Code: newRPCBytes(common.Hex2Bytes("60003560010160005260206000f3")),
MovePrecompileTo: &randomAccounts[2].addr, // Move EcRecover to this address
},
},
},
want: `{"gas":25664,"failed":false,"returnValue":"000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"}`,
},
} }
for i, tc := range testSuite { for i, tc := range testSuite {
result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config) result, err := api.TraceCall(context.Background(), tc.call, rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, tc.config)

View file

@ -640,9 +640,13 @@ func (ec *Client) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *
} }
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on // EstimateGas tries to estimate the gas needed to execute a specific transaction based on
// the current pending state of the backend blockchain. There is no guarantee that this is // the current state of the backend blockchain. There is no guarantee that this is the
// the true gas limit requirement as other transactions may be added or removed by miners, // true gas limit requirement as other transactions may be added or removed by miners, but
// but it should provide a basis for setting a reasonable default. // it should provide a basis for setting a reasonable default.
//
// Note that the state used by this method is implementation-defined by the remote RPC
// server, but it's reasonable to assume that it will either be the pending or latest
// state.
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
var hex hexutil.Uint64 var hex hexutil.Uint64
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg)) err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
@ -652,6 +656,28 @@ func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64
return uint64(hex), nil return uint64(hex), nil
} }
// EstimateGasAtBlock is almost the same as EstimateGas except that it selects the block height
// instead of using the remote RPC's default state for gas estimation.
func (ec *Client) EstimateGasAtBlock(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) (uint64, error) {
var hex hexutil.Uint64
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), toBlockNumArg(blockNumber))
if err != nil {
return 0, err
}
return uint64(hex), nil
}
// EstimateGasAtBlockHash is almost the same as EstimateGas except that it selects the block
// hash instead of using the remote RPC's default state for gas estimation.
func (ec *Client) EstimateGasAtBlockHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) (uint64, error) {
var hex hexutil.Uint64
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg), rpc.BlockNumberOrHashWithHash(blockHash, false))
if err != nil {
return 0, err
}
return uint64(hex), nil
}
// SendTransaction injects a signed transaction into the pending pool for execution. // SendTransaction injects a signed transaction into the pending pool for execution.
// //
// If the transaction was a contract creation use the TransactionReceipt method to get the // If the transaction was a contract creation use the TransactionReceipt method to get the

View file

@ -592,6 +592,33 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
if !bytes.Equal(code, penCode) { if !bytes.Equal(code, penCode) {
t.Fatalf("unexpected code: %v %v", code, penCode) t.Fatalf("unexpected code: %v %v", code, penCode)
} }
// Use HeaderByNumber to get a header for EstimateGasAtBlock and EstimateGasAtBlockHash
latestHeader, err := ec.HeaderByNumber(context.Background(), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// EstimateGasAtBlock
msg := ethereum.CallMsg{
From: testAddr,
To: &common.Address{},
Gas: 21000,
Value: big.NewInt(1),
}
gas, err := ec.EstimateGasAtBlock(context.Background(), msg, latestHeader.Number)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gas != 21000 {
t.Fatalf("unexpected gas limit: %v", gas)
}
// EstimateGasAtBlockHash
gas, err = ec.EstimateGasAtBlockHash(context.Background(), msg, latestHeader.Hash())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gas != 21000 {
t.Fatalf("unexpected gas limit: %v", gas)
}
} }
func testTransactionSender(t *testing.T, client *rpc.Client) { func testTransactionSender(t *testing.T, client *rpc.Client) {

View file

@ -257,9 +257,13 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil { if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil {
return nil, nil, err return nil, nil, err
} }
tx := call.ToTransaction(types.DynamicFeeTxType) var (
tx = call.ToTransaction(types.DynamicFeeTxType)
txHash = tx.Hash()
)
txes[i] = tx txes[i] = tx
tracer.reset(tx.Hash(), uint(i)) tracer.reset(txHash, uint(i))
sim.state.SetTxContext(txHash, i)
// EoA check is always skipped, even in validation mode. // EoA check is always skipped, even in validation mode.
msg := call.ToMessage(header.BaseFee, !sim.validate, true) msg := call.ToMessage(header.BaseFee, !sim.validate, true)
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)

View file

@ -28,6 +28,15 @@ var MainnetBootnodes = []string{
"enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", // bootnode-hetzner-fsn "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303", // bootnode-hetzner-fsn
} }
// HoodiBootnodes are the enode URLs of the P2P bootstrap nodes running on the
// Hoodi test network.
var HoodiBootnodes = []string{
// EF DevOps
"enode://2112dd3839dd752813d4df7f40936f06829fc54c0e051a93967c26e5f5d27d99d886b57b4ffcc3c475e930ec9e79c56ef1dbb7d86ca5ee83a9d2ccf36e5c240c@134.209.138.84:30303",
"enode://60203fcb3524e07c5df60a14ae1c9c5b24023ea5d47463dfae051d2c9f3219f309657537576090ca0ae641f73d419f53d8e8000d7a464319d4784acd7d2abc41@209.38.124.160:30303",
"enode://8ae4a48101b2299597341263da0deb47cc38aa4d3ef4b7430b897d49bfa10eb1ccfe1655679b1ed46928ef177fbf21b86837bd724400196c508427a6f41602cd@134.199.184.23:30303",
}
// HoleskyBootnodes are the enode URLs of the P2P bootstrap nodes running on the // HoleskyBootnodes are the enode URLs of the P2P bootstrap nodes running on the
// Holesky test network. // Holesky test network.
var HoleskyBootnodes = []string{ var HoleskyBootnodes = []string{
@ -84,6 +93,8 @@ func KnownDNSNetwork(genesis common.Hash, protocol string) string {
net = "sepolia" net = "sepolia"
case HoleskyGenesisHash: case HoleskyGenesisHash:
net = "holesky" net = "holesky"
case HoodiGenesisHash:
net = "hoodi"
default: default:
return "" return ""
} }

View file

@ -31,6 +31,7 @@ var (
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
HoleskyGenesisHash = common.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4") HoleskyGenesisHash = common.HexToHash("0xb5f7f912443c940f21fd611f12828d75b534364ed9e95ca4e307729a4661bde4")
SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9") SepoliaGenesisHash = common.HexToHash("0x25a5cc106eea7138acab33231d7160d69cb777ee0c2c553fcddf5138993e6dd9")
HoodiGenesisHash = common.HexToHash("0xbbe312868b376a3001692a646dd2d7d1e4406380dfd86b98aa8a34d1557c971b")
) )
func newUint64(val uint64) *uint64 { return &val } func newUint64(val uint64) *uint64 { return &val }
@ -125,6 +126,36 @@ var (
Prague: DefaultPragueBlobConfig, Prague: DefaultPragueBlobConfig,
}, },
} }
// HoodiChainConfig contains the chain parameters to run a node on the Hoodi test network.
HoodiChainConfig = &ChainConfig{
ChainID: big.NewInt(560048),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
ArrowGlacierBlock: nil,
GrayGlacierBlock: nil,
TerminalTotalDifficulty: big.NewInt(0),
MergeNetsplitBlock: big.NewInt(0),
ShanghaiTime: newUint64(0),
CancunTime: newUint64(0),
PragueTime: newUint64(1742999832),
DepositContractAddress: common.HexToAddress("0x00000000219ab540356cBB839Cbe05303d7705Fa"),
Ethash: new(EthashConfig),
BlobScheduleConfig: &BlobScheduleConfig{
Cancun: DefaultCancunBlobConfig,
Prague: DefaultPragueBlobConfig,
},
}
// AllEthashProtocolChanges contains every protocol change (EIPs) introduced // AllEthashProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Ethash consensus. // and accepted by the Ethereum core developers into the Ethash consensus.
AllEthashProtocolChanges = &ChainConfig{ AllEthashProtocolChanges = &ChainConfig{
@ -338,6 +369,7 @@ var NetworkNames = map[string]string{
MainnetChainConfig.ChainID.String(): "mainnet", MainnetChainConfig.ChainID.String(): "mainnet",
SepoliaChainConfig.ChainID.String(): "sepolia", SepoliaChainConfig.ChainID.String(): "sepolia",
HoleskyChainConfig.ChainID.String(): "holesky", HoleskyChainConfig.ChainID.String(): "holesky",
HoodiChainConfig.ChainID.String(): "hoodi",
} }
// ChainConfig is the core config which determines the blockchain settings. // ChainConfig is the core config which determines the blockchain settings.