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/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..0e9142434 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.ClaimedDPoSKey(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.NextClaimedDPoSKey(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 94f179c10..ae6ba7457 100644 --- a/core/transaction/crcproposaltransaction.go +++ b/core/transaction/crcproposaltransaction.go @@ -643,15 +643,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..bafc51028 100644 --- a/core/transaction/createnfttransaction.go +++ b/core/transaction/createnfttransaction.go @@ -92,7 +92,8 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } state := t.parameters.BlockChain.GetState() - crState := t.parameters.BlockChain.GetCRCommittee().GetState() + crCommittee := t.parameters.BlockChain.GetCRCommittee() + producers := state.GetDposV2Producers() nftID := common.GetNFTID(pld.ReferKey, t.hash()) var existVote bool @@ -143,29 +144,29 @@ func (t *CreateNFTTransaction) SpecialContextCheck() (elaerr.ELAError, bool) { } // nft has not been created before - if g, ok := state.NFTIDInfoHashMap[nftID]; ok { + if nftInfo := state.GetNFTInfo(nftID); nftInfo != nil { 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 +174,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 +183,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..5fba2e656 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] - if !ok { + claimAmount := t.parameters.BlockChain.GetState().GetDPoSV2RewardInfo(addr) + if claimAmount == 0 { 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..d45ac76b7 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..43579aeeb 100644 --- a/core/transaction/voting.go +++ b/core/transaction/voting.go @@ -127,13 +127,8 @@ 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] - if !exist { - return elaerr.Simple(elaerr.ErrTxInvalidOutput, errors.New("has no vote rights")), true - } - usedDPoSV2VoteRights, _ := state.UsedDposV2Votes[*stakeProgramHash] + totalVotes := state.GetDposV2VoteRights(*stakeProgramHash) + usedDPoSV2VoteRights := state.GetUsedDposV2Votes(*stakeProgramHash) var candidates []*crstate.Candidate if crCommittee.IsInVotingPeriod(blockHeight) { @@ -161,7 +156,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 } diff --git a/cr/state/committee.go b/cr/state/committee.go index bfa7b7d43..bbcbb082c 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) ClaimedDPoSKey(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) NextClaimedDPoSKey(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,15 @@ 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 +} + // get all next CRMembers ordered by CID func (c *Committee) GetNextMembers() []*CRMember { c.mtx.RLock() @@ -862,6 +912,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..2e068037a 100644 --- a/dpos/state/arbitrators.go +++ b/dpos/state/arbitrators.go @@ -120,7 +120,7 @@ func (a *Arbiters) Start() { func (a *Arbiters) SetNeedRevertToDPOSTX(need bool) { a.mtx.Lock() defer a.mtx.Unlock() - a.NeedRevertToDPOSTX = need + a.SetStateNeedRevertToDPOSTX(need) } func GetOwnerKeyStandardProgramHash(ownerKey []byte) (ownKeyProgramHash *common.Uint168, err error) { @@ -135,7 +135,7 @@ func GetOwnerKeyStandardProgramHash(ownerKey []byte) (ownKeyProgramHash *common. func (a *Arbiters) SetNeedNextTurnDPOSInfo(need bool) { a.mtx.Lock() defer a.mtx.Unlock() - a.NeedNextTurnDPOSInfo = need + a.SetStateNeedNextTurnDPOSInfo(need) } func (a *Arbiters) IsInPOWMode() bool { @@ -179,6 +179,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,10 +202,14 @@ 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) { + a.mtx.Lock() a.State.ProcessBlock(block, confirm, a.DutyIndex) + a.mtx.Unlock() a.IncreaseChainHeight(block, confirm) } @@ -679,14 +684,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) { @@ -815,15 +826,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 }) @@ -2075,8 +2090,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]...) @@ -2170,9 +2187,15 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { if height >= a.ChainParams.CRConfiguration.CRClaimDPOSNodeStartHeight { oriNeedNextTurnDPOSInfo := a.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() + }) } @@ -2186,9 +2209,15 @@ func (a *Arbiters) UpdateNextArbitrators(versionHeight, height uint32) error { if a.DPoSV2ActiveHeight == math.MaxUint32 && a.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() + }) } @@ -2801,7 +2830,7 @@ func (a *Arbiters) newCheckPoint(height uint32) *CheckPoint { ArbitersRoundReward: make(map[common.Uint168]common.Fixed64), IllegalBlocksPayloadHashes: make(map[common.Uint256]interface{}), CurrentArbitrators: a.CurrentArbitrators, - StateKeyFrame: *a.State.snapshot(), + StateKeyFrame: *a.State.Snapshot(), } point.CurrentArbitrators = copyByteList(a.CurrentArbitrators) point.CurrentCandidates = copyByteList(a.CurrentCandidates) diff --git a/dpos/state/keyframe.go b/dpos/state/keyframe.go index d7e4cc05f..4f16501c0 100644 --- a/dpos/state/keyframe.go +++ b/dpos/state/keyframe.go @@ -47,7 +47,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 diff --git a/dpos/state/state.go b/dpos/state/state.go index fbfc93834..f0f6af649 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,24 @@ type State struct { LastRenewalDPoSV2Votes map[common.Uint256]struct{} } +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() @@ -638,6 +655,51 @@ func (s *State) GetVotesWithdrawableTxInfo() map[common.Uint256]common2.OutputIn return s.StateKeyFrame.VotesWithdrawableTxInfo } +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 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DPoSV2RewardInfo[addr] +} + +// 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 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DposV2VoteRights[stakeProgramHash] +} + // 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 +864,12 @@ func (s *State) GetAllProducers() []Producer { return s.getAllProducersByCopy() } +func (s *State) GetDPoSV2ActiveHeight() uint32 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.DPoSV2ActiveHeight +} + func (s *State) GetDetailedDPoSV2Votes(stakeProgramHash *common.Uint168) []payload.DetailedVoteInfo { s.mtx.RLock() defer s.mtx.RUnlock() @@ -889,6 +957,17 @@ func (s *State) getAllNodePublicKey() map[string]struct{} { } return nodePublicKeyMap } + +func (s *State) GetNFTInfo(nftID common.Uint256) *payload.NFTInfo { + s.mtx.RLock() + defer s.mtx.RUnlock() + nftInfo, exist := s.NFTIDInfoHashMap[nftID] + if !exist { + return nil + } + return &nftInfo +} + func (s *State) GetNFTReferKey(nftID common.Uint256) (common.Uint256, error) { s.mtx.RLock() defer s.mtx.RUnlock() @@ -1309,7 +1388,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) @@ -2685,13 +2763,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 +3104,18 @@ func (s *State) ExistNFTID(id common.Uint256) bool { return exist } +func (a *State) SetStateNeedRevertToDPOSTX(need bool) { + a.mtx.Lock() + defer a.mtx.Unlock() + a.NeedRevertToDPOSTX = need +} + +func (s *State) SetStateNeedNextTurnDPOSInfo(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() @@ -3745,6 +3838,12 @@ func (s *State) RollbackTo(height uint32) error { return s.History.RollbackTo(height) } +func (s *State) Snapshot() *StateKeyFrame { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.snapshot() +} + // GetHistory returns a History state instance storing the producers and votes // on the historical height. func (s *State) GetHistory(height uint32) (*StateKeyFrame, error) { 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/servers/interfaces.go b/servers/interfaces.go index ccb5ec91d..6c7e6688a 100644 --- a/servers/interfaces.go +++ b/servers/interfaces.go @@ -620,7 +620,7 @@ 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() + info.Rewards = Chain.GetState().GetDPoSV2RewardInfo(nftStakeAddress).String() return } nftReferKey, err := Chain.GetState().GetNFTReferKey(*nftID) @@ -715,8 +715,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 +724,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 +740,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 +750,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 +762,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 +775,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 +787,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 +826,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 +844,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 +861,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 +869,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 +2331,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 +2343,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 +2361,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) }