diff --git a/op-conductor/flags/flags_test.go b/op-conductor/flags/flags_test.go index 1eb5bf423..812d9afb6 100644 --- a/op-conductor/flags/flags_test.go +++ b/op-conductor/flags/flags_test.go @@ -81,7 +81,7 @@ func TestEnvVarFormat(t *testing.T) { envFlags := envFlagGetter.GetEnvVars() require.True(t, ok, "must be able to cast the flag to an EnvVar interface") require.Equal(t, 1, len(envFlags), "flags should have exactly one env var") - expectedEnvVar := opservice.FlagNameToEnvVarName(flagName, "CONDUCTOR") + expectedEnvVar := opservice.FlagNameToEnvVarName(flagName, "OP_CONDUCTOR") require.Equal(t, expectedEnvVar, envFlags[0]) }) } diff --git a/op-e2e/actions/mint_token_test.go b/op-e2e/actions/mint_token_test.go new file mode 100644 index 000000000..10ce9a90c --- /dev/null +++ b/op-e2e/actions/mint_token_test.go @@ -0,0 +1,315 @@ +package actions + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils" + "github.com/ethereum-optimism/optimism/op-node/rollup/derive" + "github.com/ethereum-optimism/optimism/op-node/rollup/sync" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/kroma-network/kroma/kroma-bindings/bindings" + "github.com/kroma-network/kroma/kroma-bindings/predeploys" +) + +var floorUnit = big.NewInt(1e18 / 1e8) + +func TestMintToken(t *testing.T) { + tests := []struct { + name string + f func(gt *testing.T) + }{ + {"BeforeActivation", BeforeActivation}, + {"ActivatedAtGenesis", ActivatedAtGenesis}, + {"ActivatedAfterGenesis", ActivatedAfterGenesis}, + {"UntilExhausted", UntilExhausted}, + } + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + test.f(t) + }) + } +} + +func setupTestEnv(t StatefulTesting, dp *e2eutils.DeployParams) (*L1Miner, *L2Sequencer, *L2Verifier, *ethclient.Client, *L2Batcher) { + sd := e2eutils.Setup(t, dp, defaultAlloc) + log := testlog.Logger(t, log.LvlDebug) + + miner, seqEngine, seq := setupSequencerTest(t, sd, log) + verifEngine, verifier := setupVerifier(t, sd, log, miner.L1Client(t, sd.RollupCfg), miner.BlobStore(), &sync.Config{}) + rollupSeqCl := seq.RollupClient() + verifCl := verifEngine.EthClient() + + batcher := NewL2Batcher(log, sd.RollupCfg, DefaultBatcherCfg(dp), rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg)) + + seq.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + return miner, seq, verifier, verifCl, batcher +} + +func BeforeActivation(gt *testing.T) { + t := NewDefaultTesting(gt) + + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + dp.DeployConfig.MintManagerMintActivatedBlock = nil + + miner, seq, verifier, verifCl, batcher := setupTestEnv(t, dp) + + miner.ActEmptyBlock(t) + seq.ActL1HeadSignal(t) + seq.ActBuildToL1Head(t) + batcher.ActSubmitAll(t) + + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) + miner.ActL1EndBlock(t) + + verifier.ActL2PipelineFull(t) + + infoTx, err := verifCl.TransactionInBlock(t.Ctx(), verifier.L2Safe().Hash, 0) + require.NoError(t, err) + receipt, err := verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + mintEvents, err := parseMintEvents(receipt, verifCl) + require.NoError(t, err) + require.Zero(t, len(mintEvents), "mint event exists before activation") +} + +func ActivatedAtGenesis(gt *testing.T) { + t := NewDefaultTesting(gt) + + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + dp.DeployConfig.MintManagerMintActivatedBlock = (*hexutil.Big)(new(big.Int).SetUint64(0)) + + miner, seq, verifier, verifCl, batcher := setupTestEnv(t, dp) + + miner.ActEmptyBlock(t) + seq.ActL1HeadSignal(t) + seq.ActBuildToL1Head(t) + batcher.ActSubmitAll(t) + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) + miner.ActL1EndBlock(t) + verifier.ActL2PipelineFull(t) + + infoTx, err := verifCl.TransactionInBlock(t.Ctx(), verifier.L2Safe().Hash, 0) + require.NoError(t, err) + receipt, err := verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + mintEvents, err := parseMintEvents(receipt, verifCl) + require.NoError(t, err) + require.Equal(t, len(mintEvents), len(dp.DeployConfig.MintManagerRecipients)) + + epoch, _ := epochAndOffset(dp, receipt.BlockNumber) + mintAmount := mintAmountPerBlock(dp, epoch) + validateMint(t, dp, mintEvents, mintAmount) +} + +func ActivatedAfterGenesis(gt *testing.T) { + t := NewDefaultTesting(gt) + + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + offset := uint64(24) + dp.DeployConfig.MintManagerMintActivatedBlock = (*hexutil.Big)(new(big.Int).SetUint64(offset)) + + miner, seq, verifier, verifCl, batcher := setupTestEnv(t, dp) + + // Build the L1 chain to reach the activation block. + miner.ActEmptyBlock(t) + for i := 0; i < 2; i++ { + seq.ActL1HeadSignal(t) + seq.ActBuildToL1Head(t) + batcher.ActSubmitAll(t) + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) + miner.ActL1EndBlock(t) + } + verifier.ActL2PipelineFull(t) + + infoTx, err := verifCl.TransactionInBlock(t.Ctx(), verifier.L2Safe().Hash, 0) + require.NoError(t, err) + receipt, err := verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + mintEvents, err := parseMintEvents(receipt, verifCl) + require.NoError(t, err) + require.Equal(t, len(mintEvents), len(dp.DeployConfig.MintManagerRecipients)) + + // Check the initial issuance was minted correctly. + epoch, offset := epochAndOffset(dp, receipt.BlockNumber) + mintPerBlock := new(big.Int).Set(dp.DeployConfig.MintManagerInitMintPerBlock.ToInt()) + mintAmount := new(big.Int).Set(common.Big0) + for i := uint64(1); i < epoch; i++ { + mintPerBlock = mintAmountPerBlock(dp, epoch) + blocks := new(big.Int).SetUint64(dp.DeployConfig.MintManagerSlidingWindowBlocks) + mintAmount.Add(mintAmount, new(big.Int).Mul(mintPerBlock, blocks)) + } + if offset > 0 { + blocks := new(big.Int).SetUint64(offset) + mintAmount.Add(mintAmount, new(big.Int).Mul(mintPerBlock, blocks)) + } + validateMint(t, dp, mintEvents, mintAmount) + + // Build the L1 chain to verify that tokens are minted correctly after the initial issuance and with each block. + miner.ActEmptyBlock(t) + seq.ActL1HeadSignal(t) + seq.ActBuildToL1Head(t) + batcher.ActSubmitAll(t) + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) + miner.ActL1EndBlock(t) + verifier.ActL2PipelineFull(t) + + infoTx, err = verifCl.TransactionInBlock(t.Ctx(), verifier.L2Safe().Hash, 0) + require.NoError(t, err) + receipt, err = verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + mintEvents, err = parseMintEvents(receipt, verifCl) + require.NoError(t, err) + require.Equal(t, len(mintEvents), len(dp.DeployConfig.MintManagerRecipients)) + + epoch, _ = epochAndOffset(dp, receipt.BlockNumber) + mintAmount = mintAmountPerBlock(dp, epoch) + validateMint(t, dp, mintEvents, mintAmount) +} + +func UntilExhausted(gt *testing.T) { + t := NewDefaultTesting(gt) + + recipients := make([]common.Address, 20) + shares := make([]uint64, len(recipients)) + for i := range recipients { + recipients[i] = common.Address{0: 0xff, 19: byte(i + 1)} + shares[i] = uint64(100000 / len(recipients)) + } + + dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) + dp.DeployConfig.MintManagerMintActivatedBlock = (*hexutil.Big)(new(big.Int).SetUint64(0)) + dp.DeployConfig.MintManagerSlidingWindowBlocks = 1 + dp.DeployConfig.MintManagerDecayingFactor = 90000 + dp.DeployConfig.MintManagerRecipients = recipients + dp.DeployConfig.MintManagerShares = shares + + miner, seq, verifier, verifCl, batcher := setupTestEnv(t, dp) + + mintManager, err := bindings.NewMintManagerCaller(predeploys.MintManagerAddr, verifCl) + require.NoError(t, err) + + exhaustedAt := uint64(1) + for { + epoch, _ := epochAndOffset(dp, new(big.Int).SetUint64(exhaustedAt)) + mintAmount := mintAmountPerBlock(dp, epoch) + if mintAmount.Uint64() == 0 { + exhaustedAt-- + break + } + exhaustedAt++ + require.Less(gt, exhaustedAt, uint64(500), "exhausting block number is too large to test") + } + + l1BlockNumber := exhaustedAt * dp.DeployConfig.L2BlockTime / dp.DeployConfig.L1BlockTime + for i := uint64(1); i < l1BlockNumber; i++ { + miner.ActEmptyBlock(t) + seq.ActL1HeadSignal(t) + seq.ActBuildToL1Head(t) + batcher.ActSubmitAll(t) + miner.ActL1StartBlock(12)(t) + miner.ActL1IncludeTx(dp.Addresses.Batcher)(t) + miner.ActL1EndBlock(t) + } + + seq.ActL2PipelineFull(t) + verifier.ActL2PipelineFull(t) + + // Ensure that the amount of tokens minted decreases over time, + // and verify that SystemTxGas is not exceeded as gas consumption increases as the epoch increases. + var prevMintAmount *big.Int + for i := uint64(1); i < exhaustedAt; i++ { + mintAmount, err := mintManager.MintAmountPerBlock(&bind.CallOpts{}, new(big.Int).SetUint64(i)) + require.NoError(t, err) + if i > 1 { + require.Equal(t, mintAmount.Cmp(prevMintAmount), -1) + } + prevMintAmount = mintAmount + + infoTx, err := verifCl.TransactionInBlock(t.Ctx(), seq.L2Unsafe().Hash, 0) + require.NoError(t, err) + receipt, err := verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful) + require.Less(t, receipt.GasUsed, uint64(derive.RegolithSystemTxGas)) + mintEvents, err := parseMintEvents(receipt, verifCl) + require.NoError(t, err) + validateMint(t, dp, mintEvents, mintAmount) + } + + // Ensure that the token is exhausted. + infoTx, err := verifCl.TransactionInBlock(t.Ctx(), seq.L2Unsafe().Hash, 0) + require.NoError(t, err) + receipt, err := verifCl.TransactionReceipt(t.Ctx(), infoTx.Hash()) + require.NoError(t, err) + require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful) + require.Less(t, receipt.GasUsed, uint64(derive.RegolithSystemTxGas)) + mintEvents, err := parseMintEvents(receipt, verifCl) + require.NoError(t, err) + require.Zero(t, len(mintEvents)) +} + +func parseMintEvents(receipt *types.Receipt, backend bind.ContractBackend) ([]*bindings.GovernanceTokenTransfer, error) { + governanceToken, err := bindings.NewGovernanceToken(predeploys.GovernanceTokenAddr, backend) + if err != nil { + return nil, err + } + + evts := make([]*bindings.GovernanceTokenTransfer, 0) + for _, l := range receipt.Logs { + evt, err := governanceToken.ParseTransfer(*l) + if err != nil || evt.From.Cmp(common.Address{}) != 0 { + continue + } + evts = append(evts, evt) + } + + return evts, nil +} + +func validateMint(t StatefulTesting, dp *e2eutils.DeployParams, events []*bindings.GovernanceTokenTransfer, amount *big.Int) { + for i, evt := range events { + expectedTo := dp.DeployConfig.MintManagerRecipients[i] + require.Equal(t, expectedTo, evt.To) + shares := new(big.Int).SetUint64(dp.DeployConfig.MintManagerShares[i]) + expectedValue := new(big.Int) + expectedValue.Mul(shares, amount) + expectedValue.Div(expectedValue, big.NewInt(1e5)) + require.Equal(t, expectedValue, evt.Value) + } +} + +func epochAndOffset(dp *e2eutils.DeployParams, blockNumber *big.Int) (uint64, uint64) { + epoch := (blockNumber.Uint64()-1)/dp.DeployConfig.MintManagerSlidingWindowBlocks + 1 + offset := (blockNumber.Uint64()-1)%dp.DeployConfig.MintManagerSlidingWindowBlocks + 1 + + return epoch, offset +} + +func mintAmountPerBlock(dp *e2eutils.DeployParams, epoch uint64) *big.Int { + decayingFactor := new(big.Int).SetUint64(dp.DeployConfig.MintManagerDecayingFactor) + decayingDenom := new(big.Int).SetUint64(1e5) + + amount := new(big.Int).Set(dp.DeployConfig.MintManagerInitMintPerBlock.ToInt()) + for i := uint64(1); i < epoch; i++ { + amount.Mul(amount, decayingFactor).Div(amount, decayingDenom) + amount.Div(amount, floorUnit).Mul(amount, floorUnit) + } + + return amount +} diff --git a/op-e2e/bridge_test.go b/op-e2e/bridge_test.go index ca8794377..6904eadda 100644 --- a/op-e2e/bridge_test.go +++ b/op-e2e/bridge_test.go @@ -5,8 +5,11 @@ import ( "math" "math/big" "testing" + "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" @@ -19,6 +22,7 @@ import ( "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/kroma-network/kroma/kroma-bindings/bindings" "github.com/kroma-network/kroma/kroma-bindings/predeploys" + "github.com/kroma-network/kroma/op-e2e/e2eutils/geth" ) // TestERC20BridgeDeposits tests the the L1StandardBridge bridge ERC20 @@ -110,3 +114,150 @@ func TestERC20BridgeDeposits(t *testing.T) { require.NoError(t, err) require.Equal(t, l2Balance, big.NewInt(100)) } + +// TestBridgeGovernanceToken tests the L1StandardBridge bridge GovernanceToken +// functionality. +func TestBridgeGovernanceToken(t *testing.T) { + InitParallel(t) + + cfg := DefaultSystemConfig(t) + cfg.DeployConfig.MintManagerMintActivatedBlock = (*hexutil.Big)(new(big.Int).SetUint64(0)) + cfg.DeployConfig.MintManagerRecipients = []common.Address{cfg.Secrets.Addresses().Alice} + cfg.DeployConfig.MintManagerShares = []uint64{100000} + + sys, err := cfg.Start(t) + require.Nil(t, err, "Error starting up system") + defer sys.Close() + + log := testlog.Logger(t, log.LvlInfo) + log.Info("genesis", "l2", sys.RollupConfig.Genesis.L2, "l1", sys.RollupConfig.Genesis.L1, "l2_time", sys.RollupConfig.Genesis.L2Time) + + l1Client := sys.Clients["l1"] + l2Client := sys.Clients["sequencer"] + + l1TokenAddr := sys.Cfg.L1Deployments.L1GovernanceTokenProxy + l1BridgeAddr := cfg.L1Deployments.L1StandardBridgeProxy + l2TokenAddr := predeploys.GovernanceTokenAddr + l2BridgeAddr := predeploys.L2StandardBridgeAddr + + l1Opts, err := bind.NewKeyedTransactorWithChainID(sys.Cfg.Secrets.Bob, cfg.L1ChainIDBig()) + require.Nil(t, err) + l2Opts, err := bind.NewKeyedTransactorWithChainID(sys.Cfg.Secrets.Bob, cfg.L2ChainIDBig()) + require.Nil(t, err) + + // Init bridge contracts + l1Bridge, err := bindings.NewL1StandardBridge(l1BridgeAddr, l1Client) + require.NoError(t, err) + l2Bridge, err := bindings.NewL2StandardBridge(l2BridgeAddr, l2Client) + require.NoError(t, err) + + l1Token, err := bindings.NewGovernanceToken(l1TokenAddr, l1Client) + require.NoError(t, err) + l2Token, err := bindings.NewGovernanceToken(l2TokenAddr, l2Client) + require.NoError(t, err) + + // Approve GovernanceToken with the bridge on L1 and L2 + tx, err := l1Token.Approve(l1Opts, l1BridgeAddr, new(big.Int).SetUint64(math.MaxUint64)) + require.NoError(t, err) + _, err = wait.ForReceiptOK(context.Background(), l1Client, tx.Hash()) + require.NoError(t, err) + tx, err = l2Token.Approve(l2Opts, l2BridgeAddr, new(big.Int).SetUint64(math.MaxUint64)) + require.NoError(t, err) + _, err = wait.ForReceiptOK(context.Background(), l2Client, tx.Hash()) + require.NoError(t, err) + + // Wait until Alice have enough tokens on L2 + _, err = geth.WaitForBlock(big.NewInt(10), l2Client, 20*time.Second) + require.NoError(t, err) + aliceL2Opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Alice, cfg.L2ChainIDBig()) + require.Nil(t, err) + bridgeAmount, err := l2Token.BalanceOf(&bind.CallOpts{}, aliceL2Opts.From) + require.NoError(t, err) + require.NotZero(t, bridgeAmount.Uint64()) + + // Send Alice's tokens to Bob + tx, err = transactions.PadGasEstimate(aliceL2Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { + return l2Token.Transfer(opts, l2Opts.From, bridgeAmount) + }) + require.NoError(t, err) + _, err = wait.ForReceiptOK(context.Background(), l2Client, tx.Hash()) + require.NoError(t, err) + + bobL1Balance, err := l1Token.BalanceOf(&bind.CallOpts{}, l1Opts.From) + require.NoError(t, err) + require.Zero(t, bobL1Balance.Uint64()) + bobL2Balance, err := l2Token.BalanceOf(&bind.CallOpts{}, l2Opts.From) + require.NoError(t, err) + require.Equal(t, bobL2Balance, bridgeAmount) + + l1Supply, err := l1Token.TotalSupply(&bind.CallOpts{}) + require.NoError(t, err) + + // Withdraw GovernanceToken to L1 + tx, err = transactions.PadGasEstimate(l2Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { + return l2Bridge.BridgeERC20(opts, l2TokenAddr, l1TokenAddr, bridgeAmount, 100000, []byte{}) + }) + require.NoError(t, err) + receipt, err := wait.ForReceiptOK(context.Background(), l2Client, tx.Hash()) + require.NoError(t, err) + proveReceipt, finalizeReceipt := ProveAndFinalizeWithdrawal(t, cfg, sys, "verifier", cfg.Secrets.Bob, receipt) + require.Equal(t, types.ReceiptStatusSuccessful, proveReceipt.Status) + require.Equal(t, types.ReceiptStatusSuccessful, finalizeReceipt.Status) + + // Withdrawal complete, Bob's token balance increases on L1. + newBobL1Balance, err := l1Token.BalanceOf(&bind.CallOpts{}, l1Opts.From) + require.NoError(t, err) + require.Equal(t, bobL1Balance.Add(bobL1Balance, bridgeAmount).Cmp(newBobL1Balance), 0) + // Bob's token balance decreases on L2. + newBobL2Balance, err := l2Token.BalanceOf(&bind.CallOpts{}, l2Opts.From) + require.NoError(t, err) + require.Equal(t, bobL2Balance.Sub(bobL2Balance, bridgeAmount).Cmp(newBobL2Balance), 0) + // Total supply increases on L1. + newL1Supply, err := l1Token.TotalSupply(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, l1Supply.Add(l1Supply, bridgeAmount).Cmp(newL1Supply), 0) + // It's difficult to verify the exact decrease in token total supply on L2, + // as the issuance amount increases with each block + + // Deposit GovernanceToken to L2 + tx, err = transactions.PadGasEstimate(l1Opts, 1.1, func(opts *bind.TransactOpts) (*types.Transaction, error) { + return l1Bridge.BridgeERC20(opts, l1TokenAddr, l2TokenAddr, bridgeAmount, 100000, []byte{}) + }) + require.NoError(t, err) + depositReceipt, err := wait.ForReceiptOK(context.Background(), l1Client, tx.Hash()) + require.NoError(t, err) + + t.Log("Deposit through L1StandardBridge", "gas used", depositReceipt.GasUsed) + + // compute the deposit transaction hash + poll for it + portal, err := bindings.NewKromaPortal(cfg.L1Deployments.KromaPortalProxy, l1Client) + require.NoError(t, err) + + depIt, err := portal.FilterTransactionDeposited(&bind.FilterOpts{Start: 0}, nil, nil, nil) + require.NoError(t, err) + var depositEvent *bindings.KromaPortalTransactionDeposited + for depIt.Next() { + depositEvent = depIt.Event + } + require.NotNil(t, depositEvent) + + depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw) + require.NoError(t, err) + _, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash()) + require.NoError(t, err) + + // Deposit complete, Bob's token balance decreases on L1. + newBobL1Balance, err = l1Token.BalanceOf(&bind.CallOpts{}, l1Opts.From) + require.NoError(t, err) + require.Equal(t, bobL1Balance.Sub(bobL1Balance, bridgeAmount).Cmp(newBobL1Balance), 0) + // Bob's token balance increases on L2. + newBobL2Balance, err = l2Token.BalanceOf(&bind.CallOpts{}, l2Opts.From) + require.NoError(t, err) + require.Equal(t, bobL2Balance.Add(bobL2Balance, bridgeAmount).Cmp(newBobL2Balance), 0) + // Total supply decreases on L1. + newL1Supply, err = l1Token.TotalSupply(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal(t, l1Supply.Sub(l1Supply, bridgeAmount).Cmp(newL1Supply), 0) + // It's difficult to verify the exact increase in token total supply on L2, + // as the issuance amount increases with each block +} diff --git a/op-e2e/setup.go b/op-e2e/setup.go index 9045b5eea..83dc7722c 100644 --- a/op-e2e/setup.go +++ b/op-e2e/setup.go @@ -39,6 +39,8 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" + "github.com/kroma-network/kroma/op-e2e/e2eutils/batcher" + bss "github.com/ethereum-optimism/optimism/op-batcher/batcher" batcherFlags "github.com/ethereum-optimism/optimism/op-batcher/flags" "github.com/ethereum-optimism/optimism/op-e2e/config" @@ -67,7 +69,6 @@ import ( "github.com/kroma-network/kroma/kroma-chain-ops/genesis" validator "github.com/kroma-network/kroma/kroma-validator" validatormetrics "github.com/kroma-network/kroma/kroma-validator/metrics" - "github.com/kroma-network/kroma/op-e2e/e2eutils/batcher" "github.com/kroma-network/kroma/op-e2e/testdata" "github.com/kroma-network/kroma/op-service/client" ) diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index ff72bbe24..2860d93e2 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -32,6 +32,7 @@ import ( gethutils "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/wait" + "github.com/ethereum-optimism/optimism/op-e2e/testdata" "github.com/ethereum-optimism/optimism/op-node/metrics" rollupNode "github.com/ethereum-optimism/optimism/op-node/node" "github.com/ethereum-optimism/optimism/op-node/p2p" @@ -47,7 +48,6 @@ import ( "github.com/kroma-network/kroma/kroma-bindings/predeploys" val "github.com/kroma-network/kroma/kroma-validator" chal "github.com/kroma-network/kroma/kroma-validator/challenge" - "github.com/kroma-network/kroma/op-e2e/testdata" ) // TestSystemBatchType run each system e2e test case in singular batch mode and span batch mode. @@ -931,6 +931,9 @@ func TestL1InfoContract(t *testing.T) { InitParallel(t) cfg := DefaultSystemConfig(t) + // [Kroma: START] + cfg.DeployConfig.MintManagerMintActivatedBlock = (*hexutil.Big)(big.NewInt(2)) + // [Kroma: END] sys, err := cfg.Start(t) require.Nil(t, err, "Error starting up system") @@ -1032,6 +1035,24 @@ func TestL1InfoContract(t *testing.T) { checkInfoList("On sequencer with state", l1InfosFromSequencerState) checkInfoList("On verifier with tx", l1InfosFromVerifierTransactions) checkInfoList("On verifier with state", l1InfosFromVerifierState) + + // [Kroma: START] + // Ensure that the L1Info contract successfully calls MintManager and the governance token is minted. + for i := uint64(1); i < endVerifBlock.NumberU64(); i++ { + block, err := l2Verif.BlockByNumber(ctx, new(big.Int).SetUint64(i)) + require.NoError(t, err) + require.NotZero(t, len(block.Transactions())) + l1InfoReceipt, err := l2Verif.TransactionReceipt(ctx, block.Transactions()[0].Hash()) + + // L1Info contracts don't emit any events. + // All events that emitted in this transaction are assumed to be `Transfer` events of the governance token. + if i >= cfg.DeployConfig.MintManagerMintActivatedBlock.ToInt().Uint64() { + require.Equal(t, len(l1InfoReceipt.Logs), len(cfg.DeployConfig.MintManagerRecipients)) + } else { + require.Zero(t, len(l1InfoReceipt.Logs)) + } + } + // [Kroma: END] } // calcGasFees determines the actual cost of the transaction given a specific base fee diff --git a/op-node/rollup/derive/ecotone_upgrade_transactions.go b/op-node/rollup/derive/ecotone_upgrade_transactions.go index d54d7143a..582035c65 100644 --- a/op-node/rollup/derive/ecotone_upgrade_transactions.go +++ b/op-node/rollup/derive/ecotone_upgrade_transactions.go @@ -53,10 +53,9 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { Data: l1BlockDeploymentBytecode, // [Kroma: START] Gas: 500_000, - //IsSystemTransaction: false, + // IsSystemTransaction: false, // [Kroma: END] }).MarshalBinary() - if err != nil { return nil, err } @@ -75,7 +74,6 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { [Kroma: END] */ Data: gasPriceOracleDeploymentBytecode, }).MarshalBinary() - if err != nil { return nil, err } @@ -94,7 +92,6 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { [Kroma: END] */ Data: upgradeToCalldata(newL1BlockAddress), }).MarshalBinary() - if err != nil { return nil, err } @@ -113,7 +110,6 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { [Kroma: END] */ Data: upgradeToCalldata(newGasPriceOracleAddress), }).MarshalBinary() - if err != nil { return nil, err } @@ -149,7 +145,6 @@ func EcotoneNetworkUpgradeTransactions() ([]hexutil.Bytes, error) { [Kroma: END] */ SourceHash: beaconRootsSource.SourceHash(), }).MarshalBinary() - if err != nil { return nil, err } diff --git a/op-node/rollup/derive/ecotone_upgrade_transactions_test.go b/op-node/rollup/derive/ecotone_upgrade_transactions_test.go index 8a33ea586..418058464 100644 --- a/op-node/rollup/derive/ecotone_upgrade_transactions_test.go +++ b/op-node/rollup/derive/ecotone_upgrade_transactions_test.go @@ -9,8 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" - - "github.com/kroma-network/kroma/kroma-bindings/bindings" ) func TestSourcesMatchSpec(t *testing.T) { @@ -73,14 +71,14 @@ func TestEcotoneNetworkTransactions(t *testing.T) { require.Equal(t, deployL1BlockSource.SourceHash(), deployL1Block.SourceHash()) require.Nil(t, deployL1Block.To()) require.Equal(t, uint64(500_000), deployL1Block.Gas()) - require.Equal(t, bindings.L1BlockMetaData.Bin, hexutil.Bytes(deployL1Block.Data()).String()) + require.Equal(t, hexutil.Bytes(l1BlockDeploymentBytecode).String(), hexutil.Bytes(deployL1Block.Data()).String()) deployGasPriceOracleSender, deployGasPriceOracle := toDepositTxn(t, upgradeTxns[1]) require.Equal(t, deployGasPriceOracleSender, common.HexToAddress("0x4210000000000000000000000000000000000001")) require.Equal(t, deployGasPriceOracleSource.SourceHash(), deployGasPriceOracle.SourceHash()) require.Nil(t, deployGasPriceOracle.To()) require.Equal(t, uint64(1_000_000), deployGasPriceOracle.Gas()) - require.Equal(t, bindings.GasPriceOracleMetaData.Bin, hexutil.Bytes(deployGasPriceOracle.Data()).String()) + require.Equal(t, hexutil.Bytes(gasPriceOracleDeploymentBytecode).String(), hexutil.Bytes(deployGasPriceOracle.Data()).String()) updateL1BlockProxySender, updateL1BlockProxy := toDepositTxn(t, upgradeTxns[2]) require.Equal(t, updateL1BlockProxySender, common.Address{})