diff --git a/clients/consensus/chainspec.go b/clients/consensus/chainspec.go index 8fa135c1..82c10294 100644 --- a/clients/consensus/chainspec.go +++ b/clients/consensus/chainspec.go @@ -53,6 +53,8 @@ type ChainSpecConfig struct { ElectraForkEpoch *uint64 `yaml:"ELECTRA_FORK_EPOCH" check-if-fork:"ElectraForkEpoch"` FuluForkVersion phase0.Version `yaml:"FULU_FORK_VERSION" check-if-fork:"FuluForkEpoch"` FuluForkEpoch *uint64 `yaml:"FULU_FORK_EPOCH" check-if-fork:"FuluForkEpoch"` + GloasForkVersion phase0.Version `yaml:"GLOAS_FORK_VERSION" check-if-fork:"GloasForkEpoch"` + GloasForkEpoch *uint64 `yaml:"GLOAS_FORK_EPOCH" check-if-fork:"GloasForkEpoch"` // Time parameters SecondsPerSlot uint64 `yaml:"SECONDS_PER_SLOT"` diff --git a/clients/consensus/chainstate.go b/clients/consensus/chainstate.go index f96a59e7..572ba5fb 100644 --- a/clients/consensus/chainstate.go +++ b/clients/consensus/chainstate.go @@ -361,6 +361,34 @@ func (cs *ChainState) GetForkDigestForEpoch(epoch phase0.Epoch) phase0.ForkDiges return cs.GetForkDigest(currentForkVersion, currentBlobParams) } +func (cs *ChainState) GetBlobScheduleForEpoch(epoch phase0.Epoch) *BlobScheduleEntry { + if cs.specs == nil { + return nil + } + + var blobSchedule *BlobScheduleEntry + + if cs.specs.ElectraForkEpoch != nil && epoch >= phase0.Epoch(*cs.specs.ElectraForkEpoch) { + blobSchedule = &BlobScheduleEntry{ + Epoch: *cs.specs.ElectraForkEpoch, + MaxBlobsPerBlock: cs.specs.MaxBlobsPerBlockElectra, + } + } else if cs.specs.DenebForkEpoch != nil && epoch >= phase0.Epoch(*cs.specs.DenebForkEpoch) { + blobSchedule = &BlobScheduleEntry{ + Epoch: *cs.specs.DenebForkEpoch, + MaxBlobsPerBlock: cs.specs.MaxBlobsPerBlock, + } + } + + for i, blobScheduleEntry := range cs.specs.BlobSchedule { + if blobScheduleEntry.Epoch <= uint64(epoch) { + blobSchedule = &cs.specs.BlobSchedule[i] + } + } + + return blobSchedule +} + func (cs *ChainState) GetForkDigest(forkVersion phase0.Version, blobParams *BlobScheduleEntry) phase0.ForkDigest { if cs.specs == nil || cs.genesis == nil { return phase0.ForkDigest{} diff --git a/clients/execution/chainstate.go b/clients/execution/chainstate.go index 1c5ac13e..05a3166e 100644 --- a/clients/execution/chainstate.go +++ b/clients/execution/chainstate.go @@ -14,6 +14,8 @@ import ( ) var DefaultSystemContractAddresses = map[string]common.Address{ + rpc.BeaconRootsContract: common.HexToAddress("0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"), + rpc.HistoryStorageContract: common.HexToAddress("0x0000F90827F1C53a10cb7A02335B175320002935"), rpc.ConsolidationRequestContract: common.HexToAddress("0x0000BBdDc7CE488642fb579F8B00f3a590007251"), rpc.WithdrawalRequestContract: common.HexToAddress("0x00000961Ef480Eb55e80D19ad83579A64c007002"), } diff --git a/clients/execution/rpc/ethconfig.go b/clients/execution/rpc/ethconfig.go index a8ca0ecf..c4540ae7 100644 --- a/clients/execution/rpc/ethconfig.go +++ b/clients/execution/rpc/ethconfig.go @@ -11,6 +11,8 @@ const ( DepositContract = "DEPOSIT_CONTRACT_ADDRESS" ConsolidationRequestContract = "CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS" WithdrawalRequestContract = "WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS" + BeaconRootsContract = "BEACON_ROOTS_ADDRESS" + HistoryStorageContract = "HISTORY_STORAGE_ADDRESS" ) type EthConfigFork struct { diff --git a/db/schema/pgsql/20260216000000_el-execdata-rework.sql b/db/schema/pgsql/20260216000000_el-execdata-rework.sql index 72aa8daf..953a65c4 100644 --- a/db/schema/pgsql/20260216000000_el-execdata-rework.sql +++ b/db/schema/pgsql/20260216000000_el-execdata-rework.sql @@ -4,6 +4,9 @@ -- Drop el_tx_events table (event data moves to blockdb in Mode 3) DROP TABLE IF EXISTS public."el_tx_events"; +-- Drop el_internal_transactions table (replaced by trace-based el_transactions_internal) +DROP TABLE IF EXISTS public."el_internal_transactions"; + -- Add data tracking columns to el_blocks for blockdb data availability -- data_status bit flags: 0x01=events, 0x02=call traces, 0x04=state changes -- data_size: compressed size in bytes of blockdb object (0 = no data) diff --git a/db/schema/sqlite/20260216000000_el-execdata-rework.sql b/db/schema/sqlite/20260216000000_el-execdata-rework.sql index b8fcb57d..4ef6ef72 100644 --- a/db/schema/sqlite/20260216000000_el-execdata-rework.sql +++ b/db/schema/sqlite/20260216000000_el-execdata-rework.sql @@ -4,6 +4,9 @@ -- Drop el_tx_events table (event data moves to blockdb in Mode 3) DROP TABLE IF EXISTS "el_tx_events"; +-- Drop el_internal_transactions table (replaced by trace-based el_transactions_internal) +DROP TABLE IF EXISTS "el_internal_transactions"; + -- Add data tracking columns to el_blocks for blockdb data availability -- data_status bit flags: 0x01=events, 0x02=call traces, 0x04=state changes -- data_size: compressed size in bytes of blockdb object (0 = no data) diff --git a/go.mod b/go.mod index ba727852..31082e6c 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,6 @@ require ( github.com/DataDog/zstd v1.5.7 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 // indirect - github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/VictoriaMetrics/fastcache v1.13.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/casbin/govaluate v1.10.0 // indirect @@ -260,3 +259,7 @@ require ( modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.38.2 // indirect ) + +replace github.com/attestantio/go-eth2-client => github.com/pk910/go-eth2-client v0.0.0-20260107112128-b97b7522025a + +replace github.com/ethereum/go-ethereum => github.com/ethereum/go-ethereum v0.0.0-20251217020005-abaef48d549a diff --git a/go.sum b/go.sum index 68adbe14..127cfc2c 100644 --- a/go.sum +++ b/go.sum @@ -29,15 +29,11 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 h1:d/SJkN8/9Ca+1YmuDiUJxAiV4w/a9S8NcsG7GMQSrVI= github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506/go.mod h1:6TZI4FU6zT8x6ZfWa1J8YQ2NgW0wLV/W3fHRca8ISBo= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= -github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/attestantio/go-eth2-client v0.28.0 h1:2zIIIMPvSD+g6h3TgVXsoda/Yw3e+wjo1e8CZEanORU= -github.com/attestantio/go-eth2-client v0.28.0/go.mod h1:PO9sHFCq+1RiG+Eh3eOR2GYvYV64Qzg7idM3kLgCs5k= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -119,8 +115,8 @@ github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3 github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk= github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8= -github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9igY7law= -github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk= +github.com/ethereum/go-ethereum v0.0.0-20251217020005-abaef48d549a h1:ZJBRP4AuJ3K3U74tacJ8syixL536AU1B4jTCH7CDaQs= +github.com/ethereum/go-ethereum v0.0.0-20251217020005-abaef48d549a/go.mod h1:kId9vOtlYg3PZk9VwKbGlQmSACB5ESPTBGT+M9zjmok= github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8= github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk= github.com/ethpandaops/ethcore v0.0.0-20251209022721-8a6a91f46524 h1:xIg0pOUc1U+4Til1csmZjisi5gXMoI8OUn5v7c8X+JY= @@ -569,6 +565,8 @@ github.com/pion/webrtc/v4 v4.1.4 h1:/gK1ACGHXQmtyVVbJFQDxNoODg4eSRiFLB7t9r9pg8M= github.com/pion/webrtc/v4 v4.1.4/go.mod h1:Oab9npu1iZtQRMic3K3toYq5zFPvToe/QBw7dMI2ok4= github.com/pk910/dynamic-ssz v1.2.1 h1:84eNMiiOYDiNC2Y1m5A/UtIPs6u/9SsvG4RVSBRGE5U= github.com/pk910/dynamic-ssz v1.2.1/go.mod h1:HXRWLNcgj3DL65Kznrb+RdL3DEKw2JBZ/6crooqGoII= +github.com/pk910/go-eth2-client v0.0.0-20260107112128-b97b7522025a h1:913U5Ysj0Xd4j8WFUwK94vKpI6GUz52ZFi4nxgaPp4M= +github.com/pk910/go-eth2-client v0.0.0-20260107112128-b97b7522025a/go.mod h1:1MBvz1DVe6jZduHJDJuJ/1S4LzYmDmvx+r5alo4pyrM= github.com/pk910/hashtree-bindings v0.0.1 h1:Sw+UlPlrBle4LUg04kqLFybVQcfmamwKL1QsrR3GU0g= github.com/pk910/hashtree-bindings v0.0.1/go.mod h1:eayIpxMFkWzMsydESu/5bV8wglZzSE/c9mq6DQdn204= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= diff --git a/handlers/api/network_forks_v1.go b/handlers/api/network_forks_v1.go index 7a2c8229..0ddfcce3 100644 --- a/handlers/api/network_forks_v1.go +++ b/handlers/api/network_forks_v1.go @@ -112,7 +112,8 @@ func buildNetworkForks(chainState *consensus.ChainState) []*APINetworkForkInfo { // Helper function to add consensus fork addConsensusFork := func(name string, forkEpoch *uint64, forkVersion phase0.Version) { if forkEpoch != nil && *forkEpoch < uint64(18446744073709551615) { - forkDigest := chainState.GetForkDigest(forkVersion, nil) + blobParams := chainState.GetBlobScheduleForEpoch(phase0.Epoch(*forkEpoch)) + forkDigest := chainState.GetForkDigest(forkVersion, blobParams) version := fmt.Sprintf("0x%x", forkVersion) epoch := *forkEpoch forks = append(forks, &APINetworkForkInfo{ @@ -135,6 +136,7 @@ func buildNetworkForks(chainState *consensus.ChainState) []*APINetworkForkInfo { addConsensusFork("Deneb", specs.DenebForkEpoch, specs.DenebForkVersion) addConsensusFork("Electra", specs.ElectraForkEpoch, specs.ElectraForkVersion) addConsensusFork("Fulu", specs.FuluForkEpoch, specs.FuluForkVersion) + addConsensusFork("Gloas", specs.GloasForkEpoch, specs.GloasForkVersion) // Add BPO forks from BLOB_SCHEDULE for i, blobSchedule := range specs.BlobSchedule { diff --git a/handlers/index.go b/handlers/index.go index 94f15060..2352bd0f 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -290,6 +290,19 @@ func buildIndexPageData(ctx context.Context) (*models.IndexPageData, time.Durati ForkDigest: forkDigest[:], }) } + if specs.GloasForkEpoch != nil && *specs.GloasForkEpoch < uint64(18446744073709551615) { + blobParams := chainState.GetBlobScheduleForEpoch(phase0.Epoch(*specs.GloasForkEpoch)) + forkDigest := chainState.GetForkDigest(specs.GloasForkVersion, blobParams) + pageData.NetworkForks = append(pageData.NetworkForks, &models.IndexPageDataForks{ + Name: "Gloas", + Epoch: *specs.GloasForkEpoch, + Version: specs.GloasForkVersion[:], + Time: uint64(chainState.EpochToTime(phase0.Epoch(*specs.GloasForkEpoch)).Unix()), + Active: uint64(currentEpoch) >= *specs.GloasForkEpoch, + Type: "consensus", + ForkDigest: forkDigest[:], + }) + } // Add BPO forks from BLOB_SCHEDULE elBlobSchedule := services.GlobalBeaconService.GetExecutionChainState().GetFullBlobSchedule() diff --git a/handlers/slot.go b/handlers/slot.go index 0a2750c3..dfa63a6c 100644 --- a/handlers/slot.go +++ b/handlers/slot.go @@ -1,6 +1,7 @@ package handlers import ( + "bytes" "context" "encoding/hex" "encoding/json" @@ -16,11 +17,15 @@ import ( "github.com/attestantio/go-eth2-client/spec/bellatrix" "github.com/attestantio/go-eth2-client/spec/electra" "github.com/attestantio/go-eth2-client/spec/phase0" + "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/types/bal" + "github.com/ethereum/go-ethereum/rlp" "github.com/gorilla/mux" "github.com/sirupsen/logrus" "github.com/ethpandaops/dora/blockdb" + "github.com/ethpandaops/dora/clients/execution/rpc" "github.com/ethpandaops/dora/db" "github.com/ethpandaops/dora/dbtypes" "github.com/ethpandaops/dora/indexer/beacon" @@ -46,6 +51,7 @@ func Slot(w http.ResponseWriter, r *http.Request) { "slot/deposit_requests.html", "slot/withdrawal_requests.html", "slot/consolidation_requests.html", + "slot/block_access_list.html", ) var notfoundTemplateFiles = append(layoutTemplateFiles, "slot/notfound.html", @@ -82,6 +88,14 @@ func Slot(w http.ResponseWriter, r *http.Request) { return } + if urlArgs.Get("action") == "parse-access-list" { + if err := handleSlotParseAccessList(w, r); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + return + } + pageData, pageError := getSlotPageData(blockSlot, blockRootHash) if pageError != nil { handlePageError(w, r, pageError) @@ -298,6 +312,14 @@ func buildSlotPageData(ctx context.Context, blockSlot int64, blockRoot []byte) ( } pageData.Block = getSlotPageBlockData(ctx, blockData, epochStatsValues, blockUid) + // Create transaction details map for access list UI + if pageData.Block != nil && pageData.Block.Transactions != nil { + pageData.TransactionDetails = make(map[uint64]*models.SlotPageTransaction) + for _, tx := range pageData.Block.Transactions { + pageData.TransactionDetails[tx.Index] = tx + } + } + // check mev block if pageData.Block.ExecutionData != nil { mevBlock := db.GetMevBlockByBlockHash(ctx, pageData.Block.ExecutionData.BlockHash) @@ -320,6 +342,33 @@ func buildSlotPageData(ctx context.Context, blockSlot int64, blockRoot []byte) ( } } + // Add system contract addresses + pageData.SystemContracts = make(map[string]string) + + // Get system contract addresses from execution chain state + if execChainState := services.GlobalBeaconService.GetExecutionChainState(); execChainState != nil { + // Add known system contracts + consolidationAddr := execChainState.GetSystemContractAddress(rpc.ConsolidationRequestContract) + pageData.SystemContracts[consolidationAddr.Hex()] = "Consolidation Request Contract (EIP-7251)" + + withdrawalAddr := execChainState.GetSystemContractAddress(rpc.WithdrawalRequestContract) + pageData.SystemContracts[withdrawalAddr.Hex()] = "Withdrawal Request Contract (EIP-7002)" + + beaconRootsAddr := execChainState.GetSystemContractAddress(rpc.BeaconRootsContract) + pageData.SystemContracts[beaconRootsAddr.Hex()] = "Beacon Roots Contract (EIP-4788)" + + historyStorageAddr := execChainState.GetSystemContractAddress(rpc.HistoryStorageContract) + pageData.SystemContracts[historyStorageAddr.Hex()] = "History Storage Contract (EIP-2935)" + } + + // Add deposit contract from beacon chain config + if chainState := services.GlobalBeaconService.GetChainState(); chainState != nil { + if specs := chainState.GetSpecs(); specs != nil && specs.DepositContractAddress != nil { + depositAddr := common.BytesToAddress(specs.DepositContractAddress) + pageData.SystemContracts[depositAddr.Hex()] = "Deposit Contract" + } + } + return pageData, cacheTimeout } @@ -820,6 +869,86 @@ func getSlotPageBlockData(ctx context.Context, blockData *services.CombinedBlock getSlotPageTransactions(ctx, pageData, transactions, blockUid) } + if blockAccessListRlp, err := executionPayload.BlockAccessList(); err == nil { + // decode RLP encoded block access list + var blockAccessList bal.BlockAccessList + err = blockAccessList.DecodeRLP(rlp.NewStream(bytes.NewReader(blockAccessListRlp), 0)) + if err != nil { + logrus.Warnf("error decoding block access list: %v", err) + } else { + pageData.ExecutionData.BlockAccessList = make([]*models.SlotPageBlockAccessListEntry, len(blockAccessList)) + for i, entry := range blockAccessList { + balEntry := &models.SlotPageBlockAccessListEntry{ + Address: entry.Address[:], + } + + // Convert storage changes + if len(entry.StorageChanges) > 0 { + balEntry.StorageChanges = make([]*models.SlotPageBlockBALStorageChange, len(entry.StorageChanges)) + for j, storageChange := range entry.StorageChanges { + changes := make([]*models.SlotPageBlockBALStorageSlotChange, len(storageChange.Accesses)) + for k, write := range storageChange.Accesses { + valueAfter := write.ValueAfter.ToHash() + changes[k] = &models.SlotPageBlockBALStorageSlotChange{ + BlockAccessIndex: write.TxIdx, + Value: valueAfter[:], + } + } + slot := storageChange.Slot.ToHash() + balEntry.StorageChanges[j] = &models.SlotPageBlockBALStorageChange{ + Slot: slot[:], + Changes: changes, + } + } + } + + // Convert storage reads + if len(entry.StorageReads) > 0 { + balEntry.StorageReads = make([][]byte, len(entry.StorageReads)) + for j, read := range entry.StorageReads { + slot := read.ToHash() + balEntry.StorageReads[j] = slot[:] + } + } + + // Convert balance changes + if len(entry.BalanceChanges) > 0 { + balEntry.BalanceChanges = make([]*models.SlotPageBlockBALBalanceChange, len(entry.BalanceChanges)) + for j, balanceChange := range entry.BalanceChanges { + balEntry.BalanceChanges[j] = &models.SlotPageBlockBALBalanceChange{ + BlockAccessIndex: balanceChange.TxIdx, + Balance: balanceChange.Balance.Bytes(), + } + } + } + + // Convert nonce changes + if len(entry.NonceChanges) > 0 { + balEntry.NonceChanges = make([]*models.SlotPageBlockBALNonceChange, len(entry.NonceChanges)) + for j, nonceChange := range entry.NonceChanges { + balEntry.NonceChanges[j] = &models.SlotPageBlockBALNonceChange{ + BlockAccessIndex: nonceChange.TxIdx, + Nonce: nonceChange.Nonce, + } + } + } + + // Convert code changes + if len(entry.CodeChanges) > 0 { + balEntry.CodeChanges = make([]*models.SlotPageBlockBALCodeChange, len(entry.CodeChanges)) + for j, codeChange := range entry.CodeChanges { + balEntry.CodeChanges[j] = &models.SlotPageBlockBALCodeChange{ + BlockAccessIndex: codeChange.TxIdx, + Code: codeChange.Code, + } + } + } + + pageData.ExecutionData.BlockAccessList[i] = balEntry + } + } + } + // Check if execution data exists in blockdb for receipt downloads if blockdb.GlobalBlockDb != nil && blockdb.GlobalBlockDb.SupportsExecData() { hasExecData, _ := blockdb.GlobalBlockDb.HasExecData( @@ -1084,3 +1213,106 @@ func getSlotPageConsolidationRequests(pageData *models.SlotPageBlockData, consol pageData.ConsolidationRequestsCount = uint64(len(pageData.ConsolidationRequests)) } + +func handleSlotParseAccessList(w http.ResponseWriter, r *http.Request) error { + w.Header().Set("Content-Type", "application/json") + + if r.Method != http.MethodPost { + return fmt.Errorf("method not allowed") + } + + rlpHex := r.PostFormValue("rlp") + if rlpHex == "" { + return fmt.Errorf("missing rlp parameter") + } + + // Remove 0x prefix if present + rlpHex = strings.TrimPrefix(rlpHex, "0x") + + // Decode hex to bytes + rlpBytes, err := hex.DecodeString(rlpHex) + if err != nil { + return fmt.Errorf("invalid hex: %v", err) + } + + // Parse RLP encoded access list + var blockAccessList bal.BlockAccessList + err = blockAccessList.DecodeRLP(rlp.NewStream(bytes.NewReader(rlpBytes), 0)) + if err != nil { + return fmt.Errorf("invalid access list RLP: %v", err) + } + + // Convert to our model format + result := make([]*models.SlotPageBlockAccessListEntry, len(blockAccessList)) + for i, entry := range blockAccessList { + balEntry := &models.SlotPageBlockAccessListEntry{ + Address: entry.Address[:], + } + + // Convert storage changes + if len(entry.StorageChanges) > 0 { + balEntry.StorageChanges = make([]*models.SlotPageBlockBALStorageChange, len(entry.StorageChanges)) + for j, storageChange := range entry.StorageChanges { + changes := make([]*models.SlotPageBlockBALStorageSlotChange, len(storageChange.Accesses)) + for k, write := range storageChange.Accesses { + valueAfter := write.ValueAfter.ToHash() + changes[k] = &models.SlotPageBlockBALStorageSlotChange{ + BlockAccessIndex: write.TxIdx, + Value: valueAfter[:], + } + } + slot := storageChange.Slot.ToHash() + balEntry.StorageChanges[j] = &models.SlotPageBlockBALStorageChange{ + Slot: slot[:], + Changes: changes, + } + } + } + + // Convert storage reads + if len(entry.StorageReads) > 0 { + balEntry.StorageReads = make([][]byte, len(entry.StorageReads)) + for j, read := range entry.StorageReads { + slot := read.ToHash() + balEntry.StorageReads[j] = slot[:] + } + } + + // Convert balance changes + if len(entry.BalanceChanges) > 0 { + balEntry.BalanceChanges = make([]*models.SlotPageBlockBALBalanceChange, len(entry.BalanceChanges)) + for j, balanceChange := range entry.BalanceChanges { + balEntry.BalanceChanges[j] = &models.SlotPageBlockBALBalanceChange{ + BlockAccessIndex: balanceChange.TxIdx, + Balance: balanceChange.Balance.Bytes(), + } + } + } + + // Convert nonce changes + if len(entry.NonceChanges) > 0 { + balEntry.NonceChanges = make([]*models.SlotPageBlockBALNonceChange, len(entry.NonceChanges)) + for j, nonceChange := range entry.NonceChanges { + balEntry.NonceChanges[j] = &models.SlotPageBlockBALNonceChange{ + BlockAccessIndex: nonceChange.TxIdx, + Nonce: nonceChange.Nonce, + } + } + } + + // Convert code changes + if len(entry.CodeChanges) > 0 { + balEntry.CodeChanges = make([]*models.SlotPageBlockBALCodeChange, len(entry.CodeChanges)) + for j, codeChange := range entry.CodeChanges { + balEntry.CodeChanges[j] = &models.SlotPageBlockBALCodeChange{ + BlockAccessIndex: codeChange.TxIdx, + Code: codeChange.Code, + } + } + } + + result[i] = balEntry + } + + return json.NewEncoder(w).Encode(result) +} diff --git a/indexer/beacon/block_helper.go b/indexer/beacon/block_helper.go index c943ede1..8fa7069a 100644 --- a/indexer/beacon/block_helper.go +++ b/indexer/beacon/block_helper.go @@ -10,6 +10,7 @@ import ( "github.com/attestantio/go-eth2-client/spec/capella" "github.com/attestantio/go-eth2-client/spec/deneb" "github.com/attestantio/go-eth2-client/spec/electra" + "github.com/attestantio/go-eth2-client/spec/gloas" "github.com/attestantio/go-eth2-client/spec/phase0" "github.com/ethpandaops/dora/utils" dynssz "github.com/pk910/dynamic-ssz" @@ -47,6 +48,9 @@ func MarshalVersionedSignedBeaconBlockSSZ(dynSsz *dynssz.DynSsz, block *spec.Ver case spec.DataVersionFulu: version = uint64(block.Version) ssz, err = dynSsz.MarshalSSZ(block.Fulu) + case spec.DataVersionGloas: + version = uint64(block.Version) + ssz, err = dynSsz.MarshalSSZ(block.Gloas) default: err = fmt.Errorf("unknown block version") } @@ -118,6 +122,11 @@ func UnmarshalVersionedSignedBeaconBlockSSZ(dynSsz *dynssz.DynSsz, version uint6 if err := dynSsz.UnmarshalSSZ(block.Fulu, ssz); err != nil { return nil, fmt.Errorf("failed to decode fulu signed beacon block: %v", err) } + case spec.DataVersionGloas: + block.Gloas = &gloas.SignedBeaconBlock{} + if err := dynSsz.UnmarshalSSZ(block.Gloas, ssz); err != nil { + return nil, fmt.Errorf("failed to decode gloas signed beacon block: %v", err) + } default: return nil, fmt.Errorf("unknown block version") } @@ -148,6 +157,9 @@ func MarshalVersionedSignedBeaconBlockJson(block *spec.VersionedSignedBeaconBloc case spec.DataVersionFulu: version = uint64(block.Version) jsonRes, err = block.Fulu.MarshalJSON() + case spec.DataVersionGloas: + version = uint64(block.Version) + jsonRes, err = block.Gloas.MarshalJSON() default: err = fmt.Errorf("unknown block version") } @@ -201,6 +213,11 @@ func unmarshalVersionedSignedBeaconBlockJson(version uint64, ssz []byte) (*spec. if err := block.Fulu.UnmarshalJSON(ssz); err != nil { return nil, fmt.Errorf("failed to decode fulu signed beacon block: %v", err) } + case spec.DataVersionGloas: + block.Gloas = &gloas.SignedBeaconBlock{} + if err := block.Gloas.UnmarshalJSON(ssz); err != nil { + return nil, fmt.Errorf("failed to decode gloas signed beacon block: %v", err) + } default: return nil, fmt.Errorf("unknown block version") } @@ -252,6 +269,12 @@ func getStateRandaoMixes(v *spec.VersionedBeaconState) ([]phase0.Root, error) { } return v.Fulu.RANDAOMixes, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.RANDAOMixes == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.RANDAOMixes, nil default: return nil, errors.New("unknown version") } @@ -274,6 +297,8 @@ func getStateDepositIndex(state *spec.VersionedBeaconState) uint64 { return state.Electra.ETH1DepositIndex case spec.DataVersionFulu: return state.Fulu.ETH1DepositIndex + case spec.DataVersionGloas: + return state.Gloas.ETH1DepositIndex } return 0 } @@ -319,6 +344,12 @@ func getStateCurrentSyncCommittee(v *spec.VersionedBeaconState) ([]phase0.BLSPub } return v.Fulu.CurrentSyncCommittee.Pubkeys, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.CurrentSyncCommittee == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.CurrentSyncCommittee.Pubkeys, nil default: return nil, errors.New("unknown version") } @@ -349,6 +380,12 @@ func getStateDepositBalanceToConsume(v *spec.VersionedBeaconState) (phase0.Gwei, } return v.Fulu.DepositBalanceToConsume, nil + case spec.DataVersionGloas: + if v.Gloas == nil { + return 0, errors.New("no gloas block") + } + + return v.Gloas.DepositBalanceToConsume, nil default: return 0, errors.New("unknown version") } @@ -379,6 +416,12 @@ func getStatePendingDeposits(v *spec.VersionedBeaconState) ([]*electra.PendingDe } return v.Fulu.PendingDeposits, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.PendingDeposits == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.PendingDeposits, nil default: return nil, errors.New("unknown version") } @@ -409,6 +452,12 @@ func getStatePendingWithdrawals(v *spec.VersionedBeaconState) ([]*electra.Pendin } return v.Fulu.PendingPartialWithdrawals, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.PendingPartialWithdrawals == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.PendingPartialWithdrawals, nil default: return nil, errors.New("unknown version") } @@ -439,6 +488,12 @@ func getStatePendingConsolidations(v *spec.VersionedBeaconState) ([]*electra.Pen } return v.Fulu.PendingConsolidations, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.PendingConsolidations == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.PendingConsolidations, nil default: return nil, errors.New("unknown version") } @@ -465,6 +520,12 @@ func getStateProposerLookahead(v *spec.VersionedBeaconState) ([]phase0.Validator } return v.Fulu.ProposerLookahead, nil + case spec.DataVersionGloas: + if v.Gloas == nil || v.Gloas.ProposerLookahead == nil { + return nil, errors.New("no gloas block") + } + + return v.Gloas.ProposerLookahead, nil default: return nil, errors.New("unknown version") } @@ -487,6 +548,8 @@ func getBlockSize(dynSsz *dynssz.DynSsz, block *spec.VersionedSignedBeaconBlock) return dynSsz.SizeSSZ(block.Electra) case spec.DataVersionFulu: return dynSsz.SizeSSZ(block.Fulu) + case spec.DataVersionGloas: + return dynSsz.SizeSSZ(block.Gloas) default: return 0, errors.New("unknown version") } diff --git a/indexer/beacon/writedb.go b/indexer/beacon/writedb.go index 8f7f5f58..9c1a5962 100644 --- a/indexer/beacon/writedb.go +++ b/indexer/beacon/writedb.go @@ -409,6 +409,15 @@ func (dbw *dbWriter) buildDbBlock(block *Block, epochStats *EpochStats, override dbBlock.EthBaseFee = utils.GetBaseFeeAsUint64(payload.BaseFeePerGas) dbBlock.EthFeeRecipient = payload.FeeRecipient[:] } + case spec.DataVersionGloas: + if blockBody.Gloas != nil && blockBody.Gloas.Message != nil && + blockBody.Gloas.Message.Body != nil && blockBody.Gloas.Message.Body.ExecutionPayload != nil { + payload := blockBody.Gloas.Message.Body.ExecutionPayload + dbBlock.EthGasUsed = payload.GasUsed + dbBlock.EthGasLimit = payload.GasLimit + dbBlock.EthBaseFee = utils.GetBaseFeeAsUint64(payload.BaseFeePerGas) + dbBlock.EthFeeRecipient = payload.FeeRecipient[:] + } } } @@ -564,6 +573,13 @@ func (dbw *dbWriter) buildDbEpoch(epoch phase0.Epoch, blocks []*Block, epochStat dbEpoch.EthGasUsed += payload.GasUsed dbEpoch.EthGasLimit += payload.GasLimit } + case spec.DataVersionGloas: + if blockBody.Gloas != nil && blockBody.Gloas.Message != nil && + blockBody.Gloas.Message.Body != nil && blockBody.Gloas.Message.Body.ExecutionPayload != nil { + payload := blockBody.Gloas.Message.Body.ExecutionPayload + dbEpoch.EthGasUsed += payload.GasUsed + dbEpoch.EthGasLimit += payload.GasLimit + } } } } diff --git a/indexer/execution/txindexer/process_transactions.go b/indexer/execution/txindexer/process_transactions.go index 5a17c1ba..0f929d10 100644 --- a/indexer/execution/txindexer/process_transactions.go +++ b/indexer/execution/txindexer/process_transactions.go @@ -34,6 +34,10 @@ var ( // TransferBatch(address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values) topicTransferBatch = common.HexToHash("0x4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb") + + // ethereum system address that emits native ETH transfer logs. + // These must not be tracked as ERC20 transfers. + ethereumSystemAddress = common.HexToAddress("0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE") ) // txProcessingContext holds state for processing a block's transactions. @@ -644,6 +648,12 @@ func (ctx *txProcessingContext) detectTokenTransfers( return nil } + // Skip native ETH transfer logs from the ethereum system address. + // These are already tracked as regular transaction value transfers. + if log.Address == ethereumSystemAddress { + return nil + } + topic0 := log.Topics[0] transfers := make([]*pendingTokenTransfer, 0) diff --git a/templates/slot/block_access_list.html b/templates/slot/block_access_list.html new file mode 100644 index 00000000..d986237e --- /dev/null +++ b/templates/slot/block_access_list.html @@ -0,0 +1,1981 @@ +{{ define "block_access_list" }} + + +
+ {{ if .Block.ExecutionData.BlockAccessList }} +
+

Block Access List Entries

+ +
+ {{ range $i, $entry := .Block.ExecutionData.BlockAccessList }} +
+ +
+
+ +
+
+
+ {{ end }} + {{ else }} +
+ + No block access list data available for this slot. +
+ {{ end }} +
+ + + + + + + +{{ end }} \ No newline at end of file diff --git a/templates/slot/overview.html b/templates/slot/overview.html index 40c031a5..12cccef7 100644 --- a/templates/slot/overview.html +++ b/templates/slot/overview.html @@ -363,6 +363,15 @@ {{ end }} + {{ if .BlockAccessListHash }} +
+
BAL Hash:
+
+ 0x{{ printf "%x" .BlockAccessListHash }} +
+
+ {{ end }} +
Transactions:
{{ $block.TransactionsCount }}
diff --git a/templates/slot/slot.html b/templates/slot/slot.html index 74fe5c76..f769b22d 100644 --- a/templates/slot/slot.html +++ b/templates/slot/slot.html @@ -96,6 +96,11 @@

Consolidation Requests {{ .Block.ConsolidationRequestsCount }} {{ end }} + {{ if .Block.ExecutionData.BlockAccessList }} + + {{ end }} {{ if .Block }}

{{ end }} + {{ if .Block.ExecutionData.BlockAccessList }} +
+
+
+
+

Block Access List (EIP-7928)

+
+
+ {{ template "block_access_list" . }} +
+
+ {{ end }} {{ if .Block }}
@@ -358,7 +375,10 @@

Tracoor Traces

{{ end }}