diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index b03c636d..77450a71 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -568,14 +568,20 @@ func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header // Finalize implements consensus.Engine, ensuring no uncles are set, nor block // rewards given. -func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error { // No block rewards in PoA, so the state remains as is and uncles are dropped header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = types.CalcUncleHash(nil) return nil } +// Filter out system transactions from common transactions +// returns common transactions, system transactions and system transaction message provider +func (c *Clique) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions, + func(*types.Transaction, *big.Int) types.Message, error) { + return block.Transactions(), nil, nil, nil +} + // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, // nor block rewards given, and returns the final block. func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, diff --git a/consensus/consensus.go b/consensus/consensus.go index 3b83923c..bac9e2de 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -96,13 +96,17 @@ type Engine interface { // rules of a particular engine. The changes are executed inline. Prepare(chain ChainHeaderReader, header *types.Header) error + // Filter out system transactions from common transactions + // returns common transactions, system transactions and system transaction message provider + BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions, + func(*types.Transaction, *big.Int) types.Message, error) + // Finalize runs any post-transaction state modifications (e.g. block rewards) // but does not assemble the block. // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error + Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards) and assembles the final block. @@ -165,12 +169,6 @@ type HotStuff interface { // FillHeader fulfill the header with extra which contains epoch change info FillHeader(state *state.StateDB, header *types.Header) error - - // IsSystemCall return method id and true if the tx is an system transaction - IsSystemTransaction(tx *types.Transaction, header *types.Header) (string, bool) - - // HasSystemTxHook return true if systemTxHook is not nil - HasSystemTxHook() bool } // Handler should be implemented is the consensus needs to handle and send peer's message diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 6b0d72c3..daa450b9 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -586,20 +586,26 @@ func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.H // Finalize implements consensus.Engine, accumulating the block and uncle rewards, // setting the final state on the header -func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error { +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error { // Accumulate any block and uncle rewards and commit the final state root accumulateRewards(chain.Config(), state, header, uncles) header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) return nil } +// Filter out system transactions from common transactions +// returns common transactions, system transactions and system transaction message provider +func (ethash *Ethash) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions, + func(*types.Transaction, *big.Int) types.Message, error) { + return block.Transactions(), nil, nil, nil +} + // FinalizeAndAssemble implements consensus.Engine, accumulating the block and // uncle rewards, setting the final state and assembling the block. func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, []*types.Receipt, error) { - ethash.Finalize(chain, header, state, &txs, uncles, nil, nil, nil) + ethash.Finalize(chain, header, state, uncles) // Header seems complete, assemble into a block and return return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), receipts, nil diff --git a/consensus/hotstuff/backend/backend.go b/consensus/hotstuff/backend/backend.go index beae5e38..79192b61 100644 --- a/consensus/hotstuff/backend/backend.go +++ b/consensus/hotstuff/backend/backend.go @@ -69,7 +69,6 @@ type backend struct { currentBlock func() *types.Block getBlockByHash func(hash common.Hash) *types.Block hasBadBlock func(db ethdb.Reader, hash common.Hash) bool - systemTxHook SystemTxFn } func New(chainConfig *params.ChainConfig, config *hotstuff.Config, privateKey *ecdsa.PrivateKey, db ethdb.Database, mock bool) *backend { @@ -92,10 +91,8 @@ func New(chainConfig *params.ChainConfig, config *hotstuff.Config, privateKey *e } if mock { - backend.systemTxHook = nil backend.core = core.New(backend, config, signer, db, nil) } else { - backend.systemTxHook = backend.executeSystemTxs backend.core = core.New(backend, config, signer, db, backend.CheckPoint) } diff --git a/consensus/hotstuff/backend/economic.go b/consensus/hotstuff/backend/economic.go deleted file mode 100644 index f6582a9d..00000000 --- a/consensus/hotstuff/backend/economic.go +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2021 The Zion Authors - * This file is part of The Zion library. - * - * The Zion is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Zion is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with The Zion. If not, see . - */ - -package backend - -import ( - "fmt" - "math/big" - - "github.com/ethereum/go-ethereum/contracts/native/economic" - "github.com/ethereum/go-ethereum/contracts/native/utils" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/log" -) - -// reward distribute native token to `governance contract` and `reward pool` -func (s *backend) reward(state *state.StateDB, height *big.Int) error { - // genesis block do not need to distribute reward - if height.Uint64() == 0 { - return nil - } - - // get reward info list from native contract of `economic` - list, err := s.getRewardList(state, height) - if err != nil { - return err - } - - // add balance to related addresses - var sRwd string - for _, v := range list { - state.AddBalance(v.Address, v.Amount) - sRwd += fmt.Sprintf("address: %s, amount %v;", v.Address.Hex(), v.Amount) - } - log.Debug("reward", "num", height, "list", sRwd) - - return nil -} - -// prepare for slashing... -// todo(fuk): slash for governance -func (s *backend) slash(ctx *systemTxContext) error { - return s.executeTransaction(ctx, contractAddr, nil) -} - -func (s *backend) getRewardList(state *state.StateDB, height *big.Int) ([]*economic.RewardAmount, error) { - caller := s.signer.Address() - ref := s.getSystemCaller(state, height) - payload, err := new(economic.MethodRewardInput).Encode() - if err != nil { - return nil, fmt.Errorf("encode reward input failed: %v", err) - } - enc, _, err := ref.NativeCall(caller, utils.EconomicContractAddress, payload) - if err != nil { - return nil, fmt.Errorf("reward native call failed: %v", err) - } - output := new(economic.MethodRewardOutput) - if err := output.Decode(enc); err != nil { - return nil, fmt.Errorf("reward output decode failed: %v", err) - } - return output.List, nil -} diff --git a/consensus/hotstuff/backend/engine.go b/consensus/hotstuff/backend/engine.go index 3b4b7566..c9e6cd3b 100644 --- a/consensus/hotstuff/backend/engine.go +++ b/consensus/hotstuff/backend/engine.go @@ -24,11 +24,16 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/hotstuff" "github.com/ethereum/go-ethereum/consensus/misc" + "github.com/ethereum/go-ethereum/contracts/native/governance" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" @@ -109,15 +114,48 @@ func (s *backend) Prepare(chain consensus.ChainHeaderReader, header *types.Heade return nil } -func (s *backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, - uncles []*types.Header, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64) error { +// Filter out system transactions from common transactions +// returns common transactions, system transactions and system transaction message provider +func (s *backend) BlockTransactions(block *types.Block, state *state.StateDB) (types.Transactions, types.Transactions, + func(*types.Transaction, *big.Int) types.Message, error) { + systemTransactions, err := governance.AssembleSystemTransactions(state, block.NumberU64()) + if err != nil { + return nil, nil, nil, err + } + allTransactions := block.Transactions() + commonTransactionCount := len(allTransactions) - len(systemTransactions) + if commonTransactionCount < 0 { + return nil, nil, nil, fmt.Errorf("missing required system transactions, count %v", len(systemTransactions)) + } - if s.HasSystemTxHook() { - if err := s.executeSystemTxs(chain, header, state, txs, receipts, systemTxs, usedGas, false); err != nil { - return err + signer := types.MakeSigner(s.chainConfig, block.Number()) + for i, tx := range systemTransactions { + includedTx := allTransactions[commonTransactionCount + i] + if includedTx.Hash() != tx.Hash() { + return nil, nil, nil, fmt.Errorf("unexpected system tx hash detected, tx index %v, hash %s, expected: %s", commonTransactionCount + i, includedTx.Hash(), tx.Hash()) + } + from, err := signer.Sender(includedTx) + if err != nil { + return nil, nil, nil, fmt.Errorf("check system tx signature failed, %w", err) + } + if from != block.Coinbase() { + return nil, nil, nil, fmt.Errorf("check system tx signature failed, wrong signer %s", from) } } + return allTransactions[:commonTransactionCount], systemTransactions, s.asSystemMessage, nil +} +// Change message from as valid system transaction sender +func(s *backend) asSystemMessage(tx *types.Transaction, baseFee *big.Int) types.Message { + gasPrice := new(big.Int).Set(tx.GasPrice()) + if baseFee != nil { + gasPrice = math.BigMin(gasPrice.Add(tx.GasTipCap(), baseFee), tx.GasFeeCap()) + } + return types.NewMessage(utils.SystemTxSender, tx.To(), tx.Nonce(), tx.Value(), tx.Gas(), gasPrice, + new(big.Int).Set(tx.GasFeeCap()), new(big.Int).Set(tx.GasTipCap()), tx.Data(), tx.AccessList(), true) +} + +func (s *backend) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, uncles []*types.Header) error { header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) header.UncleHash = nilUncleHash return nil @@ -134,10 +172,32 @@ func (s *backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header receipts = make([]*types.Receipt, 0) } - if s.HasSystemTxHook() { - if err := s.executeSystemTxs(chain, header, state, &txs, &receipts, nil, &header.GasUsed, true); err != nil { + systemTransantions, err := governance.AssembleSystemTransactions(state, header.Number.Uint64()) + if err != nil { + return nil, nil, err + } + + for _, tx := range systemTransantions { + chainContext := chainContext{Chain: chain, engine: s} + gp := new(core.GasPool).AddGas(header.GasLimit) + if err := gp.SubGas(header.GasUsed); err != nil { return nil, nil, err } + state.Prepare(tx.Hash(), common.Hash{}, len(txs)) + receipt, err := core.ApplyTransactionWithCustomMessageProvider(s.asSystemMessage, s.chainConfig, chainContext, nil, gp, state, header, tx, &header.GasUsed, vm.Config{}) + if err != nil { + return nil, nil, err + } + if receipt.Status != types.ReceiptStatusSuccessful { + return nil, nil, fmt.Errorf("unexpected reverted system transactions tx %d [%s], status %v", len(txs), tx.Hash(), receipt.Status) + } + signer := types.MakeSigner(s.chainConfig, header.Number) + tx, err = s.signer.SignTx(tx, signer) + if err != nil { + return nil, nil, err + } + txs = append(txs, tx) + receipts = append(receipts, receipt) } // Assemble and return the final block for sealing @@ -145,42 +205,6 @@ func (s *backend) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header return block, receipts, nil } -type SystemTxFn func(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, mining bool) error - -func (s *backend) HasSystemTxHook() bool { - return s.systemTxHook != nil -} - -// executeSystemTxs governance tx execution do not allow failure, the consensus will halt if tx failed and return error. -func (s *backend) executeSystemTxs(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, - txs *[]*types.Transaction, receipts *[]*types.Receipt, systemTxs *[]*types.Transaction, usedGas *uint64, mining bool) error { - - // genesis block DONT need to execute system transaction - if header.Number.Uint64() == 0 { - return nil - } - - if err := s.reward(state, header.Number); err != nil { - return err - } - - ctx := &systemTxContext{ - chain: chain, - state: state, - header: header, - chainCtx: chainContext{Chain: chain, engine: s}, - txs: txs, - sysTxs: systemTxs, - receipts: receipts, - usedGas: usedGas, - mining: mining, - } - if err := s.execEndBlock(ctx); err != nil { - return err - } - return s.execEpochChange(state, header, ctx) -} - func (s *backend) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) (err error) { // update the block header timestamp and signature and propose the block to core engine header := block.Header() diff --git a/consensus/hotstuff/backend/governance.go b/consensus/hotstuff/backend/governance.go index 6d26e1ee..14c32dec 100644 --- a/consensus/hotstuff/backend/governance.go +++ b/consensus/hotstuff/backend/governance.go @@ -22,7 +22,6 @@ import ( "fmt" "sync/atomic" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/hotstuff" nm "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" @@ -41,7 +40,7 @@ var ( // * governance epoch changed on chain, use the new validators for an new epoch start header. // * governance epoch not changed, only set the old epoch start height in header. func (s *backend) FillHeader(state *state.StateDB, header *types.Header) error { - epoch, err := s.getGovernanceInfo(state) + epoch, err := nm.GetCurrentEpochInfoFromDB(state) if err != nil { return err } @@ -73,7 +72,7 @@ func (s *backend) CheckPoint(height uint64) (uint64, bool) { log.Warn("CheckPoint", "get state failed", err) return 0, false } - epoch, err := s.getGovernanceInfo(state) + epoch, err := nm.GetCurrentEpochInfoFromDB(state) if err != nil { log.Warn("CheckPoint", "get current epoch info, height", height, "err", err) return 0, false @@ -112,76 +111,6 @@ func (s *backend) Validators(height uint64, mining bool) (hotstuff.ValidatorSet, return vals, nil } -// IsSystemTransaction used by state processor while sync block. -func (s *backend) IsSystemTransaction(tx *types.Transaction, header *types.Header) (string, bool) { - // consider that tx is deploy transaction, so the tx.to will be nil - if tx == nil || len(tx.Data()) < 4 || tx.To() == nil { - return "", false - } - if *tx.To() != contractAddr { - return "", false - } - id := common.Bytes2Hex(tx.Data()[:4]) - if _, exist := specMethod[id]; !exist { - return id, false - } - - signer := types.MakeSigner(s.chainConfig, header.Number) - addr, err := signer.Sender(tx) - if err != nil { - return id, false - } - if header.Coinbase != addr { - return id, false - } else { - return id, true - } -} - -// header height in front of state height -func (s *backend) execEpochChange(state *state.StateDB, header *types.Header, ctx *systemTxContext) error { - - epoch, err := s.getGovernanceInfo(state) - if err != nil { - return err - } - - end := epoch.EndHeight.Uint64() - height := header.Number.Uint64() - if height != end-1 { - return nil - } - - payload, err := new(nm.ChangeEpochParam).Encode() - if err != nil { - return err - } - if err := s.executeTransaction(ctx, contractAddr, payload); err != nil { - return err - } - - log.Info("Execute governance EpochChange", "end", end, "current", height) - return nil -} - -// getGovernanceInfo call governance contract method and retrieve related info. -func (s *backend) getGovernanceInfo(state *state.StateDB) (*nm.EpochInfo, error) { - epoch, err := nm.GetCurrentEpochInfoFromDB(state) - if err != nil { - return nil, err - } - return epoch, nil -} - -// execEndBlock execute governance contract method of `EndBlock` -func (s *backend) execEndBlock(ctx *systemTxContext) error { - payload, err := new(nm.EndBlockParam).Encode() - if err != nil { - return err - } - return s.executeTransaction(ctx, contractAddr, payload) -} - // getValidatorsByHeader check if current header height is an new epoch start and retrieve the validators. func (s *backend) getValidatorsByHeader(header, parent *types.Header, chain consensus.ChainHeaderReader) ( bool, hotstuff.ValidatorSet, error) { diff --git a/consensus/hotstuff/backend/utils.go b/consensus/hotstuff/backend/utils.go index 16c56d6a..b371f291 100644 --- a/consensus/hotstuff/backend/utils.go +++ b/consensus/hotstuff/backend/utils.go @@ -19,45 +19,17 @@ package backend import ( - "bytes" - "encoding/hex" - "fmt" - "math" - "math/big" - "sync" - - "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/hotstuff" "github.com/ethereum/go-ethereum/consensus/hotstuff/validator" - "github.com/ethereum/go-ethereum/contracts/native" - "github.com/ethereum/go-ethereum/contracts/native/utils" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) // =========================== utility function ========================== -// callmsg implements core.Message to allow passing it as a transaction simulator. -type callmsg struct { - ethereum.CallMsg -} - -func (m callmsg) From() common.Address { return m.CallMsg.From } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.CallMsg.To } -func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callmsg) Gas() uint64 { return m.CallMsg.Gas } -func (m callmsg) Value() *big.Int { return m.CallMsg.Value } -func (m callmsg) Data() []byte { return m.CallMsg.Data } - // chain context type chainContext struct { Chain consensus.ChainHeaderReader @@ -73,204 +45,18 @@ func (c chainContext) GetHeader(hash common.Hash, number uint64) *types.Header { return c.Chain.GetHeader(hash, number) } -const ( - systemGas = math.MaxUint64 / 2 // system tx will be executed in evm, and gas calculating is needed. - systemGasPrice = int64(0) // consensus txs do not need to participate in gas price bidding -) - -// getSystemMessage assemble system calling fields -func (s *backend) getSystemMessage(toAddress common.Address, data []byte, value *big.Int) callmsg { - return callmsg{ - ethereum.CallMsg{ - From: utils.SystemTxSender, - Gas: systemGas, - GasPrice: big.NewInt(systemGasPrice), - Value: value, - To: &toAddress, - Data: data, - }, - } -} - -// getSystemCaller use fixed systemCaller as contract caller, and tx hash is useless in contract call. -func (s *backend) getSystemCaller(state *state.StateDB, height *big.Int) *native.ContractRef { - caller := utils.SystemTxSender - hash := common.EmptyHash - return native.NewContractRef(state, caller, caller, height, hash, systemGas, nil) -} - -// applyTransaction execute transaction without miner worker, and only succeed tx will be packed in block. -func (s *backend) applyTransaction( - chain consensus.ChainHeaderReader, - msg callmsg, - state *state.StateDB, - header *types.Header, - chainContext core.ChainContext, - commonTxs *[]*types.Transaction, receipts *[]*types.Receipt, - sysTxs *[]*types.Transaction, usedGas *uint64, mining bool, -) (err error) { - - // check msg sender - if msg.From() != utils.SystemTxSender { - return fmt.Errorf("system tx sender invalid") - } - - nonce := state.GetNonce(msg.From()) - expectedTx := types.NewTransaction(nonce, *msg.To(), msg.Value(), msg.Gas(), msg.GasPrice(), msg.Data()) - signer := types.MakeSigner(chain.Config(), header.Number) - - // miner worker execute `finalizeAndAssemble` in which the param of `mining` is true, it's denote - // that this tx comes from miner, and `validator` send governance tx in the same nonce is forbidden. - // the sender of system tx is an unusual address, let the miner `signTx` to keep the signature in tx.Data - // which denote that the system tx is mined by some one validator. this tx and the `actual` tx which - // others sync node received should be compared and ensure that they are extreme the same. - if mining { - expectedTx, err = s.signer.SignTx(expectedTx, signer) - if err != nil { - return err - } - } else { - // system tx CAN'T be nil or empty - if sysTxs == nil || len(*sysTxs) == 0 || (*sysTxs)[0] == nil { - return fmt.Errorf("supposed to get a actual transaction, but get none") - } - - // check tx hash - actualTx := (*sysTxs)[0] - if expectedHash := signer.Hash(expectedTx); !bytes.Equal(signer.Hash(actualTx).Bytes(), expectedHash.Bytes()) { - return fmt.Errorf("expected tx hash %v, nonce %d, to %s, value %s, gas %d, gasPrice %s, data %s;"+ - "get tx hash %v, nonce %d, to %s, value %s, gas %d, gasPrice %s, data %s", - expectedHash.String(), - expectedTx.Nonce(), - expectedTx.To().String(), - expectedTx.Value().String(), - expectedTx.Gas(), - expectedTx.GasPrice().String(), - hex.EncodeToString(expectedTx.Data()), - actualTx.Hash().String(), - actualTx.Nonce(), - actualTx.To().String(), - actualTx.Value().String(), - actualTx.Gas(), - actualTx.GasPrice().String(), - hex.EncodeToString(actualTx.Data()), - ) - } - - // tx signature can be recovered and the sender should be equal to block `coinbase` - sender, err := signer.Sender(actualTx) - if err != nil { - return fmt.Errorf("recover system tx sender failed, err: %v", err) - } - if sender != header.Coinbase { - return fmt.Errorf("supposed to miner %s but got %s", header.Coinbase.Hex(), sender.Hex()) - } - - // reset tx and shift system tx list to next - expectedTx = actualTx - *sysTxs = (*sysTxs)[1:] - } - - // execute system tx and get the receipt - state.Prepare(expectedTx.Hash(), common.Hash{}, len(*commonTxs)) - gasUsed, err := applyMessage(msg, state, header, chain.Config(), chainContext) - if err != nil { - return err - } - *commonTxs = append(*commonTxs, expectedTx) - var root []byte - if chain.Config().IsByzantium(header.Number) { - state.Finalise(true) - } else { - root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)).Bytes() - } - *usedGas += gasUsed - receipt := types.NewReceipt(root, false, *usedGas) - receipt.TxHash = expectedTx.Hash() - receipt.GasUsed = gasUsed - - // set the receipt logs and create a bloom for filtering - receipt.Logs = state.GetLogs(expectedTx.Hash()) - receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) - receipt.BlockHash = state.BlockHash() - receipt.BlockNumber = header.Number - receipt.TransactionIndex = uint(state.TxIndex()) - *receipts = append(*receipts, receipt) - state.SetNonce(msg.From(), nonce+1) - return nil -} - -// applyMessage -func applyMessage( - msg callmsg, - state *state.StateDB, - header *types.Header, - chainConfig *params.ChainConfig, - chainContext core.ChainContext, -) (uint64, error) { - // Create a new context to be used in the EVM environment - context := core.NewEVMBlockContext(header, chainContext, nil) - // Create a new environment which holds all relevant information - // about the transaction and calling mechanisms. - vmenv := vm.NewEVM(context, vm.TxContext{Origin: msg.From(), GasPrice: big.NewInt(0)}, state, chainConfig, vm.Config{}) - // Apply the transaction to the current state (included in the env) - ret, returnGas, err := vmenv.Call( - vm.AccountRef(msg.From()), - *msg.To(), - msg.Data(), - msg.Gas(), - msg.Value(), - ) - if err != nil { - log.Error("apply message failed", "msg", string(ret), "err", err) - } - return msg.Gas() - returnGas, err -} - func packBlock(state *state.StateDB, chain consensus.ChainHeaderReader, header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) *types.Block { - - var ( - block *types.Block - root common.Hash - ) - // perform root calculation and block reorganization at the same time which with a large number of memory copy. // and reset the header root after actions done. - wg := sync.WaitGroup{} - wg.Add(2) - go func() { - root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - wg.Done() - }() - go func() { - // the header uncle hash will be settle as EmptyUncleHash which as the same of `nilUncleHash` - block = types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) - wg.Done() - }() - wg.Wait() + root := state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) + // the header uncle hash will be settle as EmptyUncleHash which as the same of `nilUncleHash` + block := types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)) block.SetRoot(root) return block } -type systemTxContext struct { - chain consensus.ChainHeaderReader - state *state.StateDB - header *types.Header - chainCtx core.ChainContext - txs *[]*types.Transaction - sysTxs *[]*types.Transaction - receipts *[]*types.Receipt - usedGas *uint64 - mining bool -} - -func (s *backend) executeTransaction(ctx *systemTxContext, contract common.Address, payload []byte) error { - msg := s.getSystemMessage(contract, payload, common.Big0) - return s.applyTransaction(ctx.chain, msg, ctx.state, ctx.header, ctx.chainCtx, ctx.txs, ctx.receipts, ctx.sysTxs, ctx.usedGas, ctx.mining) -} - func NewDefaultValSet(list []common.Address) hotstuff.ValidatorSet { return validator.NewSet(list, hotstuff.RoundRobin) } diff --git a/contracts/native/cross_chain_manager/entrance_test.go b/contracts/native/cross_chain_manager/entrance_test.go index 58a65256..5d1498b1 100644 --- a/contracts/native/cross_chain_manager/entrance_test.go +++ b/contracts/native/cross_chain_manager/entrance_test.go @@ -31,6 +31,7 @@ import ( scom "github.com/ethereum/go-ethereum/contracts/native/cross_chain_manager/common" "github.com/ethereum/go-ethereum/contracts/native/go_abi/cross_chain_manager_abi" "github.com/ethereum/go-ethereum/contracts/native/go_abi/side_chain_manager_abi" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" "github.com/ethereum/go-ethereum/contracts/native/governance/side_chain_manager" "github.com/ethereum/go-ethereum/contracts/native/info_sync" @@ -58,7 +59,7 @@ func init() { InitCrossChainManager() info_sync.InitInfoSync() - node_manager.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) + community.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) node_manager.StoreGenesisEpoch(sdb, signers, signers) node_manager.StoreGenesisGlobalConfig(sdb) diff --git a/contracts/native/economic/economic.go b/contracts/native/economic/economic.go index 24de804a..1fc72299 100644 --- a/contracts/native/economic/economic.go +++ b/contracts/native/economic/economic.go @@ -24,8 +24,9 @@ import ( "github.com/ethereum/go-ethereum/contracts/native" . "github.com/ethereum/go-ethereum/contracts/native/go_abi/economic_abi" - nm "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" ) @@ -70,24 +71,23 @@ func TotalSupply(s *native.NativeContract) ([]byte, error) { return utils.PackOutputs(ABI, MethodTotalSupply, supply) } -func Reward(s *native.NativeContract) ([]byte, error) { - - community, err := nm.GetCommunityInfoFromDB(s.StateDB()) +func getBlockRewardList(s *native.NativeContract) ([]*RewardAmount, error) { + community, err := community.GetCommunityInfoFromDB(s.StateDB()) if err != nil { return nil, fmt.Errorf("GetCommunityInfo failed, err: %v", err) } // allow empty address as reward pool poolAddr := community.CommunityAddress - rewardPerBlock := nm.NewDecFromBigInt(RewardPerBlock) - rewardFactor := nm.NewDecFromBigInt(community.CommunityRate) + rewardPerBlock := utils.NewDecFromBigInt(RewardPerBlock) + rewardFactor := utils.NewDecFromBigInt(community.CommunityRate) poolRwdAmt, err := rewardPerBlock.MulWithPercentDecimal(rewardFactor) if err != nil { - return nil, fmt.Errorf("Calculate pool reward amount failed, err: %v ", err) + return nil, fmt.Errorf("calculate pool reward amount failed, err: %v ", err) } stakingRwdAmt, err := rewardPerBlock.Sub(poolRwdAmt) if err != nil { - return nil, fmt.Errorf("Calculate staking reward amount, failed, err: %v ", err) + return nil, fmt.Errorf("calculate staking reward amount, failed, err: %v ", err) } poolRwd := &RewardAmount{ @@ -99,7 +99,39 @@ func Reward(s *native.NativeContract) ([]byte, error) { Amount: stakingRwdAmt.BigInt(), } + return []*RewardAmount{poolRwd, stakingRwd}, nil +} + +func Reward(s *native.NativeContract) ([]byte, error) { + list, err := getBlockRewardList(s) + if err != nil { + return nil, err + } output := new(MethodRewardOutput) - output.List = []*RewardAmount{poolRwd, stakingRwd} + output.List = list return output.Encode() } + +func GenerateBlockReward(s *native.NativeContract) error { + height := s.ContractRef().BlockHeight() + // genesis block do not need to distribute reward + if height.Uint64() == 0 { + return nil + } + + // get reward info list from native contract of `economic` + list, err := getBlockRewardList(s) + if err != nil { + return err + } + + // add balance to related addresses + var sRwd string + for _, v := range list { + s.StateDB().AddBalance(v.Address, v.Amount) + sRwd += fmt.Sprintf("address: %s, amount %v;", v.Address.Hex(), v.Amount) + } + log.Debug("reward", "num", height, "list", sRwd) + + return nil +} diff --git a/contracts/native/economic/economic_test.go b/contracts/native/economic/economic_test.go index 320e5c7b..103e0895 100644 --- a/contracts/native/economic/economic_test.go +++ b/contracts/native/economic/economic_test.go @@ -28,7 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" . "github.com/ethereum/go-ethereum/contracts/native/go_abi/economic_abi" - nm "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" @@ -37,7 +37,6 @@ import ( func TestMain(m *testing.M) { InitABI() - nm.InitNodeManager() InitEconomic() os.Exit(m.Run()) } @@ -120,7 +119,7 @@ func TestReward(t *testing.T) { payload, _ := new(MethodRewardInput).Encode() raw, err := native.TestNativeCall(t, this, name, payload, tc.height, func(state *state.StateDB) { - nm.StoreCommunityInfo(state, big.NewInt(int64(tc.rate)), tc.pool) + community.StoreCommunityInfo(state, big.NewInt(int64(tc.rate)), tc.pool) }) if tc.err == nil { assert.NoError(t, err) diff --git a/contracts/native/governance/community/state.go b/contracts/native/governance/community/state.go new file mode 100644 index 00000000..fd312528 --- /dev/null +++ b/contracts/native/governance/community/state.go @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2021 The Zion Authors + * This file is part of The Zion library. + * + * The Zion is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Zion is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with The Zion. If not, see . + */ + + package community + + import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + + "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/core/state" +) + +// storage key prefix +const ( + SKP_COMMUNITY_INFO = "st_community_info" +) + +var ( + PercentDecimal = new(big.Int).Exp(big.NewInt(10), big.NewInt(4), nil) +) + +type CommunityInfo struct { + CommunityRate *big.Int + CommunityAddress common.Address +} + +func StoreCommunityInfo(s *state.StateDB, communityRate *big.Int, communityAddress common.Address) (*CommunityInfo, error) { + cache := (*state.CacheDB)(s) + communityInfo := &CommunityInfo{ + CommunityRate: communityRate, + CommunityAddress: communityAddress, + } + if err := setGenesisCommunityInfo(cache, communityInfo); err != nil { + return nil, err + } + return communityInfo, nil +} + + func setGenesisCommunityInfo(s *state.CacheDB, communityInfo *CommunityInfo) error { + if communityInfo.CommunityRate.Cmp(PercentDecimal) > 0 { + return fmt.Errorf("setGenesisCommunityInfo, CommunityRate over size") + } + key := communityInfoKey() + store, err := rlp.EncodeToBytes(communityInfo) + if err != nil { + return fmt.Errorf("setCommunityInfo, serialize community info error: %v", err) + } + customSet(s, key, store) + return nil + } + + func SetCommunityInfo(s *native.NativeContract, communityInfo *CommunityInfo) error { + if communityInfo.CommunityRate.Cmp(PercentDecimal) > 0 { + return fmt.Errorf("setCommunityInfo, CommunityRate over size") + } + key := communityInfoKey() + store, err := rlp.EncodeToBytes(communityInfo) + if err != nil { + return fmt.Errorf("setCommunityInfo, serialize community info error: %v", err) + } + set(s, key, store) + return nil + } + + func GetCommunityInfoImpl(s *native.NativeContract) (*CommunityInfo, error) { + communityInfo := new(CommunityInfo) + key := communityInfoKey() + store, err := get(s, key) + if err != nil { + return nil, fmt.Errorf("GetCommunityInfoImpl, get store error: %v", err) + } + if err := rlp.DecodeBytes(store, communityInfo); err != nil { + return nil, fmt.Errorf("GetCommunityInfoImpl, deserialize community info error: %v", err) + } + return communityInfo, nil + } + + func GetCommunityInfoFromDB(s *state.StateDB) (*CommunityInfo, error) { + cache := (*state.CacheDB)(s) + communityInfo := new(CommunityInfo) + key := communityInfoKey() + store, err := customGet(cache, key) + if err != nil { + return nil, fmt.Errorf("GetCommunityInfoFromDB, get store error: %v", err) + } + if err := rlp.DecodeBytes(store, communityInfo); err != nil { + return nil, fmt.Errorf("GetCommunityInfoFromDB, deserialize community info error: %v", err) + } + return communityInfo, nil + } + + // ==================================================================== + // + // storage basic operations + // + // ==================================================================== + + func get(s *native.NativeContract, key []byte) ([]byte, error) { + return customGet(s.GetCacheDB(), key) + } + + func set(s *native.NativeContract, key, value []byte) { + customSet(s.GetCacheDB(), key, value) + } + + func del(s *native.NativeContract, key []byte) { + customDel(s.GetCacheDB(), key) + } + + func customGet(db *state.CacheDB, key []byte) ([]byte, error) { + value, err := db.Get(key) + if err != nil { + return nil, err + } else if value == nil || len(value) == 0 { + return nil, errors.New("EOF") + } else { + return value, nil + } + } + + func customSet(db *state.CacheDB, key, value []byte) { + db.Put(key, value) + } + + func customDel(db *state.CacheDB, key []byte) { + db.Delete(key) + } + + func communityInfoKey() []byte { + return utils.ConcatKey(utils.NodeManagerContractAddress, []byte(SKP_COMMUNITY_INFO)) + } + \ No newline at end of file diff --git a/contracts/native/governance/entrance.go b/contracts/native/governance/entrance.go new file mode 100644 index 00000000..0ce0ff73 --- /dev/null +++ b/contracts/native/governance/entrance.go @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The zion network Authors + * This file is part of The poly network library. + * + * The poly network is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The poly network is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with The poly network . If not, see . + */ +package governance + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/native/go_abi/node_manager_abi" + nm "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/utils" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/types" +) + +func AssembleSystemTransactions(state *state.StateDB, height uint64) (types.Transactions, error) { + // Genesis block has no system transaction? + if height == 0 { + return nil, nil + } + + var txs types.Transactions + systemSenderNonce := state.GetNonce(utils.SystemTxSender) + // SystemTransaction: NodeManager.EndBlock + { + payload, err := new(nm.EndBlockParam).Encode() + if err != nil { + return nil, err + } + gas, err := core.IntrinsicGas(payload, nil, false, true, true) + if err != nil { + return nil, err + } + gas += nm.GasTable[node_manager_abi.MethodEndBlock] + txs = append(txs, types.NewTransaction(systemSenderNonce, utils.NodeManagerContractAddress, common.Big0, gas, common.Big0, payload)) + } + + // SystemTransaction: NodeManager.ChangeEpoch + { + epoch, err := nm.GetCurrentEpochInfoFromDB(state) + if err != nil { + return nil, err + } + + if epoch == nil || epoch.EndHeight == nil { + return nil, fmt.Errorf("unexpected epoch or epoch end height missing") + } + + if height + 1 == epoch.EndHeight.Uint64() { + payload, err := new(nm.ChangeEpochParam).Encode() + if err != nil { + return nil, err + } + gas, err := core.IntrinsicGas(payload, nil, false, true, true) + if err != nil { + return nil, err + } + gas += nm.GasTable[node_manager_abi.MethodChangeEpoch] + txs = append(txs, types.NewTransaction(systemSenderNonce + 1, utils.NodeManagerContractAddress, common.Big0, gas, common.Big0, payload)) + } + } + return txs, nil +} \ No newline at end of file diff --git a/contracts/native/governance/node_manager/distribute.go b/contracts/native/governance/node_manager/distribute.go index b7696dce..0d7355c8 100644 --- a/contracts/native/governance/node_manager/distribute.go +++ b/contracts/native/governance/node_manager/distribute.go @@ -20,10 +20,12 @@ package node_manager import ( "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" - "math/big" + "github.com/ethereum/go-ethereum/contracts/native/utils" ) const ( @@ -79,7 +81,7 @@ func IncreaseValidatorPeriod(s *native.NativeContract, validator *Validator) (ui // set accumulate rewards, incrementing period by 1 newValidatorAccumulatedRewards := &ValidatorAccumulatedRewards{ - Rewards: NewDecFromBigInt(new(big.Int)), + Rewards: utils.NewDecFromBigInt(new(big.Int)), Period: validatorAccumulatedRewards.Period + 1, } err = setValidatorAccumulatedRewards(s, validator.ConsensusAddress, newValidatorAccumulatedRewards) @@ -90,57 +92,57 @@ func IncreaseValidatorPeriod(s *native.NativeContract, validator *Validator) (ui return validatorAccumulatedRewards.Period, nil } -func withdrawStakeRewards(s *native.NativeContract, validator *Validator, stakeInfo *StakeInfo) (Dec, error) { +func withdrawStakeRewards(s *native.NativeContract, validator *Validator, stakeInfo *StakeInfo) (utils.Dec, error) { // end current period and calculate rewards endingPeriod, err := IncreaseValidatorPeriod(s, validator) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, IncreaseValidatorPeriod error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, IncreaseValidatorPeriod error: %v", err) } rewards, err := CalculateStakeRewards(s, stakeInfo.StakeAddress, validator.ConsensusAddress, endingPeriod) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, CalculateStakeRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, CalculateStakeRewards error: %v", err) } err = contract.NativeTransfer(s.StateDB(), this, stakeInfo.StakeAddress, rewards.BigInt()) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, nativeTransfer error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, nativeTransfer error: %v", err) } // update the outstanding rewards outstanding, err := getOutstandingRewards(s) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, getOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, getOutstandingRewards error: %v", err) } validatorOutstanding, err := getValidatorOutstandingRewards(s, validator.ConsensusAddress) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, getValidatorOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, getValidatorOutstandingRewards error: %v", err) } newOutstandingRewards, err := outstanding.Rewards.Sub(rewards) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, outstanding.Rewards.Sub error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, outstanding.Rewards.Sub error: %v", err) } err = setOutstandingRewards(s, &OutstandingRewards{Rewards: newOutstandingRewards}) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, setOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, setOutstandingRewards error: %v", err) } newValidatorOutstandingRewards, err := validatorOutstanding.Rewards.Sub(rewards) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, validatorOutstanding.Rewards.Sub error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, validatorOutstanding.Rewards.Sub error: %v", err) } err = setValidatorOutstandingRewards(s, validator.ConsensusAddress, &ValidatorOutstandingRewards{Rewards: newValidatorOutstandingRewards}) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, setValidatorOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, setValidatorOutstandingRewards error: %v", err) } // decrement reference count of starting period startingInfo, err := getStakeStartingInfo(s, stakeInfo.StakeAddress, validator.ConsensusAddress) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, getStakeStartingInfo error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, getStakeStartingInfo error: %v", err) } startPeriod := startingInfo.StartPeriod err = decreaseReferenceCount(s, validator.ConsensusAddress, startPeriod) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawStakeRewards, decreaseReferenceCount error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawStakeRewards, decreaseReferenceCount error: %v", err) } // remove stake starting info @@ -148,11 +150,11 @@ func withdrawStakeRewards(s *native.NativeContract, validator *Validator, stakeI return rewards, nil } -func CalculateStakeRewards(s *native.NativeContract, stakeAddress common.Address, consensusAddr common.Address, endPeriod uint64) (Dec, error) { +func CalculateStakeRewards(s *native.NativeContract, stakeAddress common.Address, consensusAddr common.Address, endPeriod uint64) (utils.Dec, error) { // fetch starting info for delegation startingInfo, err := getStakeStartingInfo(s, stakeAddress, consensusAddr) if err != nil { - return Dec{nil}, fmt.Errorf("CalculateStakeRewards, getStakeStartingInfo error: %v", err) + return utils.Dec{nil}, fmt.Errorf("CalculateStakeRewards, getStakeStartingInfo error: %v", err) } startPeriod := startingInfo.StartPeriod @@ -166,19 +168,19 @@ func CalculateStakeRewards(s *native.NativeContract, stakeAddress common.Address // return staking * (ending - starting) starting, err := getValidatorSnapshotRewards(s, consensusAddr, startPeriod) if err != nil { - return Dec{nil}, fmt.Errorf("CalculateStakeRewards, getValidatorSnapshotRewards start error: %v", err) + return utils.Dec{nil}, fmt.Errorf("CalculateStakeRewards, getValidatorSnapshotRewards start error: %v", err) } ending, err := getValidatorSnapshotRewards(s, consensusAddr, endPeriod) if err != nil { - return Dec{nil}, fmt.Errorf("CalculateStakeRewards, getValidatorSnapshotRewards end error: %v", err) + return utils.Dec{nil}, fmt.Errorf("CalculateStakeRewards, getValidatorSnapshotRewards end error: %v", err) } difference, err := ending.AccumulatedRewardsRatio.Sub(starting.AccumulatedRewardsRatio) if err != nil { - return Dec{nil}, fmt.Errorf("CalculateStakeRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("CalculateStakeRewards error: %v", err) } rewards, err := difference.MulWithTokenDecimal(stake) if err != nil { - return Dec{nil}, fmt.Errorf("CalculateStakeRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("CalculateStakeRewards error: %v", err) } return rewards, nil } @@ -206,46 +208,46 @@ func initializeStake(s *native.NativeContract, stakeInfo *StakeInfo, consensusAd return nil } -func withdrawCommission(s *native.NativeContract, stakeAddress common.Address, consensusAddr common.Address) (Dec, error) { +func withdrawCommission(s *native.NativeContract, stakeAddress common.Address, consensusAddr common.Address) (utils.Dec, error) { accumulatedCommission, err := getAccumulatedCommission(s, consensusAddr) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, getAccumulatedCommission error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, getAccumulatedCommission error: %v", err) } // update the outstanding rewards outstanding, err := getOutstandingRewards(s) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, getOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, getOutstandingRewards error: %v", err) } validatorOutstanding, err := getValidatorOutstandingRewards(s, consensusAddr) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, getValidatorOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, getValidatorOutstandingRewards error: %v", err) } newOutstandingRewards, err := outstanding.Rewards.Sub(accumulatedCommission.Amount) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, outstanding.Rewards.Sub error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, outstanding.Rewards.Sub error: %v", err) } err = setOutstandingRewards(s, &OutstandingRewards{Rewards: newOutstandingRewards}) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, setOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, setOutstandingRewards error: %v", err) } newValidatorOutstandingRewards, err := validatorOutstanding.Rewards.Sub(accumulatedCommission.Amount) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, validatorOutstanding.Rewards.Sub error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, validatorOutstanding.Rewards.Sub error: %v", err) } err = setValidatorOutstandingRewards(s, consensusAddr, &ValidatorOutstandingRewards{Rewards: newValidatorOutstandingRewards}) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, setValidatorOutstandingRewards error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, setValidatorOutstandingRewards error: %v", err) } err = contract.NativeTransfer(s.StateDB(), this, stakeAddress, accumulatedCommission.Amount.BigInt()) if err != nil { - return Dec{nil}, fmt.Errorf("withdrawCommission, nativeTransfer commission error: %v", err) + return utils.Dec{nil}, fmt.Errorf("withdrawCommission, nativeTransfer commission error: %v", err) } return accumulatedCommission.Amount, nil } -func allocateRewardsToValidator(s *native.NativeContract, validator *Validator, rewards Dec) error { +func allocateRewardsToValidator(s *native.NativeContract, validator *Validator, rewards utils.Dec) error { commission, err := validator.Commission.Rate.MulWithPercentDecimal(rewards) if err != nil { return fmt.Errorf("allocateRewardsToValidator, validator.Commission.Rate.Mul error: %v", err) diff --git a/contracts/native/governance/node_manager/external.go b/contracts/native/governance/node_manager/external.go index 7c501446..7bd7a741 100644 --- a/contracts/native/governance/node_manager/external.go +++ b/contracts/native/governance/node_manager/external.go @@ -22,6 +22,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" + "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" @@ -40,7 +42,7 @@ var ( MaxDescLength int = 2000 MaxValidatorNum int = 300 MaxUnlockingNum int = 100 - MaxStakeRate Dec = NewDecFromBigInt(new(big.Int).SetUint64(6)) // user stake can not more than 5 times of self stake + MaxStakeRate utils.Dec = utils.NewDecFromBigInt(new(big.Int).SetUint64(6)) // user stake can not more than 5 times of self stake MinBlockPerEpoch = new(big.Int).SetUint64(10000) ) @@ -57,7 +59,7 @@ func SetupGenesis(db *state.StateDB, genesis *core.Genesis) error { peers = append(peers, v.Validator) signers = append(signers, v.Signer) } - if _, err := StoreCommunityInfo(db, genesis.CommunityRate, genesis.CommunityAddress); err != nil { + if _, err := community.StoreCommunityInfo(db, genesis.CommunityRate, genesis.CommunityAddress); err != nil { return err } if _, err := StoreGenesisEpoch(db, peers, signers); err != nil { @@ -70,18 +72,6 @@ func SetupGenesis(db *state.StateDB, genesis *core.Genesis) error { return nil } -func StoreCommunityInfo(s *state.StateDB, communityRate *big.Int, communityAddress common.Address) (*CommunityInfo, error) { - cache := (*state.CacheDB)(s) - communityInfo := &CommunityInfo{ - CommunityRate: communityRate, - CommunityAddress: communityAddress, - } - if err := setGenesisCommunityInfo(cache, communityInfo); err != nil { - return nil, err - } - return communityInfo, nil -} - func StoreGenesisEpoch(s *state.StateDB, peers []common.Address, signers []common.Address) (*EpochInfo, error) { cache := (*state.CacheDB)(s) epoch := &EpochInfo{ diff --git a/contracts/native/governance/node_manager/hooks.go b/contracts/native/governance/node_manager/hooks.go index d6efe88d..ef98e1a8 100644 --- a/contracts/native/governance/node_manager/hooks.go +++ b/contracts/native/governance/node_manager/hooks.go @@ -20,33 +20,36 @@ package node_manager import ( "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" - "math/big" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" + "github.com/ethereum/go-ethereum/contracts/native/utils" ) func AfterValidatorCreated(s *native.NativeContract, validator *Validator) error { // set initial historical rewards (period 0) with reference count of 1 - err := setValidatorSnapshotRewards(s, validator.ConsensusAddress, 0, &ValidatorSnapshotRewards{NewDecFromBigInt(new(big.Int)), 1}) + err := setValidatorSnapshotRewards(s, validator.ConsensusAddress, 0, &ValidatorSnapshotRewards{utils.NewDecFromBigInt(new(big.Int)), 1}) if err != nil { return fmt.Errorf("AfterValidatorCreated, setValidatorSnapshotRewards error: %v", err) } // set accumulate rewards (starting at period 1) - err = setValidatorAccumulatedRewards(s, validator.ConsensusAddress, &ValidatorAccumulatedRewards{NewDecFromBigInt(new(big.Int)), 1}) + err = setValidatorAccumulatedRewards(s, validator.ConsensusAddress, &ValidatorAccumulatedRewards{utils.NewDecFromBigInt(new(big.Int)), 1}) if err != nil { return fmt.Errorf("AfterValidatorCreated, setValidatorAccumulatedRewards error: %v", err) } // set accumulated commission - err = setAccumulatedCommission(s, validator.ConsensusAddress, &AccumulatedCommission{NewDecFromBigInt(new(big.Int))}) + err = setAccumulatedCommission(s, validator.ConsensusAddress, &AccumulatedCommission{utils.NewDecFromBigInt(new(big.Int))}) if err != nil { return fmt.Errorf("AfterValidatorCreated, setAccumulatedCommission error: %v", err) } // set outstanding rewards - err = setValidatorOutstandingRewards(s, validator.ConsensusAddress, &ValidatorOutstandingRewards{Rewards: NewDecFromBigInt(new(big.Int))}) + err = setValidatorOutstandingRewards(s, validator.ConsensusAddress, &ValidatorOutstandingRewards{Rewards: utils.NewDecFromBigInt(new(big.Int))}) if err != nil { return fmt.Errorf("AfterValidatorCreated, setValidatorOutstandingRewards error: %v", err) } @@ -59,7 +62,7 @@ func AfterValidatorRemoved(s *native.NativeContract, validator *Validator) error if err != nil { return fmt.Errorf("AfterValidatorRemoved, getValidatorOutstandingRewards error: %v", err) } - communityInfo, err := GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return fmt.Errorf("AfterValidatorRemoved, GetCommunityInfoImpl error: %v", err) } diff --git a/contracts/native/governance/node_manager/manager.go b/contracts/native/governance/node_manager/manager.go index 045b9bb1..bab206c1 100644 --- a/contracts/native/governance/node_manager/manager.go +++ b/contracts/native/governance/node_manager/manager.go @@ -26,7 +26,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" + "github.com/ethereum/go-ethereum/contracts/native/economic" . "github.com/ethereum/go-ethereum/contracts/native/go_abi/node_manager_abi" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/rlp" ) @@ -48,7 +50,7 @@ var ( // the real gas usage of `createValidator`,`changeEpoch`,`endBlock` are 1291500, 5087250 and 343875. // in order to lower the total gas usage in an entire block, modify them to be 300000 and 200000, 150000. var ( - gasTable = map[string]uint64{ + GasTable = map[string]uint64{ MethodCreateValidator: 300000, MethodUpdateValidator: 170625, MethodUpdateCommission: 126000, @@ -86,7 +88,7 @@ func InitNodeManager() { } func RegisterNodeManagerContract(s *native.NativeContract) { - s.Prepare(ABI, gasTable) + s.Prepare(ABI, GasTable) s.Register(MethodCreateValidator, CreateValidator) s.Register(MethodUpdateValidator, UpdateValidator) @@ -188,12 +190,12 @@ func CreateValidator(s *native.NativeContract) ([]byte, error) { ConsensusAddress: params.ConsensusAddress, SignerAddress: params.SignerAddress, ProposalAddress: params.ProposalAddress, - Commission: &Commission{Rate: NewDecFromBigInt(params.Commission), UpdateHeight: height}, + Commission: &Commission{Rate: utils.NewDecFromBigInt(params.Commission), UpdateHeight: height}, Status: Unlock, Jailed: false, UnlockHeight: new(big.Int), - TotalStake: NewDecFromBigInt(initStake), - SelfStake: NewDecFromBigInt(initStake), + TotalStake: utils.NewDecFromBigInt(initStake), + SelfStake: utils.NewDecFromBigInt(initStake), Desc: params.Desc, } err = setValidator(s, validator) @@ -221,7 +223,7 @@ func CreateValidator(s *native.NativeContract) ([]byte, error) { } // deposit native token - err = deposit(s, caller, NewDecFromBigInt(initStake), validator) + err = deposit(s, caller, utils.NewDecFromBigInt(initStake), validator) if err != nil { return nil, fmt.Errorf("CreateValidator, deposit error: %v", err) } @@ -331,7 +333,7 @@ func UpdateCommission(s *native.NativeContract) ([]byte, error) { return nil, fmt.Errorf("UpdateCommission, commission can not changed in one epoch twice") } - validator.Commission = &Commission{Rate: NewDecFromBigInt(params.Commission), UpdateHeight: height} + validator.Commission = &Commission{Rate: utils.NewDecFromBigInt(params.Commission), UpdateHeight: height} err = setValidator(s, validator) if err != nil { @@ -365,7 +367,7 @@ func Stake(s *native.NativeContract) ([]byte, error) { if value.Sign() <= 0 { return nil, fmt.Errorf("Stake, amount must be positive") } - amount := NewDecFromBigInt(value) + amount := utils.NewDecFromBigInt(value) // check to see if the pubkey has been registered validator, found, err := getValidator(s, params.ConsensusAddress) @@ -428,7 +430,7 @@ func UnStake(s *native.NativeContract) ([]byte, error) { if params.Amount.Sign() <= 0 { return nil, fmt.Errorf("UnStake, amount must be positive") } - amount := NewDecFromBigInt(params.Amount) + amount := utils.NewDecFromBigInt(params.Amount) // check to see if the pubkey has been registered validator, found, err := getValidator(s, params.ConsensusAddress) @@ -600,7 +602,7 @@ func WithdrawValidator(s *native.NativeContract) ([]byte, error) { if err != nil { return nil, fmt.Errorf("WithdrawValidator, validator.TotalStake.Sub error: %v", err) } - validator.SelfStake = NewDecFromBigInt(new(big.Int)) + validator.SelfStake = utils.NewDecFromBigInt(new(big.Int)) if validator.TotalStake.IsZero() { delValidator(s, params.ConsensusAddress) err = AfterValidatorRemoved(s, validator) @@ -795,7 +797,7 @@ func WithdrawCommission(s *native.NativeContract) ([]byte, error) { if err != nil { return nil, fmt.Errorf("WithdrawCommission, withdrawCommission error: %v", err) } - err = setAccumulatedCommission(s, params.ConsensusAddress, &AccumulatedCommission{NewDecFromBigInt(new(big.Int))}) + err = setAccumulatedCommission(s, params.ConsensusAddress, &AccumulatedCommission{utils.NewDecFromBigInt(new(big.Int))}) if err != nil { return nil, fmt.Errorf("WithdrawCommission, setAccumulatedCommission error: %v", err) } @@ -813,8 +815,12 @@ func EndBlock(s *native.NativeContract) ([]byte, error) { return nil, fmt.Errorf("SystemTx authority failed") } + if err := economic.GenerateBlockReward(s); err != nil { + return nil, err + } + // contract balance = totalpool + outstanding + reward - balance := NewDecFromBigInt(s.StateDB().GetBalance(this)) + balance := utils.NewDecFromBigInt(s.StateDB().GetBalance(this)) totalPool, err := getTotalPool(s) if err != nil { @@ -843,7 +849,7 @@ func EndBlock(s *native.NativeContract) ([]byte, error) { if err != nil { return nil, fmt.Errorf("EndBlock, newRewards.DivUint64 error: %v", err) } - allocateSum := NewDecFromBigInt(new(big.Int)) + allocateSum := utils.NewDecFromBigInt(new(big.Int)) for _, v := range epochInfo.Validators { validator, found, err := getValidator(s, v) if err != nil { @@ -888,7 +894,7 @@ func GetGlobalConfig(s *native.NativeContract) ([]byte, error) { } func GetCommunityInfo(s *native.NativeContract) ([]byte, error) { - communityInfo, err := GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return nil, fmt.Errorf("GetCommunityInfo, GetCommunityInfoImpl error: %v", err) } @@ -1191,6 +1197,18 @@ func GetStakeRewards(s *native.NativeContract) ([]byte, error) { return utils.PackOutputs(ABI, MethodGetStakeRewards, enc) } +func decodeCommunityInfo(payload []byte) (*community.CommunityInfo, error) { + m := new(community.CommunityInfo) + var data struct { + CommunityInfo []byte + } + if err := utils.UnpackOutputs(ABI, MethodGetCommunityInfo, &data, payload); err != nil { + return nil, err + } + err := rlp.DecodeBytes(data.CommunityInfo, m) + return m, err +} + // GetSpecMethodID for consensus use func GetSpecMethodID() map[string]bool { return map[string]bool{"fe6f86f8": true, "083c6323": true} diff --git a/contracts/native/governance/node_manager/manager_test.go b/contracts/native/governance/node_manager/manager_test.go index f13b840d..d96b7c9d 100644 --- a/contracts/native/governance/node_manager/manager_test.go +++ b/contracts/native/governance/node_manager/manager_test.go @@ -21,10 +21,12 @@ package node_manager import ( "crypto/ecdsa" "fmt" - "github.com/ethereum/go-ethereum/contracts/native/contract" "math/big" "testing" + "github.com/ethereum/go-ethereum/contracts/native/contract" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" . "github.com/ethereum/go-ethereum/contracts/native/go_abi/node_manager_abi" @@ -49,7 +51,7 @@ func Init() { InitNodeManager() sdb = native.NewTestStateDB() testGenesisPeers, _ = native.GenerateTestPeers(testGenesisNum) - StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) + community.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) StoreGenesisEpoch(sdb, testGenesisPeers, testGenesisPeers) StoreGenesisGlobalConfig(sdb) } @@ -74,7 +76,7 @@ func TestCheckGenesis(t *testing.T) { assert.Equal(t, globalConfig.VoterValidatorNum, GenesisVoterValidatorNum) assert.Equal(t, globalConfig.ConsensusValidatorNum, GenesisConsensusValidatorNum) - communityInfo, err := GetCommunityInfoImpl(contract) + communityInfo, err := community.GetCommunityInfoImpl(contract) assert.Nil(t, err) assert.Equal(t, communityInfo.CommunityRate, big.NewInt(2000)) assert.Equal(t, communityInfo.CommunityAddress, common.EmptyAddress) @@ -104,8 +106,7 @@ func TestCheckGenesis(t *testing.T) { assert.Nil(t, err) ret, _, err = contractRef.NativeCall(common.EmptyAddress, utils.NodeManagerContractAddress, input) assert.Nil(t, err) - communityInfo2 := new(CommunityInfo) - err = communityInfo2.Decode(ret) + communityInfo2, err := decodeCommunityInfo(ret) assert.Nil(t, err) assert.Equal(t, communityInfo2.CommunityRate, big.NewInt(2000)) assert.Equal(t, communityInfo2.CommunityAddress, common.EmptyAddress) diff --git a/contracts/native/governance/node_manager/stake.go b/contracts/native/governance/node_manager/stake.go index 1586b402..00d58820 100644 --- a/contracts/native/governance/node_manager/stake.go +++ b/contracts/native/governance/node_manager/stake.go @@ -20,13 +20,15 @@ package node_manager import ( "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" - "math/big" + "github.com/ethereum/go-ethereum/contracts/native/utils" ) -func deposit(s *native.NativeContract, from common.Address, amount Dec, validator *Validator) error { +func deposit(s *native.NativeContract, from common.Address, amount utils.Dec, validator *Validator) error { // get deposit info stakeInfo, found, err := getStakeInfo(s, from, validator.ConsensusAddress) if err != nil { @@ -70,7 +72,7 @@ func deposit(s *native.NativeContract, from common.Address, amount Dec, validato return nil } -func unStake(s *native.NativeContract, from common.Address, amount Dec, validator *Validator) error { +func unStake(s *native.NativeContract, from common.Address, amount utils.Dec, validator *Validator) error { height := s.ContractRef().BlockHeight() globalConfig, err := GetGlobalConfigImpl(s) if err != nil { diff --git a/contracts/native/governance/node_manager/storage.go b/contracts/native/governance/node_manager/storage.go index 60ac2ade..bffea74f 100644 --- a/contracts/native/governance/node_manager/storage.go +++ b/contracts/native/governance/node_manager/storage.go @@ -55,7 +55,6 @@ const ( SKP_STAKE_STARTING_INFO = "st_stake_starting_info" SKP_SIGN = "st_sign" SKP_SIGNER = "st_signer" - SKP_COMMUNITY_INFO = "st_community_info" ) func setAccumulatedCommission(s *native.NativeContract, consensusAddr common.Address, accumulatedCommission *AccumulatedCommission) error { @@ -154,7 +153,7 @@ func setOutstandingRewards(s *native.NativeContract, outstandingRewards *Outstan func getOutstandingRewards(s *native.NativeContract) (*OutstandingRewards, error) { outstandingRewards := &OutstandingRewards{ - Rewards: NewDecFromBigInt(new(big.Int)), + Rewards: utils.NewDecFromBigInt(new(big.Int)), } key := outstandingRewardsKey() store, err := get(s, key) @@ -441,7 +440,7 @@ func getAllValidators(s *native.NativeContract) (*AllValidators, error) { return allValidators, nil } -func depositTotalPool(s *native.NativeContract, amount Dec) error { +func depositTotalPool(s *native.NativeContract, amount utils.Dec) error { totalPool, err := getTotalPool(s) if err != nil { return fmt.Errorf("depositTotalPool, get total pool error: %v", err) @@ -457,7 +456,7 @@ func depositTotalPool(s *native.NativeContract, amount Dec) error { return nil } -func withdrawTotalPool(s *native.NativeContract, amount Dec) error { +func withdrawTotalPool(s *native.NativeContract, amount utils.Dec) error { totalPool, err := getTotalPool(s) if err != nil { return fmt.Errorf("withdrawTotalPool, get total pool error: %v", err) @@ -484,7 +483,7 @@ func setTotalPool(s *native.NativeContract, totalPool *TotalPool) error { } func getTotalPool(s *native.NativeContract) (*TotalPool, error) { - totalPool := &TotalPool{NewDecFromBigInt(new(big.Int))} + totalPool := &TotalPool{utils.NewDecFromBigInt(new(big.Int))} key := totalPoolKey() store, err := get(s, key) if err == ErrEof { @@ -518,7 +517,7 @@ func getStakeInfo(s *native.NativeContract, stakeAddress common.Address, consens stakeInfo := &StakeInfo{ StakeAddress: stakeAddress, ConsensusAddr: consensusAddr, - Amount: NewDecFromBigInt(new(big.Int)), + Amount: utils.NewDecFromBigInt(new(big.Int)), } key := stakeInfoKey(stakeAddress, consensusAddr) store, err := get(s, key) @@ -550,14 +549,14 @@ func addUnlockingInfo(s *native.NativeContract, stakeAddress common.Address, unl return nil } -func filterExpiredUnlockingInfo(s *native.NativeContract, stakeAddress common.Address) (Dec, error) { +func filterExpiredUnlockingInfo(s *native.NativeContract, stakeAddress common.Address) (utils.Dec, error) { height := s.ContractRef().BlockHeight() unlockingInfo, err := getUnlockingInfo(s, stakeAddress) if err != nil { - return Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, GetUnlockingInfo error: %v", err) + return utils.Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, GetUnlockingInfo error: %v", err) } j := 0 - expiredSum := NewDecFromBigInt(new(big.Int)) + expiredSum := utils.NewDecFromBigInt(new(big.Int)) for _, unlockingStake := range unlockingInfo.UnlockingStake { if unlockingStake.CompleteHeight.Cmp(height) == 1 { unlockingInfo.UnlockingStake[j] = unlockingStake @@ -565,7 +564,7 @@ func filterExpiredUnlockingInfo(s *native.NativeContract, stakeAddress common.Ad } else { expiredSum, err = expiredSum.Add(unlockingStake.Amount) if err != nil { - return Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, expiredSum.Add error: %v", err) + return utils.Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, expiredSum.Add error: %v", err) } } } @@ -575,7 +574,7 @@ func filterExpiredUnlockingInfo(s *native.NativeContract, stakeAddress common.Ad } else { err = setUnlockingInfo(s, unlockingInfo) if err != nil { - return Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, setUnlockingInfo error: %v", err) + return utils.Dec{nil}, fmt.Errorf("filterExpiredUnlockingInfo, setUnlockingInfo error: %v", err) } } return expiredSum, nil @@ -725,59 +724,6 @@ func GetEpochInfoFromDB(s *state.StateDB, ID *big.Int) (*EpochInfo, error) { return epochInfo, nil } -func setGenesisCommunityInfo(s *state.CacheDB, communityInfo *CommunityInfo) error { - if communityInfo.CommunityRate.Cmp(PercentDecimal) > 0 { - return fmt.Errorf("setGenesisCommunityInfo, CommunityRate over size") - } - key := communityInfoKey() - store, err := rlp.EncodeToBytes(communityInfo) - if err != nil { - return fmt.Errorf("setCommunityInfo, serialize community info error: %v", err) - } - customSet(s, key, store) - return nil -} - -func SetCommunityInfo(s *native.NativeContract, communityInfo *CommunityInfo) error { - if communityInfo.CommunityRate.Cmp(PercentDecimal) > 0 { - return fmt.Errorf("setCommunityInfo, CommunityRate over size") - } - key := communityInfoKey() - store, err := rlp.EncodeToBytes(communityInfo) - if err != nil { - return fmt.Errorf("setCommunityInfo, serialize community info error: %v", err) - } - set(s, key, store) - return nil -} - -func GetCommunityInfoImpl(s *native.NativeContract) (*CommunityInfo, error) { - communityInfo := new(CommunityInfo) - key := communityInfoKey() - store, err := get(s, key) - if err != nil { - return nil, fmt.Errorf("GetCommunityInfoImpl, get store error: %v", err) - } - if err := rlp.DecodeBytes(store, communityInfo); err != nil { - return nil, fmt.Errorf("GetCommunityInfoImpl, deserialize community info error: %v", err) - } - return communityInfo, nil -} - -func GetCommunityInfoFromDB(s *state.StateDB) (*CommunityInfo, error) { - cache := (*state.CacheDB)(s) - communityInfo := new(CommunityInfo) - key := communityInfoKey() - store, err := customGet(cache, key) - if err != nil { - return nil, fmt.Errorf("GetCommunityInfoFromDB, get store error: %v", err) - } - if err := rlp.DecodeBytes(store, communityInfo); err != nil { - return nil, fmt.Errorf("GetCommunityInfoFromDB, deserialize community info error: %v", err) - } - return communityInfo, nil -} - // ==================================================================== // // `consensus sign` storage @@ -986,8 +932,4 @@ func signKey(hash common.Hash) []byte { func signerKey(hash common.Hash) []byte { return utils.ConcatKey(this, []byte(SKP_SIGNER), hash.Bytes()) -} - -func communityInfoKey() []byte { - return utils.ConcatKey(this, []byte(SKP_COMMUNITY_INFO)) -} +} \ No newline at end of file diff --git a/contracts/native/governance/node_manager/types.go b/contracts/native/governance/node_manager/types.go index 6865d53b..96e87f1c 100644 --- a/contracts/native/governance/node_manager/types.go +++ b/contracts/native/governance/node_manager/types.go @@ -60,8 +60,8 @@ type Validator struct { Status LockStatus Jailed bool UnlockHeight *big.Int - TotalStake Dec - SelfStake Dec + TotalStake utils.Dec + SelfStake utils.Dec Desc string } @@ -101,7 +101,7 @@ func (m Validator) IsRemoving(height *big.Int) bool { } type Commission struct { - Rate Dec + Rate utils.Dec UpdateHeight *big.Int } @@ -127,7 +127,7 @@ func (m *GlobalConfig) Decode(payload []byte) error { type StakeInfo struct { StakeAddress common.Address ConsensusAddr common.Address - Amount Dec + Amount utils.Dec } func (m *StakeInfo) Decode(payload []byte) error { @@ -159,7 +159,7 @@ type UnlockingStake struct { Height *big.Int CompleteHeight *big.Int ConsensusAddress common.Address - Amount Dec + Amount utils.Dec } type EpochInfo struct { @@ -218,7 +218,7 @@ func (m *EpochInfo) MemberList() []common.Address { } type AccumulatedCommission struct { - Amount Dec + Amount utils.Dec } func (m *AccumulatedCommission) Decode(payload []byte) error { @@ -232,7 +232,7 @@ func (m *AccumulatedCommission) Decode(payload []byte) error { } type ValidatorAccumulatedRewards struct { - Rewards Dec + Rewards utils.Dec Period uint64 } @@ -247,7 +247,7 @@ func (m *ValidatorAccumulatedRewards) Decode(payload []byte) error { } type ValidatorOutstandingRewards struct { - Rewards Dec + Rewards utils.Dec } func (m *ValidatorOutstandingRewards) Decode(payload []byte) error { @@ -261,7 +261,7 @@ func (m *ValidatorOutstandingRewards) Decode(payload []byte) error { } type OutstandingRewards struct { - Rewards Dec + Rewards utils.Dec } func (m *OutstandingRewards) Decode(payload []byte) error { @@ -275,7 +275,7 @@ func (m *OutstandingRewards) Decode(payload []byte) error { } type StakeRewards struct { - Rewards Dec + Rewards utils.Dec } func (m *StakeRewards) Decode(payload []byte) error { @@ -289,7 +289,7 @@ func (m *StakeRewards) Decode(payload []byte) error { } type ValidatorSnapshotRewards struct { - AccumulatedRewardsRatio Dec // ratio already mul decimal + AccumulatedRewardsRatio utils.Dec // ratio already mul decimal ReferenceCount uint64 } @@ -305,7 +305,7 @@ func (m *ValidatorSnapshotRewards) Decode(payload []byte) error { type StakeStartingInfo struct { StartPeriod uint64 - Stake Dec + Stake utils.Dec Height *big.Int } @@ -345,23 +345,8 @@ func (m *ConsensusSign) Hash() common.Hash { return v } -type CommunityInfo struct { - CommunityRate *big.Int - CommunityAddress common.Address -} - -func (m *CommunityInfo) Decode(payload []byte) error { - var data struct { - CommunityInfo []byte - } - if err := utils.UnpackOutputs(ABI, MethodGetCommunityInfo, &data, payload); err != nil { - return err - } - return rlp.DecodeBytes(data.CommunityInfo, m) -} - type TotalPool struct { - TotalPool Dec + TotalPool utils.Dec } func (m *TotalPool) Decode(payload []byte) error { diff --git a/contracts/native/governance/proposal_manager/proposal_manager.go b/contracts/native/governance/proposal_manager/proposal_manager.go index e657708b..a29ac3d7 100644 --- a/contracts/native/governance/proposal_manager/proposal_manager.go +++ b/contracts/native/governance/proposal_manager/proposal_manager.go @@ -21,14 +21,16 @@ package proposal_manager import ( "encoding/hex" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" . "github.com/ethereum/go-ethereum/contracts/native/go_abi/proposal_manager_abi" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/rlp" - "math/big" ) const ( @@ -271,7 +273,7 @@ func ProposeCommunity(s *native.NativeContract) ([]byte, error) { return nil, fmt.Errorf("ProposeCommunity, content is more than max length") } - info := new(node_manager.CommunityInfo) + info := new(community.CommunityInfo) err = rlp.DecodeBytes(params.Content, info) if err != nil { return nil, fmt.Errorf("ProposeCommunity, deserialize community info error: %v", err) @@ -366,7 +368,7 @@ func VoteProposal(s *native.NativeContract) ([]byte, error) { return nil, fmt.Errorf("Propose, utils.NativeTransfer error: %v", err) } - communityInfo, err := node_manager.GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return nil, fmt.Errorf("VoteProposal, node_manager.GetCommunityInfoImpl error: %v", err) } @@ -440,7 +442,7 @@ func VoteProposal(s *native.NativeContract) ([]byte, error) { return nil, fmt.Errorf("VoteProposal, cleanConfigProposalList error: %v", err) } case UpdateCommunityInfo: - info := new(node_manager.CommunityInfo) + info := new(community.CommunityInfo) err := rlp.DecodeBytes(proposal.Content, info) if err != nil { return nil, fmt.Errorf("VoteProposal, deserialize community info error: %v", err) @@ -451,7 +453,7 @@ func VoteProposal(s *native.NativeContract) ([]byte, error) { if info.CommunityRate.Sign() > 0 { communityInfo.CommunityRate = info.CommunityRate } - err = node_manager.SetCommunityInfo(s, communityInfo) + err = community.SetCommunityInfo(s, communityInfo) if err != nil { return nil, fmt.Errorf("VoteProposal, node_manager.SetCommunityInfo error: %v", err) } diff --git a/contracts/native/governance/proposal_manager/proposal_manager_test.go b/contracts/native/governance/proposal_manager/proposal_manager_test.go index d6b5d7ac..61a1e9ea 100644 --- a/contracts/native/governance/proposal_manager/proposal_manager_test.go +++ b/contracts/native/governance/proposal_manager/proposal_manager_test.go @@ -20,15 +20,17 @@ package proposal_manager import ( "crypto/ecdsa" + "math/big" + "testing" + "github.com/ethereum/go-ethereum/contracts/native/contract" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" - "math/big" - "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/crypto" @@ -50,7 +52,7 @@ func init() { InitProposalManager() sdb = native.NewTestStateDB() testGenesisPeers, _ = native.GenerateTestPeers(testGenesisNum) - node_manager.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) + community.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) node_manager.StoreGenesisEpoch(sdb, testGenesisPeers, testGenesisPeers) node_manager.StoreGenesisGlobalConfig(sdb) } @@ -69,7 +71,7 @@ func TestProposalManager(t *testing.T) { assert.Equal(t, globalConfig.ConsensusValidatorNum, node_manager.GenesisConsensusValidatorNum) assert.Equal(t, globalConfig.MinProposalStake, node_manager.GenesisMinProposalStake) - communityInfo, err := node_manager.GetCommunityInfoImpl(c) + communityInfo, err := community.GetCommunityInfoImpl(c) assert.Nil(t, err) assert.Equal(t, communityInfo.CommunityRate, big.NewInt(2000)) assert.Equal(t, communityInfo.CommunityAddress, common.EmptyAddress) @@ -257,7 +259,7 @@ func TestProposalManager(t *testing.T) { globalConfig, err = node_manager.GetGlobalConfigImpl(c) assert.Nil(t, err) assert.Equal(t, globalConfig.VoterValidatorNum, uint64(2)) - communityInfo, err = node_manager.GetCommunityInfoImpl(c) + communityInfo, err = community.GetCommunityInfoImpl(c) assert.Nil(t, err) assert.Equal(t, communityInfo.CommunityRate, big.NewInt(1000)) assert.Equal(t, sdb.GetBalance(common.EmptyAddress), new(big.Int).Mul(big.NewInt(81000), params.ZNT1)) diff --git a/contracts/native/governance/proposal_manager/storage.go b/contracts/native/governance/proposal_manager/storage.go index 683502f6..8defeda8 100644 --- a/contracts/native/governance/proposal_manager/storage.go +++ b/contracts/native/governance/proposal_manager/storage.go @@ -21,13 +21,14 @@ package proposal_manager import ( "errors" "fmt" + "math/big" + "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/contract" - "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/rlp" - "math/big" ) var ErrEof = errors.New("EOF") @@ -108,7 +109,7 @@ func removeFromProposalList(s *native.NativeContract, ID *big.Int) error { } func removeExpiredFromProposalList(s *native.NativeContract) error { - communityInfo, err := node_manager.GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return fmt.Errorf("removeExpiredFromProposalList, node_manager.GetCommunityInfoImpl error: %v", err) } @@ -183,7 +184,7 @@ func cleanConfigProposalList(s *native.NativeContract) error { } func removeExpiredFromConfigProposalList(s *native.NativeContract) error { - communityInfo, err := node_manager.GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return fmt.Errorf("removeExpiredFromConfigProposalList, node_manager.GetCommunityInfoImpl error: %v", err) } @@ -258,7 +259,7 @@ func cleanCommunityProposalList(s *native.NativeContract) error { } func removeExpiredFromCommunityProposalList(s *native.NativeContract) error { - communityInfo, err := node_manager.GetCommunityInfoImpl(s) + communityInfo, err := community.GetCommunityInfoImpl(s) if err != nil { return fmt.Errorf("removeExpiredFromCommunityProposalList, node_manager.GetCommunityInfoImpl error: %v", err) } diff --git a/contracts/native/governance/side_chain_manager/side_chain_manager_test.go b/contracts/native/governance/side_chain_manager/side_chain_manager_test.go index 2d2aaca3..f0fbef2e 100644 --- a/contracts/native/governance/side_chain_manager/side_chain_manager_test.go +++ b/contracts/native/governance/side_chain_manager/side_chain_manager_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/contracts/native" "github.com/ethereum/go-ethereum/contracts/native/go_abi/side_chain_manager_abi" + "github.com/ethereum/go-ethereum/contracts/native/governance/community" "github.com/ethereum/go-ethereum/contracts/native/governance/node_manager" "github.com/ethereum/go-ethereum/contracts/native/utils" "github.com/ethereum/go-ethereum/core/rawdb" @@ -49,7 +50,7 @@ func init() { node_manager.InitNodeManager() sdb = native.NewTestStateDB() signers, _ = native.GenerateTestPeers(2) - node_manager.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) + community.StoreCommunityInfo(sdb, big.NewInt(2000), common.EmptyAddress) node_manager.StoreGenesisEpoch(sdb, signers, signers) node_manager.StoreGenesisGlobalConfig(sdb) } diff --git a/contracts/native/utils/params.go b/contracts/native/utils/params.go index 57c1ceb2..59be8461 100644 --- a/contracts/native/utils/params.go +++ b/contracts/native/utils/params.go @@ -17,7 +17,9 @@ */ package utils -import "github.com/ethereum/go-ethereum/common" +import ( + "github.com/ethereum/go-ethereum/common" +) var ( BYTE_FALSE = []byte{0} @@ -43,4 +45,4 @@ var ( ETH_COMMON_ROUTER = uint64(2) RIPPLE_ROUTER = uint64(6) -) +) \ No newline at end of file diff --git a/contracts/native/governance/node_manager/safe_math.go b/contracts/native/utils/safe_math.go similarity index 94% rename from contracts/native/governance/node_manager/safe_math.go rename to contracts/native/utils/safe_math.go index 9d8e74ff..965259e5 100644 --- a/contracts/native/governance/node_manager/safe_math.go +++ b/contracts/native/utils/safe_math.go @@ -16,13 +16,23 @@ * along with The Zion. If not, see . */ -package node_manager +package utils import ( "errors" "math/big" ) +const ( + TokenPrecision = 18 + PercentPrecision = 4 +) + +var ( + TokenDecimal = new(big.Int).Exp(big.NewInt(10), big.NewInt(TokenPrecision), nil) + PercentDecimal = new(big.Int).Exp(big.NewInt(10), big.NewInt(PercentPrecision), nil) +) + type Dec struct { I *big.Int } @@ -149,4 +159,4 @@ func (t Dec) DivUint64(i uint64) (Dec, error) { div := new(big.Int).Div(t.I, new(big.Int).SetUint64(i)) return Dec{div}, nil -} +} \ No newline at end of file diff --git a/core/state_processor.go b/core/state_processor.go index 34fb19b9..ce2f2cc5 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -18,6 +18,7 @@ package core import ( "fmt" + "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus" @@ -70,24 +71,14 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg blockContext := NewEVMBlockContext(header, p.bc, nil) vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg) - // zion hotstuff consensus engine will add two tx for governance epoch change. - // separate the system tx and common tx before consensus `finalize` and assemble the - // tx and receipts again after `finalize` finished. - engine, isHotstuff := p.engine.(consensus.HotStuff) - txNum := len(block.Transactions()) - systemTxs := make([]*types.Transaction, 0, 2) - systemTxIds := make(map[string]int) - commonTxs := make([]*types.Transaction, 0, txNum) + // Sort out system transactions here to be handled after common transactions + commonTransactions, systemTransactions, asSystemMessage, err := p.engine.BlockTransactions(block, statedb) + if err != nil { + return nil, nil, 0, err + } // Iterate over and process the individual transactions - for i, tx := range block.Transactions() { - if isHotstuff { - if id, isSystemTx := engine.IsSystemTransaction(tx, block.Header()); isSystemTx { - systemTxs = append(systemTxs, tx) - systemTxIds[id] += 1 - continue - } - } + for i, tx := range commonTransactions { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { return nil, nil, 0, err @@ -97,25 +88,24 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } - commonTxs = append(commonTxs, tx) receipts = append(receipts, receipt) } - // the number of system transaction in range of [1, 2] except genesis block. - // each system transaction can only be executed once in a single block. - if isHotstuff && engine.HasSystemTxHook() { - if length := len(systemTxs); (block.NumberU64() > 0 && length < 1) || length > 2 { - return nil, nil, 0, fmt.Errorf("system txs list length %d invalid", length) + for i, tx := range systemTransactions { + msg := asSystemMessage(tx, header.BaseFee) + statedb.Prepare(tx.Hash(), block.Hash(), len(commonTransactions) + i) + receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, header, tx, usedGas, vmenv) + if err != nil { + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } - for id, cnt := range systemTxIds { - if cnt > 1 { - return nil, nil, 0, fmt.Errorf("system tx %s dumplicated %d ", id, cnt) - } + if receipt.Status != types.ReceiptStatusSuccessful { + return nil, nil, 0, fmt.Errorf("unexpected reverted system transactions tx %d [%s], status %v", i, tx.Hash(), receipt.Status) } + receipts = append(receipts, receipt) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - if err := p.engine.Finalize(p.bc, header, statedb, &commonTxs, block.Uncles(), &receipts, &systemTxs, usedGas); err != nil { + if err := p.engine.Finalize(p.bc, header, statedb, block.Uncles()); err != nil { return receipts, allLogs, *usedGas, err } @@ -185,3 +175,15 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) } + +// ApplyTransaction attempts to apply a transaction to the given state database +// and uses the input parameters for its environment. It returns the receipt +// for the transaction, gas used and an error if the transaction failed, +// indicating the block was invalid. +func ApplyTransactionWithCustomMessageProvider(asMessage func(*types.Transaction, *big.Int) types.Message , config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { + msg := asMessage(tx, header.BaseFee) + // Create a new context to be used in the EVM environment + blockContext := NewEVMBlockContext(header, bc, author) + vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg) + return applyTransaction(msg, config, bc, author, gp, statedb, header, tx, usedGas, vmenv) +}