From 8339e133ee46bde5d3f9a41db5005a3c626440d8 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 11:25:22 +0800 Subject: [PATCH 01/15] add function IsZero for type Hash --- common/types.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/types.go b/common/types.go index eea1201b11..72234a226b 100644 --- a/common/types.go +++ b/common/types.go @@ -73,6 +73,9 @@ func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } func Uint64ToHash(b uint64) Hash { return BytesToHash(new(big.Int).SetUint64(b).Bytes()) } func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) } +// IsZero returns if a Hash is empty +func (h Hash) IsZero() bool { return h == Hash{} } + // Get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } func (h Hash) Bytes() []byte { return h[:] } From 4a59bdf15ab30c37d62696d325f2140d79ac7034 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 11:23:08 +0800 Subject: [PATCH 02/15] add function IsZero for type Address --- common/types.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/types.go b/common/types.go index 72234a226b..905c7d5a4e 100644 --- a/common/types.go +++ b/common/types.go @@ -193,6 +193,9 @@ func IsHexAddress(s string) bool { return len(s) == 2*AddressLength && isHex(s) } +// IsZero returns if a address is empty +func (a Address) IsZero() bool { return a == Address{} } + // Get the string representation of the underlying address func (a Address) Str() string { return string(a[:]) } func (a Address) Bytes() []byte { return a[:] } From e2359d9b8cbf80baa382cd5e78adbeca4d874ce9 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 11:46:36 +0800 Subject: [PATCH 03/15] filter zero address in function GetCandidates --- core/state/statedb_utils.go | 15 ++++++++------- eth/hooks/engine_v1_hooks.go | 4 +--- internal/ethapi/api.go | 8 ++------ 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/core/state/statedb_utils.go b/core/state/statedb_utils.go index 4a0c9d45e6..a9c210b569 100644 --- a/core/state/statedb_utils.go +++ b/core/state/statedb_utils.go @@ -93,16 +93,17 @@ func GetCandidates(statedb *StateDB) []common.Address { slot := slotValidatorMapping["candidates"] slotHash := common.BigToHash(new(big.Int).SetUint64(slot)) arrLength := statedb.GetState(common.HexToAddress(common.MasternodeVotingSMC), slotHash) - keys := []common.Hash{} - for i := uint64(0); i < arrLength.Big().Uint64(); i++ { + count := arrLength.Big().Uint64() + rets := make([]common.Address, 0, count) + + for i := uint64(0); i < count; i++ { key := GetLocDynamicArrAtElement(slotHash, i, 1) - keys = append(keys, key) - } - rets := []common.Address{} - for _, key := range keys { ret := statedb.GetState(common.HexToAddress(common.MasternodeVotingSMC), key) - rets = append(rets, common.HexToAddress(ret.Hex())) + if !ret.IsZero() { + rets = append(rets, common.HexToAddress(ret.Hex())) + } } + return rets } diff --git a/eth/hooks/engine_v1_hooks.go b/eth/hooks/engine_v1_hooks.go index 9d14e7a3a4..78ec7a1e19 100644 --- a/eth/hooks/engine_v1_hooks.go +++ b/eth/hooks/engine_v1_hooks.go @@ -238,9 +238,7 @@ func AttachConsensusV1Hooks(adaptor *XDPoS.XDPoS, bc *core.BlockChain, chainConf if err != nil { return nil, err } - if address.String() != "xdc0000000000000000000000000000000000000000" { - candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) - } + candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) } // sort candidates by stake descending sort.Slice(candidates, func(i, j int) bool { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1a0ec61881..1ca90b7e40 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -756,9 +756,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd candidatesAddresses := state.GetCandidates(statedb) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) - if address.String() != "xdc0000000000000000000000000000000000000000" { - candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) - } + candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) } } if err != nil || len(candidates) == 0 { @@ -882,9 +880,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch candidatesAddresses := state.GetCandidates(statedb) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) - if address.String() != "xdc0000000000000000000000000000000000000000" { - candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) - } + candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) } } From 2c02ddc2d9a3526cde07f392eb620a92f4cbbf40 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 11:55:07 +0800 Subject: [PATCH 04/15] remove xdc0000000000000000000000000000000000000000 --- core/blockchain.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/blockchain.go b/core/blockchain.go index 7998b493cc..7fac44c075 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -2554,8 +2554,8 @@ func (bc *BlockChain) UpdateM1() error { if err != nil { return err } - //TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" - if candidate.String() != "xdc0000000000000000000000000000000000000000" { + // TODO: smart contract shouldn't return "0x0000000000000000000000000000000000000000" + if !candidate.IsZero() { ms = append(ms, utils.Masternode{Address: candidate, Stake: v}) } } From fbb8c54d8635712feb3f747293e38b9d3b9df7ce Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 12:04:24 +0800 Subject: [PATCH 05/15] remove 0x0000000000000000000000000000000000000000 --- internal/ethapi/api.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 1ca90b7e40..4514f630f5 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1008,11 +1008,12 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno var candidatesWithStakeInfo []utils.Masternode for _, candidate := range candidates { - v, err := validator.GetCandidateCap(opts, candidate) - if err != nil { - return []utils.Masternode{}, err - } - if candidate.String() != "xdc0000000000000000000000000000000000000000" { + if !candidate.IsZero() { + v, err := validator.GetCandidateCap(opts, candidate) + if err != nil { + return []utils.Masternode{}, err + } + candidatesWithStakeInfo = append(candidatesWithStakeInfo, utils.Masternode{Address: candidate, Stake: v}) } @@ -1022,6 +1023,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno }) } } + return candidatesWithStakeInfo, nil } From f4154d0479b3eb47aa7cdf819bdaa5dec773c9da Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 13:45:35 +0800 Subject: [PATCH 06/15] remove lendingstate.EmptyAddress --- XDCxlending/lendingstate/common.go | 5 ++--- XDCxlending/lendingstate/lendingitem.go | 11 ++++++----- XDCxlending/order_processor.go | 16 ++++++++-------- core/lending_pool.go | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/XDCxlending/lendingstate/common.go b/XDCxlending/lendingstate/common.go index 3f52854640..d22abc37da 100644 --- a/XDCxlending/lendingstate/common.go +++ b/XDCxlending/lendingstate/common.go @@ -2,16 +2,15 @@ package lendingstate import ( "encoding/json" - "github.com/XinFinOrg/XDPoSChain/crypto" "math/big" "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/crypto" ) var ( - EmptyAddress = "xdc0000000000000000000000000000000000000000" - EmptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") + EmptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") ) var EmptyHash = common.Hash{} diff --git a/XDCxlending/lendingstate/lendingitem.go b/XDCxlending/lendingstate/lendingitem.go index 13d9ff0ecf..09006738eb 100644 --- a/XDCxlending/lendingstate/lendingitem.go +++ b/XDCxlending/lendingstate/lendingitem.go @@ -2,14 +2,15 @@ package lendingstate import ( "fmt" + "math/big" + "strconv" + "time" + "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto/sha3" "github.com/globalsign/mgo/bson" - "math/big" - "strconv" - "time" ) const ( @@ -243,7 +244,7 @@ func (l *LendingItem) VerifyLendingSide() error { } func (l *LendingItem) VerifyCollateral(state *state.StateDB) error { - if l.CollateralToken.String() == EmptyAddress || l.CollateralToken.String() == l.LendingToken.String() { + if l.CollateralToken.IsZero() || l.CollateralToken == l.LendingToken { return fmt.Errorf("invalid collateral %s", l.CollateralToken.Hex()) } validCollateral := false @@ -329,7 +330,7 @@ func (l *LendingItem) EncodedSide() *big.Int { return big.NewInt(1) } -//verify signatures +// verify signatures func (l *LendingItem) VerifyLendingSignature() error { V := big.NewInt(int64(l.Signature.V)) R := l.Signature.R.Big() diff --git a/XDCxlending/order_processor.go b/XDCxlending/order_processor.go index 3ee8e6aade..2dcfb560e6 100644 --- a/XDCxlending/order_processor.go +++ b/XDCxlending/order_processor.go @@ -3,6 +3,8 @@ package XDCxlending import ( "encoding/json" "fmt" + "math/big" + "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" "github.com/XinFinOrg/XDPoSChain/XDCxlending/lendingstate" "github.com/XinFinOrg/XDPoSChain/common" @@ -10,7 +12,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" - "math/big" ) func (l *Lending) CommitOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, lendingStateDB *lendingstate.LendingStateDB, tradingStateDb *tradingstate.TradingStateDB, lendingOrderBook common.Hash, order *lendingstate.LendingItem) ([]*lendingstate.LendingTrade, []*lendingstate.LendingItem, error) { @@ -262,10 +263,9 @@ func (l *Lending) processOrderList(header *types.Header, coinbase common.Address collateralToken = oldestOrder.CollateralToken borrowFee = lendingstate.GetFee(statedb, oldestOrder.Relayer) } - if collateralToken.String() == lendingstate.EmptyAddress { + if collateralToken.IsZero() { return nil, nil, nil, fmt.Errorf("empty collateral") } - collateralPrice := common.BasePrice depositRate, liquidationRate, recallRate := lendingstate.GetCollateralDetail(statedb, collateralToken) if depositRate == nil || depositRate.Sign() <= 0 { return nil, nil, nil, fmt.Errorf("invalid depositRate %v", depositRate) @@ -953,11 +953,11 @@ func (l *Lending) GetMediumTradePriceBeforeEpoch(chain consensus.ChainContext, s return nil, nil } -//LendToken and CollateralToken must meet at least one of following conditions -//- Have direct pair in XDCX: lendToken/CollateralToken or CollateralToken/LendToken -//- Have pairs with XDC: -//- lendToken/XDC and CollateralToken/XDC -//- XDC/lendToken and XDC/CollateralToken +// LendToken and CollateralToken must meet at least one of following conditions +// - Have direct pair in XDCX: lendToken/CollateralToken or CollateralToken/LendToken +// - Have pairs with XDC: +// - lendToken/XDC and CollateralToken/XDC +// - XDC/lendToken and XDC/CollateralToken func (l *Lending) GetCollateralPrices(header *types.Header, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, collateralToken common.Address, lendingToken common.Address) (*big.Int, *big.Int, error) { // lendTokenXDCPrice: price of ticker lendToken/XDC // collateralXDCPrice: price of ticker collateralToken/XDC diff --git a/core/lending_pool.go b/core/lending_pool.go index 4f600b2e58..26d7a1395a 100644 --- a/core/lending_pool.go +++ b/core/lending_pool.go @@ -431,7 +431,7 @@ func (pool *LendingPool) validateNewLending(cloneStateDb *state.StateDB, cloneLe return ErrInvalidLendingType } if tx.Side() == lendingstate.Borrowing { - if tx.CollateralToken().String() == lendingstate.EmptyAddress || tx.CollateralToken().String() == tx.LendingToken().String() { + if tx.CollateralToken().IsZero() || tx.CollateralToken() == tx.LendingToken() { return ErrInvalidLendingCollateral } validCollateral := false @@ -541,7 +541,7 @@ func (pool *LendingPool) validateBalance(cloneStateDb *state.StateDB, cloneLendi // collateralPrice = BTC/USD (eg: 8000 USD) // lendTokenXDCPrice: price of lendingToken in XDC quote var lendTokenXDCPrice, collateralPrice, collateralTokenDecimal *big.Int - if collateralToken.String() != lendingstate.EmptyAddress { + if !collateralToken.IsZero() { collateralTokenDecimal, err = XDCXServ.GetTokenDecimal(pool.chain, cloneStateDb, collateralToken) if err != nil { return fmt.Errorf("validateOrder: failed to get collateralTokenDecimal. err: %v", err) From ebf1002c67b30b7ca30af6fd270bdb843e2031e0 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Mon, 17 Jul 2023 11:53:56 +0800 Subject: [PATCH 07/15] fix function GetPreviousCheckpointFromEpoch --- internal/ethapi/api.go | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 4514f630f5..45342e931a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -962,28 +962,23 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch // GetPreviousCheckpointFromEpoch returns header of the previous checkpoint func (s *PublicBlockChainAPI) GetPreviousCheckpointFromEpoch(ctx context.Context, epochNum rpc.EpochNumber) (rpc.BlockNumber, rpc.EpochNumber) { var checkpointNumber uint64 + epoch := s.b.ChainConfig().XDPoS.Epoch - if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { - currentCheckpointNumber, epochNumber, err := engine.GetCurrentEpochSwitchBlock(s.chainReader, s.b.CurrentBlock().Number()) - if err != nil { - log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get current epoch switch block information", "Block", s.b.CurrentBlock(), "Error", err) + if epochNum == rpc.LatestEpochNumber { + blockNumer := s.b.CurrentBlock().Number().Uint64() + diff := blockNumer % epoch + // checkpoint number + checkpointNumber = blockNumer - diff + epochNum = rpc.EpochNumber(checkpointNumber / epoch) + if diff > 0 { + epochNum += 1 } - if epochNum == rpc.LatestEpochNumber { - checkpointNumber = currentCheckpointNumber - epochNum = rpc.EpochNumber(epochNumber) - } else if epochNum < 2 { - checkpointNumber = 0 - } else { - blockNumberBeforeCurrentEpochSwitch := currentCheckpointNumber - 1 - checkpointNumber, _, err = engine.GetCurrentEpochSwitchBlock(s.chainReader, big.NewInt(int64(blockNumberBeforeCurrentEpochSwitch))) - if err != nil { - log.Error("[GetPreviousCheckpointFromEpoch] Error while trying to get last epoch switch block information", "Number", blockNumberBeforeCurrentEpochSwitch, "Error", err) - } - } - return rpc.BlockNumber(checkpointNumber), epochNum + } else if epochNum < 2 { + checkpointNumber = 0 } else { - panic("[GetPreviousCheckpointFromEpoch] Error while trying to get XDPoS consensus engine") + checkpointNumber = epoch * (uint64(epochNum) - 1) } + return rpc.BlockNumber(checkpointNumber), epochNum } // getCandidatesFromSmartContract returns all candidates with their capacities at the current time From 3214bbeeba35a3a0eba20280887ca444b3e43346 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 14:36:42 +0800 Subject: [PATCH 08/15] sort candidates in function GetCandidates --- internal/ethapi/api.go | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 45342e931a..7aa6a4736f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -878,6 +878,7 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch return result, err } candidatesAddresses := state.GetCandidates(statedb) + candidates = make([]utils.Masternode, 0, len(candidatesAddresses)) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) @@ -889,15 +890,8 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch result[fieldSuccess] = false return result, err } - // First, set all candidate to propose - for _, candidate := range candidates { - candidatesStatusMap[candidate.Address.String()] = map[string]interface{}{ - fieldStatus: statusProposed, - fieldCapacity: candidate.Stake, - } - } - // Second, Find candidates that have masternode status + // Find candidates that have masternode status if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { @@ -908,6 +902,15 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch } else { log.Error("Undefined XDPoS consensus engine") } + + // Set all candidate to propose + for _, candidate := range candidates { + candidatesStatusMap[candidate.Address.String()] = map[string]interface{}{ + fieldStatus: statusProposed, + fieldCapacity: candidate.Stake, + } + } + // Set masternode status for _, masternode := range masternodes { if candidatesStatusMap[masternode.String()] != nil { @@ -915,7 +918,20 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch } } - // Third, Get penalties list + var maxMasternodes int + if s.b.ChainConfig().IsTIPIncreaseMasternodes(block.Number()) { + maxMasternodes = common.MaxMasternodesV2 + } else { + maxMasternodes = common.MaxMasternodes + } + + if len(candidates) > maxMasternodes { + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 + }) + } + + // Get penalties list penalties = append(penalties, header.Penalties...) // check last 5 epochs to find penalize masternodes for i := 1; i <= common.LimitPenaltyEpoch; i++ { From 7dc5ffe45a5b66d0e0134e773750f0a7c55b1ed3 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 14:47:38 +0800 Subject: [PATCH 09/15] fix topCandidates in function GetCandidates --- internal/ethapi/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 7aa6a4736f..9bcff052e9 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -954,8 +954,8 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch penaltyList = common.ExtractAddressFromBytes(penalties) var topCandidates []utils.Masternode - if len(candidates) > common.MaxMasternodes { - topCandidates = candidates[:common.MaxMasternodes] + if len(candidates) > maxMasternodes { + topCandidates = candidates[:maxMasternodes] } else { topCandidates = candidates } From 5d24dfdf7e5b498a3d955b450af8e2c92a08dd11 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 14:50:16 +0800 Subject: [PATCH 10/15] not sort candidates in getCandidatesFromSmartContract --- internal/ethapi/api.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 9bcff052e9..f62ce241fd 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1016,7 +1016,7 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno return []utils.Masternode{}, err } - var candidatesWithStakeInfo []utils.Masternode + candidatesWithStakeInfo := make([]utils.Masternode, 0, len(candidates)) for _, candidate := range candidates { if !candidate.IsZero() { @@ -1027,12 +1027,6 @@ func (s *PublicBlockChainAPI) getCandidatesFromSmartContract() ([]utils.Masterno candidatesWithStakeInfo = append(candidatesWithStakeInfo, utils.Masternode{Address: candidate, Stake: v}) } - - if len(candidatesWithStakeInfo) > 0 { - sort.Slice(candidatesWithStakeInfo, func(i, j int) bool { - return candidatesWithStakeInfo[i].Stake.Cmp(candidatesWithStakeInfo[j].Stake) >= 0 - }) - } } return candidatesWithStakeInfo, nil From 6e7d7f500285b9f11cf070b9178868f8140e886c Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 15:10:19 +0800 Subject: [PATCH 11/15] return non-candidate masternode in GetCandidates --- internal/ethapi/api.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index f62ce241fd..bc4ff76224 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -851,7 +851,6 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch fieldSuccess: true, } epochConfig := s.b.ChainConfig().XDPoS.Epoch - candidatesStatusMap := map[string]map[string]interface{}{} checkpointNumber, epochNumber = s.GetPreviousCheckpointFromEpoch(ctx, epoch) result[fieldEpoch] = epochNumber.Int64() @@ -903,7 +902,8 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch log.Error("Undefined XDPoS consensus engine") } - // Set all candidate to propose + // Set all candidate to statusProposed + candidatesStatusMap := make(map[string]map[string]interface{}, len(candidates)) for _, candidate := range candidates { candidatesStatusMap[candidate.Address.String()] = map[string]interface{}{ fieldStatus: statusProposed, @@ -911,10 +911,17 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch } } - // Set masternode status + // Set masternodes to statusMasternode for _, masternode := range masternodes { - if candidatesStatusMap[masternode.String()] != nil { - candidatesStatusMap[masternode.String()][fieldStatus] = statusMasternode + key := masternode.String() + if candidatesStatusMap[key] != nil { + candidatesStatusMap[key][fieldStatus] = statusMasternode + } else { + candidatesStatusMap[key] = map[string]interface{}{ + fieldStatus: statusMasternode, + fieldCapacity: -1, + } + log.Warn("Masternode is not candidate", "masternode", key, "checkpointNumber", checkpointNumber, "epoch", epoch, "epochNumber", epochNumber) } } @@ -925,6 +932,11 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch maxMasternodes = common.MaxMasternodes } + if len(masternodes) >= maxMasternodes { + result[fieldCandidates] = candidatesStatusMap + return result, nil + } + if len(candidates) > maxMasternodes { sort.Slice(candidates, func(i, j int) bool { return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 From 8ed9a754b297122e4910064ca1d514edb8bf42ee Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 15:30:13 +0800 Subject: [PATCH 12/15] return more slashed nodes in GetCandidates --- internal/ethapi/api.go | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index bc4ff76224..44ad2ed51a 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -965,20 +965,19 @@ func (s *PublicBlockChainAPI) GetCandidates(ctx context.Context, epoch rpc.Epoch } penaltyList = common.ExtractAddressFromBytes(penalties) - var topCandidates []utils.Masternode - if len(candidates) > maxMasternodes { - topCandidates = candidates[:maxMasternodes] - } else { - topCandidates = candidates - } - // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top 150 candidates - // if it's SLASHED but it's out of top 150, the status should be still PROPOSED - for _, pen := range penaltyList { - for _, candidate := range topCandidates { - if candidate.Address == pen && candidatesStatusMap[pen.String()] != nil { + // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top maxMasternodes candidates. + // if it's SLASHED but it's out of top maxMasternodes, the status should be still PROPOSED. + total := len(masternodes) + for _, candidate := range candidates { + for _, pen := range penaltyList { + if candidate.Address == pen { candidatesStatusMap[pen.String()][fieldStatus] = statusSlashed + total++ + if total >= maxMasternodes { + result[fieldCandidates] = candidatesStatusMap + return result, nil + } } - penalties = append(penalties, block.Penalties()...) } } From 745e0970408f726046d64b40cf011aa10d1e8229 Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 14:22:20 +0800 Subject: [PATCH 13/15] sort candidates in function GetCandidateStatus --- internal/ethapi/api.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 44ad2ed51a..2e61139e7d 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -754,6 +754,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd return result, err } candidatesAddresses := state.GetCandidates(statedb) + candidates = make([]utils.Masternode, 0, len(candidatesAddresses)) for _, address := range candidatesAddresses { v := state.GetCandidateCap(statedb, address) candidates = append(candidates, utils.Masternode{Address: address, Stake: v}) @@ -764,6 +765,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd result[fieldSuccess] = false return result, err } + var maxMasternodes int if s.b.ChainConfig().IsTIPIncreaseMasternodes(block.Number()) { maxMasternodes = common.MaxMasternodesV2 @@ -807,6 +809,12 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd } } + if len(candidates) > maxMasternodes { + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 + }) + } + // Third, Get penalties list penalties = append(penalties, header.Penalties...) // check last 5 epochs to find penalize masternodes From e17a0869e5f4b8c19917263ef891054a0a4e588d Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 16:04:44 +0800 Subject: [PATCH 14/15] check masternode before candidate in GetCandidateStatus --- internal/ethapi/api.go | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 2e61139e7d..a3138ca257 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -773,24 +773,19 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd maxMasternodes = common.MaxMasternodes } - isTopCandidate := false - // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top 150 candidates - // if it's SLASHED but it's out of top 150, the status should be still PROPOSED + // check penalties from checkpoint headers and modify status of a node to SLASHED if it's in top maxMasternodes candidates. + // if it's SLASHED but it's out of top maxMasternodes, the status should be still PROPOSED. + isCandidate := false for i := 0; i < len(candidates); i++ { if coinbaseAddress == candidates[i].Address { - if i < maxMasternodes { - isTopCandidate = true - } + isCandidate = true result[fieldStatus] = statusProposed result[fieldCapacity] = candidates[i].Stake break } } - if !isTopCandidate { - return result, nil - } - // Second, Find candidates that have masternode status + // Get masternode list if engine, ok := s.b.GetEngine().(*XDPoS.XDPoS); ok { masternodes = engine.GetMasternodesFromCheckpointHeader(header) if len(masternodes) == 0 { @@ -801,14 +796,23 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd } else { log.Error("Undefined XDPoS consensus engine") } - // Set masternode status + + // Set to statusMasternode if it is masternode for _, masternode := range masternodes { if coinbaseAddress == masternode { result[fieldStatus] = statusMasternode + if !isCandidate { + result[fieldCapacity] = -1 + log.Warn("Find non-candidate masternode", "masternode", masternode.String(), "checkpointNumber", checkpointNumber, "epoch", epoch, "epochNumber", epochNumber) + } return result, nil } } + if !isCandidate || len(masternodes) >= maxMasternodes { + return result, nil + } + if len(candidates) > maxMasternodes { sort.Slice(candidates, func(i, j int) bool { return candidates[i].Stake.Cmp(candidates[j].Stake) > 0 From f32c66ebc171123d18553d006e35386c4ef13f6e Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Fri, 14 Jul 2023 16:30:47 +0800 Subject: [PATCH 15/15] check slashed status in GetCandidateStatus --- internal/ethapi/api.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a3138ca257..eedde75d18 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -819,7 +819,7 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd }) } - // Third, Get penalties list + // Get penalties list penalties = append(penalties, header.Penalties...) // check last 5 epochs to find penalize masternodes for i := 1; i <= common.LimitPenaltyEpoch; i++ { @@ -837,12 +837,22 @@ func (s *PublicBlockChainAPI) GetCandidateStatus(ctx context.Context, coinbaseAd penaltyList = common.ExtractAddressFromBytes(penalties) // map slashing status - for _, pen := range penaltyList { - if coinbaseAddress == pen { - result[fieldStatus] = statusSlashed - return result, nil + total := len(masternodes) + for _, candidate := range candidates { + for _, pen := range penaltyList { + if candidate.Address == pen { + if coinbaseAddress == pen { + result[fieldStatus] = statusSlashed + return result, nil + } + total++ + if total >= maxMasternodes { + return result, nil + } + } } } + return result, nil }