diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index fd99fbf58e..d720b0ce3a 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -1413,7 +1413,7 @@ func (b *Blockchain) Close() error { // CalculateBaseFee calculates the basefee of the header. func (b *Blockchain) CalculateBaseFee(parent *types.Header) uint64 { - if !b.config.Params.Forks.IsLondon(parent.Number) { + if !b.config.Params.Forks.IsActive(chain.London, parent.Number) { return chain.GenesisBaseFee } diff --git a/blockchain/blockchain_test.go b/blockchain/blockchain_test.go index b8de3dcffd..d1f882d533 100644 --- a/blockchain/blockchain_test.go +++ b/blockchain/blockchain_test.go @@ -1365,12 +1365,11 @@ func TestBlockchain_CalculateBaseFee(t *testing.T) { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { t.Parallel() - fork := chain.Fork(5) blockchain := Blockchain{ config: &chain.Chain{ Params: &chain.Params{ Forks: &chain.Forks{ - London: &fork, + chain.London: chain.NewFork(5), }, }, Genesis: &chain.Genesis{ diff --git a/blockchain/testing.go b/blockchain/testing.go index 2f3b510cd0..fe11c027c2 100644 --- a/blockchain/testing.go +++ b/blockchain/testing.go @@ -97,13 +97,14 @@ func NewTestBlockchain(t *testing.T, headers []*types.Header) *Blockchain { Number: 0, GasLimit: 0, } + forksAvail := &chain.Forks{ + chain.EIP155: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + } config := &chain.Chain{ Genesis: genesis, Params: &chain.Params{ - Forks: &chain.Forks{ - EIP155: chain.NewFork(0), - Homestead: chain.NewFork(0), - }, + Forks: forksAvail, BlockGasTarget: defaultBlockGasTarget, }, } diff --git a/chain/chain.go b/chain/chain.go index 46044d9a10..64bed7f1f5 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -41,8 +41,6 @@ type Chain struct { // Genesis specifies the header fields, state of a genesis block type Genesis struct { - Config *Params `json:"config"` - Nonce [8]byte `json:"nonce"` Timestamp uint64 `json:"timestamp"` ExtraData []byte `json:"extraData,omitempty"` diff --git a/chain/params.go b/chain/params.go index 62edcc2768..fc16f5842a 100644 --- a/chain/params.go +++ b/chain/params.go @@ -74,70 +74,46 @@ func (p *Params) GetEngine() string { return "" } -// Forks specifies when each fork is activated -type Forks struct { - Homestead *Fork `json:"homestead,omitempty"` - Byzantium *Fork `json:"byzantium,omitempty"` - Constantinople *Fork `json:"constantinople,omitempty"` - Petersburg *Fork `json:"petersburg,omitempty"` - Istanbul *Fork `json:"istanbul,omitempty"` - London *Fork `json:"london,omitempty"` - EIP150 *Fork `json:"EIP150,omitempty"` - EIP158 *Fork `json:"EIP158,omitempty"` - EIP155 *Fork `json:"EIP155,omitempty"` -} - -func (f *Forks) active(ff *Fork, block uint64) bool { - if ff == nil { - return false - } - - return ff.Active(block) -} - -func (f *Forks) IsHomestead(block uint64) bool { - return f.active(f.Homestead, block) -} - -func (f *Forks) IsByzantium(block uint64) bool { - return f.active(f.Byzantium, block) -} - -func (f *Forks) IsConstantinople(block uint64) bool { - return f.active(f.Constantinople, block) -} - -func (f *Forks) IsPetersburg(block uint64) bool { - return f.active(f.Petersburg, block) -} +// predefined forks +const ( + Homestead = "homestead" + Byzantium = "byzantium" + Constantinople = "constantinople" + Petersburg = "petersburg" + Istanbul = "istanbul" + London = "london" + EIP150 = "EIP150" + EIP158 = "EIP158" + EIP155 = "EIP155" +) -func (f *Forks) IsLondon(block uint64) bool { - return f.active(f.London, block) -} +// Forks is map which contains all forks and their starting blocks from genesis +type Forks map[string]*Fork -func (f *Forks) IsEIP150(block uint64) bool { - return f.active(f.EIP150, block) -} +// IsActive returns true if fork defined by name exists and defined for the block +func (f *Forks) IsActive(name string, block uint64) bool { + ff := (*f)[name] -func (f *Forks) IsEIP158(block uint64) bool { - return f.active(f.EIP158, block) + return ff != nil && ff.Active(block) } -func (f *Forks) IsEIP155(block uint64) bool { - return f.active(f.EIP155, block) +// SetFork adds/updates fork defined by name +func (f *Forks) SetFork(name string, value *Fork) { + (*f)[name] = value } +// At returns ForksInTime instance that shows which supported forks are enabled for the block func (f *Forks) At(block uint64) ForksInTime { return ForksInTime{ - Homestead: f.active(f.Homestead, block), - Byzantium: f.active(f.Byzantium, block), - Constantinople: f.active(f.Constantinople, block), - Petersburg: f.active(f.Petersburg, block), - Istanbul: f.active(f.Istanbul, block), - London: f.active(f.London, block), - EIP150: f.active(f.EIP150, block), - EIP158: f.active(f.EIP158, block), - EIP155: f.active(f.EIP155, block), + Homestead: f.IsActive(Homestead, block), + Byzantium: f.IsActive(Byzantium, block), + Constantinople: f.IsActive(Constantinople, block), + Petersburg: f.IsActive(Petersburg, block), + Istanbul: f.IsActive(Istanbul, block), + London: f.IsActive(London, block), + EIP150: f.IsActive(EIP150, block), + EIP158: f.IsActive(EIP158, block), + EIP155: f.IsActive(EIP155, block), } } @@ -157,6 +133,7 @@ func (f Fork) Int() *big.Int { return big.NewInt(int64(f)) } +// ForksInTime should contain all supported forks by current edge version type ForksInTime struct { Homestead, Byzantium, @@ -169,6 +146,7 @@ type ForksInTime struct { EIP155 bool } +// AllForksEnabled should contain all supported forks by current edge version var AllForksEnabled = &Forks{ Homestead: NewFork(0), EIP150: NewFork(0), diff --git a/command/genesis/params.go b/command/genesis/params.go index 95edcb5aa3..0e4e917f48 100644 --- a/command/genesis/params.go +++ b/command/genesis/params.go @@ -370,7 +370,7 @@ func (p *genesisParams) initGenesisConfig() error { // Disable london hardfork if burn contract address is not provided enabledForks := chain.AllForksEnabled if len(p.burnContracts) == 0 { - enabledForks.London = nil + enabledForks.SetFork(chain.London, nil) } chainConfig := &chain.Chain{ diff --git a/command/genesis/polybft_params.go b/command/genesis/polybft_params.go index b80135ae60..261b5dd7c5 100644 --- a/command/genesis/polybft_params.go +++ b/command/genesis/polybft_params.go @@ -20,6 +20,7 @@ import ( "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi/artifact" "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" "github.com/0xPolygon/polygon-edge/contracts" + "github.com/0xPolygon/polygon-edge/forkmanager" "github.com/0xPolygon/polygon-edge/helper/common" "github.com/0xPolygon/polygon-edge/server" "github.com/0xPolygon/polygon-edge/types" @@ -71,6 +72,10 @@ var ( // generatePolyBftChainConfig creates and persists polybft chain configuration to the provided file path func (p *genesisParams) generatePolyBftChainConfig(o command.OutputFormatter) error { + if err := forkmanager.ForkManagerInit(polybft.ForkManagerFactory, chain.AllForksEnabled); err != nil { + return err + } + // populate premine balance map premineBalances := make(map[types.Address]*premineInfo, len(p.premine)) @@ -149,7 +154,7 @@ func (p *genesisParams) generatePolyBftChainConfig(o command.OutputFormatter) er // Disable london hardfork if burn contract address is not provided enabledForks := chain.AllForksEnabled if len(p.burnContracts) == 0 { - enabledForks.London = nil + enabledForks.SetFork(chain.London, nil) } chainConfig := &chain.Chain{ diff --git a/consensus/polybft/extra_test.go b/consensus/polybft/extra_test.go index cd31c22cd4..4f33798291 100644 --- a/consensus/polybft/extra_test.go +++ b/consensus/polybft/extra_test.go @@ -496,8 +496,6 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) { const validatorsCount = 7 vals := validator.NewTestValidators(t, validatorsCount) - polyBftConfig := PolyBFTConfig{InitialValidatorSet: vals.GetParamValidators()} - delta := &validator.ValidatorSetDelta{ Added: make(validator.AccountSet, validatorsCount), Removed: bitmap.Bitmap{}, @@ -516,9 +514,6 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) { extra := Extra{Validators: delta} genesis := &chain.Genesis{ - Config: &chain.Params{Engine: map[string]interface{}{ - ConsensusName: polyBftConfig, - }}, ExtraData: extra.MarshalRLPTo(nil), } @@ -531,13 +526,7 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) { t.Run("Invalid Extra data", func(t *testing.T) { t.Parallel() - validators := validator.NewTestValidators(t, 5) - polyBftConfig := PolyBFTConfig{InitialValidatorSet: validators.GetParamValidators()} - genesis := &chain.Genesis{ - Config: &chain.Params{Engine: map[string]interface{}{ - ConsensusName: polyBftConfig, - }}, ExtraData: append(make([]byte, ExtraVanity), []byte{0x2, 0x3}...), } diff --git a/consensus/polybft/polybft.go b/consensus/polybft/polybft.go index 93435293f5..399375db9b 100644 --- a/consensus/polybft/polybft.go +++ b/consensus/polybft/polybft.go @@ -361,6 +361,11 @@ func GenesisPostHookFactory(config *chain.Chain, engineName string) func(txn *st } } +func ForkManagerFactory(forks *chain.Forks) error { + // place fork manager handler registration here + return nil +} + // Initialize initializes the consensus (e.g. setup data) func (p *Polybft) Initialize() error { p.logger.Info("initializing polybft...") diff --git a/forkmanager/fork.go b/forkmanager/fork.go new file mode 100644 index 0000000000..0556ca6c7e --- /dev/null +++ b/forkmanager/fork.go @@ -0,0 +1,78 @@ +package forkmanager + +import ( + "fmt" + + "github.com/0xPolygon/polygon-edge/chain" +) + +const InitialFork = "initialfork" + +// HandlerDesc gives description for the handler +// eq: "extra", "proposer_calculator", etc +type HandlerDesc string + +// Fork structure defines one fork +type Fork struct { + // name of the fork + Name string + // after the fork is activated, `FromBlockNumber` shows from which block is enabled + FromBlockNumber uint64 + // this value is false if fork is registered but not activated + IsActive bool + // map of all handlers registered for this fork + Handlers map[HandlerDesc]interface{} +} + +// Handler defines one custom handler +type Handler struct { + // Handler should be active from block `FromBlockNumber`` + FromBlockNumber uint64 + // instance of some structure, function etc + Handler interface{} +} + +func ForkManagerInit(factory func(*chain.Forks) error, forks *chain.Forks) error { + if factory == nil { + return nil + } + + fm := GetInstance() + fm.Clear() + + // register initial fork + fm.RegisterFork(InitialFork) + + // Register forks + for name := range *forks { + // check if fork is not supported by current edge version + if _, found := (*chain.AllForksEnabled)[name]; !found { + return fmt.Errorf("fork is not available: %s", name) + } + + fm.RegisterFork(name) + } + + // Register handlers and additional forks here + if err := factory(forks); err != nil { + return err + } + + // Activate initial fork + if err := fm.ActivateFork(InitialFork, uint64(0)); err != nil { + return err + } + + // Activate forks + for name, blockNumber := range *forks { + if blockNumber == nil { + continue + } + + if err := fm.ActivateFork(name, (uint64)(*blockNumber)); err != nil { + return err + } + } + + return nil +} diff --git a/forkmanager/fork_manager.go b/forkmanager/fork_manager.go new file mode 100644 index 0000000000..653bd860d1 --- /dev/null +++ b/forkmanager/fork_manager.go @@ -0,0 +1,229 @@ +package forkmanager + +import ( + "fmt" + "sort" + "sync" +) + +/* +Regarding whether it is okay to use the Singleton pattern in Go, it's a topic of some debate. +The Singleton pattern can introduce global state and make testing and concurrent programming more challenging. +It can also make code tightly coupled and harder to maintain. +In general, it's recommended to favor dependency injection and explicit collaboration over singletons. + +However, there might be scenarios where the Singleton pattern is still useful, +such as managing shared resources or maintaining a global configuration. +Just be mindful of the potential drawbacks and consider alternative patterns when appropriate. +*/ + +var ( + forkManagerInstance *forkManager + forkManagerInstanceLock sync.Mutex +) + +type forkManager struct { + lock sync.Mutex + + forkMap map[string]*Fork + handlersMap map[HandlerDesc][]Handler +} + +// GeInstance returns fork manager singleton instance. Thread safe +func GetInstance() *forkManager { + forkManagerInstanceLock.Lock() + defer forkManagerInstanceLock.Unlock() + + if forkManagerInstance == nil { + forkManagerInstance = &forkManager{} + forkManagerInstance.Clear() + } + + return forkManagerInstance +} + +func (fm *forkManager) Clear() { + fm.lock.Lock() + defer fm.lock.Unlock() + + fm.forkMap = map[string]*Fork{} + fm.handlersMap = map[HandlerDesc][]Handler{} +} + +// RegisterFork registers fork by its name +func (fm *forkManager) RegisterFork(name string) { + fm.lock.Lock() + defer fm.lock.Unlock() + + fm.forkMap[name] = &Fork{ + Name: name, + FromBlockNumber: 0, + IsActive: false, + Handlers: map[HandlerDesc]interface{}{}, + } +} + +// RegisterHandler registers handler by its name for specific fork +func (fm *forkManager) RegisterHandler(forkName string, handlerName HandlerDesc, handler interface{}) error { + fm.lock.Lock() + defer fm.lock.Unlock() + + fork, exists := fm.forkMap[forkName] + if !exists { + return fmt.Errorf("fork does not exist: %s", forkName) + } + + fork.Handlers[handlerName] = handler + + return nil +} + +// ActivateFork activates fork from some block number +// All handlers belong to this fork are also activated +func (fm *forkManager) ActivateFork(forkName string, blockNumber uint64) error { + fm.lock.Lock() + defer fm.lock.Unlock() + + fork, exists := fm.forkMap[forkName] + if !exists { + return fmt.Errorf("fork does not exist: %s", forkName) + } + + if fork.IsActive { + return nil // already activated + } + + fork.IsActive = true + fork.FromBlockNumber = blockNumber + + for forkHandlerName, forkHandler := range fork.Handlers { + fm.addHandler(forkHandlerName, blockNumber, forkHandler) + } + + return nil +} + +// DeactivateFork de-activates fork +// All handlers belong to this fork are also de-activated +func (fm *forkManager) DeactivateFork(forkName string) error { + fm.lock.Lock() + defer fm.lock.Unlock() + + fork, exists := fm.forkMap[forkName] + if !exists { + return fmt.Errorf("fork does not exist: %s", forkName) + } + + if !fork.IsActive { + return nil // already deactivated + } + + fork.IsActive = false + + for forkHandlerName := range fork.Handlers { + fm.removeHandler(forkHandlerName, fork.FromBlockNumber) + } + + return nil +} + +// GetHandler retrieves handler for handler name and for a block number +func (fm *forkManager) GetHandler(name HandlerDesc, blockNumber uint64) interface{} { + fm.lock.Lock() + defer fm.lock.Unlock() + + handlers, exists := fm.handlersMap[name] + if !exists { + return nil + } + + // binary search to find the latest handler defined for a specific block + pos := sort.Search(len(handlers), func(i int) bool { + return blockNumber < handlers[i].FromBlockNumber + }) - 1 + if pos < 0 { + return nil + } + + return handlers[pos].Handler +} + +// IsForkRegistered checks if fork is registered +func (fm *forkManager) IsForkRegistered(name string) bool { + fm.lock.Lock() + defer fm.lock.Unlock() + + _, exists := fm.forkMap[name] + + return exists +} + +// IsForkEnabled checks if fork is registered and enabled for specific block +func (fm *forkManager) IsForkEnabled(name string, blockNumber uint64) bool { + fm.lock.Lock() + defer fm.lock.Unlock() + + fork, exists := fm.forkMap[name] + if !exists { + return false + } + + return fork.IsActive && fork.FromBlockNumber <= blockNumber +} + +// GetForkBlock returns fork block if fork is registered and activated +func (fm *forkManager) GetForkBlock(name string) (uint64, error) { + fm.lock.Lock() + defer fm.lock.Unlock() + + fork, exists := fm.forkMap[name] + if !exists { + return 0, fmt.Errorf("fork does not exist: %s", name) + } + + if !fork.IsActive { + return 0, fmt.Errorf("fork is not active: %s", name) + } + + return fork.FromBlockNumber, nil +} + +func (fm *forkManager) addHandler(handlerName HandlerDesc, blockNumber uint64, handler interface{}) { + if handlers, exists := fm.handlersMap[handlerName]; !exists { + fm.handlersMap[handlerName] = []Handler{ + { + FromBlockNumber: blockNumber, + Handler: handler, + }, + } + } else { + // keep everything in sorted order + index := sort.Search(len(handlers), func(i int) bool { + return handlers[i].FromBlockNumber >= blockNumber + }) + handlers = append(handlers, Handler{}) + copy(handlers[index+1:], handlers[index:]) + handlers[index] = Handler{ + FromBlockNumber: blockNumber, + Handler: handler, + } + fm.handlersMap[handlerName] = handlers + } +} + +func (fm *forkManager) removeHandler(handlerName HandlerDesc, blockNumber uint64) { + handlers, exists := fm.handlersMap[handlerName] + if !exists { + return + } + + index := sort.Search(len(handlers), func(i int) bool { + return handlers[i].FromBlockNumber == blockNumber + }) + + if index != -1 { + copy(handlers[index:], handlers[index+1:]) + handlers[len(handlers)-1] = Handler{} + fm.handlersMap[handlerName] = handlers[:len(handlers)-1] + } +} diff --git a/forkmanager/fork_manager_test.go b/forkmanager/fork_manager_test.go new file mode 100644 index 0000000000..d3b91b1877 --- /dev/null +++ b/forkmanager/fork_manager_test.go @@ -0,0 +1,187 @@ +package forkmanager + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + HandlerA = HandlerDesc("HA") + HandlerB = HandlerDesc("HB") + HandlerC = HandlerDesc("HC") + HandlerD = HandlerDesc("HD") + + ForkA = "A" + ForkB = "B" + ForkC = "C" + ForkD = "D" + ForkE = "E" +) + +func TestForkManager(t *testing.T) { + t.Parallel() + + forkManager := GetInstance() + + forkManager.RegisterFork(ForkA) + forkManager.RegisterFork(ForkB) + forkManager.RegisterFork(ForkC) + forkManager.RegisterFork(ForkD) + + assert.NoError(t, forkManager.RegisterHandler(ForkA, HandlerA, func() string { return "AAH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkC, HandlerA, func() string { return "ACH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkA, HandlerB, func() string { return "BAH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkB, HandlerB, func() string { return "BBH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkD, HandlerB, func() string { return "BDH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkC, HandlerC, func() string { return "CCH" })) + + assert.NoError(t, forkManager.ActivateFork(ForkA, 0)) + assert.NoError(t, forkManager.ActivateFork(ForkB, 100)) + assert.NoError(t, forkManager.ActivateFork(ForkC, 200)) + assert.NoError(t, forkManager.ActivateFork(ForkD, 300)) + + handlersACnt := len(forkManager.handlersMap[HandlerA]) + handlersBCnt := len(forkManager.handlersMap[HandlerB]) + + assert.Equal(t, 2, handlersACnt) + assert.Equal(t, 3, handlersBCnt) + + t.Run("activate not registered fork", func(t *testing.T) { + t.Parallel() + + assert.Error(t, forkManager.ActivateFork(ForkE, 100)) + }) + + t.Run("activate already activated fork", func(t *testing.T) { + t.Parallel() + + assert.NoError(t, forkManager.ActivateFork(ForkA, 100)) + + // count not changed + assert.Equal(t, handlersACnt, len(forkManager.handlersMap[HandlerA])) + assert.Equal(t, handlersBCnt, len(forkManager.handlersMap[HandlerB])) + }) + + t.Run("is fork enabled", func(t *testing.T) { + t.Parallel() + + assert.True(t, forkManager.IsForkEnabled(ForkA, 0)) + assert.True(t, forkManager.IsForkEnabled(ForkA, 100)) + assert.True(t, forkManager.IsForkEnabled(ForkA, 200)) + + assert.False(t, forkManager.IsForkEnabled(ForkB, 0)) + assert.True(t, forkManager.IsForkEnabled(ForkB, 100)) + assert.True(t, forkManager.IsForkEnabled(ForkB, 200)) + + assert.False(t, forkManager.IsForkEnabled(ForkC, 0)) + assert.False(t, forkManager.IsForkEnabled(ForkC, 100)) + assert.True(t, forkManager.IsForkEnabled(ForkC, 200)) + assert.True(t, forkManager.IsForkEnabled(ForkC, 300)) + + assert.False(t, forkManager.IsForkEnabled(ForkD, 0)) + assert.False(t, forkManager.IsForkEnabled(ForkD, 100)) + assert.False(t, forkManager.IsForkEnabled(ForkD, 200)) + assert.True(t, forkManager.IsForkEnabled(ForkD, 300)) + assert.True(t, forkManager.IsForkEnabled(ForkD, 400)) + + assert.False(t, forkManager.IsForkEnabled(ForkE, 0)) + }) + + t.Run("is fork supported", func(t *testing.T) { + t.Parallel() + + assert.True(t, forkManager.IsForkRegistered(ForkA)) + assert.True(t, forkManager.IsForkRegistered(ForkB)) + assert.True(t, forkManager.IsForkRegistered(ForkC)) + assert.True(t, forkManager.IsForkRegistered(ForkD)) + + assert.False(t, forkManager.IsForkRegistered(ForkE)) + }) + + t.Run("get fork block", func(t *testing.T) { + t.Parallel() + + b, err := forkManager.GetForkBlock(ForkA) + assert.NoError(t, err) + assert.Equal(t, uint64(0), b) + + b, err = forkManager.GetForkBlock(ForkB) + assert.NoError(t, err) + assert.Equal(t, uint64(100), b) + + b, err = forkManager.GetForkBlock(ForkC) + assert.NoError(t, err) + assert.Equal(t, uint64(200), b) + + b, err = forkManager.GetForkBlock(ForkD) + assert.NoError(t, err) + assert.Equal(t, uint64(300), b) + + _, err = forkManager.GetForkBlock(ForkE) + assert.Error(t, err) + }) + + t.Run("register handler not existing fork", func(t *testing.T) { + t.Parallel() + + assert.Error(t, forkManager.RegisterHandler(ForkE, HandlerD, func() {})) + }) + + t.Run("get handler", func(t *testing.T) { + t.Parallel() + + execute := func(name HandlerDesc, block uint64) string { + //nolint:forcetypeassert + return forkManager.GetHandler(name, block).(func() string)() + } + + for i := uint64(0); i < uint64(4); i++ { + assert.Equal(t, "AAH", execute(HandlerA, i)) + assert.Equal(t, "BAH", execute(HandlerB, i)) + assert.Nil(t, forkManager.GetHandler(HandlerC, i)) + + assert.Equal(t, "AAH", execute(HandlerA, 100+i)) + assert.Equal(t, "BBH", execute(HandlerB, 100+i)) + assert.Nil(t, forkManager.GetHandler(HandlerC, 100+i)) + + assert.Equal(t, "ACH", execute(HandlerA, 200+i)) + assert.Equal(t, "BBH", execute(HandlerB, 200+i)) + assert.Equal(t, "CCH", execute(HandlerC, 200+i)) + + assert.Equal(t, "ACH", execute(HandlerA, 300+i)) + assert.Equal(t, "BDH", execute(HandlerB, 300+i)) + assert.Equal(t, "CCH", execute(HandlerC, 300+i)) + } + + assert.Nil(t, forkManager.GetHandler(HandlerD, 0)) + }) +} + +func TestForkManager_Deactivate(t *testing.T) { + t.Parallel() + + forkManager := &forkManager{ + forkMap: map[string]*Fork{}, + handlersMap: map[HandlerDesc][]Handler{}, + } + + forkManager.RegisterFork(ForkA) + forkManager.RegisterFork(ForkB) + + assert.NoError(t, forkManager.RegisterHandler(ForkA, HandlerA, func() string { return "AAH" })) + assert.NoError(t, forkManager.RegisterHandler(ForkB, HandlerA, func() string { return "ABH" })) + + assert.NoError(t, forkManager.ActivateFork(ForkA, 0)) + assert.NoError(t, forkManager.ActivateFork(ForkB, 0)) + + assert.Equal(t, 2, len(forkManager.handlersMap[HandlerA])) + + assert.NoError(t, forkManager.DeactivateFork(ForkA)) + + assert.Equal(t, 1, len(forkManager.handlersMap[HandlerA])) + + assert.NoError(t, forkManager.DeactivateFork(ForkB)) + + assert.Equal(t, 0, len(forkManager.handlersMap[HandlerA])) +} diff --git a/server/builtin.go b/server/builtin.go index 435bb1a282..c84e03e97c 100644 --- a/server/builtin.go +++ b/server/builtin.go @@ -19,6 +19,8 @@ type GenesisFactoryHook func(config *chain.Chain, engineName string) func(*state type ConsensusType string +type ForkManagerFactory func(forks *chain.Forks) error + const ( DevConsensus ConsensusType = "dev" IBFTConsensus ConsensusType = "ibft" @@ -46,6 +48,10 @@ var genesisCreationFactory = map[ConsensusType]GenesisFactoryHook{ PolyBFTConsensus: consensusPolyBFT.GenesisPostHookFactory, } +var forkManagerFactory = map[ConsensusType]ForkManagerFactory{ + PolyBFTConsensus: consensusPolyBFT.ForkManagerFactory, +} + func ConsensusSupported(value string) bool { _, ok := consensusBackends[ConsensusType(value)] diff --git a/server/server.go b/server/server.go index 5aca527c21..f511e500e7 100644 --- a/server/server.go +++ b/server/server.go @@ -16,6 +16,7 @@ import ( "github.com/0xPolygon/polygon-edge/blockchain/storage/leveldb" "github.com/0xPolygon/polygon-edge/blockchain/storage/memory" consensusPolyBFT "github.com/0xPolygon/polygon-edge/consensus/polybft" + "github.com/0xPolygon/polygon-edge/forkmanager" "github.com/0xPolygon/polygon-edge/archive" "github.com/0xPolygon/polygon-edge/blockchain" @@ -276,16 +277,22 @@ func NewServer(config *Config) (*Server, error) { return nil, err } + if err := forkmanager.ForkManagerInit( + forkManagerFactory[ConsensusType(engineName)], + config.Chain.Params.Forks); err != nil { + return nil, err + } + // compute the genesis root state config.Chain.Genesis.StateRoot = genesisRoot // Use the london signer with eip-155 as a fallback one var signer crypto.TxSigner = crypto.NewLondonSigner( uint64(m.config.Chain.Params.ChainID), - chain.AllForksEnabled.At(0).Homestead, + config.Chain.Params.Forks.IsActive(chain.Homestead, 0), crypto.NewEIP155Signer( uint64(m.config.Chain.Params.ChainID), - chain.AllForksEnabled.At(0).Homestead, + config.Chain.Params.Forks.IsActive(chain.Homestead, 0), ), ) diff --git a/tests/state_test.go b/tests/state_test.go index 981a9b9f86..198f39b5c1 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -42,7 +42,7 @@ func RunSpecificTest(t *testing.T, file string, c testCase, name, fork string, i var baseFee *big.Int - if config.IsLondon(0) { + if config.IsActive(chain.London, 0) { if c.Env.BaseFee != "" { baseFee = stringToBigIntT(t, c.Env.BaseFee) } else { diff --git a/tests/testing.go b/tests/testing.go index 6595a1f640..fca394139e 100644 --- a/tests/testing.go +++ b/tests/testing.go @@ -405,83 +405,72 @@ func (t *stTransaction) UnmarshalJSON(input []byte) error { var Forks = map[string]*chain.Forks{ "Frontier": {}, "Homestead": { - Homestead: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), }, "EIP150": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), }, "EIP158": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), }, "Byzantium": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), + chain.Byzantium: chain.NewFork(0), }, "Constantinople": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(0), - Constantinople: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), + chain.Byzantium: chain.NewFork(0), + chain.Constantinople: chain.NewFork(0), }, - "Istanbul": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(0), - Constantinople: chain.NewFork(0), - Petersburg: chain.NewFork(0), - Istanbul: chain.NewFork(0), + "Istchain.anbul": { + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), + chain.Byzantium: chain.NewFork(0), + chain.Constantinople: chain.NewFork(0), + chain.Petersburg: chain.NewFork(0), + chain.Istanbul: chain.NewFork(0), }, - /*"London": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(0), - Constantinople: chain.NewFork(0), - Petersburg: chain.NewFork(0), - Istanbul: chain.NewFork(0), - London: chain.NewFork(0), - },*/ "FrontierToHomesteadAt5": { - Homestead: chain.NewFork(5), + chain.Homestead: chain.NewFork(5), }, "HomesteadToEIP150At5": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(5), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(5), }, "HomesteadToDaoAt5": { - Homestead: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), }, "EIP158ToByzantiumAt5": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(5), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), + chain.Byzantium: chain.NewFork(5), }, "ByzantiumToConstantinopleAt5": { - Byzantium: chain.NewFork(0), - Constantinople: chain.NewFork(5), + chain.Byzantium: chain.NewFork(0), + chain.Constantinople: chain.NewFork(5), }, "ConstantinopleFix": { - Homestead: chain.NewFork(0), - EIP150: chain.NewFork(0), - EIP155: chain.NewFork(0), - EIP158: chain.NewFork(0), - Byzantium: chain.NewFork(0), - Constantinople: chain.NewFork(0), - Petersburg: chain.NewFork(0), + chain.Homestead: chain.NewFork(0), + chain.EIP150: chain.NewFork(0), + chain.EIP155: chain.NewFork(0), + chain.EIP158: chain.NewFork(0), + chain.Byzantium: chain.NewFork(0), + chain.Constantinople: chain.NewFork(0), + chain.Petersburg: chain.NewFork(0), }, } diff --git a/txpool/txpool_test.go b/txpool/txpool_test.go index 00f9c41fee..c19647bc23 100644 --- a/txpool/txpool_test.go +++ b/txpool/txpool_test.go @@ -31,11 +31,11 @@ const ( ) var ( - forks = &chain.Forks{ - Homestead: chain.NewFork(0), - Istanbul: chain.NewFork(0), - London: chain.NewFork(0), - } + forks = (&chain.Forks{ + chain.Homestead: chain.NewFork(0), + chain.Istanbul: chain.NewFork(0), + chain.London: chain.NewFork(0), + }) ) // addresses used in tests