From 52dab1c0a3f1b9607974a2abfd4cab1837e85da7 Mon Sep 17 00:00:00 2001 From: Jakub Pajek Date: Tue, 28 Jan 2025 16:44:29 +0900 Subject: [PATCH 1/2] ethstats: added votes count to block stats (dummy for now) --- ethstats/ethstats.go | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index 6faaf31d62f..b6c723019e5 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -585,6 +585,8 @@ type blockStats struct { TxHash common.Hash `json:"transactionsRoot"` Root common.Hash `json:"stateRoot"` Uncles uncleStats `json:"uncles"` + // ADDED by Jakub Pajek (ethstats votes count) + Votes voteStats `json:"votes"` } // txStats is the information to report about individual transactions. @@ -603,6 +605,26 @@ func (s uncleStats) MarshalJSON() ([]byte, error) { return []byte("[]"), nil } +// ADDED by Jakub Pajek BEG (ethstats votes count) + +type vote struct { + Address common.Address `json:"address"` + Proposal string `json:"proposal"` +} + +// voteStats is a custom wrapper around an vote array to force serializing +// empty arrays instead of returning null for them. +type voteStats []vote + +func (s voteStats) MarshalJSON() ([]byte, error) { + if votes := ([]vote)(s); len(votes) > 0 { + return json.Marshal(votes) + } + return []byte("[]"), nil +} + +// ADDED by Jakub Pajek END (ethstats votes count) + // reportBlock retrieves the current chain head and reports it to the stats server. func (s *Service) reportBlock(conn *connWrapper, block *types.Block) error { // Gather the block details from the header or block chain @@ -630,6 +652,8 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { td *big.Int txs []txStats uncles []*types.Header + // ADDED by Jakub Pajek (ethstats votes count) + votes []vote ) // check if backend is a full node @@ -667,6 +691,28 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { txs = []txStats{} } + // ADDED by Jakub Pajek BEG (ethstats votes count) + { + votesCount := header.Number.Int64() % 10 + votes = make([]vote, votesCount) + for i := 0; i < int(votesCount); i++ { + var proposal string + switch i % 3 { + case 0: + proposal = "signer" + case 1: + proposal = "voter" + case 2: + proposal = "drop" + } + votes[i] = vote{ + Address: common.BigToAddress(new(big.Int).SetUint64(uint64(i) + 1)), + Proposal: proposal, + } + } + } + // ADDED by Jakub Pajek END (ethstats votes count) + // Assemble and return the block stats author, _ := s.engine.Author(header) @@ -684,6 +730,8 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { TxHash: header.TxHash, Root: header.Root, Uncles: uncles, + // ADDED by Jakub Pajek (ethstats votes count) + Votes: votes, } } From 8e8b177eb3eb9e3d9a3259ab18ef31560b5d69e8 Mon Sep 17 00:00:00 2001 From: Jakub Pajek Date: Mon, 3 Feb 2025 17:46:20 +0900 Subject: [PATCH 2/2] ethstats: added votes count to block stats --- ethstats/ethstats.go | 85 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 18 deletions(-) diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go index b6c723019e5..395beee5c04 100644 --- a/ethstats/ethstats.go +++ b/ethstats/ethstats.go @@ -43,6 +43,7 @@ import ( "github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "github.com/gorilla/websocket" ) @@ -70,6 +71,8 @@ type backend interface { GetTd(ctx context.Context, hash common.Hash) *big.Int Stats() (pending int, queued int) SyncProgress() ethereum.SyncProgress + // ADDED by Jakub Pajek (ethstats votes count) + ChainConfig() *params.ChainConfig } // fullNodeBackend encompasses the functionality necessary for a full node @@ -605,7 +608,7 @@ func (s uncleStats) MarshalJSON() ([]byte, error) { return []byte("[]"), nil } -// ADDED by Jakub Pajek BEG (ethstats votes count) +// ADDED by Jakub Pajek BEG (ethstats votes count) type vote struct { Address common.Address `json:"address"` @@ -623,7 +626,36 @@ func (s voteStats) MarshalJSON() ([]byte, error) { return []byte("[]"), nil } -// ADDED by Jakub Pajek END (ethstats votes count) +// isTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block. +// It depends on the parentHash already being stored in the database. +// If the parentHash is not stored in the database a UnknownAncestor error is returned. +func (s *Service) isTTDReached(parentHash common.Hash) (bool, error) { + ttd := s.backend.ChainConfig().TerminalTotalDifficulty + if ttd == nil { + return false, nil + } + td := s.backend.GetTd(context.Background(), parentHash) + if td == nil { + return false, consensus.ErrUnknownAncestor + } + return td.Cmp(ttd) >= 0, nil +} + +// poaEngine returns the PoA consensus engine, or nil if PoW or PoS is being used. +func (s *Service) poaEngine(header *types.Header) consensus.PoA { + if pos, ok := s.engine.(consensus.PoS); ok { + if poa, ok := pos.EthOneEngine().(consensus.PoA); ok { + if reached, err := s.isTTDReached(header.ParentHash); err == nil && !reached { + return poa + } + } + } else if poa, ok := s.engine.(consensus.PoA); ok { + return poa + } + return nil +} + +// ADDED by Jakub Pajek END (ethstats votes count) // reportBlock retrieves the current chain head and reports it to the stats server. func (s *Service) reportBlock(conn *connWrapper, block *types.Block) error { @@ -692,22 +724,39 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats { } // ADDED by Jakub Pajek BEG (ethstats votes count) - { - votesCount := header.Number.Int64() % 10 - votes = make([]vote, votesCount) - for i := 0; i < int(votesCount); i++ { - var proposal string - switch i % 3 { - case 0: - proposal = "signer" - case 1: - proposal = "voter" - case 2: - proposal = "drop" - } - votes[i] = vote{ - Address: common.BigToAddress(new(big.Int).SetUint64(uint64(i) + 1)), - Proposal: proposal, + if poa, cliqueCfg := s.poaEngine(header), s.backend.ChainConfig().Clique; poa != nil && cliqueCfg != nil { + // MEMO by Jakub Pajek (clique config: variable period) + // How to handle variable epoch changing with the number of sealers? + cliqueEpoch := cliqueCfg[0].Epoch + if cliqueEpoch == 0 { + cliqueEpoch = params.CliqueEpoch + } + if checkpoint, extraBytes := header.Number.Uint64()%cliqueEpoch == 0, len(header.Extra)-params.CliqueExtraVanity-params.CliqueExtraSeal; !checkpoint && extraBytes > 0 { + voteCount := extraBytes / (common.AddressLength + 1) + votes = make([]vote, voteCount) + for voteIdx := 0; voteIdx < voteCount; voteIdx++ { + // Get the address of the vote + index := params.CliqueExtraVanity + voteIdx*(common.AddressLength+1) + var address common.Address + copy(address[:], header.Extra[index:]) + // Get the proposal of the vote + index += common.AddressLength + var proposal string + switch header.Extra[index] { + case params.CliqueExtraVoterVote: + proposal = "voter" + case params.CliqueExtraSignerVote: + proposal = "signer" + case params.CliqueExtraDropVote: + proposal = "drop" + default: + proposal = "unknown" + } + // Add the vote + votes[voteIdx] = vote{ + Address: address, + Proposal: proposal, + } } } }