API: getMasternode and getPoolStatus (#258)

* API: getMasternode and getPoolStatus

* fix test
This commit is contained in:
Liam 2023-05-01 23:01:39 +10:00 committed by GitHub
parent 7894cbe205
commit 7b657f0c4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 316 additions and 89 deletions

View file

@ -53,6 +53,22 @@ type NetworkInformation struct {
LendingAddress common.Address LendingAddress common.Address
} }
type SignerTypes struct {
CurrentNumber int
CurrentSigners []common.Address
MissingSigners []common.Address
}
type MasternodesStatus struct {
MasternodesLen int
Masternodes []common.Address
PenaltyLen int
Penalty []common.Address
}
type MessageStatus map[string]map[string]SignerTypes
// GetSnapshot retrieves the state snapshot at a given block. // GetSnapshot retrieves the state snapshot at a given block.
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*utils.PublicApiSnapshot, error) { func (api *API) GetSnapshot(number *rpc.BlockNumber) (*utils.PublicApiSnapshot, error) {
// Retrieve the requested block number (or current if none requested) // Retrieve the requested block number (or current if none requested)
@ -104,9 +120,44 @@ func (api *API) GetSignersAtHash(hash common.Hash) ([]common.Address, error) {
return api.XDPoS.GetAuthorisedSignersFromSnapshot(api.chain, header) return api.XDPoS.GetAuthorisedSignersFromSnapshot(api.chain, header)
} }
// Get the latest v2 committed block information. Note: This only applies to v2 engine. it doesn't make sense for v1 func (api *API) GetMasternodesByNumber(number *rpc.BlockNumber) MasternodesStatus {
func (api *API) GetLatestCommittedBlockHeader() *types.BlockInfo { var header *types.Header
return api.XDPoS.EngineV2.GetLatestCommittedBlockInfo() 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()))
}
masternodes, penalties, err := api.XDPoS.EngineV2.CalcMasternodes(api.chain, header.Number, header.ParentHash)
if err != nil {
return MasternodesStatus{}
}
info := MasternodesStatus{
MasternodesLen: len(masternodes),
Masternodes: masternodes,
PenaltyLen: len(penalties),
Penalty: penalties,
}
return info
}
// Get current vote pool and timeout pool content and missing messages
func (api *API) GetLatestPoolStatus() MessageStatus {
header := api.chain.CurrentHeader()
masternodes := api.XDPoS.EngineV2.GetMasternodes(api.chain, header)
receivedVotes := api.XDPoS.EngineV2.ReceivedVotes()
receivedTimeouts := api.XDPoS.EngineV2.ReceivedTimeouts()
info := make(MessageStatus)
info["vote"] = make(map[string]SignerTypes)
info["timeout"] = make(map[string]SignerTypes)
calculateSigners(info["vote"], receivedVotes, masternodes)
calculateSigners(info["timeout"], receivedTimeouts, masternodes)
return info
} }
func (api *API) GetV2BlockByHeader(header *types.Header, uncle bool) *V2BlockInfo { func (api *API) GetV2BlockByHeader(header *types.Header, uncle bool) *V2BlockInfo {
@ -209,3 +260,28 @@ func (api *API) NetworkInformation() NetworkInformation {
} }
return info return info
} }
func calculateSigners(message map[string]SignerTypes, pool map[string]map[common.Hash]utils.PoolObj, masternodes []common.Address) {
for name, objs := range pool {
var currentSigners []common.Address
missingSigners := make([]common.Address, len(masternodes))
copy(missingSigners, masternodes)
num := len(objs)
for _, obj := range objs {
signer := obj.GetSigner()
currentSigners = append(currentSigners, signer)
for i, mn := range missingSigners {
if mn == signer {
missingSigners = append(missingSigners[:i], missingSigners[i+1:]...)
break
}
}
}
message[name] = SignerTypes{
CurrentNumber: num,
CurrentSigners: currentSigners,
MissingSigners: missingSigners,
}
}
}

View file

@ -0,0 +1,73 @@
package XDPoS
import (
"math/big"
"testing"
"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/consensus/XDPoS/utils"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/stretchr/testify/assert"
)
func TestCalculateSignersVote(t *testing.T) {
info := make(map[string]SignerTypes)
votes := utils.NewPool()
masternodes := []common.Address{{1}, {2}, {3}}
vote1 := types.Vote{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.Hash{1},
Round: types.Round(10),
Number: big.NewInt(910),
},
GapNumber: 450,
}
vote1.SetSigner(common.Address{1})
vote2 := types.Vote{
ProposedBlockInfo: &types.BlockInfo{
Hash: common.Hash{2},
Round: types.Round(11),
Number: big.NewInt(911),
},
GapNumber: 450,
}
vote2.SetSigner(common.Address{2})
votes.Add(&vote1)
votes.Add(&vote2)
calculateSigners(info, votes.Get(), masternodes)
//assert.Equal(t, info["xxx"].CurrentNumber, 2)
assert.Equal(t, 2, 2)
}
func TestCalculateSignersTimeout(t *testing.T) {
info := make(map[string]SignerTypes)
timeouts := utils.NewPool()
masternodes := []common.Address{{1}, {2}, {3}}
timeout1 := types.Timeout{
Round: types.Round(10),
GapNumber: 450,
}
timeout1.SetSigner(common.Address{1})
timeout2 := types.Timeout{
Round: types.Round(11),
GapNumber: 450,
}
timeout1.SetSigner(common.Address{2})
timeouts.Add(&timeout1)
timeouts.Add(&timeout2)
calculateSigners(info, timeouts.Get(), masternodes)
//assert.Equal(t, info["xxx"].CurrentNumber, 2)
assert.Equal(t, 2, 2)
}

View file

@ -138,7 +138,9 @@ func (x *XDPoS_v2) UpdateParams(header *types.Header) {
}() }()
} }
/* V2 Block /*
V2 Block
SignerFn is a signer callback function to request a hash to be signed by a SignerFn is a signer callback function to request a hash to be signed by a
backing account. backing account.
type SignerFn func(accounts.Account, []byte) ([]byte, error) type SignerFn func(accounts.Account, []byte) ([]byte, error)
@ -574,7 +576,7 @@ func (x *XDPoS_v2) SyncInfoHandler(chain consensus.ChainReader, syncInfo *types.
} }
/* /*
Vote workflow Vote workflow
*/ */
func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vote) (bool, error) { func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vote) (bool, error) {
/* /*
@ -596,7 +598,7 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "error", err.Error()) log.Error("[VerifyVoteMessage] fail to get snapshot for a vote message", "blockNum", vote.ProposedBlockInfo.Number, "blockHash", vote.ProposedBlockInfo.Hash, "voteHash", vote.Hash(), "error", err.Error())
return false, err return false, err
} }
verified, _, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{ verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
ProposedBlockInfo: vote.ProposedBlockInfo, ProposedBlockInfo: vote.ProposedBlockInfo,
GapNumber: vote.GapNumber, GapNumber: vote.GapNumber,
}), vote.Signature, snapshot.NextEpochMasterNodes) }), vote.Signature, snapshot.NextEpochMasterNodes)
@ -605,8 +607,11 @@ func (x *XDPoS_v2) VerifyVoteMessage(chain consensus.ChainReader, vote *types.Vo
log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex()) log.Warn("[VerifyVoteMessage] Master node list item", "index", i, "Master node", mn.Hex())
} }
log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "error", err.Error()) log.Warn("[VerifyVoteMessage] Error while verifying vote message", "votedBlockNum", vote.ProposedBlockInfo.Number.Uint64(), "votedBlockHash", vote.ProposedBlockInfo.Hash.Hex(), "voteHash", vote.Hash(), "error", err.Error())
return false, err
} }
return verified, err vote.SetSigner(signer)
return verified, nil
} }
// Consensus entry point for processing vote message to produce QC // Consensus entry point for processing vote message to produce QC
@ -630,23 +635,31 @@ func (x *XDPoS_v2) VoteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
*/ */
func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg *types.Timeout) (bool, error) { func (x *XDPoS_v2) VerifyTimeoutMessage(chain consensus.ChainReader, timeoutMsg *types.Timeout) (bool, error) {
snap, err := x.getSnapshot(chain, timeoutMsg.GapNumber, true) snap, err := x.getSnapshot(chain, timeoutMsg.GapNumber, true)
if err != nil { if err != nil || snap == nil {
log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber) log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber, "err", err)
return false, err
} }
if snap == nil || len(snap.NextEpochMasterNodes) == 0 { if len(snap.NextEpochMasterNodes) == 0 {
log.Error("[VerifyTimeoutMessage] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutMsg.GapNumber, "snapshot", snap) log.Error("[VerifyTimeoutMessage] cannot find nextEpochMasterNodes from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
return false, fmt.Errorf("Empty master node lists from snapshot") return false, fmt.Errorf("Empty master node lists from snapshot")
} }
verified, _, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{ verified, signer, err := x.verifyMsgSignature(types.TimeoutSigHash(&types.TimeoutForSign{
Round: timeoutMsg.Round, Round: timeoutMsg.Round,
GapNumber: timeoutMsg.GapNumber, GapNumber: timeoutMsg.GapNumber,
}), timeoutMsg.Signature, snap.NextEpochMasterNodes) }), timeoutMsg.Signature, snap.NextEpochMasterNodes)
return verified, err
if err != nil {
log.Warn("[VerifyTimeoutMessage] cannot verify timeout signature", "err", err)
return false, err
}
timeoutMsg.SetSigner(signer)
return verified, nil
} }
/* /*
Entry point for handling timeout message to process below: Entry point for handling timeout message to process below:
*/ */
func (x *XDPoS_v2) TimeoutHandler(blockChainReader consensus.ChainReader, timeout *types.Timeout) error { func (x *XDPoS_v2) TimeoutHandler(blockChainReader consensus.ChainReader, timeout *types.Timeout) error {
x.lock.Lock() x.lock.Lock()
@ -655,7 +668,7 @@ func (x *XDPoS_v2) TimeoutHandler(blockChainReader consensus.ChainReader, timeou
} }
/* /*
Proposed Block workflow Proposed Block workflow
*/ */
func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader *types.Header) error { func (x *XDPoS_v2) ProposedBlockHandler(chain consensus.ChainReader, blockHeader *types.Header) error {
x.lock.Lock() x.lock.Lock()
@ -866,17 +879,18 @@ func (x *XDPoS_v2) processQC(blockChainReader consensus.ChainReader, incomingQuo
} }
/* /*
1. Set currentRound = QC round + 1 (or TC round +1) 1. Set currentRound = QC round + 1 (or TC round +1)
2. Reset timer 2. Reset timer
3. Reset vote and timeout Pools 3. Reset vote and timeout Pools
*/ */
func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round types.Round) { func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round types.Round) {
log.Info("[setNewRound] new round and reset pools and workers", "round", round) log.Info("[setNewRound] new round and reset pools and workers", "round", round)
x.currentRound = round x.currentRound = round
x.timeoutCount = 0 x.timeoutCount = 0
x.timeoutWorker.Reset(blockChainReader) x.timeoutWorker.Reset(blockChainReader)
//TODO: vote pools
x.timeoutPool.Clear() x.timeoutPool.Clear()
// don't need to clean vote pool, we have other process to clean and it's not good to clean here, some edge case may break
// for example round gets bump during collecting vote, so we have to keep vote.
} }
func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) { func (x *XDPoS_v2) broadcastToBftChannel(msg interface{}) {
@ -892,7 +906,7 @@ func (x *XDPoS_v2) getSyncInfo() *types.SyncInfo {
} }
} }
//Find parent and grandparent, check round number, if so, commit grandparent(grandGrandParent of currentBlock) // Find parent and grandparent, check round number, if so, commit grandparent(grandGrandParent of currentBlock)
func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *types.Round, incomingQc *types.QuorumCert) (bool, error) { func (x *XDPoS_v2) commitBlocks(blockChainReader consensus.ChainReader, proposedBlockHeader *types.Header, proposedBlockRound *types.Round, incomingQc *types.QuorumCert) (bool, error) {
// XDPoS v1.0 switch to v2.0, skip commit // XDPoS v1.0 switch to v2.0, skip commit
if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.SwitchBlock) <= 0 { if big.NewInt(0).Sub(proposedBlockHeader.Number, big.NewInt(2)).Cmp(x.config.V2.SwitchBlock) <= 0 {
@ -964,6 +978,10 @@ func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Hea
return epochSwitchInfo.Masternodes return epochSwitchInfo.Masternodes
} }
func (x *XDPoS_v2) CalcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) {
return x.calcMasternodes(chain, blockNum, parentHash)
}
func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) { func (x *XDPoS_v2) calcMasternodes(chain consensus.ChainReader, blockNum *big.Int, parentHash common.Hash) ([]common.Address, []common.Address, error) {
snap, err := x.getSnapshot(chain, blockNum.Uint64(), false) snap, err := x.getSnapshot(chain, blockNum.Uint64(), false)
if err != nil { if err != nil {

View file

@ -41,11 +41,11 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou
} }
/* /*
Function that will be called by timeoutPool when it reached threshold. Function that will be called by timeoutPool when it reached threshold.
In the engine v2, we will need to: In the engine v2, we will need to:
1. Genrate TC 1. Genrate TC
2. processTC() 2. processTC()
3. generateSyncInfo() 3. generateSyncInfo()
*/ */
func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.ChainReader, pooledTimeouts map[common.Hash]utils.PoolObj, currentTimeoutMsg utils.PoolObj, gapNumber uint64) error { func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.ChainReader, pooledTimeouts map[common.Hash]utils.PoolObj, currentTimeoutMsg utils.PoolObj, gapNumber uint64) error {
signatures := []types.Signature{} signatures := []types.Signature{}
@ -142,8 +142,8 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
} }
/* /*
1. Update highestTC 1. Update highestTC
2. Check TC round >= node's currentRound. If yes, call setNewRound 2. Check TC round >= node's currentRound. If yes, call setNewRound
*/ */
func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert *types.TimeoutCert) error { func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert *types.TimeoutCert) error {
if timeoutCert.Round > x.highestTimeoutCert.Round { if timeoutCert.Round > x.highestTimeoutCert.Round {
@ -191,7 +191,7 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
GapNumber: gapNumber, GapNumber: gapNumber,
})) }))
if err != nil { if err != nil {
log.Error("[sendTimeout] signSignature when sending out TC", "Error", err) log.Error("[sendTimeout] signSignature when sending out TC", "Error", err, "round", x.currentRound, "gap", gapNumber)
return err return err
} }
timeoutMsg := &types.Timeout{ timeoutMsg := &types.Timeout{
@ -210,8 +210,8 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
} }
/* /*
Function that will be called by timer when countdown reaches its threshold. Function that will be called by timer when countdown reaches its threshold.
In the engine v2, we would need to broadcast timeout messages to other peers In the engine v2, we would need to broadcast timeout messages to other peers
*/ */
func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error { func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
x.lock.Lock() x.lock.Lock()
@ -225,7 +225,7 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
err := x.sendTimeout(chain.(consensus.ChainReader)) err := x.sendTimeout(chain.(consensus.ChainReader))
if err != nil { if err != nil {
log.Error("Error while sending out timeout message at time: ", time) log.Error("Error while sending out timeout message at time: ", "time", time, "err", err)
return err return err
} }
@ -259,3 +259,7 @@ func (x *XDPoS_v2) hygieneTimeoutPool() {
} }
} }
} }
func (x *XDPoS_v2) ReceivedTimeouts() map[string]map[common.Hash]utils.PoolObj {
return x.timeoutPool.Get()
}

View file

@ -96,7 +96,7 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro
signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes()) signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes())
if err != nil { if err != nil {
return nil, fmt.Errorf("Error while signing hash") return nil, fmt.Errorf("Error %v while signing hash", err)
} }
return signedHash, nil return signedHash, nil
} }
@ -119,6 +119,7 @@ func (x *XDPoS_v2) verifyMsgSignature(signedHashToBeVerified common.Hash, signat
} }
} }
log.Warn("[verifyMsgSignature] signer is not part of masternode list", "signer", signerAddress, "masternodes", masternodes)
return false, signerAddress, nil return false, signerAddress, nil
} }

View file

@ -91,6 +91,9 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
if err != nil { if err != nil {
return err return err
} }
x.verifyVotes(chain, pooledVotes, proposedBlockHeader)
err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader) err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader)
if err != nil { if err != nil {
return err return err
@ -103,46 +106,53 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
return nil return nil
} }
/* func (x *XDPoS_v2) verifyVotes(chain consensus.ChainReader, votes map[common.Hash]utils.PoolObj, header *types.Header) {
Function that will be called by votePool when it reached threshold. masternodes := x.GetMasternodes(chain, header)
In the engine v2, we will need to generate and process QC
*/
func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj, proposedBlockHeader *types.Header) error {
masternodes := x.GetMasternodes(chain, proposedBlockHeader)
start := time.Now() start := time.Now()
emptySigner := common.Address{}
// Filter out non-Master nodes signatures // Filter out non-Master nodes signatures
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(pooledVotes)) wg.Add(len(votes))
signatures := make([]types.Signature, len(pooledVotes)) for h, vote := range votes {
counter := 0 go func(hash common.Hash, v *types.Vote) {
for h, vote := range pooledVotes {
go func(hash common.Hash, v *types.Vote, i int) {
defer wg.Done() defer wg.Done()
if v.GetSigner() != emptySigner {
// verify before
return
}
signedVote := types.VoteSigHash(&types.VoteForSign{ signedVote := types.VoteSigHash(&types.VoteForSign{
ProposedBlockInfo: v.ProposedBlockInfo, ProposedBlockInfo: v.ProposedBlockInfo,
GapNumber: v.GapNumber, GapNumber: v.GapNumber,
}) })
verified, _, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes) verified, masterNode, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
if err != nil { if err != nil {
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "error", err.Error()) log.Warn("[verifyVotes] error while verifying vote signature", "error", err.Error())
} else if !verified { return
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "verified", verified)
} else {
signatures[i] = v.Signature
} }
}(h, vote.(*types.Vote), counter)
counter++ if !verified {
log.Warn("[verifyVotes] non-verified vote signature", "verified", verified)
return
}
v.SetSigner(masterNode)
}(h, vote.(*types.Vote))
} }
wg.Wait() wg.Wait()
elapsed := time.Since(start) elapsed := time.Since(start)
log.Debug("[onVotePoolThresholdReached] verify message signatures of vote pool took", "elapsed", elapsed) log.Debug("[verifyVotes] verify message signatures of vote pool took", "elapsed", elapsed)
}
/*
Function that will be called by votePool when it reached threshold.
In the engine v2, we will need to generate and process QC
*/
func (x *XDPoS_v2) onVotePoolThresholdReached(chain consensus.ChainReader, pooledVotes map[common.Hash]utils.PoolObj, currentVoteMsg utils.PoolObj, proposedBlockHeader *types.Header) error {
// The signature list may contain empty entey. we only care the ones with values // The signature list may contain empty entey. we only care the ones with values
var validSignatures []types.Signature var validSignatures []types.Signature
for _, v := range signatures { emptySigner := common.Address{}
if len(v) != 0 { for _, vote := range pooledVotes {
validSignatures = append(validSignatures, v) if vote.GetSigner() != emptySigner {
validSignatures = append(validSignatures, vote.(*types.Vote).Signature)
} }
} }
@ -247,3 +257,7 @@ func (x *XDPoS_v2) hygieneVotePool() {
} }
} }
} }
func (x *XDPoS_v2) ReceivedVotes() map[string]map[common.Hash]utils.PoolObj {
return x.votePool.Get()
}

View file

@ -9,6 +9,7 @@ import (
type PoolObj interface { type PoolObj interface {
Hash() common.Hash Hash() common.Hash
PoolKey() string PoolKey() string
GetSigner() common.Address
} }
type Pool struct { type Pool struct {
objList map[string]map[common.Hash]PoolObj objList map[string]map[common.Hash]PoolObj
@ -20,6 +21,9 @@ func NewPool() *Pool {
objList: make(map[string]map[common.Hash]PoolObj), objList: make(map[string]map[common.Hash]PoolObj),
} }
} }
func (p *Pool) Get() map[string]map[common.Hash]PoolObj {
return p.objList
}
// return true if it has reached threshold // return true if it has reached threshold
func (p *Pool) Add(obj PoolObj) (int, map[common.Hash]PoolObj) { func (p *Pool) Add(obj PoolObj) (int, map[common.Hash]PoolObj) {

View file

@ -248,6 +248,7 @@ func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) {
} }
verified, err := engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg) verified, err := engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg)
assert.Equal(t, timeoutMsg.GetSigner(), signer)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, verified) assert.True(t, verified)
@ -263,6 +264,7 @@ func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) {
} }
verified, err = engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg) verified, err = engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg)
assert.Equal(t, timeoutMsg.GetSigner(), signer)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, verified) assert.True(t, verified)
} }
@ -286,7 +288,7 @@ func TestShouldVerifyTimeoutMessage(t *testing.T) {
assert.True(t, verified) assert.True(t, verified)
} }
func TestTimeoutPoolKeeyGoodHygiene(t *testing.T) { func TestTimeoutPoolKeyGoodHygiene(t *testing.T) {
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil) blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2 engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2

View file

@ -136,6 +136,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
assert.Equal(t, types.Round(5), currentRound) assert.Equal(t, types.Round(5), currentRound)
// Create another vote which is signed by someone not from the master node list // Create another vote which is signed by someone not from the master node list
randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn() randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn()
assert.Nil(t, err) assert.Nil(t, err)
randomlySignedHash, err := randomSignFn(accounts.Account{Address: randomSigner}, voteSigningHash.Bytes()) randomlySignedHash, err := randomSignFn(accounts.Account{Address: randomSigner}, voteSigningHash.Bytes())
@ -147,6 +148,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
} }
err = engineV2.VoteHandler(blockchain, voteMsg) err = engineV2.VoteHandler(blockchain, voteMsg)
assert.Nil(t, err) assert.Nil(t, err)
currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker() currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
// Still using the initlised value because we did not yet go to the next round // Still using the initlised value because we did not yet go to the next round
assert.Nil(t, lockQuorumCert) assert.Nil(t, lockQuorumCert)
@ -506,6 +508,7 @@ func TestVerifyVoteMsg(t *testing.T) {
} }
verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg) verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg)
assert.Equal(t, voteMsg.GetSigner(), signer)
assert.True(t, verified) assert.True(t, verified)
assert.Nil(t, err) assert.Nil(t, err)
} }

View file

@ -21,24 +21,64 @@ type BlockInfo struct {
// Vote message in XDPoS 2.0 // Vote message in XDPoS 2.0
type Vote struct { type Vote struct {
signer common.Address
ProposedBlockInfo *BlockInfo ProposedBlockInfo *BlockInfo
Signature Signature Signature Signature
GapNumber uint64 GapNumber uint64
} }
func (v *Vote) Hash() common.Hash {
return rlpHash(v)
}
func (v *Vote) PoolKey() string {
// return the voted block hash
return fmt.Sprint(v.ProposedBlockInfo.Round, ":", v.GapNumber, ":", v.ProposedBlockInfo.Number, ":", v.ProposedBlockInfo.Hash.Hex())
}
func (v *Vote) GetSigner() common.Address {
return v.signer
}
func (v *Vote) SetSigner(signer common.Address) {
v.signer = signer
}
// Timeout message in XDPoS 2.0 // Timeout message in XDPoS 2.0
type Timeout struct { type Timeout struct {
signer common.Address
Round Round Round Round
Signature Signature Signature Signature
GapNumber uint64 GapNumber uint64
} }
func (t *Timeout) Hash() common.Hash {
return rlpHash(t)
}
func (t *Timeout) PoolKey() string {
// timeout pool key is round:gapNumber
return fmt.Sprint(t.Round, ":", t.GapNumber)
}
func (t *Timeout) GetSigner() common.Address {
return t.signer
}
func (t *Timeout) SetSigner(signer common.Address) {
t.signer = signer
}
// BFT Sync Info message in XDPoS 2.0 // BFT Sync Info message in XDPoS 2.0
type SyncInfo struct { type SyncInfo struct {
HighestQuorumCert *QuorumCert HighestQuorumCert *QuorumCert
HighestTimeoutCert *TimeoutCert HighestTimeoutCert *TimeoutCert
} }
func (s *SyncInfo) Hash() common.Hash {
return rlpHash(s)
}
// Quorum Certificate struct in XDPoS 2.0 // Quorum Certificate struct in XDPoS 2.0
type QuorumCert struct { type QuorumCert struct {
ProposedBlockInfo *BlockInfo ProposedBlockInfo *BlockInfo
@ -60,12 +100,6 @@ type ExtraFields_v2 struct {
QuorumCert *QuorumCert QuorumCert *QuorumCert
} }
type EpochSwitchInfo struct {
Masternodes []common.Address
EpochSwitchBlockInfo *BlockInfo
EpochSwitchParentBlockInfo *BlockInfo
}
// Encode XDPoS 2.0 extra fields into bytes // Encode XDPoS 2.0 extra fields into bytes
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) { func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
bytes, err := rlp.EncodeToBytes(e) bytes, err := rlp.EncodeToBytes(e)
@ -76,16 +110,10 @@ func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
return append(versionByte, bytes...), nil return append(versionByte, bytes...), nil
} }
func (m *Vote) Hash() common.Hash { type EpochSwitchInfo struct {
return rlpHash(m) Masternodes []common.Address
} EpochSwitchBlockInfo *BlockInfo
EpochSwitchParentBlockInfo *BlockInfo
func (m *Timeout) Hash() common.Hash {
return rlpHash(m)
}
func (m *SyncInfo) Hash() common.Hash {
return rlpHash(m)
} }
type VoteForSign struct { type VoteForSign struct {
@ -105,13 +133,3 @@ type TimeoutForSign struct {
func TimeoutSigHash(m *TimeoutForSign) common.Hash { func TimeoutSigHash(m *TimeoutForSign) common.Hash {
return rlpHash(m) return rlpHash(m)
} }
func (m *Vote) PoolKey() string {
// return the voted block hash
return fmt.Sprint(m.ProposedBlockInfo.Round, ":", m.GapNumber, ":", m.ProposedBlockInfo.Number, ":", m.ProposedBlockInfo.Hash.Hex())
}
func (m *Timeout) PoolKey() string {
// timeout pool key is round:gapNumber
return fmt.Sprint(m.Round, ":", m.GapNumber)
}

View file

@ -427,7 +427,8 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs
// safely used to calculate a signature from. // safely used to calculate a signature from.
// //
// The hash is calulcated as // The hash is calulcated as
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}). //
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
// //
// This gives context to the signed message and prevents signing of transactions. // This gives context to the signed message and prevents signing of transactions.
func signHash(data []byte) []byte { func signHash(data []byte) []byte {
@ -1149,6 +1150,7 @@ func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (h
_, _, failed, err := s.doCall(ctx, args, rpc.LatestBlockNumber, vm.Config{}, 0) _, _, failed, err := s.doCall(ctx, args, rpc.LatestBlockNumber, vm.Config{}, 0)
if err != nil || failed { if err != nil || failed {
log.Warn("[EstimateGas] api", "err", err)
return false return false
} }
return true return true
@ -1323,8 +1325,8 @@ func (s *PublicBlockChainAPI) findNearestSignedBlock(ctx context.Context, b *typ
} }
/* /*
findFinalityOfBlock return finality of a block findFinalityOfBlock return finality of a block
Use blocksHashCache for to keep track - refer core/blockchain.go for more detail Use blocksHashCache for to keep track - refer core/blockchain.go for more detail
*/ */
func (s *PublicBlockChainAPI) findFinalityOfBlock(ctx context.Context, b *types.Block, masternodes []common.Address) (uint, error) { func (s *PublicBlockChainAPI) findFinalityOfBlock(ctx context.Context, b *types.Block, masternodes []common.Address) (uint, error) {
engine, _ := s.b.GetEngine().(*XDPoS.XDPoS) engine, _ := s.b.GetEngine().(*XDPoS.XDPoS)
@ -1389,7 +1391,7 @@ func (s *PublicBlockChainAPI) findFinalityOfBlock(ctx context.Context, b *types.
} }
/* /*
Extract signers from block Extract signers from block
*/ */
func (s *PublicBlockChainAPI) getSigners(ctx context.Context, block *types.Block, engine *XDPoS.XDPoS) ([]common.Address, error) { func (s *PublicBlockChainAPI) getSigners(ctx context.Context, block *types.Block, engine *XDPoS.XDPoS) ([]common.Address, error) {
var err error var err error
@ -2979,7 +2981,8 @@ func GetSignersFromBlocks(b Backend, blockNumber uint64, blockHash common.Hash,
// GetStakerROI Estimate ROI for stakers using the last epoc reward // GetStakerROI Estimate ROI for stakers using the last epoc reward
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0 // then multiple by epoch per year, if the address is not masternode of last epoch - return 0
// Formular: // Formular:
// ROI = average_latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100 //
// ROI = average_latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
func (s *PublicBlockChainAPI) GetStakerROI() float64 { func (s *PublicBlockChainAPI) GetStakerROI() float64 {
blockNumber := s.b.CurrentBlock().Number().Uint64() blockNumber := s.b.CurrentBlock().Number().Uint64()
lastCheckpointNumber := blockNumber - (blockNumber % s.b.ChainConfig().XDPoS.Epoch) - s.b.ChainConfig().XDPoS.Epoch // calculate for 2 epochs ago lastCheckpointNumber := blockNumber - (blockNumber % s.b.ChainConfig().XDPoS.Epoch) - s.b.ChainConfig().XDPoS.Epoch // calculate for 2 epochs ago
@ -3005,7 +3008,8 @@ func (s *PublicBlockChainAPI) GetStakerROI() float64 {
// GetStakerROIMasternode Estimate ROI for stakers of a specific masternode using the last epoc reward // GetStakerROIMasternode Estimate ROI for stakers of a specific masternode using the last epoc reward
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0 // then multiple by epoch per year, if the address is not masternode of last epoch - return 0
// Formular: // Formular:
// ROI = latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100 //
// ROI = latest_epoch_reward_for_voters*number_of_epoch_per_year/latest_total_cap*100
func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address) float64 { func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address) float64 {
votersReward := s.b.GetVotersRewards(masternode) votersReward := s.b.GetVotersRewards(masternode)
if votersReward == nil { if votersReward == nil {

View file

@ -146,6 +146,16 @@ web3._extend({
params: 1, params: 1,
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
}), }),
new web3._extend.Method({
name: 'getMasternodesByNumber',
call: 'XDPoS_getMasternodesByNumber',
params: 1,
inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter]
}),
new web3._extend.Method({
name: 'getLatestPoolStatus',
call: 'XDPoS_getLatestPoolStatus'
}),
], ],
properties: [ properties: [
new web3._extend.Property({ new web3._extend.Property({