Skip to content

Commit

Permalink
feat: brioche first implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
egonspace committed May 14, 2024
1 parent cad51f5 commit 19434b8
Show file tree
Hide file tree
Showing 14 changed files with 259 additions and 58 deletions.
6 changes: 2 additions & 4 deletions consensus/ethash/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
})
Expand All @@ -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
Expand Down
10 changes: 10 additions & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package core

import (
"bytes"
"fmt"
"math/big"

Expand Down Expand Up @@ -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.
Expand Down
24 changes: 6 additions & 18 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
34 changes: 33 additions & 1 deletion core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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: &params.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)
}
}
8 changes: 4 additions & 4 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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, 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...)
}
// 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) {
Expand Down
5 changes: 4 additions & 1 deletion core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
}
1 change: 1 addition & 0 deletions eth/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion eth/state_accessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand Down
41 changes: 35 additions & 6 deletions params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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)
)

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand All @@ -454,6 +478,7 @@ func (c *ChainConfig) String() string {
c.MergeForkBlock,
c.PangyoBlock,
c.ApplepieBlock,
c.BriocheBlock,
c.TerminalTotalDifficulty,
engine,
)
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 19434b8

Please sign in to comment.