diff options
author | Wei-Ning Huang <w@byzantine-lab.io> | 2019-07-25 20:41:46 +0800 |
---|---|---|
committer | Wei-Ning Huang <w@byzantine-lab.io> | 2019-09-17 16:57:31 +0800 |
commit | 174f6bfcdf4e2c7fea3a071653908b477ad9a3b3 (patch) | |
tree | 7990bd656bd9cc7ef3014534ad2fee5163da3790 | |
parent | a9a85fa746c727063015d6e70881426ce8b3a3fb (diff) | |
download | go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar.gz go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar.bz2 go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar.lz go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar.xz go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.tar.zst go-tangerine-174f6bfcdf4e2c7fea3a071653908b477ad9a3b3.zip |
core: add GovUtil to unify governance state related access
Add GovUtil so we could use the same logic in everywhere that requires
access to governance state, such as configuration and CRS.
-rw-r--r-- | consensus/dexcon/dexcon.go | 19 | ||||
-rw-r--r-- | consensus/dexcon/dexcon_test.go | 4 | ||||
-rw-r--r-- | core/governance.go | 138 | ||||
-rw-r--r-- | core/headerchain.go | 5 | ||||
-rw-r--r-- | core/state_transition.go | 27 | ||||
-rw-r--r-- | core/tx_pool.go | 36 | ||||
-rw-r--r-- | core/vm/oracle.go | 2 | ||||
-rw-r--r-- | core/vm/oracle_contracts.go | 102 | ||||
-rw-r--r-- | core/vm/oracle_contracts_test.go | 4 | ||||
-rw-r--r-- | core/vm/utils.go | 92 | ||||
-rw-r--r-- | dex/api_backend.go | 6 | ||||
-rw-r--r-- | dex/app.go | 41 | ||||
-rw-r--r-- | dex/app_test.go | 13 | ||||
-rw-r--r-- | dex/downloader/testchain_test.go | 8 | ||||
-rw-r--r-- | dex/governance.go | 10 |
15 files changed, 302 insertions, 205 deletions
diff --git a/consensus/dexcon/dexcon.go b/consensus/dexcon/dexcon.go index e47a8a8e2..8ececb469 100644 --- a/consensus/dexcon/dexcon.go +++ b/consensus/dexcon/dexcon.go @@ -32,7 +32,7 @@ import ( ) type GovernanceStateFetcher interface { - GetStateForConfigAtRound(round uint64) *vm.GovernanceState + GetConfigState(round uint64) (*vm.GovernanceState, error) DKGSetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error) } @@ -114,7 +114,10 @@ func (d *Dexcon) Prepare(chain consensus.ChainReader, header *types.Header) erro func (d *Dexcon) inExtendedRound(header *types.Header, state *state.StateDB) bool { gs := vm.GovernanceState{state} - rgs := d.govStateFetcer.GetStateForConfigAtRound(header.Round) + rgs, err := d.govStateFetcer.GetConfigState(header.Round) + if err != nil { + panic(err) + } roundEnd := gs.RoundHeight(new(big.Int).SetUint64(header.Round)).Uint64() + rgs.RoundLength().Uint64() @@ -126,7 +129,10 @@ func (d *Dexcon) inExtendedRound(header *types.Header, state *state.StateDB) boo } func (d *Dexcon) calculateBlockReward(round uint64) *big.Int { - gs := d.govStateFetcer.GetStateForConfigAtRound(round) + gs, err := d.govStateFetcer.GetConfigState(round) + if err != nil { + panic(err) + } config := gs.Configuration() blocksPerRound := config.RoundLength @@ -169,7 +175,10 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta panic(err) } - gcs := d.govStateFetcer.GetStateForConfigAtRound(header.Round - 1) + gcs, err := d.govStateFetcer.GetConfigState(header.Round - 1) + if err != nil { + panic(err) + } for addr := range addrs { offset := gcs.NodesOffsetByNodeKeyAddress(addr) @@ -182,7 +191,7 @@ func (d *Dexcon) Finalize(chain consensus.ChainReader, header *types.Header, sta prevRoundHeight := gs.RoundHeight(big.NewInt(int64(header.Round - 1))) if lastHeight.Uint64() < prevRoundHeight.Uint64() { - log.Info("Disqualify node", "round", header.Round, "nodePubKey", hex.EncodeToString(node.PublicKey)) + log.Debug("Disqualify node", "round", header.Round, "nodePubKey", hex.EncodeToString(node.PublicKey)) err = gs.Disqualify(node) if err != nil { log.Error("Failed to disqualify node", "err", err) diff --git a/consensus/dexcon/dexcon_test.go b/consensus/dexcon/dexcon_test.go index 09b7281ef..3478b911b 100644 --- a/consensus/dexcon/dexcon_test.go +++ b/consensus/dexcon/dexcon_test.go @@ -35,8 +35,8 @@ type govStateFetcher struct { statedb *state.StateDB } -func (g *govStateFetcher) GetStateForConfigAtRound(_ uint64) *vm.GovernanceState { - return &vm.GovernanceState{g.statedb} +func (g *govStateFetcher) GetConfigState(_ uint64) (*vm.GovernanceState, error) { + return &vm.GovernanceState{g.statedb}, nil } func (g *govStateFetcher) DKGSetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error) { diff --git a/core/governance.go b/core/governance.go index 792c94621..8bad0ca98 100644 --- a/core/governance.go +++ b/core/governance.go @@ -19,7 +19,6 @@ import ( "github.com/tangerine-network/go-tangerine/common" "github.com/tangerine-network/go-tangerine/core/state" "github.com/tangerine-network/go-tangerine/core/vm" - "github.com/tangerine-network/go-tangerine/crypto" "github.com/tangerine-network/go-tangerine/log" ) @@ -64,6 +63,7 @@ type Governance struct { nodeSetCache *dexCore.NodeSetCache dkgCache *simplelru.LRU dkgCacheMu sync.RWMutex + util vm.GovUtil } func NewGovernance(db GovernanceStateDB) *Governance { @@ -77,113 +77,84 @@ func NewGovernance(db GovernanceStateDB) *Governance { dkgCache: cache, } g.nodeSetCache = dexCore.NewNodeSetCache(g) + g.util = vm.GovUtil{g} return g } -func (g *Governance) GetHeadState() *vm.GovernanceState { +func (g *Governance) GetHeadGovState() (*vm.GovernanceState, error) { headState, err := g.db.State() if err != nil { log.Error("Governance head state not ready", "err", err) - panic(err) + return nil, err } - return &vm.GovernanceState{StateDB: headState} + return &vm.GovernanceState{StateDB: headState}, nil } -func (g *Governance) getHelperAtRound(round uint64) *vm.GovernanceState { - height := g.GetRoundHeight(round) - - // Sanity check - if round != 0 && height == 0 { - log.Error("Governance state incorrect", "round", round, "got height", height) - panic("round != 0 but height == 0") - } - - s, err := g.db.StateAt(height) - if err != nil { - log.Error("Governance state not ready", "round", round, "height", height, "err", err) - panic(err) - } - return &vm.GovernanceState{StateDB: s} +func (g *Governance) StateAt(height uint64) (*state.StateDB, error) { + return g.db.StateAt(height) } -func (g *Governance) GetStateForConfigAtRound(round uint64) *vm.GovernanceState { - if round < dexCore.ConfigRoundShift { - round = 0 - } else { - round -= dexCore.ConfigRoundShift - } - return g.getHelperAtRound(round) +func (g *Governance) GetConfigState(round uint64) (*vm.GovernanceState, error) { + return g.util.GetConfigState(round) } -func (g *Governance) GetStateAtRound(round uint64) *vm.GovernanceState { - height := g.GetRoundHeight(round) - s, err := g.db.StateAt(height) +func (g *Governance) GetStateForDKGAtRound(round uint64) (*vm.GovernanceState, error) { + gs, err := g.GetHeadGovState() if err != nil { - log.Error("Governance state not ready", "round", round, "height", height, "err", err) - panic(err) + return nil, err } - return &vm.GovernanceState{StateDB: s} -} - -func (g *Governance) GetStateForDKGAtRound(round uint64) *vm.GovernanceState { - dkgRound := g.GetHeadState().DKGRound().Uint64() + dkgRound := gs.DKGRound().Uint64() if round > dkgRound { - return nil + return nil, fmt.Errorf("invalid round input: %d, dkgRound: %d", round, dkgRound) } if round == dkgRound { - return g.GetHeadState() + return gs, nil } - return g.GetStateAtRound(round) + return g.util.GetStateAtRound(round) } func (g *Governance) CRSRound() uint64 { - return g.GetHeadState().CRSRound().Uint64() + gs, err := g.GetHeadGovState() + if err != nil { + log.Error("Failed to get head governance state", "err", err) + return 0 + } + return gs.CRSRound().Uint64() } // CRS returns the CRS for a given round. func (g *Governance) CRS(round uint64) coreCommon.Hash { - if round <= dexCore.DKGDelayRound { - s := g.GetStateAtRound(0) - crs := s.CRS() - for i := uint64(0); i < round; i++ { - crs = crypto.Keccak256Hash(crs[:]) - } - return coreCommon.Hash(crs) - } - if round > g.CRSRound() { - return coreCommon.Hash{} - } - var s *vm.GovernanceState - if round == g.CRSRound() { - s = g.GetHeadState() - } else { - s = g.GetStateAtRound(round) - } - return coreCommon.Hash(s.CRS()) + return coreCommon.Hash(g.util.CRS(round)) +} + +func (g *Governance) GetRoundHeight(round uint64) uint64 { + return g.util.GetRoundHeight(round) } func (g *Governance) Configuration(round uint64) *coreTypes.Config { - configHelper := g.GetStateForConfigAtRound(round) - c := configHelper.Configuration() + s, err := g.util.GetConfigState(round) + if err != nil { + panic(err) + } + c := s.Configuration() return &coreTypes.Config{ LambdaBA: time.Duration(c.LambdaBA) * time.Millisecond, LambdaDKG: time.Duration(c.LambdaDKG) * time.Millisecond, - NotarySetSize: uint32(configHelper.NotarySetSize().Uint64()), + NotarySetSize: uint32(s.NotarySetSize().Uint64()), RoundLength: c.RoundLength, MinBlockInterval: time.Duration(c.MinBlockInterval) * time.Millisecond, } } -func (g *Governance) GetRoundHeight(round uint64) uint64 { - return g.GetHeadState().RoundHeight(big.NewInt(int64(round))).Uint64() -} - // NodeSet returns the current node set. func (g *Governance) NodeSet(round uint64) []coreCrypto.PublicKey { - s := g.GetStateForConfigAtRound(round) - var pks []coreCrypto.PublicKey + configState, err := g.util.GetConfigState(round) + if err != nil { + panic(err) + } - for _, n := range s.QualifiedNodes() { + var pks []coreCrypto.PublicKey + for _, n := range configState.QualifiedNodes() { pk, err := coreEcdsa.NewPublicKeyFromByteSlice(n.PublicKey) if err != nil { panic(err) @@ -233,9 +204,9 @@ func (g *Governance) DKGSetNodeKeyAddresses(round uint64) (map[common.Address]st } func (g *Governance) getOrUpdateDKGCache(round uint64) *dkgCacheItem { - s := g.GetStateForDKGAtRound(round) - if s == nil { - log.Error("Failed to get DKG state", "round", round) + s, err := g.GetStateForDKGAtRound(round) + if err != nil { + log.Error("Failed to get DKG state", "round", round, "err", err) return nil } reset := s.DKGResetCount(new(big.Int).SetUint64(round)) @@ -289,8 +260,9 @@ func (g *Governance) DKGMasterPublicKeys(round uint64) []*dkgTypes.MasterPublicK } func (g *Governance) IsDKGMPKReady(round uint64) bool { - s := g.GetStateForDKGAtRound(round) - if s == nil { + s, err := g.GetStateForDKGAtRound(round) + if err != nil { + log.Error("Failed to get state for DKG", "round", round, "err", err) return false } config := g.Configuration(round) @@ -300,8 +272,9 @@ func (g *Governance) IsDKGMPKReady(round uint64) bool { } func (g *Governance) IsDKGFinal(round uint64) bool { - s := g.GetStateForDKGAtRound(round) - if s == nil { + s, err := g.GetStateForDKGAtRound(round) + if err != nil { + log.Error("Failed to get state for DKG", "round", round, "err", err) return false } config := g.Configuration(round) @@ -311,18 +284,19 @@ func (g *Governance) IsDKGFinal(round uint64) bool { } func (g *Governance) IsDKGSuccess(round uint64) bool { - s := g.GetStateForDKGAtRound(round) - if s == nil { + s, err := g.GetStateForDKGAtRound(round) + if err != nil { + log.Error("Failed to get state for DKG", "round", round, "err", err) return false } return s.DKGSuccessesCount().Uint64() >= uint64(coreUtils.GetDKGValidThreshold(g.Configuration(round))) } -func (g *Governance) MinGasPrice(round uint64) *big.Int { - return g.GetStateForConfigAtRound(round).MinGasPrice() -} - func (g *Governance) DKGResetCount(round uint64) uint64 { - return g.GetHeadState().DKGResetCount(big.NewInt(int64(round))).Uint64() + gs, err := g.GetHeadGovState() + if err != nil { + log.Error("Failed to get head governance state", "err", err) + } + return gs.DKGResetCount(big.NewInt(int64(round))).Uint64() } diff --git a/core/headerchain.go b/core/headerchain.go index 7714f568c..7f48a5f57 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -425,7 +425,10 @@ func (c *headerVerifierCache) state(round uint64) *vm.GovernanceState { if state, exist := c.stateCache.Get(round); exist { return state.(*vm.GovernanceState) } - state := c.gov.GetStateForConfigAtRound(round) + state, err := c.gov.GetConfigState(round) + if err != nil { + panic(err) + } c.stateCache.Add(round, state) return state } diff --git a/core/state_transition.go b/core/state_transition.go index 1b8a85655..c03ba3110 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -24,10 +24,10 @@ import ( "sync/atomic" "github.com/tangerine-network/go-tangerine/common" + "github.com/tangerine-network/go-tangerine/core/state" "github.com/tangerine-network/go-tangerine/core/vm" "github.com/tangerine-network/go-tangerine/log" "github.com/tangerine-network/go-tangerine/params" - dexCore "github.com/tangerine-network/tangerine-consensus/core" ) var legacyEvm = flag.Bool("legacy-evm", false, "make evm run origin logic") @@ -190,6 +190,14 @@ func (st *StateTransition) preCheck() error { return st.buyGas() } +func (st *StateTransition) GetHeadGovState() (*vm.GovernanceState, error) { + return &vm.GovernanceState{st.state}, nil +} + +func (st *StateTransition) StateAt(height uint64) (*state.StateDB, error) { + return st.evm.StateAtNumber(height) +} + func (st *StateTransition) inExtendedRound() bool { // If we are running tests with chian_makers.go, there will be no valid // blockchain instance for st.evm.StateAtNumber to work correctly. Simply @@ -205,25 +213,18 @@ func (st *StateTransition) inExtendedRound() bool { } } - gs := vm.GovernanceState{st.state} - round := st.evm.Round.Uint64() - if round < dexCore.ConfigRoundShift { - round = 0 - } else { - round -= dexCore.ConfigRoundShift - } - configHeight := gs.RoundHeight(new(big.Int).SetUint64(round)) - state, err := st.evm.StateAtNumber(configHeight.Uint64()) + gs := vm.GovernanceState{st.state} + rgs, err := vm.GovUtil{st}.GetConfigState(round) if err != nil { - panic(err) + log.Error("Failed to get config state", "round", round, "err", err) + return false } - rgs := vm.GovernanceState{state} roundEnd := gs.RoundHeight(st.evm.Round).Uint64() + rgs.RoundLength().Uint64() - // Round 0 starts and height 0 instead of height 1. + // Round 0 starts at height 0 instead of height 1. if round == 0 { roundEnd += 1 } diff --git a/core/tx_pool.go b/core/tx_pool.go index 115a915b4..0f6b8d7e6 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -24,8 +24,6 @@ import ( "sync" "time" - dexCore "github.com/tangerine-network/tangerine-consensus/core" - "github.com/tangerine-network/go-tangerine/common" "github.com/tangerine-network/go-tangerine/common/prque" "github.com/tangerine-network/go-tangerine/core/state" @@ -375,6 +373,18 @@ func (pool *TxPool) lockedReset(oldHead, newHead *types.Header) { pool.reset(oldHead, newHead) } +func (pool *TxPool) GetHeadGovState() (*vm.GovernanceState, error) { + return &vm.GovernanceState{pool.currentState}, nil +} + +func (pool *TxPool) StateAt(height uint64) (*state.StateDB, error) { + block := pool.chain.GetBlockByNumber(height) + if block == nil { + return nil, fmt.Errorf("Failed to get block, height = %d", height) + } + return pool.chain.StateAt(block.Header().Root) +} + // reset retrieves the current state of the blockchain and ensures the content // of the transaction pool is valid with regard to the chain state. func (pool *TxPool) reset(oldHead, newHead *types.Header) { @@ -391,26 +401,12 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.pendingState = state.ManageState(statedb) pool.currentMaxGas = newHead.GasLimit if oldHead == nil || oldHead.Round != newHead.Round { - round := newHead.Round - if round < dexCore.ConfigRoundShift { - round = 0 - } else { - round -= dexCore.ConfigRoundShift - } - state := &vm.GovernanceState{StateDB: statedb} - height := state.RoundHeight(new(big.Int).SetUint64((round))).Uint64() - block := pool.chain.GetBlockByNumber(height) - if block == nil { - log.Error("Failed to get block", "round", round, "height", height) - panic("cannot get config for new round's min gas price") - } - configState, err := pool.chain.StateAt(block.Header().Root) + gs, err := vm.GovUtil{pool}.GetConfigState(newHead.Round) if err != nil { - log.Error("Failed to get txpool state for min gas price", "err", err) - panic("cannot get state for new round's min gas price") + log.Error("Failed to get config state", "round", newHead.Round, "err", err) + panic(err) } - govState := &vm.GovernanceState{StateDB: configState} - pool.setGovPrice(govState.MinGasPrice()) + pool.setGovPrice(gs.MinGasPrice()) } // validate the pool of pending transactions, this will remove diff --git a/core/vm/oracle.go b/core/vm/oracle.go index ee28343ae..8c8a3006c 100644 --- a/core/vm/oracle.go +++ b/core/vm/oracle.go @@ -45,7 +45,7 @@ type OracleContract interface { var OracleContracts = map[common.Address]func() OracleContract{ GovernanceContractAddress: func() OracleContract { return &GovernanceContract{ - coreDKGUtils: &defaultCoreDKGUtils{}, + coreDKGUtil: &defaultCoreDKGUtil{}, } }, RandomContractAddress: func() OracleContract { diff --git a/core/vm/oracle_contracts.go b/core/vm/oracle_contracts.go index e850811ff..a2c7d7814 100644 --- a/core/vm/oracle_contracts.go +++ b/core/vm/oracle_contracts.go @@ -29,6 +29,7 @@ import ( "github.com/tangerine-network/go-tangerine/accounts/abi" "github.com/tangerine-network/go-tangerine/common" + "github.com/tangerine-network/go-tangerine/core/state" "github.com/tangerine-network/go-tangerine/core/types" "github.com/tangerine-network/go-tangerine/crypto" "github.com/tangerine-network/go-tangerine/params" @@ -529,6 +530,16 @@ func (s *GovernanceState) QualifiedNodes() []*nodeInfo { } return nodes } +func (s *GovernanceState) FinedNodes() []*nodeInfo { + var nodes []*nodeInfo + for i := int64(0); i < int64(s.LenNodes().Uint64()); i++ { + node := s.Node(big.NewInt(i)) + if node.Fined.Cmp(big.NewInt(0)) > 0 { + nodes = append(nodes, node) + } + } + return nodes +} // mapping(address => uint256) public nodesOffsetByAddress; func (s *GovernanceState) NodesOffsetByAddress(addr common.Address) *big.Int { @@ -1274,47 +1285,19 @@ func (s *GovernanceState) emitDKGReset(round *big.Int, blockHeight *big.Int) { }) } -func getRoundState(evm *EVM, round *big.Int) (*GovernanceState, error) { - gs := &GovernanceState{evm.StateDB} - height := gs.RoundHeight(round).Uint64() - if round.Uint64() > dexCore.ConfigRoundShift { - if height == 0 { - return nil, errExecutionReverted - } - } - statedb, err := evm.StateAtNumber(height) - return &GovernanceState{statedb}, err -} - -func getConfigState(evm *EVM, round *big.Int) (*GovernanceState, error) { - configRound := big.NewInt(0) - if round.Uint64() > dexCore.ConfigRoundShift { - configRound = new(big.Int).Sub(round, big.NewInt(int64(dexCore.ConfigRoundShift))) - } - return getRoundState(evm, configRound) -} - -type coreDKGUtils interface { +type coreDKGUtil interface { NewGroupPublicKey(*GovernanceState, *big.Int, int) (tsigVerifierIntf, error) } type tsigVerifierIntf interface { VerifySignature(coreCommon.Hash, coreCrypto.Signature) bool } -// GovernanceContract represents the governance contract of DEXCON. -type GovernanceContract struct { - evm *EVM - state GovernanceState - contract *Contract - coreDKGUtils coreDKGUtils -} - -// defaultCoreDKGUtils implements coreDKGUtils. -type defaultCoreDKGUtils struct { +// defaultCoreDKGUtil implements coreDKGUtil. +type defaultCoreDKGUtil struct { gpk atomic.Value } -func (c *defaultCoreDKGUtils) NewGroupPublicKey( +func (c *defaultCoreDKGUtil) NewGroupPublicKey( state *GovernanceState, round *big.Int, threshold int) (tsigVerifierIntf, error) { var gpk *dkgTypes.GroupPublicKey var err error @@ -1337,6 +1320,23 @@ func (c *defaultCoreDKGUtils) NewGroupPublicKey( return gpk, nil } +// GovernanceContract represents the governance contract of DEXCON. +type GovernanceContract struct { + evm *EVM + state GovernanceState + contract *Contract + coreDKGUtil coreDKGUtil + util GovUtil +} + +func (g *GovernanceContract) StateAt(height uint64) (*state.StateDB, error) { + return g.evm.StateAtNumber(height) +} + +func (g *GovernanceContract) GetHeadGovState() (*GovernanceState, error) { + return &g.state, nil +} + func (g *GovernanceContract) Address() common.Address { return GovernanceContractAddress } @@ -1358,7 +1358,7 @@ func (g *GovernanceContract) useGas(gas uint64) ([]byte, error) { } func (g *GovernanceContract) configNotarySetSize(round *big.Int) *big.Int { - s, err := getConfigState(g.evm, round) + s, err := g.util.GetConfigState(round.Uint64()) if err != nil { return big.NewInt(0) } @@ -1366,34 +1366,15 @@ func (g *GovernanceContract) configNotarySetSize(round *big.Int) *big.Int { } func (g *GovernanceContract) getNotarySet(round *big.Int) map[coreTypes.NodeID]struct{} { - crsRound := g.state.CRSRound() - var crs common.Hash - cmp := round.Cmp(crsRound) - if round.Cmp(big.NewInt(int64(dexCore.DKGDelayRound))) <= 0 { - state, err := getRoundState(g.evm, big.NewInt(0)) - if err != nil { - return map[coreTypes.NodeID]struct{}{} - } - crs = state.CRS() - for i := uint64(0); i < round.Uint64(); i++ { - crs = crypto.Keccak256Hash(crs[:]) - } - } else if cmp > 0 { + crs := g.util.CRS(round.Uint64()) + if crs == (common.Hash{}) { return map[coreTypes.NodeID]struct{}{} - } else if cmp == 0 { - crs = g.state.CRS() - } else { - state, err := getRoundState(g.evm, round) - if err != nil { - return map[coreTypes.NodeID]struct{}{} - } - crs = state.CRS() } target := coreTypes.NewNotarySetTarget(coreCommon.Hash(crs)) ns := coreTypes.NewNodeSet() - state, err := getConfigState(g.evm, round) + state, err := g.util.GetConfigState(round.Uint64()) if err != nil { return map[coreTypes.NodeID]struct{}{} } @@ -2000,7 +1981,7 @@ func (g *GovernanceContract) proposeCRS(nextRound *big.Int, signedCRS []byte) ([ threshold := coreUtils.GetDKGThreshold(&coreTypes.Config{ NotarySetSize: uint32(g.state.NotarySetSize().Uint64())}) - dkgGPK, err := g.coreDKGUtils.NewGroupPublicKey(&g.state, nextRound, threshold) + dkgGPK, err := g.coreDKGUtil.NewGroupPublicKey(&g.state, nextRound, threshold) if err != nil { return nil, errExecutionReverted } @@ -2137,7 +2118,7 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) { new(big.Int).Mul(big.NewInt(100), resetCount)) roundHeight := g.state.RoundHeight(round) - gs, err := getConfigState(g.evm, round) + gs, err := g.util.GetConfigState(round.Uint64()) if err != nil { return nil, err } @@ -2167,7 +2148,7 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) { // If 2f + 1 of DKG set is finalized, check if DKG succeeded. if g.state.DKGFinalizedsCount().Uint64() >= threshold { - gpk, err := g.coreDKGUtils.NewGroupPublicKey(&g.state, nextRound, tsigThreshold) + gpk, err := g.coreDKGUtil.NewGroupPublicKey(&g.state, nextRound, tsigThreshold) if gpk, ok := gpk.(*dkgTypes.GroupPublicKey); ok { if len(gpk.QualifyNodeIDs) < coreUtils.GetDKGValidThreshold(&coreTypes.Config{ NotarySetSize: uint32(g.configNotarySetSize(nextRound).Uint64())}) { @@ -2191,7 +2172,7 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) { g.fineFailStopDKG(tsigThreshold) // Update CRS. - state, err := getRoundState(g.evm, round) + state, err := g.util.GetStateAtRound(round.Uint64()) if err != nil { return nil, errExecutionReverted } @@ -2208,7 +2189,7 @@ func (g *GovernanceContract) resetDKG(newSignedCRS []byte) ([]byte, error) { prevCRS = crypto.Keccak256Hash(prevCRS[:]) } - dkgGPK, err := g.coreDKGUtils.NewGroupPublicKey(state, round, + dkgGPK, err := g.coreDKGUtil.NewGroupPublicKey(state, round, coreUtils.GetDKGThreshold(&coreTypes.Config{ NotarySetSize: uint32(g.configNotarySetSize(round).Uint64())})) if err != nil { @@ -2251,6 +2232,7 @@ func (g *GovernanceContract) Run(evm *EVM, input []byte, contract *Contract) (re g.evm = evm g.state = GovernanceState{evm.StateDB} g.contract = contract + g.util = GovUtil{g} // Parse input. method, exists := GovernanceABI.Sig2Method[string(input[:4])] diff --git a/core/vm/oracle_contracts_test.go b/core/vm/oracle_contracts_test.go index ef6b9bde8..f0b045ac8 100644 --- a/core/vm/oracle_contracts_test.go +++ b/core/vm/oracle_contracts_test.go @@ -237,7 +237,7 @@ func (g *GovernanceContractTestSuite) SetupTest() { func (g *GovernanceContractTestSuite) TearDownTest() { OracleContracts[GovernanceContractAddress] = func() OracleContract { return &GovernanceContract{ - coreDKGUtils: &defaultCoreDKGUtils{}, + coreDKGUtil: &defaultCoreDKGUtil{}, } } } @@ -1176,7 +1176,7 @@ func (g *GovernanceContractTestSuite) TestResetDKG() { } OracleContracts[GovernanceContractAddress] = func() OracleContract { return &GovernanceContract{ - coreDKGUtils: mock, + coreDKGUtil: mock, } } diff --git a/core/vm/utils.go b/core/vm/utils.go new file mode 100644 index 000000000..0cc6343cc --- /dev/null +++ b/core/vm/utils.go @@ -0,0 +1,92 @@ +package vm + +import ( + "errors" + "math/big" + + "github.com/tangerine-network/go-tangerine/common" + "github.com/tangerine-network/go-tangerine/core/state" + "github.com/tangerine-network/go-tangerine/crypto" + "github.com/tangerine-network/go-tangerine/log" + dexCore "github.com/tangerine-network/tangerine-consensus/core" +) + +type GovUtilInterface interface { + GetHeadGovState() (*GovernanceState, error) + StateAt(height uint64) (*state.StateDB, error) +} + +type GovUtil struct { + Intf GovUtilInterface +} + +func (g GovUtil) GetRoundHeight(round uint64) uint64 { + gs, err := g.Intf.GetHeadGovState() + if err != nil { + return 0 + } + return gs.RoundHeight(big.NewInt(int64(round))).Uint64() +} + +func (g GovUtil) GetStateAtRound(round uint64) (*GovernanceState, error) { + height := g.GetRoundHeight(round) + + if round != 0 && height == 0 { + log.Error("Governance state incorrect", "round", round, "got height", height) + return nil, errors.New("incorrect governance state") + } + + s, err := g.Intf.StateAt(height) + if err != nil { + return nil, err + } + return &GovernanceState{StateDB: s}, nil +} + +func (g GovUtil) GetConfigState(round uint64) (*GovernanceState, error) { + if round < dexCore.ConfigRoundShift { + round = 0 + } else { + round -= dexCore.ConfigRoundShift + } + return g.GetStateAtRound(round) +} + +func (g *GovUtil) CRSRound() uint64 { + gs, err := g.Intf.GetHeadGovState() + if err != nil { + return 0 + } + return gs.CRSRound().Uint64() +} + +func (g GovUtil) CRS(round uint64) common.Hash { + if round <= dexCore.DKGDelayRound { + s, err := g.GetStateAtRound(0) + if err != nil { + return common.Hash{} + } + crs := s.CRS() + for i := uint64(0); i < round; i++ { + crs = crypto.Keccak256Hash(crs[:]) + } + return crs + } + if round > g.CRSRound() { + return common.Hash{} + } + var s *GovernanceState + var err error + if round == g.CRSRound() { + s, err = g.Intf.GetHeadGovState() + if err != nil { + return common.Hash{} + } + } else { + s, err = g.GetStateAtRound(round) + if err != nil { + return common.Hash{} + } + } + return s.CRS() +} diff --git a/dex/api_backend.go b/dex/api_backend.go index 39c34550b..31b5a650e 100644 --- a/dex/api_backend.go +++ b/dex/api_backend.go @@ -187,7 +187,11 @@ func (b *DexAPIBackend) ProtocolVersion() int { } func (b *DexAPIBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.dex.governance.MinGasPrice(b.dex.blockchain.CurrentBlock().Round()), nil + gs, err := b.dex.governance.GetConfigState(b.dex.blockchain.CurrentBlock().Round()) + if err != nil { + return nil, err + } + return gs.MinGasPrice(), nil } func (b *DexAPIBackend) ChainDb() ethdb.Database { diff --git a/dex/app.go b/dex/app.go index fb8ffa46d..10f977c2f 100644 --- a/dex/app.go +++ b/dex/app.go @@ -102,9 +102,14 @@ func (d *DexconApp) validateNonce(txs types.Transactions) (map[common.Address]ui // validateGasPrice checks if no gas price is lower than minGasPrice defined in // governance contract. func (d *DexconApp) validateGasPrice(txs types.Transactions, round uint64) bool { - minGasPrice := d.gov.MinGasPrice(round) + config, err := d.gov.RawConfiguration(round) + if err != nil { + log.Error("Failed to get configuration", "err", err) + return false + } + for _, tx := range txs { - if minGasPrice.Cmp(tx.GasPrice()) > 0 { + if config.MinGasPrice.Cmp(tx.GasPrice()) > 0 { return false } } @@ -180,8 +185,12 @@ func (d *DexconApp) preparePayload(ctx context.Context, position coreTypes.Posit return } - blockGasLimit := new(big.Int).SetUint64(d.gov.DexconConfiguration(position.Round).BlockGasLimit) - minGasPrice := d.gov.DexconConfiguration(position.Round).MinGasPrice + config, err := d.gov.RawConfiguration(position.Round) + if err != nil { + return + } + + blockGasLimit := new(big.Int).SetUint64(config.BlockGasLimit) blockGasUsed := new(big.Int) allTxs := make([]*types.Transaction, 0, 10000) @@ -217,8 +226,8 @@ addressMap: // Warning: the pending tx will also affect by syncing, so startIndex maybe negative for i := startIndex; i >= 0 && i < len(txs); i++ { tx := txs[i] - if minGasPrice.Cmp(tx.GasPrice()) > 0 { - log.Error("Invalid gas price minGas(%v) > get(%v)", minGasPrice, tx.GasPrice()) + if config.MinGasPrice.Cmp(tx.GasPrice()) > 0 { + log.Error("Invalid gas price minGas(%v) > get(%v)", config.MinGasPrice, tx.GasPrice()) break } @@ -372,8 +381,14 @@ func (d *DexconApp) VerifyBlock(block *coreTypes.Block) coreTypes.BlockVerifySta } } + config, err := d.gov.RawConfiguration(block.Position.Round) + if err != nil { + log.Error("Failed to get raw configuration", "err", err) + return coreTypes.VerifyRetryLater + } + // Validate if balance is enough for TXs in this block. - blockGasLimit := new(big.Int).SetUint64(d.gov.DexconConfiguration(block.Position.Round).BlockGasLimit) + blockGasLimit := new(big.Int).SetUint64(config.BlockGasLimit) blockGasUsed := new(big.Int) for _, tx := range transactions { @@ -436,7 +451,10 @@ func (d *DexconApp) BlockDelivered( var owner common.Address if !block.IsEmpty() { - gs := d.gov.GetStateForConfigAtRound(block.Position.Round) + gs, err := d.gov.GetConfigState(block.Position.Round) + if err != nil { + panic(err) + } node, err := gs.GetNodeByID(block.ProposerID) if err != nil { panic(err) @@ -444,11 +462,16 @@ func (d *DexconApp) BlockDelivered( owner = node.Owner } + config, err := d.gov.RawConfiguration(block.Position.Round) + if err != nil { + panic(err) + } + newBlock := types.NewBlock(&types.Header{ Number: new(big.Int).SetUint64(block.Position.Height), Time: uint64(block.Timestamp.UnixNano() / 1000000), Coinbase: owner, - GasLimit: d.gov.DexconConfiguration(block.Position.Round).BlockGasLimit, + GasLimit: config.BlockGasLimit, Difficulty: big.NewInt(1), Round: block.Position.Round, DexconMeta: dexconMeta, diff --git a/dex/app_test.go b/dex/app_test.go index 22b85b766..db9feb228 100644 --- a/dex/app_test.go +++ b/dex/app_test.go @@ -471,7 +471,12 @@ func (t *ppBlockLimitTester) ValidateResults(results []reflect.Value) error { } app := t.App.(*DexconApp) - blockLimit := app.gov.DexconConfiguration(t.round).BlockGasLimit + config, err := app.gov.RawConfiguration(t.round) + if err != nil { + return fmt.Errorf("unable to get raw configuration: %v", err) + } + + blockLimit := config.BlockGasLimit totalGas := uint64(0) for _, tx := range txs { totalGas += tx.Gas() @@ -2161,7 +2166,11 @@ func (f *TxFactory) Run() { blockchain := f.App.(*DexconApp).blockchain txPool := f.App.(*DexconApp).txPool for { - gasPrice := f.App.(*DexconApp).gov.GetHeadState().MinGasPrice() + hs, err := f.App.(*DexconApp).gov.GetHeadGovState() + if err != nil { + panic(err) + } + gasPrice := hs.MinGasPrice() for i, key := range f.keys { go func(at int, nonce uint64, key *ecdsa.PrivateKey) { f.stopTimeMu.RLock() diff --git a/dex/downloader/testchain_test.go b/dex/downloader/testchain_test.go index 9595dc212..32e6110c4 100644 --- a/dex/downloader/testchain_test.go +++ b/dex/downloader/testchain_test.go @@ -332,15 +332,15 @@ func (g *govStateFetcher) SnapshotRound(round uint64, root common.Hash) { g.rootByRound[round] = root } -func (g *govStateFetcher) GetStateForConfigAtRound(round uint64) *vm.GovernanceState { +func (g *govStateFetcher) GetConfigState(round uint64) (*vm.GovernanceState, error) { if root, ok := g.rootByRound[round]; ok { s, err := state.New(root, g.db) if err != nil { - panic(err) + return nil, err } - return &vm.GovernanceState{s} + return &vm.GovernanceState{s}, nil } - return nil + return nil, nil } func (g *govStateFetcher) DKGSetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error) { diff --git a/dex/governance.go b/dex/governance.go index 1ca94e1dc..50de31a0e 100644 --- a/dex/governance.go +++ b/dex/governance.go @@ -58,9 +58,13 @@ func NewDexconGovernance(backend *DexAPIBackend, chainConfig *params.ChainConfig return g } -// DexconConfiguration return raw config in state. -func (d *DexconGovernance) DexconConfiguration(round uint64) *params.DexconConfig { - return d.GetStateForConfigAtRound(round).Configuration() +// RawConfiguration return raw config in state. +func (d *DexconGovernance) RawConfiguration(round uint64) (*params.DexconConfig, error) { + gs, err := d.GetConfigState(round) + if err != nil { + return nil, err + } + return gs.Configuration(), nil } func (d *DexconGovernance) sendGovTx(ctx context.Context, data []byte) error { |