Skip to content

Commit

Permalink
add l2 relayer unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
colinlyguo committed Jul 4, 2024
1 parent 24b15fc commit fcdc232
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 28 deletions.
38 changes: 37 additions & 1 deletion common/types/message/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ type BatchProof struct {
GitVersion string `json:"git_version,omitempty"`
}

// SanityCheck checks whether an BatchProof is in a legal format
// SanityCheck checks whether a BatchProof is in a legal format
func (ap *BatchProof) SanityCheck() error {
if ap == nil {
return errors.New("agg_proof is nil")
Expand All @@ -243,20 +243,23 @@ func (ap *BatchProof) SanityCheck() error {
if len(ap.Proof) == 0 {
return errors.New("proof not ready")
}

if len(ap.Proof)%32 != 0 {
return fmt.Errorf("proof buffer has wrong length, expected: 32, got: %d", len(ap.Proof))
}

if len(ap.Instances) == 0 {
return errors.New("instance not ready")
}

if len(ap.Instances)%32 != 0 {
return fmt.Errorf("instance buffer has wrong length, expected: 32, got: %d", len(ap.Instances))
}

if len(ap.Vk) == 0 {
return errors.New("vk not ready")
}

if len(ap.Vk)%32 != 0 {
return fmt.Errorf("vk buffer has wrong length, expected: 32, got: %d", len(ap.Vk))
}
Expand All @@ -272,3 +275,36 @@ type BundleProof struct {
// cross-reference between cooridinator computation and prover compution
GitVersion string `json:"git_version,omitempty"`
}

// SanityCheck checks whether a BundleProof is in a legal format
func (ap *BundleProof) SanityCheck() error {
if ap == nil {
return errors.New("agg_proof is nil")
}

if len(ap.Proof) == 0 {
return errors.New("proof not ready")
}

if len(ap.Proof)%32 != 0 {
return fmt.Errorf("proof buffer has wrong length, expected: 32, got: %d", len(ap.Proof))
}

if len(ap.Instances) == 0 {
return errors.New("instance not ready")
}

if len(ap.Instances)%32 != 0 {
return fmt.Errorf("instance buffer has wrong length, expected: 32, got: %d", len(ap.Instances))
}

if len(ap.Vk) == 0 {
return errors.New("vk not ready")
}

if len(ap.Vk)%32 != 0 {
return fmt.Errorf("vk buffer has wrong length, expected: 32, got: %d", len(ap.Vk))
}

return nil
}
13 changes: 13 additions & 0 deletions rollup/internal/controller/relayer/l2_relayer.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,10 @@ func (r *Layer2Relayer) finalizeBundle(bundle *orm.Bundle, withProof bool) error
if err != nil {
return fmt.Errorf("failed to get verified proof by bundle index: %d, err: %w", bundle.Index, err)
}

if err = aggProof.SanityCheck(); err != nil {
return fmt.Errorf("failed to check agg_proof sanity, index: %d, err: %w", bundle.Index, err)
}
}

calldata, err := r.constructFinalizeBundlePayloadCodecV3(dbBatch, aggProof)
Expand Down Expand Up @@ -731,6 +735,15 @@ func (r *Layer2Relayer) finalizeBundle(bundle *orm.Bundle, withProof bool) error
if updateErr := r.batchOrm.UpdateProvingStatusByBundleHash(r.ctx, bundle.Hash, types.ProvingTaskVerified); updateErr != nil {
return updateErr
}
for batchIndex := bundle.StartBatchIndex; batchIndex <= bundle.EndBatchIndex; batchIndex++ {
tmpBatch, getErr := r.batchOrm.GetBatchByIndex(r.ctx, batchIndex)
if getErr != nil {
return getErr
}
if updateErr := r.chunkOrm.UpdateProvingStatusByBatchHash(r.ctx, tmpBatch.Hash, types.ProvingTaskVerified); updateErr != nil {
return updateErr
}
}
return nil
})
if txErr != nil {
Expand Down
242 changes: 229 additions & 13 deletions rollup/internal/controller/relayer/l2_relayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,13 @@ func testL2RelayerProcessCommittedBatches(t *testing.T) {
defer database.CloseDB(db)

l2Cfg := cfg.L2Config
chainConfig := &params.ChainConfig{}
var chainConfig *params.ChainConfig
if codecVersion == encoding.CodecV0 {
chainConfig.BernoulliBlock = big.NewInt(0)
chainConfig = &params.ChainConfig{}
} else if codecVersion == encoding.CodecV1 {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0)}
} else {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0)}
}
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, chainConfig, true, ServiceTypeL2RollupRelayer, nil)
assert.NoError(t, err)
Expand Down Expand Up @@ -171,6 +175,66 @@ func testL2RelayerProcessCommittedBatches(t *testing.T) {
}
}

func testL2RelayerProcessPendingBundles(t *testing.T) {
codecVersions := []encoding.CodecVersion{encoding.CodecV3}
for _, codecVersion := range codecVersions {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)

l2Cfg := cfg.L2Config
var chainConfig *params.ChainConfig
if codecVersion == encoding.CodecV3 {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64)}
}
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, chainConfig, true, ServiceTypeL2RollupRelayer, nil)
assert.NoError(t, err)

batch := &encoding.Batch{
Index: 1,
TotalL1MessagePoppedBefore: 0,
ParentBatchHash: common.Hash{},
Chunks: []*encoding.Chunk{chunk1, chunk2},
}

batchOrm := orm.NewBatch(db)
dbBatch, err := batchOrm.InsertBatch(context.Background(), batch, codecVersion, rutils.BatchMetrics{})
assert.NoError(t, err)

bundleOrm := orm.NewBundle(db)
bundle, err := bundleOrm.InsertBundle(context.Background(), []*orm.Batch{dbBatch}, codecVersion)
assert.NoError(t, err)

err = bundleOrm.UpdateRollupStatus(context.Background(), bundle.Hash, types.RollupPending)
assert.NoError(t, err)

err = bundleOrm.UpdateProvingStatus(context.Background(), dbBatch.Hash, types.ProvingTaskVerified)
assert.NoError(t, err)

relayer.ProcessPendingBundles()

bundles, err := bundleOrm.GetBundles(context.Background(), map[string]interface{}{"hash": bundle.Hash}, nil, 0)
assert.NoError(t, err)
assert.Equal(t, 1, len(bundles))
// no valid proof, rollup status remains the same
assert.Equal(t, types.RollupPending, types.RollupStatus(bundles[0].RollupStatus))

proof := &message.BundleProof{
Proof: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
Instances: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
Vk: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
}
err = bundleOrm.UpdateProofAndProvingStatusByHash(context.Background(), bundle.Hash, proof, types.ProvingTaskVerified, 600)
assert.NoError(t, err)

relayer.ProcessPendingBundles()
bundles, err = bundleOrm.GetBundles(context.Background(), map[string]interface{}{"hash": bundle.Hash}, nil, 0)
assert.NoError(t, err)
assert.Equal(t, 1, len(bundles))
assert.Equal(t, types.RollupFinalizing, types.RollupStatus(bundles[0].RollupStatus))
relayer.StopSenders()
}
}

func testL2RelayerFinalizeTimeoutBatches(t *testing.T) {
codecVersions := []encoding.CodecVersion{encoding.CodecV0, encoding.CodecV1, encoding.CodecV2}
for _, codecVersion := range codecVersions {
Expand All @@ -180,9 +244,13 @@ func testL2RelayerFinalizeTimeoutBatches(t *testing.T) {
l2Cfg := cfg.L2Config
l2Cfg.RelayerConfig.EnableTestEnvBypassFeatures = true
l2Cfg.RelayerConfig.FinalizeBatchWithoutProofTimeoutSec = 0
chainConfig := &params.ChainConfig{}
var chainConfig *params.ChainConfig
if codecVersion == encoding.CodecV0 {
chainConfig.BernoulliBlock = big.NewInt(0)
chainConfig = &params.ChainConfig{}
} else if codecVersion == encoding.CodecV1 {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0)}
} else {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0)}
}
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, chainConfig, true, ServiceTypeL2RollupRelayer, nil)
assert.NoError(t, err)
Expand Down Expand Up @@ -213,29 +281,108 @@ func testL2RelayerFinalizeTimeoutBatches(t *testing.T) {
err = chunkOrm.UpdateBatchHashInRange(context.Background(), chunkDB1.Index, chunkDB2.Index, dbBatch.Hash, nil)
assert.NoError(t, err)

// Check the database for the updated status using TryTimes.
ok := utils.TryTimes(5, func() bool {
assert.Eventually(t, func() bool {
relayer.ProcessCommittedBatches()
time.Sleep(time.Second)

batchInDB, batchErr := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": dbBatch.Hash}, nil, 0)
if batchErr != nil {
return false
}

batchStatus := len(batchInDB) == 1 && types.RollupStatus(batchInDB[0].RollupStatus) == types.RollupFinalizing &&
types.ProvingStatus(batchInDB[0].ProvingStatus) == types.ProvingTaskVerified

chunks, chunkErr := chunkOrm.GetChunksByBatchHash(context.Background(), dbBatch.Hash)
if chunkErr != nil {
return false
}

batchStatus := len(batchInDB) == 1 && types.RollupStatus(batchInDB[0].RollupStatus) == types.RollupFinalizing &&
types.ProvingStatus(batchInDB[0].ProvingStatus) == types.ProvingTaskVerified

chunkStatus := len(chunks) == 2 && types.ProvingStatus(chunks[0].ProvingStatus) == types.ProvingTaskVerified &&
types.ProvingStatus(chunks[1].ProvingStatus) == types.ProvingTaskVerified

return batchStatus && chunkStatus
})
assert.True(t, ok)
}, 5*time.Second, 100*time.Millisecond, "Batch or Chunk status did not update as expected")
relayer.StopSenders()
}
}

func testL2RelayerFinalizeTimeoutBundles(t *testing.T) {
codecVersions := []encoding.CodecVersion{encoding.CodecV3}
for _, codecVersion := range codecVersions {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)

l2Cfg := cfg.L2Config
l2Cfg.RelayerConfig.EnableTestEnvBypassFeatures = true
l2Cfg.RelayerConfig.FinalizeBundleWithoutProofTimeoutSec = 0
var chainConfig *params.ChainConfig
if codecVersion == encoding.CodecV3 {
chainConfig = &params.ChainConfig{BernoulliBlock: big.NewInt(0), CurieBlock: big.NewInt(0), DarwinTime: new(uint64)}
}
relayer, err := NewLayer2Relayer(context.Background(), l2Cli, db, l2Cfg.RelayerConfig, chainConfig, true, ServiceTypeL2RollupRelayer, nil)
assert.NoError(t, err)

l2BlockOrm := orm.NewL2Block(db)
err = l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
assert.NoError(t, err)
chunkOrm := orm.NewChunk(db)
chunkDB1, err := chunkOrm.InsertChunk(context.Background(), chunk1, codecVersion, rutils.ChunkMetrics{})
assert.NoError(t, err)
chunkDB2, err := chunkOrm.InsertChunk(context.Background(), chunk2, codecVersion, rutils.ChunkMetrics{})
assert.NoError(t, err)

batch := &encoding.Batch{
Index: 1,
TotalL1MessagePoppedBefore: 0,
ParentBatchHash: common.Hash{},
Chunks: []*encoding.Chunk{chunk1, chunk2},
}

batchOrm := orm.NewBatch(db)
dbBatch, err := batchOrm.InsertBatch(context.Background(), batch, codecVersion, rutils.BatchMetrics{})
assert.NoError(t, err)

err = batchOrm.UpdateRollupStatus(context.Background(), dbBatch.Hash, types.RollupCommitted)
assert.NoError(t, err)

err = chunkOrm.UpdateBatchHashInRange(context.Background(), chunkDB1.Index, chunkDB2.Index, dbBatch.Hash, nil)
assert.NoError(t, err)

bundleOrm := orm.NewBundle(db)
bundle, err := bundleOrm.InsertBundle(context.Background(), []*orm.Batch{dbBatch}, codecVersion)
assert.NoError(t, err)

err = batchOrm.UpdateBundleHashInRange(context.Background(), dbBatch.Index, dbBatch.Index, bundle.Hash, nil)
assert.NoError(t, err)

assert.Eventually(t, func() bool {
relayer.ProcessPendingBundles()

bundleInDB, bundleErr := bundleOrm.GetBundles(context.Background(), map[string]interface{}{"hash": bundle.Hash}, nil, 0)
if bundleErr != nil {
return false
}

bundleStatus := len(bundleInDB) == 1 && types.RollupStatus(bundleInDB[0].RollupStatus) == types.RollupFinalizing &&
types.ProvingStatus(bundleInDB[0].ProvingStatus) == types.ProvingTaskVerified

batchInDB, batchErr := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": dbBatch.Hash}, nil, 0)
if batchErr != nil {
return false
}

batchStatus := len(batchInDB) == 1 && types.ProvingStatus(batchInDB[0].ProvingStatus) == types.ProvingTaskVerified

chunks, chunkErr := chunkOrm.GetChunksByBatchHash(context.Background(), dbBatch.Hash)
if chunkErr != nil {
return false
}

chunkStatus := len(chunks) == 2 && types.ProvingStatus(chunks[0].ProvingStatus) == types.ProvingTaskVerified &&
types.ProvingStatus(chunks[1].ProvingStatus) == types.ProvingTaskVerified

return bundleStatus && batchStatus && chunkStatus
}, 5*time.Second, 100*time.Millisecond, "Bundle or Batch or Chunk status did not update as expected")
relayer.StopSenders()
}
}
Expand Down Expand Up @@ -296,7 +443,7 @@ func testL2RelayerCommitConfirm(t *testing.T) {
assert.True(t, ok)
}

func testL2RelayerFinalizeConfirm(t *testing.T) {
func testL2RelayerFinalizeBatchConfirm(t *testing.T) {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)

Expand Down Expand Up @@ -352,6 +499,75 @@ func testL2RelayerFinalizeConfirm(t *testing.T) {
assert.True(t, ok)
}

func testL2RelayerFinalizeBundleConfirm(t *testing.T) {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)

// Create and set up the Layer2 Relayer.
l2Cfg := cfg.L2Config
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
l2Relayer, err := NewLayer2Relayer(ctx, l2Cli, db, l2Cfg.RelayerConfig, &params.ChainConfig{}, true, ServiceTypeL2RollupRelayer, nil)
assert.NoError(t, err)
defer l2Relayer.StopSenders()

// Simulate message confirmations.
isSuccessful := []bool{true, false}
batchOrm := orm.NewBatch(db)
bundleOrm := orm.NewBundle(db)
batchHashes := make([]string, len(isSuccessful))
bundleHashes := make([]string, len(isSuccessful))
for i := range batchHashes {
batch := &encoding.Batch{
Index: uint64(i + 1),
TotalL1MessagePoppedBefore: 0,
ParentBatchHash: common.Hash{},
Chunks: []*encoding.Chunk{chunk1, chunk2},
}

dbBatch, err := batchOrm.InsertBatch(context.Background(), batch, encoding.CodecV0, rutils.BatchMetrics{})
assert.NoError(t, err)
batchHashes[i] = dbBatch.Hash

bundle, err := bundleOrm.InsertBundle(context.Background(), []*orm.Batch{dbBatch}, encoding.CodecV3)
assert.NoError(t, err)
bundleHashes[i] = bundle.Hash

err = batchOrm.UpdateBundleHashInRange(context.Background(), dbBatch.Index, dbBatch.Index, bundle.Hash)
assert.NoError(t, err)
}

for i, bundleHash := range bundleHashes {
l2Relayer.finalizeSender.SendConfirmation(&sender.Confirmation{
ContextID: "finalizeBundle-" + bundleHash,
IsSuccessful: isSuccessful[i],
TxHash: common.HexToHash("0x123456789abcdef"),
SenderType: types.SenderTypeFinalizeBatch,
})
}

assert.Eventually(t, func() bool {
expectedStatuses := []types.RollupStatus{
types.RollupFinalized,
types.RollupFinalizeFailed,
}

for i, bundleHash := range bundleHashes {
bundleInDB, err := bundleOrm.GetBundles(context.Background(), map[string]interface{}{"hash": bundleHash}, nil, 0)
if err != nil || len(bundleInDB) != 1 || types.RollupStatus(bundleInDB[0].RollupStatus) != expectedStatuses[i] {
return false
}

batchInDB, err := batchOrm.GetBatches(context.Background(), map[string]interface{}{"hash": batchHashes[i]}, nil, 0)
if err != nil || len(batchInDB) != 1 || types.RollupStatus(batchInDB[0].RollupStatus) != expectedStatuses[i] {
return false
}
}

return true
}, 5*time.Second, 100*time.Millisecond, "Bundle or Batch status did not update as expected")
}

func testL2RelayerGasOracleConfirm(t *testing.T) {
db := setupL2RelayerDB(t)
defer database.CloseDB(db)
Expand Down
Loading

0 comments on commit fcdc232

Please sign in to comment.