diff --git a/consensus/XDPoS/api.go b/consensus/XDPoS/api.go index 3bace2c902b6..ef07ea8a30ca 100644 --- a/consensus/XDPoS/api.go +++ b/consensus/XDPoS/api.go @@ -769,3 +769,81 @@ func (api *API) GetBlockInfoByEpochNum(epochNumber uint64) (*utils.EpochNumInfo, } return api.GetBlockInfoByV2EpochNum(epochNumber) } + +// GetSigningTxCountByEpoch returns the signing transaction count for ALL masternodes +// (including non-active ones) in the epoch that immediately precedes the epoch +// that starts at epochBlockNum. In other words, it walks blocks from +// epochBlockNum-1 backwards to the previous epoch-switch block (inclusive). +// epochBlockNum must be an epoch-switch block number that marks the start of an epoch. +func (api *API) GetSigningTxCountByEpoch(epochBlockNum rpc.BlockNumber) (map[common.Address]uint64, error) { + header, err := api.getHeaderFromApiBlockNum(&epochBlockNum) + if err != nil { + return nil, err + } + if header == nil { + return nil, fmt.Errorf("block %d not found", epochBlockNum) + } + + isEpochSwitch, _, err := api.XDPoS.IsEpochSwitch(header) + if err != nil { + return nil, err + } + if !isEpochSwitch { + return nil, fmt.Errorf("block %d is not an epoch switch block", epochBlockNum) + } + + // Walk backwards from epochBlockNum-1 to the previous epoch switch block, + // collecting signing txs from every block. + mapBlkHash := map[uint64]common.Hash{} + // sigData maps blockHash -> list of signers who signed for that block + sigData := make(map[common.Hash][]common.Address) + + h := header + for i := header.Number.Uint64() - 1; ; i-- { + parentHash := h.ParentHash + h = api.chain.GetHeader(parentHash, i) + if h == nil { + return nil, fmt.Errorf("failed to get header at number %d hash %s", i, parentHash.Hex()) + } + + mapBlkHash[i] = h.Hash() + + signingTxs, ok := api.XDPoS.GetCachedSigningTxs(h.Hash()) + if !ok { + block := api.chain.GetBlock(h.Hash(), i) + if block == nil { + return nil, fmt.Errorf("failed to get block at number %d hash %s", i, h.Hash().Hex()) + } + signingTxs = api.XDPoS.CacheSigningTxs(h.Hash(), block.Transactions()) + } + for _, tx := range signingTxs { + blkHash := common.BytesToHash(tx.Data()[len(tx.Data())-32:]) + from := *tx.From() + sigData[blkHash] = append(sigData[blkHash], from) + } + + prevIsEpochSwitch, _, err := api.XDPoS.IsEpochSwitch(h) + if err != nil { + return nil, err + } + if prevIsEpochSwitch || i == 0 { + break + } + } + + // Count signings: for each block at MergeSignRange boundary, tally unique signers. + result := make(map[common.Address]uint64) + for blockNum, blkHash := range mapBlkHash { + if blockNum%common.MergeSignRange != 0 { + continue + } + seen := make(map[common.Address]bool) + for _, addr := range sigData[blkHash] { + if !seen[addr] { + seen[addr] = true + result[addr]++ + } + } + } + return result, nil +} diff --git a/consensus/XDPoS/engines/engine_v2/timeout.go b/consensus/XDPoS/engines/engine_v2/timeout.go index 474831fb3fd7..394d5e1eb08f 100644 --- a/consensus/XDPoS/engines/engine_v2/timeout.go +++ b/consensus/XDPoS/engines/engine_v2/timeout.go @@ -124,7 +124,7 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound type Round: epochRound, Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, } - log.Info("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch) + log.Debug("[getTCEpochInfo] Init epochInfo", "number", epochBlockInfo.Number, "round", epochRound, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch) for epochBlockInfo.Round > timeoutRound && tempTCEpoch > 0 { tempTCEpoch-- epochBlockInfo, err = x.GetBlockByEpochNumber(chain, tempTCEpoch) @@ -135,7 +135,7 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound type log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch) } tcEpoch := tempTCEpoch - log.Info("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tcEpoch) + log.Debug("[getTCEpochInfo] Final TC epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tcEpoch) epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash) if err != nil { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 51d79f531d52..c1961d03dab1 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -142,6 +142,12 @@ web3._extend({ params: 3, inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter] }), + new web3._extend.Method({ + name: 'getSigningTxCountByEpoch', + call: 'XDPoS_getSigningTxCountByEpoch', + params: 1, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] + }), ], properties: [ new web3._extend.Property({