From 43b5a98a705ed0555cdf8692730cdbce88a7a65b Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Wed, 4 Dec 2024 17:26:39 -0500 Subject: [PATCH 1/8] WIP on "astria forks" config changes --- consensus/misc/eip1559/eip1559.go | 5 +- core/blockchain.go | 2 +- core/genesis.go | 2 +- go.mod | 10 +- go.sum | 20 +-- grpc/execution/server.go | 115 +++++------- params/config.go | 284 +++++++++++++++++++++++------- 7 files changed, 280 insertions(+), 158 deletions(-) diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 8c50c52b0..152060d7e 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -90,10 +90,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number.Uint64()+1))) baseFee := num.Sub(parent.BaseFee, num) - lowerBound := common.Big0 - if config.AstriaEIP1559Params != nil { - lowerBound = config.AstriaEIP1559Params.MinBaseFeeAt(parent.Number.Uint64() + 1) - } + lowerBound := config.GetAstriaForks().MinBaseFeeAt(parent.Number.Uint64() + 1) return math.BigMax(baseFee, lowerBound) } diff --git a/core/blockchain.go b/core/blockchain.go index ca79738d6..bbef6026f 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -325,7 +325,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis bc.currentBlock.Store(bc.genesisBlock.Header()) bc.currentFinalBlock.Store(bc.genesisBlock.Header()) bc.currentSafeBlock.Store(bc.genesisBlock.Header()) - bc.currentBaseCelestiaHeight.Store(bc.Config().AstriaCelestiaInitialHeight) + bc.currentBaseCelestiaHeight.Store(bc.Config().GetAstriaForks().GetForkAtHeight(1).Celestia.StartHeight) // Update chain info data metrics chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainID.String()}) diff --git a/core/genesis.go b/core/genesis.go index 1ac4ce1fc..447dd5de2 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -429,7 +429,7 @@ func (g *Genesis) ToBlock() *types.Block { extraData := g.ExtraData if g.Config.AstriaOverrideGenesisExtraData { - extraData = g.Config.AstriaExtraData() + extraData = g.Config.AstriaExtraData(1) } head := &types.Header{ diff --git a/go.mod b/go.mod index 1055cd47c..8efbc839d 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/ethereum/go-ethereum go 1.21 require ( - buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-20241017141511-7e4bcc0ebba5.1 - buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.1-20241017141511-7e4bcc0ebba5.1 - buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.1-20240911152449-eeebd3decdce.1 - buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.1-20241017141511-71aab1871615.1 + buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1 + buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1 + buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 + buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 github.com/Microsoft/go-winio v0.6.1 github.com/VictoriaMetrics/fastcache v1.12.1 @@ -79,7 +79,7 @@ require ( golang.org/x/time v0.5.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d google.golang.org/grpc v1.64.1 - google.golang.org/protobuf v1.35.1 + google.golang.org/protobuf v1.35.2 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 83a47af38..0407c8168 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ -buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-20241017141511-7e4bcc0ebba5.1 h1:v7QnrDjNmG7I/0aqZdtlP3cBPQGd62w4AYVF8TfAcHM= -buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-20241017141511-7e4bcc0ebba5.1/go.mod h1:T5EsLvEE5UMk62gVSwNY/7XlxknAP3sL8tYRsU68b4s= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.1-20241017141511-7e4bcc0ebba5.1 h1:3G2O21DuY5Y/G32tP1mAI16AxwDYTscG2YaOb/WQty0= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.1-20241017141511-7e4bcc0ebba5.1/go.mod h1:U4LUlabiYNYBd1pqYS9o8SsHjBRoEBysrfRVnebzJH0= -buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.1-20240911152449-eeebd3decdce.1 h1:kG4riHqlF9X6iZ1Oxs5/6ul6aue7MS+A6DK6HAchuTk= -buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.1-20240911152449-eeebd3decdce.1/go.mod h1:n9L7X3VAj4od4VHf2ScJuHARUUQTSxJqtRHZk/7Ptt0= -buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.1-20241017141511-71aab1871615.1 h1:hPMoxTiT7jJjnIbWqneBbL05VeVOTD9UeC/qdvzHL8g= -buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.1-20241017141511-71aab1871615.1/go.mod h1:2uasRFMH+a3DaF34c1o+w7/YtYnoknmARyYpb9W2QIc= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1 h1:N+QXc1yaxcVDZRZrdjtfcZgnuwYmKfAjjaJXWq8ZvK8= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1/go.mod h1:/rU1NViOHDnGkCo0cWFsrI1NXDvNJmgMB/yTrRSY6xU= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1 h1:tVea7fkypwPQCCDkY+yma8V4iWVN7b9ZpRSzvuUBNhc= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1/go.mod h1:PDytSMSvh3RzdjqGE/abPqSxFTZtahXu2rmM+9PQhvY= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 h1:NtZp7kyoPm4vxioQPvaPfXNoQUAzRCOKnTfaj/fgBAQ= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1/go.mod h1:I9FcB1oNqT1nI+ny0GD8gF9YrIYrHmczgNu6MTE9fAo= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 h1:d3Cu4wQ6dAf2P+8gSsClUdWe+waNXrHCO02EJrJruFk= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1/go.mod h1:oyKOX3YjAN6T+jYc5Rai+RgrZD9b+sDXpdTSAgIISUA= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -895,8 +895,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 971a4d613..37570c667 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -24,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/miner" - "github.com/ethereum/go-ethereum/params" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" @@ -45,11 +44,6 @@ type ExecutionServiceServerV1 struct { genesisInfoCalled bool getCommitmentStateCalled bool - - bridgeAddresses map[string]*params.AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account - bridgeAllowedAssets map[string]struct{} // a set of allowed asset IDs structs are left empty - - nextFeeRecipient common.Address // Fee recipient for the next block } var ( @@ -81,74 +75,35 @@ func NewExecutionServiceServerV1(eth *eth.Ethereum) (*ExecutionServiceServerV1, return nil, errors.New("rollup name not set") } - if bc.Config().AstriaSequencerInitialHeight == 0 { + fork := bc.Config().GetAstriaForks().GetForkAtHeight(bc.CurrentBlock().Number.Uint64()) + + if fork.Sequencer.ChainID == "" { + return nil, errors.New("sequencer chain ID not set") + } + + if fork.Sequencer.StartHeight == 0 { return nil, errors.New("sequencer initial height not set") } - if bc.Config().AstriaCelestiaInitialHeight == 0 { - return nil, errors.New("celestia initial height not set") + if fork.Celestia.ChainID == "" { + return nil, errors.New("celestia chain ID not set") } - if bc.Config().AstriaCelestiaHeightVariance == 0 { - return nil, errors.New("celestia height variance not set") + if fork.Celestia.StartHeight == 0 { + return nil, errors.New("celestia initial height not set") } - bridgeAddresses := make(map[string]*params.AstriaBridgeAddressConfig) - bridgeAllowedAssets := make(map[string]struct{}) - if bc.Config().AstriaBridgeAddressConfigs == nil { - log.Warn("bridge addresses not set") - } else { - nativeBridgeSeen := false - for _, cfg := range bc.Config().AstriaBridgeAddressConfigs { - err := cfg.Validate(bc.Config().AstriaSequencerAddressPrefix) - if err != nil { - return nil, fmt.Errorf("invalid bridge address config: %w", err) - } - - if cfg.Erc20Asset == nil { - if nativeBridgeSeen { - return nil, errors.New("only one native bridge address is allowed") - } - nativeBridgeSeen = true - } - - if cfg.Erc20Asset != nil && cfg.SenderAddress == (common.Address{}) { - return nil, errors.New("astria bridge sender address must be set for bridged ERC20 assets") - } - - bridgeCfg := cfg - bridgeAddresses[cfg.BridgeAddress] = &bridgeCfg - bridgeAllowedAssets[cfg.AssetDenom] = struct{}{} - if cfg.Erc20Asset == nil { - log.Info("bridge for sequencer native asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom) - } else { - log.Info("bridge for ERC20 asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom, "contractAddress", cfg.Erc20Asset.ContractAddress) - } - } + if fork.Celestia.HeightVariance == 0 { + return nil, errors.New("celestia height variance not set") } - // To decrease compute cost, we identify the next fee recipient at the start - // and update it as we execute blocks. - nextFeeRecipient := common.Address{} - if bc.Config().AstriaFeeCollectors == nil { + if fork.FeeCollector == (common.Address{}) { log.Warn("fee asset collectors not set, assets will be burned") - } else { - maxHeightCollectorMatch := uint32(0) - nextBlock := uint32(bc.CurrentBlock().Number.Int64()) + 1 - for height, collector := range bc.Config().AstriaFeeCollectors { - if height <= nextBlock && height > maxHeightCollectorMatch { - maxHeightCollectorMatch = height - nextFeeRecipient = collector - } - } } return &ExecutionServiceServerV1{ - eth: eth, - bc: bc, - bridgeAddresses: bridgeAddresses, - bridgeAllowedAssets: bridgeAllowedAssets, - nextFeeRecipient: nextFeeRecipient, + eth: eth, + bc: bc, }, nil } @@ -159,10 +114,15 @@ func (s *ExecutionServiceServerV1) GetGenesisInfo(ctx context.Context, req *astr rollupHash := sha256.Sum256([]byte(s.bc.Config().AstriaRollupName)) rollupId := primitivev1.RollupId{Inner: rollupHash[:]} + fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(s.bc.CurrentBlock().Number.Uint64()) + res := &astriaPb.GenesisInfo{ - RollupId: &rollupId, - SequencerGenesisBlockHeight: s.bc.Config().AstriaSequencerInitialHeight, - CelestiaBlockVariance: s.bc.Config().AstriaCelestiaHeightVariance, + RollupId: &rollupId, + SequencerChainId: fork.Sequencer.ChainID, + SequencerStartBlockHeight: fork.Sequencer.StartHeight, + SequencerStopBlockHeight: fork.Sequencer.StopHeight, + CelestiaChainId: fork.Celestia.ChainID, + CelestiaBlockVariance: fork.Celestia.HeightVariance, } log.Info("GetGenesisInfo completed", "response", res) @@ -242,6 +202,7 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria s.blockExecutionLock.Lock() defer s.blockExecutionLock.Unlock() + // Deliberately called after lock, to more directly measure the time spent executing executionStart := time.Now() defer executeBlockTimer.UpdateSince(executionStart) @@ -250,6 +211,16 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria return nil, status.Error(codes.PermissionDenied, "Cannot execute block until GetGenesisInfo && GetCommitmentState methods are called") } + // the height that this block will be at + height := s.bc.CurrentBlock().Number.Uint64() + 1 + + fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(height) + + // this fork halts the chain + if fork.Halt { + return nil, status.Error(codes.FailedPrecondition, "Block cannot be created at halted fork") + } + // Validate block being created has valid previous hash prevHeadHash := common.BytesToHash(req.PrevBlockHash) softHash := s.bc.CurrentSafeBlock().Hash() @@ -257,12 +228,9 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria return nil, status.Error(codes.FailedPrecondition, "Block can only be created on top of soft block.") } - // the height that this block will be at - height := s.bc.CurrentBlock().Number.Uint64() + 1 - txsToProcess := types.Transactions{} for _, tx := range req.Transactions { - unmarshalledTx, err := validateAndUnmarshalSequencerTx(height, tx, s.bridgeAddresses, s.bridgeAllowedAssets) + unmarshalledTx, err := validateAndUnmarshalSequencerTx(height, tx, fork.BridgeAddresses, fork.BridgeAllowedAssets) if err != nil { log.Debug("failed to validate sequencer tx, ignoring", "tx", tx, "err", err) continue @@ -279,7 +247,7 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria Parent: prevHeadHash, Timestamp: uint64(req.GetTimestamp().GetSeconds()), Random: common.Hash{}, - FeeRecipient: s.nextFeeRecipient, + FeeRecipient: s.bc.Config().GetAstriaForks().GetForkAtHeight(height).FeeCollector, } payload, err := s.eth.Miner().BuildPayload(payloadAttributes) if err != nil { @@ -312,10 +280,6 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria }, } - if next, ok := s.bc.Config().AstriaFeeCollectors[res.Number+1]; ok { - s.nextFeeRecipient = next - } - log.Info("ExecuteBlock completed", "block_num", res.Number, "timestamp", res.Timestamp) totalExecutedTxCount.Inc(int64(len(block.Transactions()))) executeBlockSuccessCount.Inc(1) @@ -340,6 +304,11 @@ func (s *ExecutionServiceServerV1) GetCommitmentState(ctx context.Context, req * celestiaBlock := s.bc.CurrentBaseCelestiaHeight() + fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(uint64(softBlock.Number)) + if fork.Height == uint64(softBlock.Number) && fork.Celestia.StartHeight > celestiaBlock { + celestiaBlock = fork.Celestia.StartHeight + } + res := &astriaPb.CommitmentState{ Soft: softBlock, Firm: firmBlock, diff --git a/params/config.go b/params/config.go index d9b40b881..3b12ec156 100644 --- a/params/config.go +++ b/params/config.go @@ -18,6 +18,7 @@ package params import ( "encoding/json" + "errors" "fmt" "math/big" "sort" @@ -378,29 +379,68 @@ type ChainConfig struct { IsDevMode bool `json:"isDev,omitempty"` // Astria Specific Configuration - AstriaOverrideGenesisExtraData bool `json:"astriaOverrideGenesisExtraData,omitempty"` - AstriaExtraDataOverride hexutil.Bytes `json:"astriaExtraDataOverride,omitempty"` - AstriaRollupName string `json:"astriaRollupName"` - AstriaSequencerInitialHeight uint32 `json:"astriaSequencerInitialHeight"` - AstriaSequencerAddressPrefix string `json:"astriaSequencerAddressPrefix,omitempty"` - AstriaCelestiaInitialHeight uint64 `json:"astriaCelestiaInitialHeight"` - AstriaCelestiaHeightVariance uint64 `json:"astriaCelestiaHeightVariance,omitempty"` - AstriaBridgeAddressConfigs []AstriaBridgeAddressConfig `json:"astriaBridgeAddresses,omitempty"` - AstriaFeeCollectors map[uint32]common.Address `json:"astriaFeeCollectors"` - AstriaEIP1559Params *AstriaEIP1559Params `json:"astriaEIP1559Params,omitempty"` + AstriaOverrideGenesisExtraData bool `json:"astriaOverrideGenesisExtraData,omitempty"` + AstriaRollupName string `json:"astriaRollupName"` + AstriaForks *AstriaForks `json:"astriaForks,omitempty"` } -func (c *ChainConfig) AstriaExtraData() []byte { - if c.AstriaExtraDataOverride != nil { - return c.AstriaExtraDataOverride +type AstriaForkConfig struct { + Height uint64 `json:"height"` + Halt bool `json:"halt,omitempty"` + SnapshotChecksum string `json:"snapshotChecksum,omitempty"` + ExtraDataOverride hexutil.Bytes `json:"extraDataOverride,omitempty"` + FeeCollector *common.Address `json:"feeCollector,omitempty"` + EIP1559Params *AstriaEIP1559Param `json:"eip1559Params,omitempty"` + Sequencer *AstriaSequencerConfig `json:"sequencer,omitempty"` + Celestia *AstriaCelestiaConfig `json:"celestia,omitempty"` + BridgeAddresses []AstriaBridgeAddressConfig `json:"bridgeAddresses,omitempty"` +} + +type AstriaForkData struct { + Height uint64 + Halt bool + SnapshotChecksum string + ExtraDataOverride hexutil.Bytes + FeeCollector common.Address + EIP1559Params AstriaEIP1559Param + Sequencer AstriaSequencerConfig + Celestia AstriaCelestiaConfig + BridgeAddresses map[string]*AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account + BridgeAllowedAssets map[string]struct{} // a set of allowed asset IDs structs are left empty +} + +type AstriaSequencerConfig struct { + ChainID string `json:"chainId"` + AddressPrefix string `json:"addressPrefix,omitempty"` + StartHeight uint32 `json:"startHeight"` + StopHeight uint32 `json:"-"` + RollupStartHeight uint64 `json:"-"` +} + +type AstriaCelestiaConfig struct { + ChainID string `json:"chainId"` + StartHeight uint64 `json:"startHeight"` + HeightVariance uint64 `json:"heightVariance"` +} + +type AstriaEIP1559Param struct { + MinBaseFee uint64 `json:"minBaseFee"` + ElasticityMultiplier uint64 `json:"elasticityMultiplier"` + BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` +} + +func (c *ChainConfig) AstriaExtraData(height uint64) []byte { + fork := c.GetAstriaForks().GetForkAtHeight(height) + if fork.ExtraDataOverride != nil { + return fork.ExtraDataOverride } // create default extradata extra, _ := rlp.EncodeToBytes([]interface{}{ c.AstriaRollupName, - c.AstriaSequencerInitialHeight, - c.AstriaCelestiaInitialHeight, - c.AstriaCelestiaHeightVariance, + fork.Sequencer.StartHeight, + fork.Celestia.StartHeight, + fork.Celestia.HeightVariance, }) if uint64(len(extra)) > MaximumExtraDataSize { log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", MaximumExtraDataSize) @@ -409,70 +449,192 @@ func (c *ChainConfig) AstriaExtraData() []byte { return extra } -type AstriaEIP1559Param struct { - MinBaseFee uint64 `json:"minBaseFee"` - ElasticityMultiplier uint64 `json:"elasticityMultiplier"` - BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` +type AstriaForks struct { + orderedForks []AstriaForkData + forkMap map[string]AstriaForkConfig } -type AstriaEIP1559Params struct { - heights map[uint64]AstriaEIP1559Param - orderedHeights []uint64 -} +func NewAstriaForks(forks map[string]AstriaForkConfig) (*AstriaForks, error) { + if forks == nil { + return &AstriaForks{ + orderedForks: []AstriaForkData{}, + forkMap: make(map[string]AstriaForkConfig), + }, nil + } -func NewAstriaEIP1559Params(heights map[uint64]AstriaEIP1559Param) *AstriaEIP1559Params { - orderedHeights := []uint64{} - for k := range heights { - orderedHeights = append(orderedHeights, k) + // Create sorted array of fork names and heights + type nameHeight struct { + name string + height uint64 + } + sortedNames := make([]nameHeight, 0, len(forks)) + for name, fork := range forks { + sortedNames = append(sortedNames, nameHeight{name, fork.Height}) } - sort.Slice(orderedHeights, func(i, j int) bool { return orderedHeights[i] > orderedHeights[j] }) + sort.Slice(sortedNames, func(i, j int) bool { + return sortedNames[i].height < sortedNames[j].height + }) + + nativeBridgeSeen := false + orderedForks := make([]AstriaForkData, len(sortedNames)) + + for i, nh := range sortedNames { + currentFork := forks[nh.name] - return &AstriaEIP1559Params{ - heights: heights, - orderedHeights: orderedHeights, + if i > 0 { + // Copy previous fork's configuration as the base + orderedForks[i] = orderedForks[i-1] + } else { + // set default values + orderedForks[i] = GetDefaultAstriaForkData() + } + + // Set fork-specific fields + orderedForks[i].Height = currentFork.Height + orderedForks[i].Halt = currentFork.Halt + orderedForks[i].SnapshotChecksum = "" + + // Override with any new values from current fork + if currentFork.SnapshotChecksum != "" { + orderedForks[i].SnapshotChecksum = currentFork.SnapshotChecksum + } + + if currentFork.ExtraDataOverride != nil { + orderedForks[i].ExtraDataOverride = currentFork.ExtraDataOverride + } + + if currentFork.FeeCollector != nil { + orderedForks[i].FeeCollector = *currentFork.FeeCollector + } + + if currentFork.EIP1559Params != nil { + orderedForks[i].EIP1559Params = *currentFork.EIP1559Params + } + + if currentFork.Sequencer != nil { + orderedForks[i].Sequencer = *currentFork.Sequencer + orderedForks[i].Sequencer.RollupStartHeight = currentFork.Height + // set stop height for previous fork if sequencer data is changed + if i > 0 { + orderedForks[i-1].Sequencer.StopHeight = orderedForks[i-1].Sequencer.StartHeight + uint32(currentFork.Height-orderedForks[i-1].Sequencer.RollupStartHeight) + } + } + + if currentFork.Celestia != nil { + orderedForks[i].Celestia = *currentFork.Celestia + } + + if len(currentFork.BridgeAddresses) > 0 { + for _, cfg := range currentFork.BridgeAddresses { + err := cfg.Validate(orderedForks[i].Sequencer.AddressPrefix) + if err != nil { + return nil, fmt.Errorf("invalid bridge address config: %w", err) + } + + if cfg.Erc20Asset == nil { + if nativeBridgeSeen { + return nil, errors.New("only one native bridge address is allowed") + } + nativeBridgeSeen = true + } + + if cfg.Erc20Asset != nil && cfg.SenderAddress == (common.Address{}) { + return nil, errors.New("astria bridge sender address must be set for bridged ERC20 assets") + } + + bridgeCfg := cfg + orderedForks[i].BridgeAddresses[cfg.BridgeAddress] = &bridgeCfg + orderedForks[i].BridgeAllowedAssets[cfg.AssetDenom] = struct{}{} + if cfg.Erc20Asset == nil { + log.Info("bridge for sequencer native asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom) + } else { + log.Info("bridge for ERC20 asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom, "contractAddress", cfg.Erc20Asset.ContractAddress) + } + } + } } + + return &AstriaForks{ + orderedForks: orderedForks, + forkMap: forks, + }, nil } -func (c *AstriaEIP1559Params) MinBaseFeeAt(height uint64) *big.Int { - for _, h := range c.orderedHeights { - if height >= h { - return big.NewInt(0).SetUint64(c.heights[h].MinBaseFee) - } +func GetDefaultAstriaForkData() AstriaForkData { + return AstriaForkData{ + Height: 1, + FeeCollector: common.Address{}, + EIP1559Params: AstriaEIP1559Param{ + MinBaseFee: 0, + ElasticityMultiplier: DefaultElasticityMultiplier, + BaseFeeChangeDenominator: DefaultBaseFeeChangeDenominator, + }, + BridgeAddresses: make(map[string]*AstriaBridgeAddressConfig), + BridgeAllowedAssets: make(map[string]struct{}), } - return common.Big0 } -func (c *AstriaEIP1559Params) ElasticityMultiplierAt(height uint64) uint64 { - for _, h := range c.orderedHeights { - if height >= h { - return c.heights[h].ElasticityMultiplier - } +func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { + if len(c.orderedForks) == 0 { + return GetDefaultAstriaForkData() + } + + if height < c.orderedForks[0].Height { + return GetDefaultAstriaForkData() } - return DefaultElasticityMultiplier + + idx := sort.Search(len(c.orderedForks), func(i int) bool { + return c.orderedForks[i].Height > height + }) - 1 + + if idx < 0 { + return GetDefaultAstriaForkData() + } + + return c.orderedForks[idx] } -func (c *AstriaEIP1559Params) BaseFeeChangeDenominatorAt(height uint64) uint64 { - for _, h := range c.orderedHeights { - if height >= h { - return c.heights[h].BaseFeeChangeDenominator - } +func (c *AstriaForks) GetNextForkAtHeight(height uint64) *AstriaForkData { + idx := sort.Search(len(c.orderedForks), func(i int) bool { + return c.orderedForks[i].Height > height + }) + if idx < 0 { + return nil } - return DefaultBaseFeeChangeDenominator + return &c.orderedForks[idx] +} + +func (c *AstriaForks) MinBaseFeeAt(height uint64) *big.Int { + return big.NewInt(0).SetUint64(c.GetForkAtHeight(height).EIP1559Params.MinBaseFee) } -func (c AstriaEIP1559Params) MarshalJSON() ([]byte, error) { - return json.Marshal(c.heights) +func (c *AstriaForks) MarshalJSON() ([]byte, error) { + return json.Marshal(c.forkMap) } -func (c *AstriaEIP1559Params) UnmarshalJSON(data []byte) error { - var heights map[uint64]AstriaEIP1559Param - if err := json.Unmarshal(data, &heights); err != nil { +func (c *AstriaForks) UnmarshalJSON(data []byte) error { + var forkMap map[string]AstriaForkConfig + if err := json.Unmarshal(data, &forkMap); err != nil { + return err + } + + newForks, err := NewAstriaForks(forkMap) + if err != nil { return err } - *c = *NewAstriaEIP1559Params(heights) + + *c = *newForks return nil } +func (c *ChainConfig) GetAstriaForks() *AstriaForks { + if c.AstriaForks == nil { + forks, _ := NewAstriaForks(nil) + return forks + } + return c.AstriaForks +} + // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} @@ -850,18 +1012,12 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, // BaseFeeChangeDenominator bounds the amount the base fee can change between blocks. func (c *ChainConfig) BaseFeeChangeDenominator(height uint64) uint64 { - if c.AstriaEIP1559Params != nil { - return c.AstriaEIP1559Params.BaseFeeChangeDenominatorAt(height) - } - return DefaultBaseFeeChangeDenominator + return c.GetAstriaForks().GetForkAtHeight(height).EIP1559Params.BaseFeeChangeDenominator } // ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. func (c *ChainConfig) ElasticityMultiplier(height uint64) uint64 { - if c.AstriaEIP1559Params != nil { - return c.AstriaEIP1559Params.ElasticityMultiplierAt(height) - } - return DefaultElasticityMultiplier + return c.GetAstriaForks().GetForkAtHeight(height).EIP1559Params.ElasticityMultiplier } // LatestFork returns the latest time-based fork that would be active for the given time. From a63cd800f2d3924ebbfed3b16446d9572543e351 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Wed, 4 Dec 2024 22:27:25 -0500 Subject: [PATCH 2/8] fix logic for BaseCelestiaHeight changing at forks --- grpc/execution/server.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 37570c667..aca3920a0 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -305,14 +305,11 @@ func (s *ExecutionServiceServerV1) GetCommitmentState(ctx context.Context, req * celestiaBlock := s.bc.CurrentBaseCelestiaHeight() fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(uint64(softBlock.Number)) - if fork.Height == uint64(softBlock.Number) && fork.Celestia.StartHeight > celestiaBlock { - celestiaBlock = fork.Celestia.StartHeight - } res := &astriaPb.CommitmentState{ Soft: softBlock, Firm: firmBlock, - BaseCelestiaHeight: celestiaBlock, + BaseCelestiaHeight: max(celestiaBlock, fork.Celestia.StartHeight), } log.Info("GetCommitmentState completed", "soft_height", res.Soft.Number, "firm_height", res.Firm.Number, "base_celestia_height", res.BaseCelestiaHeight) From a85e7ee4a14c9aa535fe769d19656ae5b6b0d7d2 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Wed, 4 Dec 2024 22:27:48 -0500 Subject: [PATCH 3/8] move astria config to separate file, add some validation --- params/astria_config.go | 363 ++++++++++++++++++++++++++++++++++++++++ params/config.go | 329 ++---------------------------------- 2 files changed, 376 insertions(+), 316 deletions(-) create mode 100644 params/astria_config.go diff --git a/params/astria_config.go b/params/astria_config.go new file mode 100644 index 000000000..6980ebf2a --- /dev/null +++ b/params/astria_config.go @@ -0,0 +1,363 @@ +package params + +import ( + "encoding/json" + "errors" + "fmt" + "math/big" + "sort" + + "github.com/btcsuite/btcd/btcutil/bech32" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rlp" +) + +type AstriaForks struct { + orderedForks []AstriaForkData + forkMap map[string]AstriaForkConfig +} + +type AstriaForkConfig struct { + Height uint64 `json:"height"` + Halt bool `json:"halt,omitempty"` + SnapshotChecksum string `json:"snapshotChecksum,omitempty"` + ExtraDataOverride hexutil.Bytes `json:"extraDataOverride,omitempty"` + FeeCollector *common.Address `json:"feeCollector,omitempty"` + EIP1559Params *AstriaEIP1559Param `json:"eip1559Params,omitempty"` + Sequencer *AstriaSequencerConfig `json:"sequencer,omitempty"` + Celestia *AstriaCelestiaConfig `json:"celestia,omitempty"` + BridgeAddresses []AstriaBridgeAddressConfig `json:"bridgeAddresses,omitempty"` +} + +type AstriaForkData struct { + Name string + Height uint64 + Halt bool + SnapshotChecksum string + ExtraDataOverride hexutil.Bytes + FeeCollector common.Address + EIP1559Params AstriaEIP1559Param + Sequencer AstriaSequencerConfig + Celestia AstriaCelestiaConfig + BridgeAddresses map[string]*AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account + BridgeAllowedAssets map[string]struct{} // a set of allowed asset IDs structs are left empty +} + +type AstriaSequencerConfig struct { + ChainID string `json:"chainId"` + AddressPrefix string `json:"addressPrefix,omitempty"` + StartHeight uint32 `json:"startHeight"` + StopHeight uint32 `json:"-"` + RollupStartHeight uint64 `json:"-"` +} + +type AstriaCelestiaConfig struct { + ChainID string `json:"chainId"` + StartHeight uint64 `json:"startHeight"` + HeightVariance uint64 `json:"heightVariance"` +} + +type AstriaEIP1559Param struct { + MinBaseFee uint64 `json:"minBaseFee"` + ElasticityMultiplier uint64 `json:"elasticityMultiplier"` + BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` +} + +func (c *ChainConfig) AstriaExtraData(height uint64) []byte { + fork := c.GetAstriaForks().GetForkAtHeight(height) + if fork.ExtraDataOverride != nil { + return fork.ExtraDataOverride + } + + // create default extradata + extra, _ := rlp.EncodeToBytes([]interface{}{ + c.AstriaRollupName, + fork.Sequencer.StartHeight, + fork.Celestia.StartHeight, + fork.Celestia.HeightVariance, + }) + if uint64(len(extra)) > MaximumExtraDataSize { + log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", MaximumExtraDataSize) + extra = nil + } + return extra +} + +func NewAstriaForks(forks map[string]AstriaForkConfig) (*AstriaForks, error) { + if forks == nil { + return &AstriaForks{ + orderedForks: []AstriaForkData{}, + forkMap: make(map[string]AstriaForkConfig), + }, nil + } + + // Create sorted array of fork names and heights + type nameHeight struct { + name string + height uint64 + } + sortedNames := make([]nameHeight, 0, len(forks)) + for name, fork := range forks { + sortedNames = append(sortedNames, nameHeight{name, fork.Height}) + } + sort.Slice(sortedNames, func(i, j int) bool { + return sortedNames[i].height < sortedNames[j].height + }) + + nativeBridgeSeen := false + orderedForks := make([]AstriaForkData, len(sortedNames)) + + for i, nh := range sortedNames { + currentFork := forks[nh.name] + + if i > 0 { + // Copy previous fork's configuration as the base + orderedForks[i] = orderedForks[i-1] + } else { + // set default values + orderedForks[i] = GetDefaultAstriaForkData() + } + + // Set fork-specific fields + orderedForks[i].Name = nh.name + orderedForks[i].Height = currentFork.Height + orderedForks[i].Halt = currentFork.Halt + orderedForks[i].SnapshotChecksum = "" + + // Override with any new values from current fork + if currentFork.SnapshotChecksum != "" { + orderedForks[i].SnapshotChecksum = currentFork.SnapshotChecksum + } + + if currentFork.ExtraDataOverride != nil { + orderedForks[i].ExtraDataOverride = currentFork.ExtraDataOverride + } + + if currentFork.FeeCollector != nil { + orderedForks[i].FeeCollector = *currentFork.FeeCollector + } + + if currentFork.EIP1559Params != nil { + orderedForks[i].EIP1559Params = *currentFork.EIP1559Params + } + + if currentFork.Sequencer != nil { + orderedForks[i].Sequencer = *currentFork.Sequencer + orderedForks[i].Sequencer.RollupStartHeight = currentFork.Height + // set stop height for previous fork if sequencer data is changed + if i > 0 { + orderedForks[i-1].Sequencer.StopHeight = orderedForks[i-1].Sequencer.StartHeight + uint32(currentFork.Height-orderedForks[i-1].Sequencer.RollupStartHeight) + } + } + + if currentFork.Celestia != nil { + orderedForks[i].Celestia = *currentFork.Celestia + } + + if len(currentFork.BridgeAddresses) > 0 { + for _, cfg := range currentFork.BridgeAddresses { + err := cfg.Validate(orderedForks[i].Sequencer.AddressPrefix) + if err != nil { + return nil, fmt.Errorf("invalid bridge address config: %w", err) + } + + if cfg.Erc20Asset == nil { + if nativeBridgeSeen { + return nil, errors.New("only one native bridge address is allowed") + } + nativeBridgeSeen = true + } + + if cfg.Erc20Asset != nil && cfg.SenderAddress == (common.Address{}) { + return nil, errors.New("astria bridge sender address must be set for bridged ERC20 assets") + } + + bridgeCfg := cfg + orderedForks[i].BridgeAddresses[cfg.BridgeAddress] = &bridgeCfg + orderedForks[i].BridgeAllowedAssets[cfg.AssetDenom] = struct{}{} + if cfg.Erc20Asset == nil { + log.Info("bridge for sequencer native asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom) + } else { + log.Info("bridge for ERC20 asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom, "contractAddress", cfg.Erc20Asset.ContractAddress) + } + } + } + } + + if err := validateAstriaForks(orderedForks); err != nil { + return nil, err + } + + return &AstriaForks{ + orderedForks: orderedForks, + forkMap: forks, + }, nil +} + +func validateAstriaForks(forks []AstriaForkData) error { + for _, fork := range forks { + if !fork.Halt { + if fork.Sequencer.ChainID == "" { + return fmt.Errorf("fork %s: sequencer chain ID not set", fork.Name) + } + + if fork.Sequencer.StartHeight == 0 { + return fmt.Errorf("fork %s: sequencer initial height not set", fork.Name) + } + + if fork.Celestia.ChainID == "" { + return fmt.Errorf("fork %s: celestia chain ID not set", fork.Name) + } + + if fork.Celestia.StartHeight == 0 { + return fmt.Errorf("fork %s: celestia initial height not set", fork.Name) + } + + if fork.Celestia.HeightVariance == 0 { + return fmt.Errorf("fork %s: celestia height variance not set", fork.Name) + } + + if fork.FeeCollector == (common.Address{}) { + log.Warn("fee asset collectors not set, assets will be burned", "fork", fork.Name) + } + } else { + log.Warn("fork will halt", "fork", fork.Name, "height", fork.Height) + } + } + + return nil +} + +func GetDefaultAstriaForkData() AstriaForkData { + return AstriaForkData{ + Height: 1, + FeeCollector: common.Address{}, + EIP1559Params: AstriaEIP1559Param{ + MinBaseFee: 0, + ElasticityMultiplier: DefaultElasticityMultiplier, + BaseFeeChangeDenominator: DefaultBaseFeeChangeDenominator, + }, + BridgeAddresses: make(map[string]*AstriaBridgeAddressConfig), + BridgeAllowedAssets: make(map[string]struct{}), + } +} + +func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { + if len(c.orderedForks) == 0 { + return GetDefaultAstriaForkData() + } + + if height < c.orderedForks[0].Height { + return GetDefaultAstriaForkData() + } + + idx := sort.Search(len(c.orderedForks), func(i int) bool { + return c.orderedForks[i].Height > height + }) - 1 + + if idx < 0 { + return GetDefaultAstriaForkData() + } + + return c.orderedForks[idx] +} + +func (c *AstriaForks) GetNextForkAtHeight(height uint64) *AstriaForkData { + idx := sort.Search(len(c.orderedForks), func(i int) bool { + return c.orderedForks[i].Height > height + }) + if idx < 0 { + return nil + } + return &c.orderedForks[idx] +} + +func (c *AstriaForks) MinBaseFeeAt(height uint64) *big.Int { + return big.NewInt(0).SetUint64(c.GetForkAtHeight(height).EIP1559Params.MinBaseFee) +} + +func (c *AstriaForks) MarshalJSON() ([]byte, error) { + return json.Marshal(c.forkMap) +} + +func (c *AstriaForks) UnmarshalJSON(data []byte) error { + var forkMap map[string]AstriaForkConfig + if err := json.Unmarshal(data, &forkMap); err != nil { + return err + } + + newForks, err := NewAstriaForks(forkMap) + if err != nil { + return err + } + + *c = *newForks + return nil +} + +func (c *ChainConfig) GetAstriaForks() *AstriaForks { + if c.AstriaForks == nil { + forks, _ := NewAstriaForks(nil) + return forks + } + return c.AstriaForks +} + +type AstriaBridgeAddressConfig struct { + BridgeAddress string `json:"bridgeAddress"` + SenderAddress common.Address `json:"senderAddress,omitempty"` + StartHeight uint32 `json:"startHeight"` + AssetDenom string `json:"assetDenom"` + AssetPrecision uint16 `json:"assetPrecision"` + Erc20Asset *AstriaErc20AssetConfig `json:"erc20Asset,omitempty"` +} + +type AstriaErc20AssetConfig struct { + ContractAddress common.Address `json:"contractAddress"` + ContractPrecision uint16 `json:"contractPrecision"` +} + +func (abc *AstriaBridgeAddressConfig) Validate(genesisPrefix string) error { + prefix, byteAddress, err := bech32.Decode(abc.BridgeAddress) + if err != nil { + return fmt.Errorf("bridge address must be a bech32 encoded string") + } + byteAddress, err = bech32.ConvertBits(byteAddress, 5, 8, false) + if err != nil { + return fmt.Errorf("failed to convert address to 8 bit") + } + if prefix != genesisPrefix { + return fmt.Errorf("bridge address must have prefix %s", genesisPrefix) + } + if len(byteAddress) != 20 { + return fmt.Errorf("bridge address must have resolve to 20 byte address, got %d", len(byteAddress)) + } + if abc.StartHeight == 0 { + return fmt.Errorf("start height must be greater than 0") + } + if abc.AssetDenom == "" { + return fmt.Errorf("asset denom must be set") + } + if abc.Erc20Asset == nil && abc.AssetPrecision > 18 { + return fmt.Errorf("asset precision of native asset must be less than or equal to 18") + } + if abc.Erc20Asset != nil && abc.AssetPrecision > abc.Erc20Asset.ContractPrecision { + return fmt.Errorf("asset precision must be less than or equal to contract precision") + } + + return nil +} + +func (abc *AstriaBridgeAddressConfig) ScaledDepositAmount(deposit *big.Int) *big.Int { + var exponent uint16 + if abc.Erc20Asset != nil { + exponent = abc.Erc20Asset.ContractPrecision - abc.AssetPrecision + } else { + exponent = 18 - abc.AssetPrecision + } + multiplier := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(exponent)), nil) + + return new(big.Int).Mul(deposit, multiplier) +} diff --git a/params/config.go b/params/config.go index 3b12ec156..a1fefe7ba 100644 --- a/params/config.go +++ b/params/config.go @@ -17,19 +17,11 @@ package params import ( - "encoding/json" - "errors" "fmt" "math/big" - "sort" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params/forks" - "github.com/ethereum/go-ethereum/rlp" - - "github.com/btcsuite/btcd/btcutil/bech32" ) // Genesis hashes to enforce below configs on. @@ -384,257 +376,6 @@ type ChainConfig struct { AstriaForks *AstriaForks `json:"astriaForks,omitempty"` } -type AstriaForkConfig struct { - Height uint64 `json:"height"` - Halt bool `json:"halt,omitempty"` - SnapshotChecksum string `json:"snapshotChecksum,omitempty"` - ExtraDataOverride hexutil.Bytes `json:"extraDataOverride,omitempty"` - FeeCollector *common.Address `json:"feeCollector,omitempty"` - EIP1559Params *AstriaEIP1559Param `json:"eip1559Params,omitempty"` - Sequencer *AstriaSequencerConfig `json:"sequencer,omitempty"` - Celestia *AstriaCelestiaConfig `json:"celestia,omitempty"` - BridgeAddresses []AstriaBridgeAddressConfig `json:"bridgeAddresses,omitempty"` -} - -type AstriaForkData struct { - Height uint64 - Halt bool - SnapshotChecksum string - ExtraDataOverride hexutil.Bytes - FeeCollector common.Address - EIP1559Params AstriaEIP1559Param - Sequencer AstriaSequencerConfig - Celestia AstriaCelestiaConfig - BridgeAddresses map[string]*AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account - BridgeAllowedAssets map[string]struct{} // a set of allowed asset IDs structs are left empty -} - -type AstriaSequencerConfig struct { - ChainID string `json:"chainId"` - AddressPrefix string `json:"addressPrefix,omitempty"` - StartHeight uint32 `json:"startHeight"` - StopHeight uint32 `json:"-"` - RollupStartHeight uint64 `json:"-"` -} - -type AstriaCelestiaConfig struct { - ChainID string `json:"chainId"` - StartHeight uint64 `json:"startHeight"` - HeightVariance uint64 `json:"heightVariance"` -} - -type AstriaEIP1559Param struct { - MinBaseFee uint64 `json:"minBaseFee"` - ElasticityMultiplier uint64 `json:"elasticityMultiplier"` - BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` -} - -func (c *ChainConfig) AstriaExtraData(height uint64) []byte { - fork := c.GetAstriaForks().GetForkAtHeight(height) - if fork.ExtraDataOverride != nil { - return fork.ExtraDataOverride - } - - // create default extradata - extra, _ := rlp.EncodeToBytes([]interface{}{ - c.AstriaRollupName, - fork.Sequencer.StartHeight, - fork.Celestia.StartHeight, - fork.Celestia.HeightVariance, - }) - if uint64(len(extra)) > MaximumExtraDataSize { - log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", MaximumExtraDataSize) - extra = nil - } - return extra -} - -type AstriaForks struct { - orderedForks []AstriaForkData - forkMap map[string]AstriaForkConfig -} - -func NewAstriaForks(forks map[string]AstriaForkConfig) (*AstriaForks, error) { - if forks == nil { - return &AstriaForks{ - orderedForks: []AstriaForkData{}, - forkMap: make(map[string]AstriaForkConfig), - }, nil - } - - // Create sorted array of fork names and heights - type nameHeight struct { - name string - height uint64 - } - sortedNames := make([]nameHeight, 0, len(forks)) - for name, fork := range forks { - sortedNames = append(sortedNames, nameHeight{name, fork.Height}) - } - sort.Slice(sortedNames, func(i, j int) bool { - return sortedNames[i].height < sortedNames[j].height - }) - - nativeBridgeSeen := false - orderedForks := make([]AstriaForkData, len(sortedNames)) - - for i, nh := range sortedNames { - currentFork := forks[nh.name] - - if i > 0 { - // Copy previous fork's configuration as the base - orderedForks[i] = orderedForks[i-1] - } else { - // set default values - orderedForks[i] = GetDefaultAstriaForkData() - } - - // Set fork-specific fields - orderedForks[i].Height = currentFork.Height - orderedForks[i].Halt = currentFork.Halt - orderedForks[i].SnapshotChecksum = "" - - // Override with any new values from current fork - if currentFork.SnapshotChecksum != "" { - orderedForks[i].SnapshotChecksum = currentFork.SnapshotChecksum - } - - if currentFork.ExtraDataOverride != nil { - orderedForks[i].ExtraDataOverride = currentFork.ExtraDataOverride - } - - if currentFork.FeeCollector != nil { - orderedForks[i].FeeCollector = *currentFork.FeeCollector - } - - if currentFork.EIP1559Params != nil { - orderedForks[i].EIP1559Params = *currentFork.EIP1559Params - } - - if currentFork.Sequencer != nil { - orderedForks[i].Sequencer = *currentFork.Sequencer - orderedForks[i].Sequencer.RollupStartHeight = currentFork.Height - // set stop height for previous fork if sequencer data is changed - if i > 0 { - orderedForks[i-1].Sequencer.StopHeight = orderedForks[i-1].Sequencer.StartHeight + uint32(currentFork.Height-orderedForks[i-1].Sequencer.RollupStartHeight) - } - } - - if currentFork.Celestia != nil { - orderedForks[i].Celestia = *currentFork.Celestia - } - - if len(currentFork.BridgeAddresses) > 0 { - for _, cfg := range currentFork.BridgeAddresses { - err := cfg.Validate(orderedForks[i].Sequencer.AddressPrefix) - if err != nil { - return nil, fmt.Errorf("invalid bridge address config: %w", err) - } - - if cfg.Erc20Asset == nil { - if nativeBridgeSeen { - return nil, errors.New("only one native bridge address is allowed") - } - nativeBridgeSeen = true - } - - if cfg.Erc20Asset != nil && cfg.SenderAddress == (common.Address{}) { - return nil, errors.New("astria bridge sender address must be set for bridged ERC20 assets") - } - - bridgeCfg := cfg - orderedForks[i].BridgeAddresses[cfg.BridgeAddress] = &bridgeCfg - orderedForks[i].BridgeAllowedAssets[cfg.AssetDenom] = struct{}{} - if cfg.Erc20Asset == nil { - log.Info("bridge for sequencer native asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom) - } else { - log.Info("bridge for ERC20 asset initialized", "bridgeAddress", cfg.BridgeAddress, "assetDenom", cfg.AssetDenom, "contractAddress", cfg.Erc20Asset.ContractAddress) - } - } - } - } - - return &AstriaForks{ - orderedForks: orderedForks, - forkMap: forks, - }, nil -} - -func GetDefaultAstriaForkData() AstriaForkData { - return AstriaForkData{ - Height: 1, - FeeCollector: common.Address{}, - EIP1559Params: AstriaEIP1559Param{ - MinBaseFee: 0, - ElasticityMultiplier: DefaultElasticityMultiplier, - BaseFeeChangeDenominator: DefaultBaseFeeChangeDenominator, - }, - BridgeAddresses: make(map[string]*AstriaBridgeAddressConfig), - BridgeAllowedAssets: make(map[string]struct{}), - } -} - -func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { - if len(c.orderedForks) == 0 { - return GetDefaultAstriaForkData() - } - - if height < c.orderedForks[0].Height { - return GetDefaultAstriaForkData() - } - - idx := sort.Search(len(c.orderedForks), func(i int) bool { - return c.orderedForks[i].Height > height - }) - 1 - - if idx < 0 { - return GetDefaultAstriaForkData() - } - - return c.orderedForks[idx] -} - -func (c *AstriaForks) GetNextForkAtHeight(height uint64) *AstriaForkData { - idx := sort.Search(len(c.orderedForks), func(i int) bool { - return c.orderedForks[i].Height > height - }) - if idx < 0 { - return nil - } - return &c.orderedForks[idx] -} - -func (c *AstriaForks) MinBaseFeeAt(height uint64) *big.Int { - return big.NewInt(0).SetUint64(c.GetForkAtHeight(height).EIP1559Params.MinBaseFee) -} - -func (c *AstriaForks) MarshalJSON() ([]byte, error) { - return json.Marshal(c.forkMap) -} - -func (c *AstriaForks) UnmarshalJSON(data []byte) error { - var forkMap map[string]AstriaForkConfig - if err := json.Unmarshal(data, &forkMap); err != nil { - return err - } - - newForks, err := NewAstriaForks(forkMap) - if err != nil { - return err - } - - *c = *newForks - return nil -} - -func (c *ChainConfig) GetAstriaForks() *AstriaForks { - if c.AstriaForks == nil { - forks, _ := NewAstriaForks(nil) - return forks - } - return c.AstriaForks -} - // EthashConfig is the consensus engine configs for proof-of-work based sealing. type EthashConfig struct{} @@ -743,6 +484,19 @@ func (c *ChainConfig) Description() string { if c.VerkleTime != nil { banner += fmt.Sprintf(" - Verkle: @%-10v\n", *c.VerkleTime) } + banner += "\n" + + // Add Astria forks + banner += "Astria forks (block based):\n" + forks := c.GetAstriaForks().orderedForks + for _, fork := range forks { + banner += fmt.Sprintf(" - %-30s #%-8v", fork.Name+":", fork.Height) + if fork.Halt { + banner += " (!chain halts at this height)" + } + banner += "\n" + } + return banner } @@ -1207,60 +961,3 @@ func (c *ChainConfig) Rules(num *big.Int, isMerge bool, timestamp uint64) Rules IsVerkle: isMerge && c.IsVerkle(num, timestamp), } } - -type AstriaBridgeAddressConfig struct { - BridgeAddress string `json:"bridgeAddress"` - SenderAddress common.Address `json:"senderAddress,omitempty"` - StartHeight uint32 `json:"startHeight"` - AssetDenom string `json:"assetDenom"` - AssetPrecision uint16 `json:"assetPrecision"` - Erc20Asset *AstriaErc20AssetConfig `json:"erc20Asset,omitempty"` -} - -type AstriaErc20AssetConfig struct { - ContractAddress common.Address `json:"contractAddress"` - ContractPrecision uint16 `json:"contractPrecision"` -} - -func (abc *AstriaBridgeAddressConfig) Validate(genesisPrefix string) error { - prefix, byteAddress, err := bech32.Decode(abc.BridgeAddress) - if err != nil { - return fmt.Errorf("bridge address must be a bech32 encoded string") - } - byteAddress, err = bech32.ConvertBits(byteAddress, 5, 8, false) - if err != nil { - return fmt.Errorf("failed to convert address to 8 bit") - } - if prefix != genesisPrefix { - return fmt.Errorf("bridge address must have prefix %s", genesisPrefix) - } - if len(byteAddress) != 20 { - return fmt.Errorf("bridge address must have resolve to 20 byte address, got %d", len(byteAddress)) - } - if abc.StartHeight == 0 { - return fmt.Errorf("start height must be greater than 0") - } - if abc.AssetDenom == "" { - return fmt.Errorf("asset denom must be set") - } - if abc.Erc20Asset == nil && abc.AssetPrecision > 18 { - return fmt.Errorf("asset precision of native asset must be less than or equal to 18") - } - if abc.Erc20Asset != nil && abc.AssetPrecision > abc.Erc20Asset.ContractPrecision { - return fmt.Errorf("asset precision must be less than or equal to contract precision") - } - - return nil -} - -func (abc *AstriaBridgeAddressConfig) ScaledDepositAmount(deposit *big.Int) *big.Int { - var exponent uint16 - if abc.Erc20Asset != nil { - exponent = abc.Erc20Asset.ContractPrecision - abc.AssetPrecision - } else { - exponent = 18 - abc.AssetPrecision - } - multiplier := new(big.Int).Exp(big.NewInt(10), big.NewInt(int64(exponent)), nil) - - return new(big.Int).Mul(deposit, multiplier) -} From 6610cb08d81f0345fa390a4010ff866a3a5acd22 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Thu, 5 Dec 2024 13:35:27 -0500 Subject: [PATCH 4/8] simplify and correct some logic --- params/astria_config.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/params/astria_config.go b/params/astria_config.go index 6980ebf2a..8a1a5663a 100644 --- a/params/astria_config.go +++ b/params/astria_config.go @@ -245,22 +245,13 @@ func GetDefaultAstriaForkData() AstriaForkData { } func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { - if len(c.orderedForks) == 0 { - return GetDefaultAstriaForkData() - } - - if height < c.orderedForks[0].Height { - return GetDefaultAstriaForkData() - } - idx := sort.Search(len(c.orderedForks), func(i int) bool { - return c.orderedForks[i].Height > height - }) - 1 - - if idx < 0 { + return c.orderedForks[i].Height >= height + }) + // no named fork at this height + if idx == len(c.orderedForks) { return GetDefaultAstriaForkData() } - return c.orderedForks[idx] } @@ -268,7 +259,7 @@ func (c *AstriaForks) GetNextForkAtHeight(height uint64) *AstriaForkData { idx := sort.Search(len(c.orderedForks), func(i int) bool { return c.orderedForks[i].Height > height }) - if idx < 0 { + if idx == len(c.orderedForks) { return nil } return &c.orderedForks[idx] From 3aae19adf1f925b98beac114ab1e2020281537d6 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Thu, 12 Dec 2024 23:30:22 -0500 Subject: [PATCH 5/8] fix some tests and issues with astria forks --- dev/geth-genesis-local.json | 27 ++++++++------ eth/backend.go | 1 - go.mod | 4 +- go.sum | 8 ++-- grpc/execution/server.go | 8 +++- grpc/execution/server_test.go | 19 +++++----- grpc/execution/test_utils.go | 62 ++++++++++++++++++------------- grpc/execution/validation_test.go | 20 +++++++--- params/astria_config.go | 7 +++- 9 files changed, 94 insertions(+), 62 deletions(-) diff --git a/dev/geth-genesis-local.json b/dev/geth-genesis-local.json index ae8aed165..b5f4a079f 100644 --- a/dev/geth-genesis-local.json +++ b/dev/geth-genesis-local.json @@ -17,17 +17,22 @@ "ethash": {}, "astriaRollupName": "astria-test-chain-1", "astriaOverrideGenesisExtraData": true, - "astriaSequencerInitialHeight": 2, - "astriaSequencerAddressPrefix": "astria", - "astriaCelestiaInitialHeight": 2, - "astriaCelestiaHeightVariance": 10, - "astriaBridgeAddresses": [], - "astriaBridgeSenderAddress": "0x0000000000000000000000000000000000000000", - "astriaFeeCollectors": { - "1": "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30" - }, - "astriaEIP1559Params": { - "1": { "minBaseFee": 0, "elasticityMultiplier": 2, "BaseFeeChangeDenominator": 8 } + "astriaForks": { + "testLaunch": { + "height": 1, + "feeCollector": "0xaC21B97d35Bf75A7dAb16f35b111a50e78A72F30", + "eip1559Params": { "minBaseFee": 0, "elasticityMultiplier": 2, "BaseFeeChangeDenominator": 8 }, + "sequencer": { + "chainId": "sequencer-test-chain-0", + "addressPrefix": "astria", + "startHeight": 2 + }, + "celestia": { + "chainId": "mocha-4", + "startHeight": 2, + "heightVariance": 10 + } + } } }, "difficulty": "10000000", diff --git a/eth/backend.go b/eth/backend.go index bea001c68..54657ec67 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -258,7 +258,6 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } eth.miner = miner.New(eth, config.Miner, eth.engine) - eth.miner.SetExtra(chainConfig.AstriaExtraData()) eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} if eth.APIBackend.allowUnprotectedTxs { diff --git a/go.mod b/go.mod index 8efbc839d..d1a14fad0 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module github.com/ethereum/go-ethereum go 1.21 require ( - buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1 - buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1 + buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1 + buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1 buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 diff --git a/go.sum b/go.sum index 0407c8168..537988714 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1 h1:N+QXc1yaxcVDZRZrdjtfcZgnuwYmKfAjjaJXWq8ZvK8= -buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-c6f7271b2514.1/go.mod h1:/rU1NViOHDnGkCo0cWFsrI1NXDvNJmgMB/yTrRSY6xU= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1 h1:tVea7fkypwPQCCDkY+yma8V4iWVN7b9ZpRSzvuUBNhc= -buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-c6f7271b2514.1/go.mod h1:PDytSMSvh3RzdjqGE/abPqSxFTZtahXu2rmM+9PQhvY= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1 h1:uaDI8dZs+IE3KDmZaXME5Tr81uQ6jBb64uNVYeHJ2r8= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1/go.mod h1:6bUdMcnGoOiSPYZ+7Cwz24xU9zUrvLlfIifHAzUudnY= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1 h1:27BBCnfzO1X5HMhHpQ0TNr/FZuUE3OvfG/B3g0e6+5s= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1/go.mod h1:PDytSMSvh3RzdjqGE/abPqSxFTZtahXu2rmM+9PQhvY= buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 h1:NtZp7kyoPm4vxioQPvaPfXNoQUAzRCOKnTfaj/fgBAQ= buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1/go.mod h1:I9FcB1oNqT1nI+ny0GD8gF9YrIYrHmczgNu6MTE9fAo= buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 h1:d3Cu4wQ6dAf2P+8gSsClUdWe+waNXrHCO02EJrJruFk= diff --git a/grpc/execution/server.go b/grpc/execution/server.go index aca3920a0..03733e750 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -75,7 +75,7 @@ func NewExecutionServiceServerV1(eth *eth.Ethereum) (*ExecutionServiceServerV1, return nil, errors.New("rollup name not set") } - fork := bc.Config().GetAstriaForks().GetForkAtHeight(bc.CurrentBlock().Number.Uint64()) + fork := bc.Config().GetAstriaForks().GetForkAtHeight(bc.CurrentSafeBlock().Number.Uint64()) if fork.Sequencer.ChainID == "" { return nil, errors.New("sequencer chain ID not set") @@ -114,7 +114,7 @@ func (s *ExecutionServiceServerV1) GetGenesisInfo(ctx context.Context, req *astr rollupHash := sha256.Sum256([]byte(s.bc.Config().AstriaRollupName)) rollupId := primitivev1.RollupId{Inner: rollupHash[:]} - fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(s.bc.CurrentBlock().Number.Uint64()) + fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(s.bc.CurrentSafeBlock().Number.Uint64() + 1) res := &astriaPb.GenesisInfo{ RollupId: &rollupId, @@ -123,6 +123,7 @@ func (s *ExecutionServiceServerV1) GetGenesisInfo(ctx context.Context, req *astr SequencerStopBlockHeight: fork.Sequencer.StopHeight, CelestiaChainId: fork.Celestia.ChainID, CelestiaBlockVariance: fork.Celestia.HeightVariance, + RollupStartBlockHeight: fork.Height, } log.Info("GetGenesisInfo completed", "response", res) @@ -242,6 +243,9 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria // the Miner when building a payload. s.eth.TxPool().SetAstriaOrdered(txsToProcess) + // set extra data + s.eth.Miner().SetExtra(s.bc.Config().AstriaExtraData(height)) + // Build a payload to add to the chain payloadAttributes := &miner.BuildPayloadArgs{ Parent: prevHeadHash, diff --git a/grpc/execution/server_test.go b/grpc/execution/server_test.go index 15ad3538d..d827434cb 100644 --- a/grpc/execution/server_test.go +++ b/grpc/execution/server_test.go @@ -1,12 +1,16 @@ package execution import ( - astriaPb "buf.build/gen/go/astria/execution-apis/protocolbuffers/go/astria/execution/v1" - primitivev1 "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" - sequencerblockv1 "buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go/astria/sequencerblock/v1" "bytes" "context" "crypto/sha256" + "math/big" + "testing" + "time" + + astriaPb "buf.build/gen/go/astria/execution-apis/protocolbuffers/go/astria/execution/v1" + primitivev1 "buf.build/gen/go/astria/primitives/protocolbuffers/go/astria/primitive/v1" + sequencerblockv1 "buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go/astria/sequencerblock/v1" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" @@ -16,9 +20,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - "math/big" - "testing" - "time" ) func TestExecutionService_GetGenesisInfo(t *testing.T) { @@ -30,8 +31,8 @@ func TestExecutionService_GetGenesisInfo(t *testing.T) { hashedRollupId := sha256.Sum256([]byte(ethservice.BlockChain().Config().AstriaRollupName)) require.True(t, bytes.Equal(genesisInfo.RollupId.Inner, hashedRollupId[:]), "RollupId is not correct") - require.Equal(t, genesisInfo.GetSequencerGenesisBlockHeight(), ethservice.BlockChain().Config().AstriaSequencerInitialHeight, "SequencerInitialHeight is not correct") - require.Equal(t, genesisInfo.GetCelestiaBlockVariance(), ethservice.BlockChain().Config().AstriaCelestiaHeightVariance, "CelestiaHeightVariance is not correct") + require.Equal(t, genesisInfo.GetSequencerStartBlockHeight(), ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1).Sequencer.StartHeight, "SequencerInitialHeight is not correct") + require.Equal(t, genesisInfo.GetCelestiaBlockVariance(), ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1).Celestia.HeightVariance, "CelestiaHeightVariance is not correct") require.True(t, serviceV1Alpha1.genesisInfoCalled, "GetGenesisInfo should be called") } @@ -56,7 +57,7 @@ func TestExecutionServiceServerV1Alpha2_GetCommitmentState(t *testing.T) { require.True(t, bytes.Equal(commitmentState.Firm.Hash, firmBlock.Hash().Bytes()), "Firm Block Hashes do not match") require.True(t, bytes.Equal(commitmentState.Firm.ParentBlockHash, firmBlock.ParentHash.Bytes()), "Firm Block Parent Hash do not match") require.Equal(t, uint64(commitmentState.Firm.Number), firmBlock.Number.Uint64(), "Firm Block Number do not match") - require.Equal(t, commitmentState.BaseCelestiaHeight, ethservice.BlockChain().Config().AstriaCelestiaInitialHeight, "BaseCelestiaHeight is not correct") + require.Equal(t, commitmentState.BaseCelestiaHeight, ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1).Celestia.StartHeight, "BaseCelestiaHeight is not correct") require.True(t, serviceV1Alpha1.getCommitmentStateCalled, "GetCommitmentState should be called") } diff --git a/grpc/execution/test_utils.go b/grpc/execution/test_utils.go index dedab1aa5..a137df254 100644 --- a/grpc/execution/test_utils.go +++ b/grpc/execution/test_utils.go @@ -54,26 +54,10 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block, stri panic(err) } - config.AstriaRollupName = "astria" - config.AstriaSequencerAddressPrefix = "astria" - config.AstriaSequencerInitialHeight = 10 - config.AstriaCelestiaInitialHeight = 10 - config.AstriaCelestiaHeightVariance = 10 - - bech32mBridgeAddress, err := bech32.EncodeM(config.AstriaSequencerAddressPrefix, bridgeAddressBytes) + bech32mBridgeAddress, err := bech32.EncodeM("astria", bridgeAddressBytes) if err != nil { panic(err) } - config.AstriaBridgeAddressConfigs = []params.AstriaBridgeAddressConfig{ - { - BridgeAddress: bech32mBridgeAddress, - SenderAddress: common.Address{}, - StartHeight: 2, - AssetDenom: "nria", - AssetPrecision: 18, - Erc20Asset: nil, - }, - } feeCollectorKey, err := crypto.GenerateKey() if err != nil { @@ -81,9 +65,33 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block, stri } feeCollector := crypto.PubkeyToAddress(feeCollectorKey.PublicKey) - astriaFeeCollectors := make(map[uint32]common.Address) - astriaFeeCollectors[1] = feeCollector - config.AstriaFeeCollectors = astriaFeeCollectors + config.AstriaRollupName = "astria" + config.AstriaForks, _ = params.NewAstriaForks(map[string]params.AstriaForkConfig{ + "genesis": { + Height: 1, + FeeCollector: &feeCollector, + Sequencer: ¶ms.AstriaSequencerConfig{ + ChainID: "astria", + AddressPrefix: "astria", + StartHeight: 10, + }, + Celestia: ¶ms.AstriaCelestiaConfig{ + ChainID: "celestia", + StartHeight: 10, + HeightVariance: 10, + }, + BridgeAddresses: []params.AstriaBridgeAddressConfig{ + { + BridgeAddress: bech32mBridgeAddress, + SenderAddress: common.Address{}, + StartHeight: 2, + AssetDenom: "nria", + AssetPrecision: 18, + Erc20Asset: nil, + }, + }, + }, + }) genesis := &core.Genesis{ Config: &config, @@ -138,16 +146,18 @@ func setupExecutionService(t *testing.T, noOfBlocksToGenerate int) (*eth.Ethereu serviceV1Alpha1, err := NewExecutionServiceServerV1(ethservice) require.Nil(t, err, "can't create execution service") - feeCollector := crypto.PubkeyToAddress(feeCollectorKey.PublicKey) - require.Equal(t, feeCollector, serviceV1Alpha1.nextFeeRecipient, "nextFeeRecipient not set correctly") + fork := genesis.Config.AstriaForks.GetForkAtHeight(1) - bridgeAsset := genesis.Config.AstriaBridgeAddressConfigs[0].AssetDenom - _, ok := serviceV1Alpha1.bridgeAllowedAssets[bridgeAsset] - require.True(t, ok, "bridgeAllowedAssetIDs does not contain bridge asset id") + feeCollector := crypto.PubkeyToAddress(feeCollectorKey.PublicKey) + require.Equal(t, feeCollector, fork.FeeCollector, "feeCollector not set correctly") - _, ok = serviceV1Alpha1.bridgeAddresses[bridgeAddress] + bridgeCfg, ok := fork.BridgeAddresses[bridgeAddress] require.True(t, ok, "bridgeAddress not set correctly") + bridgeAsset := bridgeCfg.AssetDenom + _, ok = fork.BridgeAllowedAssets[bridgeAsset] + require.True(t, ok, "bridgeAllowedAssetIDs does not contain bridge asset id") + _, err = ethservice.BlockChain().InsertChain(blocks) require.Nil(t, err, "can't insert blocks") diff --git a/grpc/execution/validation_test.go b/grpc/execution/validation_test.go index 9c2b149d6..13d95f41b 100644 --- a/grpc/execution/validation_test.go +++ b/grpc/execution/validation_test.go @@ -53,7 +53,7 @@ func generateBech32MAddress() string { } func TestSequenceTxValidation(t *testing.T) { - ethservice, serviceV1Alpha1 := setupExecutionService(t, 10) + ethservice, _ := setupExecutionService(t, 10) blobTx, err := testBlobTx().MarshalBinary() require.Nil(t, err, "failed to marshal random blob tx: %v", err) @@ -72,18 +72,26 @@ func TestSequenceTxValidation(t *testing.T) { require.Nil(t, err, "failed to generate chain destination key: %v", err) chainDestinationAddress := crypto.PubkeyToAddress(chainDestinationKey.PublicKey) - bridgeAssetDenom := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].AssetDenom + fork := ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1) + + var bridgeAssetDenom string + var bridgeAddress string + for _, bridgeCfg := range fork.BridgeAddresses { + bridgeAssetDenom = bridgeCfg.AssetDenom + bridgeAddress = bridgeCfg.BridgeAddress + break + } + require.NotEmpty(t, bridgeAssetDenom, "bridgeAssetDenom not found") + invalidBridgeAssetDenom := "invalid-asset-denom" invalidHeightBridgeAssetDenom := "invalid-height-asset-denom" invalidHeightBridgeAddressBech32m := generateBech32MAddress() - serviceV1Alpha1.bridgeAddresses[invalidHeightBridgeAddressBech32m] = ¶ms.AstriaBridgeAddressConfig{ + fork.BridgeAddresses[invalidHeightBridgeAddressBech32m] = ¶ms.AstriaBridgeAddressConfig{ AssetDenom: invalidHeightBridgeAssetDenom, StartHeight: 100, } - bridgeAddress := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].BridgeAddress - tests := []struct { description string sequencerTx *sequencerblockv1.RollupData @@ -196,7 +204,7 @@ func TestSequenceTxValidation(t *testing.T) { for _, test := range tests { t.Run(test.description, func(t *testing.T) { - _, err := validateAndUnmarshalSequencerTx(2, test.sequencerTx, serviceV1Alpha1.bridgeAddresses, serviceV1Alpha1.bridgeAllowedAssets) + _, err := validateAndUnmarshalSequencerTx(2, test.sequencerTx, fork.BridgeAddresses, fork.BridgeAllowedAssets) if test.wantErr == "" && err == nil { return } diff --git a/params/astria_config.go b/params/astria_config.go index 8a1a5663a..e76604a83 100644 --- a/params/astria_config.go +++ b/params/astria_config.go @@ -190,6 +190,11 @@ func NewAstriaForks(forks map[string]AstriaForkConfig) (*AstriaForks, error) { return nil, err } + // Sort orderedForks in descending order by Height + sort.Slice(orderedForks, func(i, j int) bool { + return orderedForks[i].Height > orderedForks[j].Height + }) + return &AstriaForks{ orderedForks: orderedForks, forkMap: forks, @@ -246,7 +251,7 @@ func GetDefaultAstriaForkData() AstriaForkData { func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { idx := sort.Search(len(c.orderedForks), func(i int) bool { - return c.orderedForks[i].Height >= height + return c.orderedForks[i].Height <= height }) // no named fork at this height if idx == len(c.orderedForks) { From 69cc30931c98ee9c6d8d9ed77b99bf2024ae8ea7 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Tue, 14 Jan 2025 15:02:31 -0500 Subject: [PATCH 6/8] Add HaltAtStopHeight to genesis info --- consensus/misc/eip1559/eip1559.go | 8 ++--- core/chain_makers.go | 4 +-- go.mod | 10 +++--- go.sum | 18 ++++++++++ grpc/execution/server.go | 2 ++ grpc/execution/server_test.go | 22 ++++++++++--- miner/worker.go | 2 +- params/astria_config.go | 18 +++++++--- params/astria_config_test.go | 55 ++++++++++++++++--------------- params/config.go | 16 ++------- 10 files changed, 95 insertions(+), 60 deletions(-) diff --git a/consensus/misc/eip1559/eip1559.go b/consensus/misc/eip1559/eip1559.go index 152060d7e..c0a4cb78a 100644 --- a/consensus/misc/eip1559/eip1559.go +++ b/consensus/misc/eip1559/eip1559.go @@ -35,7 +35,7 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade // Verify that the gas limit remains within allowed bounds parentGasLimit := parent.GasLimit if !config.IsLondon(parent.Number) { - parentGasLimit = parent.GasLimit * config.ElasticityMultiplier(parent.Number.Uint64()) + parentGasLimit = parent.GasLimit * config.GetAstriaForks().ElasticityMultiplierAt(parent.Number.Uint64()) } if err := misc.VerifyGaslimit(parentGasLimit, header.GasLimit); err != nil { return err @@ -60,7 +60,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { return new(big.Int).SetUint64(params.InitialBaseFee) } - parentGasTarget := parent.GasLimit / config.ElasticityMultiplier(parent.Number.Uint64()+1) + parentGasTarget := parent.GasLimit / config.GetAstriaForks().ElasticityMultiplierAt(parent.Number.Uint64()+1) // If the parent gasUsed is the same as the target, the baseFee remains unchanged. if parent.GasUsed == parentGasTarget { return new(big.Int).Set(parent.BaseFee) @@ -77,7 +77,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.SetUint64(parent.GasUsed - parentGasTarget) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) - num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number.Uint64()+1))) + num.Div(num, denom.SetUint64(config.GetAstriaForks().BaseFeeChangeDenominatorAt(parent.Number.Uint64()+1))) baseFeeDelta := math.BigMax(num, common.Big1) return num.Add(parent.BaseFee, baseFeeDelta) @@ -87,7 +87,7 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header) *big.Int { num.SetUint64(parentGasTarget - parent.GasUsed) num.Mul(num, parent.BaseFee) num.Div(num, denom.SetUint64(parentGasTarget)) - num.Div(num, denom.SetUint64(config.BaseFeeChangeDenominator(parent.Number.Uint64()+1))) + num.Div(num, denom.SetUint64(config.GetAstriaForks().BaseFeeChangeDenominatorAt(parent.Number.Uint64()+1))) baseFee := num.Sub(parent.BaseFee, num) lowerBound := config.GetAstriaForks().MinBaseFeeAt(parent.Number.Uint64() + 1) diff --git a/core/chain_makers.go b/core/chain_makers.go index 68f7bc529..bbd535b52 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -233,7 +233,7 @@ func (b *BlockGen) AddUncle(h *types.Header) { if b.cm.config.IsLondon(h.Number) { h.BaseFee = eip1559.CalcBaseFee(b.cm.config, parent) if !b.cm.config.IsLondon(parent.Number) { - parentGasLimit := parent.GasLimit * b.cm.config.ElasticityMultiplier(parent.Number.Uint64()) + parentGasLimit := parent.GasLimit * b.cm.config.GetAstriaForks().ElasticityMultiplierAt(parent.Number.Uint64()) h.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) } } @@ -540,7 +540,7 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi if cm.config.IsLondon(header.Number) { header.BaseFee = eip1559.CalcBaseFee(cm.config, parent.Header()) if !cm.config.IsLondon(parent.Number()) { - parentGasLimit := parent.GasLimit() * cm.config.ElasticityMultiplier(parent.Number().Uint64()) + parentGasLimit := parent.GasLimit() * cm.config.GetAstriaForks().ElasticityMultiplierAt(parent.Number().Uint64()) header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit) } } diff --git a/go.mod b/go.mod index d1a14fad0..df7755a41 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module github.com/ethereum/go-ethereum go 1.21 require ( - buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1 - buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1 - buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 - buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 + buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-d9b5ae55864c.2 + buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.36.2-00000000000000-d9b5ae55864c.1 + buf.build/gen/go/astria/primitives/protocolbuffers/go v1.36.2-00000000000000-d95ace43ccf0.1 + buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.36.2-00000000000000-9be5ad1ca3f1.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.0 github.com/Microsoft/go-winio v0.6.1 github.com/VictoriaMetrics/fastcache v1.12.1 @@ -79,7 +79,7 @@ require ( golang.org/x/time v0.5.0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d google.golang.org/grpc v1.64.1 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.2 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 537988714..a8e2e86f4 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,25 @@ buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1 h1:uaDI8dZs+IE3KDmZaXME5Tr81uQ6jBb64uNVYeHJ2r8= buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-39902173feaa.1/go.mod h1:6bUdMcnGoOiSPYZ+7Cwz24xU9zUrvLlfIifHAzUudnY= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-d9b5ae55864c.2 h1:q+20sqaEUPt3R/KOWjM1KrmfFtMtCJN3grbRX/b9ySQ= +buf.build/gen/go/astria/execution-apis/grpc/go v1.5.1-00000000000000-d9b5ae55864c.2/go.mod h1:xZRJPCBZP0R0gl3kg44oW5bGWG8pHrrGLtA8B6LsOOc= buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1 h1:27BBCnfzO1X5HMhHpQ0TNr/FZuUE3OvfG/B3g0e6+5s= buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.35.2-00000000000000-39902173feaa.1/go.mod h1:PDytSMSvh3RzdjqGE/abPqSxFTZtahXu2rmM+9PQhvY= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.36.1-00000000000000-d9b5ae55864c.1 h1:IFOlTF2lzUE0IzJfM/Zrm7CIKfVcV5JU0w6f2oGLTfg= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.36.1-00000000000000-d9b5ae55864c.1/go.mod h1:iaB9+a1Tvk6iiD3NzlXI8GFLs0AqAVNRL2n8vTHkdLY= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.36.2-00000000000000-d9b5ae55864c.1 h1:mo8BZoepJW2FrrwuJZxVq7JXHZ7IeHeF3xIcEjCQ9lc= +buf.build/gen/go/astria/execution-apis/protocolbuffers/go v1.36.2-00000000000000-d9b5ae55864c.1/go.mod h1:rWf3Lvbpv9nzynfUQASrpbDydoRzyT/bm0nIFMhMkPc= buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1 h1:NtZp7kyoPm4vxioQPvaPfXNoQUAzRCOKnTfaj/fgBAQ= buf.build/gen/go/astria/primitives/protocolbuffers/go v1.35.2-00000000000000-d95ace43ccf0.1/go.mod h1:I9FcB1oNqT1nI+ny0GD8gF9YrIYrHmczgNu6MTE9fAo= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.36.1-00000000000000-d95ace43ccf0.1 h1:MjGboR15UjIZBUXofmCMnerkyeBGTOOiNwJPHPfOyqU= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.36.1-00000000000000-d95ace43ccf0.1/go.mod h1:HnX2FkSKZuD3zPFBR+Q17WzloqvIbFd0pYE++or/x2Q= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.36.2-00000000000000-d95ace43ccf0.1 h1:xNuYV8H8GbUpcMM7S2iGwjg7HswyE/2YrsbLybxLto4= +buf.build/gen/go/astria/primitives/protocolbuffers/go v1.36.2-00000000000000-d95ace43ccf0.1/go.mod h1:Lk1TBSGhOGvbtj0lb7eTeq+Z4N86/67Ay+WWxbqhh6s= buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1 h1:d3Cu4wQ6dAf2P+8gSsClUdWe+waNXrHCO02EJrJruFk= buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.35.2-00000000000000-9be5ad1ca3f1.1/go.mod h1:oyKOX3YjAN6T+jYc5Rai+RgrZD9b+sDXpdTSAgIISUA= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.36.1-00000000000000-9be5ad1ca3f1.1 h1:cWlFRWGhQ1+xdF9F4Z1mmRGFY0xZpudLWBy5KrHhKiY= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.36.1-00000000000000-9be5ad1ca3f1.1/go.mod h1:CVq79u6hTk0buR3M2g3Yg/rLRN6MMxTf9Nn5JWUIeFc= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.36.2-00000000000000-9be5ad1ca3f1.1 h1:Cd/jFk9rohdB1ckA30caRjQleFC8J67fNXgtyWkQadI= +buf.build/gen/go/astria/sequencerblock-apis/protocolbuffers/go v1.36.2-00000000000000-9be5ad1ca3f1.1/go.mod h1:JUPGxE+f8MD5sDPN1B9IZxqa8uD+gc1D6qaj+D7dHuU= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -897,6 +911,10 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 03733e750..819cde825 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -115,6 +115,7 @@ func (s *ExecutionServiceServerV1) GetGenesisInfo(ctx context.Context, req *astr rollupId := primitivev1.RollupId{Inner: rollupHash[:]} fork := s.bc.Config().GetAstriaForks().GetForkAtHeight(s.bc.CurrentSafeBlock().Number.Uint64() + 1) + nextFork := s.bc.Config().GetAstriaForks().GetNextForkAtHeight(s.bc.CurrentSafeBlock().Number.Uint64() + 1) res := &astriaPb.GenesisInfo{ RollupId: &rollupId, @@ -124,6 +125,7 @@ func (s *ExecutionServiceServerV1) GetGenesisInfo(ctx context.Context, req *astr CelestiaChainId: fork.Celestia.ChainID, CelestiaBlockVariance: fork.Celestia.HeightVariance, RollupStartBlockHeight: fork.Height, + HaltAtStopHeight: nextFork != nil && nextFork.Halt, } log.Info("GetGenesisInfo completed", "response", res) diff --git a/grpc/execution/server_test.go b/grpc/execution/server_test.go index d827434cb..c92748903 100644 --- a/grpc/execution/server_test.go +++ b/grpc/execution/server_test.go @@ -248,6 +248,13 @@ func TestExecutionServiceServerV1Alpha2_ExecuteBlock(t *testing.T) { }, } + fork := ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1) + var bridgeConfig params.AstriaBridgeAddressConfig + for _, cfg := range fork.BridgeAddresses { + bridgeConfig = *cfg + break + } + for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { // reset the blockchain with each test @@ -287,8 +294,8 @@ func TestExecutionServiceServerV1Alpha2_ExecuteBlock(t *testing.T) { // create deposit tx if depositTxAmount is non zero if tt.depositTxAmount.Cmp(big.NewInt(0)) != 0 { depositAmount := bigIntToProtoU128(tt.depositTxAmount) - bridgeAddress := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].BridgeAddress - bridgeAssetDenom := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].AssetDenom + bridgeAddress := bridgeConfig.BridgeAddress + bridgeAssetDenom := bridgeConfig.AssetDenom // create new chain destination address for better testing chainDestinationAddressPrivKey, err := crypto.GenerateKey() @@ -376,10 +383,17 @@ func TestExecutionServiceServerV1Alpha2_ExecuteBlockAndUpdateCommitment(t *testi }) } + fork := ethservice.BlockChain().Config().AstriaForks.GetForkAtHeight(1) + var bridgeConfig params.AstriaBridgeAddressConfig + for _, cfg := range fork.BridgeAddresses { + bridgeConfig = *cfg + break + } + amountToDeposit := big.NewInt(1000000000000000000) depositAmount := bigIntToProtoU128(amountToDeposit) - bridgeAddress := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].BridgeAddress - bridgeAssetDenom := ethservice.BlockChain().Config().AstriaBridgeAddressConfigs[0].AssetDenom + bridgeAddress := bridgeConfig.BridgeAddress + bridgeAssetDenom := bridgeConfig.AssetDenom // create new chain destination address for better testing chainDestinationAddressPrivKey, err := crypto.GenerateKey() diff --git a/miner/worker.go b/miner/worker.go index 7b36a2e87..c349f7afe 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -130,7 +130,7 @@ func (miner *Miner) prepareWork(genParams *generateParams) (*environment, error) if miner.chainConfig.IsLondon(header.Number) { header.BaseFee = eip1559.CalcBaseFee(miner.chainConfig, parent) if !miner.chainConfig.IsLondon(parent.Number) { - parentGasLimit := parent.GasLimit * miner.chainConfig.ElasticityMultiplier(parent.Number.Uint64()) + parentGasLimit := parent.GasLimit * miner.chainConfig.GetAstriaForks().ElasticityMultiplierAt(parent.Number.Uint64()) header.GasLimit = core.CalcGasLimit(parentGasLimit, miner.config.GasCeil) } } diff --git a/params/astria_config.go b/params/astria_config.go index e76604a83..5eb21904e 100644 --- a/params/astria_config.go +++ b/params/astria_config.go @@ -25,7 +25,7 @@ type AstriaForkConfig struct { SnapshotChecksum string `json:"snapshotChecksum,omitempty"` ExtraDataOverride hexutil.Bytes `json:"extraDataOverride,omitempty"` FeeCollector *common.Address `json:"feeCollector,omitempty"` - EIP1559Params *AstriaEIP1559Param `json:"eip1559Params,omitempty"` + EIP1559Params *AstriaEIP1559Params `json:"eip1559Params,omitempty"` Sequencer *AstriaSequencerConfig `json:"sequencer,omitempty"` Celestia *AstriaCelestiaConfig `json:"celestia,omitempty"` BridgeAddresses []AstriaBridgeAddressConfig `json:"bridgeAddresses,omitempty"` @@ -38,7 +38,7 @@ type AstriaForkData struct { SnapshotChecksum string ExtraDataOverride hexutil.Bytes FeeCollector common.Address - EIP1559Params AstriaEIP1559Param + EIP1559Params AstriaEIP1559Params Sequencer AstriaSequencerConfig Celestia AstriaCelestiaConfig BridgeAddresses map[string]*AstriaBridgeAddressConfig // astria bridge addess to config for that bridge account @@ -59,7 +59,7 @@ type AstriaCelestiaConfig struct { HeightVariance uint64 `json:"heightVariance"` } -type AstriaEIP1559Param struct { +type AstriaEIP1559Params struct { MinBaseFee uint64 `json:"minBaseFee"` ElasticityMultiplier uint64 `json:"elasticityMultiplier"` BaseFeeChangeDenominator uint64 `json:"baseFeeChangeDenominator"` @@ -239,7 +239,7 @@ func GetDefaultAstriaForkData() AstriaForkData { return AstriaForkData{ Height: 1, FeeCollector: common.Address{}, - EIP1559Params: AstriaEIP1559Param{ + EIP1559Params: AstriaEIP1559Params{ MinBaseFee: 0, ElasticityMultiplier: DefaultElasticityMultiplier, BaseFeeChangeDenominator: DefaultBaseFeeChangeDenominator, @@ -274,6 +274,16 @@ func (c *AstriaForks) MinBaseFeeAt(height uint64) *big.Int { return big.NewInt(0).SetUint64(c.GetForkAtHeight(height).EIP1559Params.MinBaseFee) } +// BaseFeeChangeDenominator bounds the amount the base fee can change between blocks. +func (c *AstriaForks) BaseFeeChangeDenominatorAt(height uint64) uint64 { + return c.GetForkAtHeight(height).EIP1559Params.BaseFeeChangeDenominator +} + +// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. +func (c *AstriaForks) ElasticityMultiplierAt(height uint64) uint64 { + return c.GetForkAtHeight(height).EIP1559Params.ElasticityMultiplier +} + func (c *AstriaForks) MarshalJSON() ([]byte, error) { return json.Marshal(c.forkMap) } diff --git a/params/astria_config_test.go b/params/astria_config_test.go index a91b41b14..38b4867e2 100644 --- a/params/astria_config_test.go +++ b/params/astria_config_test.go @@ -1,7 +1,6 @@ package params import ( - "encoding/json" "fmt" "math/big" "reflect" @@ -14,30 +13,32 @@ import ( ) func TestAstriaEIP1559Params(t *testing.T) { - jsonBuf := []byte(`{ - "1":{ "minBaseFee": 45000000000, "elasticityMultiplier": 4, "baseFeeChangeDenominator": 100 }, - "101":{ "minBaseFee": 120000000, "elasticityMultiplier": 11, "baseFeeChangeDenominator": 250 }, - "15":{ "minBaseFee": 15000000000, "elasticityMultiplier": 5, "baseFeeChangeDenominator": 50 } - }`) - - var eip1559Params AstriaEIP1559Params - err := json.Unmarshal(jsonBuf, &eip1559Params) - if err != nil { - t.Errorf("unexpected err %v", err) - } - - expected := AstriaEIP1559Params{ - heights: map[uint64]AstriaEIP1559Param{ - 1: {MinBaseFee: 45000000000, ElasticityMultiplier: 4, BaseFeeChangeDenominator: 100}, - 101: {MinBaseFee: 120000000, ElasticityMultiplier: 11, BaseFeeChangeDenominator: 250}, - 15: {MinBaseFee: 15000000000, ElasticityMultiplier: 5, BaseFeeChangeDenominator: 50}, + astriaForks, _ := NewAstriaForks(map[string]AstriaForkConfig{ + "genesis": { + Height: 1, + EIP1559Params: &AstriaEIP1559Params{ + MinBaseFee: 45000000000, + ElasticityMultiplier: 4, + BaseFeeChangeDenominator: 100, + }, }, - orderedHeights: []uint64{101, 15, 1}, - } - - if !reflect.DeepEqual(eip1559Params, expected) { - t.Errorf("expected %v, got %v", expected, eip1559Params) - } + "fork1": { + Height: 15, + EIP1559Params: &AstriaEIP1559Params{ + MinBaseFee: 15000000000, + ElasticityMultiplier: 5, + BaseFeeChangeDenominator: 50, + }, + }, + "fork2": { + Height: 101, + EIP1559Params: &AstriaEIP1559Params{ + MinBaseFee: 120000000, + ElasticityMultiplier: 11, + BaseFeeChangeDenominator: 250, + }, + }, + }) minBaseTests := map[uint64]*big.Int{ 0: common.Big0, @@ -54,7 +55,7 @@ func TestAstriaEIP1559Params(t *testing.T) { } for height, expected := range minBaseTests { - if got := eip1559Params.MinBaseFeeAt(height); got.Cmp(expected) != 0 { + if got := astriaForks.MinBaseFeeAt(height); got.Cmp(expected) != 0 { t.Errorf("MinBaseFeeAt(%d): expected %v, got %v", height, expected, got) } } @@ -74,7 +75,7 @@ func TestAstriaEIP1559Params(t *testing.T) { } for height, expected := range elasticityMultiplierTests { - if got := eip1559Params.ElasticityMultiplierAt(height); got != expected { + if got := astriaForks.ElasticityMultiplierAt(height); got != expected { t.Errorf("ElasticityMultiplierAt(%d): expected %v, got %v", height, expected, got) } } @@ -94,7 +95,7 @@ func TestAstriaEIP1559Params(t *testing.T) { } for height, expected := range baseFeeChangeDenominatorTests { - if got := eip1559Params.BaseFeeChangeDenominatorAt(height); got != expected { + if got := astriaForks.BaseFeeChangeDenominatorAt(height); got != expected { t.Errorf("BaseFeeChangeDenominatorAt(%d): expected %v, got %v", height, expected, got) } } diff --git a/params/config.go b/params/config.go index a1fefe7ba..f71fa3f2f 100644 --- a/params/config.go +++ b/params/config.go @@ -488,9 +488,9 @@ func (c *ChainConfig) Description() string { // Add Astria forks banner += "Astria forks (block based):\n" - forks := c.GetAstriaForks().orderedForks - for _, fork := range forks { - banner += fmt.Sprintf(" - %-30s #%-8v", fork.Name+":", fork.Height) + forks := c.GetAstriaForks().forkMap + for forkName, fork := range forks { + banner += fmt.Sprintf(" - %-30s #%-8v", forkName+":", fork.Height) if fork.Halt { banner += " (!chain halts at this height)" } @@ -764,16 +764,6 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, headNumber *big.Int, return nil } -// BaseFeeChangeDenominator bounds the amount the base fee can change between blocks. -func (c *ChainConfig) BaseFeeChangeDenominator(height uint64) uint64 { - return c.GetAstriaForks().GetForkAtHeight(height).EIP1559Params.BaseFeeChangeDenominator -} - -// ElasticityMultiplier bounds the maximum gas limit an EIP-1559 block may have. -func (c *ChainConfig) ElasticityMultiplier(height uint64) uint64 { - return c.GetAstriaForks().GetForkAtHeight(height).EIP1559Params.ElasticityMultiplier -} - // LatestFork returns the latest time-based fork that would be active for the given time. func (c *ChainConfig) LatestFork(time uint64) forks.Fork { // Assume last non-time-based fork has passed. From 5886c6630945d557fb6cb8e5fdcee7f47cabeef3 Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Tue, 14 Jan 2025 15:51:02 -0500 Subject: [PATCH 7/8] fixing tests --- grpc/execution/server.go | 2 +- params/astria_config_test.go | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 819cde825..51881c9ab 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -75,7 +75,7 @@ func NewExecutionServiceServerV1(eth *eth.Ethereum) (*ExecutionServiceServerV1, return nil, errors.New("rollup name not set") } - fork := bc.Config().GetAstriaForks().GetForkAtHeight(bc.CurrentSafeBlock().Number.Uint64()) + fork := bc.Config().GetAstriaForks().GetForkAtHeight(max(bc.CurrentSafeBlock().Number.Uint64(), 1)) if fork.Sequencer.ChainID == "" { return nil, errors.New("sequencer chain ID not set") diff --git a/params/astria_config_test.go b/params/astria_config_test.go index 38b4867e2..750ceb796 100644 --- a/params/astria_config_test.go +++ b/params/astria_config_test.go @@ -16,6 +16,15 @@ func TestAstriaEIP1559Params(t *testing.T) { astriaForks, _ := NewAstriaForks(map[string]AstriaForkConfig{ "genesis": { Height: 1, + Sequencer: &AstriaSequencerConfig{ + ChainID: "sequencer-test-chain-0", + StartHeight: 2, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "mocha-4", + StartHeight: 2, + HeightVariance: 10, + }, EIP1559Params: &AstriaEIP1559Params{ MinBaseFee: 45000000000, ElasticityMultiplier: 4, From 6bdcf95d25d4b96d004b3198531fb0e684afffba Mon Sep 17 00:00:00 2001 From: Josh Dechant Date: Wed, 15 Jan 2025 22:42:14 -0500 Subject: [PATCH 8/8] resolved bug with GetNextForkAtHeight; added clarity; added some tests --- params/astria_config.go | 21 +-- params/astria_config_test.go | 321 +++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 10 deletions(-) diff --git a/params/astria_config.go b/params/astria_config.go index 5eb21904e..444ddf1e1 100644 --- a/params/astria_config.go +++ b/params/astria_config.go @@ -15,7 +15,7 @@ import ( ) type AstriaForks struct { - orderedForks []AstriaForkData + orderedForks []AstriaForkData // sorted in descending order by Height forkMap map[string]AstriaForkConfig } @@ -237,6 +237,7 @@ func validateAstriaForks(forks []AstriaForkData) error { func GetDefaultAstriaForkData() AstriaForkData { return AstriaForkData{ + Name: "default", Height: 1, FeeCollector: common.Address{}, EIP1559Params: AstriaEIP1559Params{ @@ -261,13 +262,13 @@ func (c *AstriaForks) GetForkAtHeight(height uint64) AstriaForkData { } func (c *AstriaForks) GetNextForkAtHeight(height uint64) *AstriaForkData { - idx := sort.Search(len(c.orderedForks), func(i int) bool { - return c.orderedForks[i].Height > height - }) - if idx == len(c.orderedForks) { - return nil + // orderedForks are sorted in descending order; iterate from the end + for i := len(c.orderedForks) - 1; i >= 0; i-- { + if c.orderedForks[i].Height > height { + return &c.orderedForks[i] + } } - return &c.orderedForks[idx] + return nil } func (c *AstriaForks) MinBaseFeeAt(height uint64) *big.Int { @@ -325,7 +326,7 @@ type AstriaErc20AssetConfig struct { ContractPrecision uint16 `json:"contractPrecision"` } -func (abc *AstriaBridgeAddressConfig) Validate(genesisPrefix string) error { +func (abc *AstriaBridgeAddressConfig) Validate(expectedPrefix string) error { prefix, byteAddress, err := bech32.Decode(abc.BridgeAddress) if err != nil { return fmt.Errorf("bridge address must be a bech32 encoded string") @@ -334,8 +335,8 @@ func (abc *AstriaBridgeAddressConfig) Validate(genesisPrefix string) error { if err != nil { return fmt.Errorf("failed to convert address to 8 bit") } - if prefix != genesisPrefix { - return fmt.Errorf("bridge address must have prefix %s", genesisPrefix) + if prefix != expectedPrefix { + return fmt.Errorf("bridge address must have prefix %s", expectedPrefix) } if len(byteAddress) != 20 { return fmt.Errorf("bridge address must have resolve to 20 byte address, got %d", len(byteAddress)) diff --git a/params/astria_config_test.go b/params/astria_config_test.go index 750ceb796..1d4d6fc35 100644 --- a/params/astria_config_test.go +++ b/params/astria_config_test.go @@ -253,3 +253,324 @@ func TestAstriaBridgeConfigValidation(t *testing.T) { }) } } + +func TestGetForkAtHeight(t *testing.T) { + forkMap := map[string]AstriaForkConfig{ + "fork1": { + Height: 1, + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain1", + StartHeight: 1, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "celestia1", + StartHeight: 1, + HeightVariance: 100, + }, + }, + "fork2": { + Height: 1000, + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain2", + StartHeight: 2, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "celestia2", + StartHeight: 2, + HeightVariance: 200, + }, + }, + "fork3": { + Height: 2000, + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain3", + StartHeight: 3, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "celestia3", + StartHeight: 3, + HeightVariance: 300, + }, + }, + } + + forks, err := NewAstriaForks(forkMap) + if err != nil { + t.Fatalf("failed to create forks: %v", err) + } + + tests := []struct { + description string + height uint64 + wantFork string + }{ + { + description: "height 1 returns first fork", + height: 1, + wantFork: "fork1", + }, + { + description: "height 500 returns first fork", + height: 500, + wantFork: "fork1", + }, + { + description: "height 1000 returns second fork", + height: 1000, + wantFork: "fork2", + }, + { + description: "height 1500 returns second fork", + height: 1500, + wantFork: "fork2", + }, + { + description: "height 2000 returns third fork", + height: 2000, + wantFork: "fork3", + }, + { + description: "height 3000 returns third fork", + height: 3000, + wantFork: "fork3", + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + fork := forks.GetForkAtHeight(test.height) + if fork.Name != test.wantFork { + t.Errorf("got fork %s, want %s", fork.Name, test.wantFork) + } + }) + } +} + +func TestGetNextForkAtHeight(t *testing.T) { + forkMap := map[string]AstriaForkConfig{ + "fork1": { + Height: 1, + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain1", + StartHeight: 1, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "celestia1", + StartHeight: 1, + HeightVariance: 100, + }, + }, + "fork2": { + Height: 1000, + }, + "fork3": { + Height: 2000, + }, + } + + forks, err := NewAstriaForks(forkMap) + if err != nil { + t.Fatalf("failed to create forks: %v", err) + } + + tests := []struct { + description string + height uint64 + wantFork *string + }{ + { + description: "height 0 returns first fork", + height: 0, + wantFork: strPtr("fork1"), + }, + { + description: "height 1 returns second fork", + height: 1, + wantFork: strPtr("fork2"), + }, + { + description: "height 500 returns second fork", + height: 500, + wantFork: strPtr("fork2"), + }, + { + description: "height 1500 returns third fork", + height: 1500, + wantFork: strPtr("fork3"), + }, + { + description: "height 1999 returns third fork", + height: 1999, + wantFork: strPtr("fork3"), + }, + { + description: "height 2000 returns nil", + height: 2000, + wantFork: nil, + }, + { + description: "height 3000 returns nil", + height: 3000, + wantFork: nil, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + nextFork := forks.GetNextForkAtHeight(test.height) + if test.wantFork == nil { + if nextFork != nil { + t.Errorf("got fork %v, want nil", nextFork) + } + return + } + if nextFork == nil { + t.Errorf("got nil fork, want %s", *test.wantFork) + return + } + if nextFork.Name != *test.wantFork { + t.Errorf("got fork %s, want %s", nextFork.Name, *test.wantFork) + } + }) + } +} + +func strPtr(s string) *string { + return &s +} + +func TestAstriaForksInheritance(t *testing.T) { + forkMap := map[string]AstriaForkConfig{ + "fork1": { + Height: 1, + EIP1559Params: &AstriaEIP1559Params{ + MinBaseFee: 1000, + ElasticityMultiplier: 2, + BaseFeeChangeDenominator: 8, + }, + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain1", + StartHeight: 1, + }, + Celestia: &AstriaCelestiaConfig{ + ChainID: "celestia1", + StartHeight: 1, + HeightVariance: 100, + }, + }, + "fork2": { + Height: 200, + // override EIP1559Params + EIP1559Params: &AstriaEIP1559Params{ + MinBaseFee: 2000, + ElasticityMultiplier: 4, + BaseFeeChangeDenominator: 16, + }, + }, + "fork3": { + Height: 300, + // override sequencer config + Sequencer: &AstriaSequencerConfig{ + ChainID: "chain3", + StartHeight: 3, + }, + // EIP1559Params should be inherited from fork2 + }, + } + + forks, err := NewAstriaForks(forkMap) + if err != nil { + t.Fatalf("failed to create forks: %v", err) + } + + type testCheck struct { + minBaseFee uint64 + elasticityMultiplier uint64 + baseFeeChangeDenominator uint64 + sequencerChainID string + sequencerStartHeight uint32 + celestiaChainID string + celestiaStartHeight uint64 + celestiaHeightVariance uint64 + } + + tests := []struct { + description string + height uint64 + checks testCheck + }{ + { + description: "fork1 sets initial values", + height: 150, + checks: testCheck{ + minBaseFee: 1000, + elasticityMultiplier: 2, + baseFeeChangeDenominator: 8, + sequencerChainID: "chain1", + sequencerStartHeight: 1, + celestiaChainID: "celestia1", + celestiaStartHeight: 1, + celestiaHeightVariance: 100, + }, + }, + { + description: "fork2 inherits everything but EIP1559Params", + height: 250, + checks: testCheck{ + minBaseFee: 2000, + elasticityMultiplier: 4, + baseFeeChangeDenominator: 16, + sequencerChainID: "chain1", + sequencerStartHeight: 1, + celestiaChainID: "celestia1", + celestiaStartHeight: 1, + celestiaHeightVariance: 100, + }, + }, + { + description: "fork3 inherits EIP1559Params but changes sequencer", + height: 350, + checks: testCheck{ + minBaseFee: 2000, + elasticityMultiplier: 4, + baseFeeChangeDenominator: 16, + sequencerChainID: "chain3", + sequencerStartHeight: 3, + celestiaChainID: "celestia1", + celestiaStartHeight: 1, + celestiaHeightVariance: 100, + }, + }, + } + + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + fork := forks.GetForkAtHeight(test.height) + + if got := fork.EIP1559Params.MinBaseFee; got != test.checks.minBaseFee { + t.Errorf("MinBaseFee = %v, want %v", got, test.checks.minBaseFee) + } + if got := fork.EIP1559Params.ElasticityMultiplier; got != test.checks.elasticityMultiplier { + t.Errorf("ElasticityMultiplier = %v, want %v", got, test.checks.elasticityMultiplier) + } + if got := fork.EIP1559Params.BaseFeeChangeDenominator; got != test.checks.baseFeeChangeDenominator { + t.Errorf("BaseFeeChangeDenominator = %v, want %v", got, test.checks.baseFeeChangeDenominator) + } + if got := fork.Sequencer.ChainID; got != test.checks.sequencerChainID { + t.Errorf("Sequencer.ChainID = %v, want %v", got, test.checks.sequencerChainID) + } + if got := fork.Sequencer.StartHeight; got != test.checks.sequencerStartHeight { + t.Errorf("Sequencer.StartHeight = %v, want %v", got, test.checks.sequencerStartHeight) + } + if got := fork.Celestia.ChainID; got != test.checks.celestiaChainID { + t.Errorf("Celestia.ChainID = %v, want %v", got, test.checks.celestiaChainID) + } + if got := fork.Celestia.StartHeight; got != test.checks.celestiaStartHeight { + t.Errorf("Celestia.StartHeight = %v, want %v", got, test.checks.celestiaStartHeight) + } + if got := fork.Celestia.HeightVariance; got != test.checks.celestiaHeightVariance { + t.Errorf("Celestia.HeightVariance = %v, want %v", got, test.checks.celestiaHeightVariance) + } + }) + } +}