mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
API: getMasternode and getPoolStatus (#258)
* API: getMasternode and getPoolStatus * fix test
This commit is contained in:
parent
7894cbe205
commit
7b657f0c4e
12 changed files with 316 additions and 89 deletions
|
|
@ -53,6 +53,22 @@ type NetworkInformation struct {
|
|||
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.
|
||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*utils.PublicApiSnapshot, error) {
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Get the latest v2 committed block information. Note: This only applies to v2 engine. it doesn't make sense for v1
|
||||
func (api *API) GetLatestCommittedBlockHeader() *types.BlockInfo {
|
||||
return api.XDPoS.EngineV2.GetLatestCommittedBlockInfo()
|
||||
func (api *API) GetMasternodesByNumber(number *rpc.BlockNumber) MasternodesStatus {
|
||||
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()))
|
||||
}
|
||||
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 {
|
||||
|
|
@ -209,3 +260,28 @@ func (api *API) NetworkInformation() NetworkInformation {
|
|||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
73
consensus/XDPoS/api_test.go
Normal file
73
consensus/XDPoS/api_test.go
Normal 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)
|
||||
}
|
||||
|
|
@ -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
|
||||
backing account.
|
||||
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) {
|
||||
/*
|
||||
|
|
@ -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())
|
||||
return false, err
|
||||
}
|
||||
verified, _, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
||||
verified, signer, err := x.verifyMsgSignature(types.VoteSigHash(&types.VoteForSign{
|
||||
ProposedBlockInfo: vote.ProposedBlockInfo,
|
||||
GapNumber: vote.GapNumber,
|
||||
}), 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] 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
|
||||
|
|
@ -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) {
|
||||
snap, err := x.getSnapshot(chain, timeoutMsg.GapNumber, true)
|
||||
if err != nil {
|
||||
log.Error("[VerifyTimeoutMessage] Fail to get snapshot when verifying timeout message!", "messageGapNumber", timeoutMsg.GapNumber)
|
||||
if err != nil || snap == nil {
|
||||
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 {
|
||||
log.Error("[VerifyTimeoutMessage] Something wrong with the snapshot from gapNumber", "messageGapNumber", timeoutMsg.GapNumber, "snapshot", snap)
|
||||
if len(snap.NextEpochMasterNodes) == 0 {
|
||||
log.Error("[VerifyTimeoutMessage] cannot find nextEpochMasterNodes from snapshot", "messageGapNumber", timeoutMsg.GapNumber)
|
||||
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,
|
||||
GapNumber: timeoutMsg.GapNumber,
|
||||
}), 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 {
|
||||
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 {
|
||||
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)
|
||||
2. Reset timer
|
||||
3. Reset vote and timeout Pools
|
||||
1. Set currentRound = QC round + 1 (or TC round +1)
|
||||
2. Reset timer
|
||||
3. Reset vote and timeout Pools
|
||||
*/
|
||||
func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round types.Round) {
|
||||
log.Info("[setNewRound] new round and reset pools and workers", "round", round)
|
||||
x.currentRound = round
|
||||
x.timeoutCount = 0
|
||||
x.timeoutWorker.Reset(blockChainReader)
|
||||
//TODO: vote pools
|
||||
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{}) {
|
||||
|
|
@ -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) {
|
||||
// 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 {
|
||||
|
|
@ -964,6 +978,10 @@ func (x *XDPoS_v2) GetMasternodes(chain consensus.ChainReader, header *types.Hea
|
|||
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) {
|
||||
snap, err := x.getSnapshot(chain, blockNum.Uint64(), false)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ func (x *XDPoS_v2) timeoutHandler(blockChainReader consensus.ChainReader, timeou
|
|||
}
|
||||
|
||||
/*
|
||||
Function that will be called by timeoutPool when it reached threshold.
|
||||
In the engine v2, we will need to:
|
||||
1. Genrate TC
|
||||
2. processTC()
|
||||
3. generateSyncInfo()
|
||||
Function that will be called by timeoutPool when it reached threshold.
|
||||
In the engine v2, we will need to:
|
||||
1. Genrate TC
|
||||
2. processTC()
|
||||
3. generateSyncInfo()
|
||||
*/
|
||||
func (x *XDPoS_v2) onTimeoutPoolThresholdReached(blockChainReader consensus.ChainReader, pooledTimeouts map[common.Hash]utils.PoolObj, currentTimeoutMsg utils.PoolObj, gapNumber uint64) error {
|
||||
signatures := []types.Signature{}
|
||||
|
|
@ -142,8 +142,8 @@ func (x *XDPoS_v2) verifyTC(chain consensus.ChainReader, timeoutCert *types.Time
|
|||
}
|
||||
|
||||
/*
|
||||
1. Update highestTC
|
||||
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
||||
1. Update highestTC
|
||||
2. Check TC round >= node's currentRound. If yes, call setNewRound
|
||||
*/
|
||||
func (x *XDPoS_v2) processTC(blockChainReader consensus.ChainReader, timeoutCert *types.TimeoutCert) error {
|
||||
if timeoutCert.Round > x.highestTimeoutCert.Round {
|
||||
|
|
@ -191,7 +191,7 @@ func (x *XDPoS_v2) sendTimeout(chain consensus.ChainReader) error {
|
|||
GapNumber: gapNumber,
|
||||
}))
|
||||
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
|
||||
}
|
||||
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.
|
||||
In the engine v2, we would need to broadcast timeout messages to other peers
|
||||
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
|
||||
*/
|
||||
func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
|
||||
x.lock.Lock()
|
||||
|
|
@ -225,7 +225,7 @@ func (x *XDPoS_v2) OnCountdownTimeout(time time.Time, chain interface{}) error {
|
|||
|
||||
err := x.sendTimeout(chain.(consensus.ChainReader))
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ func (x *XDPoS_v2) signSignature(signingHash common.Hash) (types.Signature, erro
|
|||
|
||||
signedHash, err := signFn(accounts.Account{Address: signer}, signingHash.Bytes())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error while signing hash")
|
||||
return nil, fmt.Errorf("Error %v while signing hash", err)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x.verifyVotes(chain, pooledVotes, proposedBlockHeader)
|
||||
|
||||
err = x.onVotePoolThresholdReached(chain, pooledVotes, voteMsg, proposedBlockHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -103,46 +106,53 @@ func (x *XDPoS_v2) voteHandler(chain consensus.ChainReader, voteMsg *types.Vote)
|
|||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
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 {
|
||||
|
||||
masternodes := x.GetMasternodes(chain, proposedBlockHeader)
|
||||
func (x *XDPoS_v2) verifyVotes(chain consensus.ChainReader, votes map[common.Hash]utils.PoolObj, header *types.Header) {
|
||||
masternodes := x.GetMasternodes(chain, header)
|
||||
start := time.Now()
|
||||
emptySigner := common.Address{}
|
||||
// Filter out non-Master nodes signatures
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(pooledVotes))
|
||||
signatures := make([]types.Signature, len(pooledVotes))
|
||||
counter := 0
|
||||
for h, vote := range pooledVotes {
|
||||
go func(hash common.Hash, v *types.Vote, i int) {
|
||||
wg.Add(len(votes))
|
||||
for h, vote := range votes {
|
||||
go func(hash common.Hash, v *types.Vote) {
|
||||
defer wg.Done()
|
||||
if v.GetSigner() != emptySigner {
|
||||
// verify before
|
||||
return
|
||||
}
|
||||
signedVote := types.VoteSigHash(&types.VoteForSign{
|
||||
ProposedBlockInfo: v.ProposedBlockInfo,
|
||||
GapNumber: v.GapNumber,
|
||||
})
|
||||
verified, _, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
|
||||
verified, masterNode, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
|
||||
if err != nil {
|
||||
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "error", err.Error())
|
||||
} else if !verified {
|
||||
log.Warn("[onVotePoolThresholdReached] Skip not verified vote signatures when building QC", "verified", verified)
|
||||
} else {
|
||||
signatures[i] = v.Signature
|
||||
log.Warn("[verifyVotes] error while verifying vote signature", "error", err.Error())
|
||||
return
|
||||
}
|
||||
}(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()
|
||||
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
|
||||
var validSignatures []types.Signature
|
||||
for _, v := range signatures {
|
||||
if len(v) != 0 {
|
||||
validSignatures = append(validSignatures, v)
|
||||
emptySigner := common.Address{}
|
||||
for _, vote := range pooledVotes {
|
||||
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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
type PoolObj interface {
|
||||
Hash() common.Hash
|
||||
PoolKey() string
|
||||
GetSigner() common.Address
|
||||
}
|
||||
type Pool struct {
|
||||
objList map[string]map[common.Hash]PoolObj
|
||||
|
|
@ -20,6 +21,9 @@ func NewPool() *Pool {
|
|||
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
|
||||
func (p *Pool) Add(obj PoolObj) (int, map[common.Hash]PoolObj) {
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) {
|
|||
}
|
||||
|
||||
verified, err := engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg)
|
||||
assert.Equal(t, timeoutMsg.GetSigner(), signer)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, verified)
|
||||
|
||||
|
|
@ -263,6 +264,7 @@ func TestShouldVerifyTimeoutMessageForFirstV2Block(t *testing.T) {
|
|||
}
|
||||
|
||||
verified, err = engineV2.VerifyTimeoutMessage(blockchain, timeoutMsg)
|
||||
assert.Equal(t, timeoutMsg.GetSigner(), signer)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, verified)
|
||||
}
|
||||
|
|
@ -286,7 +288,7 @@ func TestShouldVerifyTimeoutMessage(t *testing.T) {
|
|||
assert.True(t, verified)
|
||||
}
|
||||
|
||||
func TestTimeoutPoolKeeyGoodHygiene(t *testing.T) {
|
||||
func TestTimeoutPoolKeyGoodHygiene(t *testing.T) {
|
||||
blockchain, _, _, signer, signFn, _ := PrepareXDCTestBlockChainForV2Engine(t, 905, params.TestXDPoSMockChainConfig, nil)
|
||||
engineV2 := blockchain.Engine().(*XDPoS.XDPoS).EngineV2
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
|||
assert.Equal(t, types.Round(5), currentRound)
|
||||
|
||||
// Create another vote which is signed by someone not from the master node list
|
||||
|
||||
randomSigner, randomSignFn, err := backends.SimulateWalletAddressAndSignFn()
|
||||
assert.Nil(t, err)
|
||||
randomlySignedHash, err := randomSignFn(accounts.Account{Address: randomSigner}, voteSigningHash.Bytes())
|
||||
|
|
@ -147,6 +148,7 @@ func TestVoteMessageHandlerSuccessfullyGeneratedAndProcessQC(t *testing.T) {
|
|||
}
|
||||
err = engineV2.VoteHandler(blockchain, voteMsg)
|
||||
assert.Nil(t, err)
|
||||
|
||||
currentRound, lockQuorumCert, highestQuorumCert, _, _, _ = engineV2.GetPropertiesFaker()
|
||||
// Still using the initlised value because we did not yet go to the next round
|
||||
assert.Nil(t, lockQuorumCert)
|
||||
|
|
@ -506,6 +508,7 @@ func TestVerifyVoteMsg(t *testing.T) {
|
|||
}
|
||||
|
||||
verified, err = engineV2.VerifyVoteMessage(blockchain, voteMsg)
|
||||
assert.Equal(t, voteMsg.GetSigner(), signer)
|
||||
assert.True(t, verified)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,24 +21,64 @@ type BlockInfo struct {
|
|||
|
||||
// Vote message in XDPoS 2.0
|
||||
type Vote struct {
|
||||
signer common.Address
|
||||
ProposedBlockInfo *BlockInfo
|
||||
Signature Signature
|
||||
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
|
||||
type Timeout struct {
|
||||
signer common.Address
|
||||
Round Round
|
||||
Signature Signature
|
||||
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
|
||||
type SyncInfo struct {
|
||||
HighestQuorumCert *QuorumCert
|
||||
HighestTimeoutCert *TimeoutCert
|
||||
}
|
||||
|
||||
func (s *SyncInfo) Hash() common.Hash {
|
||||
return rlpHash(s)
|
||||
}
|
||||
|
||||
// Quorum Certificate struct in XDPoS 2.0
|
||||
type QuorumCert struct {
|
||||
ProposedBlockInfo *BlockInfo
|
||||
|
|
@ -60,12 +100,6 @@ type ExtraFields_v2 struct {
|
|||
QuorumCert *QuorumCert
|
||||
}
|
||||
|
||||
type EpochSwitchInfo struct {
|
||||
Masternodes []common.Address
|
||||
EpochSwitchBlockInfo *BlockInfo
|
||||
EpochSwitchParentBlockInfo *BlockInfo
|
||||
}
|
||||
|
||||
// Encode XDPoS 2.0 extra fields into bytes
|
||||
func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
|
||||
bytes, err := rlp.EncodeToBytes(e)
|
||||
|
|
@ -76,16 +110,10 @@ func (e *ExtraFields_v2) EncodeToBytes() ([]byte, error) {
|
|||
return append(versionByte, bytes...), nil
|
||||
}
|
||||
|
||||
func (m *Vote) Hash() common.Hash {
|
||||
return rlpHash(m)
|
||||
}
|
||||
|
||||
func (m *Timeout) Hash() common.Hash {
|
||||
return rlpHash(m)
|
||||
}
|
||||
|
||||
func (m *SyncInfo) Hash() common.Hash {
|
||||
return rlpHash(m)
|
||||
type EpochSwitchInfo struct {
|
||||
Masternodes []common.Address
|
||||
EpochSwitchBlockInfo *BlockInfo
|
||||
EpochSwitchParentBlockInfo *BlockInfo
|
||||
}
|
||||
|
||||
type VoteForSign struct {
|
||||
|
|
@ -105,13 +133,3 @@ type TimeoutForSign struct {
|
|||
func TimeoutSigHash(m *TimeoutForSign) common.Hash {
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -427,7 +427,8 @@ func (s *PrivateAccountAPI) SignTransaction(ctx context.Context, args SendTxArgs
|
|||
// safely used to calculate a signature from.
|
||||
//
|
||||
// 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.
|
||||
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)
|
||||
if err != nil || failed {
|
||||
log.Warn("[EstimateGas] api", "err", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
@ -1323,8 +1325,8 @@ func (s *PublicBlockChainAPI) findNearestSignedBlock(ctx context.Context, b *typ
|
|||
}
|
||||
|
||||
/*
|
||||
findFinalityOfBlock return finality of a block
|
||||
Use blocksHashCache for to keep track - refer core/blockchain.go for more detail
|
||||
findFinalityOfBlock return finality of a block
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0
|
||||
// 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 {
|
||||
blockNumber := s.b.CurrentBlock().Number().Uint64()
|
||||
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
|
||||
// then multiple by epoch per year, if the address is not masternode of last epoch - return 0
|
||||
// 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 {
|
||||
votersReward := s.b.GetVotersRewards(masternode)
|
||||
if votersReward == nil {
|
||||
|
|
|
|||
|
|
@ -146,6 +146,16 @@ web3._extend({
|
|||
params: 1,
|
||||
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: [
|
||||
new web3._extend.Property({
|
||||
|
|
|
|||
Loading…
Reference in a new issue