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
}
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,
}
}
}

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
backing account.
type SignerFn func(accounts.Account, []byte) ([]byte, 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,19 +635,27 @@ 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
}
/*
@ -875,8 +888,9 @@ func (x *XDPoS_v2) setNewRound(blockChainReader consensus.ChainReader, round typ
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{}) {
@ -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 {

View file

@ -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{
@ -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()
}

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())
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
}

View file

@ -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
}
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(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, masterNode, err := x.verifyMsgSignature(signedVote, v.Signature, masternodes)
if err != nil {
log.Warn("[verifyVotes] error while verifying vote signature", "error", err.Error())
return
}
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("[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 {
masternodes := x.GetMasternodes(chain, proposedBlockHeader)
start := time.Now()
// 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) {
defer wg.Done()
signedVote := types.VoteSigHash(&types.VoteForSign{
ProposedBlockInfo: v.ProposedBlockInfo,
GapNumber: v.GapNumber,
})
verified, _, 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
}
}(h, vote.(*types.Vote), counter)
counter++
}
wg.Wait()
elapsed := time.Since(start)
log.Debug("[onVotePoolThresholdReached] verify message signatures of vote pool took", "elapsed", elapsed)
// 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()
}

View file

@ -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) {

View file

@ -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

View file

@ -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)
}

View file

@ -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)
}

View file

@ -427,6 +427,7 @@ 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}).
//
// This gives context to the signed message and prevents signing of transactions.
@ -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
@ -2979,6 +2981,7 @@ 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
func (s *PublicBlockChainAPI) GetStakerROI() float64 {
blockNumber := s.b.CurrentBlock().Number().Uint64()
@ -3005,6 +3008,7 @@ 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
func (s *PublicBlockChainAPI) GetStakerROIMasternode(masternode common.Address) float64 {
votersReward := s.b.GetVotersRewards(masternode)

View file

@ -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({