From 17cec0087587c2f547bc641e7f2673c8118c9880 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 28 Dec 2018 10:37:07 +0700 Subject: [PATCH 1/2] create new api get reward --- cmd/tomo/main.go | 1 + cmd/utils/flags.go | 11 +++++++++- consensus/posv/posv.go | 21 ++++++++++++++++-- contracts/utils.go | 6 +++--- core/blockchain.go | 13 ++++++++--- eth/api_backend.go | 11 ++++++++++ eth/backend.go | 43 +++++++++++++++++++++++++++++++------ eth/config.go | 2 ++ internal/ethapi/api.go | 11 ++++++++++ internal/ethapi/backend.go | 1 + internal/web3ext/web3ext.go | 5 +++++ les/api_backend.go | 10 +++++++++ 12 files changed, 120 insertions(+), 15 deletions(-) diff --git a/cmd/tomo/main.go b/cmd/tomo/main.go index 751c4babf0..88ddafbd65 100644 --- a/cmd/tomo/main.go +++ b/cmd/tomo/main.go @@ -122,6 +122,7 @@ var ( //utils.ExtraDataFlag, configFileFlag, utils.AnnounceTxsFlag, + utils.StoreRewardFlag, } rpcFlags = []cli.Flag{ diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index a33380d9ff..6144dd57fc 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -117,6 +117,10 @@ var ( Name: "announce-txs", Usage: "Always commit transactions", } + StoreRewardFlag = cli.BoolFlag{ + Name: "store-reward", + Usage: "Store reward to file", + } DataDirFlag = DirectoryFlag{ Name: "datadir", Usage: "Data directory for the databases and keystore", @@ -1082,7 +1086,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { // TODO(fjl): force-enable this in --dev mode cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name) } - + if ctx.GlobalIsSet(StoreRewardFlag.Name) { + cfg.StoreRewardFolder = filepath.Join(stack.DataDir(), "tomo", "rewards") + if _, err := os.Stat(cfg.StoreRewardFolder); os.IsNotExist(err) { + os.Mkdir(cfg.StoreRewardFolder, os.ModePerm) + } + } // Override any default configs for hard coded networks. switch { case ctx.GlobalBool(TestnetFlag.Name): diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 9e88f5cdf5..4b50a589c8 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -215,13 +215,14 @@ type Posv struct { signatures *lru.ARCCache // Signatures of recent blocks to speed up mining validatorSignatures *lru.ARCCache // Signatures of recent blocks to speed up mining verifiedHeaders *lru.ARCCache + rewards *lru.ARCCache proposals map[common.Address]bool // Current list of proposals we are pushing signer common.Address // Ethereum address of the signing key signFn clique.SignerFn // Signer function to authorize hashes with lock sync.RWMutex // Protects the signer fields - HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error + HookReward func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) (error, map[string]interface{}) HookPenalty func(chain consensus.ChainReader, blockNumberEpoc uint64) ([]common.Address, error) HookValidator func(header *types.Header, signers []common.Address) ([]byte, error) HookVerifyMNs func(header *types.Header, signers []common.Address) error @@ -240,6 +241,7 @@ func New(config *params.PosvConfig, db ethdb.Database) *Posv { signatures, _ := lru.NewARC(inmemorySnapshots) validatorSignatures, _ := lru.NewARC(inmemorySnapshots) verifiedHeaders, _ := lru.NewARC(inmemorySnapshots) + rewards, _ := lru.NewARC(inmemorySnapshots) return &Posv{ config: &conf, db: db, @@ -247,6 +249,7 @@ func New(config *params.PosvConfig, db ethdb.Database) *Posv { signatures: signatures, verifiedHeaders: verifiedHeaders, validatorSignatures: validatorSignatures, + rewards: rewards, proposals: make(map[common.Address]bool), } } @@ -847,9 +850,11 @@ func (c *Posv) Finalize(chain consensus.ChainReader, header *types.Header, state rCheckpoint := chain.Config().Posv.RewardCheckpoint if c.HookReward != nil && number%rCheckpoint == 0 { - if err := c.HookReward(chain, state, header); err != nil { + err, rewardResults := c.HookReward(chain, state, header) + if err != nil { return nil, err } + c.rewards.Add(header.Hash(), rewardResults) } // the state remains as is and uncles are dropped @@ -1079,3 +1084,15 @@ func Hop(len, pre, cur int) int { return len - 1 } } + +func (c *Posv) GetRewards(hash common.Hash) map[string]interface{} { + rewards, ok := c.rewards.Get(hash) + if !ok { + return nil + } + return rewards.(map[string]interface{}) +} + +func (c *Posv) InsertRewards(hash common.Hash, rewards map[string]interface{}) { + c.rewards.Add(hash, rewards) +} diff --git a/contracts/utils.go b/contracts/utils.go index 05e10740d7..42643b4571 100644 --- a/contracts/utils.go +++ b/contracts/utils.go @@ -387,17 +387,17 @@ func GetCandidatesOwnerBySigner(validator *contractValidator.TomoValidator, sign } // Calculate reward for holders. -func CalculateRewardForHolders(foudationWalletAddr common.Address, validator *contractValidator.TomoValidator, state *state.StateDB, signer common.Address, calcReward *big.Int) error { +func CalculateRewardForHolders(foudationWalletAddr common.Address, validator *contractValidator.TomoValidator, state *state.StateDB, signer common.Address, calcReward *big.Int) (error, map[common.Address]*big.Int) { rewards, err := GetRewardBalancesRate(foudationWalletAddr, signer, calcReward, validator) if err != nil { - return err + return err, nil } if len(rewards) > 0 { for holder, reward := range rewards { state.AddBalance(holder, reward) } } - return nil + return nil, rewards } // Get reward balance rates for master node, founder and holders. diff --git a/core/blockchain.go b/core/blockchain.go index 1ea79d0440..b7985a8999 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -141,9 +141,10 @@ type BlockChain struct { validator Validator // block and state validator interface vmConfig vm.Config - badBlocks *lru.Cache // Bad block cache - IPCEndpoint string - Client *ethclient.Client // Global ipc client instance. + badBlocks *lru.Cache // Bad block cache + IPCEndpoint string + Client *ethclient.Client // Global ipc client instance. + HookWriteRewards func(header *types.Header) } // NewBlockChain returns a fully initialised block chain using information @@ -1222,6 +1223,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty } } } + if bc.HookWriteRewards != nil { + bc.HookWriteRewards(block.Header()) + } } // Append a single chain head event if we've progressed the chain if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { @@ -1428,6 +1432,9 @@ func (bc *BlockChain) insertBlock(block *types.Block) ([]interface{}, []*types.L events = append(events, ChainHeadEvent{block}) log.Debug("New ChainHeadEvent from fetcher ", "number", block.NumberU64(), "hash", block.Hash()) } + if bc.HookWriteRewards != nil { + bc.HookWriteRewards(block.Header()) + } return events, coalescedLogs, nil } diff --git a/eth/api_backend.go b/eth/api_backend.go index 10a3e3f537..3b34e843ef 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -18,6 +18,7 @@ package eth import ( "context" + "github.com/ethereum/go-ethereum/consensus/posv" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -233,3 +234,13 @@ func (b *EthApiBackend) GetIPCClient() (*ethclient.Client, error) { func (b *EthApiBackend) GetEngine() consensus.Engine { return b.eth.engine } + +func (s *EthApiBackend) GetRewardByHash(hash common.Hash) map[string]interface{} { + if c, ok := s.eth.Engine().(*posv.Posv); ok { + rewards := c.GetRewards(hash) + if rewards != nil { + return rewards + } + } + return make(map[string]interface{}) +} diff --git a/eth/backend.go b/eth/backend.go index c1d5b1a553..c05909bfd6 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -18,9 +18,12 @@ package eth import ( + "encoding/json" "errors" "fmt" + "io/ioutil" "math/big" + "path/filepath" "runtime" "sync" "sync/atomic" @@ -286,10 +289,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } // Hook calculates reward for masternodes - c.HookReward = func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) error { + c.HookReward = func(chain consensus.ChainReader, state *state.StateDB, header *types.Header) (error, map[string]interface{}) { client, err := eth.blockchain.GetClient() if err != nil { log.Error("Fail to connect IPC client for blockSigner", "error", err) + return err, nil } number := header.Number.Uint64() rCheckpoint := chain.Config().Posv.RewardCheckpoint @@ -297,6 +301,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if foudationWalletAddr == (common.Address{}) { log.Error("Foundation Wallet Address is empty", "error", foudationWalletAddr) } + rewards := make(map[string]interface{}) if number > 0 && number-rCheckpoint > 0 && foudationWalletAddr != (common.Address{}) { start := time.Now() // Get signers in blockSigner smartcontract. @@ -309,30 +314,36 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { signers, err := contracts.GetRewardForCheckpoint(chain, addr, number, rCheckpoint, client, totalSigner) if err != nil { log.Error("Fail to get signers for reward checkpoint", "error", err) + return err, nil } + rewards["signers"] = signers rewardSigners, err := contracts.CalculateRewardForSigner(chainReward, signers, *totalSigner) if err != nil { log.Error("Fail to calculate reward for signers", "error", err) + return err, nil } // Get validator. validator, err := contract.NewTomoValidator(common.HexToAddress(common.MasternodeVotingSMC), client) if err != nil { log.Error("Fail get instance of Tomo Validator", "error", err) - - return err + return err, nil } // Add reward for coin holders. + voterResults := make(map[common.Address]interface{}) if len(signers) > 0 { for signer, calcReward := range rewardSigners { - err := contracts.CalculateRewardForHolders(foudationWalletAddr, validator, state, signer, calcReward) + err, rewards := contracts.CalculateRewardForHolders(foudationWalletAddr, validator, state, signer, calcReward) if err != nil { log.Error("Fail to calculate reward for holders.", "error", err) + return err, nil } + voterResults[signer] = rewards } } + rewards["rewards"] = voterResults log.Debug("Time Calculated HookReward ", "block", header.Number.Uint64(), "time", common.PrettyDuration(time.Since(start))) } - return nil + return nil, rewards } // Hook verifies masternodes set @@ -363,8 +374,28 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } return false } + eth.blockchain.HookWriteRewards = func(header *types.Header) { + if len(config.StoreRewardFolder) > 0 { + rewards := c.GetRewards(header.Hash()) + if rewards == nil { + rewards = c.GetRewards(header.HashNoValidator()) + if rewards != nil { + c.InsertRewards(header.Hash(), rewards) + } + } + if rewards == nil { + return + } + data, err := json.Marshal(rewards) + if err == nil { + err = ioutil.WriteFile(filepath.Join(config.StoreRewardFolder, header.Number.String()+"."+header.Hash().Hex()), data, 0644) + } + if err != nil { + log.Error("Error when save reward info ", "number", header.Number, "hash", header.Hash().Hex(), "err", err) + } + } + } } - return eth, nil } diff --git a/eth/config.go b/eth/config.go index dbfe973913..6f0d58955e 100644 --- a/eth/config.go +++ b/eth/config.go @@ -114,6 +114,8 @@ type Config struct { // Miscellaneous options DocRoot string `toml:"-"` + + StoreRewardFolder string } type configMarshaling struct { diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 3ec5f33a76..4a6f1f5583 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -492,6 +492,17 @@ func (s *PublicBlockChainAPI) BlockNumber() *big.Int { return header.Number } +// BlockNumber returns the block number of the chain head. +func (s *PublicBlockChainAPI) GetRewardByHash(hash common.Hash) map[string]interface{} { + if c, ok := s.b.GetEngine().(*posv.Posv); ok { + rewards := c.GetRewards(hash) + if rewards != nil { + return rewards + } + } + return make(map[string]interface{}) +} + // GetBalance returns the amount of wei for the given address in the state of the // given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta // block numbers are also allowed. diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index deb6d89a3c..7614557ee2 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -73,6 +73,7 @@ type Backend interface { CurrentBlock() *types.Block GetIPCClient() (*ethclient.Client, error) GetEngine() consensus.Engine + GetRewardByHash(hash common.Hash) map[string]interface{} } func GetAPIs(apiBackend Backend) []rpc.API { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 7f9b342e4e..fc2c552f4f 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -460,6 +460,11 @@ web3._extend({ call: 'eth_getRawTransactionByHash', params: 1 }), + new web3._extend.Method({ + name: 'getRewardByHash', + call: 'eth_getRewardByHash', + params: 1 + }), new web3._extend.Method({ name: 'getRawTransactionFromBlock', call: function(args) { diff --git a/les/api_backend.go b/les/api_backend.go index 35050ee929..152aab79ee 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -18,6 +18,7 @@ package les import ( "context" + "github.com/ethereum/go-ethereum/consensus/posv" "math/big" "github.com/ethereum/go-ethereum/accounts" @@ -200,3 +201,12 @@ func (b *LesApiBackend) GetIPCClient() (*ethclient.Client, error) { func (b *LesApiBackend) GetEngine() consensus.Engine { return b.eth.engine } +func (s *LesApiBackend) GetRewardByHash(hash common.Hash) map[string]interface{} { + if c, ok := s.eth.Engine().(*posv.Posv); ok { + rewards := c.GetRewards(hash) + if rewards != nil { + return rewards + } + } + return make(map[string]interface{}) +} From 1baaa30ad70c1d3062040cd2d9f5fbfd44b5f8d7 Mon Sep 17 00:00:00 2001 From: Nguyen Ba Tam Date: Fri, 28 Dec 2018 10:37:34 +0700 Subject: [PATCH 2/2] bump up v1.2.0 --- params/version.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/params/version.go b/params/version.go index 905daa6466..948cbc8caf 100644 --- a/params/version.go +++ b/params/version.go @@ -22,8 +22,8 @@ import ( const ( VersionMajor = 1 // Major version component of the current release - VersionMinor = 1 // Minor version component of the current release - VersionPatch = 3 // Patch version component of the current release + VersionMinor = 2 // Minor version component of the current release + VersionPatch = 0 // Patch version component of the current release VersionMeta = "stable" // Version metadata to append to the version string )