From aa097463928498e02ab8b58c613624ecabb975e9 Mon Sep 17 00:00:00 2001 From: Igor Crevar Date: Fri, 2 Jun 2023 13:20:01 +0200 Subject: [PATCH] extra data forking poc --- chain/params.go | 10 +- consensus/consensus.go | 2 +- consensus/dev/dev.go | 4 +- consensus/dummy/dummy.go | 4 +- consensus/ibft/ibft.go | 4 +- consensus/polybft/checkpoint_manager.go | 6 +- consensus/polybft/consensus_metrics.go | 2 +- consensus/polybft/consensus_runtime.go | 8 +- consensus/polybft/extra.go | 145 +++++-------------- consensus/polybft/extra_fork_base.go | 131 +++++++++++++++++ consensus/polybft/extra_fork_myfirstfork.go | 54 +++++++ consensus/polybft/extra_fork_mysecondfork.go | 65 +++++++++ consensus/polybft/extra_test.go | 10 +- consensus/polybft/fsm.go | 27 +++- consensus/polybft/fsm_test.go | 14 +- consensus/polybft/hash.go | 2 +- consensus/polybft/polybft.go | 25 +++- consensus/polybft/runtime_helpers.go | 2 +- consensus/polybft/validators_snapshot.go | 2 +- e2e-polybft/e2e/bridge_test.go | 10 +- e2e-polybft/e2e/consensus_test.go | 4 +- e2e-polybft/framework/test-cluster.go | 7 + e2e-polybft/framework/test-server.go | 2 +- jsonrpc/eth_blockchain_test.go | 4 +- jsonrpc/eth_endpoint.go | 4 +- jsonrpc/mocks_test.go | 4 +- 26 files changed, 389 insertions(+), 163 deletions(-) create mode 100644 consensus/polybft/extra_fork_base.go create mode 100644 consensus/polybft/extra_fork_myfirstfork.go create mode 100644 consensus/polybft/extra_fork_mysecondfork.go diff --git a/chain/params.go b/chain/params.go index fc16f5842a..9bfd39099f 100644 --- a/chain/params.go +++ b/chain/params.go @@ -85,6 +85,8 @@ const ( EIP150 = "EIP150" EIP158 = "EIP158" EIP155 = "EIP155" + MyFirstFork = "myfirstfork" + MySecondFork = "mysecondfork" ) // Forks is map which contains all forks and their starting blocks from genesis @@ -114,6 +116,8 @@ func (f *Forks) At(block uint64) ForksInTime { EIP150: f.IsActive(EIP150, block), EIP158: f.IsActive(EIP158, block), EIP155: f.IsActive(EIP155, block), + MyFirstFork: f.IsActive(MyFirstFork, block), + MySecondFork: f.IsActive(MySecondFork, block), } } @@ -143,7 +147,9 @@ type ForksInTime struct { London, EIP150, EIP158, - EIP155 bool + EIP155, + MyFirstFork, + MySecondFork bool } // AllForksEnabled should contain all supported forks by current edge version @@ -157,4 +163,6 @@ var AllForksEnabled = &Forks{ Petersburg: NewFork(0), Istanbul: NewFork(0), London: NewFork(0), + MyFirstFork: NewFork(10), + MySecondFork: NewFork(20), } diff --git a/consensus/consensus.go b/consensus/consensus.go index 25ceba2e54..4403cf02f6 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -38,7 +38,7 @@ type Consensus interface { GetBridgeProvider() BridgeDataProvider // FilterExtra filters extra data in header that is not a part of block hash - FilterExtra(extra []byte) ([]byte, error) + FilterExtra(*types.Header) ([]byte, error) // Initialize initializes the consensus (e.g. setup data) Initialize() error diff --git a/consensus/dev/dev.go b/consensus/dev/dev.go index ca10891878..77318871b1 100644 --- a/consensus/dev/dev.go +++ b/consensus/dev/dev.go @@ -250,6 +250,6 @@ func (d *Dev) GetBridgeProvider() consensus.BridgeDataProvider { return nil } -func (d *Dev) FilterExtra(extra []byte) ([]byte, error) { - return extra, nil +func (d *Dev) FilterExtra(header *types.Header) ([]byte, error) { + return header.ExtraData, nil } diff --git a/consensus/dummy/dummy.go b/consensus/dummy/dummy.go index b4ccce873d..5e73e0ab37 100644 --- a/consensus/dummy/dummy.go +++ b/consensus/dummy/dummy.go @@ -79,8 +79,8 @@ func (d *Dummy) GetBridgeProvider() consensus.BridgeDataProvider { return nil } -func (d *Dummy) FilterExtra(extra []byte) ([]byte, error) { - return extra, nil +func (d *Dummy) FilterExtra(header *types.Header) ([]byte, error) { + return header.ExtraData, nil } func (d *Dummy) run() { diff --git a/consensus/ibft/ibft.go b/consensus/ibft/ibft.go index 08c719ddd0..3058443a58 100644 --- a/consensus/ibft/ibft.go +++ b/consensus/ibft/ibft.go @@ -552,8 +552,8 @@ func (i *backendIBFT) GetBridgeProvider() consensus.BridgeDataProvider { } // FilterExtra is the implementation of Consensus interface -func (i *backendIBFT) FilterExtra(extra []byte) ([]byte, error) { - return extra, nil +func (i *backendIBFT) FilterExtra(header *types.Header) ([]byte, error) { + return header.ExtraData, nil } // updateCurrentModules updates Signer, Hooks, and Validators diff --git a/consensus/polybft/checkpoint_manager.go b/consensus/polybft/checkpoint_manager.go index 3544e6d4ee..da7ed88508 100644 --- a/consensus/polybft/checkpoint_manager.go +++ b/consensus/polybft/checkpoint_manager.go @@ -142,7 +142,7 @@ func (c *checkpointManager) submitCheckpoint(latestHeader *types.Header, isEndOf return fmt.Errorf("block %d was not found", initialBlockNumber) } - parentExtra, err = GetIbftExtra(parentHeader.ExtraData) + parentExtra, err = GetIbftExtra(parentHeader.ExtraData, parentHeader.Number) if err != nil { return err } @@ -155,7 +155,7 @@ func (c *checkpointManager) submitCheckpoint(latestHeader *types.Header, isEndOf return fmt.Errorf("block %d was not found", blockNumber) } - currentExtra, err = GetIbftExtra(currentHeader.ExtraData) + currentExtra, err = GetIbftExtra(currentHeader.ExtraData, currentHeader.Number) if err != nil { return err } @@ -182,7 +182,7 @@ func (c *checkpointManager) submitCheckpoint(latestHeader *types.Header, isEndOf // (in case there were pending checkpoint blocks) if currentExtra == nil { // we need to send checkpoint for the latest block - currentExtra, err = GetIbftExtra(latestHeader.ExtraData) + currentExtra, err = GetIbftExtra(latestHeader.ExtraData, latestHeader.Number) if err != nil { return err } diff --git a/consensus/polybft/consensus_metrics.go b/consensus/polybft/consensus_metrics.go index 29a916abca..dfad56dfaa 100644 --- a/consensus/polybft/consensus_metrics.go +++ b/consensus/polybft/consensus_metrics.go @@ -25,7 +25,7 @@ func updateBlockMetrics(currentBlock *types.Block, parentHeader *types.Header) e // update the number of transactions in the block metric metrics.SetGauge([]string{consensusMetricsPrefix, "num_txs"}, float32(len(currentBlock.Transactions))) - extra, err := GetIbftExtra(currentBlock.Header.ExtraData) + extra, err := GetIbftExtra(currentBlock.Header.ExtraData, currentBlock.Number()) if err != nil { return err } diff --git a/consensus/polybft/consensus_runtime.go b/consensus/polybft/consensus_runtime.go index 0b64602d6a..a4982ab7fa 100644 --- a/consensus/polybft/consensus_runtime.go +++ b/consensus/polybft/consensus_runtime.go @@ -520,7 +520,7 @@ func (c *consensusRuntime) calculateCommitEpochInput( return nil } - blockExtra, err := GetIbftExtra(currentBlock.ExtraData) + blockExtra, err := GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) if err != nil { return nil, nil, err } @@ -694,7 +694,7 @@ func (c *consensusRuntime) IsValidProposalHash(proposal *proto.Proposal, hash [] return false } - extra, err := GetIbftExtra(block.Header.ExtraData) + extra, err := GetIbftExtra(block.Header.ExtraData, block.Number()) if err != nil { c.logger.Error("failed to retrieve extra", "block number", block.Number(), "error", err) @@ -800,7 +800,7 @@ func (c *consensusRuntime) BuildPrePrepareMessage( return nil } - extra, err := GetIbftExtra(block.Header.ExtraData) + extra, err := GetIbftExtra(block.Header.ExtraData, block.Number()) if err != nil { c.logger.Error("failed to retrieve extra for block %d: %w", block.Number(), err) @@ -932,7 +932,7 @@ func (c *consensusRuntime) getFirstBlockOfEpoch(epochNumber uint64, latestHeader blockHeader := latestHeader - blockExtra, err := GetIbftExtra(latestHeader.ExtraData) + blockExtra, err := GetIbftExtra(latestHeader.ExtraData, latestHeader.Number) if err != nil { return 0, err } diff --git a/consensus/polybft/extra.go b/consensus/polybft/extra.go index 2f6a6b5f0e..36343180ca 100644 --- a/consensus/polybft/extra.go +++ b/consensus/polybft/extra.go @@ -26,122 +26,52 @@ var PolyBFTMixDigest = types.StringToHash("adce6e5230abe012342a44e4e9b6d05997d6f // Extra defines the structure of the extra field for Istanbul type Extra struct { - Validators *validator.ValidatorSetDelta - Parent *Signature - Committed *Signature - Checkpoint *CheckpointData + Validators *validator.ValidatorSetDelta + Parent *Signature + Committed *Signature + Checkpoint *CheckpointData + Dummy1 string // MyFirstFork fork + Dummy2 string // MySecondFork fork + BlockNumber uint64 // field used by forking manager } // MarshalRLPTo defines the marshal function wrapper for Extra -func (i *Extra) MarshalRLPTo(dst []byte) []byte { +func (e *Extra) MarshalRLPTo(dst []byte) []byte { ar := &fastrlp.Arena{} - return append(make([]byte, ExtraVanity), i.MarshalRLPWith(ar).MarshalTo(dst)...) + return append(make([]byte, ExtraVanity), e.MarshalRLPWith(ar).MarshalTo(dst)...) } // MarshalRLPWith defines the marshal function implementation for Extra -func (i *Extra) MarshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { - vv := ar.NewArray() - - // Validators - if i.Validators == nil { - vv.Set(ar.NewNullArray()) - } else { - vv.Set(i.Validators.MarshalRLPWith(ar)) - } - - // Parent Signatures - if i.Parent == nil { - vv.Set(ar.NewNullArray()) - } else { - vv.Set(i.Parent.MarshalRLPWith(ar)) - } - - // Committed Signatures - if i.Committed == nil { - vv.Set(ar.NewNullArray()) - } else { - vv.Set(i.Committed.MarshalRLPWith(ar)) - } - - // Checkpoint - if i.Checkpoint == nil { - vv.Set(ar.NewNullArray()) - } else { - vv.Set(i.Checkpoint.MarshalRLPWith(ar)) - } - - return vv +func (e *Extra) MarshalRLPWith(ar *fastrlp.Arena) *fastrlp.Value { + return GetExtraHandler(e.BlockNumber).MarshalRLPWith(e, ar) } // UnmarshalRLP defines the unmarshal function wrapper for Extra -func (i *Extra) UnmarshalRLP(input []byte) error { - return fastrlp.UnmarshalRLP(input[ExtraVanity:], i) +func (e *Extra) UnmarshalRLP(input []byte) error { + return fastrlp.UnmarshalRLP(input[ExtraVanity:], e) } // UnmarshalRLPWith defines the unmarshal implementation for Extra -func (i *Extra) UnmarshalRLPWith(v *fastrlp.Value) error { - const expectedElements = 4 - - elems, err := v.GetElems() - if err != nil { - return err - } - - if num := len(elems); num != expectedElements { - return fmt.Errorf("incorrect elements count to decode Extra, expected %d but found %d", expectedElements, num) - } - - // Validators - if elems[0].Elems() > 0 { - i.Validators = &validator.ValidatorSetDelta{} - if err := i.Validators.UnmarshalRLPWith(elems[0]); err != nil { - return err - } - } - - // Parent Signatures - if elems[1].Elems() > 0 { - i.Parent = &Signature{} - if err := i.Parent.UnmarshalRLPWith(elems[1]); err != nil { - return err - } - } - - // Committed Signatures - if elems[2].Elems() > 0 { - i.Committed = &Signature{} - if err := i.Committed.UnmarshalRLPWith(elems[2]); err != nil { - return err - } - } - - // Checkpoint - if elems[3].Elems() > 0 { - i.Checkpoint = &CheckpointData{} - if err := i.Checkpoint.UnmarshalRLPWith(elems[3]); err != nil { - return err - } - } - - return nil +func (e *Extra) UnmarshalRLPWith(v *fastrlp.Value) error { + return GetExtraHandler(e.BlockNumber).UnmarshalRLPWith(e, v) } // ValidateFinalizedData contains extra data validations for finalized headers -func (i *Extra) ValidateFinalizedData(header *types.Header, parent *types.Header, parents []*types.Header, +func (e *Extra) ValidateFinalizedData(header *types.Header, parent *types.Header, parents []*types.Header, chainID uint64, consensusBackend polybftBackend, domain []byte, logger hclog.Logger) error { // validate committed signatures blockNumber := header.Number - if i.Committed == nil { + if e.Committed == nil { return fmt.Errorf("failed to verify signatures for block %d, because signatures are not present", blockNumber) } - if i.Checkpoint == nil { + if e.Checkpoint == nil { return fmt.Errorf("failed to verify signatures for block %d, because checkpoint data are not present", blockNumber) } // validate current block signatures - checkpointHash, err := i.Checkpoint.Hash(chainID, header.Number, header.Hash) + checkpointHash, err := e.Checkpoint.Hash(chainID, header.Number, header.Hash) if err != nil { return fmt.Errorf("failed to calculate proposal hash: %w", err) } @@ -151,34 +81,34 @@ func (i *Extra) ValidateFinalizedData(header *types.Header, parent *types.Header return fmt.Errorf("failed to validate header for block %d. could not retrieve block validators:%w", blockNumber, err) } - if err := i.Committed.Verify(validators, checkpointHash, domain, logger); err != nil { + if err := e.Committed.Verify(validators, checkpointHash, domain, logger); err != nil { return fmt.Errorf("failed to verify signatures for block %d (proposal hash %s): %w", blockNumber, checkpointHash, err) } - parentExtra, err := GetIbftExtra(parent.ExtraData) + parentExtra, err := GetIbftExtra(parent.ExtraData, parent.Number) if err != nil { return fmt.Errorf("failed to verify signatures for block %d: %w", blockNumber, err) } // validate parent signatures - if err := i.ValidateParentSignatures(blockNumber, consensusBackend, parents, + if err := e.ValidateParentSignatures(blockNumber, consensusBackend, parents, parent, parentExtra, chainID, domain, logger); err != nil { return err } - return i.Checkpoint.ValidateBasic(parentExtra.Checkpoint) + return e.Checkpoint.ValidateBasic(parentExtra.Checkpoint) } // ValidateParentSignatures validates signatures for parent block -func (i *Extra) ValidateParentSignatures(blockNumber uint64, consensusBackend polybftBackend, parents []*types.Header, +func (e *Extra) ValidateParentSignatures(blockNumber uint64, consensusBackend polybftBackend, parents []*types.Header, parent *types.Header, parentExtra *Extra, chainID uint64, domain []byte, logger hclog.Logger) error { // skip block 1 because genesis does not have committed signatures if blockNumber <= 1 { return nil } - if i.Parent == nil { + if e.Parent == nil { return fmt.Errorf("failed to verify signatures for parent of block %d because signatures are not present", blockNumber) } @@ -197,7 +127,7 @@ func (i *Extra) ValidateParentSignatures(blockNumber uint64, consensusBackend po return fmt.Errorf("failed to calculate parent proposal hash: %w", err) } - if err := i.Parent.Verify(parentValidators, parentCheckpointHash, domain, logger); err != nil { + if err := e.Parent.Verify(parentValidators, parentCheckpointHash, domain, logger); err != nil { return fmt.Errorf("failed to verify signatures for parent of block %d (proposal hash: %s): %w", blockNumber, parentCheckpointHash, err) } @@ -205,6 +135,10 @@ func (i *Extra) ValidateParentSignatures(blockNumber uint64, consensusBackend po return nil } +func (e *Extra) ValidateAdditional(header *types.Header) error { + return GetExtraHandler(e.BlockNumber).ValidateAdditional(e, header) +} + // Signature represents aggregated signatures of signers accompanied with a bitmap // (in order to be able to determine identities of each signer) type Signature struct { @@ -464,29 +398,24 @@ func (c *CheckpointData) Validate(parentCheckpoint *CheckpointData, // GetIbftExtraClean returns unmarshaled extra field from the passed in header, // but without signatures for the given header (it only includes signatures for the parent block) -func GetIbftExtraClean(extraRaw []byte) ([]byte, error) { - extra, err := GetIbftExtra(extraRaw) +func GetIbftExtraClean(extraRaw []byte, blockNumber uint64) ([]byte, error) { + extra, err := GetIbftExtra(extraRaw, blockNumber) if err != nil { return nil, err } - ibftExtra := &Extra{ - Parent: extra.Parent, - Validators: extra.Validators, - Checkpoint: extra.Checkpoint, - Committed: &Signature{}, - } - - return ibftExtra.MarshalRLPTo(nil), nil + return GetExtraHandler(blockNumber).GetIbftExtraClean(extra).MarshalRLPTo(nil), nil } // GetIbftExtra returns the istanbul extra data field from the passed in header -func GetIbftExtra(extraRaw []byte) (*Extra, error) { +func GetIbftExtra(extraRaw []byte, blockNumber uint64) (*Extra, error) { if len(extraRaw) < ExtraVanity { return nil, fmt.Errorf("wrong extra size: %d", len(extraRaw)) } - extra := &Extra{} + extra := &Extra{ + BlockNumber: blockNumber, + } if err := extra.UnmarshalRLP(extraRaw); err != nil { return nil, err diff --git a/consensus/polybft/extra_fork_base.go b/consensus/polybft/extra_fork_base.go new file mode 100644 index 0000000000..604a49f733 --- /dev/null +++ b/consensus/polybft/extra_fork_base.go @@ -0,0 +1,131 @@ +package polybft + +import ( + "fmt" + + "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" + "github.com/0xPolygon/polygon-edge/forkmanager" + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +func init() { + // for tests + forkManagerInstance.RegisterFork(forkmanager.InitialFork) + _ = forkManagerInstance.RegisterHandler(forkmanager.InitialFork, extraHandler, &ExtraHandlerBase{}) + _ = forkManagerInstance.ActivateFork(forkmanager.InitialFork, 0) +} + +const extraHandler forkmanager.HandlerDesc = "extra" + +type IExtraHandler interface { + MarshalRLPWith(extra *Extra, ar *fastrlp.Arena) *fastrlp.Value + UnmarshalRLPWith(extra *Extra, v *fastrlp.Value) error + ValidateAdditional(extra *Extra, header *types.Header) error + GetIbftExtraClean(extra *Extra) *Extra +} + +var forkManagerInstance = forkmanager.GetInstance() + +func GetExtraHandler(blockNumber uint64) IExtraHandler { + handler := forkManagerInstance.GetHandler(extraHandler, blockNumber) + + return handler.(IExtraHandler) //nolint:forcetypeassert +} + +type ExtraHandlerBase struct { +} + +// MarshalRLPWith defines the marshal function implementation for Extra +func (e *ExtraHandlerBase) MarshalRLPWith(extra *Extra, ar *fastrlp.Arena) *fastrlp.Value { + return extraMarshalBaseFields(extra, ar, ar.NewArray()) +} + +func (e *ExtraHandlerBase) UnmarshalRLPWith(extra *Extra, v *fastrlp.Value) error { + _, err := extraUnmarshalBaseFields(extra, v, 4) + + return err +} + +func (e *ExtraHandlerBase) ValidateAdditional(extra *Extra, header *types.Header) error { + return nil +} + +func (e *ExtraHandlerBase) GetIbftExtraClean(extra *Extra) *Extra { + return &Extra{ + BlockNumber: extra.BlockNumber, + Parent: extra.Parent, + Validators: extra.Validators, + Checkpoint: extra.Checkpoint, + Committed: &Signature{}, + } +} + +func extraMarshalBaseFields(extra *Extra, ar *fastrlp.Arena, vv *fastrlp.Value) *fastrlp.Value { + if extra.Validators == nil { + vv.Set(ar.NewNullArray()) + } else { + vv.Set(extra.Validators.MarshalRLPWith(ar)) + } + + if extra.Parent == nil { + vv.Set(ar.NewNullArray()) + } else { + vv.Set(extra.Parent.MarshalRLPWith(ar)) + } + + if extra.Committed == nil { + vv.Set(ar.NewNullArray()) + } else { + vv.Set(extra.Committed.MarshalRLPWith(ar)) + } + + if extra.Checkpoint == nil { + vv.Set(ar.NewNullArray()) + } else { + vv.Set(extra.Checkpoint.MarshalRLPWith(ar)) + } + + return vv +} + +func extraUnmarshalBaseFields(extra *Extra, v *fastrlp.Value, expectedCnt int) ([]*fastrlp.Value, error) { + elems, err := v.GetElems() + if err != nil { + return nil, err + } + + if num := len(elems); num != expectedCnt { + return nil, fmt.Errorf("incorrect elements count to decode extra, expected %d but found %d", expectedCnt, num) + } + + if elems[0].Elems() > 0 { + extra.Validators = &validator.ValidatorSetDelta{} + if err := extra.Validators.UnmarshalRLPWith(elems[0]); err != nil { + return nil, err + } + } + + if elems[1].Elems() > 0 { + extra.Parent = &Signature{} + if err := extra.Parent.UnmarshalRLPWith(elems[1]); err != nil { + return nil, err + } + } + + if elems[2].Elems() > 0 { + extra.Committed = &Signature{} + if err := extra.Committed.UnmarshalRLPWith(elems[2]); err != nil { + return nil, err + } + } + + if elems[3].Elems() > 0 { + extra.Checkpoint = &CheckpointData{} + if err := extra.Checkpoint.UnmarshalRLPWith(elems[3]); err != nil { + return nil, err + } + } + + return elems, nil +} diff --git a/consensus/polybft/extra_fork_myfirstfork.go b/consensus/polybft/extra_fork_myfirstfork.go new file mode 100644 index 0000000000..219661e049 --- /dev/null +++ b/consensus/polybft/extra_fork_myfirstfork.go @@ -0,0 +1,54 @@ +package polybft + +import ( + "fmt" + + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +type ExtraHandlerMyFirstFork struct { +} + +// MarshalRLPWith defines the marshal function implementation for Extra +func (e *ExtraHandlerMyFirstFork) MarshalRLPWith(extra *Extra, ar *fastrlp.Arena) *fastrlp.Value { + vv := extraMarshalBaseFields(extra, ar, ar.NewArray()) + + vv.Set(ar.NewString(extra.Dummy1)) + + return vv +} + +// UnmarshalRLPWith defines the unmarshal implementation for Extra +func (e *ExtraHandlerMyFirstFork) UnmarshalRLPWith(extra *Extra, v *fastrlp.Value) error { + elems, err := extraUnmarshalBaseFields(extra, v, 5) + if err != nil { + return err + } + + extra.Dummy1, err = elems[4].GetString() + if err != nil { + return fmt.Errorf("dummy1 bytes: %w", err) + } + + return nil +} + +func (e *ExtraHandlerMyFirstFork) ValidateAdditional(extra *Extra, header *types.Header) error { + if extra.Dummy1 == "" { + return fmt.Errorf("dummy1 is empty for block %d", header.Number) + } + + return nil +} + +func (e *ExtraHandlerMyFirstFork) GetIbftExtraClean(extra *Extra) *Extra { + return &Extra{ + BlockNumber: extra.BlockNumber, + Parent: extra.Parent, + Validators: extra.Validators, + Checkpoint: extra.Checkpoint, + Committed: &Signature{}, + Dummy1: extra.Dummy1, + } +} diff --git a/consensus/polybft/extra_fork_mysecondfork.go b/consensus/polybft/extra_fork_mysecondfork.go new file mode 100644 index 0000000000..fabbde9bdc --- /dev/null +++ b/consensus/polybft/extra_fork_mysecondfork.go @@ -0,0 +1,65 @@ +package polybft + +import ( + "fmt" + + "github.com/0xPolygon/polygon-edge/types" + "github.com/umbracle/fastrlp" +) + +type ExtraHandlerMySecondFork struct { +} + +// MarshalRLPWith defines the marshal function implementation for Extra +func (e *ExtraHandlerMySecondFork) MarshalRLPWith(extra *Extra, ar *fastrlp.Arena) *fastrlp.Value { + vv := extraMarshalBaseFields(extra, ar, ar.NewArray()) + + vv.Set(ar.NewString(extra.Dummy1)) + vv.Set(ar.NewString(extra.Dummy2)) + + return vv +} + +// UnmarshalRLPWith defines the unmarshal implementation for Extra +func (e *ExtraHandlerMySecondFork) UnmarshalRLPWith(extra *Extra, v *fastrlp.Value) error { + elems, err := extraUnmarshalBaseFields(extra, v, 6) + if err != nil { + return err + } + + extra.Dummy1, err = elems[4].GetString() + if err != nil { + return fmt.Errorf("dummy1 bytes: %w", err) + } + + extra.Dummy2, err = elems[5].GetString() + if err != nil { + return fmt.Errorf("dummy2 bytes: %w", err) + } + + return nil +} + +func (e *ExtraHandlerMySecondFork) ValidateAdditional(extra *Extra, header *types.Header) error { + if extra.Dummy1 == "" { + return fmt.Errorf("dummy1 is empty for block %d", header.Number) + } + + if extra.Dummy2 == "" { + return fmt.Errorf("dummy2 is empty for block %d", header.Number) + } + + return nil +} + +func (e *ExtraHandlerMySecondFork) GetIbftExtraClean(extra *Extra) *Extra { + return &Extra{ + BlockNumber: extra.BlockNumber, + Parent: extra.Parent, + Validators: extra.Validators, + Checkpoint: extra.Checkpoint, + Committed: &Signature{}, + Dummy1: extra.Dummy1, + Dummy2: extra.Dummy2, + } +} diff --git a/consensus/polybft/extra_test.go b/consensus/polybft/extra_test.go index 4f33798291..a7cf439368 100644 --- a/consensus/polybft/extra_test.go +++ b/consensus/polybft/extra_test.go @@ -135,7 +135,7 @@ func TestExtra_UnmarshalRLPWith_NegativeCases(t *testing.T) { extra := &Extra{} ar := &fastrlp.Arena{} - require.ErrorContains(t, extra.UnmarshalRLPWith(ar.NewArray()), "incorrect elements count to decode Extra, expected 4 but found 0") + require.ErrorContains(t, extra.UnmarshalRLPWith(ar.NewArray()), "incorrect elements count to decode extra, expected 4 but found 0") }) t.Run("Incorrect ValidatorSetDelta marshalled", func(t *testing.T) { @@ -517,7 +517,7 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) { ExtraData: extra.MarshalRLPTo(nil), } - genesisExtra, err := GetIbftExtra(genesis.ExtraData) + genesisExtra, err := GetIbftExtra(genesis.ExtraData, genesis.Number) assert.NoError(t, err) assert.Len(t, genesisExtra.Validators.Added, validatorsCount) assert.Empty(t, genesisExtra.Validators.Removed) @@ -530,7 +530,7 @@ func TestExtra_InitGenesisValidatorsDelta(t *testing.T) { ExtraData: append(make([]byte, ExtraVanity), []byte{0x2, 0x3}...), } - _, err := GetIbftExtra(genesis.ExtraData) + _, err := GetIbftExtra(genesis.ExtraData, genesis.Number) require.Error(t, err) }) @@ -570,7 +570,7 @@ func Test_GetIbftExtraClean(t *testing.T) { }, } - extraClean, err := GetIbftExtraClean(extra.MarshalRLPTo(nil)) + extraClean, err := GetIbftExtraClean(extra.MarshalRLPTo(nil), extra.BlockNumber) require.NoError(t, err) extraTwo := &Extra{} @@ -595,7 +595,7 @@ func Test_GetIbftExtraClean_Fail(t *testing.T) { _, err := rand.Read(randomBytes[:]) require.NoError(t, err) - extra, err := GetIbftExtraClean(append(randomBytes[:], []byte{0x12, 0x6}...)) + extra, err := GetIbftExtraClean(append(randomBytes[:], []byte{0x12, 0x6}...), 0) require.Error(t, err) require.Nil(t, extra) } diff --git a/consensus/polybft/fsm.go b/consensus/polybft/fsm.go index e7603236af..cad0be3a4e 100644 --- a/consensus/polybft/fsm.go +++ b/consensus/polybft/fsm.go @@ -9,12 +9,14 @@ import ( "github.com/0xPolygon/go-ibft/messages" "github.com/0xPolygon/go-ibft/messages/proto" + "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/consensus/polybft/bitmap" "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" bls "github.com/0xPolygon/polygon-edge/consensus/polybft/signer" "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" "github.com/0xPolygon/polygon-edge/contracts" + "github.com/0xPolygon/polygon-edge/forkmanager" "github.com/0xPolygon/polygon-edge/state" "github.com/0xPolygon/polygon-edge/types" hcf "github.com/hashicorp/go-hclog" @@ -106,14 +108,17 @@ type fsm struct { func (f *fsm) BuildProposal(currentRound uint64) ([]byte, error) { parent := f.parent - extraParent, err := GetIbftExtra(parent.ExtraData) + extraParent, err := GetIbftExtra(parent.ExtraData, parent.Number) if err != nil { return nil, err } //nolint:godox // TODO: we will need to revisit once slashing is implemented (to be fixed in EVM-519) - extra := &Extra{Parent: extraParent.Committed} + extra := &Extra{ + Parent: extraParent.Committed, + BlockNumber: parent.Number + 1, + } // for non-epoch ending blocks, currentValidatorsHash is the same as the nextValidatorsHash nextValidators := f.validators.Accounts() @@ -177,6 +182,14 @@ func (f *fsm) BuildProposal(currentRound uint64) ([]byte, error) { EventRoot: f.exitEventRootHash, } + if forkmanager.GetInstance().IsForkEnabled(chain.London, parent.Number+1) { + extra.Dummy1 = "Prolece" + } + + if forkmanager.GetInstance().IsForkEnabled(chain.Constantinople, parent.Number+1) { + extra.Dummy2 = "Slece" + } + f.logger.Debug("[Build Proposal]", "Current validators hash", currentValidatorsHash, "Next validators hash", nextValidatorsHash) @@ -303,12 +316,12 @@ func (f *fsm) Validate(proposal []byte) error { ) } - extra, err := GetIbftExtra(block.Header.ExtraData) + extra, err := GetIbftExtra(block.Header.ExtraData, block.Number()) if err != nil { return fmt.Errorf("cannot get extra data:%w", err) } - parentExtra, err := GetIbftExtra(f.parent.ExtraData) + parentExtra, err := GetIbftExtra(f.parent.ExtraData, f.parent.Number) if err != nil { return err } @@ -326,6 +339,10 @@ func (f *fsm) Validate(proposal []byte) error { return err } + if err := extra.ValidateAdditional(block.Header); err != nil { + return err + } + if err := f.VerifyStateTransactions(block.Transactions); err != nil { return err } @@ -521,7 +538,7 @@ func (f *fsm) Insert(proposal []byte, committedSeals []*messages.CommittedSeal) // In this function we should try to return little to no errors since // at this point everything we have to do is just commit something that // we should have already computed beforehand. - extra, err := GetIbftExtra(newBlock.Block.Header.ExtraData) + extra, err := GetIbftExtra(newBlock.Block.Header.ExtraData, newBlock.Block.Number()) if err != nil { return nil, fmt.Errorf("failed to insert proposal, due to not being able to extract extra data: %w", err) } diff --git a/consensus/polybft/fsm_test.go b/consensus/polybft/fsm_test.go index fd2cc06533..340cba69b5 100644 --- a/consensus/polybft/fsm_test.go +++ b/consensus/polybft/fsm_test.go @@ -341,11 +341,11 @@ func TestFSM_BuildProposal_EpochEndingBlock_ValidatorsDeltaExists(t *testing.T) } proposal, err := fsm.BuildProposal(0) - assert.NoError(t, err) - assert.NotNil(t, proposal) + require.NoError(t, err) + require.NotNil(t, proposal) - blockExtra, err := GetIbftExtra(stateBlock.Block.Header.ExtraData) - assert.NoError(t, err) + blockExtra, err := GetIbftExtra(stateBlock.Block.Header.ExtraData, parentBlockNumber+1) + require.NoError(t, err) assert.Len(t, blockExtra.Validators.Added, 2) assert.False(t, blockExtra.Validators.IsEmpty()) @@ -391,11 +391,11 @@ func TestFSM_BuildProposal_NonEpochEndingBlock_ValidatorsDeltaEmpty(t *testing.T exitEventRootHash: types.ZeroHash, logger: hclog.NewNullLogger()} proposal, err := fsm.BuildProposal(0) - assert.NoError(t, err) + require.NoError(t, err) assert.NotNil(t, proposal) - blockExtra, err := GetIbftExtra(stateBlock.Block.Header.ExtraData) - assert.NoError(t, err) + blockExtra, err := GetIbftExtra(stateBlock.Block.Header.ExtraData, stateBlock.Block.Number()) + require.NoError(t, err) assert.True(t, blockExtra.Validators.IsEmpty()) blockBuilderMock.AssertExpectations(t) diff --git a/consensus/polybft/hash.go b/consensus/polybft/hash.go index 7f769bc07c..6ca837c5d5 100644 --- a/consensus/polybft/hash.go +++ b/consensus/polybft/hash.go @@ -17,7 +17,7 @@ func setupHeaderHashFunc() { types.HeaderHash = func(h *types.Header) types.Hash { // when hashing the block for signing we have to remove from // the extra field the seal and committed seal items - extra, err := GetIbftExtraClean(h.ExtraData) + extra, err := GetIbftExtraClean(h.ExtraData, h.Number) if err != nil { return types.ZeroHash } diff --git a/consensus/polybft/polybft.go b/consensus/polybft/polybft.go index 399375db9b..9f4974aaa0 100644 --- a/consensus/polybft/polybft.go +++ b/consensus/polybft/polybft.go @@ -16,6 +16,7 @@ import ( "github.com/0xPolygon/polygon-edge/consensus/polybft/validator" "github.com/0xPolygon/polygon-edge/consensus/polybft/wallet" "github.com/0xPolygon/polygon-edge/contracts" + "github.com/0xPolygon/polygon-edge/forkmanager" "github.com/0xPolygon/polygon-edge/helper/common" "github.com/0xPolygon/polygon-edge/helper/progress" "github.com/0xPolygon/polygon-edge/network" @@ -362,8 +363,18 @@ func GenesisPostHookFactory(config *chain.Chain, engineName string) func(txn *st } func ForkManagerFactory(forks *chain.Forks) error { - // place fork manager handler registration here - return nil + fm := forkmanager.GetInstance() + + // Register handlers here + if err := fm.RegisterHandler(forkmanager.InitialFork, extraHandler, &ExtraHandlerBase{}); err != nil { + return err + } + + if err := fm.RegisterHandler(chain.MyFirstFork, extraHandler, &ExtraHandlerMyFirstFork{}); err != nil { + return err + } + + return fm.RegisterHandler(chain.MySecondFork, extraHandler, &ExtraHandlerMySecondFork{}) } // Initialize initializes the consensus (e.g. setup data) @@ -641,11 +652,15 @@ func (p *Polybft) verifyHeaderImpl(parent, header *types.Header, blockTimeDrift } // decode the extra data - extra, err := GetIbftExtra(header.ExtraData) + extra, err := GetIbftExtra(header.ExtraData, header.Number) if err != nil { return fmt.Errorf("failed to verify header for block %d. get extra error = %w", header.Number, err) } + if err := extra.ValidateAdditional(header); err != nil { + return err + } + // validate extra data return extra.ValidateFinalizedData( header, parent, parents, p.blockchain.GetChainID(), p, bls.DomainCheckpointManager, p.logger) @@ -680,6 +695,6 @@ func (p *Polybft) GetBridgeProvider() consensus.BridgeDataProvider { // GetBridgeProvider is an implementation of Consensus interface // Filters extra data to not contain Committed field -func (p *Polybft) FilterExtra(extra []byte) ([]byte, error) { - return GetIbftExtraClean(extra) +func (p *Polybft) FilterExtra(header *types.Header) ([]byte, error) { + return GetIbftExtraClean(header.ExtraData, header.Number) } diff --git a/consensus/polybft/runtime_helpers.go b/consensus/polybft/runtime_helpers.go index f2ffa2a49a..29b82bce8a 100644 --- a/consensus/polybft/runtime_helpers.go +++ b/consensus/polybft/runtime_helpers.go @@ -18,7 +18,7 @@ func getBlockData(blockNumber uint64, blockchainBackend blockchainBackend) (*typ return nil, nil, blockchain.ErrNoBlock } - blockExtra, err := GetIbftExtra(blockHeader.ExtraData) + blockExtra, err := GetIbftExtra(blockHeader.ExtraData, blockHeader.Number) if err != nil { return nil, nil, err } diff --git a/consensus/polybft/validators_snapshot.go b/consensus/polybft/validators_snapshot.go index d4ba539920..ff6678b5bb 100644 --- a/consensus/polybft/validators_snapshot.go +++ b/consensus/polybft/validators_snapshot.go @@ -174,7 +174,7 @@ func (v *validatorsSnapshotCache) computeSnapshot( } } - extra, err := GetIbftExtra(header.ExtraData) + extra, err := GetIbftExtra(header.ExtraData, header.Number) if err != nil { return nil, fmt.Errorf("failed to decode extra from the block#%d: %w", header.Number, err) } diff --git a/e2e-polybft/e2e/bridge_test.go b/e2e-polybft/e2e/bridge_test.go index f13a2ccfe7..29d046b75f 100644 --- a/e2e-polybft/e2e/bridge_test.go +++ b/e2e-polybft/e2e/bridge_test.go @@ -139,7 +139,7 @@ func TestE2E_Bridge_Transfers(t *testing.T) { currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber) @@ -411,7 +411,7 @@ func TestE2E_Bridge_DepositAndWithdrawERC721(t *testing.T) { currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber) @@ -594,7 +594,7 @@ func TestE2E_Bridge_DepositAndWithdrawERC1155(t *testing.T) { currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) currentEpoch := currentExtra.Checkpoint.EpochNumber @@ -785,7 +785,7 @@ func TestE2E_Bridge_ChangeVotingPower(t *testing.T) { currentBlock, err := childRelayer.Client().Eth().GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) targetEpoch := currentExtra.Checkpoint.EpochNumber + 2 @@ -960,7 +960,7 @@ func TestE2E_Bridge_Transfers_AccessLists(t *testing.T) { currentBlock, err := childEthEndpoint.GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber) diff --git a/e2e-polybft/e2e/consensus_test.go b/e2e-polybft/e2e/consensus_test.go index 493d6e0eb3..8ca56d2b60 100644 --- a/e2e-polybft/e2e/consensus_test.go +++ b/e2e-polybft/e2e/consensus_test.go @@ -165,7 +165,7 @@ func TestE2E_Consensus_RegisterValidator(t *testing.T) { genesisBlock, err := owner.JSONRPC().Eth().GetBlockByNumber(0, false) require.NoError(t, err) - _, err = polybft.GetIbftExtra(genesisBlock.ExtraData) + _, err = polybft.GetIbftExtra(genesisBlock.ExtraData, genesisBlock.Number) require.NoError(t, err) // owner whitelists both new validators @@ -359,7 +359,7 @@ func TestE2E_Consensus_Validator_Unstake(t *testing.T) { currentBlock, err := srv.JSONRPC().Eth().GetBlockByNumber(ethgo.Latest, false) require.NoError(t, err) - currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData) + currentExtra, err := polybft.GetIbftExtra(currentBlock.ExtraData, currentBlock.Number) require.NoError(t, err) t.Logf("Latest block number: %d, epoch number: %d\n", currentBlock.Number, currentExtra.Checkpoint.EpochNumber) diff --git a/e2e-polybft/framework/test-cluster.go b/e2e-polybft/framework/test-cluster.go index 47526249e1..4ee378f1e8 100644 --- a/e2e-polybft/framework/test-cluster.go +++ b/e2e-polybft/framework/test-cluster.go @@ -18,9 +18,11 @@ import ( "testing" "time" + "github.com/0xPolygon/polygon-edge/chain" "github.com/0xPolygon/polygon-edge/command/genesis" "github.com/0xPolygon/polygon-edge/consensus/polybft" "github.com/0xPolygon/polygon-edge/consensus/polybft/contractsapi" + "github.com/0xPolygon/polygon-edge/forkmanager" "github.com/0xPolygon/polygon-edge/helper/common" "github.com/0xPolygon/polygon-edge/txrelayer" "github.com/0xPolygon/polygon-edge/types" @@ -58,6 +60,11 @@ var ( func init() { startTime = time.Now().UTC().UnixMilli() + + // register forks + if err := forkmanager.ForkManagerInit(polybft.ForkManagerFactory, chain.AllForksEnabled); err != nil { + panic(err) //nolint:gocritic + } } func resolveBinary() string { diff --git a/e2e-polybft/framework/test-server.go b/e2e-polybft/framework/test-server.go index bb655c2156..689d46c925 100644 --- a/e2e-polybft/framework/test-server.go +++ b/e2e-polybft/framework/test-server.go @@ -325,7 +325,7 @@ func (t *TestServer) HasValidatorSealed(firstBlock, lastBlock uint64, validators return false, err } - extra, err := polybft.GetIbftExtra(block.ExtraData) + extra, err := polybft.GetIbftExtra(block.ExtraData, block.Number) if err != nil { return false, err } diff --git a/jsonrpc/eth_blockchain_test.go b/jsonrpc/eth_blockchain_test.go index d59019809d..869ef80c75 100644 --- a/jsonrpc/eth_blockchain_test.go +++ b/jsonrpc/eth_blockchain_test.go @@ -585,8 +585,8 @@ func (m *mockBlockStore) SubscribeEvents() blockchain.Subscription { return nil } -func (m *mockBlockStore) FilterExtra(extra []byte) ([]byte, error) { - return extra, nil +func (m *mockBlockStore) FilterExtra(header *types.Header) ([]byte, error) { + return header.ExtraData, nil } func newTestBlock(number uint64, hash types.Hash) *types.Block { diff --git a/jsonrpc/eth_endpoint.go b/jsonrpc/eth_endpoint.go index 4fbc257b2d..559a30bd57 100644 --- a/jsonrpc/eth_endpoint.go +++ b/jsonrpc/eth_endpoint.go @@ -70,7 +70,7 @@ type ethBlockchainStore interface { type ethFilter interface { // FilterExtra filters extra data from header extra that is not included in block hash - FilterExtra(extra []byte) ([]byte, error) + FilterExtra(*types.Header) ([]byte, error) } // ethStore provides access to the methods needed by eth endpoint @@ -154,7 +154,7 @@ func (e *Eth) filterExtra(block *types.Block) error { // and not a copy, so changing it, actually changes it in storage as well headerCopy := block.Header.Copy() - filteredExtra, err := e.store.FilterExtra(headerCopy.ExtraData) + filteredExtra, err := e.store.FilterExtra(headerCopy) if err != nil { return err } diff --git a/jsonrpc/mocks_test.go b/jsonrpc/mocks_test.go index 1078090d39..70ba3dd0e5 100644 --- a/jsonrpc/mocks_test.go +++ b/jsonrpc/mocks_test.go @@ -197,6 +197,6 @@ func (m *mockStore) GetStateSyncProof(stateSyncID uint64) (types.Proof, error) { return ssp, nil } -func (m *mockStore) FilterExtra(extra []byte) ([]byte, error) { - return extra, nil +func (m *mockStore) FilterExtra(header *types.Header) ([]byte, error) { + return header.ExtraData, nil }