From 19434b8de6c8613be1ef1a5a660d1098cfaa6610 Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 14 May 2024 11:44:48 +0900 Subject: [PATCH 01/21] feat: brioche first implementation --- consensus/ethash/consensus.go | 6 +-- core/block_validator.go | 10 +++++ core/blockchain.go | 24 +++--------- core/blockchain_test.go | 34 ++++++++++++++++- core/state_processor.go | 8 ++-- core/types.go | 5 ++- eth/downloader/downloader.go | 1 + eth/state_accessor.go | 2 +- go.mod | 4 +- params/config.go | 41 ++++++++++++++++++--- wemix/admin.go | 69 +++++++++++++++++++++++++++-------- wemix/admin_test.go | 48 ++++++++++++++++++++++++ wemix/miner/miner.go | 6 +-- wemix/rewards_test.go | 59 ++++++++++++++++++++++++++++-- 14 files changed, 259 insertions(+), 58 deletions(-) create mode 100644 wemix/admin_test.go diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 506bd8cb8114..4ab4413b8443 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -42,7 +42,6 @@ import ( var ( FrontierBlockReward = big.NewInt(5e+18) // Block reward in wei for successfully mining a block ByzantiumBlockReward = big.NewInt(3e+18) // Block reward in wei for successfully mining a block upward from Byzantium - WemixBlockReward = big.NewInt(0) // Block reward in wei for Wemix ConstantinopleBlockReward = big.NewInt(2e+18) // Block reward in wei for successfully mining a block upward from Constantinople maxUncles = 2 // Maximum number of uncles allowed in a single block allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks @@ -698,9 +697,8 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) } else { - blockReward = WemixBlockReward coinbase, rewards, err := wemixminer.CalculateRewards( - header.Number, blockReward, header.Fees, + config, header.Number, header.Fees, func(addr common.Address, amt *big.Int) { state.AddBalance(addr, amt) }) @@ -713,7 +711,7 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header if err == wemixminer.ErrNotInitialized { reward := new(big.Int) if header.Fees != nil { - reward.Add(blockReward, header.Fees) + reward.Add(reward, header.Fees) } state.AddBalance(header.Coinbase, reward) return nil diff --git a/core/block_validator.go b/core/block_validator.go index 028beadc491a..7b5f8f8ad518 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "fmt" "math/big" @@ -108,6 +109,15 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return nil } +// ValidateReward validates the reward info of remote block with the reward processed locally +func (v *BlockValidator) ValidateReward(header *types.Header, localHeader *types.Header) error { + // verify rewards info + if !bytes.Equal(header.Rewards, localHeader.Rewards) { + return fmt.Errorf("invalid rewards (remote: %x local: %x)", header.Rewards, localHeader.Rewards) + } + return nil +} + // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas close to the provided target, and increase it towards // the target if the baseline gas is lower. diff --git a/core/blockchain.go b/core/blockchain.go index 41d4bbeb84e2..8a570e7fa758 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -43,7 +43,6 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" - wemixminer "github.com/ethereum/go-ethereum/wemix/miner" lru "github.com/hashicorp/golang-lru" ) @@ -1608,11 +1607,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1) } - // wemix: reward calculation uses governance contract, meaning - // the previous block is required. For fast sync, we need to wait for - // governance is initialized and try again. - retryCount := 2 - retry: statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps) if err != nil { return it.index, err @@ -1642,13 +1636,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) + appliedHeader, receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } - // Update the metrics touched during block processing accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them @@ -1665,17 +1658,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Validate the state using the default validator substart = time.Now() if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, fees); err != nil { - if retryCount--; !wemixminer.IsPoW() && retryCount > 0 { - // make sure the previous block exists in order to calculate rewards distribution - for try := 100; try > 0; try-- { - if _, _, err := wemixminer.CalculateRewards(block.Number(), big.NewInt(0), big.NewInt(100000000), nil); err == nil { - break - } - time.Sleep(100 * time.Millisecond) - } - goto retry - } + bc.reportBlock(block, receipts, err) + atomic.StoreUint32(&followupInterrupt, 1) + return it.index, err + } + if err := bc.validator.ValidateReward(block.Header(), appliedHeader); err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index cda30f0f5860..568bc677ef29 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -159,17 +159,24 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) + appliedHeader, receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err } + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees) if err != nil { blockchain.reportBlock(block, receipts, err) return err } + err = blockchain.validator.ValidateReward(block.Header(), appliedHeader) + if err != nil { + blockchain.reportBlock(block, receipts, err) + return err + } + blockchain.chainmu.MustLock() rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1))) rawdb.WriteBlock(blockchain.db, block) @@ -3770,3 +3777,28 @@ func TestSetCanonical(t *testing.T) { chain.SetCanonical(canon[DefaultCacheConfig.TriesInMemory-1]) verify(canon[DefaultCacheConfig.TriesInMemory-1]) } + +func TestRewardValidation(t *testing.T) { + // Configure and generate a sample block chain + var ( + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + deleteAddr = common.Address{1} + gspec = &Genesis{ + Config: ¶ms.ChainConfig{ChainID: big.NewInt(1), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}, + Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}}, + } + genesis = gspec.MustCommit(db) + ) + + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, nil) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } +} diff --git a/core/state_processor.go b/core/state_processor.go index e2b2c1fc5efc..743d3d6f98ed 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -56,7 +56,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*types.Header, types.Receipts, []*types.Log, uint64, *big.Int, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -77,12 +77,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) @@ -90,7 +90,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) - return receipts, allLogs, *usedGas, fees, nil + return header, receipts, allLogs, *usedGas, fees, nil } func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { diff --git a/core/types.go b/core/types.go index ae909a48fb1f..3c565d1118cf 100644 --- a/core/types.go +++ b/core/types.go @@ -34,6 +34,9 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error + + // ValidateReward validates the reward info of the block header with the reward processed in local + ValidateReward(header *types.Header, localHeader *types.Header) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -49,5 +52,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*types.Header, types.Receipts, []*types.Log, uint64, *big.Int, error) } diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index 9eef5dfd8d70..9537a5a3e7fa 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -384,6 +384,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td, ttd *big.Int, // Post a user notification of the sync (only once per session) if atomic.CompareAndSwapInt32(&d.notified, 0, 1) { log.Info("Block synchronisation started") + defer log.Info("Block synchronisation finished") } if mode == SnapSync { // Snap sync uses the snapshot namespace to store potentially flakey data until diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 98be012f3326..481909968fd0 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/go.mod b/go.mod index 1956e64450e9..0e3cb402128d 100644 --- a/go.mod +++ b/go.mod @@ -60,6 +60,8 @@ require ( github.com/stretchr/testify v1.7.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef + github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 + github.com/yoseplee/vrf v0.0.0-20210814110709-d1caf509310b go.etcd.io/etcd/api/v3 v3.5.2 go.etcd.io/etcd/client/v3 v3.5.2 go.etcd.io/etcd/server/v3 v3.5.2 @@ -72,8 +74,6 @@ require ( golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023 gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/urfave/cli.v1 v1.20.0 - github.com/yoseplee/vrf v0.0.0-20210814110709-d1caf509310b - github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 ) require ( diff --git a/params/config.go b/params/config.go index 39d494868726..8847ddfdf93d 100644 --- a/params/config.go +++ b/params/config.go @@ -159,7 +159,13 @@ var ( LondonBlock: big.NewInt(0), PangyoBlock: big.NewInt(0), ApplepieBlock: big.NewInt(20_476_911), + BriocheBlock: big.NewInt(53_557_371), // 24-07-01 00:00:00 (UTC) expected Ethash: new(EthashConfig), + Brioche: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalving: big.NewInt(53_557_371), + HalvingPeriod: big.NewInt(63_115_200), + }, } // WemixTestnetChainConfig contains the chain parameters to run a node on the Wemix test network. @@ -180,7 +186,13 @@ var ( LondonBlock: big.NewInt(0), PangyoBlock: big.NewInt(10_000_000), ApplepieBlock: big.NewInt(26_240_268), + BriocheBlock: big.NewInt(60_537_845), // 24-06-17 02:00:00 (UTC) expected Ethash: new(EthashConfig), + Brioche: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalving: big.NewInt(60_537_845), + HalvingPeriod: big.NewInt(63_115_200), + }, } // SepoliaChainConfig contains the chain parameters to run a node on the Sepolia test network. @@ -303,16 +315,16 @@ var ( // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil} + AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, new(EthashConfig), nil, nil} // AllCliqueProtocolChanges contains every protocol change (EIPs) introduced // and accepted by the Ethereum core developers into the Clique consensus. // // This configuration is intentionally not using keyed fields to force anyone // adding flags to the config to also have to set these fields. - AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}} + AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil} - TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, new(EthashConfig), nil} + TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, nil, nil, new(EthashConfig), nil, nil} TestRules = TestChainConfig.Rules(new(big.Int), false) ) @@ -395,19 +407,31 @@ type ChainConfig struct { MergeForkBlock *big.Int `json:"mergeForkBlock,omitempty"` // EIP-3675 (TheMerge) switch block (nil = no fork, 0 = already in merge proceedings) PangyoBlock *big.Int `json:"pangyoBlock,omitempty"` // Pangyo switch block (nil = no fork, 0 = already on pangyo) ApplepieBlock *big.Int `json:"applepieBlock,omitempty"` // Applepie switch block (nil = no fork, 0 = already on applepie) + BriocheBlock *big.Int `json:"briocheBlock,omitempty"` // Brioche switch block (nil = no fork, 0 = already on brioche) // TerminalTotalDifficulty is the amount of total difficulty reached by // the network that triggers the consensus upgrade. TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"` // Various consensus engines - Ethash *EthashConfig `json:"ethash,omitempty"` - Clique *CliqueConfig `json:"clique,omitempty"` + Ethash *EthashConfig `json:"ethash,omitempty"` + Clique *CliqueConfig `json:"clique,omitempty"` + Brioche *BriocheConfig `json:"brioche,omitempty"` // if this config is nil, brioche halving is not applied } // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} +// Brioche halving configuration +type BriocheConfig struct { + // if the chain is on brioche hard fork, `RewardAmount` of gov contract is not used rather this `BlockReward` is used + BlockReward *big.Int `json:"blockReward,omitempty"` // if nil, then default block reward is 1e18 (=1 wemix) + FirstHalving *big.Int `json:"firstHalving,omitempty"` // if nil, then halving is not work + HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // if nil, then halving is not work + LastHalving *big.Int `json:"lastHalving,omitempty"` // if nil, then halving goes on continuously + ZeroReward *big.Int `json:"zeroReward,omitempty"` // if nil, +} + // String implements the stringer interface, returning the consensus engine details. func (c *EthashConfig) String() string { return "ethash" @@ -435,7 +459,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, Terminal TD: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -454,6 +478,7 @@ func (c *ChainConfig) String() string { c.MergeForkBlock, c.PangyoBlock, c.ApplepieBlock, + c.BriocheBlock, c.TerminalTotalDifficulty, engine, ) @@ -537,6 +562,10 @@ func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool { return isForked(c.ArrowGlacierBlock, num) } +func (c *ChainConfig) IsBrioche(num *big.Int) bool { + return isForked(c.BriocheBlock, num) +} + // IsTerminalPoWBlock returns whether the given block is the last block of PoW stage. func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool { if c.TerminalTotalDifficulty == nil { diff --git a/wemix/admin.go b/wemix/admin.go index 5441dfa14f65..f0470b68c0c5 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -123,11 +123,12 @@ type rewardParameters struct { var ( // "Wemix Registry" - magic, _ = big.NewInt(0).SetString("0x57656d6978205265676973747279", 0) - etcdClusterName = "Wemix" - big0 = big.NewInt(0) - nilAddress = common.Address{} - admin *wemixAdmin + magic, _ = big.NewInt(0).SetString("0x57656d6978205265676973747279", 0) + etcdClusterName = "Wemix" + big0 = big.NewInt(0) + nilAddress = common.Address{} + defaultBriocheBlockReward = int64(1e18) + admin *wemixAdmin ErrAlreadyRunning = errors.New("already running") ErrExists = errors.New("already exists") @@ -1051,7 +1052,7 @@ func handleBlock94Rewards(height *big.Int, rp *rewardParameters, fees *big.Int) // distributeRewards divides the rewardAmount among members according to their // stakes, and allocates rewards to staker, ecoSystem, and maintenance accounts. -func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([]reward, error) { +func distributeRewards(height *big.Int, rp *rewardParameters, blockReward *big.Int, fees *big.Int) ([]reward, error) { dm := new(big.Int) for i := 0; i < len(rp.distributionMethod); i++ { dm.Add(dm, rp.distributionMethod[i]) @@ -1061,14 +1062,14 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] } v10000 := big.NewInt(10000) - minerAmount := new(big.Int).Set(rp.rewardAmount) + minerAmount := new(big.Int).Set(blockReward) minerAmount.Div(minerAmount.Mul(minerAmount, rp.distributionMethod[0]), v10000) - stakerAmount := new(big.Int).Set(rp.rewardAmount) + stakerAmount := new(big.Int).Set(blockReward) stakerAmount.Div(stakerAmount.Mul(stakerAmount, rp.distributionMethod[1]), v10000) - ecoSystemAmount := new(big.Int).Set(rp.rewardAmount) + ecoSystemAmount := new(big.Int).Set(blockReward) ecoSystemAmount.Div(ecoSystemAmount.Mul(ecoSystemAmount, rp.distributionMethod[2]), v10000) // the rest goes to maintenance - maintenanceAmount := new(big.Int).Set(rp.rewardAmount) + maintenanceAmount := new(big.Int).Set(blockReward) maintenanceAmount.Sub(maintenanceAmount, minerAmount) maintenanceAmount.Sub(maintenanceAmount, stakerAmount) maintenanceAmount.Sub(maintenanceAmount, ecoSystemAmount) @@ -1102,7 +1103,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] } d.Mul(d, vn) b.Sub(b, d) - for i, ix := 0, height.Int64()%int64(n); b.Cmp(v0) > 0; i, ix = i+1, (ix+1)%int64(n) { + for ix := height.Int64() % int64(n); b.Cmp(v0) > 0; ix = (ix + 1) % int64(n) { rewards[ix].Reward.Add(rewards[ix].Reward, v1) b.Sub(b, v1) } @@ -1146,7 +1147,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, fees *big.Int) ([] return rewards, nil } -func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { +func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1179,7 +1180,32 @@ func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalan coinbase.SetBytes(rp.members[mix].Reward.Bytes()) } - rr, errr := distributeRewards(num, rp, fees) + // block reward + // - not brioche chain: use `EnvStorageImp.getBlockRewardAmount()` + // - brioche chain + // - config.Brioche.BlockReward != nil: config.Brioche.BlockReward + // - config.Brioche.BlockReward == nil: 1e18 + // - apply halving for BlockReward + var blockReward *big.Int + if config.IsBrioche(num) { + if config.Brioche != nil && config.Brioche.BlockReward != nil { + blockReward = big.NewInt(0).Set(config.Brioche.BlockReward) + } else { + blockReward = big.NewInt(defaultBriocheBlockReward) // default brioche block reward + } + if config.Brioche != nil && + config.Brioche.FirstHalving != nil && + config.Brioche.HalvingPeriod != nil && + num.Cmp(config.Brioche.FirstHalving) >= 0 { + past := big.NewInt(0).Set(num) + past.Sub(past, config.Brioche.FirstHalving) + blockReward = halveRewards(blockReward, config.Brioche.HalvingPeriod, past) + } + } else { + // if the wemix chain is not on brioche hard fork, use the `rewardAmount` from gov contract + blockReward = big.NewInt(0).Set(rp.rewardAmount) + } + rr, errr := distributeRewards(num, rp, blockReward, fees) if errr != nil { err = errr return @@ -1195,8 +1221,21 @@ func (ma *wemixAdmin) calculateRewards(num, blockReward, fees *big.Int, addBalan return } -func calculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { - return admin.calculateRewards(num, blockReward, fees, addBalance) +func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { + return admin.calculateRewards(config, num, fees, addBalance) +} + +func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int) *big.Int { + result := big.NewInt(0).Set(baseReward) + past := big.NewInt(0).Set(pastBlocks) + for { + result = result.Div(result, big.NewInt(2)) + if past.Cmp(halvePeriod) < 0 { + break + } + past = past.Sub(past, halvePeriod) + } + return result } func verifyRewards(num *big.Int, rewards string) error { diff --git a/wemix/admin_test.go b/wemix/admin_test.go new file mode 100644 index 000000000000..d912dcf667d1 --- /dev/null +++ b/wemix/admin_test.go @@ -0,0 +1,48 @@ +package wemix + +import ( + "math/big" + "testing" +) + +func TestHalveRewards(t *testing.T) { + testcases := []struct { + reward *big.Int + period *big.Int + past *big.Int + expected *big.Int + }{ + // sample test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), big.NewInt(25e16)}, + + // brioche halving test + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), big.NewInt(125e15)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), big.NewInt(625e14)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), big.NewInt(3125e13)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), big.NewInt(15625e12)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), big.NewInt(78125e11)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), big.NewInt(390625e10)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), big.NewInt(1953125e9)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), big.NewInt(9765625e8)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), big.NewInt(48828125e7)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), big.NewInt(244140625e6)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), big.NewInt(1220703125e5)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), big.NewInt(6103515625e4)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), big.NewInt(30517578125e3)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), big.NewInt(152587890625e2)}, + } + + for _, testcase := range testcases { + halved := halveRewards(testcase.reward, testcase.period, testcase.past) + if testcase.expected.Cmp(halved) != 0 { + t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", testcase.expected, halved) + } + } +} diff --git a/wemix/miner/miner.go b/wemix/miner/miner.go index 45abda5a5c59..0b369b3b063c 100644 --- a/wemix/miner/miner.go +++ b/wemix/miner/miner.go @@ -16,7 +16,7 @@ var ( AmPartnerFunc func() bool IsPartnerFunc func(string) bool AmHubFunc func(string) int - CalculateRewardsFunc func(*big.Int, *big.Int, *big.Int, func(common.Address, *big.Int)) (*common.Address, []byte, error) + CalculateRewardsFunc func(*params.ChainConfig, *big.Int, *big.Int, func(common.Address, *big.Int)) (*common.Address, []byte, error) VerifyRewardsFunc func(*big.Int, string) error GetCoinbaseFunc func(height *big.Int) (coinbase common.Address, err error) SignBlockFunc func(height *big.Int, hash common.Hash) (coinbase common.Address, sig []byte, err error) @@ -79,11 +79,11 @@ func IsPoW() bool { return params.ConsensusMethod == params.ConsensusPoW } -func CalculateRewards(num, blockReward, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { +func CalculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { if CalculateRewardsFunc == nil { return nil, nil, ErrNotInitialized } else { - return CalculateRewardsFunc(num, blockReward, fees, addBalance) + return CalculateRewardsFunc(config, num, fees, addBalance) } } diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 0737abd492c1..ea90b90f42a0 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -4,12 +4,41 @@ package wemix import ( "encoding/json" + "github.com/ethereum/go-ethereum/common" "math/big" + "strconv" "testing" - - "github.com/ethereum/go-ethereum/common" + "time" ) +func TestBlockMintingQuantity(t *testing.T) { + minted, _ := big.NewInt(0).SetString("379886454000000000000000000", 0) // 379,886,454 wemix + annualReward, _ := big.NewInt(0).SetString("500000000000000000", 0) // 0.5 wemix + t.Logf("initial minted = %d", minted) + t.Logf("reward = %d", annualReward) + for i := 2024; i < 2054; i += 2 { + minted = new(big.Int).Add(minted, new(big.Int).Mul(annualReward, big.NewInt(63115200))) + annualReward = new(big.Int).Div(annualReward, big.NewInt(2)) + t.Logf("minted = %d", minted) + t.Logf("reward = %d", annualReward) + } + for i := 2054; i < 2100; i += 2 { + minted = new(big.Int).Add(minted, new(big.Int).Mul(annualReward, big.NewInt(63115200))) + } + t.Logf("total minted = %d", minted) +} + +func TestHalvesPeriod(t *testing.T) { + halvesTime := big.NewInt(1719792000) // 2024-07-01 00:00:00 (UTC) + blockPeriod := big.NewInt(63_115_200) // 86400 * 730.5 + loc, _ := time.LoadLocation("UTC") + for i := 0; i < 17; i++ { + tm, _ := strconv.ParseInt(halvesTime.String(), 10, 64) + t.Logf("~ %v(%d)", time.Unix(tm, 0).In(loc), halvesTime) + halvesTime.Add(halvesTime, blockPeriod) + } +} + // TestDistributeRewards tests the DistributeRewards function func TestDistributeRewards(t *testing.T) { @@ -135,11 +164,35 @@ func TestDistributeRewards(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the distributeRewards function - rewards, err := distributeRewards(tt.height, tt.rp, tt.fees) + rewards, err := distributeRewards(tt.height, tt.rp, big.NewInt(defaultBriocheBlockReward), tt.fees) rewardsString, _ := json.Marshal(rewards) if string(rewardsString) != tt.want { t.Errorf("distributeRewards() failed: %v, %v <-> %v", err, tt.want, string(rewardsString)) } + + distTotal := big.NewInt(0) + for _, dist := range tt.rp.distributionMethod { + distTotal.Add(distTotal, dist) + } + totalRewards := big.NewInt(0) + memberRewards := big.NewInt(0) + for i, reward := range rewards { + totalRewards.Add(totalRewards, reward.Reward) + if i < len(tt.rp.members) { + memberRewards.Add(memberRewards, reward.Reward) + } + } + totalAmount := big.NewInt(0).Set(tt.rp.rewardAmount) + totalAmount.Add(totalAmount, tt.fees) + memberAmount := big.NewInt(0).Set(tt.rp.rewardAmount) + memberAmount = memberAmount.Mul(memberAmount, tt.rp.distributionMethod[0]) + memberAmount = memberAmount.Div(memberAmount, distTotal) + if memberRewards.Cmp(memberAmount) != 0 { + t.Errorf("members reward amount mismatched! sum=%d, rewards=%d", memberRewards, memberAmount) + } + if totalRewards.Cmp(totalAmount) != 0 { + t.Errorf("total reward amount mismatched! sum=%d, rewards=%d", totalRewards, totalAmount) + } }) } } From a1a06a7fce632fd3f1918b628474541b35b8ae79 Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 10:51:35 +0900 Subject: [PATCH 02/21] fix: add more configuration --- params/config.go | 22 +++--- wemix/admin.go | 52 +++++++------ wemix/admin_test.go | 171 +++++++++++++++++++++++++++++++++++------- wemix/rewards_test.go | 30 -------- 4 files changed, 187 insertions(+), 88 deletions(-) diff --git a/params/config.go b/params/config.go index 8847ddfdf93d..34962e69d351 100644 --- a/params/config.go +++ b/params/config.go @@ -162,9 +162,9 @@ var ( BriocheBlock: big.NewInt(53_557_371), // 24-07-01 00:00:00 (UTC) expected Ethash: new(EthashConfig), Brioche: &BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalving: big.NewInt(53_557_371), - HalvingPeriod: big.NewInt(63_115_200), + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(53_557_371), + HalvingPeriod: big.NewInt(63_115_200), }, } @@ -189,9 +189,9 @@ var ( BriocheBlock: big.NewInt(60_537_845), // 24-06-17 02:00:00 (UTC) expected Ethash: new(EthashConfig), Brioche: &BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalving: big.NewInt(60_537_845), - HalvingPeriod: big.NewInt(63_115_200), + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(60_537_845), + HalvingPeriod: big.NewInt(63_115_200), }, } @@ -425,11 +425,11 @@ type EthashConfig struct{} // Brioche halving configuration type BriocheConfig struct { // if the chain is on brioche hard fork, `RewardAmount` of gov contract is not used rather this `BlockReward` is used - BlockReward *big.Int `json:"blockReward,omitempty"` // if nil, then default block reward is 1e18 (=1 wemix) - FirstHalving *big.Int `json:"firstHalving,omitempty"` // if nil, then halving is not work - HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // if nil, then halving is not work - LastHalving *big.Int `json:"lastHalving,omitempty"` // if nil, then halving goes on continuously - ZeroReward *big.Int `json:"zeroReward,omitempty"` // if nil, + BlockReward *big.Int `json:"blockReward,omitempty"` // if nil, then default block reward is 1e18 (=1 wemix) + FirstHalvingBlock *big.Int `json:"firstHalvingBlock,omitempty"` // if nil, then halving is not work. including this block + HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // if nil, then halving is not work + HalvingTimes uint64 `json:"halvingTimes,omitempty"` // if 0, then no halving + NoRewardHereAfter *big.Int `json:"noRewardHereAfter,omitempty"` // if nil, block reward goes on endlessly } // String implements the stringer interface, returning the consensus engine details. diff --git a/wemix/admin.go b/wemix/admin.go index f0470b68c0c5..e3e86cd450ec 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -1180,31 +1180,20 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi coinbase.SetBytes(rp.members[mix].Reward.Bytes()) } - // block reward - // - not brioche chain: use `EnvStorageImp.getBlockRewardAmount()` - // - brioche chain - // - config.Brioche.BlockReward != nil: config.Brioche.BlockReward - // - config.Brioche.BlockReward == nil: 1e18 - // - apply halving for BlockReward var blockReward *big.Int if config.IsBrioche(num) { - if config.Brioche != nil && config.Brioche.BlockReward != nil { - blockReward = big.NewInt(0).Set(config.Brioche.BlockReward) - } else { - blockReward = big.NewInt(defaultBriocheBlockReward) // default brioche block reward - } - if config.Brioche != nil && - config.Brioche.FirstHalving != nil && - config.Brioche.HalvingPeriod != nil && - num.Cmp(config.Brioche.FirstHalving) >= 0 { - past := big.NewInt(0).Set(num) - past.Sub(past, config.Brioche.FirstHalving) - blockReward = halveRewards(blockReward, config.Brioche.HalvingPeriod, past) - } + blockReward = getBriocheBlockReward(config.Brioche, num) } else { // if the wemix chain is not on brioche hard fork, use the `rewardAmount` from gov contract blockReward = big.NewInt(0).Set(rp.rewardAmount) } + + // block reward + // - not brioche chain: use `EnvStorageImp.getBlockRewardAmount()` + // - brioche chain + // - config.Brioche.BlockReward != nil: config.Brioche.BlockReward + // - config.Brioche.BlockReward == nil: 1e18 + // - apply halving for BlockReward rr, errr := distributeRewards(num, rp, blockReward, fees) if errr != nil { err = errr @@ -1221,14 +1210,35 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi return } +func getBriocheBlockReward(brioche *params.BriocheConfig, num *big.Int) *big.Int { + blockReward := big.NewInt(defaultBriocheBlockReward) // default brioche block reward + if brioche != nil { + if brioche.BlockReward != nil { + blockReward = big.NewInt(0).Set(brioche.BlockReward) + } + if brioche.NoRewardHereAfter != nil && + brioche.NoRewardHereAfter.Cmp(num) <= 0 { + blockReward = big.NewInt(0) + } else if brioche.FirstHalvingBlock != nil && + brioche.HalvingPeriod != nil && + brioche.HalvingTimes > 0 && + num.Cmp(brioche.FirstHalvingBlock) >= 0 { + past := big.NewInt(0).Set(num) + past.Sub(past, brioche.FirstHalvingBlock) + blockReward = halveRewards(blockReward, brioche.HalvingPeriod, past, brioche.HalvingTimes) + } + } + return blockReward +} + func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { return admin.calculateRewards(config, num, fees, addBalance) } -func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int) *big.Int { +func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int, halvingTimes uint64) *big.Int { result := big.NewInt(0).Set(baseReward) past := big.NewInt(0).Set(pastBlocks) - for { + for ; halvingTimes > 0; halvingTimes-- { result = result.Div(result, big.NewInt(2)) if past.Cmp(halvePeriod) < 0 { break diff --git a/wemix/admin_test.go b/wemix/admin_test.go index d912dcf667d1..d3c466f613e9 100644 --- a/wemix/admin_test.go +++ b/wemix/admin_test.go @@ -1,6 +1,7 @@ package wemix import ( + "github.com/ethereum/go-ethereum/params" "math/big" "testing" ) @@ -10,39 +11,157 @@ func TestHalveRewards(t *testing.T) { reward *big.Int period *big.Int past *big.Int + times uint64 expected *big.Int }{ // sample test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, big.NewInt(25e16)}, + + // times test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, big.NewInt(25e16)}, // brioche halving test - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), big.NewInt(125e15)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), big.NewInt(625e14)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), big.NewInt(3125e13)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), big.NewInt(15625e12)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), big.NewInt(78125e11)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), big.NewInt(390625e10)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), big.NewInt(1953125e9)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), big.NewInt(9765625e8)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), big.NewInt(48828125e7)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), big.NewInt(244140625e6)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), big.NewInt(1220703125e5)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), big.NewInt(6103515625e4)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), big.NewInt(30517578125e3)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, big.NewInt(125e15)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, big.NewInt(625e14)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, big.NewInt(3125e13)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, big.NewInt(15625e12)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, big.NewInt(78125e11)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, big.NewInt(390625e10)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, big.NewInt(1953125e9)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, big.NewInt(9765625e8)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, big.NewInt(48828125e7)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, big.NewInt(244140625e6)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, big.NewInt(1220703125e5)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, big.NewInt(6103515625e4)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, big.NewInt(30517578125e3)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, big.NewInt(152587890625e2)}, + } + + for _, tc := range testcases { + halved := halveRewards(tc.reward, tc.period, tc.past, tc.times) + if tc.expected.Cmp(halved) != 0 { + t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) + } + } +} + +func TestGetBriocheBlockReward(t *testing.T) { + testcases := []struct { + briocheConfig *params.BriocheConfig + blockNum *big.Int + expected *big.Int + }{ + // normal case + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(5e17), + }, + + // base block reward variations + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(7e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(35e17), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(3), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(5e17), + }, + + // no halving + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: nil, + HalvingPeriod: big.NewInt(100), + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1e18), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: nil, + HalvingTimes: 10, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + HalvingTimes: 0, + NoRewardHereAfter: big.NewInt(101), + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, } - for _, testcase := range testcases { - halved := halveRewards(testcase.reward, testcase.period, testcase.past) - if testcase.expected.Cmp(halved) != 0 { - t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", testcase.expected, halved) + for _, tc := range testcases { + actual := getBriocheBlockReward(tc.briocheConfig, tc.blockNum) + if tc.expected.Cmp(actual) != 0 { + t.Errorf("getBriocheReward mismatched (expected=%v, actual=%v)", tc.expected, actual) } } } diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index ea90b90f42a0..edd6ba2f9f2d 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -6,39 +6,9 @@ import ( "encoding/json" "github.com/ethereum/go-ethereum/common" "math/big" - "strconv" "testing" - "time" ) -func TestBlockMintingQuantity(t *testing.T) { - minted, _ := big.NewInt(0).SetString("379886454000000000000000000", 0) // 379,886,454 wemix - annualReward, _ := big.NewInt(0).SetString("500000000000000000", 0) // 0.5 wemix - t.Logf("initial minted = %d", minted) - t.Logf("reward = %d", annualReward) - for i := 2024; i < 2054; i += 2 { - minted = new(big.Int).Add(minted, new(big.Int).Mul(annualReward, big.NewInt(63115200))) - annualReward = new(big.Int).Div(annualReward, big.NewInt(2)) - t.Logf("minted = %d", minted) - t.Logf("reward = %d", annualReward) - } - for i := 2054; i < 2100; i += 2 { - minted = new(big.Int).Add(minted, new(big.Int).Mul(annualReward, big.NewInt(63115200))) - } - t.Logf("total minted = %d", minted) -} - -func TestHalvesPeriod(t *testing.T) { - halvesTime := big.NewInt(1719792000) // 2024-07-01 00:00:00 (UTC) - blockPeriod := big.NewInt(63_115_200) // 86400 * 730.5 - loc, _ := time.LoadLocation("UTC") - for i := 0; i < 17; i++ { - tm, _ := strconv.ParseInt(halvesTime.String(), 10, 64) - t.Logf("~ %v(%d)", time.Unix(tm, 0).In(loc), halvesTime) - halvesTime.Add(halvesTime, blockPeriod) - } -} - // TestDistributeRewards tests the DistributeRewards function func TestDistributeRewards(t *testing.T) { From bb64392962a0e10cb43bfe8da9e3252f4c3eb610 Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 14:22:20 +0900 Subject: [PATCH 03/21] fix: apply review; valdiate in consensus --- consensus/ethash/consensus.go | 15 +++++++++++++++ core/block_validator.go | 10 ---------- core/blockchain.go | 8 +------- core/blockchain_test.go | 8 +------- core/state_processor.go | 8 ++++---- core/types.go | 5 +---- eth/state_accessor.go | 2 +- wemix/admin_test.go | 3 ++- wemix/rewards_test.go | 3 ++- 9 files changed, 27 insertions(+), 35 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 4ab4413b8443..47c0aa230220 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -604,10 +604,25 @@ 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) error { + var proposedReward []byte + if header.Rewards != nil { + // `accumulateRewards` updates `header.Rewards` field, + // so we save `header.Rewards` of proposed block before calling the function + // But if we're a miner and are making a block, the field should be nil. + proposedReward = header.Rewards + } + // Accumulate any block and uncle rewards and commit the final state root if err := accumulateRewards(chain.Config(), state, header, uncles); err != nil { return err } + + if proposedReward != nil { + // validate the rewards from the proposed block with calculated value locally + if !bytes.Equal(header.Rewards, proposedReward) { + return fmt.Errorf("invalid rewards (remote: %x local: %x)", proposedReward, header.Rewards) + } + } header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) return nil } diff --git a/core/block_validator.go b/core/block_validator.go index 7b5f8f8ad518..028beadc491a 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -17,7 +17,6 @@ package core import ( - "bytes" "fmt" "math/big" @@ -109,15 +108,6 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return nil } -// ValidateReward validates the reward info of remote block with the reward processed locally -func (v *BlockValidator) ValidateReward(header *types.Header, localHeader *types.Header) error { - // verify rewards info - if !bytes.Equal(header.Rewards, localHeader.Rewards) { - return fmt.Errorf("invalid rewards (remote: %x local: %x)", header.Rewards, localHeader.Rewards) - } - return nil -} - // CalcGasLimit computes the gas limit of the next block after parent. It aims // to keep the baseline gas close to the provided target, and increase it towards // the target if the baseline gas is lower. diff --git a/core/blockchain.go b/core/blockchain.go index 8a570e7fa758..e5a6291ba581 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1636,7 +1636,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - appliedHeader, receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) @@ -1662,12 +1662,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err } - - if err := bc.validator.ValidateReward(block.Header(), appliedHeader); err != nil { - bc.reportBlock(block, receipts, err) - atomic.StoreUint32(&followupInterrupt, 1) - return it.index, err - } proctime := time.Since(start) // Update the metrics touched during block validation diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 568bc677ef29..03b70d26c803 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -159,7 +159,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - appliedHeader, receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) + receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err @@ -171,12 +171,6 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { return err } - err = blockchain.validator.ValidateReward(block.Header(), appliedHeader) - if err != nil { - blockchain.reportBlock(block, receipts, err) - return err - } - blockchain.chainmu.MustLock() rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1))) rawdb.WriteBlock(blockchain.db, block) diff --git a/core/state_processor.go b/core/state_processor.go index 743d3d6f98ed..e2b2c1fc5efc 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -56,7 +56,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*types.Header, types.Receipts, []*types.Log, uint64, *big.Int, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -77,12 +77,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { - return nil, nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { - return nil, nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) @@ -90,7 +90,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) - return header, receipts, allLogs, *usedGas, fees, nil + return receipts, allLogs, *usedGas, fees, nil } func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { diff --git a/core/types.go b/core/types.go index 3c565d1118cf..ae909a48fb1f 100644 --- a/core/types.go +++ b/core/types.go @@ -34,9 +34,6 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error - - // ValidateReward validates the reward info of the block header with the reward processed in local - ValidateReward(header *types.Header, localHeader *types.Header) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -52,5 +49,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*types.Header, types.Receipts, []*types.Log, uint64, *big.Int, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 481909968fd0..98be012f3326 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/wemix/admin_test.go b/wemix/admin_test.go index d3c466f613e9..98b08f15852a 100644 --- a/wemix/admin_test.go +++ b/wemix/admin_test.go @@ -1,9 +1,10 @@ package wemix import ( - "github.com/ethereum/go-ethereum/params" "math/big" "testing" + + "github.com/ethereum/go-ethereum/params" ) func TestHalveRewards(t *testing.T) { diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index edd6ba2f9f2d..595be9ee03b4 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -4,9 +4,10 @@ package wemix import ( "encoding/json" - "github.com/ethereum/go-ethereum/common" "math/big" "testing" + + "github.com/ethereum/go-ethereum/common" ) // TestDistributeRewards tests the DistributeRewards function From 098f5e85a14eb46a21e678abd9c354e71fc92bab Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 16:00:58 +0900 Subject: [PATCH 04/21] fix: halving rate, fix ci-lint --- build/checksums.txt | 50 +++++++- build/ci.go | 2 +- consensus/ethash/consensus.go | 2 +- params/config.go | 13 ++- wemix/admin.go | 11 +- wemix/admin_test.go | 214 ++++++++++++++++++++++++++++------ 6 files changed, 241 insertions(+), 51 deletions(-) diff --git a/build/checksums.txt b/build/checksums.txt index 4b7056e119dd..64c7fa70e77c 100644 --- a/build/checksums.txt +++ b/build/checksums.txt @@ -33,4 +33,52 @@ e9cb6f691e62a4d8b28dd52d2eab57cca72acfd5083b3c5417a72d2eb64def09 golangci-lint- d058dfb0c7fbd73be70f285d3f8d4d424192fe9b19760ddbb0b2c4b743b8656c golangci-lint-1.49.0-windows-amd64.zip c049d80297228db7065eabeac5114f77f04415dcd9b944e8d7c6426d9dd6e9dd golangci-lint-1.49.0-windows-arm64.zip ec9164bab7134ddb94f51c17fd37c109b0801ecd5494b6c0e27ca7898fbd7469 golangci-lint-1.49.0-windows-armv6.zip -68fd9e880d98073f436c58b6f6d2c141881ef49b06ca31137bc19da4e4e3b996 golangci-lint-1.49.0-windows-armv7.zip \ No newline at end of file +68fd9e880d98073f436c58b6f6d2c141881ef49b06ca31137bc19da4e4e3b996 golangci-lint-1.49.0-windows-armv7.zip + +01301af199161c5ea33a0350581e1bed43289a56b2fbed7539f99fca2f898278 golangci-lint-1.54.2-freebsd-amd64.tar.gz +03f1962e95066105476b8eb88b5685a95aba8ee0b41f5e986f2a61a4e1834738 golangci-lint-1.54.2-linux-mips64le.tar.gz +0a57b225c621b465bd67b7cab87e9e13063704468eb382653b5c163ec23abaa4 golangci-lint-1.54.2-windows-armv6.zip +1762e02abd5d2d7997c04682d8efe9faaa23760fa73b8a50cffc57e68f1b1173 golangci-lint-1.54.2-linux-386.deb +17c9ca05253efe833d47f38caf670aad2202b5e6515879a99873fabd4c7452b3 golangci-lint-1.54.2-linux-amd64.tar.gz +18172629827a2d595fff6a9933d9d20dc8e8ec140ad00a38e4d5723dcb7ed305 golangci-lint-1.54.2-linux-armv6.deb +19b389bc965b1fc687087dc34f09e7188f582d106c88c6c506b384be1e6656f6 golangci-lint-1.54.2-linux-amd64.rpm +2268895575ac5921633c8dd94844dea5765f1011c7814bcb73429120849a8f67 golangci-lint-1.54.2-linux-mips64le.deb +2e1a11787b08137ba16c3be6ab87de3ef5de470758b7a42441497f259e8e5a1d golangci-lint-1.54.2-windows-386.zip +30e7440fa14e2a9f4a9b9b825c61c1d6858073c74ea0aba3b2d42f08a81f4c4b golangci-lint417421bc38ff51dc18a75aa132df94a67ea078b2af2d973cdf3b1badca4b58bc golangci-lint-1.54.2-linux-ppc64le.tar.gz +42eb5bf1c935eb98f6c9ee1ce282ca0abe48475ea6bc8f682967855bd0bc5bdc golangci-lint-1.54.2-linux-mips64.rpm +50aaf3eb9b51efd2cb8ebab91725fbb1fae55d3399b66b3733118facb5087828 golangci-lint-1.54.2-source.tar.gz +522d26f9f2fefa28cc229d4acdef47e436637b7c5eaec4426217afa9061825c0 golangci-lint-1.54.2-linux-ppc64le.deb +58e0cd3b77cf0d30f59be804796a7fcae96b9fbd80c0ca7c824ec4b09de804c0 golangci-lint-1.54.2-linux-386.rpm +6a324882e39b47939b8bec23f04449c730123c9d2ff771c3f40942ab37a26d8d golangci-lint-1.54.2-windows-arm64.zip +7038b5c5cd477efbb1c828fbdf075704033f96f33598d72765f23aa87c286583 golangci-lint-1.54.2-linux-arm64.rpm +78c9f1acc357b8f9ba2a57dac40d61a091299877b1eeef6d1599f8a8715c023e golangci-lint-1.54.2-linux-loong64.tar.gz +7b11ea9de9aad90e55c66434a8a4cd30da0792b45e12a4bb5fbfd684042176fe golangci-lint-1.54.2-freebsd-armv6.tar.gz +7b33fb1be2f26b7e3d1f3c10ce9b2b5ce6d13bb1d8468a4b2ba794f05b4445e1 golangci-lint-1.54.2-darwin-arm64.tar.gz +7ce3a9e62a8835907300830aa9ac3c2e8e424c2bf98a4dcfe7e5221bd0272a89 golangci-lint-1.54.2-linux-riscv64.rpm +7eda41281f5fd722f4b60c86259bd6ece5eeef08d94c73e55987b45763d5ac9f golangci-lint-1.54.2-freebsd-armv7.tar.gz +8125ce58581d795356a09c71f82ce4fb9368e06b7ce393676907661b62c1c55b golangci-lint-1.54.2-linux-riscv64.tar.gz +839b4b620f75d9d827ec4f7ed1cf592f730a3fc5c6e2bf01f64c03ddd8d4d76c golangci-lint-1.54.2-netbsd-386.tar.gz +85674f50113bb49ffae80733b4ad493c7b58a6031ee7ded3fda2a0ae1bf94c88 golangci-lint-1.54.2-linux-loong64.rpm +860e2ab2453e7fa81d28487b91b6e479d9414baa464abac8ce1bb8f203affeec golangci-lint-1.54.2-linux-s390x.deb +8b3e8c296a9714641961e87403e4865787f16bfdce6e1ea31d46978cfbdb64e7 golangci-lint-1.54.2-linux-arm64.deb +8e42c8204dc2441b5f6873032f1f674b6b6946042d759bc244194d14da8ee7c5 golangci-lint-1.54.2-linux-riscv64.deb +8e8754108de4d8accf6bf209780b8a31ffee0d04eff6393e5a3c84c84991cd39 golangci-lint-1.54.2-linux-s390x.tar.gz +8fc12d9ab18982968a568619290c02c6a9622e29eb475db8b6eac4c1ffc1121a golangci-lint-1.54.2-linux-mips64.deb +9124be2628a93e4d4a65bf48dd049f85c20d1236e66ac321169490745f5de20e golangci-lint-1.54.2-netbsd-armv6.tar.gz +925c4097eae9e035b0b052a66d0a149f861e2ab611a4e677c7ffd2d4e05b9b89 golangci-lint-1.54.2-darwin-amd64.tar.gz +9db1154c2bfdab52822afb7ea8d505ef9e3d519402258f93a9809f4b871b9446 golangci-lint-1.54.2-freebsd-386.tar.gz +9fadc9ed7fe9a2c3abcb9113a580004d3262dbdef0d44d4271d1b7ad89657e7b golangci-lint-1.54.2-linux-s390x.rpm +a0fb8740e9771ff0b96511b04017940ada4c3d1e07b2e3c246a5078514c1be9e golangci-lint-1.54.2-illumos-amd64.tar.gz +a47ed1a6473ff9ccd2cc29d85fc197b6718fad8afcec3ae804070002f5d5f8a8 golangci-lint-1.54.2-netbsd-amd64.tar.gz +a9f14b33473c65fcfbf411ec054b53a87dbb849f4e09ee438f1ee76dbf3f3d4e golangci-lint-1.54.2-linux-arm64.tar.gz +abf8e2164bca508ab68e520ca51e887ca26498ff4c57e904a27cce05b426c81d golangci-lint-1.54.2-lintae6c9e000cdac4f39b64d01b4e8091a1600d97ffccbd1883a342e4693aade68f golangci-lint-1.54.2-linux-mips64le.rpm +b6352e83cc781c5f7e7b74d1e0e6dc137a9a200c3e852bb14009c01b8b19a7b1 golangci-lint-1.54.2-linux-armv6.tar.gz +c3a693f657488a928fc0bdd76ea242b33e2b0e77748b1f31db8a85eb84cf7954 golangci-lint-1.54.2-linux-ppc64le.rpm +c5fd737f6e3c49e168ff764b48ad4c212131d933538aa5973a60d85b8d030926 golangci-lint-1.54.2-linux-armv7.tar.gz +ce17d122f3f93e0a9e52009d2c03cc1c1a1ae28338c2702a1f53eccd10a1afa3 golangci-lint-1.54.2-windows-amd64.zip +d00b58c3fea03e314b5f0f8680606781d3c3a3428e6a4c8645997d537acd1992 golangci-lint-1.54.2-linux-mips64.tar.gz +e3091e11f040a7177f6c227514b61301189fb1bf37f4fb9da2efa041456ab81d golangci-lint-1.54.2-linux-armv7.deb +e4cfe85c6e39b7dab9d8b7f668222d9066ee8f064cd3dbd4153d15b280d636f4 golangci-lint-1.54.2-netbsd-armv7.tar.gz +f0df36248c26e30f8dd516ffc0f2158d450df733f684ddd429e3aa2d5cc23bfb golangci-lint-1.54.2-linux-armv6.rpm +f2b801c93ccfbd3d17132360f632c0281f78e5780f02727115172a33afd0b08e golangci-lint-1.54.2-linux-loong64.deb +f67b8fa171605747da66f93892257337d7ae71be59ca7b07548555f025abe836 golangci-lint-1.54.2-linux-386.tar.gz diff --git a/build/ci.go b/build/ci.go index d6431ecd5fcc..8628af54cf37 100644 --- a/build/ci.go +++ b/build/ci.go @@ -343,7 +343,7 @@ func doLint(cmdline []string) { // downloadLinter downloads and unpacks golangci-lint. func downloadLinter(cachedir string) string { - const version = "1.49.0" + const version = "1.54.2" csdb := build.MustLoadChecksums("build/checksums.txt") arch := runtime.GOARCH diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 47c0aa230220..a2180e9b3e78 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -617,7 +617,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types. return err } - if proposedReward != nil { + if proposedReward != nil && chain.Config().IsBrioche(header.Number) { // validate the rewards from the proposed block with calculated value locally if !bytes.Equal(header.Rewards, proposedReward) { return fmt.Errorf("invalid rewards (remote: %x local: %x)", proposedReward, header.Rewards) diff --git a/params/config.go b/params/config.go index 34962e69d351..2a384d10ce3a 100644 --- a/params/config.go +++ b/params/config.go @@ -424,12 +424,13 @@ type EthashConfig struct{} // Brioche halving configuration type BriocheConfig struct { - // if the chain is on brioche hard fork, `RewardAmount` of gov contract is not used rather this `BlockReward` is used - BlockReward *big.Int `json:"blockReward,omitempty"` // if nil, then default block reward is 1e18 (=1 wemix) - FirstHalvingBlock *big.Int `json:"firstHalvingBlock,omitempty"` // if nil, then halving is not work. including this block - HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // if nil, then halving is not work - HalvingTimes uint64 `json:"halvingTimes,omitempty"` // if 0, then no halving - NoRewardHereAfter *big.Int `json:"noRewardHereAfter,omitempty"` // if nil, block reward goes on endlessly + // if the chain is on brioche hard fork, `RewardAmount` of gov contract is not used rather this BlockReward is used + BlockReward *big.Int `json:"blockReward,omitempty"` // nil - use default block reward(1e18) + FirstHalvingBlock *big.Int `json:"firstHalvingBlock,omitempty"` // nil - halving is not work. including this block + HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // nil - halving is not work + NoRewardHereafter *big.Int `json:"noRewardHereafter,omitempty"` // nil - block reward goes on endlessly + HalvingTimes uint64 `json:"halvingTimes,omitempty"` // 0 - no halving + HalvingRate uint32 `json:"halvingRate,omitempty"` // 0<=HalvingRate<=100; 0 - no reward on halving; 100 - no halving } // String implements the stringer interface, returning the consensus engine details. diff --git a/wemix/admin.go b/wemix/admin.go index e3e86cd450ec..a763eb1cad19 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -1216,8 +1216,8 @@ func getBriocheBlockReward(brioche *params.BriocheConfig, num *big.Int) *big.Int if brioche.BlockReward != nil { blockReward = big.NewInt(0).Set(brioche.BlockReward) } - if brioche.NoRewardHereAfter != nil && - brioche.NoRewardHereAfter.Cmp(num) <= 0 { + if brioche.NoRewardHereafter != nil && + brioche.NoRewardHereafter.Cmp(num) <= 0 { blockReward = big.NewInt(0) } else if brioche.FirstHalvingBlock != nil && brioche.HalvingPeriod != nil && @@ -1225,7 +1225,7 @@ func getBriocheBlockReward(brioche *params.BriocheConfig, num *big.Int) *big.Int num.Cmp(brioche.FirstHalvingBlock) >= 0 { past := big.NewInt(0).Set(num) past.Sub(past, brioche.FirstHalvingBlock) - blockReward = halveRewards(blockReward, brioche.HalvingPeriod, past, brioche.HalvingTimes) + blockReward = halveRewards(blockReward, brioche.HalvingPeriod, past, brioche.HalvingTimes, brioche.HalvingRate) } } return blockReward @@ -1235,11 +1235,12 @@ func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance return admin.calculateRewards(config, num, fees, addBalance) } -func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int, halvingTimes uint64) *big.Int { +func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int, halvingTimes uint64, halvingRate uint32) *big.Int { result := big.NewInt(0).Set(baseReward) past := big.NewInt(0).Set(pastBlocks) for ; halvingTimes > 0; halvingTimes-- { - result = result.Div(result, big.NewInt(2)) + result = result.Mul(result, big.NewInt(int64(halvingRate))) + result = result.Div(result, big.NewInt(100)) if past.Cmp(halvePeriod) < 0 { break } diff --git a/wemix/admin_test.go b/wemix/admin_test.go index 98b08f15852a..dcca75724a9a 100644 --- a/wemix/admin_test.go +++ b/wemix/admin_test.go @@ -13,45 +13,53 @@ func TestHalveRewards(t *testing.T) { period *big.Int past *big.Int times uint64 + rate uint32 expected *big.Int }{ // sample test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, 50, big.NewInt(25e16)}, // times test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, big.NewInt(1e18)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, big.NewInt(1e18)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, 50, big.NewInt(25e16)}, + + // rate test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 10, big.NewInt(1e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(200), 3, 10, big.NewInt(1e15)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 20, big.NewInt(4e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 30, big.NewInt(9e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 90, big.NewInt(81e16)}, // brioche halving test - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, big.NewInt(125e15)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, big.NewInt(625e14)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, big.NewInt(3125e13)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, big.NewInt(15625e12)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, big.NewInt(78125e11)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, big.NewInt(390625e10)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, big.NewInt(1953125e9)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, big.NewInt(9765625e8)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, big.NewInt(48828125e7)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, big.NewInt(244140625e6)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, big.NewInt(1220703125e5)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, big.NewInt(6103515625e4)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, big.NewInt(30517578125e3)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, big.NewInt(152587890625e2)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, 50, big.NewInt(125e15)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, 50, big.NewInt(625e14)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, 50, big.NewInt(3125e13)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, 50, big.NewInt(15625e12)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, 50, big.NewInt(78125e11)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, 50, big.NewInt(390625e10)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, 50, big.NewInt(1953125e9)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, 50, big.NewInt(9765625e8)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, 50, big.NewInt(48828125e7)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, 50, big.NewInt(244140625e6)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, 50, big.NewInt(1220703125e5)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, 50, big.NewInt(6103515625e4)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, 50, big.NewInt(30517578125e3)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, 50, big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, 50, big.NewInt(152587890625e2)}, } for _, tc := range testcases { - halved := halveRewards(tc.reward, tc.period, tc.past, tc.times) + halved := halveRewards(tc.reward, tc.period, tc.past, tc.times, tc.rate) if tc.expected.Cmp(halved) != 0 { t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) } @@ -70,12 +78,25 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(5e17), }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(201), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(25e16), + }, // base block reward variations { @@ -83,8 +104,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(7e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(35e17), @@ -94,8 +116,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(3), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(1), @@ -105,8 +128,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(0), @@ -116,8 +140,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: nil, // it will use the default block reward FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(5e17), @@ -129,8 +154,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: nil, // it will use the default block reward FirstHalvingBlock: nil, HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(1e18), @@ -140,8 +166,9 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: nil, + NoRewardHereafter: big.NewInt(101), HalvingTimes: 10, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(10), @@ -151,12 +178,125 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), HalvingTimes: 0, - NoRewardHereAfter: big.NewInt(101), + HalvingRate: 50, }, blockNum: big.NewInt(100), expected: big.NewInt(10), }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(99), // not yet halving time + expected: big.NewInt(10), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 100, // no halving rate + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + + // no reward case + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 0, // no reward + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(199), + expected: big.NewInt(5), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(0), + }, + + // halving rate variations + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1e17), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(1e16), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 1, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(1e12), + }, + { + briocheConfig: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 99, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(970299e12), + }, } for _, tc := range testcases { From c1bed51dcfd75892c1ff0c9862bce860182fb75d Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 18:18:08 +0900 Subject: [PATCH 05/21] fix: move halving logic to BriocheConfig --- core/blockchain_test.go | 26 ---- params/config.go | 43 +++++- params/config_test.go | 315 ++++++++++++++++++++++++++++++++++++++++ wemix/admin.go | 39 +---- wemix/admin_test.go | 308 --------------------------------------- 5 files changed, 359 insertions(+), 372 deletions(-) delete mode 100644 wemix/admin_test.go diff --git a/core/blockchain_test.go b/core/blockchain_test.go index 03b70d26c803..cda30f0f5860 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -164,7 +164,6 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees) if err != nil { blockchain.reportBlock(block, receipts, err) @@ -3771,28 +3770,3 @@ func TestSetCanonical(t *testing.T) { chain.SetCanonical(canon[DefaultCacheConfig.TriesInMemory-1]) verify(canon[DefaultCacheConfig.TriesInMemory-1]) } - -func TestRewardValidation(t *testing.T) { - // Configure and generate a sample block chain - var ( - db = rawdb.NewMemoryDatabase() - key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000) - deleteAddr = common.Address{1} - gspec = &Genesis{ - Config: ¶ms.ChainConfig{ChainID: big.NewInt(1), EIP150Block: big.NewInt(0), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}, - Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}}, - } - genesis = gspec.MustCommit(db) - ) - - blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) - defer blockchain.Stop() - - blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, nil) - - if _, err := blockchain.InsertChain(blocks); err != nil { - t.Fatal(err) - } -} diff --git a/params/config.go b/params/config.go index 2a384d10ce3a..2e7c12fd9754 100644 --- a/params/config.go +++ b/params/config.go @@ -165,6 +165,9 @@ var ( BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(53_557_371), HalvingPeriod: big.NewInt(63_115_200), + NoRewardHereafter: big.NewInt(1_000_000_000), // TODO fix last reward block + HalvingTimes: 16, + HalvingRate: 50, }, } @@ -186,12 +189,15 @@ var ( LondonBlock: big.NewInt(0), PangyoBlock: big.NewInt(10_000_000), ApplepieBlock: big.NewInt(26_240_268), - BriocheBlock: big.NewInt(60_537_845), // 24-06-17 02:00:00 (UTC) expected + BriocheBlock: big.NewInt(60_537_845), // TODO fix hardfork date Ethash: new(EthashConfig), Brioche: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(60_537_845), HalvingPeriod: big.NewInt(63_115_200), + NoRewardHereafter: big.NewInt(1_000_000_000), // TODO fix last reward block + HalvingTimes: 16, + HalvingRate: 50, }, } @@ -433,6 +439,41 @@ type BriocheConfig struct { HalvingRate uint32 `json:"halvingRate,omitempty"` // 0<=HalvingRate<=100; 0 - no reward on halving; 100 - no halving } +func (bc *BriocheConfig) GetBriocheBlockReward(defaultReward *big.Int, num *big.Int) *big.Int { + blockReward := big.NewInt(0).Set(defaultReward) // default brioche block reward + if bc != nil { + if bc.BlockReward != nil { + blockReward = big.NewInt(0).Set(bc.BlockReward) + } + if bc.NoRewardHereafter != nil && + bc.NoRewardHereafter.Cmp(num) <= 0 { + blockReward = big.NewInt(0) + } else if bc.FirstHalvingBlock != nil && + bc.HalvingPeriod != nil && + bc.HalvingTimes > 0 && + num.Cmp(bc.FirstHalvingBlock) >= 0 { + blockReward = bc.halveRewards(blockReward, num) + } + } + return blockReward +} + +func (bc *BriocheConfig) halveRewards(baseReward *big.Int, num *big.Int) *big.Int { + result := big.NewInt(0).Set(baseReward) + past := big.NewInt(0).Set(num) + past.Sub(past, bc.FirstHalvingBlock) + halvingTimes := bc.HalvingTimes + for ; halvingTimes > 0; halvingTimes-- { + result = result.Mul(result, big.NewInt(int64(bc.HalvingRate))) + result = result.Div(result, big.NewInt(100)) + if past.Cmp(bc.HalvingPeriod) < 0 { + break + } + past = past.Sub(past, bc.HalvingPeriod) + } + return result +} + // String implements the stringer interface, returning the consensus engine details. func (c *EthashConfig) String() string { return "ethash" diff --git a/params/config_test.go b/params/config_test.go index 3c8ebaf4a511..9e528f3cf0b1 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -96,3 +96,318 @@ func TestCheckCompatible(t *testing.T) { } } } + +func TestHalveRewards(t *testing.T) { + testcases := []struct { + reward *big.Int + period *big.Int + past *big.Int + times uint64 + rate uint32 + expected *big.Int + }{ + // sample test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, 50, big.NewInt(25e16)}, + + // times test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, 50, big.NewInt(1e18)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, 50, big.NewInt(25e16)}, + + // rate test + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 10, big.NewInt(1e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(200), 3, 10, big.NewInt(1e15)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 20, big.NewInt(4e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 30, big.NewInt(9e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 90, big.NewInt(81e16)}, + + // brioche halving test + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, 50, big.NewInt(5e17)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, 50, big.NewInt(25e16)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, 50, big.NewInt(125e15)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, 50, big.NewInt(625e14)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, 50, big.NewInt(3125e13)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, 50, big.NewInt(15625e12)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, 50, big.NewInt(78125e11)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, 50, big.NewInt(390625e10)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, 50, big.NewInt(1953125e9)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, 50, big.NewInt(9765625e8)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, 50, big.NewInt(48828125e7)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, 50, big.NewInt(244140625e6)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, 50, big.NewInt(1220703125e5)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, 50, big.NewInt(6103515625e4)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, 50, big.NewInt(30517578125e3)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, 50, big.NewInt(152587890625e2)}, + {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, 50, big.NewInt(152587890625e2)}, + } + + for _, tc := range testcases { + brioche := &BriocheConfig{ + BlockReward: tc.reward, + FirstHalvingBlock: big.NewInt(0), + HalvingPeriod: tc.period, + HalvingTimes: tc.times, + HalvingRate: tc.rate, + } + halved := brioche.halveRewards(tc.reward, tc.period) + if tc.expected.Cmp(halved) != 0 { + t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) + } + } +} + +func TestGetBriocheBlockReward(t *testing.T) { + defaultBlockReward := big.NewInt(1234e14) + testcases := []struct { + briocheConfig *BriocheConfig + blockNum *big.Int + expected *big.Int + }{ + // nil case + { + briocheConfig: nil, + blockNum: big.NewInt(100), + expected: defaultBlockReward, + }, + + // normal case + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(5e17), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(201), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(25e16), + }, + + // base block reward variations + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(7e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(35e17), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(3), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(5e17), + }, + + // no halving + { + briocheConfig: &BriocheConfig{ + BlockReward: nil, // it will use the default block reward + FirstHalvingBlock: nil, + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1e18), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: nil, + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(101), + HalvingTimes: 0, + HalvingRate: 50, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(99), // not yet halving time + expected: big.NewInt(10), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 100, // no halving rate + }, + blockNum: big.NewInt(100), + expected: big.NewInt(10), + }, + + // no reward case + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 0, // no reward + }, + blockNum: big.NewInt(100), + expected: big.NewInt(0), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(199), + expected: big.NewInt(5), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(10), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(200), + HalvingTimes: 10, + HalvingRate: 50, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(0), + }, + + // halving rate variations + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(100), + expected: big.NewInt(1e17), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 10, + }, + blockNum: big.NewInt(200), + expected: big.NewInt(1e16), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 1, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(1e12), + }, + { + briocheConfig: &BriocheConfig{ + BlockReward: big.NewInt(1e18), + FirstHalvingBlock: big.NewInt(100), + HalvingPeriod: big.NewInt(100), + NoRewardHereafter: big.NewInt(1000), + HalvingTimes: 10, + HalvingRate: 99, + }, + blockNum: big.NewInt(300), + expected: big.NewInt(970299e12), + }, + } + + for _, tc := range testcases { + actual := tc.briocheConfig.GetBriocheBlockReward(defaultBlockReward, tc.blockNum) + if tc.expected.Cmp(actual) != 0 { + t.Errorf("getBriocheReward mismatched (expected=%v, actual=%v)", tc.expected, actual) + } + } +} diff --git a/wemix/admin.go b/wemix/admin.go index a763eb1cad19..65fbb4e3920a 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -127,7 +127,7 @@ var ( etcdClusterName = "Wemix" big0 = big.NewInt(0) nilAddress = common.Address{} - defaultBriocheBlockReward = int64(1e18) + defaultBriocheBlockReward = big.NewInt(1e18) admin *wemixAdmin ErrAlreadyRunning = errors.New("already running") @@ -1182,7 +1182,7 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi var blockReward *big.Int if config.IsBrioche(num) { - blockReward = getBriocheBlockReward(config.Brioche, num) + blockReward = config.Brioche.GetBriocheBlockReward(defaultBriocheBlockReward, num) } else { // if the wemix chain is not on brioche hard fork, use the `rewardAmount` from gov contract blockReward = big.NewInt(0).Set(rp.rewardAmount) @@ -1210,45 +1210,10 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi return } -func getBriocheBlockReward(brioche *params.BriocheConfig, num *big.Int) *big.Int { - blockReward := big.NewInt(defaultBriocheBlockReward) // default brioche block reward - if brioche != nil { - if brioche.BlockReward != nil { - blockReward = big.NewInt(0).Set(brioche.BlockReward) - } - if brioche.NoRewardHereafter != nil && - brioche.NoRewardHereafter.Cmp(num) <= 0 { - blockReward = big.NewInt(0) - } else if brioche.FirstHalvingBlock != nil && - brioche.HalvingPeriod != nil && - brioche.HalvingTimes > 0 && - num.Cmp(brioche.FirstHalvingBlock) >= 0 { - past := big.NewInt(0).Set(num) - past.Sub(past, brioche.FirstHalvingBlock) - blockReward = halveRewards(blockReward, brioche.HalvingPeriod, past, brioche.HalvingTimes, brioche.HalvingRate) - } - } - return blockReward -} - func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { return admin.calculateRewards(config, num, fees, addBalance) } -func halveRewards(baseReward *big.Int, halvePeriod *big.Int, pastBlocks *big.Int, halvingTimes uint64, halvingRate uint32) *big.Int { - result := big.NewInt(0).Set(baseReward) - past := big.NewInt(0).Set(pastBlocks) - for ; halvingTimes > 0; halvingTimes-- { - result = result.Mul(result, big.NewInt(int64(halvingRate))) - result = result.Div(result, big.NewInt(100)) - if past.Cmp(halvePeriod) < 0 { - break - } - past = past.Sub(past, halvePeriod) - } - return result -} - func verifyRewards(num *big.Int, rewards string) error { return nil //return admin.verifyRewards(num, rewards) diff --git a/wemix/admin_test.go b/wemix/admin_test.go deleted file mode 100644 index dcca75724a9a..000000000000 --- a/wemix/admin_test.go +++ /dev/null @@ -1,308 +0,0 @@ -package wemix - -import ( - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/params" -) - -func TestHalveRewards(t *testing.T) { - testcases := []struct { - reward *big.Int - period *big.Int - past *big.Int - times uint64 - rate uint32 - expected *big.Int - }{ - // sample test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 4, 50, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(99), 4, 50, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 4, 50, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(101), 4, 50, big.NewInt(25e16)}, - - // times test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(0), 0, 50, big.NewInt(1e18)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 0, 50, big.NewInt(1e18)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 1, 50, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 50, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 3, 50, big.NewInt(25e16)}, - - // rate test - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 10, big.NewInt(1e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(200), 3, 10, big.NewInt(1e15)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 20, big.NewInt(4e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 30, big.NewInt(9e16)}, - {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 90, big.NewInt(81e16)}, - - // brioche halving test - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, 50, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 - 1), 16, 50, big.NewInt(5e17)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200), 16, 50, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200*2 - 1), 16, 50, big.NewInt(25e16)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 2), 16, 50, big.NewInt(125e15)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 3), 16, 50, big.NewInt(625e14)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 4), 16, 50, big.NewInt(3125e13)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 5), 16, 50, big.NewInt(15625e12)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 6), 16, 50, big.NewInt(78125e11)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 7), 16, 50, big.NewInt(390625e10)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 8), 16, 50, big.NewInt(1953125e9)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 9), 16, 50, big.NewInt(9765625e8)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 10), 16, 50, big.NewInt(48828125e7)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 11), 16, 50, big.NewInt(244140625e6)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 12), 16, 50, big.NewInt(1220703125e5)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 13), 16, 50, big.NewInt(6103515625e4)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 14), 16, 50, big.NewInt(30517578125e3)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 16, 50, big.NewInt(152587890625e2)}, - {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(63115200 * 15), 17, 50, big.NewInt(152587890625e2)}, - } - - for _, tc := range testcases { - halved := halveRewards(tc.reward, tc.period, tc.past, tc.times, tc.rate) - if tc.expected.Cmp(halved) != 0 { - t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) - } - } -} - -func TestGetBriocheBlockReward(t *testing.T) { - testcases := []struct { - briocheConfig *params.BriocheConfig - blockNum *big.Int - expected *big.Int - }{ - // normal case - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(5e17), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(201), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(200), - expected: big.NewInt(25e16), - }, - - // base block reward variations - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(7e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(35e17), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(3), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(1), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(0), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: nil, // it will use the default block reward - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(5e17), - }, - - // no halving - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: nil, // it will use the default block reward - FirstHalvingBlock: nil, - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(1e18), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: nil, - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(10), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), - HalvingTimes: 0, - HalvingRate: 50, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(10), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(99), // not yet halving time - expected: big.NewInt(10), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), - HalvingTimes: 10, - HalvingRate: 100, // no halving rate - }, - blockNum: big.NewInt(100), - expected: big.NewInt(10), - }, - - // no reward case - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), - HalvingTimes: 10, - HalvingRate: 0, // no reward - }, - blockNum: big.NewInt(100), - expected: big.NewInt(0), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(199), - expected: big.NewInt(5), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(10), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), - HalvingTimes: 10, - HalvingRate: 50, - }, - blockNum: big.NewInt(200), - expected: big.NewInt(0), - }, - - // halving rate variations - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), - HalvingTimes: 10, - HalvingRate: 10, - }, - blockNum: big.NewInt(100), - expected: big.NewInt(1e17), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), - HalvingTimes: 10, - HalvingRate: 10, - }, - blockNum: big.NewInt(200), - expected: big.NewInt(1e16), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), - HalvingTimes: 10, - HalvingRate: 1, - }, - blockNum: big.NewInt(300), - expected: big.NewInt(1e12), - }, - { - briocheConfig: ¶ms.BriocheConfig{ - BlockReward: big.NewInt(1e18), - FirstHalvingBlock: big.NewInt(100), - HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), - HalvingTimes: 10, - HalvingRate: 99, - }, - blockNum: big.NewInt(300), - expected: big.NewInt(970299e12), - }, - } - - for _, tc := range testcases { - actual := getBriocheBlockReward(tc.briocheConfig, tc.blockNum) - if tc.expected.Cmp(actual) != 0 { - t.Errorf("getBriocheReward mismatched (expected=%v, actual=%v)", tc.expected, actual) - } - } -} From 8402eed731778ae336b9a8955adf684e404757f2 Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 18:43:29 +0900 Subject: [PATCH 06/21] fix: fix test --- params/config.go | 7 ++++--- params/config_test.go | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/params/config.go b/params/config.go index 2e7c12fd9754..b29acc9370f0 100644 --- a/params/config.go +++ b/params/config.go @@ -436,7 +436,7 @@ type BriocheConfig struct { HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // nil - halving is not work NoRewardHereafter *big.Int `json:"noRewardHereafter,omitempty"` // nil - block reward goes on endlessly HalvingTimes uint64 `json:"halvingTimes,omitempty"` // 0 - no halving - HalvingRate uint32 `json:"halvingRate,omitempty"` // 0<=HalvingRate<=100; 0 - no reward on halving; 100 - no halving + HalvingRate uint32 `json:"halvingRate,omitempty"` // 0 - no reward on halving; 100 - no halving; >100 - increasing reward } func (bc *BriocheConfig) GetBriocheBlockReward(defaultReward *big.Int, num *big.Int) *big.Int { @@ -464,7 +464,7 @@ func (bc *BriocheConfig) halveRewards(baseReward *big.Int, num *big.Int) *big.In past.Sub(past, bc.FirstHalvingBlock) halvingTimes := bc.HalvingTimes for ; halvingTimes > 0; halvingTimes-- { - result = result.Mul(result, big.NewInt(int64(bc.HalvingRate))) + result = result.Mul(result, big.NewInt(int64(bc.HalvingRate))) // `HalvingRate` may be greater than 100 theoretically result = result.Div(result, big.NewInt(100)) if past.Cmp(bc.HalvingPeriod) < 0 { break @@ -807,7 +807,7 @@ type Rules struct { IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsBerlin, IsLondon bool IsMerge bool - IsPangyo, IsApplepie bool + IsPangyo, IsApplepie, IsBrioche bool } // Rules ensures c's ChainID is not nil. @@ -831,5 +831,6 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules { IsMerge: isMerge, IsPangyo: c.IsPangyo(num), IsApplepie: c.IsApplepie(num), + IsBrioche: c.IsBrioche(num), } } diff --git a/params/config_test.go b/params/config_test.go index 9e528f3cf0b1..db09c2ec700d 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -125,6 +125,7 @@ func TestHalveRewards(t *testing.T) { {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 20, big.NewInt(4e16)}, {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 30, big.NewInt(9e16)}, {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 90, big.NewInt(81e16)}, + {big.NewInt(1e18), big.NewInt(100), big.NewInt(100), 2, 200, big.NewInt(4e18)}, // brioche halving test {big.NewInt(1e18), big.NewInt(63115200), big.NewInt(0), 16, 50, big.NewInt(5e17)}, @@ -156,7 +157,7 @@ func TestHalveRewards(t *testing.T) { HalvingTimes: tc.times, HalvingRate: tc.rate, } - halved := brioche.halveRewards(tc.reward, tc.period) + halved := brioche.halveRewards(tc.reward, tc.past) if tc.expected.Cmp(halved) != 0 { t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) } From 1bddf95445179ef2032127b9c19fd3483cf7ff83 Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 16 May 2024 18:48:54 +0900 Subject: [PATCH 07/21] fix: fix lint error --- wemix/rewards_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 595be9ee03b4..69c8954d28a0 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -135,7 +135,7 @@ func TestDistributeRewards(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the distributeRewards function - rewards, err := distributeRewards(tt.height, tt.rp, big.NewInt(defaultBriocheBlockReward), tt.fees) + rewards, err := distributeRewards(tt.height, tt.rp, defaultBriocheBlockReward, tt.fees) rewardsString, _ := json.Marshal(rewards) if string(rewardsString) != tt.want { t.Errorf("distributeRewards() failed: %v, %v <-> %v", err, tt.want, string(rewardsString)) From 24c9a5a38f537cacc4a77419ec9a52dcce92ab21 Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 17 May 2024 14:49:28 +0900 Subject: [PATCH 08/21] fix: refactoring reward validation --- consensus/beacon/consensus.go | 10 ++-- consensus/clique/clique.go | 8 ++- consensus/consensus.go | 6 ++- consensus/ethash/consensus.go | 40 ++++++++------- consensus/ethash/ethash.go | 4 ++ core/block_validator.go | 8 ++- core/blockchain.go | 4 +- core/blockchain_test.go | 4 +- core/chain_makers.go | 3 +- core/state_processor.go | 10 ++-- core/types.go | 4 +- eth/state_accessor.go | 2 +- params/config.go | 21 ++++---- params/config_test.go | 26 ++++++++-- wemix/admin.go | 26 +++++----- wemix/rewards_test.go | 94 ++++++++++++++++++++++++++++++++--- 16 files changed, 191 insertions(+), 79 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 3611093423bc..5f2b26d45b0a 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -167,6 +167,10 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo return nil } +func (beacon *Beacon) ValidateEngineSpecific(*params.ChainConfig, *types.Header, *big.Int, interface{}) error { + return nil +} + // verifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum consensus engine. The difference between the beacon and classic is // (a) The following fields are expected to be constants: @@ -265,17 +269,17 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine, setting the final state on the header -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) error { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) (interface{}, error) { // Finalize is different with Prepare, it can be used in both block generation // and verification. So determine the consensus rules by header type. if !beacon.IsPoSHeader(header) { beacon.ethone.Finalize(chain, header, state, txs, uncles) - return nil + return nil, nil } // The block reward is no longer handled here. It's done by the // external consensus engine. header.Root = state.IntermediateRoot(true) - return nil + return nil, nil } // FinalizeAndAssemble implements consensus.Engine, setting the final state and diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 5e9b27eb2503..81f72acdbf75 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -561,11 +561,11 @@ 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) error { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) (interface{}, 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 + return nil, nil } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, @@ -578,6 +578,10 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil } +func (c *Clique) ValidateEngineSpecific(*params.ChainConfig, *types.Header, *big.Int, interface{}) error { + return nil +} + // Authorize injects a private key into the consensus engine to mint new blocks // with. func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { diff --git a/consensus/consensus.go b/consensus/consensus.go index 482c280cce04..6098eb88ad7a 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -89,8 +89,9 @@ type Engine interface { // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). + // Finalize returns engine specific output which can be validated by ValidateEngineSpecific Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header) error + uncles []*types.Header) (interface{}, error) // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards) and assembles the final block. @@ -100,6 +101,9 @@ type Engine interface { FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) + // ValidateEngineSpecific validates some header fields with processed output + ValidateEngineSpecific(config *params.ChainConfig, header *types.Header, blockFees *big.Int, output interface{}) error + // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. // diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index a2180e9b3e78..82cb2e430938 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -603,35 +603,21 @@ 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) error { - var proposedReward []byte - if header.Rewards != nil { - // `accumulateRewards` updates `header.Rewards` field, - // so we save `header.Rewards` of proposed block before calling the function - // But if we're a miner and are making a block, the field should be nil. - proposedReward = header.Rewards - } - +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) (interface{}, error) { // Accumulate any block and uncle rewards and commit the final state root if err := accumulateRewards(chain.Config(), state, header, uncles); err != nil { - return err + return nil, err } - if proposedReward != nil && chain.Config().IsBrioche(header.Number) { - // validate the rewards from the proposed block with calculated value locally - if !bytes.Equal(header.Rewards, proposedReward) { - return fmt.Errorf("invalid rewards (remote: %x local: %x)", proposedReward, header.Rewards) - } - } header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - return nil + return &FinalizedOutput{reward: header.Rewards}, 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, error) { // Finalize block - if err := ethash.Finalize(chain, header, state, txs, uncles); err != nil { + if _, err := ethash.Finalize(chain, header, state, txs, uncles); err != nil { return nil, err } @@ -650,6 +636,24 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil } +func (ethash *Ethash) ValidateEngineSpecific(config *params.ChainConfig, header *types.Header, blockFees *big.Int, output interface{}) error { + if !wemixminer.IsPoW() && header.Fees.Cmp(blockFees) != 0 { + return fmt.Errorf("invalid fees collected (remote: %v local: %v)", header.Fees, blockFees) + } + + if !wemixminer.IsPoW() && config.IsBrioche(header.Number) { + if out, ok := output.(*FinalizedOutput); ok { + // validate the rewards from the proposed block with calculated value locally + if !bytes.Equal(header.Rewards, out.reward) { + return fmt.Errorf("invalid rewards (remote: %x local: %x)", header.Rewards, out.reward) + } + } else { + return fmt.Errorf("invalid finalized output (%v)", output) + } + } + return nil +} + // SealHash returns the hash of a block prior to it being sealed. func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewLegacyKeccak256() diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index c196ad062170..98c57f636f33 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -431,6 +431,10 @@ type Config struct { Log log.Logger `toml:"-"` } +type FinalizedOutput struct { + reward []byte +} + // Ethash is a consensus engine based on proof-of-work implementing the ethash // algorithm. type Ethash struct { diff --git a/core/block_validator.go b/core/block_validator.go index 028beadc491a..dff3e8dc1f1e 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -80,14 +80,11 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int, finalizedOutput interface{}) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) } - if !wemixminer.IsPoW() && block.Fees().Cmp(fees) != 0 { - return fmt.Errorf("invalid fees collected (remote: %v local: %v)", block.Fees(), fees) - } // Validate the received block's bloom with the one derived from the generated receipts. // For valid blocks this should always validate to true. @@ -105,7 +102,8 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } - return nil + + return v.engine.ValidateEngineSpecific(v.bc.chainConfig, header, fees, finalizedOutput) } // CalcGasLimit computes the gas limit of the next block after parent. It aims diff --git a/core/blockchain.go b/core/blockchain.go index e5a6291ba581..79bb7d3d065e 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1636,7 +1636,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, fees, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, fees, fo, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) @@ -1657,7 +1657,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Validate the state using the default validator substart = time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, fees); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, fees, fo); err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index cda30f0f5860..eda7bbb30fba 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -159,12 +159,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, fees, err := blockchain.processor.Process(block, statedb, vm.Config{}) + receipts, _, usedGas, fees, fo, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees, fo) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/chain_makers.go b/core/chain_makers.go index db059c6e5071..a8a0437b9f7c 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -104,8 +104,7 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), len(b.txs)) - fees := new(big.Int) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, fees, vm.Config{}) + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, b.header.Fees, vm.Config{}) if err != nil { panic(err) } diff --git a/core/state_processor.go b/core/state_processor.go index e2b2c1fc5efc..42c0fc1615c5 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -56,7 +56,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, interface{}, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -77,20 +77,20 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, big.NewInt(0), nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { - return nil, nil, 0, big.NewInt(0), fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, big.NewInt(0), nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + fo, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) - return receipts, allLogs, *usedGas, fees, nil + return receipts, allLogs, *usedGas, fees, fo, err } func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { diff --git a/core/types.go b/core/types.go index ae909a48fb1f..2aa4fc15fb5a 100644 --- a/core/types.go +++ b/core/types.go @@ -33,7 +33,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int, finalizedOutput interface{}) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -49,5 +49,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, interface{}, error) } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 98be012f3326..481909968fd0 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/params/config.go b/params/config.go index b29acc9370f0..27310d53256e 100644 --- a/params/config.go +++ b/params/config.go @@ -459,19 +459,16 @@ func (bc *BriocheConfig) GetBriocheBlockReward(defaultReward *big.Int, num *big. } func (bc *BriocheConfig) halveRewards(baseReward *big.Int, num *big.Int) *big.Int { - result := big.NewInt(0).Set(baseReward) - past := big.NewInt(0).Set(num) - past.Sub(past, bc.FirstHalvingBlock) - halvingTimes := bc.HalvingTimes - for ; halvingTimes > 0; halvingTimes-- { - result = result.Mul(result, big.NewInt(int64(bc.HalvingRate))) // `HalvingRate` may be greater than 100 theoretically - result = result.Div(result, big.NewInt(100)) - if past.Cmp(bc.HalvingPeriod) < 0 { - break - } - past = past.Sub(past, bc.HalvingPeriod) + elapsed := new(big.Int).Sub(num, bc.FirstHalvingBlock) + times := new(big.Int).Add(common.Big1, new(big.Int).Div(elapsed, bc.HalvingPeriod)) + if times.Uint64() > bc.HalvingTimes { + times = big.NewInt(int64(bc.HalvingTimes)) } - return result + + reward := new(big.Int).Set(baseReward) + numerator := new(big.Int).Exp(big.NewInt(int64(bc.HalvingRate)), times, nil) + denominator := new(big.Int).Exp(big.NewInt(100), times, nil) + return reward.Div(reward.Mul(reward, numerator), denominator) } // String implements the stringer interface, returning the consensus engine details. diff --git a/params/config_test.go b/params/config_test.go index db09c2ec700d..0db609be3209 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -167,12 +167,14 @@ func TestHalveRewards(t *testing.T) { func TestGetBriocheBlockReward(t *testing.T) { defaultBlockReward := big.NewInt(1234e14) testcases := []struct { + id int32 briocheConfig *BriocheConfig blockNum *big.Int expected *big.Int }{ // nil case { + id: 1, briocheConfig: nil, blockNum: big.NewInt(100), expected: defaultBlockReward, @@ -180,6 +182,7 @@ func TestGetBriocheBlockReward(t *testing.T) { // normal case { + id: 2, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -192,6 +195,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(5e17), }, { + id: 3, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -206,6 +210,7 @@ func TestGetBriocheBlockReward(t *testing.T) { // base block reward variations { + id: 4, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(7e18), FirstHalvingBlock: big.NewInt(100), @@ -218,6 +223,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(35e17), }, { + id: 5, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(3), FirstHalvingBlock: big.NewInt(100), @@ -230,6 +236,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(1), }, { + id: 6, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1), FirstHalvingBlock: big.NewInt(100), @@ -242,6 +249,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(0), }, { + id: 7, briocheConfig: &BriocheConfig{ BlockReward: nil, // it will use the default block reward FirstHalvingBlock: big.NewInt(100), @@ -251,11 +259,12 @@ func TestGetBriocheBlockReward(t *testing.T) { HalvingRate: 50, }, blockNum: big.NewInt(100), - expected: big.NewInt(5e17), + expected: big.NewInt(0).Div(defaultBlockReward, big.NewInt(2)), }, // no halving { + id: 8, briocheConfig: &BriocheConfig{ BlockReward: nil, // it will use the default block reward FirstHalvingBlock: nil, @@ -265,9 +274,10 @@ func TestGetBriocheBlockReward(t *testing.T) { HalvingRate: 50, }, blockNum: big.NewInt(100), - expected: big.NewInt(1e18), + expected: defaultBlockReward, }, { + id: 9, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -280,6 +290,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(10), }, { + id: 10, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -292,6 +303,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(10), }, { + id: 11, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -304,6 +316,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(10), }, { + id: 12, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -318,6 +331,7 @@ func TestGetBriocheBlockReward(t *testing.T) { // no reward case { + id: 13, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -330,6 +344,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(0), }, { + id: 14, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -342,6 +357,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(5), }, { + id: 15, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), @@ -356,6 +372,7 @@ func TestGetBriocheBlockReward(t *testing.T) { // halving rate variations { + id: 16, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -368,6 +385,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(1e17), }, { + id: 17, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -380,6 +398,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(1e16), }, { + id: 18, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -392,6 +411,7 @@ func TestGetBriocheBlockReward(t *testing.T) { expected: big.NewInt(1e12), }, { + id: 19, briocheConfig: &BriocheConfig{ BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), @@ -408,7 +428,7 @@ func TestGetBriocheBlockReward(t *testing.T) { for _, tc := range testcases { actual := tc.briocheConfig.GetBriocheBlockReward(defaultBlockReward, tc.blockNum) if tc.expected.Cmp(actual) != 0 { - t.Errorf("getBriocheReward mismatched (expected=%v, actual=%v)", tc.expected, actual) + t.Errorf("getBriocheReward mismatched (id=%d, expected=%v, actual=%v)", tc.id, tc.expected, actual) } } } diff --git a/wemix/admin.go b/wemix/admin.go index 65fbb4e3920a..b3b0a92e1191 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -1147,17 +1147,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, blockReward *big.I return rewards, nil } -func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - rp, err := ma.getRewardParams(ctx, big.NewInt(num.Int64()-1)) - if err != nil { - // all goes to the coinbase - err = wemixminer.ErrNotInitialized - return - } - +func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { if (rp.staker == nil && rp.ecoSystem == nil && rp.maintenance == nil) || len(rp.members) == 0 { // handle testnet block 94 rewards if rewards94 := handleBlock94Rewards(num, rp, fees); rewards94 != nil { @@ -1175,7 +1165,7 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi // determine coinbase if len(rp.members) > 0 { - mix := int(num.Int64()/ma.blocksPer) % len(rp.members) + mix := int(num.Int64()/rp.blocksPer) % len(rp.members) coinbase = &common.Address{} coinbase.SetBytes(rp.members[mix].Reward.Bytes()) } @@ -1211,7 +1201,17 @@ func (ma *wemixAdmin) calculateRewards(config *params.ChainConfig, num, fees *bi } func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { - return admin.calculateRewards(config, num, fees, addBalance) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + rp, err := admin.getRewardParams(ctx, big.NewInt(num.Int64()-1)) + if err != nil { + // all goes to the coinbase + err = wemixminer.ErrNotInitialized + return nil, nil, err + } + + return calculateRewardsWithParams(config, rp, num, fees, addBalance) } func verifyRewards(num *big.Int, rewards string) error { diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 69c8954d28a0..3b4d33e4c47e 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -8,8 +8,23 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + wemixminer "github.com/ethereum/go-ethereum/wemix/miner" ) +func hexToBigInt(hexNum string) *big.Int { + if num, ok := new(big.Int).SetString(hexNum[2:], 16); ok { + return num + } else { + return nil + } +} + // TestDistributeRewards tests the DistributeRewards function func TestDistributeRewards(t *testing.T) { @@ -18,14 +33,6 @@ func TestDistributeRewards(t *testing.T) { return &address } - hexToBigInt := func(hexNum string) *big.Int { - if num, ok := new(big.Int).SetString(hexNum[2:], 16); ok { - return num - } else { - return nil - } - } - // Test cases tests := []struct { name string @@ -168,4 +175,75 @@ func TestDistributeRewards(t *testing.T) { } } +func calculateRewardsForTest(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { + rp := &rewardParameters{ + rewardAmount: big.NewInt(1e18), + staker: &common.Address{0x11}, + ecoSystem: &common.Address{0x22}, + maintenance: &common.Address{0x33}, + feeCollector: &common.Address{0x44}, + members: []*wemixMember{ + { + Staker: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Reward: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Stake: hexToBigInt("0x1a784379d99db42000000"), + }, + { + Staker: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Reward: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Stake: hexToBigInt("0xe8ef1e96ae3897800000"), + }, + { + Staker: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Reward: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Stake: hexToBigInt("0xc92b9a6adc4825c00000"), + }, + }, + blocksPer: 1, + distributionMethod: []*big.Int{big.NewInt(4000), big.NewInt(1000), big.NewInt(2500), big.NewInt(2500)}, + } + + return calculateRewardsWithParams(config, rp, num, fees, addBalance) +} + +func TestRewardValidation(t *testing.T) { + // use wemix consensus + params.ConsensusMethod = params.ConsensusPoA + wemixminer.CalculateRewardsFunc = calculateRewardsForTest + + var ( + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(1000000000) + deleteAddr = common.Address{1} + gspec = &core.Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + Brioche: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(100), + FirstHalvingBlock: big.NewInt(0), + HalvingPeriod: big.NewInt(10), + NoRewardHereafter: big.NewInt(30), + HalvingTimes: 3, + HalvingRate: 50, + }}, + Alloc: core.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: big.NewInt(0)}}, + } + genesis = gspec.MustCommit(db) + ) + + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + gspec.Config.Brioche.BlockReward = big.NewInt(200) + // TODO: core.GenerateChain does not make a wemix block including Fees, Rewards etc. + // TODO: implement wemix.GenerateChain function + blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, nil) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } +} + // EOF From 4fb0f5be0296648d10a7682aacafe5441807756d Mon Sep 17 00:00:00 2001 From: egonspace Date: Fri, 17 May 2024 17:13:50 +0900 Subject: [PATCH 09/21] test: reward validation --- core/block_validator.go | 9 ++- core/chain_makers.go | 9 ++- wemix/rewards_test.go | 123 ++++++++++++++++++++++++++++------------ 3 files changed, 104 insertions(+), 37 deletions(-) diff --git a/core/block_validator.go b/core/block_validator.go index dff3e8dc1f1e..fa01fa8e1a9f 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -97,13 +97,20 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD if receiptSha != header.ReceiptHash { return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } + + // validate engine specific; ethash.engine validate the fees and rewards + err := v.engine.ValidateEngineSpecific(v.bc.chainConfig, header, fees, finalizedOutput) + if err != nil { + return err + } + // Validate the state root against the received state root and throw // an error if they don't match. if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) } - return v.engine.ValidateEngineSpecific(v.bc.chainConfig, header, fees, finalizedOutput) + return nil } // CalcGasLimit computes the gas limit of the next block after parent. It aims diff --git a/core/chain_makers.go b/core/chain_makers.go index a8a0437b9f7c..0f4dea2c9b06 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -104,7 +104,13 @@ func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) { b.SetCoinbase(common.Address{}) } b.statedb.Prepare(tx.Hash(), len(b.txs)) - receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, b.header.Fees, vm.Config{}) + var fees *big.Int + if b.header.Fees != nil { + fees = b.header.Fees // wemix block has `Fees` field + } else { + fees = new(big.Int) + } + receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, fees, vm.Config{}) if err != nil { panic(err) } @@ -303,6 +309,7 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S }), GasLimit: parent.GasLimit(), Number: new(big.Int).Add(parent.Number(), common.Big1), + Fees: new(big.Int), Time: time, } if chain.Config().IsLondon(header.Number) { diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 3b4d33e4c47e..f8a6fff8c133 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -3,14 +3,18 @@ package wemix import ( + "bytes" + "crypto/ecdsa" "encoding/json" "math/big" + "strings" "testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" @@ -175,51 +179,51 @@ func TestDistributeRewards(t *testing.T) { } } -func calculateRewardsForTest(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { - rp := &rewardParameters{ - rewardAmount: big.NewInt(1e18), - staker: &common.Address{0x11}, - ecoSystem: &common.Address{0x22}, - maintenance: &common.Address{0x33}, - feeCollector: &common.Address{0x44}, - members: []*wemixMember{ - { - Staker: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), - Reward: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), - Stake: hexToBigInt("0x1a784379d99db42000000"), - }, - { - Staker: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), - Reward: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), - Stake: hexToBigInt("0xe8ef1e96ae3897800000"), - }, - { - Staker: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), - Reward: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), - Stake: hexToBigInt("0xc92b9a6adc4825c00000"), - }, - }, - blocksPer: 1, - distributionMethod: []*big.Int{big.NewInt(4000), big.NewInt(1000), big.NewInt(2500), big.NewInt(2500)}, +func makeCalculateRewardFunc(rp *rewardParameters) func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { + return func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { + return calculateRewardsWithParams(config, rp, num, fees, addBalance) } +} - return calculateRewardsWithParams(config, rp, num, fees, addBalance) +func makeSignBlockFunc(privateKey *ecdsa.PrivateKey) func(height *big.Int, hash common.Hash) (common.Address, []byte, error) { + return func(height *big.Int, hash common.Hash) (coinbase common.Address, sig []byte, err error) { + data := append(height.Bytes(), hash.Bytes()...) + data = crypto.Keccak256(data) + sig, _ = crypto.Sign(data, privateKey) + return crypto.PubkeyToAddress(privateKey.PublicKey), sig, nil + } +} + +func verifyBlockSigForTest(height *big.Int, coinbase common.Address, nodeId []byte, hash common.Hash, sig []byte, checkMinerLimit bool) bool { + var data []byte + data = append(height.Bytes(), hash.Bytes()...) + data = crypto.Keccak256(data) + pubKey, err := crypto.SigToPub(data, sig) + if err != nil { + return false + } + signer := crypto.PubkeyToAddress(*pubKey) + if err != nil || !bytes.Equal(coinbase.Bytes(), signer.Bytes()) { + return false + } + return true } func TestRewardValidation(t *testing.T) { // use wemix consensus params.ConsensusMethod = params.ConsensusPoA - wemixminer.CalculateRewardsFunc = calculateRewardsForTest var ( db = rawdb.NewMemoryDatabase() key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") address = crypto.PubkeyToAddress(key.PublicKey) - funds = big.NewInt(1000000000) + funds = big.NewInt(100000000000000) deleteAddr = common.Address{1} gspec = &core.Genesis{ Config: ¶ms.ChainConfig{ - ChainID: big.NewInt(1), + ChainID: big.NewInt(1), + LondonBlock: common.Big0, + BriocheBlock: common.Big0, Brioche: ¶ms.BriocheConfig{ BlockReward: big.NewInt(100), FirstHalvingBlock: big.NewInt(0), @@ -231,18 +235,67 @@ func TestRewardValidation(t *testing.T) { Alloc: core.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: big.NewInt(0)}}, } genesis = gspec.MustCommit(db) + signer = types.LatestSigner(gspec.Config) ) + rp := &rewardParameters{ + rewardAmount: big.NewInt(1e18), + staker: &common.Address{0x11}, + ecoSystem: &common.Address{0x22}, + maintenance: &common.Address{0x33}, + feeCollector: &common.Address{0x44}, + members: []*wemixMember{ + { + Staker: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Reward: common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + Stake: hexToBigInt("0x1a784379d99db42000000"), + }, + { + Staker: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Reward: common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + Stake: hexToBigInt("0xe8ef1e96ae3897800000"), + }, + { + Staker: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Reward: common.HexToAddress("0x452893ed818c0e3ea6f415aeab8ef08778087fc6"), + Stake: hexToBigInt("0xc92b9a6adc4825c00000"), + }, + }, + blocksPer: 1, + distributionMethod: []*big.Int{big.NewInt(4000), big.NewInt(1000), big.NewInt(2500), big.NewInt(2500)}, + } + + wemixminer.CalculateRewardsFunc = makeCalculateRewardFunc(rp) + wemixminer.SignBlockFunc = makeSignBlockFunc(key) + wemixminer.VerifyBlockSigFunc = verifyBlockSigForTest + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) defer blockchain.Stop() - gspec.Config.Brioche.BlockReward = big.NewInt(200) - // TODO: core.GenerateChain does not make a wemix block including Fees, Rewards etc. - // TODO: implement wemix.GenerateChain function - blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 1, nil) + byzantineConfig := ¶ms.ChainConfig{ + ChainID: gspec.Config.ChainID, + LondonBlock: gspec.Config.LondonBlock, + BriocheBlock: gspec.Config.BriocheBlock, + Brioche: ¶ms.BriocheConfig{ + BlockReward: big.NewInt(200), // different reward!! + FirstHalvingBlock: gspec.Config.Brioche.FirstHalvingBlock, + HalvingPeriod: gspec.Config.Brioche.HalvingPeriod, + NoRewardHereafter: gspec.Config.Brioche.NoRewardHereafter, + HalvingTimes: gspec.Config.Brioche.HalvingTimes, + HalvingRate: gspec.Config.Brioche.HalvingRate, + }} + blocks, _ := core.GenerateChain(byzantineConfig, genesis, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) if _, err := blockchain.InsertChain(blocks); err != nil { - t.Fatal(err) + if !strings.HasPrefix(err.Error(), "invalid rewards") { + t.Fatal(err) + } } } From ff062d2024d3f9b319e6f54a8f774fa67871ec95 Mon Sep 17 00:00:00 2001 From: egonspace Date: Mon, 20 May 2024 16:30:15 +0900 Subject: [PATCH 10/21] test: add hardfork test --- consensus/beacon/consensus.go | 6 +- consensus/clique/clique.go | 4 +- consensus/consensus.go | 5 +- consensus/ethash/consensus.go | 31 ++------- consensus/ethash/ethash.go | 4 -- core/block_validator.go | 9 +-- core/blockchain.go | 4 +- core/blockchain_test.go | 4 +- core/state_processor.go | 16 +++-- core/types.go | 6 +- eth/state_accessor.go | 2 +- wemix/rewards_test.go | 124 +++++++++++++++++++++++++++++++++- 12 files changed, 153 insertions(+), 62 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index 5f2b26d45b0a..aaac10652c03 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -269,17 +269,17 @@ func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.H } // Finalize implements consensus.Engine, setting the final state on the header -func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) (interface{}, error) { +func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) error { // Finalize is different with Prepare, it can be used in both block generation // and verification. So determine the consensus rules by header type. if !beacon.IsPoSHeader(header) { beacon.ethone.Finalize(chain, header, state, txs, uncles) - return nil, nil + return nil } // The block reward is no longer handled here. It's done by the // external consensus engine. header.Root = state.IntermediateRoot(true) - return nil, nil + return nil } // FinalizeAndAssemble implements consensus.Engine, setting the final state and diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 81f72acdbf75..86164b3067a3 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -561,11 +561,11 @@ 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) (interface{}, error) { +func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, 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, nil + return nil } // FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set, diff --git a/consensus/consensus.go b/consensus/consensus.go index 6098eb88ad7a..43d5f29661eb 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -91,7 +91,7 @@ type Engine interface { // consensus rules that happen at finalization (e.g. block rewards). // Finalize returns engine specific output which can be validated by ValidateEngineSpecific Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, - uncles []*types.Header) (interface{}, error) + uncles []*types.Header) error // FinalizeAndAssemble runs any post-transaction state modifications (e.g. block // rewards) and assembles the final block. @@ -101,9 +101,6 @@ type Engine interface { FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) - // ValidateEngineSpecific validates some header fields with processed output - ValidateEngineSpecific(config *params.ChainConfig, header *types.Header, blockFees *big.Int, output interface{}) error - // Seal generates a new sealing request for the given input block and pushes // the result into the given channel. // diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 82cb2e430938..72a25ff51c42 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -603,21 +603,21 @@ 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) (interface{}, error) { +func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) error { // Accumulate any block and uncle rewards and commit the final state root if err := accumulateRewards(chain.Config(), state, header, uncles); err != nil { - return nil, err + return err } header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number)) - return &FinalizedOutput{reward: header.Rewards}, nil + return 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, error) { // Finalize block - if _, err := ethash.Finalize(chain, header, state, txs, uncles); err != nil { + if err := ethash.Finalize(chain, header, state, txs, uncles); err != nil { return nil, err } @@ -636,24 +636,6 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), nil } -func (ethash *Ethash) ValidateEngineSpecific(config *params.ChainConfig, header *types.Header, blockFees *big.Int, output interface{}) error { - if !wemixminer.IsPoW() && header.Fees.Cmp(blockFees) != 0 { - return fmt.Errorf("invalid fees collected (remote: %v local: %v)", header.Fees, blockFees) - } - - if !wemixminer.IsPoW() && config.IsBrioche(header.Number) { - if out, ok := output.(*FinalizedOutput); ok { - // validate the rewards from the proposed block with calculated value locally - if !bytes.Equal(header.Rewards, out.reward) { - return fmt.Errorf("invalid rewards (remote: %x local: %x)", header.Rewards, out.reward) - } - } else { - return fmt.Errorf("invalid finalized output (%v)", output) - } - } - return nil -} - // SealHash returns the hash of a block prior to it being sealed. func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { hasher := sha3.NewLegacyKeccak256() @@ -716,16 +698,13 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) } else { - coinbase, rewards, err := wemixminer.CalculateRewards( + _, rewards, err := wemixminer.CalculateRewards( config, header.Number, header.Fees, func(addr common.Address, amt *big.Int) { state.AddBalance(addr, amt) }) if err == nil { header.Rewards = rewards - if coinbase != nil { - header.Coinbase = *coinbase - } } else { if err == wemixminer.ErrNotInitialized { reward := new(big.Int) diff --git a/consensus/ethash/ethash.go b/consensus/ethash/ethash.go index 98c57f636f33..c196ad062170 100644 --- a/consensus/ethash/ethash.go +++ b/consensus/ethash/ethash.go @@ -431,10 +431,6 @@ type Config struct { Log log.Logger `toml:"-"` } -type FinalizedOutput struct { - reward []byte -} - // Ethash is a consensus engine based on proof-of-work implementing the ethash // algorithm. type Ethash struct { diff --git a/core/block_validator.go b/core/block_validator.go index fa01fa8e1a9f..1bc13d8a2694 100644 --- a/core/block_validator.go +++ b/core/block_validator.go @@ -18,7 +18,6 @@ package core import ( "fmt" - "math/big" "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/core/state" @@ -80,7 +79,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error { // transition, such as amount of used gas, the receipt roots and the state root // itself. ValidateState returns a database batch if the validation was a success // otherwise nil and an error is returned. -func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int, finalizedOutput interface{}) error { +func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { header := block.Header() if block.GasUsed() != usedGas { return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) @@ -98,12 +97,6 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) } - // validate engine specific; ethash.engine validate the fees and rewards - err := v.engine.ValidateEngineSpecific(v.bc.chainConfig, header, fees, finalizedOutput) - if err != nil { - return err - } - // Validate the state root against the received state root and throw // an error if they don't match. if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { diff --git a/core/blockchain.go b/core/blockchain.go index 79bb7d3d065e..14e9ba61a6f8 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -1636,7 +1636,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Process block using the parent state as reference point substart := time.Now() - receipts, logs, usedGas, fees, fo, err := bc.processor.Process(block, statedb, bc.vmConfig) + receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig) if err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) @@ -1657,7 +1657,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals, setHead bool) // Validate the state using the default validator substart = time.Now() - if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, fees, fo); err != nil { + if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil { bc.reportBlock(block, receipts, err) atomic.StoreUint32(&followupInterrupt, 1) return it.index, err diff --git a/core/blockchain_test.go b/core/blockchain_test.go index eda7bbb30fba..6842049880ab 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -159,12 +159,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error { if err != nil { return err } - receipts, _, usedGas, fees, fo, err := blockchain.processor.Process(block, statedb, vm.Config{}) + receipts, _, usedGas, err := blockchain.processor.Process(block, statedb, vm.Config{}) if err != nil { blockchain.reportBlock(block, receipts, err) return err } - err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, fees, fo) + err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas) if err != nil { blockchain.reportBlock(block, receipts, err) return err diff --git a/core/state_processor.go b/core/state_processor.go index 42c0fc1615c5..676b98a5f49a 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "fmt" "math/big" @@ -56,7 +57,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consen // Process returns the receipts and logs accumulated during the process and // returns the amount of gas that was used in the process. If any of the // transactions failed to execute due to insufficient gas it will return an error. -func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, interface{}, error) { +func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { var ( receipts types.Receipts usedGas = new(uint64) @@ -77,20 +78,25 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg for i, tx := range block.Transactions() { msg, err := tx.AsMessage(types.MakeSigner(p.config, header.Number), header.BaseFee) if err != nil { - return nil, nil, 0, big.NewInt(0), nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { - return nil, nil, 0, big.NewInt(0), nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) + return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } receipts = append(receipts, receipt) allLogs = append(allLogs, receipt.Logs...) } // Finalize the block, applying any consensus engine specific extras (e.g. block rewards) - fo, err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) + header.Fees = fees - return receipts, allLogs, *usedGas, fees, fo, err + if !bytes.Equal(block.Header().Hash().Bytes(), header.Hash().Bytes()) { + return nil, nil, 0, fmt.Errorf("Remote block hash is different from being processed one locally (remote=%v, local=%v)", block.Header().Hash(), header.Hash()) + } + + return receipts, allLogs, *usedGas, err } func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { diff --git a/core/types.go b/core/types.go index 2aa4fc15fb5a..4c5b74a49865 100644 --- a/core/types.go +++ b/core/types.go @@ -17,8 +17,6 @@ package core import ( - "math/big" - "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -33,7 +31,7 @@ type Validator interface { // ValidateState validates the given statedb and optionally the receipts and // gas used. - ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64, fees *big.Int, finalizedOutput interface{}) error + ValidateState(block *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error } // Prefetcher is an interface for pre-caching transaction signatures and state. @@ -49,5 +47,5 @@ type Processor interface { // Process processes the state changes according to the Ethereum rules by running // the transaction messages using the statedb and applying any rewards to both // the processor (coinbase) and any included uncles. - Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, *big.Int, interface{}, error) + Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) } diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 481909968fd0..70969c3e1424 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -131,7 +131,7 @@ func (eth *Ethereum) StateAtBlock(block *types.Block, reexec uint64, base *state if current = eth.blockchain.GetBlockByNumber(next); current == nil { return nil, fmt.Errorf("block #%d not found", next) } - _, _, _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) + _, _, _, err := eth.blockchain.Processor().Process(current, statedb, vm.Config{}) if err != nil { return nil, fmt.Errorf("processing block %d failed: %v", current.NumberU64(), err) } diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index f8a6fff8c133..8d76f58d48a1 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -293,9 +293,131 @@ func TestRewardValidation(t *testing.T) { }) if _, err := blockchain.InsertChain(blocks); err != nil { - if !strings.HasPrefix(err.Error(), "invalid rewards") { + if !strings.HasPrefix(err.Error(), "Remote block hash is different") { t.Fatal(err) } + } else { + t.Fatal("Reward validation failed") + } +} + +func TestBriocheHardFork(t *testing.T) { + // use wemix consensus + params.ConsensusMethod = params.ConsensusPoA + + var ( + db = rawdb.NewMemoryDatabase() + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + address = crypto.PubkeyToAddress(key.PublicKey) + funds = big.NewInt(100000000000000000) + deleteAddr = common.Address{1} + gspec = &core.Genesis{ + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + LondonBlock: common.Big0, + BriocheBlock: big.NewInt(2), + Brioche: ¶ms.BriocheConfig{ + // 1 block reward: 1e18 + // 2 block reward: 4e17 (brioche start) + // 3 block reward: 4e17 + // 4 block reward: 2e17 (first halving) + // 5 block reward: 2e17 + // 6 block reward: 1e17 (second halving) + // 7~ block reward: 0 + BlockReward: big.NewInt(4e17), + FirstHalvingBlock: big.NewInt(4), + HalvingPeriod: big.NewInt(2), + NoRewardHereafter: big.NewInt(7), + HalvingTimes: 2, + HalvingRate: 50, + }}, + Alloc: core.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: big.NewInt(0)}}, + } + genesis = gspec.MustCommit(db) + signer = types.LatestSigner(gspec.Config) + ) + + expectedBlockReward := []*big.Int{ + big.NewInt(0), // zero block reward; not used + big.NewInt(1e18), + big.NewInt(4e17), + big.NewInt(4e17), + big.NewInt(2e17), + big.NewInt(2e17), + big.NewInt(1e17), + big.NewInt(0), + } + + miners := []common.Address{ + common.HexToAddress("0x02b4b2d83786c8ee315db2ddac704794850d2149"), + common.HexToAddress("0xb16d2494fddfa4c000deaf642d47673e5ca74e07"), + } + rp := &rewardParameters{ + rewardAmount: big.NewInt(1e18), + staker: &common.Address{0x11}, + ecoSystem: &common.Address{0x22}, + maintenance: &common.Address{0x33}, + feeCollector: &common.Address{0x44}, + members: []*wemixMember{ + { + Staker: miners[0], + Reward: miners[0], + Stake: hexToBigInt("0xFE1C215E8F838E00000"), // 75e21 (75%) + }, + { + Staker: miners[1], + Reward: miners[1], + Stake: hexToBigInt("0x54B40B1F852BDA00000"), // 25e21 (25%) + }, + }, + blocksPer: 1, + distributionMethod: []*big.Int{big.NewInt(5000), big.NewInt(0), big.NewInt(2500), big.NewInt(2500)}, // miner, staker, eco, maintenance + } + + wemixminer.CalculateRewardsFunc = makeCalculateRewardFunc(rp) + wemixminer.SignBlockFunc = makeSignBlockFunc(key) + wemixminer.VerifyBlockSigFunc = verifyBlockSigForTest + + blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + parent := genesis + for i := 1; i <= 7; i++ { + statedb, _ := blockchain.State() + miner0Bal := statedb.GetBalance(miners[0]) + miner1Bal := statedb.GetBalance(miners[1]) + + blocks, _ := core.GenerateChain(gspec.Config, parent, ethash.NewFaker(), db, 1, func(i int, gen *core.BlockGen) { + tx, err := types.SignTx(types.NewTransaction(gen.TxNonce(address), common.Address{0x00}, big.NewInt(1), params.TxGas, gen.BaseFee(), nil), signer, key) + if err != nil { + panic(err) + } + gen.AddTx(tx) + }) + + if _, err := blockchain.InsertChain(blocks); err != nil { + t.Fatal(err) + } + statedb, _ = blockchain.State() + + miner0Reward := new(big.Int).Div(expectedBlockReward[i], big.NewInt(2)) + miner0Reward = miner0Reward.Mul(miner0Reward, big.NewInt(3)) + miner0Reward = miner0Reward.Div(miner0Reward, big.NewInt(4)) + + miner1Reward := new(big.Int).Div(expectedBlockReward[i], big.NewInt(2)) + miner1Reward = miner1Reward.Div(miner1Reward, big.NewInt(4)) + + miner0Bal = new(big.Int).Add(miner0Bal, miner0Reward) + miner1Bal = new(big.Int).Add(miner1Bal, miner1Reward) + if statedb.GetBalance(miners[0]).Cmp(miner0Bal) != 0 { + t.Logf("miner bal = %v, expected = %v", statedb.GetBalance(miners[0]), miner0Bal) + t.Fatal("block reward mismatched for miner0") + } + if statedb.GetBalance(miners[1]).Cmp(miner1Bal) != 0 { + t.Fatal("block reward mismatched for miner1") + } + + parent = blocks[0] } } From 638743190f45e0af1a9f58d705456bf0b8d3ab75 Mon Sep 17 00:00:00 2001 From: egonspace Date: Mon, 20 May 2024 17:19:47 +0900 Subject: [PATCH 11/21] fix: test failure --- consensus/beacon/consensus.go | 4 ---- consensus/clique/clique.go | 4 ---- consensus/consensus.go | 1 - core/chain_makers.go | 4 +++- 4 files changed, 3 insertions(+), 10 deletions(-) diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index aaac10652c03..3611093423bc 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -167,10 +167,6 @@ func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Blo return nil } -func (beacon *Beacon) ValidateEngineSpecific(*params.ChainConfig, *types.Header, *big.Int, interface{}) error { - return nil -} - // verifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum consensus engine. The difference between the beacon and classic is // (a) The following fields are expected to be constants: diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go index 86164b3067a3..5e9b27eb2503 100644 --- a/consensus/clique/clique.go +++ b/consensus/clique/clique.go @@ -578,10 +578,6 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header * return types.NewBlock(header, txs, nil, receipts, trie.NewStackTrie(nil)), nil } -func (c *Clique) ValidateEngineSpecific(*params.ChainConfig, *types.Header, *big.Int, interface{}) error { - return nil -} - // Authorize injects a private key into the consensus engine to mint new blocks // with. func (c *Clique) Authorize(signer common.Address, signFn SignerFn) { diff --git a/consensus/consensus.go b/consensus/consensus.go index 43d5f29661eb..482c280cce04 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -89,7 +89,6 @@ type Engine interface { // // Note: The block header and state database might be updated to reflect any // consensus rules that happen at finalization (e.g. block rewards). - // Finalize returns engine specific output which can be validated by ValidateEngineSpecific Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) error diff --git a/core/chain_makers.go b/core/chain_makers.go index 0f4dea2c9b06..4db017f3d621 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -309,9 +309,11 @@ func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.S }), GasLimit: parent.GasLimit(), Number: new(big.Int).Add(parent.Number(), common.Big1), - Fees: new(big.Int), Time: time, } + if !wemixminer.IsPoW() { + header.Fees = new(big.Int) + } if chain.Config().IsLondon(header.Number) { header.BaseFee = misc.CalcBaseFee(chain.Config(), parent.Header()) if !chain.Config().IsLondon(parent.Number()) { From 393fc741afa347a64506918a37200ccc49e01665 Mon Sep 17 00:00:00 2001 From: egonspace Date: Mon, 20 May 2024 17:27:00 +0900 Subject: [PATCH 12/21] fix: apply comment --- core/state_processor.go | 5 ++--- core/types/block.go | 3 +++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 676b98a5f49a..7550f71f2827 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -17,7 +17,6 @@ package core import ( - "bytes" "fmt" "math/big" @@ -92,8 +91,8 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg err := p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles()) header.Fees = fees - if !bytes.Equal(block.Header().Hash().Bytes(), header.Hash().Bytes()) { - return nil, nil, 0, fmt.Errorf("Remote block hash is different from being processed one locally (remote=%v, local=%v)", block.Header().Hash(), header.Hash()) + if block.Header().Hash() != header.Hash() { + return nil, nil, 0, fmt.Errorf("remote block hash is different from being processed one locally (remote=%v, local=%v)", block.Header().Hash(), header.Hash()) } return receipts, allLogs, *usedGas, err diff --git a/core/types/block.go b/core/types/block.go index f55a0ad127ff..cb61514a931d 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -324,6 +324,9 @@ func CopyHeader(h *Header) *Header { cpy.Rewards = make([]byte, len(h.Rewards)) copy(cpy.Rewards, h.Rewards) } + if h.Fees != nil { + cpy.Fees = new(big.Int).Set(h.Fees) + } return &cpy } From fe76fa841a229808e0ff6df6c9ef4bc0eda1adda Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 21 May 2024 08:12:09 +0900 Subject: [PATCH 13/21] fix: apaply comment and fix test failure --- params/config.go | 14 +++++++------- params/config_test.go | 40 ++++++++++++++++++++-------------------- wemix/rewards_test.go | 12 ++++++------ 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/params/config.go b/params/config.go index 27310d53256e..1bdd1807064c 100644 --- a/params/config.go +++ b/params/config.go @@ -165,7 +165,7 @@ var ( BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(53_557_371), HalvingPeriod: big.NewInt(63_115_200), - NoRewardHereafter: big.NewInt(1_000_000_000), // TODO fix last reward block + FinishRewardBlock: big.NewInt(1_000_000_000), // TODO fix last reward block HalvingTimes: 16, HalvingRate: 50, }, @@ -195,7 +195,7 @@ var ( BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(60_537_845), HalvingPeriod: big.NewInt(63_115_200), - NoRewardHereafter: big.NewInt(1_000_000_000), // TODO fix last reward block + FinishRewardBlock: big.NewInt(1_000_000_000), // TODO fix last reward block HalvingTimes: 16, HalvingRate: 50, }, @@ -434,7 +434,7 @@ type BriocheConfig struct { BlockReward *big.Int `json:"blockReward,omitempty"` // nil - use default block reward(1e18) FirstHalvingBlock *big.Int `json:"firstHalvingBlock,omitempty"` // nil - halving is not work. including this block HalvingPeriod *big.Int `json:"halvingPeriod,omitempty"` // nil - halving is not work - NoRewardHereafter *big.Int `json:"noRewardHereafter,omitempty"` // nil - block reward goes on endlessly + FinishRewardBlock *big.Int `json:"finishRewardBlock,omitempty"` // nil - block reward goes on endlessly HalvingTimes uint64 `json:"halvingTimes,omitempty"` // 0 - no halving HalvingRate uint32 `json:"halvingRate,omitempty"` // 0 - no reward on halving; 100 - no halving; >100 - increasing reward } @@ -445,20 +445,20 @@ func (bc *BriocheConfig) GetBriocheBlockReward(defaultReward *big.Int, num *big. if bc.BlockReward != nil { blockReward = big.NewInt(0).Set(bc.BlockReward) } - if bc.NoRewardHereafter != nil && - bc.NoRewardHereafter.Cmp(num) <= 0 { + if bc.FinishRewardBlock != nil && + bc.FinishRewardBlock.Cmp(num) <= 0 { blockReward = big.NewInt(0) } else if bc.FirstHalvingBlock != nil && bc.HalvingPeriod != nil && bc.HalvingTimes > 0 && num.Cmp(bc.FirstHalvingBlock) >= 0 { - blockReward = bc.halveRewards(blockReward, num) + blockReward = bc.calcHalvedReward(blockReward, num) } } return blockReward } -func (bc *BriocheConfig) halveRewards(baseReward *big.Int, num *big.Int) *big.Int { +func (bc *BriocheConfig) calcHalvedReward(baseReward *big.Int, num *big.Int) *big.Int { elapsed := new(big.Int).Sub(num, bc.FirstHalvingBlock) times := new(big.Int).Add(common.Big1, new(big.Int).Div(elapsed, bc.HalvingPeriod)) if times.Uint64() > bc.HalvingTimes { diff --git a/params/config_test.go b/params/config_test.go index 0db609be3209..07395ec1a017 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -157,9 +157,9 @@ func TestHalveRewards(t *testing.T) { HalvingTimes: tc.times, HalvingRate: tc.rate, } - halved := brioche.halveRewards(tc.reward, tc.past) + halved := brioche.calcHalvedReward(tc.reward, tc.past) if tc.expected.Cmp(halved) != 0 { - t.Errorf("halveRewards mismatched (expected=%v, actual=%v)", tc.expected, halved) + t.Errorf("halved reward mismatched (expected=%v, actual=%v)", tc.expected, halved) } } } @@ -187,7 +187,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -200,7 +200,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(201), + FinishRewardBlock: big.NewInt(201), HalvingTimes: 10, HalvingRate: 50, }, @@ -215,7 +215,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(7e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -228,7 +228,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(3), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -241,7 +241,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -254,7 +254,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: nil, // it will use the default block reward FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -269,7 +269,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: nil, // it will use the default block reward FirstHalvingBlock: nil, HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -282,7 +282,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: nil, - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 10, HalvingRate: 50, }, @@ -295,7 +295,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(101), + FinishRewardBlock: big.NewInt(101), HalvingTimes: 0, HalvingRate: 50, }, @@ -308,7 +308,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), + FinishRewardBlock: big.NewInt(200), HalvingTimes: 10, HalvingRate: 50, }, @@ -321,7 +321,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), + FinishRewardBlock: big.NewInt(200), HalvingTimes: 10, HalvingRate: 100, // no halving rate }, @@ -336,7 +336,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), + FinishRewardBlock: big.NewInt(200), HalvingTimes: 10, HalvingRate: 0, // no reward }, @@ -349,7 +349,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), + FinishRewardBlock: big.NewInt(200), HalvingTimes: 10, HalvingRate: 50, }, @@ -362,7 +362,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(10), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(200), + FinishRewardBlock: big.NewInt(200), HalvingTimes: 10, HalvingRate: 50, }, @@ -377,7 +377,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), + FinishRewardBlock: big.NewInt(1000), HalvingTimes: 10, HalvingRate: 10, }, @@ -390,7 +390,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), + FinishRewardBlock: big.NewInt(1000), HalvingTimes: 10, HalvingRate: 10, }, @@ -403,7 +403,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), + FinishRewardBlock: big.NewInt(1000), HalvingTimes: 10, HalvingRate: 1, }, @@ -416,7 +416,7 @@ func TestGetBriocheBlockReward(t *testing.T) { BlockReward: big.NewInt(1e18), FirstHalvingBlock: big.NewInt(100), HalvingPeriod: big.NewInt(100), - NoRewardHereafter: big.NewInt(1000), + FinishRewardBlock: big.NewInt(1000), HalvingTimes: 10, HalvingRate: 99, }, diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index 8d76f58d48a1..b26279d507ad 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -146,7 +146,7 @@ func TestDistributeRewards(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Call the distributeRewards function - rewards, err := distributeRewards(tt.height, tt.rp, defaultBriocheBlockReward, tt.fees) + rewards, err := distributeRewards(tt.height, tt.rp, tt.rp.rewardAmount, tt.fees) rewardsString, _ := json.Marshal(rewards) if string(rewardsString) != tt.want { t.Errorf("distributeRewards() failed: %v, %v <-> %v", err, tt.want, string(rewardsString)) @@ -228,7 +228,7 @@ func TestRewardValidation(t *testing.T) { BlockReward: big.NewInt(100), FirstHalvingBlock: big.NewInt(0), HalvingPeriod: big.NewInt(10), - NoRewardHereafter: big.NewInt(30), + FinishRewardBlock: big.NewInt(30), HalvingTimes: 3, HalvingRate: 50, }}, @@ -280,7 +280,7 @@ func TestRewardValidation(t *testing.T) { BlockReward: big.NewInt(200), // different reward!! FirstHalvingBlock: gspec.Config.Brioche.FirstHalvingBlock, HalvingPeriod: gspec.Config.Brioche.HalvingPeriod, - NoRewardHereafter: gspec.Config.Brioche.NoRewardHereafter, + FinishRewardBlock: gspec.Config.Brioche.FinishRewardBlock, HalvingTimes: gspec.Config.Brioche.HalvingTimes, HalvingRate: gspec.Config.Brioche.HalvingRate, }} @@ -293,11 +293,11 @@ func TestRewardValidation(t *testing.T) { }) if _, err := blockchain.InsertChain(blocks); err != nil { - if !strings.HasPrefix(err.Error(), "Remote block hash is different") { + if !strings.HasPrefix(err.Error(), "remote block hash is different") { t.Fatal(err) } } else { - t.Fatal("Reward validation failed") + t.Fatal("reward validation failed") } } @@ -327,7 +327,7 @@ func TestBriocheHardFork(t *testing.T) { BlockReward: big.NewInt(4e17), FirstHalvingBlock: big.NewInt(4), HalvingPeriod: big.NewInt(2), - NoRewardHereafter: big.NewInt(7), + FinishRewardBlock: big.NewInt(7), HalvingTimes: 2, HalvingRate: 50, }}, From 1fd5ec7dd4dc2436b85dc6d7ab64c331e84baeb7 Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 21 May 2024 09:54:27 +0900 Subject: [PATCH 14/21] fix: remove needless coinbase setting --- consensus/ethash/consensus.go | 2 +- wemix/admin.go | 13 +++---------- wemix/miner/miner.go | 6 +++--- wemix/rewards_test.go | 4 ++-- 4 files changed, 9 insertions(+), 16 deletions(-) diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go index 72a25ff51c42..535d5b6c344e 100644 --- a/consensus/ethash/consensus.go +++ b/consensus/ethash/consensus.go @@ -698,7 +698,7 @@ func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header } state.AddBalance(header.Coinbase, reward) } else { - _, rewards, err := wemixminer.CalculateRewards( + rewards, err := wemixminer.CalculateRewards( config, header.Number, header.Fees, func(addr common.Address, amt *big.Int) { state.AddBalance(addr, amt) diff --git a/wemix/admin.go b/wemix/admin.go index b3b0a92e1191..b2a3b0b9e52c 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -1147,7 +1147,7 @@ func distributeRewards(height *big.Int, rp *rewardParameters, blockReward *big.I return rewards, nil } -func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (coinbase *common.Address, rewards []byte, err error) { +func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (rewards []byte, err error) { if (rp.staker == nil && rp.ecoSystem == nil && rp.maintenance == nil) || len(rp.members) == 0 { // handle testnet block 94 rewards if rewards94 := handleBlock94Rewards(num, rp, fees); rewards94 != nil { @@ -1163,13 +1163,6 @@ func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters return } - // determine coinbase - if len(rp.members) > 0 { - mix := int(num.Int64()/rp.blocksPer) % len(rp.members) - coinbase = &common.Address{} - coinbase.SetBytes(rp.members[mix].Reward.Bytes()) - } - var blockReward *big.Int if config.IsBrioche(num) { blockReward = config.Brioche.GetBriocheBlockReward(defaultBriocheBlockReward, num) @@ -1200,7 +1193,7 @@ func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters return } -func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { +func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -1208,7 +1201,7 @@ func calculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance if err != nil { // all goes to the coinbase err = wemixminer.ErrNotInitialized - return nil, nil, err + return nil, err } return calculateRewardsWithParams(config, rp, num, fees, addBalance) diff --git a/wemix/miner/miner.go b/wemix/miner/miner.go index 0b369b3b063c..339cb9f0e57c 100644 --- a/wemix/miner/miner.go +++ b/wemix/miner/miner.go @@ -16,7 +16,7 @@ var ( AmPartnerFunc func() bool IsPartnerFunc func(string) bool AmHubFunc func(string) int - CalculateRewardsFunc func(*params.ChainConfig, *big.Int, *big.Int, func(common.Address, *big.Int)) (*common.Address, []byte, error) + CalculateRewardsFunc func(*params.ChainConfig, *big.Int, *big.Int, func(common.Address, *big.Int)) ([]byte, error) VerifyRewardsFunc func(*big.Int, string) error GetCoinbaseFunc func(height *big.Int) (coinbase common.Address, err error) SignBlockFunc func(height *big.Int, hash common.Hash) (coinbase common.Address, sig []byte, err error) @@ -79,9 +79,9 @@ func IsPoW() bool { return params.ConsensusMethod == params.ConsensusPoW } -func CalculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { +func CalculateRewards(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { if CalculateRewardsFunc == nil { - return nil, nil, ErrNotInitialized + return nil, ErrNotInitialized } else { return CalculateRewardsFunc(config, num, fees, addBalance) } diff --git a/wemix/rewards_test.go b/wemix/rewards_test.go index b26279d507ad..d989116e79ab 100644 --- a/wemix/rewards_test.go +++ b/wemix/rewards_test.go @@ -179,8 +179,8 @@ func TestDistributeRewards(t *testing.T) { } } -func makeCalculateRewardFunc(rp *rewardParameters) func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { - return func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) (*common.Address, []byte, error) { +func makeCalculateRewardFunc(rp *rewardParameters) func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { + return func(config *params.ChainConfig, num, fees *big.Int, addBalance func(common.Address, *big.Int)) ([]byte, error) { return calculateRewardsWithParams(config, rp, num, fees, addBalance) } } From 439c8426d6c694f17cd3a8da0520922374d823c0 Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 21 May 2024 11:03:57 +0900 Subject: [PATCH 15/21] fix: remove unsued arguments --- core/state_processor.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 7550f71f2827..2f7dd6627b38 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -80,7 +80,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } statedb.Prepare(tx.Hash(), i) - receipt, err := applyTransaction(msg, p.config, p.bc, nil, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) + receipt, err := applyTransaction(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, fees, vmenv) if err != nil { return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -98,7 +98,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg return receipts, allLogs, *usedGas, err } -func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { +func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, fees *big.Int, evm *vm.EVM) (*types.Receipt, error) { // Create a new context to be used in the EVM environment. txContext := NewEVMTxContext(msg) evm.Reset(txContext, statedb) @@ -156,5 +156,5 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo // 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.Number, header.Hash(), tx, usedGas, fees, vmenv) + return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, fees, vmenv) } From 39f6894785d855049e3e579f749c42c8ae01890f Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 21 May 2024 17:25:05 +0900 Subject: [PATCH 16/21] fix: configure loading for testnet --- core/genesis.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/genesis.go b/core/genesis.go index b4a318727a69..f98aa8966071 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -302,7 +302,10 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override // chain config as that would be AllProtocolChanges (applying any new fork // on top of an existing private network genesis block). In that case, only // apply the overrides. - if genesis == nil && !(stored == params.MainnetGenesisHash || stored == params.WemixMainnetGenesisHash) { + if genesis == nil && + !(stored == params.MainnetGenesisHash || + stored == params.WemixMainnetGenesisHash || + stored == params.WemixTestnetGenesisHash) { newcfg = storedcfg if overrideArrowGlacier != nil { newcfg.ArrowGlacierBlock = overrideArrowGlacier From 39c46c1a1469e0f38a0428986157ada1b99749e1 Mon Sep 17 00:00:00 2001 From: egonspace Date: Tue, 21 May 2024 18:41:54 +0900 Subject: [PATCH 17/21] fix: add checking hard fork order, compatibility --- params/config.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/params/config.go b/params/config.go index 1bdd1807064c..1cc89dd6d7b2 100644 --- a/params/config.go +++ b/params/config.go @@ -498,7 +498,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, Engine: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, Engine: %v, BriocheConfig: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -520,6 +520,7 @@ func (c *ChainConfig) String() string { c.BriocheBlock, c.TerminalTotalDifficulty, engine, + c.Brioche, ) } @@ -660,6 +661,9 @@ func (c *ChainConfig) CheckConfigForkOrder() error { {name: "londonBlock", block: c.LondonBlock}, {name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true}, {name: "mergeStartBlock", block: c.MergeForkBlock, optional: true}, + {name: "pangyoBlock", block: c.PangyoBlock, optional: true}, + {name: "applepieBlock", block: c.ApplepieBlock, optional: true}, + {name: "briocheBlock", block: c.BriocheBlock, optional: true}, } { if lastFork.name != "" { // Next one must be higher number @@ -735,6 +739,15 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi if isForkIncompatible(c.MergeForkBlock, newcfg.MergeForkBlock, head) { return newCompatError("Merge Start fork block", c.MergeForkBlock, newcfg.MergeForkBlock) } + if isForkIncompatible(c.PangyoBlock, newcfg.PangyoBlock, head) { + return newCompatError("Pangyo fork block", c.PangyoBlock, newcfg.PangyoBlock) + } + if isForkIncompatible(c.ApplepieBlock, newcfg.ApplepieBlock, head) { + return newCompatError("Applepie fork block", c.ApplepieBlock, newcfg.ApplepieBlock) + } + if isForkIncompatible(c.BriocheBlock, newcfg.BriocheBlock, head) { + return newCompatError("Brioche fork block", c.BriocheBlock, newcfg.BriocheBlock) + } return nil } From 54963bd59880b709ab7d093b0b62cc9eb4c1b52d Mon Sep 17 00:00:00 2001 From: egonspace Date: Wed, 22 May 2024 16:16:28 +0900 Subject: [PATCH 18/21] fix: apply comment --- params/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/params/config.go b/params/config.go index 1cc89dd6d7b2..ee00dd125862 100644 --- a/params/config.go +++ b/params/config.go @@ -498,7 +498,7 @@ func (c *ChainConfig) String() string { default: engine = "unknown" } - return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, Engine: %v, BriocheConfig: %v}", + return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, MergeFork: %v, PangyoFork: %v, ApplepieFork: %v, BriocheFork: %v, Terminal TD: %v, BriocheConfig: %v, Engine: %v}", c.ChainID, c.HomesteadBlock, c.DAOForkBlock, @@ -519,8 +519,8 @@ func (c *ChainConfig) String() string { c.ApplepieBlock, c.BriocheBlock, c.TerminalTotalDifficulty, - engine, c.Brioche, + engine, ) } From 5e6eddfc392427529c2bd3cebe8acc0644872694 Mon Sep 17 00:00:00 2001 From: egonspace Date: Wed, 22 May 2024 16:46:40 +0900 Subject: [PATCH 19/21] fix: apply comment --- wemix/admin.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wemix/admin.go b/wemix/admin.go index b2a3b0b9e52c..3b0f4d07d75e 100644 --- a/wemix/admin.go +++ b/wemix/admin.go @@ -1168,7 +1168,7 @@ func calculateRewardsWithParams(config *params.ChainConfig, rp *rewardParameters blockReward = config.Brioche.GetBriocheBlockReward(defaultBriocheBlockReward, num) } else { // if the wemix chain is not on brioche hard fork, use the `rewardAmount` from gov contract - blockReward = big.NewInt(0).Set(rp.rewardAmount) + blockReward = new(big.Int).Set(rp.rewardAmount) } // block reward From 3b9ae394a1c60a9ce7f075c5e1b7e8ca4c1b548c Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 23 May 2024 08:08:51 +0900 Subject: [PATCH 20/21] fix: stringify brioch config --- ethclient/ethclient_test.go | 2 +- params/config.go | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index dd01c08f539e..b475feba2c3f 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -401,7 +401,7 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { t.Fatalf("unexpected error: %v", err) } - // Test tx in block interupted. + // Test tx in block interrupted. ctx, cancel := context.WithCancel(context.Background()) cancel() tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) diff --git a/params/config.go b/params/config.go index ee00dd125862..fa323cf58746 100644 --- a/params/config.go +++ b/params/config.go @@ -471,6 +471,17 @@ func (bc *BriocheConfig) calcHalvedReward(baseReward *big.Int, num *big.Int) *bi return reward.Div(reward.Mul(reward, numerator), denominator) } +func (bc *BriocheConfig) String() string { + return fmt.Sprintf("{BlockReward: %v FirstHalvingBlock: %v HalvingPeriod: %v FinishRewardBlock: %v HalvingTimes: %v HalvingRate: %v}", + bc.BlockReward, + bc.FirstHalvingBlock, + bc.HalvingPeriod, + bc.FinishRewardBlock, + bc.HalvingTimes, + bc.HalvingRate, + ) +} + // String implements the stringer interface, returning the consensus engine details. func (c *EthashConfig) String() string { return "ethash" From f410c84ebb9ac95357c0bde70f85014786bee58b Mon Sep 17 00:00:00 2001 From: egonspace Date: Thu, 23 May 2024 09:28:55 +0900 Subject: [PATCH 21/21] fix: add debugging code --- ethclient/ethclient_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go index b475feba2c3f..5b418026d3c9 100644 --- a/ethclient/ethclient_test.go +++ b/ethclient/ethclient_test.go @@ -404,6 +404,9 @@ func testTransactionInBlockInterrupted(t *testing.T, client *rpc.Client) { // Test tx in block interrupted. ctx, cancel := context.WithCancel(context.Background()) cancel() + if ctx.Err() != context.Canceled { + t.Fatal("context is not canceled") + } tx, err := ec.TransactionInBlock(ctx, block.Hash(), 0) if tx != nil { t.Fatal("transaction should be nil")