diff --git a/README.md b/README.md index ed662b633..b6f99cd51 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,20 @@ Make sure the [macOS version](https://en.wikipedia.org/wiki/MacOS#Release_histor ```bash $ uname -srm -Darwin 18.7.0 x86_64 +Darwin 20.04 x86_64 ``` -Use [Homebrew](https://brew.sh/) to install Golang 1.16 +Use [Homebrew](https://brew.sh/) to install Golang 1.20 ```bash -$ brew install go@1.16 +$ brew install go@1.20 ``` Check the golang version. Make sure they are the following version number or above. ```bash $ go version -go version go1.16.5 darwin/amd64 +go version go1.20.3 darwin/amd64 ``` ### 2. Ubuntu Prerequisites @@ -40,7 +40,7 @@ Make sure your ubuntu version is 18.04 or later. ```bash $ cat /etc/issue -Ubuntu 18.04.5 LTS \n \l +Ubuntu 20.04 LTS \n \l ``` Install Git. @@ -52,8 +52,8 @@ $ sudo apt-get install -y git Install Go distribution. ```bash -$ curl -O https://golang.org/dl/go1.13.15.linux-amd64.tar.gz -$ tar -xvf go1.13.15.linux-amd64.tar.gz +$ curl -O https://golang.org/dl/go1.20.3.linux-amd64.tar.gz +$ tar -xvf go1.20.3.linux-amd64.tar.gz $ sudo chown -R root:root ./go $ sudo mv go /usr/local $ export GOPATH=$HOME/go diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 0dde2b7c8..4201024f6 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -698,7 +698,6 @@ func (b *BlockChain) GetHeight() uint32 { return b.getHeight() } - func (b *BlockChain) getHeight() uint32 { if len(b.Nodes) == 0 { return 0 @@ -1645,9 +1644,7 @@ func (b *BlockChain) maybeAcceptBlock(block *Block, confirm *payload.Confirm) (b b.state.RevertToPOWBlockHeight, false) DefaultLedger.Arbitrators.DumpInfo(block.Height) } - events.Notify(events.ETBlockProcessed, block) - // Notify the caller that the new block was accepted into the block // chain. The caller would typically want to react by relaying the // inventory to other peers. diff --git a/common/config/config.go b/common/config/config.go index 4ea91e800..2f51126e7 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -197,6 +197,9 @@ func GetDefaultParams() *Configuration { NFTStartHeight: 1405000, NFTV2StartHeight: math.MaxUint32, // todo complete me DexStartHeight: math.MaxUint32, // todo complete me + ChangeVoteTargetStartHeight: math.MaxUint32, // todo complete me + RenewalVotingTargetDuration: 14 * 720, // todo complete me + MinRenewalVotingTargetLockTime: 720, // todo complete me OriginArbiters: []string{ "0248df6705a909432be041e0baa25b8f648741018f70d1911f2ed28778db4b8fe4", "02771faf0f4d4235744b30972d5f2c470993920846c761e4d08889ecfdc061cddf", @@ -388,6 +391,9 @@ func (p *Configuration) TestNet() *Configuration { p.DPoSConfiguration.NFTStartHeight = 1098000 p.DPoSConfiguration.NFTV2StartHeight = 1171000 p.DPoSConfiguration.DexStartHeight = 1171000 + p.DPoSConfiguration.ChangeVoteTargetStartHeight = 1252600 + p.DPoSConfiguration.RenewalVotingTargetDuration = 14 * 720 + p.DPoSConfiguration.MinRenewalVotingTargetLockTime = 720 p.HttpInfoPort = 21333 p.HttpRestPort = 21334 @@ -520,8 +526,11 @@ func (p *Configuration) RegNet() *Configuration { p.ProducerSchnorrStartHeight = math.MaxUint32 p.CRSchnorrStartHeight = math.MaxUint32 p.VotesSchnorrStartHeight = math.MaxUint32 - p.MultiExchangeVotesStartHeight = math.MaxUint32 // todo complete me - p.DPoSConfiguration.DexStartHeight = math.MaxUint32 // todo complete me + p.MultiExchangeVotesStartHeight = math.MaxUint32 // todo complete me + p.DPoSConfiguration.DexStartHeight = math.MaxUint32 // todo complete me + p.DPoSConfiguration.ChangeVoteTargetStartHeight = math.MaxUint32 // todo complete me + p.DPoSConfiguration.RenewalVotingTargetDuration = 14 * 720 // todo complete me + p.DPoSConfiguration.MinRenewalVotingTargetLockTime = 720 // todo complete me p.CRConfiguration.ChangeSideChainMinGasPriceHeight = math.MaxUint32 // todo complete me @@ -745,6 +754,12 @@ type DPoSConfiguration struct { NFTV2StartHeight uint32 `screw:"--NFTV2StartHeight" usage:"the start height of NFT 2.0 transaction"` // DexStartHeight defines the height of DEX started. DexStartHeight uint32 `screw:"--dexstartheight" usage:"the starting height of Dex support"` + // ChangeVoteTargetStartHeight defines the starting height for allowing changes to the voting target. + ChangeVoteTargetStartHeight uint32 `screw:"--changevotetargetstartheight" usage:"the starting height for allowing changes to the voting target"` + // RenewalVotingTargetDuration defines the duration of renewal voting target. + RenewalVotingTargetDuration uint32 `crew:"--renewalvotingtargetduration" usage:"the duration of renewal voting target"` + // MinRenewalVotingTargetLockTime defines the min lock time of renewal voting target. + MinRenewalVotingTargetLockTime uint32 `crew:"--minrenewalvotingtargetlocktime" usage:"the min lock time of renewal voting target"` } type CRConfiguration struct { diff --git a/core/checkpoint/manager.go b/core/checkpoint/manager.go index c9f70b0d3..2c0a6a63b 100644 --- a/core/checkpoint/manager.go +++ b/core/checkpoint/manager.go @@ -355,7 +355,6 @@ func (m *Manager) onBlockSaved(block *types.DposBlock, <-reply } } - if block.Height >= originalHeight+v.SavePeriod() { v.SetHeight(block.Height) diff --git a/core/transaction/activateproducertransaction.go b/core/transaction/activateproducertransaction.go index 42f6ac206..d763d0a62 100644 --- a/core/transaction/activateproducertransaction.go +++ b/core/transaction/activateproducertransaction.go @@ -175,7 +175,7 @@ func (t *ActivateProducerTransaction) SpecialContextCheck() (elaerr.ELAError, bo } var minActivateAmount common.Fixed64 - if t.parameters.BlockHeight >= t.parameters.BlockChain.GetState().DPoSV2ActiveHeight { + if t.parameters.BlockHeight >= t.parameters.BlockChain.GetState().GetDPoSV2ActiveHeight() { switch producer.Identity() { case state.DPoSV1: return elaerr.Simple(elaerr.ErrTxPayload, errors.New("not allow to activate producer 1.0")), true diff --git a/core/transaction/crcappropriationtransaction.go b/core/transaction/crcappropriationtransaction.go index 66df1a2b4..6e8116ff8 100644 --- a/core/transaction/crcappropriationtransaction.go +++ b/core/transaction/crcappropriationtransaction.go @@ -139,7 +139,7 @@ func (t *CRCAppropriationTransaction) SpecialContextCheck() (result elaerr.ELAEr // // Outputs has check in CheckTransactionOutput function: // first one to CRCommitteeAddress, second one to CRAssetsAddress - appropriationAmount := t.parameters.BlockChain.GetCRCommittee().AppropriationAmount + appropriationAmount := t.parameters.BlockChain.GetCRCommittee().GetAppropriationAmount() if appropriationAmount != t.Outputs()[0].Value { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("invalid appropriation amount %s, need to be %s", diff --git a/core/transaction/crcouncilmemberclaimnodetransaction.go b/core/transaction/crcouncilmemberclaimnodetransaction.go index fcdd77430..cdf7959bd 100644 --- a/core/transaction/crcouncilmemberclaimnodetransaction.go +++ b/core/transaction/crcouncilmemberclaimnodetransaction.go @@ -7,7 +7,6 @@ package transaction import ( "bytes" - "encoding/hex" "errors" "fmt" @@ -64,7 +63,7 @@ func (t *CRCouncilMemberClaimNodeTransaction) SpecialContextCheck() (result elae switch t.payloadVersion { case payload.CurrentCRClaimDPoSNodeVersion: crMember = t.parameters.BlockChain.GetCRCommittee().GetMember(did) - if _, ok := comm.ClaimedDPoSKeys[hex.EncodeToString(manager.NodePublicKey)]; ok { + if ok := comm.PubKeyExistClaimedDPoSKeys(manager.NodePublicKey); ok { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("producer already registered")), true } // check duplication of node. @@ -73,7 +72,7 @@ func (t *CRCouncilMemberClaimNodeTransaction) SpecialContextCheck() (result elae } case payload.NextCRClaimDPoSNodeVersion: crMember = t.parameters.BlockChain.GetCRCommittee().GetNextMember(did) - if _, ok := comm.NextClaimedDPoSKeys[hex.EncodeToString(manager.NodePublicKey)]; ok { + if ok := comm.PubKeyExistNextClaimedDPoSKey(manager.NodePublicKey); ok { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("producer already registered")), true } // check duplication of node. diff --git a/core/transaction/crcproposaltransaction.go b/core/transaction/crcproposaltransaction.go index 44adb6b33..c6266945e 100644 --- a/core/transaction/crcproposaltransaction.go +++ b/core/transaction/crcproposaltransaction.go @@ -679,15 +679,15 @@ func (t *CRCProposalTransaction) checkNormalOrELIPProposal(params *TransactionPa if finalPaymentCount != 1 { return errors.New("final payment count invalid") } - if amount > (t.parameters.BlockChain.GetCRCommittee().CRCCurrentStageAmount- - t.parameters.BlockChain.GetCRCommittee().CommitteeUsedAmount)*blockchain.CRCProposalBudgetsPercentage/100 { + if amount > (t.parameters.BlockChain.GetCRCommittee().GetCRCCurrentStageAmount()- + t.parameters.BlockChain.GetCRCommittee().GetCommitteeUsedAmount())*blockchain.CRCProposalBudgetsPercentage/100 { return errors.New("budgets exceeds 10% of CRC committee balance") - } else if amount > t.parameters.BlockChain.GetCRCommittee().CRCCurrentStageAmount- - t.parameters.BlockChain.GetCRCommittee().CRCCommitteeUsedAmount-proposalsUsedAmount { + } else if amount > t.parameters.BlockChain.GetCRCommittee().GetCRCCurrentStageAmount()- + t.parameters.BlockChain.GetCRCommittee().GetCRCCommitteeUsedAmount()-proposalsUsedAmount { return errors.New(fmt.Sprintf("budgets exceeds the balance of CRC"+ " committee, proposal hash:%s, budgets:%s, need <= %s", - common.ToReversedString(proposal.Hash(PayloadVersion)), amount, t.parameters.BlockChain.GetCRCommittee().CRCCurrentStageAmount- - t.parameters.BlockChain.GetCRCommittee().CRCCommitteeUsedAmount-proposalsUsedAmount)) + common.ToReversedString(proposal.Hash(PayloadVersion)), amount, t.parameters.BlockChain.GetCRCommittee().GetCRCCurrentStageAmount()- + t.parameters.BlockChain.GetCRCommittee().GetCRCCommitteeUsedAmount()-proposalsUsedAmount)) } else if amount < 0 { return errors.New("budgets is invalid") } diff --git a/core/transaction/createnfttransaction.go b/core/transaction/createnfttransaction.go index 30d49fced..12d06f739 100644 --- a/core/transaction/createnfttransaction.go +++ b/core/transaction/createnfttransaction.go @@ -92,7 +92,21 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } state := t.parameters.BlockChain.GetState() - crState := t.parameters.BlockChain.GetCRCommittee().GetState() + crCommittee := t.parameters.BlockChain.GetCRCommittee() + + processingReferKeys := make(map[common.Uint256]struct{}, 0) + for _, v := range state.GetRenewalTargetTransactionsInfo() { + for _, tx := range v { + pd := tx.Payload().(*payload.Voting) + for _, content := range pd.RenewalContents { + processingReferKeys[content.ReferKey] = struct{}{} + } + } + } + if _, ok := processingReferKeys[pld.ReferKey]; ok { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("refer key is processing")), true + } + producers := state.GetDposV2Producers() nftID := common.GetNFTID(pld.ReferKey, t.hash()) var existVote bool @@ -143,29 +157,29 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } // nft has not been created before - if g, ok := state.NFTIDInfoHashMap[nftID]; ok { + if nftInfo, ok := state.GetNFTInfo(nftID); ok { log.Warnf("NFT has been create before, side chain genesis block "+ - "hash: %s", g) + "hash: %s", nftInfo.GenesisBlockHash.String()) return elaerr.Simple(elaerr.ErrTxPayload, errors.New("NFT has been created before")), true } // check the vote rights is enough or not - totalVoteRights := state.DposV2VoteRights[*stakeProgramHash] + totalVoteRights, _ := state.GetDposV2VoteRights(*stakeProgramHash) var usedCRVotes common.Fixed64 - if ucv := crState.UsedCRVotes[*stakeProgramHash]; ucv != nil { + if ucv := crCommittee.GetUsedCRVotes(*stakeProgramHash); ucv != nil { for _, v := range ucv { usedCRVotes += v.Votes } } var usedCRImpeachmentVotes common.Fixed64 - if ucv := crState.UsedCRImpeachmentVotes[*stakeProgramHash]; ucv != nil { + if ucv := crCommittee.GetUsedCRImpeachmentVotes(*stakeProgramHash); ucv != nil { for _, v := range ucv { usedCRImpeachmentVotes += v.Votes } } var usedCRProposalVotes common.Fixed64 - if ucv := crState.UsedCRCProposalVotes[*stakeProgramHash]; ucv != nil { + if ucv := crCommittee.GetUsedCRCProposalVotes(*stakeProgramHash); ucv != nil { for _, v := range ucv { if usedCRProposalVotes < v.Votes { usedCRProposalVotes = v.Votes @@ -173,7 +187,7 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } } var usedDPoSVotes common.Fixed64 - if udv := state.UsedDposVotes[*stakeProgramHash]; udv != nil { + if udv, ok := state.GetUsedDposVotes(*stakeProgramHash); ok { for _, v := range udv { if usedDPoSVotes < v.Votes { usedDPoSVotes = v.Votes @@ -182,7 +196,7 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } blockHeight := t.parameters.BlockHeight - if blockHeight < state.DPoSV2ActiveHeight { + if blockHeight < state.GetDPoSV2ActiveHeight() { if nftAmount > totalVoteRights-usedDPoSVotes { log.Errorf("vote rights is not enough, nft amount:%s, "+ "total vote rights:%s, used DPoS 1.0 votes:%s", diff --git a/core/transaction/dposv2claimrewardtransaction.go b/core/transaction/dposv2claimrewardtransaction.go index 1f583fc12..94b9525bf 100644 --- a/core/transaction/dposv2claimrewardtransaction.go +++ b/core/transaction/dposv2claimrewardtransaction.go @@ -98,13 +98,13 @@ func (t *DPoSV2ClaimRewardTransaction) SpecialContextCheck() (elaerr.ELAError, b if err != nil { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("Programs code to address error")), true } - claimAmount, ok := t.parameters.BlockChain.GetState().DPoSV2RewardInfo[addr] + claimAmount, ok := t.parameters.BlockChain.GetState().GetDPoSV2RewardInfo(addr) if !ok { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("no reward to claim for such address")), true } if claimAmount < claimReward.Value { - return elaerr.Simple(elaerr.ErrTxPayload, errors.New("claim reward exceeded , max claim reward "+claimAmount.String()+"current:"+ claimAmount.String())), true + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("claim reward exceeded , max claim reward "+claimAmount.String()+"current:"+claimAmount.String())), true } if claimReward.Value <= t.parameters.Config.CRConfiguration.RealWithdrawSingleFee { diff --git a/core/transaction/registerproducertransaction.go b/core/transaction/registerproducertransaction.go index cdf66e099..2b52acba9 100644 --- a/core/transaction/registerproducertransaction.go +++ b/core/transaction/registerproducertransaction.go @@ -9,14 +9,13 @@ import ( "bytes" "errors" "fmt" - state2 "github.com/elastos/Elastos.ELA/dpos/state" - "github.com/elastos/Elastos.ELA/blockchain" "github.com/elastos/Elastos.ELA/common" "github.com/elastos/Elastos.ELA/core/contract" "github.com/elastos/Elastos.ELA/core/types/payload" crstate "github.com/elastos/Elastos.ELA/cr/state" "github.com/elastos/Elastos.ELA/crypto" + state2 "github.com/elastos/Elastos.ELA/dpos/state" elaerr "github.com/elastos/Elastos.ELA/errors" "github.com/elastos/Elastos.ELA/vm" ) @@ -201,12 +200,11 @@ func (t *RegisterProducerTransaction) SpecialContextCheck() (elaerr.ELAError, bo state := t.parameters.BlockChain.GetState() if height < t.parameters.Config.DPoSV2StartHeight && t.payloadVersion == payload.ProducerInfoDposV2Version { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("can not register dposv2 before dposv2 start height")), true - } else if height > state.DPoSV2ActiveHeight && t.payloadVersion == payload.ProducerInfoVersion { + } else if height > state.GetDPoSV2ActiveHeight() && t.payloadVersion == payload.ProducerInfoVersion { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("can not register dposv1 after dposv2 active height")), true } else if height < t.parameters.Config.SupportMultiCodeHeight && t.payloadVersion == payload.ProducerInfoMultiVersion { return elaerr.Simple(elaerr.ErrTxPayload, fmt.Errorf("not support ProducerInfoMultiVersion when height is not reach SupportMultiCodeHeight")), true } - var ownKeyProgramHash *common.Uint168 ownKeyProgramHash, err := state2.GetOwnerKeyDepositProgramHash(info.OwnerKey) diff --git a/core/transaction/returnvotes.go b/core/transaction/returnvotes.go index 6e81e0ca8..941a43e8b 100644 --- a/core/transaction/returnvotes.go +++ b/core/transaction/returnvotes.go @@ -103,15 +103,15 @@ func (t *ReturnVotesTransaction) SpecialContextCheck() (result elaerr.ELAError, stakeProgramHash := ct.ToProgramHash() state := t.parameters.BlockChain.GetState() commitee := t.parameters.BlockChain.GetCRCommittee() - voteRights := state.DposV2VoteRights[*stakeProgramHash] + voteRights, _ := state.GetDposV2VoteRights(*stakeProgramHash) usedDposVoteRights := state.GetUsedDPoSVoteRights(stakeProgramHash) - usedDposV2VoteRights := state.UsedDposV2Votes[*stakeProgramHash] + usedDposV2VoteRights := state.GetUsedDposV2Votes(*stakeProgramHash) cs := commitee.GetState() usedCRVoteRights := cs.GetUsedCRVoteRights(stakeProgramHash) usedCRImpeachmentVoteRights := cs.GetUsedCRImpeachmentVoteRights(stakeProgramHash) usedCRCProposalVoteRights := cs.GetUsedCRCProposalVoteRights(stakeProgramHash) - if t.parameters.BlockHeight > state.DPoSV2ActiveHeight { + if t.parameters.BlockHeight > state.GetDPoSV2ActiveHeight() { if pl.Value > voteRights-usedDposV2VoteRights || pl.Value > voteRights-usedCRVoteRights || pl.Value > voteRights-usedCRImpeachmentVoteRights || diff --git a/core/transaction/reverttodpostransaction.go b/core/transaction/reverttodpostransaction.go index 81bf25a9e..a62fc9e3f 100644 --- a/core/transaction/reverttodpostransaction.go +++ b/core/transaction/reverttodpostransaction.go @@ -115,7 +115,7 @@ func (t *RevertToDPOSTransaction) SpecialContextCheck() (elaerr.ELAError, bool) } // to avoid init DPOSWorkHeight repeatedly - if t.parameters.BlockChain.GetState().DPOSWorkHeight > t.parameters.BlockHeight { + if t.parameters.BlockChain.GetState().GetDPOSWorkHeight() > t.parameters.BlockHeight { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("already receieved revertodpos")), true } diff --git a/core/transaction/reverttopowtransaction.go b/core/transaction/reverttopowtransaction.go index 69eec9289..df1af1de9 100644 --- a/core/transaction/reverttopowtransaction.go +++ b/core/transaction/reverttopowtransaction.go @@ -95,11 +95,11 @@ func (t *RevertToPOWTransaction) SpecialContextCheck() (result elaerr.ELAError, } } case payload.NoProducers: - if !t.parameters.BlockChain.GetState().NoProducers { + if !t.parameters.BlockChain.GetState().GetNoProducers() { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("current producers is enough")), true } case payload.NoClaimDPOSNode: - if !t.parameters.BlockChain.GetState().NoClaimDPOSNode { + if !t.parameters.BlockChain.GetState().GetNoClaimDPOSNode() { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("current CR member claimed DPoS node")), true } } diff --git a/core/transaction/transactionchecker.go b/core/transaction/transactionchecker.go index ed58d9119..f423daaea 100644 --- a/core/transaction/transactionchecker.go +++ b/core/transaction/transactionchecker.go @@ -358,7 +358,7 @@ func (t *DefaultChecker) tryCheckVoteOutputs() error { candidates = []*crstate.Candidate{} } dposv2Run := false - if blockHeight >= dposState.DPoSV2ActiveHeight { + if blockHeight >= dposState.GetDPoSV2ActiveHeight() { dposv2Run = true } err := t.checkVoteOutputs(blockHeight, txn.Outputs(), t.references, diff --git a/core/transaction/updateproducertransaction.go b/core/transaction/updateproducertransaction.go index c6fb9e4cd..509d00921 100644 --- a/core/transaction/updateproducertransaction.go +++ b/core/transaction/updateproducertransaction.go @@ -149,7 +149,7 @@ func (t *UpdateProducerTransaction) SpecialContextCheck() (elaerr.ELAError, bool if producer == nil { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("updating unknown producer")), true } - stake := t.parameters.BlockChain.GetState() + dposState := t.parameters.BlockChain.GetState() //if producer is already dposv2 switch producer.Identity() { case state.DPoSV1: @@ -173,7 +173,7 @@ func (t *UpdateProducerTransaction) SpecialContextCheck() (elaerr.ELAError, bool } case state.DPoSV1V2: - if t.parameters.BlockHeight >= stake.DPoSV2ActiveHeight { + if t.parameters.BlockHeight >= dposState.GetDPoSV2ActiveHeight() { if t.parameters.BlockHeight > producer.Info().StakeUntil { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("producer already expired and dposv2 already started, can not update anything ")), true } @@ -246,7 +246,7 @@ func (t *UpdateProducerTransaction) additionalProducerInfoCheck(info *payload.Pr return errors.New("invalid node public key in payload") } - for _, m := range t.parameters.BlockChain.GetCRCommittee().Members { + for _, m := range t.parameters.BlockChain.GetCRCommittee().GetMembers() { if bytes.Equal(m.DPOSPublicKey, info.NodePublicKey) { return errors.New("node public key can't equal with current CR Node PK") } @@ -255,7 +255,7 @@ func (t *UpdateProducerTransaction) additionalProducerInfoCheck(info *payload.Pr } } - for _, m := range t.parameters.BlockChain.GetCRCommittee().NextMembers { + for _, m := range t.parameters.BlockChain.GetCRCommittee().GetNextMembers() { if bytes.Equal(m.DPOSPublicKey, info.NodePublicKey) { return errors.New("node public key can't equal with next CR Node PK") } diff --git a/core/transaction/voting.go b/core/transaction/voting.go index fdec41103..5bc813e07 100644 --- a/core/transaction/voting.go +++ b/core/transaction/voting.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "github.com/elastos/Elastos.ELA/core/contract/program" + state2 "github.com/elastos/Elastos.ELA/dpos/state" "github.com/elastos/Elastos.ELA/common" "github.com/elastos/Elastos.ELA/core/contract" @@ -32,6 +33,13 @@ func (t *VotingTransaction) HeightVersionCheck() error { return errors.New(fmt.Sprintf("not support %s transaction "+ "before DPoSV2StartHeight", t.TxType().Name())) } + if blockHeight < chainParams.DPoSConfiguration.ChangeVoteTargetStartHeight { + if t.PayloadVersion() == payload.RenewalVoteTargetVersion { + return errors.New(fmt.Sprintf("not support %s transaction "+ + "with payload version RenewalVoteTargetVersion before "+ + "ChangeVoteTargetStartHeight", t.TxType().Name())) + } + } return nil } @@ -127,13 +135,12 @@ func (t *VotingTransaction) SpecialContextCheck() (result elaerr.ELAError, end b } stakeProgramHash := ct.ToProgramHash() state := t.parameters.BlockChain.GetState() - //commitee := t.parameters.BlockChain.GetCRCommittee() - voteRights := state.DposV2VoteRights - totalVotes, exist := voteRights[*stakeProgramHash] + totalVotes, exist := state.GetDposV2VoteRights(*stakeProgramHash) if !exist { return elaerr.Simple(elaerr.ErrTxInvalidOutput, errors.New("has no vote rights")), true } - usedDPoSV2VoteRights, _ := state.UsedDposV2Votes[*stakeProgramHash] + + usedDPoSV2VoteRights := state.GetUsedDposV2Votes(*stakeProgramHash) var candidates []*crstate.Candidate if crCommittee.IsInVotingPeriod(blockHeight) { @@ -161,7 +168,7 @@ func (t *VotingTransaction) SpecialContextCheck() (result elaerr.ELAError, end b switch content.VoteType { case outputpayload.Delegate: - if blockHeight > state.DPoSV2ActiveHeight { + if blockHeight > state.GetDPoSV2ActiveHeight() { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("delegate votes is not allowed in DPoS V2")), true } @@ -204,6 +211,17 @@ func (t *VotingTransaction) SpecialContextCheck() (result elaerr.ELAError, end b return elaerr.Simple(elaerr.ErrTxPayload, errors.New("renewal contents is nil")), true } + + processingReferKeys := make(map[common.Uint256]struct{}, 0) + for _, v := range state.GetRenewalTargetTransactionsInfo() { + for _, tx := range v { + pd := tx.Payload().(*payload.Voting) + for _, content := range pd.RenewalContents { + processingReferKeys[content.ReferKey] = struct{}{} + } + } + } + for _, content := range pld.RenewalContents { producer := state.GetProducer(content.VotesInfo.Candidate) if producer == nil { @@ -231,6 +249,74 @@ func (t *VotingTransaction) SpecialContextCheck() (result elaerr.ELAError, end b if !bytes.Equal(vote.Info[0].Candidate, content.VotesInfo.Candidate) { return elaerr.Simple(elaerr.ErrTxPayload, errors.New("candidate should be the same one")), true } + if _, ok := processingReferKeys[content.ReferKey]; ok { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("refer key is processing")), true + } + } + case payload.RenewalVoteTargetVersion: + if len(pld.RenewalContents) == 0 { + return elaerr.Simple(elaerr.ErrTxPayload, + errors.New("renewal contents is nil")), true + } + + processingReferKeys := make(map[common.Uint256]struct{}, 0) + for _, v := range state.GetRenewalTargetTransactionsInfo() { + for _, tx := range v { + pd := tx.Payload().(*payload.Voting) + for _, content := range pd.RenewalContents { + processingReferKeys[content.ReferKey] = struct{}{} + } + } + } + + for _, content := range pld.RenewalContents { + + v2Producers := t.parameters.BlockChain.GetState().GetDposV2Producers() + var oriVote *payload.DetailedVoteInfo + for _, p := range v2Producers { + v, err := p.GetDetailedDPoSV2Votes(*stakeProgramHash, content.ReferKey) + if err != nil { + continue + } + oriVote = &v + break + } + if oriVote == nil { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("votes can not found")), true + } + + newTarget := state.GetProducer(content.VotesInfo.Candidate) + if newTarget == nil { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("producer can not found")), true + } + if newTarget.State() != state2.Active { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("new target producer should be active")), true + } + if oriVote.VoteType != outputpayload.DposV2 { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("invalid vote type")), true + } + if len(oriVote.Info) != 1 || oriVote.Info[0].Votes != content.VotesInfo.Votes { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("votes not equal")), true + } + if content.VotesInfo.LockTime < oriVote.Info[0].LockTime { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("new lock time < old lock time")), true + } + if content.VotesInfo.LockTime > newTarget.Info().StakeUntil { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("new lock time > producer StakeUntil")), true + } + if content.VotesInfo.LockTime-blockHeight > t.parameters.Config.DPoSConfiguration.DPoSV2MaxVotesLockTime { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("invalid lock time > DPoSV2MaxVotesLockTime")), true + } + if content.VotesInfo.LockTime-blockHeight <= t.parameters.Config.DPoSConfiguration.MinRenewalVotingTargetLockTime+ + t.parameters.Config.DPoSConfiguration.RenewalVotingTargetDuration { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("invalid lock time")), true + } + if bytes.Equal(oriVote.Info[0].Candidate, content.VotesInfo.Candidate) { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("candidate should not be the same one")), true + } + if _, ok := processingReferKeys[content.ReferKey]; ok { + return elaerr.Simple(elaerr.ErrTxPayload, errors.New("refer key is processing")), true + } } default: return elaerr.Simple(elaerr.ErrTxPayload, errors.New("invalid payload version")), true diff --git a/core/types/payload/voting.go b/core/types/payload/voting.go index a20d414b0..9ee4705d5 100644 --- a/core/types/payload/voting.go +++ b/core/types/payload/voting.go @@ -19,6 +19,7 @@ import ( const VoteVersion byte = 0x00 const RenewalVoteVersion byte = 0x01 +const RenewalVoteTargetVersion byte = 0x02 type Voting struct { Contents []VotesContent @@ -46,7 +47,7 @@ func (p *Voting) Serialize(w io.Writer, version byte) error { return err } } - case RenewalVoteVersion: + case RenewalVoteVersion, RenewalVoteTargetVersion: if err := common.WriteVarUint(w, uint64(len(p.RenewalContents))); err != nil { return err } @@ -75,7 +76,7 @@ func (p *Voting) Deserialize(r io.Reader, version byte) error { } p.Contents = append(p.Contents, content) } - case RenewalVoteVersion: + case RenewalVoteVersion, RenewalVoteTargetVersion: p.RenewalContents = make([]RenewalVotesContent, 0) for i := uint64(0); i < contentsCount; i++ { var renewalContents RenewalVotesContent @@ -193,7 +194,6 @@ func (v *VotesWithLockTime) String() string { "}\n\t\t\t\t") } -// // VotesContent defines the vote type and vote information of candidates. type VotesContent struct { VoteType outputpayload.VoteType diff --git a/cr/state/committee.go b/cr/state/committee.go index bfa7b7d43..295c94309 100644 --- a/cr/state/committee.go +++ b/cr/state/committee.go @@ -76,6 +76,47 @@ func (c *Committee) GetProposalManager() *ProposalManager { return c.manager } +func (c *Committee) PubKeyExistClaimedDPoSKeys(publicKey []byte) bool { + c.mtx.RLock() + defer c.mtx.RUnlock() + if _, ok := c.ClaimedDPoSKeys[hex.EncodeToString(publicKey)]; ok { + return true + } + return false +} + +func (c *Committee) PubKeyExistNextClaimedDPoSKey(publicKey []byte) bool { + c.mtx.RLock() + defer c.mtx.RUnlock() + if _, ok := c.NextClaimedDPoSKeys[hex.EncodeToString(publicKey)]; ok { + return true + } + return false +} +func (c *Committee) GetAppropriationAmount() common.Fixed64 { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.AppropriationAmount +} + +func (c *Committee) GetCRCCurrentStageAmount() common.Fixed64 { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.CRCCurrentStageAmount +} + +func (c *Committee) GetCommitteeUsedAmount() common.Fixed64 { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.CommitteeUsedAmount +} + +func (c *Committee) GetCRCCommitteeUsedAmount() common.Fixed64 { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.CRCCommitteeUsedAmount +} + func (c *Committee) ExistCR(programCode []byte) bool { c.mtx.RLock() defer c.mtx.RUnlock() @@ -223,6 +264,33 @@ func (c *Committee) GetCurrentMembers() []*CRMember { return result } +// get all next CRMembers ordered by CID +func (c *Committee) GetMembers() []*CRMember { + c.mtx.RLock() + defer c.mtx.RUnlock() + + result := getCRMembers(c.Members) + return result +} + +func (c *Committee) GetNeedAppropriation() bool { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.NeedAppropriation +} + +func (c *Committee) GetNeedRecordProposalResult() bool { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.NeedRecordProposalResult +} + +func (c *Committee) GetLastCommitteeHeight() uint32 { + c.mtx.RLock() + defer c.mtx.RUnlock() + return c.LastCommitteeHeight +} + // get all next CRMembers ordered by CID func (c *Committee) GetNextMembers() []*CRMember { c.mtx.RLock() @@ -862,6 +930,27 @@ func (c *Committee) GetCommitteeCanUseAmount() common.Fixed64 { return c.CRCCurrentStageAmount - c.CRCCommitteeUsedAmount } +func (c *Committee) GetUsedCRVotes(stakeProgramHash common.Uint168) []payload.VotesWithLockTime { + c.mtx.RLock() + defer c.mtx.RUnlock() + usedCRVotes := c.state.UsedCRVotes[stakeProgramHash] + return usedCRVotes +} + +func (c *Committee) GetUsedCRImpeachmentVotes(stakeProgramHash common.Uint168) []payload.VotesWithLockTime { + c.mtx.RLock() + defer c.mtx.RUnlock() + usedCRImpeachmentVotes := c.state.UsedCRImpeachmentVotes[stakeProgramHash] + return usedCRImpeachmentVotes +} + +func (c *Committee) GetUsedCRCProposalVotes(stakeProgramHash common.Uint168) []payload.VotesWithLockTime { + c.mtx.RLock() + defer c.mtx.RUnlock() + usedCRCProposalVotes := c.state.UsedCRCProposalVotes[stakeProgramHash] + return usedCRCProposalVotes +} + func (c *Committee) recordCurrentStageAmount(height uint32, lockedAmount common.Fixed64) { oriCurrentStageAmount := c.CRCCurrentStageAmount oriAppropriationAmount := c.AppropriationAmount diff --git a/dpos/state/arbitrators.go b/dpos/state/arbitrators.go index 3bd9d09d4..8d191580e 100644 --- a/dpos/state/arbitrators.go +++ b/dpos/state/arbitrators.go @@ -118,9 +118,7 @@ func (a *Arbiters) Start() { } func (a *Arbiters) SetNeedRevertToDPOSTX(need bool) { - a.mtx.Lock() - defer a.mtx.Unlock() - a.NeedRevertToDPOSTX = need + a.State.SetNeedRevertToDPOSTX(need) } func GetOwnerKeyStandardProgramHash(ownerKey []byte) (ownKeyProgramHash *common.Uint168, err error) { @@ -133,25 +131,15 @@ func GetOwnerKeyStandardProgramHash(ownerKey []byte) (ownKeyProgramHash *common. } func (a *Arbiters) SetNeedNextTurnDPOSInfo(need bool) { - a.mtx.Lock() - defer a.mtx.Unlock() - a.NeedNextTurnDPOSInfo = need + a.State.SetNeedNextTurnDPOSInfo(need) } func (a *Arbiters) IsInPOWMode() bool { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.isInPOWMode() -} - -func (a *Arbiters) isInPOWMode() bool { - return a.ConsensusAlgorithm == POW + return a.State.IsInPOWMode() } func (a *Arbiters) GetRevertToPOWBlockHeight() uint32 { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.RevertToPOWBlockHeight + return a.State.GetRevertToPOWBlockHeight() } func (a *Arbiters) RegisterFunction(bestHeight func() uint32, @@ -166,9 +154,7 @@ func (a *Arbiters) RegisterFunction(bestHeight func() uint32, } func (a *Arbiters) IsNeedNextTurnDPOSInfo() bool { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.NeedNextTurnDPOSInfo + return a.State.IsNeedNextTurnDPOSInfo() } func (a *Arbiters) RecoverFromCheckPoints(point *CheckPoint) { @@ -179,6 +165,7 @@ func (a *Arbiters) RecoverFromCheckPoints(point *CheckPoint) { func (a *Arbiters) recoverFromCheckPoints(point *CheckPoint) { // reset history + a.State.mtx.Lock() a.History = utils.NewHistory(maxHistoryCapacity) a.State.History = utils.NewHistory(maxHistoryCapacity) @@ -201,6 +188,8 @@ func (a *Arbiters) recoverFromCheckPoints(point *CheckPoint) { a.nextCRCArbitersMap = point.NextCRCArbitersMap a.nextCRCArbiters = point.NextCRCArbiters a.forceChanged = point.ForceChanged + a.State.mtx.Unlock() + } func (a *Arbiters) ProcessBlock(block *types.Block, confirm *payload.Confirm) { @@ -238,9 +227,7 @@ func (a *Arbiters) CheckDPOSIllegalTx(block *types.Block) error { } func (a *Arbiters) CheckRevertToDPOSTX(block *types.Block) error { - a.mtx.Lock() - needRevertToDPOSTX := a.NeedRevertToDPOSTX - a.mtx.Unlock() + needRevertToDPOSTX := a.State.GetNeedRevertToDPOSTX() var revertToDPOSTxCount uint32 for _, tx := range block.Transactions { @@ -264,9 +251,7 @@ func (a *Arbiters) CheckRevertToDPOSTX(block *types.Block) error { } func (a *Arbiters) CheckNextTurnDPOSInfoTx(block *types.Block) error { - a.mtx.Lock() - needNextTurnDposInfo := a.NeedNextTurnDPOSInfo - a.mtx.Unlock() + needNextTurnDposInfo := a.State.GetNeedNextTurnDPOSInfo() var nextTurnDPOSInfoTxCount uint32 for _, tx := range block.Transactions { @@ -289,10 +274,7 @@ func (a *Arbiters) CheckNextTurnDPOSInfoTx(block *types.Block) error { } func (a *Arbiters) CheckCRCAppropriationTx(block *types.Block) error { - a.mtx.Lock() - needAppropriation := a.CRCommittee.NeedAppropriation - a.mtx.Unlock() - + needAppropriation := a.CRCommittee.GetNeedAppropriation() var appropriationCount uint32 for _, tx := range block.Transactions { if tx.IsCRCAppropriationTx() { @@ -315,9 +297,7 @@ func (a *Arbiters) CheckCRCAppropriationTx(block *types.Block) error { } func (a *Arbiters) CheckCustomIDResultsTx(block *types.Block) error { - a.mtx.Lock() - needCustomProposalResult := a.CRCommittee.NeedRecordProposalResult - a.mtx.Unlock() + needCustomProposalResult := a.CRCommittee.GetNeedRecordProposalResult() var cidProposalResultCount uint32 for _, tx := range block.Transactions { @@ -433,11 +413,7 @@ func (a *Arbiters) GetFinalRoundChange() common.Fixed64 { } func (a *Arbiters) GetLastBlockTimestamp() uint32 { - a.mtx.Lock() - result := a.LastBlockTimestamp - a.mtx.Unlock() - - return result + return a.State.GetLastBlockTimestamp() } func (a *Arbiters) ForceChange(height uint32) error { @@ -526,7 +502,8 @@ func (a *Arbiters) notifyNextTurnDPOSInfoTx(blockHeight, versionHeight uint32, f return } - if blockHeight+uint32(a.ChainParams.DPoSConfiguration.NormalArbitratorsCount+len(a.ChainParams.DPoSConfiguration.CRCArbiters)) >= a.DPoSV2ActiveHeight { + if blockHeight+uint32(a.ChainParams.DPoSConfiguration.NormalArbitratorsCount+ + len(a.ChainParams.DPoSConfiguration.CRCArbiters)) >= a.State.getDPoSV2ActiveHeight() { nextTurnDPOSInfoTx := a.createNextTurnDPOSInfoTransactionV1(blockHeight, forceChange) go events.Notify(events.ETAppendTxToTxPool, nextTurnDPOSInfoTx) @@ -570,8 +547,8 @@ func (a *Arbiters) IncreaseChainHeight(block *types.Block, confirm *payload.Conf "error: %s, revert to POW mode", block.Height, err)) } case normalChange: - if a.isDPoSV2Run(block.Height) { - if block.Height == a.DPoSV2ActiveHeight { + if a.State.isDPoSV2Run(block.Height) { + if block.Height == a.State.getDPoSV2ActiveHeight() { if err := a.clearingDPOSReward(block, block.Height, true); err != nil { panic(fmt.Sprintf("normal change fail when clear DPOS reward: "+ " transaction, height: %d, error: %s", block.Height, err)) @@ -606,7 +583,7 @@ func (a *Arbiters) IncreaseChainHeight(block *types.Block, confirm *payload.Conf a.History.Commit(block.Height) bestHeight := a.bestHeight() if a.ConsensusAlgorithm != POW && block.Height >= bestHeight { - if len(a.CurrentArbitrators) == 0 && (a.NoClaimDPOSNode || a.NoProducers) { + if len(a.CurrentArbitrators) == 0 && (a.State.NoClaimDPOSNode || a.State.NoProducers) { a.createRevertToPOWTransaction(block.Height) } } @@ -619,7 +596,7 @@ func (a *Arbiters) IncreaseChainHeight(block *types.Block, confirm *payload.Conf if block.Height > bestHeight-MaxSnapshotLength { a.SnapshotByHeight(block.Height) } - if block.Height >= bestHeight && (a.NeedNextTurnDPOSInfo || forceChanged) { + if block.Height >= bestHeight && (a.State.NeedNextTurnDPOSInfo || forceChanged) { a.notifyNextTurnDPOSInfoTx(block.Height, block.Height+1, forceChanged) } a.mtx.Unlock() @@ -639,10 +616,10 @@ func (a *Arbiters) IncreaseChainHeight(block *types.Block, confirm *payload.Conf func (a *Arbiters) createRevertToPOWTransaction(blockHeight uint32) { var revertType payload.RevertType - if a.NoProducers { + if a.State.NoProducers { revertType = payload.NoProducers } else { - if blockHeight > a.DPoSV2ActiveHeight { + if blockHeight > a.State.DPoSV2ActiveHeight { log.Info("no need to create no claim DPoS node transaction") return } @@ -679,14 +656,20 @@ func (a *Arbiters) revertToPOWAtNextTurn(height uint32) { a.nextCRCArbitersMap = make(map[common.Uint168]ArbiterMember) a.nextCRCArbiters = make([]ArbiterMember, 0) if a.ConsensusAlgorithm == DPOS { + a.State.mtx.Lock() a.NoProducers = true + a.State.mtx.Unlock() + } }, func() { a.nextArbitrators = oriNextArbitrators a.nextCandidates = oriNextCandidates a.nextCRCArbitersMap = oriNextCRCArbitersMap a.nextCRCArbiters = oriNextCRCArbiters + a.State.mtx.Lock() a.NoProducers = oriNoProducers + a.State.mtx.Unlock() + }) } func (a *Arbiters) AccumulateReward(block *types.Block, confirm *payload.Confirm) { @@ -697,20 +680,11 @@ func (a *Arbiters) AccumulateReward(block *types.Block, confirm *payload.Confirm // is already DPoS V2. when we are here we need new reward. func (a *Arbiters) IsDPoSV2Run(blockHeight uint32) bool { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.isDPoSV2Run(blockHeight) + return a.State.IsDPoSV2Run(blockHeight) } func (a *Arbiters) GetDPoSV2ActiveHeight() uint32 { - a.mtx.Lock() - defer a.mtx.Unlock() - return a.DPoSV2ActiveHeight -} - -// is already DPoS V2. when we are here we need new reward. -func (a *Arbiters) isDPoSV2Run(blockHeight uint32) bool { - return blockHeight >= a.DPoSV2ActiveHeight + return a.State.GetDPoSV2ActiveHeight() } func (a *Arbiters) getDPoSV2RewardsV2(dposReward common.Fixed64, sponsor []byte, height uint32) (rewards map[string]common.Fixed64) { @@ -733,8 +707,8 @@ func (a *Arbiters) getDPoSV2RewardsV2(dposReward common.Fixed64, sponsor []byte, if !isCR { // DPoS votes reward is: reward * 3 /4 votesReward := dposReward * 3 / 4 - - producer := a.getProducer(sponsor) + //in ProcessBlock so no use lock + producer := a.State.getProducer(sponsor) if producer == nil { log.Error("v2 accumulateReward Sponsor not exist ", hex.EncodeToString(sponsor)) return @@ -804,7 +778,7 @@ func (a *Arbiters) accumulateReward(block *types.Block, confirm *payload.Confirm accumulative += dposReward } - if a.isDPoSV2Run(block.Height) { + if a.State.isDPoSV2Run(block.Height) { log.Debugf("accumulateReward dposReward %v", dposReward) oriDutyIndex := a.DutyIndex oriForceChanged := a.forceChanged @@ -815,15 +789,19 @@ func (a *Arbiters) accumulateReward(block *types.Block, confirm *payload.Confirm } a.History.Append(block.Height, func() { + a.State.mtx.Lock() for k, v := range rewards { a.DPoSV2RewardInfo[k] += v } + a.State.mtx.Unlock() a.forceChanged = false a.DutyIndex = oriDutyIndex + 1 }, func() { + a.State.mtx.Lock() for k, v := range rewards { a.DPoSV2RewardInfo[k] -= v } + a.State.mtx.Unlock() a.forceChanged = oriForceChanged a.DutyIndex = oriDutyIndex }) @@ -936,7 +914,7 @@ func (a *Arbiters) distributeWithNormalArbitratorsV3(height uint32, reward commo math.Floor(totalBlockConfirmReward / float64(arbitersCount))) totalVotesInRound := a.CurrentReward.TotalVotesInRound log.Debugf("distributeWithNormalArbitratorsV3 TotalVotesInRound %f", a.CurrentReward.TotalVotesInRound) - if a.ConsensusAlgorithm == POW || len(a.CurrentArbitrators) == 0 || + if a.State.ConsensusAlgorithm == POW || len(a.CurrentArbitrators) == 0 || len(a.ChainParams.DPoSConfiguration.CRCArbiters) == len(a.CurrentArbitrators) { // if no normal DPOS node, need to destroy reward. roundReward[*a.ChainParams.DestroyELAProgramHash] = reward @@ -957,7 +935,7 @@ func (a *Arbiters) distributeWithNormalArbitratorsV3(height uint32, reward commo rewardHash = *a.ChainParams.DestroyELAProgramHash } else if len(m.crMember.DPOSPublicKey) == 0 { nodePK := arbiter.GetNodePublicKey() - ownerPK := a.getProducerKey(nodePK) + ownerPK := a.State.getProducerKey(nodePK) opk, err := common.HexStringToBytes(ownerPK) if err != nil { panic("get owner public key err:" + err.Error()) @@ -1502,6 +1480,8 @@ func (a *Arbiters) isNextCRCArbitrator(pk []byte) bool { } func (a *Arbiters) IsNextCRCArbitrator(pk []byte) bool { + a.mtx.Lock() + defer a.mtx.Unlock() for _, v := range a.nextCRCArbiters { if bytes.Equal(v.GetNodePublicKey(), pk) { return true @@ -1511,6 +1491,8 @@ func (a *Arbiters) IsNextCRCArbitrator(pk []byte) bool { } func (a *Arbiters) IsMemberElectedNextCRCArbitrator(pk []byte) bool { + a.mtx.Lock() + defer a.mtx.Unlock() for _, v := range a.nextCRCArbiters { if bytes.Equal(v.GetNodePublicKey(), pk) && v.(*crcArbiter).crMember.MemberState == state.MemberElected { return true @@ -1979,7 +1961,7 @@ func (a *Arbiters) createNextTurnDPOSInfoTransactionV1(blockHeight uint32, force func (a *Arbiters) updateNextTurnInfo(height uint32, producers []ArbiterMember, unclaimed int) { nextCRCArbiters := a.nextArbitrators - if !a.isDposV2Active() { + if !a.State.isDposV2Active() { a.nextArbitrators = append(a.nextArbitrators, producers...) } else { a.nextArbitrators = producers @@ -1997,7 +1979,7 @@ func (a *Arbiters) updateNextTurnInfo(height uint32, producers []ArbiterMember, } func (a *Arbiters) getProducers(count int, height uint32) ([]ArbiterMember, error) { - if !a.isDPoSV2Run(height) { + if !a.State.isDPoSV2Run(height) { return a.GetNormalArbitratorsDesc(height, count, a.getSortedProducers(), 0) } else { @@ -2042,10 +2024,10 @@ func (a *Arbiters) getSortedProducersWithRandom(height uint32, unclaimedCount in // if the last random producer is not found or the poll ranked in the top // 23(may be 35) or the state is not active, need to get a candidate as // DPOS node at random. - if a.LastRandomCandidateHeight != 0 && - height-a.LastRandomCandidateHeight < a.ChainParams.DPoSConfiguration.RandomCandidatePeriod { + if a.State.LastRandomCandidateHeight != 0 && + height-a.State.LastRandomCandidateHeight < a.ChainParams.DPoSConfiguration.RandomCandidatePeriod { for i, p := range votedProducers { - if common.BytesToHexString(p.info.OwnerKey) == a.LastRandomCandidateOwner { + if common.BytesToHexString(p.info.OwnerKey) == a.State.LastRandomCandidateOwner { if i < unclaimedCount+a.ChainParams.DPoSConfiguration.NormalArbitratorsCount-1 || p.state != Active { // need get again at random. break @@ -2075,8 +2057,10 @@ func (a *Arbiters) getSortedProducersWithRandom(height uint32, unclaimedCount in candidateProducer := votedProducers[selectedCandidateIndex] // todo need to use History? + a.State.mtx.Lock() a.LastRandomCandidateHeight = height a.LastRandomCandidateOwner = common.BytesToHexString(candidateProducer.info.OwnerKey) + a.State.mtx.Unlock() newProducers := make([]*Producer, 0, len(votedProducers)) newProducers = append(newProducers, votedProducers[:unclaimedCount+normalCount]...) @@ -2158,21 +2142,20 @@ func (a *Arbiters) getCandidateIndexAtRandom(height uint32, unclaimedCount, vote return rand.Intn(candidatesCount), nil } -func (a *Arbiters) isDposV2Active() bool { - if a.DPoSV2ActiveHeight != math.MaxUint32 { - return true - } - return len(a.DposV2EffectedProducers) >= a.ChainParams.DPoSConfiguration.NormalArbitratorsCount*3/2 -} - func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { if height >= a.ChainParams.CRConfiguration.CRClaimDPOSNodeStartHeight { - oriNeedNextTurnDPOSInfo := a.NeedNextTurnDPOSInfo + oriNeedNextTurnDPOSInfo := a.State.NeedNextTurnDPOSInfo a.History.Append(height, func() { + a.State.mtx.Lock() a.NeedNextTurnDPOSInfo = true + a.State.mtx.Unlock() + }, func() { + a.State.mtx.Lock() a.NeedNextTurnDPOSInfo = oriNeedNextTurnDPOSInfo + a.State.mtx.Unlock() + }) } @@ -2183,12 +2166,18 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { a.TryLeaveUnderStaffed(a.IsAbleToRecoverFromUnderstaffedState) } - if a.DPoSV2ActiveHeight == math.MaxUint32 && a.isDposV2Active() { + if a.State.getDPoSV2ActiveHeight() == math.MaxUint32 && a.State.isDposV2Active() { oriHeight := height a.History.Append(height, func() { + a.State.mtx.Lock() a.DPoSV2ActiveHeight = height + a.ChainParams.CRConfiguration.MemberCount + uint32(a.ChainParams.DPoSConfiguration.NormalArbitratorsCount) + a.State.mtx.Unlock() + }, func() { + a.State.mtx.Lock() a.DPoSV2ActiveHeight = oriHeight + a.State.mtx.Unlock() + }) } @@ -2202,7 +2191,7 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { count := a.ChainParams.DPoSConfiguration.NormalArbitratorsCount var votedProducers []*Producer var crAndVotedProducersStr []string - if a.isDposV2Active() { + if a.State.isDposV2Active() { crAndVotedProducersStr, err = a.getRandomDposV2Producers(height, unclaimed, choosingCRArbiters) if err != nil { return err @@ -2215,7 +2204,7 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { } var producers []ArbiterMember var err error - if a.isDposV2Active() { + if a.State.isDposV2Active() { producers, err = a.GetDposV2NormalArbitratorsDesc(count+int(a.ChainParams.CRConfiguration.MemberCount), crAndVotedProducersStr, choosingCRArbiters) } else { producers, err = a.GetNormalArbitratorsDesc(versionHeight, count, @@ -2240,15 +2229,15 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { a.nextCRCArbiters = oriNextCRCArbiters }) } else { - if !a.isDposV2Active() { + if !a.State.isDposV2Active() { if height >= a.ChainParams.DPoSConfiguration.NoCRCDPOSNodeHeight { count := len(a.ChainParams.DPoSConfiguration.CRCArbiters) + a.ChainParams.DPoSConfiguration.NormalArbitratorsCount var newSelected bool for _, p := range votedProducers { producer := p ownerPK := common.BytesToHexString(producer.info.OwnerKey) - if ownerPK == a.LastRandomCandidateOwner && - height-a.LastRandomCandidateHeight == uint32(count) { + if ownerPK == a.State.LastRandomCandidateOwner && + height-a.State.LastRandomCandidateHeight == uint32(count) { newSelected = true } } @@ -2264,7 +2253,7 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { } ownerPK := common.BytesToHexString(producer.info.OwnerKey) oriRandomInactiveCount := producer.randomCandidateInactiveCount - if ownerPK == a.LastRandomCandidateOwner { + if ownerPK == a.State.LastRandomCandidateOwner { a.History.Append(height, func() { producer.selected = true producer.randomCandidateInactiveCount = 0 @@ -2289,7 +2278,7 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { }) var candidates []ArbiterMember - if !a.isDposV2Active() { + if !a.State.isDposV2Active() { candidates, err = a.GetCandidatesDesc(versionHeight, count+unclaimed, votedProducers) } else { @@ -2347,7 +2336,7 @@ func (a *Arbiters) resetNextArbiterByCRC(versionHeight uint32, height uint32) (i needReset = true } else if versionHeight >= a.ChainParams.CRConfiguration.ChangeCommitteeNewCRHeight { var votedProducers []*Producer - if a.isDposV2Active() { + if a.State.isDposV2Active() { votedProducers = a.State.GetDposV2ActiveProducers() } else { votedProducers = a.State.GetVotedProducers() @@ -2357,7 +2346,7 @@ func (a *Arbiters) resetNextArbiterByCRC(versionHeight uint32, height uint32) (i return unclaimed, nil, errors.New("votedProducers less than CRCArbiters") } - if a.isDposV2Active() { + if a.State.isDposV2Active() { sort.Slice(votedProducers, func(i, j int) bool { if votedProducers[i].GetTotalDPoSV2VoteRights() == votedProducers[j].GetTotalDPoSV2VoteRights() { return bytes.Compare(votedProducers[i].info.NodePublicKey, @@ -2638,7 +2627,7 @@ func (a *Arbiters) GetDposV2CandidatesDesc(startIndex int, if exist { result = append(result, crc) } else { - ar, err := NewDPoSArbiter(a.getProducer(ownkey)) + ar, err := NewDPoSArbiter(a.State.getProducer(ownkey)) if err != nil { return nil, err } @@ -2675,7 +2664,8 @@ func (a *Arbiters) snapshotVotesStates(height uint32) error { log.Debugf("snapshotVotesStates height %d begin", height) var nextReward RewardData recordVotes := func(nodePublicKey []byte) error { - producer := a.GetProducer(nodePublicKey) + //in process block so do not lock + producer := a.State.getProducer(nodePublicKey) if producer == nil { return errors.New("get producer by node public key failed") } @@ -2714,7 +2704,7 @@ func (a *Arbiters) snapshotVotesStates(height uint32) error { if a.isNextCRCArbitrator(ar.GetNodePublicKey()) { continue } - producer := a.GetProducer(ar.GetNodePublicKey()) + producer := a.State.getProducer(ar.GetNodePublicKey()) if producer == nil { return errors.New("get producer by node public key failed") } diff --git a/dpos/state/keyframe.go b/dpos/state/keyframe.go index d7e4cc05f..82b05981c 100644 --- a/dpos/state/keyframe.go +++ b/dpos/state/keyframe.go @@ -7,6 +7,8 @@ package state import ( "fmt" + "github.com/elastos/Elastos.ELA/core/types/functions" + "github.com/elastos/Elastos.ELA/core/types/interfaces" "io" "math" @@ -47,7 +49,7 @@ type StateKeyFrame struct { Votes map[string]struct{} // NFT - // key: ID value: (genesis block hash, createNFT tx hash) + // key: NFTID value: (genesis block hash, createNFT tx hash) NFTIDInfoHashMap map[common.Uint256]payload.NFTInfo // dpos 2.0 @@ -71,6 +73,9 @@ type StateKeyFrame struct { // votes withdraw VotesWithdrawableTxInfo map[common.Uint256]common2.OutputInfo + // key: height value: votings tx + RenewalTargetTransactionsInfo map[uint32][]interfaces.Transaction + EmergencyInactiveArbiters map[string]struct{} LastRandomCandidateOwner string VersionStartHeight uint32 @@ -122,17 +127,18 @@ func (s *StateKeyFrame) snapshot() *StateKeyFrame { UsedDposVotes: make(map[common.Uint168][]payload.VotesWithLockTime), UsedDposV2Votes: make(map[common.Uint168]common.Fixed64), - DepositOutputs: make(map[string]common.Fixed64), - DPoSV2RewardInfo: make(map[string]common.Fixed64), - DposV2RewardClaimingInfo: make(map[string]common.Fixed64), - DposV2RewardClaimedInfo: make(map[string]common.Fixed64), - WithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), - ClaimingRewardAddr: make(map[common.Uint256]common.Uint168), - VotesWithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), - Nicknames: make(map[string]struct{}), - SpecialTxHashes: make(map[common.Uint256]struct{}), - PreBlockArbiters: make(map[string]struct{}), - ProducerDepositMap: make(map[common.Uint168]struct{}), + DepositOutputs: make(map[string]common.Fixed64), + DPoSV2RewardInfo: make(map[string]common.Fixed64), + DposV2RewardClaimingInfo: make(map[string]common.Fixed64), + DposV2RewardClaimedInfo: make(map[string]common.Fixed64), + WithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), + ClaimingRewardAddr: make(map[common.Uint256]common.Uint168), + VotesWithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), + RenewalTargetTransactionsInfo: make(map[uint32][]interfaces.Transaction), + Nicknames: make(map[string]struct{}), + SpecialTxHashes: make(map[common.Uint256]struct{}), + PreBlockArbiters: make(map[string]struct{}), + ProducerDepositMap: make(map[common.Uint168]struct{}), } state.NodeOwnerKeys = copyStringMap(s.NodeOwnerKeys) state.CurrentCRNodeOwnerKeys = copyStringMap(s.CurrentCRNodeOwnerKeys) @@ -160,6 +166,7 @@ func (s *StateKeyFrame) snapshot() *StateKeyFrame { state.ClaimingRewardAddr = copyRewardClaimingAddrMap(s.ClaimingRewardAddr) state.VotesWithdrawableTxInfo = copyWithdrawableTransactionsMap(s.VotesWithdrawableTxInfo) + state.RenewalTargetTransactionsInfo = copyRenewalTargetTransactionsMap(s.RenewalTargetTransactionsInfo) state.Nicknames = copyStringSet(s.Nicknames) state.SpecialTxHashes = copyHashSet(s.SpecialTxHashes) @@ -255,6 +262,9 @@ func (s *StateKeyFrame) Serialize(w io.Writer) (err error) { if err = s.serializeWithdrawableTransactionsMap(s.VotesWithdrawableTxInfo, w); err != nil { return } + if err = s.SerializeRenewalTargetMap(s.RenewalTargetTransactionsInfo, w); err != nil { + return + } if err = s.SerializeStringSet(s.Nicknames, w); err != nil { return } @@ -376,6 +386,11 @@ func (s *StateKeyFrame) Deserialize(r io.Reader) (err error) { if s.VotesWithdrawableTxInfo, err = s.deserializeWithdrawableTransactionsMap(r); err != nil { return } + + if s.RenewalTargetTransactionsInfo, err = s.deserializeRenewalTargetTransactionsMap(r); err != nil { + return + } + if s.Nicknames, err = s.DeserializeStringSet(r); err != nil { return } @@ -494,6 +509,27 @@ func (p *StateKeyFrame) serializeWithdrawableTransactionsMap( return } +func (s *StateKeyFrame) SerializeRenewalTargetMap(vmap map[uint32][]interfaces.Transaction, + w io.Writer) (err error) { + if err = common.WriteVarUint(w, uint64(len(vmap))); err != nil { + return + } + for k, v := range vmap { + if err = common.WriteUint32(w, k); err != nil { + return + } + if err = common.WriteVarUint(w, uint64(len(v))); err != nil { + return + } + for _, tx := range v { + if err = tx.Serialize(w); err != nil { + return err + } + } + } + return +} + func (p *StateKeyFrame) deserializeWithdrawableTransactionsMap(r io.Reader) ( withdrawableTxsMap map[common.Uint256]common2.OutputInfo, err error) { var count uint64 @@ -510,6 +546,42 @@ func (p *StateKeyFrame) deserializeWithdrawableTransactionsMap(r io.Reader) ( if err = withdrawInfo.Deserialize(r); err != nil { return } + withdrawableTxsMap[hash] = withdrawInfo + } + return +} + +func (p *StateKeyFrame) deserializeRenewalTargetTransactionsMap(r io.Reader) ( + renewalTargetTxsMap map[uint32][]interfaces.Transaction, err error) { + var count uint64 + if count, err = common.ReadVarUint(r, 0); err != nil { + return + } + renewalTargetTxsMap = make(map[uint32][]interfaces.Transaction) + for i := uint64(0); i < count; i++ { + var height uint32 + if height, err = common.ReadUint32(r); err != nil { + return + } + var votesCount uint64 + if votesCount, err = common.ReadVarUint(r, 0); err != nil { + return + } + txs := make([]interfaces.Transaction, 0) + for i := uint64(0); i < votesCount; i++ { + var txn interfaces.Transaction + txn, err = functions.GetTransactionByBytes(r) + if err != nil { + log.Errorf("invalid renewal transaction") + return + } + if err = txn.Deserialize(r); err != nil { + return + } + txs = append(txs, txn) + } + + renewalTargetTxsMap[height] = txs } return } @@ -902,36 +974,37 @@ func (kf *StateKeyFrame) GetUsedDPoSVoteRights(stakeProgramHash *common.Uint168) func NewStateKeyFrame() *StateKeyFrame { info := make(map[string]common.Fixed64) return &StateKeyFrame{ - NodeOwnerKeys: make(map[string]string), - CurrentCRNodeOwnerKeys: make(map[string]string), - NextCRNodeOwnerKeys: make(map[string]string), - PendingProducers: make(map[string]*Producer), - ActivityProducers: make(map[string]*Producer), - InactiveProducers: make(map[string]*Producer), - CanceledProducers: make(map[string]*Producer), - IllegalProducers: make(map[string]*Producer), - PendingCanceledProducers: make(map[string]*Producer), - DposV2EffectedProducers: make(map[string]*Producer), - Votes: make(map[string]struct{}), - DposV2VoteRights: make(map[common.Uint168]common.Fixed64), - NFTIDInfoHashMap: make(map[common.Uint256]payload.NFTInfo), - UsedDposVotes: make(map[common.Uint168][]payload.VotesWithLockTime), - UsedDposV2Votes: make(map[common.Uint168]common.Fixed64), - DepositOutputs: make(map[string]common.Fixed64), - DPoSV2RewardInfo: info, - DposV2RewardClaimingInfo: make(map[string]common.Fixed64), - DposV2RewardClaimedInfo: make(map[string]common.Fixed64), - WithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), - ClaimingRewardAddr: make(map[common.Uint256]common.Uint168), - VotesWithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), - Nicknames: make(map[string]struct{}), - SpecialTxHashes: make(map[common.Uint256]struct{}), - PreBlockArbiters: make(map[string]struct{}), - EmergencyInactiveArbiters: make(map[string]struct{}), - ProducerDepositMap: make(map[common.Uint168]struct{}), - VersionStartHeight: 0, - VersionEndHeight: 0, - DPoSV2ActiveHeight: math.MaxUint32, + NodeOwnerKeys: make(map[string]string), + CurrentCRNodeOwnerKeys: make(map[string]string), + NextCRNodeOwnerKeys: make(map[string]string), + PendingProducers: make(map[string]*Producer), + ActivityProducers: make(map[string]*Producer), + InactiveProducers: make(map[string]*Producer), + CanceledProducers: make(map[string]*Producer), + IllegalProducers: make(map[string]*Producer), + PendingCanceledProducers: make(map[string]*Producer), + DposV2EffectedProducers: make(map[string]*Producer), + Votes: make(map[string]struct{}), + DposV2VoteRights: make(map[common.Uint168]common.Fixed64), + NFTIDInfoHashMap: make(map[common.Uint256]payload.NFTInfo), + UsedDposVotes: make(map[common.Uint168][]payload.VotesWithLockTime), + UsedDposV2Votes: make(map[common.Uint168]common.Fixed64), + DepositOutputs: make(map[string]common.Fixed64), + DPoSV2RewardInfo: info, + DposV2RewardClaimingInfo: make(map[string]common.Fixed64), + DposV2RewardClaimedInfo: make(map[string]common.Fixed64), + WithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), + ClaimingRewardAddr: make(map[common.Uint256]common.Uint168), + VotesWithdrawableTxInfo: make(map[common.Uint256]common2.OutputInfo), + RenewalTargetTransactionsInfo: make(map[uint32][]interfaces.Transaction), + Nicknames: make(map[string]struct{}), + SpecialTxHashes: make(map[common.Uint256]struct{}), + PreBlockArbiters: make(map[string]struct{}), + EmergencyInactiveArbiters: make(map[string]struct{}), + ProducerDepositMap: make(map[common.Uint168]struct{}), + VersionStartHeight: 0, + VersionEndHeight: 0, + DPoSV2ActiveHeight: math.MaxUint32, } } @@ -1027,6 +1100,14 @@ func copyWithdrawableTransactionsMap(src map[common.Uint256]common2.OutputInfo) return } +func copyRenewalTargetTransactionsMap(src map[uint32][]interfaces.Transaction) (dst map[uint32][]interfaces.Transaction) { + dst = map[uint32][]interfaces.Transaction{} + for k, v := range src { + dst[k] = v + } + return +} + func copyRewardClaimingAddrMap(src map[common.Uint256]common.Uint168) (dst map[common.Uint256]common.Uint168) { dst = map[common.Uint256]common.Uint168{} for k, v := range src { diff --git a/dpos/state/state.go b/dpos/state/state.go index fbfc93834..e45a7dbec 100644 --- a/dpos/state/state.go +++ b/dpos/state/state.go @@ -10,11 +10,6 @@ import ( "encoding/hex" "errors" "fmt" - "io" - "math" - "sort" - "sync" - "github.com/elastos/Elastos.ELA/common" "github.com/elastos/Elastos.ELA/common/config" "github.com/elastos/Elastos.ELA/core/contract" @@ -31,6 +26,10 @@ import ( "github.com/elastos/Elastos.ELA/p2p" "github.com/elastos/Elastos.ELA/p2p/msg" "github.com/elastos/Elastos.ELA/utils" + "io" + "math" + "sort" + "sync" ) // ProducerState represents the state of a producer. @@ -608,6 +607,42 @@ type State struct { LastRenewalDPoSV2Votes map[common.Uint256]struct{} } +func (s *State) IsInPOWMode() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.ConsensusAlgorithm == POW +} + +func (s *State) GetRevertToPOWBlockHeight() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.RevertToPOWBlockHeight +} + +func (s *State) IsNeedNextTurnDPOSInfo() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.NeedNextTurnDPOSInfo +} + +func (s *State) GetDPOSWorkHeight() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DPOSWorkHeight +} + +func (s *State) GetNoProducers() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.NoProducers +} + +func (s *State) GetNoClaimDPOSNode() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.NoClaimDPOSNode +} + func (s *State) DPoSV2Started() bool { s.mtx.RLock() defer s.mtx.RUnlock() @@ -618,12 +653,6 @@ func (s *State) dposV2Started() bool { return s.DPoSV2ActiveHeight != math.MaxUint32 } -func (s *State) isDposV2Active() bool { - log.Errorf("isDposV2Active len(a.DposV2EffectedProducers) %d GeneralArbiters %d", len(s.DposV2EffectedProducers), - s.ChainParams.DPoSConfiguration.NormalArbitratorsCount) - return len(s.DposV2EffectedProducers) >= s.ChainParams.DPoSConfiguration.NormalArbitratorsCount*3/2 -} - func (s *State) GetRealWithdrawTransactions() map[common.Uint256]common2.OutputInfo { s.mtx.RLock() defer s.mtx.RUnlock() @@ -638,6 +667,67 @@ func (s *State) GetVotesWithdrawableTxInfo() map[common.Uint256]common2.OutputIn return s.StateKeyFrame.VotesWithdrawableTxInfo } +func (s *State) GetRenewalTargetTransactionsInfo() map[uint32][]interfaces.Transaction { + s.mtx.RLock() + defer s.mtx.RUnlock() + + return s.RenewalTargetTransactionsInfo +} + +func (s *State) GetDposV2RewardClaimedInfo(addr string) common.Fixed64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DposV2RewardClaimedInfo[addr] +} + +func (s *State) GetDPoSV2RewardInfo(addr string) (common.Fixed64, bool) { + s.mtx.RLock() + defer s.mtx.RUnlock() + claimAmount, ok := s.DPoSV2RewardInfo[addr] + return claimAmount, ok +} + +// copy +func (s *State) CopyDPoSV2RewardInfo() map[string]common.Fixed64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + rewardInfo := copyFixed64Map(s.DPoSV2RewardInfo) + return rewardInfo +} + +func (s *State) GetDposV2RewardClaimingInfo(addr string) common.Fixed64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DposV2RewardClaimingInfo[addr] +} + +func (s *State) GetUsedDposV2Votes(stakeProgramHash common.Uint168) common.Fixed64 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.UsedDposV2Votes[stakeProgramHash] +} + +func (s *State) GetUsedDposVotes(stakeProgramHash common.Uint168) ([]payload.VotesWithLockTime, bool) { + s.mtx.RLock() + defer s.mtx.RUnlock() + usedDposVotes, ok := s.UsedDposVotes[stakeProgramHash] + return usedDposVotes, ok +} + +func (s *State) GetDposV2VoteRights(stakeProgramHash common.Uint168) (common.Fixed64, bool) { + s.mtx.RLock() + defer s.mtx.RUnlock() + totalVotes, exist := s.DposV2VoteRights[stakeProgramHash] + return totalVotes, exist + +} + +func (s *State) GetProducerKey(publicKey []byte) string { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.getProducerKey(publicKey) +} + // getProducerKey returns the producer's owner public key string, whether the // given public key is the producer's node public key or owner public key. func (s *State) getProducerKey(publicKey []byte) string { @@ -802,6 +892,33 @@ func (s *State) GetAllProducers() []Producer { return s.getAllProducersByCopy() } +func (s *State) GetLastRandomCandidateHeight() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.LastRandomCandidateHeight +} +func (s *State) GetLastRandomCandidateOwner() string { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.LastRandomCandidateOwner +} + +func (s *State) isDposV2Active() bool { + if s.DPoSV2ActiveHeight != math.MaxUint32 { + return true + } + return len(s.DposV2EffectedProducers) >= s.ChainParams.DPoSConfiguration.NormalArbitratorsCount*3/2 +} + +func (s *State) IsDposV2Active() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + if s.DPoSV2ActiveHeight != math.MaxUint32 { + return true + } + return len(s.DposV2EffectedProducers) >= s.ChainParams.DPoSConfiguration.NormalArbitratorsCount*3/2 +} + func (s *State) GetDetailedDPoSV2Votes(stakeProgramHash *common.Uint168) []payload.DetailedVoteInfo { s.mtx.RLock() defer s.mtx.RUnlock() @@ -889,6 +1006,14 @@ func (s *State) getAllNodePublicKey() map[string]struct{} { } return nodePublicKeyMap } + +func (s *State) GetNFTInfo(nftID common.Uint256) (payload.NFTInfo, bool) { + s.mtx.RLock() + defer s.mtx.RUnlock() + nftInfo, exist := s.NFTIDInfoHashMap[nftID] + return nftInfo, exist +} + func (s *State) GetNFTReferKey(nftID common.Uint256) (common.Uint256, error) { s.mtx.RLock() defer s.mtx.RUnlock() @@ -1058,6 +1183,26 @@ func (s *State) GetConsensusAlgorithm() ConsesusAlgorithm { return s.ConsensusAlgorithm } +func (s *State) getDPoSV2ActiveHeight() uint32 { + return s.DPoSV2ActiveHeight +} + +func (s *State) GetDPoSV2ActiveHeight() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DPoSV2ActiveHeight +} + +func (s *State) isDPoSV2Run(blockHeight uint32) bool { + return blockHeight >= s.DPoSV2ActiveHeight +} + +func (s *State) IsDPoSV2Run(blockHeight uint32) bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return blockHeight >= s.DPoSV2ActiveHeight +} + // IsActiveProducer returns if a producer is in activate list according to the // public key. func (s *State) IsActiveProducer(publicKey []byte) bool { @@ -1309,7 +1454,6 @@ func (s *State) ProcessBlock(block *types.Block, confirm *payload.Confirm, dutyI // Commit changes here if no errors found. s.History.Commit(block.Height) - if block.Height >= s.ChainParams.DPoSV2StartHeight && len(s.WithdrawableTxInfo) != 0 { s.createDposV2ClaimRewardRealWithdrawTransaction(block.Height) @@ -1741,6 +1885,13 @@ func (s *State) processTransactions(txs []interfaces.Transaction, height uint32) } } + if rtxs := s.RenewalTargetTransactionsInfo[height- + s.ChainParams.DPoSConfiguration.RenewalVotingTargetDuration]; len(rtxs) != 0 { + // process renewal voting target transactions + for _, tx := range rtxs { + s.processRenewalVotingTargetContent(tx, height) + } + } } // processTransaction take a transaction and the height it has been packed into @@ -2053,6 +2204,17 @@ func (s *State) processVoting(tx interfaces.Transaction, height uint32) { s.processVotingContent(tx, height) case payload.RenewalVoteVersion: s.processRenewalVotingContent(tx, height) + case payload.RenewalVoteTargetVersion: + oriList := copyRenewalTargetTransactionsMap(s.RenewalTargetTransactionsInfo) + s.History.Append(height, func() { + if _, ok := s.RenewalTargetTransactionsInfo[height]; !ok { + s.RenewalTargetTransactionsInfo[height] = make([]interfaces.Transaction, 0) + } + s.RenewalTargetTransactionsInfo[height] = append(s.RenewalTargetTransactionsInfo[height], tx) + }, func() { + s.RenewalTargetTransactionsInfo = oriList + }) + //s.processRenewalVotingTargetContent(tx, height) } } @@ -2243,6 +2405,74 @@ func (s *State) processRenewalVotingContent(tx interfaces.Transaction, height ui } } +func (s *State) processRenewalVotingTargetContent(tx interfaces.Transaction, height uint32) { + // get stake address + code := tx.Programs()[0].Code + ct, _ := contract.CreateStakeContractByCode(code) + stakeAddress := ct.ToProgramHash() + pld := tx.Payload().(*payload.Voting) + for _, cont := range pld.RenewalContents { + content := cont + + v2Producers := s.getDposV2Producers() + var oriVote *payload.DetailedVoteInfo + var oriProducer *Producer + for _, p := range v2Producers { + v, err := p.GetDetailedDPoSV2Votes(*stakeAddress, content.ReferKey) + if err != nil { + continue + } + oriVote = &v + oriProducer = p + break + } + if oriVote == nil { + log.Errorf("invalid voting refer key:%s, statke addr:%s", content.ReferKey, stakeAddress) + return + } + + // get producer and update the votes + newProducer := s.getDPoSV2Producer(content.VotesInfo.Candidate) + if newProducer == nil { + log.Info("can not find producer ", hex.EncodeToString(content.VotesInfo.Candidate)) + continue + } + + // record all new votes information + detailVoteInfo := payload.DetailedVoteInfo{ + StakeProgramHash: *stakeAddress, + TransactionHash: tx.Hash(), + BlockHeight: height, + PayloadVersion: oriVote.PayloadVersion, + VoteType: outputpayload.DposV2, + Info: []payload.VotesWithLockTime{content.VotesInfo}, + } + + s.LastRenewalDPoSV2Votes[content.ReferKey] = struct{}{} + + referKey := detailVoteInfo.ReferKey() + oriRenewalTargetInfo := s.RenewalTargetTransactionsInfo[height] + s.History.Append(height, func() { + if newProducer.detailedDPoSV2Votes == nil { + newProducer.detailedDPoSV2Votes = make(map[common.Uint168]map[common.Uint256]payload.DetailedVoteInfo) + } + if _, ok := newProducer.detailedDPoSV2Votes[*stakeAddress]; !ok { + newProducer.detailedDPoSV2Votes[*stakeAddress] = make(map[common.Uint256]payload.DetailedVoteInfo, 0) + } + newProducer.detailedDPoSV2Votes[*stakeAddress][referKey] = detailVoteInfo + delete(oriProducer.detailedDPoSV2Votes[*stakeAddress], content.ReferKey) + delete(s.RenewalTargetTransactionsInfo, height) + }, func() { + delete(newProducer.detailedDPoSV2Votes[*stakeAddress], referKey) + if len(newProducer.detailedDPoSV2Votes[*stakeAddress]) == 0 { + delete(newProducer.detailedDPoSV2Votes, *stakeAddress) + } + oriProducer.detailedDPoSV2Votes[*stakeAddress][content.ReferKey] = *oriVote + s.RenewalTargetTransactionsInfo[height] = oriRenewalTargetInfo + }) + } +} + // processDeposit takes a transaction output with deposit program hash. func (s *State) processDeposit(tx interfaces.Transaction, height uint32) { for i, output := range tx.Outputs() { @@ -2685,13 +2915,16 @@ func (s *State) processRetVotesRewardRealWithdraw(tx interfaces.Transaction, hei func (s *State) processCreateNFT(tx interfaces.Transaction, height uint32) { nftPayload := tx.Payload().(*payload.CreateNFT) nftID := common.GetNFTID(nftPayload.ReferKey, tx.Hash()) - - // record the relationship map between ID and genesis block hash - s.NFTIDInfoHashMap[nftID] = payload.NFTInfo{ - ReferKey: nftPayload.ReferKey, - GenesisBlockHash: nftPayload.GenesisBlockHash, - CreateNFTTxHash: tx.Hash(), - } + s.History.Append(height, func() { + // record the relationship map between ID and genesis block hash + s.NFTIDInfoHashMap[nftID] = payload.NFTInfo{ + ReferKey: nftPayload.ReferKey, + GenesisBlockHash: nftPayload.GenesisBlockHash, + CreateNFTTxHash: tx.Hash(), + } + }, func() { + delete(s.NFTIDInfoHashMap, nftID) + }) producers := s.getDposV2Producers() for _, producer := range producers { @@ -3023,6 +3256,36 @@ func (s *State) ExistNFTID(id common.Uint256) bool { return exist } +func (s *State) GetNeedRevertToDPOSTX() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.NeedRevertToDPOSTX +} + +func (s *State) GetNeedNextTurnDPOSInfo() bool { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.NeedNextTurnDPOSInfo +} + +func (s *State) GetLastBlockTimestamp() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.LastBlockTimestamp +} + +func (a *State) SetNeedRevertToDPOSTX(need bool) { + a.mtx.Lock() + defer a.mtx.Unlock() + a.NeedRevertToDPOSTX = need +} + +func (s *State) SetNeedNextTurnDPOSInfo(need bool) { + s.mtx.Lock() + defer s.mtx.Unlock() + s.NeedNextTurnDPOSInfo = need +} + func (s *State) CanNFTDestroy(IDs []common.Uint256) []common.Uint256 { s.mtx.RLock() defer s.mtx.RUnlock() diff --git a/dpos/state/state_test.go b/dpos/state/state_test.go index ad0fba806..d5dcb2a25 100644 --- a/dpos/state/state_test.go +++ b/dpos/state/state_test.go @@ -4,13 +4,12 @@ import ( "bytes" "encoding/hex" "fmt" - "testing" - "github.com/elastos/Elastos.ELA/common" "github.com/elastos/Elastos.ELA/core/contract" "github.com/elastos/Elastos.ELA/core/types/payload" "github.com/elastos/Elastos.ELA/crypto" "github.com/stretchr/testify/assert" + "testing" ) func TestProducer_ActivateRequestHeight(t *testing.T) { diff --git a/events/events.go b/events/events.go index d68aea450..0cfbb531e 100644 --- a/events/events.go +++ b/events/events.go @@ -106,10 +106,10 @@ func (n EventType) String() string { // Event defines notification that is sent to the caller via the callback // function provided during the call to New and consists of a notification type // as well as associated data that depends on the type as follows: -// - ETBlockAccepted: *types.Block -// - ETBlockConnected: *types.Block -// - ETBlockDisconnected: *types.Block -// - ETTransactionAccepted: *types.BaseTransaction +// - ETBlockAccepted: *types.Block +// - ETBlockConnected: *types.Block +// - ETBlockDisconnected: *types.Block +// - ETTransactionAccepted: *types.BaseTransaction type Event struct { Type EventType Data interface{} diff --git a/events/events_test.go b/events/events_test.go index ab2c644f9..ec12f7d65 100644 --- a/events/events_test.go +++ b/events/events_test.go @@ -6,6 +6,9 @@ package events import ( + "fmt" + "math" + "math/big" "testing" "time" @@ -13,6 +16,22 @@ import ( "github.com/stretchr/testify/assert" ) +func TestNotify2(t *testing.T) { + // 创建一个 big.Int 对象 + intValue := big.NewInt(10000) + + // 转换为 big.Float + floatValue := new(big.Float).SetInt(intValue) + decimal := int(3) + floatValue.Quo(floatValue, new(big.Float).SetFloat64(math.Pow(float64(10), float64(decimal)))) + + // 将 big.Float 格式化为字符串 + formattedValue := floatValue.Text('f', 18) + + fmt.Println(formattedValue) + +} + func TestNotify(t *testing.T) { test.SkipShort(t) notifyChan := make(chan struct{}) diff --git a/mempool/conflictfunc.go b/mempool/conflictfunc.go index 5fccefbf2..ec6062717 100644 --- a/mempool/conflictfunc.go +++ b/mempool/conflictfunc.go @@ -680,3 +680,27 @@ func strCreateNFTID(tx interfaces.Transaction) (interface{}, error) { } return p.StakeAddress, nil } + +func hashArrayRenewalTargetReferKeys(tx interfaces.Transaction) (interface{}, error) { + arrayHash := make([]common.Uint256, 0) + if tx.TxType() == common2.Voting { + p, ok := tx.Payload().(*payload.Voting) + if !ok { + return nil, fmt.Errorf( + "CRC proposal payload cast failed, tx:%s", tx.Hash()) + } + + for _, c := range p.RenewalContents { + arrayHash = append(arrayHash, c.ReferKey) + } + } else if tx.TxType() == common2.CreateNFT { + p, ok := tx.Payload().(*payload.CreateNFT) + if !ok { + return nil, fmt.Errorf( + "CreateNFT payload cast failed, tx: %s", tx.Hash()) + } + arrayHash = append(arrayHash, p.ReferKey) + } + + return arrayHash, nil +} diff --git a/mempool/conflictmanager.go b/mempool/conflictmanager.go index 5301959ab..037162df2 100644 --- a/mempool/conflictmanager.go +++ b/mempool/conflictmanager.go @@ -53,6 +53,7 @@ const ( slotCreateNFT = "createnft" slotCreateNFTStakeAddr = "createnftstakeaddr" slotNFTDestroyFromSideChainHash = "NFTDestroyFromSideChainHash" + slotRenewalVotingTarget = "RenewalVotingTarget" ) type conflict struct { @@ -608,6 +609,19 @@ func newConflictManager() conflictManager { }, ), }, + { + name: slotRenewalVotingTarget, + slot: newConflictSlot(hashArray, + keyTypeFuncPair{ + Type: common2.CreateNFT, + Func: hashArrayRenewalTargetReferKeys, + }, + keyTypeFuncPair{ + Type: common2.Voting, + Func: hashArrayRenewalTargetReferKeys, + }, + ), + }, }, } } diff --git a/servers/interfaces.go b/servers/interfaces.go index daba1d833..37b8caa05 100644 --- a/servers/interfaces.go +++ b/servers/interfaces.go @@ -620,7 +620,8 @@ func GetNFTInfo(params Params) map[string]interface{} { info.StartHeight = detailVoteInfo.BlockHeight info.EndHeight = detailVoteInfo.Info[0].LockTime info.Votes = detailVoteInfo.Info[0].Votes.String() - info.Rewards = Chain.GetState().DPoSV2RewardInfo[nftStakeAddress].String() + rewards, _ := Chain.GetState().GetDPoSV2RewardInfo(nftStakeAddress) + info.Rewards = rewards.String() return } nftReferKey, err := Chain.GetState().GetNFTReferKey(*nftID) @@ -715,8 +716,7 @@ func GetVoteRights(params Params) map[string]interface{} { RemainVoteRight []string `json:"remainvoteright"` //index is same to VoteType } var result []*detailedVoteRight - state := Chain.GetState() - crstate := Chain.GetCRCommittee().GetState() + crCommittee := Chain.GetCRCommittee() for _, address := range addresses { if !strings.HasPrefix(address, "S") { return ResponsePack(InvalidParams, "invalid stake address need prefix s") @@ -725,10 +725,9 @@ func GetVoteRights(params Params) map[string]interface{} { if err != nil { return ResponsePack(InvalidParams, "invalid stake address") } - voteRights := state.DposV2VoteRights stakeProgramHash := *programhash //get totalVotes - totalVotesRight := voteRights[stakeProgramHash] + totalVotesRight, _ := Chain.GetState().GetDposV2VoteRights(stakeProgramHash) vote := &detailedVoteRight{ StakeAddress: address, TotalVotesRight: totalVotesRight.String(), @@ -742,8 +741,7 @@ func GetVoteRights(params Params) map[string]interface{} { RemainVoteRight: make([]string, 5), } // dposv1 - - if udv := state.UsedDposVotes[stakeProgramHash]; !dposV2 && udv != nil { + if udv, _ := Chain.GetState().GetUsedDposVotes(stakeProgramHash); !dposV2 && udv != nil { for _, v := range udv { vote.UsedVotesInfo.UsedDPoSVotes = append(vote.UsedVotesInfo.UsedDPoSVotes, VotesWithLockTimeInfo{ Candidate: hex.EncodeToString(v.Candidate), @@ -753,7 +751,7 @@ func GetVoteRights(params Params) map[string]interface{} { } } // crc - if ucv := crstate.UsedCRVotes[stakeProgramHash]; ucv != nil { + if ucv := crCommittee.GetUsedCRVotes(stakeProgramHash); ucv != nil { for _, v := range ucv { c, _ := common.Uint168FromBytes(v.Candidate) candidate, _ := c.ToAddress() @@ -765,7 +763,7 @@ func GetVoteRights(params Params) map[string]interface{} { } } // cr Impeachment - if uciv := crstate.UsedCRImpeachmentVotes[stakeProgramHash]; uciv != nil { + if uciv := crCommittee.GetUsedCRImpeachmentVotes(stakeProgramHash); uciv != nil { for _, v := range uciv { c, _ := common.Uint168FromBytes(v.Candidate) candidate, _ := c.ToAddress() @@ -778,7 +776,7 @@ func GetVoteRights(params Params) map[string]interface{} { } // cr Proposal - if ucpv := crstate.UsedCRCProposalVotes[stakeProgramHash]; ucpv != nil { + if ucpv := crCommittee.GetUsedCRCProposalVotes(stakeProgramHash); ucpv != nil { for _, v := range ucpv { proposalHash, _ := common.Uint256FromBytes(v.Candidate) vote.UsedVotesInfo.UsedCRCProposalVotes = append(vote.UsedVotesInfo.UsedCRCProposalVotes, VotesWithLockTimeInfo{ @@ -790,7 +788,7 @@ func GetVoteRights(params Params) map[string]interface{} { } // dposv2 - if dpv2 := state.GetDetailedDPoSV2Votes(&stakeProgramHash); dpv2 != nil { + if dpv2 := Chain.GetState().GetDetailedDPoSV2Votes(&stakeProgramHash); dpv2 != nil { for i, v := range dpv2 { address, _ := v.StakeProgramHash.ToAddress() vote.UsedVotesInfo.UsedDPoSV2Votes = append(vote.UsedVotesInfo.UsedDPoSV2Votes, DetailedVoteInfo{ @@ -829,15 +827,14 @@ func GetVoteRights(params Params) map[string]interface{} { } func GetUsedVoteRight(voteType outputpayload.VoteType, stakeProgramHash *common.Uint168) (common.Fixed64, error) { - state := Chain.GetState() - crstate := Chain.GetCRCommittee().GetState() + crCommittee := Chain.GetCRCommittee() usedDposVote := common.Fixed64(0) switch voteType { case outputpayload.Delegate: - if Chain.GetHeight() >= Chain.GetState().DPoSV2ActiveHeight { + if Chain.GetHeight() >= Chain.GetState().GetDPoSV2ActiveHeight() { usedDposVote = 0 } else { - if dposVotes, ok := state.UsedDposVotes[*stakeProgramHash]; ok { + if dposVotes, ok := Chain.GetState().GetUsedDposVotes(*stakeProgramHash); ok { maxVotes := common.Fixed64(0) for _, votesInfo := range dposVotes { if votesInfo.Votes > maxVotes { @@ -848,13 +845,13 @@ func GetUsedVoteRight(voteType outputpayload.VoteType, stakeProgramHash *common. } } case outputpayload.CRC: - if usedCRVoteRights, ok := crstate.UsedCRVotes[*stakeProgramHash]; ok { + if usedCRVoteRights := crCommittee.GetUsedCRVotes(*stakeProgramHash); usedCRVoteRights != nil { for _, votesInfo := range usedCRVoteRights { usedDposVote += votesInfo.Votes } } case outputpayload.CRCProposal: - if usedCRCProposalVoteRights, ok := crstate.UsedCRCProposalVotes[*stakeProgramHash]; ok { + if usedCRCProposalVoteRights := crCommittee.GetUsedCRCProposalVotes(*stakeProgramHash); usedCRCProposalVoteRights != nil { maxVotes := common.Fixed64(0) for _, votesInfo := range usedCRCProposalVoteRights { if votesInfo.Votes > maxVotes { @@ -865,7 +862,7 @@ func GetUsedVoteRight(voteType outputpayload.VoteType, stakeProgramHash *common. } case outputpayload.CRCImpeachment: - if usedCRImpeachmentVoteRights, ok := crstate.UsedCRImpeachmentVotes[*stakeProgramHash]; ok { + if usedCRImpeachmentVoteRights := crCommittee.GetUsedCRImpeachmentVotes(*stakeProgramHash); usedCRImpeachmentVoteRights != nil { for _, votesInfo := range usedCRImpeachmentVoteRights { usedDposVote += votesInfo.Votes } @@ -873,7 +870,7 @@ func GetUsedVoteRight(voteType outputpayload.VoteType, stakeProgramHash *common. case outputpayload.DposV2: addr, _ := stakeProgramHash.ToAddress() fmt.Println("addr", addr) - usedDposVote = state.UsedDposV2Votes[*stakeProgramHash] + usedDposVote = Chain.GetState().GetUsedDposV2Votes(*stakeProgramHash) default: return 0, errors.New("unsupport vote type") } @@ -2335,9 +2332,9 @@ func DposV2RewardInfo(param Params) map[string]interface{} { } } - claimable := Chain.GetState().DPoSV2RewardInfo[stakeAddress] - claiming := Chain.GetState().DposV2RewardClaimingInfo[stakeAddress] - claimed := Chain.GetState().DposV2RewardClaimedInfo[stakeAddress] + claimable, _ := Chain.GetState().GetDPoSV2RewardInfo(stakeAddress) + claiming := Chain.GetState().GetDposV2RewardClaimingInfo(stakeAddress) + claimed := Chain.GetState().GetDposV2RewardClaimedInfo(stakeAddress) result := RPCDposV2RewardInfo{ Address: addr, Claimable: claimable.String(), @@ -2347,13 +2344,13 @@ func DposV2RewardInfo(param Params) map[string]interface{} { return ResponsePack(Success, result) } else { var result []RPCDposV2RewardInfo - dposV2RewardInfo := Chain.GetState().DPoSV2RewardInfo + dposV2RewardInfo := Chain.GetState().CopyDPoSV2RewardInfo() for addr, value := range dposV2RewardInfo { result = append(result, RPCDposV2RewardInfo{ Address: addr, Claimable: value.String(), - Claiming: Chain.GetState().DposV2RewardClaimingInfo[addr].String(), - Claimed: Chain.GetState().DposV2RewardClaimedInfo[addr].String(), + Claiming: Chain.GetState().GetDposV2RewardClaimingInfo(addr).String(), + Claimed: Chain.GetState().GetDposV2RewardClaimedInfo(addr).String(), }) } @@ -2365,7 +2362,7 @@ func GetDPosV2Info(param Params) map[string]interface{} { result := &RPCDPosV2Info{ ConsensusAlgorithm: Chain.GetState().GetConsensusAlgorithm().String(), Height: Store.GetHeight(), - DPoSV2ActiveHeight: Chain.GetState().DPoSV2ActiveHeight, + DPoSV2ActiveHeight: Chain.GetState().GetDPoSV2ActiveHeight(), } return ResponsePack(Success, result) }