mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 10:50:44 +00:00
Merge pull request #382 from XinFinOrg/new-api-to-list-out-missed-rounds-miners
New api to list out missed rounds miners
This commit is contained in:
commit
e659cb70a8
9 changed files with 225 additions and 12 deletions
|
|
@ -433,6 +433,15 @@ func (x *XDPoS) GetCurrentEpochSwitchBlock(chain consensus.ChainReader, blockNum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS) CalculateMissingRounds(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiMissedRoundsMetadata, error) {
|
||||||
|
switch x.config.BlockConsensusVersion(header.Number, header.Extra, ExtraFieldCheck) {
|
||||||
|
case params.ConsensusEngineVersion2:
|
||||||
|
return x.EngineV2.CalculateMissingRounds(chain, header)
|
||||||
|
default: // Default "v1"
|
||||||
|
return nil, fmt.Errorf("Not supported in the v1 consensus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Same DB across all consensus engines
|
// Same DB across all consensus engines
|
||||||
func (x *XDPoS) GetDb() ethdb.Database {
|
func (x *XDPoS) GetDb() ethdb.Database {
|
||||||
return x.db
|
return x.db
|
||||||
|
|
|
||||||
|
|
@ -224,16 +224,7 @@ func (api *API) GetV2BlockByHeader(header *types.Header, uncle bool) *V2BlockInf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) GetV2BlockByNumber(number *rpc.BlockNumber) *V2BlockInfo {
|
func (api *API) GetV2BlockByNumber(number *rpc.BlockNumber) *V2BlockInfo {
|
||||||
var header *types.Header
|
header := api.getHeaderFromApiBlockNum(number)
|
||||||
if number == nil || *number == rpc.LatestBlockNumber {
|
|
||||||
header = api.chain.CurrentHeader()
|
|
||||||
} else if *number == rpc.CommittedBlockNumber {
|
|
||||||
hash := api.XDPoS.EngineV2.GetLatestCommittedBlockInfo().Hash
|
|
||||||
header = api.chain.GetHeaderByHash(hash)
|
|
||||||
} else {
|
|
||||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if header == nil {
|
if header == nil {
|
||||||
return &V2BlockInfo{
|
return &V2BlockInfo{
|
||||||
Number: big.NewInt(number.Int64()),
|
Number: big.NewInt(number.Int64()),
|
||||||
|
|
@ -285,6 +276,26 @@ func (api *API) NetworkInformation() NetworkInformation {
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
An API exclusively for V2 consensus, designed to assist in troubleshooting miners by identifying who mined during their allocated term.
|
||||||
|
*/
|
||||||
|
func (api *API) GetMissedRoundsInEpochByBlockNum(number *rpc.BlockNumber) (*utils.PublicApiMissedRoundsMetadata, error) {
|
||||||
|
return api.XDPoS.CalculateMissingRounds(api.chain, api.getHeaderFromApiBlockNum(number))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (api *API) getHeaderFromApiBlockNum(number *rpc.BlockNumber) *types.Header {
|
||||||
|
var header *types.Header
|
||||||
|
if number == nil || *number == rpc.LatestBlockNumber {
|
||||||
|
header = api.chain.CurrentHeader()
|
||||||
|
} else if *number == rpc.CommittedBlockNumber {
|
||||||
|
hash := api.XDPoS.EngineV2.GetLatestCommittedBlockInfo().Hash
|
||||||
|
header = api.chain.GetHeaderByHash(hash)
|
||||||
|
} else {
|
||||||
|
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||||
|
}
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
func calculateSigners(message map[string]SignerTypes, pool map[string]map[common.Hash]utils.PoolObj, masternodes []common.Address) {
|
func calculateSigners(message map[string]SignerTypes, pool map[string]map[common.Hash]utils.PoolObj, masternodes []common.Address) {
|
||||||
for name, objs := range pool {
|
for name, objs := range pool {
|
||||||
var currentSigners []common.Address
|
var currentSigners []common.Address
|
||||||
|
|
|
||||||
|
|
@ -360,7 +360,7 @@ func (x *XDPoS_v2) Prepare(chain consensus.ChainReader, header *types.Header) er
|
||||||
}
|
}
|
||||||
|
|
||||||
if header.Coinbase != signer {
|
if header.Coinbase != signer {
|
||||||
log.Error("[Prepare] The mined blocker header coinbase address mismatch with waller address", "headerCoinbase", header.Coinbase.Hex(), "WalletAddress", signer.Hex())
|
log.Error("[Prepare] The mined blocker header coinbase address mismatch with wallet address", "headerCoinbase", header.Coinbase.Hex(), "WalletAddress", signer.Hex())
|
||||||
return consensus.ErrCoinbaseMismatch
|
return consensus.ErrCoinbaseMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,3 +163,54 @@ func (x *XDPoS_v2) GetSignersFromSnapshot(chain consensus.ChainReader, header *t
|
||||||
snap, err := x.getSnapshot(chain, header.Number.Uint64(), false)
|
snap, err := x.getSnapshot(chain, header.Number.Uint64(), false)
|
||||||
return snap.NextEpochMasterNodes, err
|
return snap.NextEpochMasterNodes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *XDPoS_v2) CalculateMissingRounds(chain consensus.ChainReader, header *types.Header) (*utils.PublicApiMissedRoundsMetadata, error) {
|
||||||
|
var missedRounds []utils.MissedRoundInfo
|
||||||
|
switchInfo, err := x.getEpochSwitchInfo(chain, header, header.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
masternodes := switchInfo.Masternodes
|
||||||
|
|
||||||
|
// Loop through from the epoch switch block to the current "header" block
|
||||||
|
nextHeader := header
|
||||||
|
for nextHeader.Number.Cmp(switchInfo.EpochSwitchBlockInfo.Number) > 0 {
|
||||||
|
parentHeader := chain.GetHeaderByHash(nextHeader.ParentHash)
|
||||||
|
parentRound, err := x.GetRoundNumber(parentHeader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
currRound, err := x.GetRoundNumber(nextHeader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// This indicates that an increment in the round number is missing during the block production process.
|
||||||
|
if parentRound+1 != currRound {
|
||||||
|
// We need to iterate from the parentRound to the currRound to determine which miner did not perform mining.
|
||||||
|
for i := parentRound + 1; i < currRound; i++ {
|
||||||
|
leaderIndex := uint64(i) % x.config.Epoch % uint64(len(masternodes))
|
||||||
|
whosTurn := masternodes[leaderIndex]
|
||||||
|
missedRounds = append(
|
||||||
|
missedRounds,
|
||||||
|
utils.MissedRoundInfo{
|
||||||
|
Round: i,
|
||||||
|
Miner: whosTurn,
|
||||||
|
CurrentBlockHash: nextHeader.Hash(),
|
||||||
|
CurrentBlockNum: nextHeader.Number,
|
||||||
|
ParentBlockHash: parentHeader.Hash(),
|
||||||
|
ParentBlockNum: parentHeader.Number,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Assign the pointer to the next one
|
||||||
|
nextHeader = parentHeader
|
||||||
|
}
|
||||||
|
missedRoundsMetadata := &utils.PublicApiMissedRoundsMetadata{
|
||||||
|
EpochRound: switchInfo.EpochSwitchBlockInfo.Round,
|
||||||
|
EpochBlockNumber: switchInfo.EpochSwitchBlockInfo.Number,
|
||||||
|
MissedRounds: missedRounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
return missedRoundsMetadata, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,17 @@ type PublicApiSnapshot struct {
|
||||||
Votes []*clique.Vote `json:"votes"` // List of votes cast in chronological order
|
Votes []*clique.Vote `json:"votes"` // List of votes cast in chronological order
|
||||||
Tally map[common.Address]clique.Tally `json:"tally"` // Current vote tally to avoid recalculating
|
Tally map[common.Address]clique.Tally `json:"tally"` // Current vote tally to avoid recalculating
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MissedRoundInfo struct {
|
||||||
|
Round types.Round
|
||||||
|
Miner common.Address
|
||||||
|
CurrentBlockHash common.Hash
|
||||||
|
CurrentBlockNum *big.Int
|
||||||
|
ParentBlockHash common.Hash
|
||||||
|
ParentBlockNum *big.Int
|
||||||
|
}
|
||||||
|
type PublicApiMissedRoundsMetadata struct {
|
||||||
|
EpochRound types.Round
|
||||||
|
EpochBlockNumber *big.Int
|
||||||
|
MissedRounds []MissedRoundInfo
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfigApi(t *testing.T) {
|
func TestConfigApi(t *testing.T) {
|
||||||
|
|
||||||
bc := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
|
bc := backends.NewXDCSimulatedBackend(core.GenesisAlloc{
|
||||||
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
|
voterAddr: {Balance: new(big.Int).SetUint64(10000000000)},
|
||||||
}, 10000000, params.TestXDPoSMockChainConfig)
|
}, 10000000, params.TestXDPoSMockChainConfig)
|
||||||
|
|
|
||||||
111
consensus/tests/engine_v2_tests/api_test.go
Normal file
111
consensus/tests/engine_v2_tests/api_test.go
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
package engine_v2_tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rpc"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetMissedRoundsInEpochByBlockNumOnlyForV2Consensus(t *testing.T) {
|
||||||
|
_, bc, _, _, _ := PrepareXDCTestBlockChainWith128Candidates(t, 1802, params.TestXDPoSMockChainConfig)
|
||||||
|
|
||||||
|
engine := bc.GetBlockChain().Engine().(*XDPoS.XDPoS)
|
||||||
|
blockNum := rpc.BlockNumber(123)
|
||||||
|
|
||||||
|
data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.EqualError(t, err, "Not supported in the v1 consensus")
|
||||||
|
assert.Nil(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMissedRoundsInEpochByBlockNumReturnEmptyForV2(t *testing.T) {
|
||||||
|
_, bc, cb, _, _ := PrepareXDCTestBlockChainWith128Candidates(t, 1802, params.TestXDPoSMockChainConfig)
|
||||||
|
|
||||||
|
engine := bc.GetBlockChain().Engine().(*XDPoS.XDPoS)
|
||||||
|
blockNum := rpc.BlockNumber(cb.NumberU64())
|
||||||
|
|
||||||
|
data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, types.Round(900), data.EpochRound)
|
||||||
|
assert.Equal(t, big.NewInt(1800), data.EpochBlockNumber)
|
||||||
|
assert.Equal(t, 0, len(data.MissedRounds))
|
||||||
|
|
||||||
|
blockNum = rpc.BlockNumber(1800)
|
||||||
|
|
||||||
|
data, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, types.Round(900), data.EpochRound)
|
||||||
|
assert.Equal(t, big.NewInt(1800), data.EpochBlockNumber)
|
||||||
|
assert.Equal(t, 0, len(data.MissedRounds))
|
||||||
|
|
||||||
|
blockNum = rpc.BlockNumber(1801)
|
||||||
|
|
||||||
|
data, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, types.Round(900), data.EpochRound)
|
||||||
|
assert.Equal(t, big.NewInt(1800), data.EpochBlockNumber)
|
||||||
|
assert.Equal(t, 0, len(data.MissedRounds))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMissedRoundsInEpochByBlockNumReturnEmptyForV2FistEpoch(t *testing.T) {
|
||||||
|
_, bc, _, _, _ := PrepareXDCTestBlockChainWith128Candidates(t, 1802, params.TestXDPoSMockChainConfig)
|
||||||
|
|
||||||
|
engine := bc.GetBlockChain().Engine().(*XDPoS.XDPoS)
|
||||||
|
blockNum := rpc.BlockNumber(901)
|
||||||
|
|
||||||
|
data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, types.Round(1), data.EpochRound)
|
||||||
|
assert.Equal(t, big.NewInt(901), data.EpochBlockNumber)
|
||||||
|
assert.Equal(t, 0, len(data.MissedRounds))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetMissedRoundsInEpochByBlockNum(t *testing.T) {
|
||||||
|
blockchain, bc, currentBlock, signer, signFn := PrepareXDCTestBlockChainWith128Candidates(t, 1802, params.TestXDPoSMockChainConfig)
|
||||||
|
chainConfig := params.TestXDPoSMockChainConfig
|
||||||
|
engine := bc.GetBlockChain().Engine().(*XDPoS.XDPoS)
|
||||||
|
blockCoinBase := signer.Hex()
|
||||||
|
|
||||||
|
startingBlockNum := currentBlock.Number().Int64() + 1
|
||||||
|
// Skipped the round
|
||||||
|
roundNumber := startingBlockNum - chainConfig.XDPoS.V2.SwitchBlock.Int64() + 2
|
||||||
|
block := CreateBlock(blockchain, chainConfig, currentBlock, int(startingBlockNum), roundNumber, blockCoinBase, signer, signFn, nil, nil, "b345a8560bd51926803dd17677c9f0751193914a851a4ec13063d6bf50220b53")
|
||||||
|
err := blockchain.InsertBlock(block)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update Signer as there is no previous signer assigned
|
||||||
|
err = UpdateSigner(blockchain)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNum := rpc.BlockNumber(1803)
|
||||||
|
|
||||||
|
data, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetMissedRoundsInEpochByBlockNum(&blockNum)
|
||||||
|
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, types.Round(900), data.EpochRound)
|
||||||
|
assert.Equal(t, big.NewInt(1800), data.EpochBlockNumber)
|
||||||
|
assert.Equal(t, 2, len(data.MissedRounds))
|
||||||
|
assert.NotEmpty(t, data.MissedRounds[0].Miner)
|
||||||
|
assert.Equal(t, data.MissedRounds[0].Round, types.Round(903))
|
||||||
|
assert.Equal(t, data.MissedRounds[0].CurrentBlockNum, big.NewInt(1803))
|
||||||
|
assert.Equal(t, data.MissedRounds[0].ParentBlockNum, big.NewInt(1802))
|
||||||
|
assert.NotEmpty(t, data.MissedRounds[1].Miner)
|
||||||
|
assert.Equal(t, data.MissedRounds[1].Round, types.Round(904))
|
||||||
|
assert.Equal(t, data.MissedRounds[0].CurrentBlockNum, big.NewInt(1803))
|
||||||
|
assert.Equal(t, data.MissedRounds[0].ParentBlockNum, big.NewInt(1802))
|
||||||
|
|
||||||
|
assert.NotEqual(t, data.MissedRounds[0].Miner, data.MissedRounds[1].Miner)
|
||||||
|
}
|
||||||
|
|
@ -545,6 +545,18 @@ func PrepareXDCTestBlockChainWith128Candidates(t *testing.T, numOfBlocks int, ch
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First v2 block
|
||||||
|
if (int64(i) - chainConfig.XDPoS.V2.SwitchBlock.Int64()) == 1 {
|
||||||
|
lastv1BlockNumber := block.Header().Number.Uint64() - 1
|
||||||
|
checkpointBlockNumber := lastv1BlockNumber - lastv1BlockNumber%chainConfig.XDPoS.Epoch
|
||||||
|
checkpointHeader := blockchain.GetHeaderByNumber(checkpointBlockNumber)
|
||||||
|
err := engine.EngineV2.Initial(blockchain, checkpointHeader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
currentBlock = block
|
currentBlock = block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,12 @@ web3._extend({
|
||||||
name: 'getLatestPoolStatus',
|
name: 'getLatestPoolStatus',
|
||||||
call: 'XDPoS_getLatestPoolStatus'
|
call: 'XDPoS_getLatestPoolStatus'
|
||||||
}),
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'getMissedRoundsInEpochByBlockNum',
|
||||||
|
call: 'XDPoS_getMissedRoundsInEpochByBlockNum',
|
||||||
|
params: 1,
|
||||||
|
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
properties: [
|
properties: [
|
||||||
new web3._extend.Property({
|
new web3._extend.Property({
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue