diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 8ace36596f..947fd32bb1 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -138,18 +138,8 @@ func (s *EthereumAPI) Syncing() (interface{}, error) { return false, nil } -type GetChainConfigResponse struct { - *params.ChainConfig - params.UpgradeConfig `json:"upgrades"` -} - -func (s *BlockChainAPI) GetChainConfig(ctx context.Context) GetChainConfigResponse { - config := s.b.ChainConfig() - resp := GetChainConfigResponse{ - ChainConfig: config, - UpgradeConfig: config.UpgradeConfig, - } - return resp +func (s *BlockChainAPI) GetChainConfig(ctx context.Context) *params.ChainConfigWithUpgradesMarshalled { + return s.b.ChainConfig().ToWithUpgradesMarshalled() } // TxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. diff --git a/params/config.go b/params/config.go index 1f2289f2a9..81067168f3 100644 --- a/params/config.go +++ b/params/config.go @@ -40,6 +40,8 @@ import ( "github.com/ethereum/go-ethereum/common" ) +const maxJSONLen = 64 * 1024 * 1024 // 64MB + var ( errNonGenesisForkByHeight = errors.New("subnet-evm only supports forking by height at the genesis block") @@ -211,6 +213,53 @@ func (c ChainConfig) MarshalJSON() ([]byte, error) { return json.Marshal(raw) } +type ChainConfigWithUpgradesMarshalled struct { + *ChainConfig + UpgradeConfig UpgradeConfig `json:"upgrades,omitempty"` +} + +// MarshalJSON implements json.Marshaler. This is a workaround for the fact that +// the embedded ChainConfig struct has a MarshalJSON method, which prevents +// the default JSON marshalling from working for UpgradeConfig. +// TODO: consider removing this method by allowing external tag for the embedded +// ChainConfig struct. +func (s *ChainConfigWithUpgradesMarshalled) MarshalJSON() ([]byte, error) { + // embed the ChainConfig struct into the response + chainConfigJSON, err := json.Marshal(s.ChainConfig) + if err != nil { + return nil, err + } + if len(chainConfigJSON) > maxJSONLen { + return nil, errors.New("value too large") + } + + type upgrades struct { + UpgradeConfig UpgradeConfig `json:"upgrades"` + } + + upgradeJSON, err := json.Marshal(upgrades{s.UpgradeConfig}) + if err != nil { + return nil, err + } + if len(upgradeJSON) > maxJSONLen { + return nil, errors.New("value too large") + } + + // merge the two JSON objects + mergedJSON := make([]byte, 0, len(chainConfigJSON)+len(upgradeJSON)+1) + mergedJSON = append(mergedJSON, chainConfigJSON[:len(chainConfigJSON)-1]...) + mergedJSON = append(mergedJSON, ',') + mergedJSON = append(mergedJSON, upgradeJSON[1:]...) + return mergedJSON, nil +} + +func (c *ChainConfig) ToWithUpgradesMarshalled() *ChainConfigWithUpgradesMarshalled { + return &ChainConfigWithUpgradesMarshalled{ + ChainConfig: c, + UpgradeConfig: c.UpgradeConfig, + } +} + // UpgradeConfig includes the following configs that may be specified in upgradeBytes: // - Timestamps that enable avalanche network upgrades, // - Enabling or disabling precompiles as network upgrades. diff --git a/params/config_test.go b/params/config_test.go index a407c81bae..1b4fadbdb3 100644 --- a/params/config_test.go +++ b/params/config_test.go @@ -34,6 +34,7 @@ import ( "github.com/ava-labs/subnet-evm/precompile/contracts/nativeminter" "github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager" + "github.com/ava-labs/subnet-evm/precompile/contracts/txallowlist" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" ) @@ -227,3 +228,20 @@ func TestActivePrecompiles(t *testing.T) { rules1 := config.AvalancheRules(common.Big0, common.Big1) require.False(t, rules1.IsPrecompileEnabled(nativeminter.Module.Address)) } + +func TestChainConfigMarshalWithUpgrades(t *testing.T) { + config := ChainConfigWithUpgradesMarshalled{ + ChainConfig: TestChainConfig, + UpgradeConfig: UpgradeConfig{ + PrecompileUpgrades: []PrecompileUpgrade{ + { + Config: txallowlist.NewConfig(big.NewInt(100), nil, nil), + }, + }, + }, + } + result, err := json.Marshal(&config) + require.NoError(t, err) + expectedJSON := `{"chainId":1,"feeConfig":{"gasLimit":8000000,"targetBlockRate":2,"minBaseFee":25000000000,"targetGas":15000000,"baseFeeChangeDenominator":36,"minBlockGasCost":0,"maxBlockGasCost":1000000,"blockGasCostStep":200000},"homesteadBlock":0,"eip150Block":0,"eip150Hash":"0x0000000000000000000000000000000000000000000000000000000000000000","eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":0,"subnetEVMTimestamp":0,"upgrades":{"precompileUpgrades":[{"txAllowListConfig":{"blockTimestamp":100}}]}}` + require.JSONEq(t, expectedJSON, string(result)) +}