feat(consensus): add RPC endpoint to query signing tx count by epoch

This commit is contained in:
liam.lai 2026-03-23 01:19:13 -07:00
parent 9579593438
commit 99bbb60b9b
3 changed files with 80 additions and 2 deletions

View file

@ -769,3 +769,75 @@ func (api *API) GetBlockInfoByEpochNum(epochNumber uint64) (*utils.EpochNumInfo,
} }
return api.GetBlockInfoByV2EpochNum(epochNumber) return api.GetBlockInfoByV2EpochNum(epochNumber)
} }
// GetSigningTxCountByEpoch returns the signing transaction count for ALL masternodes
// (including non-active ones) in the epoch that ends at epochBlockNum.
// epochBlockNum must be an epoch-switch block number.
func (api *API) GetSigningTxCountByEpoch(epochBlockNum rpc.BlockNumber) (map[common.Address]uint64, error) {
header := api.chain.GetHeaderByNumber(uint64(epochBlockNum.Int64()))
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 {
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
}

View file

@ -124,7 +124,7 @@ func (x *XDPoS_v2) getTCEpochInfo(chain consensus.ChainReader, timeoutRound type
Round: epochRound, Round: epochRound,
Number: epochSwitchInfo.EpochSwitchBlockInfo.Number, 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 { for epochBlockInfo.Round > timeoutRound && tempTCEpoch > 0 {
tempTCEpoch-- tempTCEpoch--
epochBlockInfo, err = x.GetBlockByEpochNumber(chain, 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) log.Debug("[getTCEpochInfo] Loop to get right epochInfo", "number", epochBlockInfo.Number, "round", epochBlockInfo.Round, "tcRound", timeoutRound, "tcEpoch", tempTCEpoch)
} }
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) epochInfo, err := x.getEpochSwitchInfo(chain, nil, epochBlockInfo.Hash)
if err != nil { if err != nil {

View file

@ -142,6 +142,12 @@ web3._extend({
params: 3, params: 3,
inputFormatter: [null, web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter] 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: [ properties: [
new web3._extend.Property({ new web3._extend.Property({