-
Notifications
You must be signed in to change notification settings - Fork 6
test(etc): enhance live RPC tests with ECBP-1100, ECIP-1099, and fork verification #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| //go:build live | ||
|
|
||
| // Package live_etc provides pre-fork integration tests that verify current | ||
| // ETC and Mordor chain state via JSON-RPC. These tests require a running | ||
| // core-geth node and are excluded from normal test runs. | ||
| // | ||
| // Run with: go test -tags live ./tests/live_etc/ -v | ||
| // | ||
| // Environment variables: | ||
| // | ||
| // MORDOR_RPC - Mordor RPC endpoint (default: http://localhost:8545) | ||
| // ETC_RPC - ETC mainnet RPC endpoint (default: https://etc.rivet.link) | ||
| package live_etc | ||
|
|
||
| import ( | ||
| "context" | ||
| "encoding/json" | ||
| "math/big" | ||
| "os" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/ethereum/go-ethereum/common" | ||
| "github.com/ethereum/go-ethereum/common/hexutil" | ||
| "github.com/ethereum/go-ethereum/rpc" | ||
| ) | ||
|
|
||
| // Pre-olympia chain constants (NO olympia-specific values here) | ||
| const ( | ||
| MordorChainID = 63 | ||
| ETCMainnetChainID = 61 | ||
|
|
||
| // ECIP-1017 era parameters | ||
| ClassicEraLength = 5_000_000 | ||
| MordorEraLength = 2_000_000 | ||
|
|
||
| // ECIP-1099 etchash fork blocks | ||
| ClassicECIP1099Block = 11_700_000 | ||
| MordorECIP1099Block = 2_520_000 | ||
|
|
||
| // ECBP-1100 (MESS) activation windows | ||
| MordorECBP1100Activate = 2_380_000 | ||
| MordorECBP1100Deactivate = 10_400_000 | ||
|
|
||
| ClassicECBP1100Activate = 11_380_000 | ||
| ClassicECBP1100Deactivate = 19_250_000 | ||
|
|
||
| // Spiral fork blocks | ||
| MordorSpiralBlock = 9_957_000 | ||
| ClassicSpiralBlock = 19_250_000 | ||
|
|
||
| // Gas limits (pre-olympia) | ||
| ETCGasLimit = 8_000_000 | ||
|
|
||
| // Etchash epoch lengths | ||
| EpochLengthDefault = 30_000 | ||
| EpochLengthECIP1099 = 60_000 | ||
| ) | ||
|
|
||
| // Known genesis hashes | ||
| var ( | ||
| MordorGenesisHash = common.HexToHash("0xa68ebde7932f0bf2579b075499416f0a693de84c26b05cd01de86e60aad05ec0") | ||
| ETCGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") | ||
| ) | ||
|
|
||
| // rpcBlock is a minimal block structure for JSON-RPC responses. | ||
| type rpcBlock struct { | ||
| Number *hexutil.Big `json:"number"` | ||
| Hash common.Hash `json:"hash"` | ||
| ParentHash common.Hash `json:"parentHash"` | ||
| GasUsed *hexutil.Big `json:"gasUsed"` | ||
| GasLimit *hexutil.Big `json:"gasLimit"` | ||
| StateRoot common.Hash `json:"stateRoot"` | ||
| Miner common.Address `json:"miner"` | ||
| Difficulty *hexutil.Big `json:"difficulty"` | ||
| Nonce string `json:"nonce"` | ||
| MixHash common.Hash `json:"mixHash"` | ||
| Timestamp *hexutil.Big `json:"timestamp"` | ||
| } | ||
|
|
||
| // getMordorRPC returns the Mordor RPC endpoint. | ||
| func getMordorRPC() string { | ||
| if v := os.Getenv("MORDOR_RPC"); v != "" { | ||
| return v | ||
| } | ||
| return "http://localhost:8545" | ||
| } | ||
|
|
||
| // getETCRPC returns the ETC mainnet RPC endpoint. | ||
| func getETCRPC() string { | ||
| if v := os.Getenv("ETC_RPC"); v != "" { | ||
| return v | ||
| } | ||
| return "https://etc.rivet.link" | ||
| } | ||
|
|
||
| // dialRPC connects to an RPC endpoint with a 10-second timeout. | ||
| func dialRPC(t *testing.T, endpoint string) *rpc.Client { | ||
| t.Helper() | ||
| ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
| defer cancel() | ||
| client, err := rpc.DialContext(ctx, endpoint) | ||
| if err != nil { | ||
| t.Skipf("cannot connect to %s: %v", endpoint, err) | ||
| } | ||
| return client | ||
| } | ||
|
|
||
| // getChainID returns the chain ID from eth_chainId. | ||
| func getChainID(t *testing.T, client *rpc.Client) uint64 { | ||
| t.Helper() | ||
| var result hexutil.Big | ||
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| if err := client.CallContext(ctx, &result, "eth_chainId"); err != nil { | ||
| t.Fatalf("eth_chainId failed: %v", err) | ||
| } | ||
| return result.ToInt().Uint64() | ||
| } | ||
|
|
||
| // getBlockByNumber fetches a block by number. | ||
| func getBlockByNumber(t *testing.T, client *rpc.Client, num *big.Int) *rpcBlock { | ||
| t.Helper() | ||
| var block rpcBlock | ||
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| var numArg string | ||
| if num == nil { | ||
| numArg = "latest" | ||
| } else { | ||
| numArg = hexutil.EncodeBig(num) | ||
| } | ||
| if err := client.CallContext(ctx, &block, "eth_getBlockByNumber", numArg, false); err != nil { | ||
| t.Fatalf("eth_getBlockByNumber(%s) failed: %v", numArg, err) | ||
| } | ||
| return &block | ||
| } | ||
|
|
||
| // getNetVersion returns the network version from net_version. | ||
| func getNetVersion(t *testing.T, client *rpc.Client) string { | ||
| t.Helper() | ||
| var result string | ||
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| if err := client.CallContext(ctx, &result, "net_version"); err != nil { | ||
| t.Fatalf("net_version failed: %v", err) | ||
| } | ||
| return result | ||
| } | ||
|
|
||
| // getSyncing returns whether the node is syncing. | ||
| func getSyncing(t *testing.T, client *rpc.Client) json.RawMessage { | ||
| t.Helper() | ||
| var result json.RawMessage | ||
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
| defer cancel() | ||
| if err := client.CallContext(ctx, &result, "eth_syncing"); err != nil { | ||
| t.Fatalf("eth_syncing failed: %v", err) | ||
| } | ||
| return result | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,166 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| //go:build live | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package live_etc | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "math/big" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "testing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetChainID verifies the ETC mainnet reports chain ID 61. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetChainID(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| chainID := getChainID(t, client) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if chainID != ETCMainnetChainID { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("ETC mainnet chain ID = %d, want %d", chainID, ETCMainnetChainID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetGenesisHash verifies the ETC mainnet genesis block hash | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (same as original Ethereum genesis — the chain split happened at block 1,920,000). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetGenesisHash(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| genesis := getBlockByNumber(t, client, big.NewInt(0)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if genesis.Hash != ETCGenesisHash { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("ETC genesis hash = %s, want %s", genesis.Hash.Hex(), ETCGenesisHash.Hex()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetPoW verifies that ETC mainnet blocks have valid PoW fields. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetPoW(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block := getBlockByNumber(t, client, nil) // latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if block.Difficulty == nil || block.Difficulty.ToInt().Sign() <= 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Error("latest block has zero or nil difficulty — not PoW") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetECIP1017Era verifies the current ECIP-1017 era based on | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the latest block number. As of 2026, ETC is in era 4 (blocks 20M-25M). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetECIP1017Era(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block := getBlockByNumber(t, client, nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blockNum := block.Number.ToInt().Int64() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if blockNum < ClassicEraLength { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Skipf("block %d is before era 1 start", blockNum) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| era := (blockNum - 1) / ClassicEraLength | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Logf("ETC mainnet block %d is in era %d", blockNum, era) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // As of early 2026, should be in era 4 (20M+) heading toward era 5 (25M+) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if era < 3 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("expected era >= 3 for current ETC mainnet, got era %d (block %d)", era, blockNum) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+44
to
+63
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetGasLimit verifies the ETC mainnet gas limit is around 8M. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetGasLimit(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block := getBlockByNumber(t, client, nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gasLimit := block.GasLimit.ToInt().Uint64() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if gasLimit < 7_000_000 || gasLimit > 9_000_000 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("ETC mainnet gas limit = %d, expected ~8M", gasLimit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetNetVersion verifies net_version returns "1" (ETC network ID). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetNetVersion(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| version := getNetVersion(t, client) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if version != "1" { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("ETC mainnet net_version = %q, want %q", version, "1") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetDAOForkBlock verifies that ETC did NOT execute the DAO fork. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Block 1,920,000 should have the "classic" state root (no irregular state change). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetDAOForkBlock(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The DAO fork block | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| block := getBlockByNumber(t, client, big.NewInt(1920000)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if block.Number.ToInt().Int64() != 1920000 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected block 1920000, got %d", block.Number.ToInt().Int64()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ETC's block 1920000 hash — confirms this chain rejected the DAO fork | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| expectedHash := "0x94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if block.Hash.Hex() != expectedHash { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("DAO fork block hash = %s, want %s (ETC classic chain)", block.Hash.Hex(), expectedHash) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+90
to
+107
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetECBP1100Deactivated verifies ECBP-1100 (MESS) is deactivated | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // on ETC mainnet. MESS was active 11,380,000→19,250,000 (deactivated at Spiral). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetECBP1100Deactivated(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latest := getBlockByNumber(t, client, nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blockNum := latest.Number.ToInt().Int64() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if blockNum < ClassicECBP1100Deactivate { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Skipf("ETC mainnet %d has not reached ECBP-1100 deactivation (%d)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blockNum, ClassicECBP1100Deactivate) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Logf("ETC mainnet block %d is past ECBP-1100 deactivation at %d (Spiral)", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blockNum, ClassicECBP1100Deactivate) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // TestETCMainnetECIP1099Epoch verifies ECIP-1099 epoch calculation on live chain. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // After block 11,700,000, epochs are 60,000 blocks long. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestETCMainnetECIP1099Epoch(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| client := dialRPC(t, getETCRPC()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer client.Close() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| latest := getBlockByNumber(t, client, nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| blockNum := latest.Number.ToInt().Uint64() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if blockNum < ClassicECIP1099Block { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Skipf("ETC mainnet block %d is before ECIP-1099 activation", blockNum) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| epoch := blockNum / EpochLengthECIP1099 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Logf("ETC mainnet block %d is in etchash epoch %d (60K-block epochs)", blockNum, epoch) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if epoch == 0 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Errorf("epoch should not be 0 at block %d", blockNum) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+141
to
+146
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| epoch := blockNum / EpochLengthECIP1099 | |
| t.Logf("ETC mainnet block %d is in etchash epoch %d (60K-block epochs)", blockNum, epoch) | |
| if epoch == 0 { | |
| t.Errorf("epoch should not be 0 at block %d", blockNum) | |
| } | |
| // Pre-ECIP-1099 epochs are 30,000 blocks long; post-ECIP-1099 epochs are 60,000 blocks long. | |
| const epochLengthPreECIP1099 uint64 = 30000 | |
| preBlock := uint64(ClassicECIP1099Block - 1) | |
| postBlock := uint64(ClassicECIP1099Block) | |
| preEpoch := preBlock / epochLengthPreECIP1099 | |
| postEpoch := postBlock / EpochLengthECIP1099 | |
| t.Logf("ETC mainnet ECIP-1099 boundary: pre-block %d in epoch %d (30K-block epochs), post-block %d in epoch %d (60K-block epochs)", | |
| preBlock, preEpoch, postBlock, postEpoch) | |
| if preEpoch == 0 { | |
| t.Errorf("pre-ECIP-1099 epoch should not be 0 at block %d", preBlock) | |
| } | |
| if postEpoch == 0 { | |
| t.Errorf("post-ECIP-1099 epoch should not be 0 at block %d", postBlock) | |
| } | |
| if preEpoch == postEpoch { | |
| t.Errorf("expected different epochs across ECIP-1099 boundary, got pre=%d (block %d), post=%d (block %d)", | |
| preEpoch, preBlock, postEpoch, postBlock) | |
| } | |
| // Also log the epoch for the latest block using the post-ECIP-1099 epoch length. | |
| latestEpoch := blockNum / EpochLengthECIP1099 | |
| t.Logf("ETC mainnet latest block %d is in etchash epoch %d (60K-block epochs)", blockNum, latestEpoch) |
Copilot
AI
Mar 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR description mentions (1) fork verification by cross-referencing chain config for all 14 ECIP-1066 forks and (2) Spiral fork verification of EIP-3855/EIP-3860/EIP-6049. In this change set, the Spiral tests only check that the fork block exists and has non-zero difficulty/gasLimit, and there are no ECIP-1066 fork cross-reference tests. Please either add the missing assertions/tests or update the PR description to match what’s implemented.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getBlockByNumber unmarshals into a non-pointer struct. If the RPC returns JSON
nullfor an unavailable block (common on pruned/partial endpoints), rpc.CallContext will succeed and leaveblockzero-valued, causing downstream nil dereferences (e.g., block.Number.ToInt()). Consider unmarshaling intovar block *rpcBlockand failing the test whenblock == nil(mirroring ethclient.HeaderByNumber).