Skip to content

Commit

Permalink
fix(e2e): fix challenge e2e tests not working properly
Browse files Browse the repository at this point in the history
  • Loading branch information
seolaoh committed Mar 18, 2024
1 parent 646eb95 commit 8907e09
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 46 deletions.
71 changes: 67 additions & 4 deletions kroma-chain-ops/genesis/layer_one.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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)
}
Expand All @@ -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
}
24 changes: 11 additions & 13 deletions kroma-validator/guardian.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
6 changes: 4 additions & 2 deletions op-e2e/actions/l2_challenger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 7 additions & 5 deletions op-e2e/actions/l2_guardian.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
1 change: 1 addition & 0 deletions op-e2e/actions/l2_runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
22 changes: 12 additions & 10 deletions op-e2e/e2eutils/geth/geth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}

Expand Down
53 changes: 41 additions & 12 deletions op-e2e/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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()

Expand All @@ -1496,24 +1498,49 @@ 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)
require.NoError(t, err)

// 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
Expand All @@ -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)

Expand Down

0 comments on commit 8907e09

Please sign in to comment.