Skip to content

Commit

Permalink
feat(e2e): add action tests for new validator system
Browse files Browse the repository at this point in the history
  • Loading branch information
sm-stack committed Apr 18, 2024
1 parent c118d2b commit 8c95626
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 45 deletions.
1 change: 0 additions & 1 deletion kroma-chain-ops/genesis/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,6 @@ func NewL2StorageConfig(config *DeployConfig, block *types.Block) (state.Storage
storage["GovernanceToken"] = state.StorageValues{
"_name": config.GovernanceTokenName,
"_symbol": config.GovernanceTokenSymbol,
"_owner": config.GovernanceTokenOwner,
}
}
storage["ProxyAdmin"] = state.StorageValues{
Expand Down
6 changes: 3 additions & 3 deletions op-e2e/actions/l2_challenger.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ func (v *L2Validator) ActCreateChallenge(t Testing, outputIndex *big.Int) common
return status == chal.StatusNone || status == chal.StatusChallengerTimeout
}, "challenge is already in progress")

hasEnoughDeposit, err := v.challenger.HasEnoughDeposit(t.Ctx())
require.NoError(t, err, "unable to check challenger deposit")
require.True(t, hasEnoughDeposit, "challenger not enough deposit to create challenge")
canCreateChallenge, err := v.challenger.CanCreateChallenge(t.Ctx())
require.NoError(t, err, "unable to check challenger balance")
require.True(t, canCreateChallenge, "challenger not enough deposit to create challenge")

tx, err := v.challenger.CreateChallenge(t.Ctx(), outputRange)
require.NoError(t, err, "unable to create create challenge tx")
Expand Down
117 changes: 90 additions & 27 deletions op-e2e/actions/l2_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,30 @@ import (
)

type ValidatorCfg struct {
OutputOracleAddr common.Address
ColosseumAddr common.Address
SecurityCouncilAddr common.Address
ValidatorPoolAddr common.Address
ValidatorKey *ecdsa.PrivateKey
AllowNonFinalized bool
OutputOracleAddr common.Address
ColosseumAddr common.Address
SecurityCouncilAddr common.Address
ValidatorPoolAddr common.Address
ValidatorManagerAddr common.Address
AssetManagerAddr common.Address
ValidatorKey *ecdsa.PrivateKey
AllowNonFinalized bool
}

type L2Validator struct {
log log.Logger
l1 *ethclient.Client
l2os *validator.L2OutputSubmitter
challenger *validator.Challenger
guardian *validator.Guardian
address common.Address
privKey *ecdsa.PrivateKey
l2ooContractAddr common.Address
valPoolContractAddr common.Address
lastTx common.Hash
cfg *validator.Config
log log.Logger
l1 *ethclient.Client
l2os *validator.L2OutputSubmitter
challenger *validator.Challenger
guardian *validator.Guardian
address common.Address
privKey *ecdsa.PrivateKey
l2ooContractAddr common.Address
valPoolContractAddr common.Address
valManagerContractAddr common.Address
assetManagerContractAddr common.Address
lastTx common.Hash
cfg *validator.Config
}

func NewL2Validator(t Testing, log log.Logger, cfg *ValidatorCfg, l1 *ethclient.Client, l2 *ethclient.Client, rollupCl *sources.RollupClient) *L2Validator {
Expand All @@ -66,6 +70,8 @@ func NewL2Validator(t Testing, log log.Logger, cfg *ValidatorCfg, l1 *ethclient.
validatorCfg := validator.Config{
L2OutputOracleAddr: cfg.OutputOracleAddr,
ValidatorPoolAddr: cfg.ValidatorPoolAddr,
ValidatorManagerAddr: cfg.ValidatorManagerAddr,
AssetManagerAddr: cfg.AssetManagerAddr,
ColosseumAddr: cfg.ColosseumAddr,
SecurityCouncilAddr: cfg.SecurityCouncilAddr,
ChallengerPollInterval: time.Second,
Expand Down Expand Up @@ -106,16 +112,18 @@ func NewL2Validator(t Testing, log log.Logger, cfg *ValidatorCfg, l1 *ethclient.
require.NoError(t, err)

return &L2Validator{
log: log,
l1: l1,
l2os: l2os,
challenger: challenger,
guardian: guardian,
address: from,
privKey: cfg.ValidatorKey,
l2ooContractAddr: cfg.OutputOracleAddr,
valPoolContractAddr: cfg.ValidatorPoolAddr,
cfg: &validatorCfg,
log: log,
l1: l1,
l2os: l2os,
challenger: challenger,
guardian: guardian,
address: from,
privKey: cfg.ValidatorKey,
l2ooContractAddr: cfg.OutputOracleAddr,
valPoolContractAddr: cfg.ValidatorPoolAddr,
valManagerContractAddr: cfg.ValidatorManagerAddr,
assetManagerContractAddr: cfg.AssetManagerAddr,
cfg: &validatorCfg,
}
}

Expand Down Expand Up @@ -198,9 +206,64 @@ func (v *L2Validator) ActDeposit(t Testing, depositAmount uint64) {
v.sendTx(t, &v.valPoolContractAddr, new(big.Int).SetUint64(depositAmount), txData, 1)
}

func (v *L2Validator) ActRegisterValidator(t Testing, assets *big.Int) {
valManagerABI, err := bindings.ValidatorManagerMetaData.GetAbi()
require.NoError(t, err)

txData, err := valManagerABI.Pack(
"registerValidator",
assets,
uint8(10),
uint8(2),
)
require.NoError(t, err)

v.sendTx(t, &v.valManagerContractAddr, common.Big0, txData, 2)
}

// TODO: remove this function once MintManager is implemented
func (v *L2Validator) ActMintGovToken(t Testing, amount uint64) {
tokenAddr := v.getGovTokenAddr(t)
governanceTokenABI, err := bindings.GovernanceTokenMetaData.GetAbi()
require.NoError(t, err)

txData, err := governanceTokenABI.Pack("mint", v.address, new(big.Int).SetUint64(amount))
require.NoError(t, err)

v.sendTx(t, &tokenAddr, common.Big0, txData, 1)
}

func (v *L2Validator) ActApprove(t Testing, amount uint64) {
tokenAddr := v.getGovTokenAddr(t)
governanceTokenABI, err := bindings.GovernanceTokenMetaData.GetAbi()
require.NoError(t, err)

txData, err := governanceTokenABI.Pack("approve", &v.assetManagerContractAddr, new(big.Int).SetUint64(amount))
require.NoError(t, err)

v.sendTx(t, &tokenAddr, common.Big0, txData, 1)
}

func (v *L2Validator) fetchOutput(t Testing, blockNumber *big.Int) *eth.OutputResponse {
output, err := v.l2os.FetchOutput(t.Ctx(), blockNumber)
require.NoError(t, err)

return output
}

func (v *L2Validator) getGovTokenAddr(t Testing) common.Address {
assetManagerABI, err := bindings.AssetManagerMetaData.GetAbi()
require.NoError(t, err)

callData, err := assetManagerABI.Pack("ASSET_TOKEN")
require.NoError(t, err)

returnData, err := v.l1.CallContract(t.Ctx(), ethereum.CallMsg{
To: &v.assetManagerContractAddr,
Data: callData,
}, nil)
require.NoError(t, err)

tokenAddr := common.BytesToAddress(returnData)
return tokenAddr
}
116 changes: 102 additions & 14 deletions op-e2e/actions/l2_validator_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package actions

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
Expand All @@ -15,14 +16,14 @@ import (
"github.com/kroma-network/kroma/kroma-bindings/bindings"
)


// TestValidatorBatchType run each validator-related test case in singular batch mode and span batch mode.
func TestValidatorBatchType(t *testing.T) {
tests := []struct {
name string
f func(gt *testing.T, deltaTimeOffset *hexutil.Uint64)
}{
{"RunValidatorTest", RunValidatorTest},
{"RunValidatorPoolTest", RunValidatorPoolTest},
{"RunValidatorManagerTest", RunValidatorManagerTest},
}
for _, test := range tests {
test := test
Expand All @@ -40,12 +41,7 @@ func TestValidatorBatchType(t *testing.T) {
}
}

func RunValidatorTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisDeltaTimeOffset = deltaTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug)
func setupValidatorTest(t StatefulTesting, dp *e2eutils.DeployParams, sd *e2eutils.SetupData, log log.Logger) (*L1Miner, *L2Engine, *L2Sequencer, *L2Validator) {
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)

rollupSeqCl := sequencer.RollupClient()
Expand All @@ -56,12 +52,14 @@ func RunValidatorTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
}, rollupSeqCl, miner.EthClient(), seqEngine.EthClient(), seqEngine.EngineClient(t, sd.RollupCfg))

validator := NewL2Validator(t, log, &ValidatorCfg{
OutputOracleAddr: sd.DeploymentsL1.L2OutputOracleProxy,
ValidatorPoolAddr: sd.DeploymentsL1.ValidatorPoolProxy,
ColosseumAddr: sd.DeploymentsL1.ColosseumProxy,
SecurityCouncilAddr: sd.DeploymentsL1.SecurityCouncilProxy,
ValidatorKey: dp.Secrets.TrustedValidator,
AllowNonFinalized: false,
OutputOracleAddr: sd.DeploymentsL1.L2OutputOracleProxy,
ValidatorPoolAddr: sd.DeploymentsL1.ValidatorPoolProxy,
ValidatorManagerAddr: sd.DeploymentsL1.ValidatorManagerProxy,
AssetManagerAddr: sd.DeploymentsL1.AssetManagerProxy,
ColosseumAddr: sd.DeploymentsL1.ColosseumProxy,
SecurityCouncilAddr: sd.DeploymentsL1.SecurityCouncilProxy,
ValidatorKey: dp.Secrets.TrustedValidator,
AllowNonFinalized: false,
}, miner.EthClient(), seqEngine.EthClient(), sequencer.RollupClient())

// L1 block
Expand All @@ -83,6 +81,17 @@ func RunValidatorTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
sequencer.ActL1SafeSignal(t)
sequencer.ActL1FinalizedSignal(t)

return miner, seqEngine, sequencer, validator
}

func RunValidatorPoolTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisDeltaTimeOffset = deltaTimeOffset
sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer, validator := setupValidatorTest(t, dp, sd, log)

// deposit bond for validator
validator.ActDeposit(t, 1_000)
miner.includeL1Block(t, validator.address, 12)
Expand Down Expand Up @@ -123,3 +132,82 @@ func RunValidatorTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match")
}

func RunValidatorManagerTest(gt *testing.T, deltaTimeOffset *hexutil.Uint64) {
t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2GenesisDeltaTimeOffset = deltaTimeOffset
b := new(big.Int).SetUint64(0)
hu := hexutil.Big(*b)
dp.DeployConfig.ValidatorPoolTerminateOutputIndex = &hu
sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer, validator := setupValidatorTest(t, dp, sd, log)

// deposit bond for validator
validator.ActDeposit(t, 1_000)
miner.includeL1Block(t, validator.address, 12)
// Submit first output to ValidatorPool
for {
waitTime := validator.CalculateWaitTime(t)
if waitTime > 0 {
break
}
// submit to L1
validator.ActSubmitL2Output(t)
// include output on L1
miner.includeL1Block(t, validator.address, 12)
miner.ActEmptyBlock(t)
// Check submission was successful
receipt, err := miner.EthClient().TransactionReceipt(t.Ctx(), validator.LastSubmitL2OutputTx())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "submission failed")
}

// mint governance token and approve
validator.ActMintGovToken(t, 1_000)
miner.includeL1Block(t, validator.address, 12)

validator.ActApprove(t, 1_000)
miner.includeL1Block(t, validator.address, 12)

// register validator
validator.ActRegisterValidator(t, new(big.Int).SetUint64(1_000))
miner.includeL1Block(t, validator.address, 12)

require.Equal(t, sequencer.SyncStatus().UnsafeL2, sequencer.SyncStatus().FinalizedL2)
// create l2 output submission transactions until there is nothing left to submit
for {
waitTime := validator.CalculateWaitTime(t)
if waitTime > 0 {
break
}
// and submit it to L1
validator.ActSubmitL2Output(t)
// include output on L1
miner.includeL1Block(t, validator.address, 12)
miner.ActEmptyBlock(t)
// Check submission was successful
receipt, err := miner.EthClient().TransactionReceipt(t.Ctx(), validator.LastSubmitL2OutputTx())
require.NoError(t, err)
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "submission failed")
}

// check that L1 stored the expected output root
outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient())
require.NoError(t, err)
// NOTE(chokobole): Comment these 2 lines because of the reason above.
// If Proto Dank Sharding is introduced, the below code fix may be restored.
// block := sequencer.SyncStatus().FinalizedL2
// outputOnL1, err := outputOracleContract.GetL2OutputAfter(nil, new(big.Int).SetUint64(block.Number))
blockNum, err := outputOracleContract.LatestBlockNumber(&bind.CallOpts{})
require.NoError(t, err)
outputOnL1, err := outputOracleContract.GetL2OutputAfter(&bind.CallOpts{}, blockNum)
require.NoError(t, err)
block, err := seqEngine.EthClient().BlockByNumber(t.Ctx(), blockNum)
require.NoError(t, err)
require.Less(t, block.Time(), outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of L2 tx output submission, past L2 block")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), blockNum.Uint64())
require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match")
}
4 changes: 4 additions & 0 deletions op-e2e/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,8 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
ColosseumAddress: config.L1Deployments.ColosseumProxy.Hex(),
ValPoolAddress: config.L1Deployments.ValidatorPoolProxy.Hex(),
ValManagerAddress: config.L1Deployments.ValidatorManagerProxy.Hex(),
AssetManagerAddress: config.L1Deployments.AssetManagerProxy.Hex(),
ChallengerPollInterval: 500 * time.Millisecond,
TxMgrConfig: newTxMgrConfig(sys.EthInstances["l1"].WSEndpoint(), cfg.Secrets.TrustedValidator),
AllowNonFinalized: cfg.NonFinalizedOutputs,
Expand Down Expand Up @@ -788,6 +790,8 @@ func (cfg SystemConfig) Start(t *testing.T, _opts ...SystemConfigOption) (*Syste
L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
ColosseumAddress: config.L1Deployments.ColosseumProxy.Hex(),
ValPoolAddress: config.L1Deployments.ValidatorPoolProxy.Hex(),
ValManagerAddress: config.L1Deployments.ValidatorManagerProxy.Hex(),
AssetManagerAddress: config.L1Deployments.AssetManagerProxy.Hex(),
ChallengerPollInterval: 500 * time.Millisecond,
ProverRPC: "http://0.0.0.0:0",
TxMgrConfig: newTxMgrConfig(sys.EthInstances["l1"].WSEndpoint(), cfg.Secrets.Challenger1),
Expand Down

0 comments on commit 8c95626

Please sign in to comment.