Skip to content

Commit

Permalink
Add x/rollup genesis state (#289)
Browse files Browse the repository at this point in the history
Adds a genesis state to x/rollup to allow module specific params to be
configured.
  • Loading branch information
natebeauregard authored Nov 7, 2024
1 parent 562d332 commit a51efc6
Show file tree
Hide file tree
Showing 21 changed files with 953 additions and 134 deletions.
5 changes: 2 additions & 3 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package builder
import (
"context"
"fmt"
"math/big"
"slices"

abcitypes "github.com/cometbft/cometbft/abci/types"
Expand Down Expand Up @@ -379,11 +378,11 @@ func parseWithdrawalEventAttributes(withdrawalEvent *abcitypes.Event) (*crossdom
}
params.Value = value
case rolluptypes.AttributeKeyGasLimit:
gasLimitBz, err := hexutil.Decode(attr.Value)
gasLimit, err := hexutil.DecodeBig(attr.Value)
if err != nil {
return nil, fmt.Errorf("decode gas limit: %v", err)
}
params.GasLimit = new(big.Int).SetBytes(gasLimitBz)
params.GasLimit = gasLimit
case rolluptypes.AttributeKeyData:
data, err := hexutil.Decode(attr.Value)
if err != nil {
Expand Down
16 changes: 14 additions & 2 deletions builder/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package builder_test

import (
"context"
"encoding/json"
"fmt"
"math/big"
"testing"
Expand All @@ -28,6 +29,7 @@ import (
"github.com/polymerdao/monomer/testutils"
"github.com/polymerdao/monomer/x/rollup/types"
"github.com/stretchr/testify/require"
"github.com/tidwall/sjson"
)

type testEnvironment struct {
Expand Down Expand Up @@ -363,7 +365,10 @@ func TestBuildRollupTxs(t *testing.T) {
require.NotNil(t, withdrawalTxResult)
require.Truef(t, withdrawalTxResult.Result.IsOK(), "User withdrawal transaction not successful")

// TODO: Check fee withdrawal transaction result once a genesis state for the minimum fee withdrawal amount is added
feeWithdrawalTxResult, err := env.txStore.Get(bfttypes.Tx(feeWithdrawalTx).Hash())
require.NoError(t, err)
require.NotNil(t, feeWithdrawalTxResult)
require.Truef(t, feeWithdrawalTxResult.Result.IsOK(), "Fee withdrawal transaction not successful")

// Verify block creation
genesisBlock, err := env.blockStore.BlockByHeight(uint64(preBuildInfo.GetLastBlockHeight()))
Expand Down Expand Up @@ -414,9 +419,16 @@ func setupTestEnvironment(t *testing.T) testEnvironment {
ethstatedb := testutils.NewEthStateDB(t)

app := testapp.NewTest(t, chainID.String())
appState := testapp.MakeGenesisAppState(t, app)

// Set min_fee_withdrawal_amount to 0 for testing purposes since fees will not be collected in the test environment.
rollupAppState, err := sjson.Set(string(appState[types.ModuleName]), "params.min_fee_withdrawal_amount", "0")
require.NoError(t, err)
appState[types.ModuleName] = json.RawMessage(rollupAppState)

g := &genesis.Genesis{
ChainID: chainID,
AppState: testapp.MakeGenesisAppState(t, app),
AppState: appState,
}

eventBus := bfttypes.NewEventBus()
Expand Down
Binary file modified cmd/monogen/testapp.zip
Binary file not shown.
1 change: 1 addition & 0 deletions cmd/monogen/testapp/app/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var (
stakingtypes.ModuleName,
genutiltypes.ModuleName,
// chain modules
rolluptypes.ModuleName,
// this line is used by starport scaffolding # stargate/app/initGenesis,
}

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/tidwall/sjson v1.2.5
go.uber.org/mock v0.4.0
golang.org/x/crypto v0.25.0
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
Expand Down Expand Up @@ -292,6 +293,9 @@ require (
github.com/tdewolff/parse/v2 v2.7.8 // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/gjson v1.14.2 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/tyler-smith/go-bip39 v1.1.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,14 @@ github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2l
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
Expand Down
14 changes: 14 additions & 0 deletions proto/rollup/v1/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
syntax = "proto3";
package rollup.v1;

import "gogoproto/gogo.proto";
import "amino/amino.proto";
import "rollup/v1/rollup.proto";

option go_package = "github.com/polymerdao/monomer/x/rollup/types";

// GenesisState defines the rollup module's genesis state.
message GenesisState {
// params defines all the parameters of the module.
Params params = 1 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true];
}
12 changes: 12 additions & 0 deletions proto/rollup/v1/l1blockinfo.proto → proto/rollup/v1/rollup.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import "gogoproto/gogo.proto";

option go_package = "github.com/polymerdao/monomer/x/rollup/types";

// Params defines the genesis parameters for the rollup module.
message Params {
// L1 address to forward the L2 fees to.
string l1_fee_recipient = 1;
// L1 address of the cross-domain messenger contract.
string l1_cross_domain_messenger = 2;
// Minimum amount of L2 fees that the FeeCollector account must have before they can be withdrawn.
uint64 min_fee_withdrawal_amount = 3;
// L1 gas limit for withdrawing fees to the L1 recipient address.
uint64 fee_withdrawal_gas_limit = 4;
}

// L1BlockInfo represents information about an L1 block and associated L2 data.
message L1BlockInfo {
uint64 number = 1;
Expand Down
1 change: 0 additions & 1 deletion testutils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ func GenerateERC20DepositTx(t *testing.T, tokenAddr, userAddr common.Address, am

to := testutils.RandomAddress(rng)
depositTx := &gethtypes.DepositTx{
// TODO: remove hardcoded address once a genesis state is configured
// L2 aliased L1CrossDomainMessenger proxy address
From: crossdomain.ApplyL1ToL2Alias(common.HexToAddress("0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE")),
To: &to,
Expand Down
10 changes: 7 additions & 3 deletions x/rollup/keeper/deposits.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func (k *Keeper) setL1BlockInfo(ctx sdk.Context, info types.L1BlockInfo) error {
if err != nil {
return types.WrapError(err, "marshal L1 block info")
}
if err = k.storeService.OpenKVStore(ctx).Set([]byte(types.KeyL1BlockInfo), infoBytes); err != nil {
if err = k.storeService.OpenKVStore(ctx).Set([]byte(types.L1BlockInfoKey), infoBytes); err != nil {
return types.WrapError(err, "set latest L1 block info")
}
return nil
Expand Down Expand Up @@ -130,9 +130,13 @@ func (k *Keeper) processL1UserDepositTxs(
}
mintEvents = append(mintEvents, *mintEvent)

// TODO: remove hardcoded address once a genesis state is configured
params, err := k.GetParams(ctx)
if err != nil {
return nil, types.WrapError(types.ErrInitiateFeeWithdrawal, "failed to get params: %v", err)
}

// Convert the L1CrossDomainMessenger address to its L2 aliased address
aliasedL1CrossDomainMessengerAddress := crossdomain.ApplyL1ToL2Alias(common.HexToAddress("0x9A9f2CCfdE556A7E9Ff0848998Aa4a0CFD8863AE"))
aliasedL1CrossDomainMessengerAddress := crossdomain.ApplyL1ToL2Alias(common.HexToAddress(params.L1CrossDomainMessenger))

// Check if the tx is a cross domain message from the aliased L1CrossDomainMessenger address
if from == aliasedL1CrossDomainMessengerAddress && tx.Data() != nil {
Expand Down
25 changes: 25 additions & 0 deletions x/rollup/keeper/genesis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package keeper

import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/polymerdao/monomer/x/rollup/types"
)

// InitGenesis - Init store state from genesis data
func (k *Keeper) InitGenesis(ctx sdk.Context, state types.GenesisState) { //nolint:gocritic // hugeParam
if err := k.SetParams(ctx, &state.Params); err != nil {
panic(fmt.Errorf("failed to set params: %w", err))
}
}

// ExportGenesis returns a GenesisState for a given context and keeper
func (k *Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { //nolint:gocritic // hugeParam
params, err := k.GetParams(ctx)
if err != nil {
panic(fmt.Errorf("failed to get params: %w", err))
}

return types.NewGenesisState(*params)
}
26 changes: 25 additions & 1 deletion x/rollup/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (k *Keeper) EmitEvents(goCtx context.Context, events sdk.Events) {
}

func (k *Keeper) GetL1BlockInfo(ctx sdk.Context) (*types.L1BlockInfo, error) { //nolint:gocritic // hugeParam
l1BlockInfoBz, err := k.storeService.OpenKVStore(ctx).Get([]byte(types.KeyL1BlockInfo))
l1BlockInfoBz, err := k.storeService.OpenKVStore(ctx).Get([]byte(types.L1BlockInfoKey))
if err != nil {
return nil, fmt.Errorf("get l1 block info: %w", err)
}
Expand All @@ -60,3 +60,27 @@ func (k *Keeper) GetL1BlockInfo(ctx sdk.Context) (*types.L1BlockInfo, error) { /
}
return &l1BlockInfo, nil
}

func (k *Keeper) GetParams(ctx sdk.Context) (*types.Params, error) { //nolint:gocritic // hugeParam
paramsBz, err := k.storeService.OpenKVStore(ctx).Get([]byte(types.ParamsKey))
if err != nil {
return nil, fmt.Errorf("get params: %w", err)
}
var params types.Params
if err = params.Unmarshal(paramsBz); err != nil {
return nil, fmt.Errorf("unmarshal params: %w", err)
}
return &params, nil
}

func (k *Keeper) SetParams(ctx sdk.Context, params *types.Params) error { //nolint:gocritic // hugeParam
paramsBz, err := params.Marshal()
if err != nil {
return fmt.Errorf("marshal params: %w", err)
}
err = k.storeService.OpenKVStore(ctx).Set([]byte(types.ParamsKey), paramsBz)
if err != nil {
return fmt.Errorf("set params: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion x/rollup/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *KeeperTestSuite) mockMintETH() {
func (s *KeeperTestSuite) mockFeeCollector() {
mockFeeCollectorAddress := sdk.AccAddress("fee_collector")
s.accountKeeper.EXPECT().GetModuleAddress(authtypes.FeeCollectorName).Return(mockFeeCollectorAddress).AnyTimes()
s.bankKeeper.EXPECT().GetBalance(s.ctx, mockFeeCollectorAddress, types.WEI).Return(sdk.NewCoin(types.WEI, math.NewInt(100_000))).AnyTimes()
s.bankKeeper.EXPECT().GetBalance(s.ctx, mockFeeCollectorAddress, types.WEI).Return(sdk.NewCoin(types.WEI, math.NewInt(1_000_000))).AnyTimes()
s.bankKeeper.EXPECT().SendCoinsFromModuleToModule(s.ctx, authtypes.FeeCollectorName, types.ModuleName, gomock.Any()).Return(nil).AnyTimes()
s.bankKeeper.EXPECT().BurnCoins(s.ctx, types.ModuleName, gomock.Any()).Return(nil).AnyTimes()
}
21 changes: 11 additions & 10 deletions x/rollup/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (k *Keeper) InitiateWithdrawal(
sdk.NewAttribute(types.AttributeKeySender, msg.Sender),
sdk.NewAttribute(types.AttributeKeyL1Target, msg.Target),
sdk.NewAttribute(types.AttributeKeyValue, withdrawalValueHex),
sdk.NewAttribute(types.AttributeKeyGasLimit, hexutil.Encode(msg.GasLimit)),
sdk.NewAttribute(types.AttributeKeyGasLimit, hexutil.EncodeBig(new(big.Int).SetBytes(msg.GasLimit))),
sdk.NewAttribute(types.AttributeKeyData, hexutil.Encode(msg.Data)),
// The nonce attribute will be set by Monomer
),
Expand All @@ -88,23 +88,24 @@ func (k *Keeper) InitiateFeeWithdrawal(
) (*types.MsgInitiateFeeWithdrawalResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// TODO: make minWithdrawalAmount and l1recipientAddr configurable once a genesis state is added
const (
minWithdrawalAmount = 100
l1recipientAddr = "0x63d93aC6FA6B4021527e967ac3Eb29F2B3E52B96"
feeWithdrawalGasLimit = 400_000
)
params, err := k.GetParams(ctx)
if err != nil {
return nil, types.WrapError(types.ErrInitiateFeeWithdrawal, "failed to get params: %v", err)
}
l1recipientAddr := params.L1FeeRecipient

feeCollectorAddr := k.accountkeeper.GetModuleAddress(authtypes.FeeCollectorName)
if feeCollectorAddr == nil {
return nil, types.WrapError(types.ErrInitiateFeeWithdrawal, "failed to get fee collector address")
}

feeCollectorBalance := k.bankkeeper.GetBalance(ctx, feeCollectorAddr, types.WEI)
if feeCollectorBalance.Amount.LT(math.NewInt(minWithdrawalAmount)) {
if feeCollectorBalance.Amount.LT(math.NewIntFromUint64(params.MinFeeWithdrawalAmount)) {
return nil, types.WrapError(
types.ErrInitiateFeeWithdrawal,
"fee collector balance is below the minimum withdrawal amount: %v", feeCollectorBalance.String(),
"fee collector balance is below the minimum withdrawal amount: (balance: %v, min withdrawal amount: %v)",
feeCollectorBalance.String(),
params.MinFeeWithdrawalAmount,
)
}

Expand All @@ -131,7 +132,7 @@ func (k *Keeper) InitiateFeeWithdrawal(
sdk.NewAttribute(types.AttributeKeySender, feeCollectorAddrStr),
sdk.NewAttribute(types.AttributeKeyL1Target, l1recipientAddr),
sdk.NewAttribute(types.AttributeKeyValue, withdrawalValueHex),
sdk.NewAttribute(types.AttributeKeyGasLimit, hexutil.EncodeBig(big.NewInt(feeWithdrawalGasLimit))),
sdk.NewAttribute(types.AttributeKeyGasLimit, hexutil.EncodeBig(new(big.Int).SetUint64(params.FeeWithdrawalGasLimit))),
sdk.NewAttribute(types.AttributeKeyData, "0x"),
// The nonce attribute will be set by Monomer
),
Expand Down
6 changes: 5 additions & 1 deletion x/rollup/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (s *KeeperTestSuite) TestApplyL1Txs() {
// Verify that the l1 block info and l1 block history are saved to the store
expectedBlockInfo := eth.BlockToInfo(testutils.GenerateL1Block())

l1BlockInfoBz := s.rollupStore.Get([]byte(types.KeyL1BlockInfo))
l1BlockInfoBz := s.rollupStore.Get([]byte(types.L1BlockInfoKey))
s.Require().NotNil(l1BlockInfoBz)

l1BlockInfo := &types.L1BlockInfo{}
Expand Down Expand Up @@ -251,6 +251,10 @@ func (s *KeeperTestSuite) TestInitiateFeeWithdrawal() {
}
s.mockFeeCollector()

params := types.DefaultParams()
err := s.rollupKeeper.SetParams(sdk.UnwrapSDKContext(s.ctx), &params)
s.Require().NoError(err)

resp, err := s.rollupKeeper.InitiateFeeWithdrawal(s.ctx, &types.MsgInitiateFeeWithdrawal{
Sender: sdk.AccAddress("addr").String(),
})
Expand Down
Loading

0 comments on commit a51efc6

Please sign in to comment.