diff --git a/kroma-chain-ops/genesis/layer_one.go b/kroma-chain-ops/genesis/layer_one.go index 6d791b809..28baf19c0 100644 --- a/kroma-chain-ops/genesis/layer_one.go +++ b/kroma-chain-ops/genesis/layer_one.go @@ -11,9 +11,10 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" gstate "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" - "github.com/kroma-network/kroma/kroma-bindings/bindings" "github.com/ethereum-optimism/optimism/op-chain-ops/state" + "github.com/kroma-network/kroma/kroma-bindings/bindings" ) var ( @@ -120,9 +121,10 @@ func PostProcessL1DeveloperGenesis(stateDB *state.MemoryStateDB, deployments *L1 slot := common.BigToHash(big.NewInt(int64(entry.Slot))) stateDB.SetState(deployments.KromaPortalProxy, slot, common.Hash{}) - log.Info("Post process update", "address", deployments.KromaPortalProxy, "slot", slot.Hex(), "value", common.Hash{}.Hex()) + log.Info("Post process update", "name", "KromaPortal", "address", deployments.KromaPortalProxy, "slot", slot.Hex(), "value", common.Hash{}.Hex()) - // [Kroma: START] Transfer ownership of SystemConfig to ProxyAdminOwner for test + // [Kroma: START] + // Transfer ownership of SystemConfig to ProxyAdminOwner for test if !stateDB.Exist(deployments.SystemConfigProxy) { return fmt.Errorf("sysCfg proxy doesn't exist at %s", deployments.SystemConfigProxy) } @@ -140,7 +142,68 @@ func PostProcessL1DeveloperGenesis(stateDB *state.MemoryStateDB, deployments *L1 val := stateDB.GetState(deployments.ProxyAdmin, common.BigToHash(common.Big0)) stateDB.SetState(deployments.SystemConfigProxy, slot, val) - log.Info("Post process update", "address", deployments.SystemConfigProxy, "slot", slot.Hex(), "value", val.Hex()) + log.Info("Post process update", "name", "SystemConfig", "address", deployments.SystemConfigProxy, "slot", slot.Hex(), "value", val.Hex()) + + // Change the key of _quorumNumeratorHistory in UpgradeGovernor to 1 which means that quorumNumerator has been set at L1 block number 1 for guardian test + if !stateDB.Exist(deployments.UpgradeGovernorProxy) { + return fmt.Errorf("upgardeGovernor proxy doesn't exist at %s", deployments.UpgradeGovernorProxy) + } + + layout, err = bindings.GetStorageLayout("UpgradeGovernor") + if err != nil { + return errors.New("failed to get storage layout for UpgradeGovernor") + } + + entry, err = layout.GetStorageLayoutEntry("_quorumNumeratorHistory") + if err != nil { + return errors.New("failed to get storage layout entry for UpgradeGovernor._quorumNumeratorHistory") + } + slot = common.BigToHash(big.NewInt(int64(entry.Slot))) + slot = crypto.Keccak256Hash(slot.Bytes()) + + beforeVal := stateDB.GetState(deployments.UpgradeGovernorProxy, slot) + checkpointVal := make([]byte, 28) + copy(checkpointVal, beforeVal[:28]) + checkpointKey := [4]byte{} + checkpointKey[3] = 0x01 + val = common.BytesToHash(append(checkpointVal, checkpointKey[:]...)) + + stateDB.SetState(deployments.UpgradeGovernorProxy, slot, val) + afterVal := stateDB.GetState(deployments.UpgradeGovernorProxy, slot) + log.Info("Post process update", "name", "UpgradeGovernor", "address", deployments.UpgradeGovernorProxy, "slot", slot.Hex(), "beforeVal", beforeVal.Hex(), "afterVal", afterVal.Hex()) + + // Change the keys of _totalCheckpoints in SecurityCouncilToken to 1 which means that tokens have been minted at L1 block number 1 for guardian test + if !stateDB.Exist(deployments.SecurityCouncilTokenProxy) { + return fmt.Errorf("securityCouncilToken proxy doesn't exist at %s", deployments.SecurityCouncilTokenProxy) + } + + layout, err = bindings.GetStorageLayout("SecurityCouncilToken") + if err != nil { + return errors.New("failed to get storage layout for SecurityCouncilToken") + } + + entry, err = layout.GetStorageLayoutEntry("_totalCheckpoints") + if err != nil { + return errors.New("failed to get storage layout entry for SecurityCouncilToken._totalCheckpoints") + } + slot = common.BigToHash(big.NewInt(int64(entry.Slot))) + startSlot := new(big.Int).SetBytes(crypto.Keccak256(slot.Bytes())) + + mintedNum := stateDB.GetState(deployments.SecurityCouncilTokenProxy, slot).Big().Uint64() + for i := 0; uint64(i) < mintedNum; i++ { + slot = common.BigToHash(new(big.Int).Add(startSlot, big.NewInt(int64(i)))) + + beforeVal = stateDB.GetState(deployments.SecurityCouncilTokenProxy, slot) + checkpointVal = make([]byte, 28) + copy(checkpointVal, beforeVal[:28]) + checkpointKey = [4]byte{} + checkpointKey[3] = 0x01 + val = common.BytesToHash(append(checkpointVal, checkpointKey[:]...)) + + stateDB.SetState(deployments.SecurityCouncilTokenProxy, slot, val) + afterVal = stateDB.GetState(deployments.SecurityCouncilTokenProxy, slot) + log.Info("Post process update", "name", "SecurityCouncilToken", "address", deployments.SecurityCouncilTokenProxy, "slot", slot.Hex(), "beforeVal", beforeVal.Hex(), "afterVal", afterVal.Hex()) + } // [Kroma: END] return nil } diff --git a/kroma-validator/guardian.go b/kroma-validator/guardian.go index e5bc7255a..fae63a10c 100644 --- a/kroma-validator/guardian.go +++ b/kroma-validator/guardian.go @@ -286,9 +286,7 @@ func (g *Guardian) inspectOutput(outputIndex, fromBlock, toBlock *big.Int) { return default: retry := func() bool { - cCtx, cCancel := context.WithTimeout(g.ctx, g.cfg.NetworkTimeout) - defer cCancel() - isFinalized, err := g.IsOutputFinalized(cCtx, outputIndex) + isFinalized, err := g.isOutputFinalized(g.ctx, outputIndex) if err != nil { g.log.Error("unable to check if the output is finalized or not", "err", err, "outputIndex", outputIndex) return true @@ -300,7 +298,7 @@ func (g *Guardian) inspectOutput(outputIndex, fromBlock, toBlock *big.Int) { return false } - cCtx, cCancel = context.WithTimeout(g.ctx, g.cfg.NetworkTimeout) + cCtx, cCancel := context.WithTimeout(g.ctx, g.cfg.NetworkTimeout) defer cCancel() isInCreationPeriod, err := g.colosseumContract.IsInCreationPeriod(optsutils.NewSimpleCallOpts(cCtx), outputIndex) if err != nil { @@ -416,7 +414,7 @@ func (g *Guardian) tryConfirmRequestValidationTx(event *bindings.SecurityCouncil return fmt.Errorf("failed to get output index after. (l2BlockNumber: %d): %w", event.L2BlockNumber.Int64(), err) } - needConfirm, err := g.checkConfirmCondition(event.TransactionId, outputIndex) + needConfirm, err := g.CheckConfirmCondition(g.ctx, event.TransactionId, outputIndex) if err != nil { return fmt.Errorf("failed to check confirm condition. (transactionId: %d): %w", event.TransactionId.Int64(), err) } @@ -448,7 +446,7 @@ func (g *Guardian) tryConfirmRequestValidationTx(event *bindings.SecurityCouncil } func (g *Guardian) tryConfirmRequestDeletionTx(event *bindings.SecurityCouncilDeletionRequested) error { - needConfirm, err := g.checkConfirmCondition(event.TransactionId, event.OutputIndex) + needConfirm, err := g.CheckConfirmCondition(g.ctx, event.TransactionId, event.OutputIndex) if err != nil { return fmt.Errorf("failed to check confirm condition. (transactionId: %d): %w", event.TransactionId.Int64(), err) } @@ -486,8 +484,8 @@ func (g *Guardian) tryConfirmRequestDeletionTx(event *bindings.SecurityCouncilDe return nil } -func (g *Guardian) checkConfirmCondition(transactionId *big.Int, outputIndex *big.Int) (bool, error) { - outputFinalized, err := g.IsOutputFinalized(g.ctx, outputIndex) +func (g *Guardian) CheckConfirmCondition(ctx context.Context, transactionId *big.Int, outputIndex *big.Int) (bool, error) { + outputFinalized, err := g.isOutputFinalized(ctx, outputIndex) if err != nil { return true, fmt.Errorf("failed to get if output is finalized. (outputIndex: %d): %w", outputIndex.Int64(), err) } @@ -496,7 +494,7 @@ func (g *Guardian) checkConfirmCondition(transactionId *big.Int, outputIndex *bi return false, nil } - isConfirmed, err := g.isTransactionConfirmed(transactionId) + isConfirmed, err := g.isTransactionConfirmed(ctx, transactionId) if err != nil { return true, fmt.Errorf("failed to get confirmation. (transactionId: %d): %w", transactionId.Int64(), err) } @@ -505,7 +503,7 @@ func (g *Guardian) checkConfirmCondition(transactionId *big.Int, outputIndex *bi return false, nil } - cCtx, cCancel := context.WithTimeout(g.ctx, g.cfg.NetworkTimeout) + cCtx, cCancel := context.WithTimeout(ctx, g.cfg.NetworkTimeout) defer cCancel() executionTx, err := g.securityCouncilContract.Transactions(optsutils.NewSimpleCallOpts(cCtx), transactionId) if err != nil { @@ -602,14 +600,14 @@ func (g *Guardian) getL2OutputIndexAfter(l2BlockNumber *big.Int) (*big.Int, erro return g.l2ooContract.GetL2OutputIndexAfter(optsutils.NewSimpleCallOpts(cCtx), l2BlockNumber) } -func (g *Guardian) IsOutputFinalized(ctx context.Context, outputIndex *big.Int) (bool, error) { +func (g *Guardian) isOutputFinalized(ctx context.Context, outputIndex *big.Int) (bool, error) { cCtx, cCancel := context.WithTimeout(ctx, g.cfg.NetworkTimeout) defer cCancel() return g.l2ooContract.IsFinalized(optsutils.NewSimpleCallOpts(cCtx), outputIndex) } -func (g *Guardian) isTransactionConfirmed(transactionId *big.Int) (bool, error) { - cCtx, cCancel := context.WithTimeout(g.ctx, g.cfg.NetworkTimeout) +func (g *Guardian) isTransactionConfirmed(ctx context.Context, transactionId *big.Int) (bool, error) { + cCtx, cCancel := context.WithTimeout(ctx, g.cfg.NetworkTimeout) defer cCancel() return g.securityCouncilContract.IsConfirmed(optsutils.NewSimpleCallOpts(cCtx), transactionId) } diff --git a/op-e2e/actions/l2_challenger_test.go b/op-e2e/actions/l2_challenger_test.go index 8ae1b8013..9effd27f2 100644 --- a/op-e2e/actions/l2_challenger_test.go +++ b/op-e2e/actions/l2_challenger_test.go @@ -328,11 +328,13 @@ interaction: outputDeleted := val.IsOutputDeleted(remoteOutput.OutputRoot) require.True(rt.t, outputDeleted, "output is not deleted") - // guardian validates deleted output by challenger is invalid after challenge is proven + // guardian validates deleted output by challenger is valid, so confirm the transaction to roll back the challenge + needConfirm := rt.guardian.ActCheckConfirmCondition(rt.t, rt.outputIndex, transactionId) + require.True(rt.t, needConfirm, "confirmation condition is not met") outputBlockNum := rt.outputOnL1.L2BlockNumber.Uint64() isEqual := rt.guardian.ActValidateL2Output(rt.t, rt.outputOnL1.OutputRoot, outputBlockNum) require.True(rt.t, isEqual, "deleted output is expected equal but actually not equal") - rt.txHash = rt.guardian.ActConfirmTransaction(rt.t, rt.outputIndex, transactionId) + rt.txHash = rt.guardian.ActConfirmTransaction(rt.t, transactionId) rt.IncludeL1Block(rt.guardian.address) break interaction default: diff --git a/op-e2e/actions/l2_guardian.go b/op-e2e/actions/l2_guardian.go index b13959aba..8301b5685 100644 --- a/op-e2e/actions/l2_guardian.go +++ b/op-e2e/actions/l2_guardian.go @@ -9,17 +9,19 @@ import ( "github.com/ethereum-optimism/optimism/op-service/eth" ) +func (v *L2Validator) ActCheckConfirmCondition(t Testing, outputIndex *big.Int, transactionId *big.Int) bool { + needConfirm, err := v.guardian.CheckConfirmCondition(t.Ctx(), transactionId, outputIndex) + require.NoError(t, err, "unable to check confirm condition") + return needConfirm +} + func (v *L2Validator) ActValidateL2Output(t Testing, outputRoot eth.Bytes32, l2BlockNumber uint64) bool { isEqual, err := v.guardian.ValidateL2Output(t.Ctx(), outputRoot, l2BlockNumber) require.NoError(t, err, "unable to validate l2Output") return isEqual } -func (v *L2Validator) ActConfirmTransaction(t Testing, outputIndex *big.Int, transactionId *big.Int) common.Hash { - outputFinalized, err := v.guardian.IsOutputFinalized(t.Ctx(), outputIndex) - require.NoError(t, err, "unable to get if output is finalized") - require.False(t, outputFinalized, "output is already finalized") - +func (v *L2Validator) ActConfirmTransaction(t Testing, transactionId *big.Int) common.Hash { tx, err := v.guardian.ConfirmTransaction(t.Ctx(), transactionId) require.NoError(t, err, "unable to confirm transaction") diff --git a/op-e2e/actions/l2_runtime.go b/op-e2e/actions/l2_runtime.go index 57d05b191..0f765e50d 100644 --- a/op-e2e/actions/l2_runtime.go +++ b/op-e2e/actions/l2_runtime.go @@ -59,6 +59,7 @@ type Runtime struct { type SetupSequencerTestFunc = func(t Testing, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Sequencer) +// defaultRuntime is currently only used for l2_challenger_test func defaultRuntime(gt *testing.T, setupSequencerTest SetupSequencerTestFunc) Runtime { t := NewDefaultTesting(gt) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) diff --git a/op-e2e/e2eutils/geth/geth.go b/op-e2e/e2eutils/geth/geth.go index fc8660acf..2eb24f389 100644 --- a/op-e2e/e2eutils/geth/geth.go +++ b/op-e2e/e2eutils/geth/geth.go @@ -67,16 +67,18 @@ func InitL1(chainID uint64, blockTime uint64, genesis *core.Genesis, c clock.Clo func defaultNodeConfig(name string, jwtPath string) *node.Config { return &node.Config{ - Name: name, - WSHost: "127.0.0.1", - WSPort: 0, - AuthAddr: "127.0.0.1", - AuthPort: 0, - HTTPHost: "127.0.0.1", - HTTPPort: 0, - WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, - HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, - JWTSecret: jwtPath, + Name: name, + WSHost: "127.0.0.1", + WSPort: 0, + AuthAddr: "127.0.0.1", + AuthPort: 0, + HTTPHost: "127.0.0.1", + HTTPPort: 0, + // [Kroma: START] + WSModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "kroma"}, + HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine", "kroma"}, + // [Kroma: END] + JWTSecret: jwtPath, } } diff --git a/op-e2e/system_test.go b/op-e2e/system_test.go index 3ef39d2aa..d39cbbcb8 100644 --- a/op-e2e/system_test.go +++ b/op-e2e/system_test.go @@ -28,8 +28,6 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/exp/slices" - "github.com/kroma-network/kroma/kroma-bindings/bindings" - "github.com/kroma-network/kroma/kroma-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-e2e/config" gethutils "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/geth" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/transactions" @@ -45,6 +43,8 @@ import ( "github.com/ethereum-optimism/optimism/op-service/retry" "github.com/ethereum-optimism/optimism/op-service/sources" "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/kroma-network/kroma/kroma-bindings/bindings" + "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" @@ -1469,10 +1469,12 @@ func TestChallenge(t *testing.T) { // SecurityCouncil is already deployed securityCouncil, err := bindings.NewSecurityCouncilCaller(cfg.L1Deployments.SecurityCouncilProxy, l1Client) require.NoError(t, err) + securityCouncilABI, err := bindings.SecurityCouncilMetaData.GetAbi() + require.NoError(t, err) targetOutputOracleIndex := uint64(math.Ceil(float64(testdata.TargetBlockNumber) / float64(cfg.DeployConfig.L2OutputOracleSubmissionInterval))) - // set a timeout for one cycle of challenge + // set a timeout for waiting READY_TO_PROVE of challenge ctx, cancel := context.WithTimeout(context.Background(), 200*time.Second) defer cancel() @@ -1496,14 +1498,37 @@ func TestChallenge(t *testing.T) { } cancel() - // set a timeout for security council to validate output - ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) + findTransactionId := func() *big.Int { + validationRequestedEvent := securityCouncilABI.Events["ValidationRequested"] + eventIDTopic := []common.Hash{validationRequestedEvent.ID} + toBlock := latestBlock(t, l1Client) + + query := ethereum.FilterQuery{ + ToBlock: big.NewInt(int64(toBlock)), + Addresses: []common.Address{cfg.L1Deployments.SecurityCouncilProxy}, + Topics: [][]common.Hash{eventIDTopic}, + } + + cCtx, cCancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cCancel() + logs, err := l1Client.FilterLogs(cCtx, query) + require.NoError(t, err) + require.Equal(t, len(logs), 1) + + return new(big.Int).SetBytes(logs[0].Topics[1][:]) + } + + // set a timeout for security council to validate output (provingTimeout + buffer) + provingTimeout, err := colosseum.PROVINGTIMEOUT(&bind.CallOpts{}) + ctx, cancel = context.WithTimeout(context.Background(), time.Duration(provingTimeout.Uint64()+5)*time.Second) defer cancel() + numCheck := 0 for ; ; <-ticker.C { select { case <-ctx.Done(): - // after security council timed out, the challenge is regarded to be correct + // after enough time for security council elapsed, the challenge is regarded to be correct + require.True(t, numCheck >= 5, "at least 5 sec should be elapsed after challenge succeed") return default: challengeStatus, err := colosseum.GetStatus(&bind.CallOpts{}, new(big.Int).SetUint64(targetOutputOracleIndex), cfg.Secrets.Addresses().Challenger1) @@ -1511,9 +1536,11 @@ func TestChallenge(t *testing.T) { // after challenge is proven, status is NONE if challengeStatus == chal.StatusNone { - // check tx not executed - tx, err := securityCouncil.Transactions(&bind.CallOpts{}, new(big.Int).SetUint64(0)) + // check validation request tx exists and not executed + transactionId := findTransactionId() + tx, err := securityCouncil.Transactions(&bind.CallOpts{}, transactionId) require.NoError(t, err) + require.NotEqual(t, tx.Target, common.Address{}) require.False(t, tx.Executed) // check output is deleted by challenger @@ -1522,15 +1549,17 @@ func TestChallenge(t *testing.T) { require.Equal(t, output.Submitter, cfg.Secrets.Addresses().Challenger1) outputDeleted := val.IsOutputDeleted(output.OutputRoot) require.True(t, outputDeleted) + + numCheck++ + + if numCheck >= 5 { + return + } } } } } -func safeAddBig(a *big.Int, b *big.Int) *big.Int { - return new(big.Int).Add(a, b) -} - func TestBatcherMultiTx(t *testing.T) { InitParallel(t)