package orm import ( "context" "encoding/json" "math/big" "os" "testing" "github.com/scroll-tech/go-ethereum/common" gethTypes "github.com/scroll-tech/go-ethereum/core/types" "github.com/stretchr/testify/assert" "gorm.io/gorm" "scroll-tech/database/migrate" "scroll-tech/common/testcontainers" "scroll-tech/common/types" "scroll-tech/common/types/encoding" "scroll-tech/common/types/encoding/codecv0" "scroll-tech/common/types/encoding/codecv1" ) var ( testApps *testcontainers.TestcontainerApps db *gorm.DB l2BlockOrm *L2Block chunkOrm *Chunk batchOrm *Batch pendingTransactionOrm *PendingTransaction block1 *encoding.Block block2 *encoding.Block ) func TestMain(m *testing.M) { t := &testing.T{} defer func() { if testApps != nil { testApps.Free() } tearDownEnv(t) }() setupEnv(t) m.Run() } func setupEnv(t *testing.T) { var err error testApps = testcontainers.NewTestcontainerApps() assert.NoError(t, testApps.StartPostgresContainer()) db, err = testApps.GetGormDBClient() assert.NoError(t, err) sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) batchOrm = NewBatch(db) chunkOrm = NewChunk(db) l2BlockOrm = NewL2Block(db) pendingTransactionOrm = NewPendingTransaction(db) templateBlockTrace, err := os.ReadFile("../../../common/testdata/blockTrace_02.json") assert.NoError(t, err) block1 = &encoding.Block{} err = json.Unmarshal(templateBlockTrace, block1) assert.NoError(t, err) templateBlockTrace, err = os.ReadFile("../../../common/testdata/blockTrace_03.json") assert.NoError(t, err) block2 = &encoding.Block{} err = json.Unmarshal(templateBlockTrace, block2) assert.NoError(t, err) } func tearDownEnv(t *testing.T) { sqlDB, err := db.DB() assert.NoError(t, err) sqlDB.Close() } func TestL1BlockOrm(t *testing.T) { sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) l1BlockOrm := NewL1Block(db) // mock blocks block1 := L1Block{Number: 1, Hash: "hash1"} block2 := L1Block{Number: 2, Hash: "hash2"} block3 := L1Block{Number: 3, Hash: "hash3"} block2AfterReorg := L1Block{Number: 2, Hash: "hash2-reorg"} err = l1BlockOrm.InsertL1Blocks(context.Background(), []L1Block{block1, block2, block3}) assert.NoError(t, err) height, err := l1BlockOrm.GetLatestL1BlockHeight(context.Background()) assert.NoError(t, err) assert.Equal(t, uint64(3), height) blocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) assert.NoError(t, err) assert.Len(t, blocks, 3) assert.Equal(t, "hash1", blocks[0].Hash) assert.Equal(t, "hash2", blocks[1].Hash) assert.Equal(t, "hash3", blocks[2].Hash) // reorg handling: insert another block with same height and different hash err = l1BlockOrm.InsertL1Blocks(context.Background(), []L1Block{block2AfterReorg}) assert.NoError(t, err) blocks, err = l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) assert.NoError(t, err) assert.Len(t, blocks, 2) assert.Equal(t, "hash1", blocks[0].Hash) assert.Equal(t, "hash2-reorg", blocks[1].Hash) err = l1BlockOrm.UpdateL1GasOracleStatusAndOracleTxHash(context.Background(), "hash1", types.GasOracleImported, "txhash1") assert.NoError(t, err) updatedBlocks, err := l1BlockOrm.GetL1Blocks(context.Background(), map[string]interface{}{}) assert.NoError(t, err) assert.Len(t, updatedBlocks, 2) assert.Equal(t, types.GasOracleImported, types.GasOracleStatus(updatedBlocks[0].GasOracleStatus)) assert.Equal(t, "txhash1", updatedBlocks[0].OracleTxHash) } func TestL2BlockOrm(t *testing.T) { sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) err = l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2}) assert.NoError(t, err) height, err := l2BlockOrm.GetL2BlocksLatestHeight(context.Background()) assert.NoError(t, err) assert.Equal(t, uint64(3), height) chunkHashes, err := l2BlockOrm.GetChunkHashes(context.Background(), 0) assert.NoError(t, err) assert.Len(t, chunkHashes, 2) assert.Equal(t, "", chunkHashes[0]) assert.Equal(t, "", chunkHashes[1]) blocks, err := l2BlockOrm.GetL2BlocksInRange(context.Background(), 2, 3) assert.NoError(t, err) assert.Len(t, blocks, 2) assert.Equal(t, block1, blocks[0]) assert.Equal(t, block2, blocks[1]) err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 2, 2, "test hash") assert.NoError(t, err) chunkHashes, err = l2BlockOrm.GetChunkHashes(context.Background(), 0) assert.NoError(t, err) assert.Len(t, chunkHashes, 2) assert.Equal(t, "test hash", chunkHashes[0]) assert.Equal(t, "", chunkHashes[1]) } func TestChunkOrm(t *testing.T) { codecVersions := []encoding.CodecVersion{encoding.CodecV0, encoding.CodecV1} chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} chunk2 := &encoding.Chunk{Blocks: []*encoding.Block{block2}} for _, codecVersion := range codecVersions { sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) var chunkHash1 common.Hash var chunkHash2 common.Hash if codecVersion == encoding.CodecV0 { daChunk1, createErr := codecv0.NewDAChunk(chunk1, 0) assert.NoError(t, createErr) chunkHash1, err = daChunk1.Hash() assert.NoError(t, err) daChunk2, createErr := codecv0.NewDAChunk(chunk2, chunk1.NumL1Messages(0)) assert.NoError(t, createErr) chunkHash2, err = daChunk2.Hash() assert.NoError(t, err) } else { daChunk1, createErr := codecv1.NewDAChunk(chunk1, 0) assert.NoError(t, createErr) chunkHash1, err = daChunk1.Hash() assert.NoError(t, err) daChunk2, createErr := codecv1.NewDAChunk(chunk2, chunk1.NumL1Messages(0)) assert.NoError(t, createErr) chunkHash2, err = daChunk2.Hash() assert.NoError(t, err) } dbChunk1, err := chunkOrm.InsertChunk(context.Background(), chunk1, codecVersion) assert.NoError(t, err) assert.Equal(t, dbChunk1.Hash, chunkHash1.Hex()) dbChunk2, err := chunkOrm.InsertChunk(context.Background(), chunk2, codecVersion) assert.NoError(t, err) assert.Equal(t, dbChunk2.Hash, chunkHash2.Hex()) chunks, err := chunkOrm.GetChunksGEIndex(context.Background(), 0, 0) assert.NoError(t, err) assert.Len(t, chunks, 2) assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash) assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash) assert.Equal(t, "", chunks[0].BatchHash) assert.Equal(t, "", chunks[1].BatchHash) err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash1.Hex(), types.ProvingTaskVerified) assert.NoError(t, err) err = chunkOrm.UpdateProvingStatus(context.Background(), chunkHash2.Hex(), types.ProvingTaskAssigned) assert.NoError(t, err) chunks, err = chunkOrm.GetChunksInRange(context.Background(), 0, 1) assert.NoError(t, err) assert.Len(t, chunks, 2) assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash) assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash) assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(chunks[0].ProvingStatus)) assert.Equal(t, types.ProvingTaskAssigned, types.ProvingStatus(chunks[1].ProvingStatus)) err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, "test hash") assert.NoError(t, err) chunks, err = chunkOrm.GetChunksGEIndex(context.Background(), 0, 0) assert.NoError(t, err) assert.Len(t, chunks, 2) assert.Equal(t, chunkHash1.Hex(), chunks[0].Hash) assert.Equal(t, chunkHash2.Hex(), chunks[1].Hash) assert.Equal(t, "test hash", chunks[0].BatchHash) assert.Equal(t, "", chunks[1].BatchHash) } } func TestBatchOrm(t *testing.T) { codecVersions := []encoding.CodecVersion{encoding.CodecV0, encoding.CodecV1} chunk1 := &encoding.Chunk{Blocks: []*encoding.Block{block1}} chunk2 := &encoding.Chunk{Blocks: []*encoding.Block{block2}} for _, codecVersion := range codecVersions { sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) batch := &encoding.Batch{ Index: 0, TotalL1MessagePoppedBefore: 0, ParentBatchHash: common.Hash{}, Chunks: []*encoding.Chunk{chunk1}, } batch1, err := batchOrm.InsertBatch(context.Background(), batch, codecVersion) assert.NoError(t, err) hash1 := batch1.Hash batch1, err = batchOrm.GetBatchByIndex(context.Background(), 0) assert.NoError(t, err) var batchHash1 string if codecVersion == encoding.CodecV0 { daBatch1, createErr := codecv0.NewDABatchFromBytes(batch1.BatchHeader) assert.NoError(t, createErr) batchHash1 = daBatch1.Hash().Hex() } else { daBatch1, createErr := codecv1.NewDABatchFromBytes(batch1.BatchHeader) assert.NoError(t, createErr) batchHash1 = daBatch1.Hash().Hex() } assert.Equal(t, hash1, batchHash1) batch = &encoding.Batch{ Index: 1, TotalL1MessagePoppedBefore: 0, ParentBatchHash: common.Hash{}, Chunks: []*encoding.Chunk{chunk2}, } batch2, err := batchOrm.InsertBatch(context.Background(), batch, codecVersion) assert.NoError(t, err) hash2 := batch2.Hash batch2, err = batchOrm.GetBatchByIndex(context.Background(), 1) assert.NoError(t, err) var batchHash2 string if codecVersion == encoding.CodecV0 { daBatch2, createErr := codecv0.NewDABatchFromBytes(batch2.BatchHeader) assert.NoError(t, createErr) batchHash2 = daBatch2.Hash().Hex() } else { daBatch2, createErr := codecv1.NewDABatchFromBytes(batch2.BatchHeader) assert.NoError(t, createErr) batchHash2 = daBatch2.Hash().Hex() } assert.Equal(t, hash2, batchHash2) count, err := batchOrm.GetBatchCount(context.Background()) assert.NoError(t, err) assert.Equal(t, uint64(2), count) err = batchOrm.UpdateRollupStatus(context.Background(), batchHash1, types.RollupCommitFailed) assert.NoError(t, err) pendingBatches, err := batchOrm.GetFailedAndPendingBatches(context.Background(), 100) assert.NoError(t, err) assert.Equal(t, 2, len(pendingBatches)) rollupStatus, err := batchOrm.GetRollupStatusByHashList(context.Background(), []string{batchHash1, batchHash2}) assert.NoError(t, err) assert.Equal(t, 2, len(rollupStatus)) assert.Equal(t, types.RollupCommitFailed, rollupStatus[0]) assert.Equal(t, types.RollupPending, rollupStatus[1]) err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified) assert.NoError(t, err) dbProof, err := batchOrm.GetVerifiedProofByHash(context.Background(), batchHash1) assert.Error(t, err) assert.Nil(t, dbProof) err = batchOrm.UpdateProvingStatus(context.Background(), batchHash2, types.ProvingTaskVerified) assert.NoError(t, err) err = batchOrm.UpdateRollupStatus(context.Background(), batchHash2, types.RollupFinalized) assert.NoError(t, err) err = batchOrm.UpdateL2GasOracleStatusAndOracleTxHash(context.Background(), batchHash2, types.GasOracleImported, "oracleTxHash") assert.NoError(t, err) updatedBatch, err := batchOrm.GetLatestBatch(context.Background()) assert.NoError(t, err) assert.NotNil(t, updatedBatch) assert.Equal(t, types.ProvingTaskVerified, types.ProvingStatus(updatedBatch.ProvingStatus)) assert.Equal(t, types.RollupFinalized, types.RollupStatus(updatedBatch.RollupStatus)) assert.Equal(t, types.GasOracleImported, types.GasOracleStatus(updatedBatch.OracleStatus)) assert.Equal(t, "oracleTxHash", updatedBatch.OracleTxHash) err = batchOrm.UpdateCommitTxHashAndRollupStatus(context.Background(), batchHash2, "commitTxHash", types.RollupCommitted) assert.NoError(t, err) updatedBatch, err = batchOrm.GetLatestBatch(context.Background()) assert.NoError(t, err) assert.NotNil(t, updatedBatch) assert.Equal(t, "commitTxHash", updatedBatch.CommitTxHash) assert.Equal(t, types.RollupCommitted, types.RollupStatus(updatedBatch.RollupStatus)) err = batchOrm.UpdateFinalizeTxHashAndRollupStatus(context.Background(), batchHash2, "finalizeTxHash", types.RollupFinalizeFailed) assert.NoError(t, err) updatedBatch, err = batchOrm.GetLatestBatch(context.Background()) assert.NoError(t, err) assert.NotNil(t, updatedBatch) assert.Equal(t, "finalizeTxHash", updatedBatch.FinalizeTxHash) assert.Equal(t, types.RollupFinalizeFailed, types.RollupStatus(updatedBatch.RollupStatus)) } } func TestPendingTransactionOrm(t *testing.T) { sqlDB, err := db.DB() assert.NoError(t, err) assert.NoError(t, migrate.ResetDB(sqlDB)) tx0 := gethTypes.NewTx(&gethTypes.DynamicFeeTx{ Nonce: 0, To: &common.Address{}, Data: []byte{}, Gas: 21000, AccessList: gethTypes.AccessList{}, Value: big.NewInt(0), ChainID: big.NewInt(1), GasTipCap: big.NewInt(0), GasFeeCap: big.NewInt(1), V: big.NewInt(0), R: big.NewInt(0), S: big.NewInt(0), }) tx1 := gethTypes.NewTx(&gethTypes.DynamicFeeTx{ Nonce: 0, To: &common.Address{}, Data: []byte{}, Gas: 42000, AccessList: gethTypes.AccessList{}, Value: big.NewInt(0), ChainID: big.NewInt(1), GasTipCap: big.NewInt(1), GasFeeCap: big.NewInt(2), V: big.NewInt(0), R: big.NewInt(0), S: big.NewInt(0), }) senderMeta := &SenderMeta{ Name: "testName", Service: "testService", Address: common.HexToAddress("0x1"), Type: types.SenderTypeCommitBatch, } err = pendingTransactionOrm.InsertPendingTransaction(context.Background(), "test", senderMeta, tx0, 0) assert.NoError(t, err) err = pendingTransactionOrm.InsertPendingTransaction(context.Background(), "test", senderMeta, tx1, 0) assert.NoError(t, err) err = pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(context.Background(), tx0.Hash(), types.TxStatusReplaced) assert.NoError(t, err) txs, err := pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), senderMeta.Type, 2) assert.NoError(t, err) assert.Len(t, txs, 2) assert.Equal(t, tx1.Type(), txs[1].Type) assert.Equal(t, tx1.Nonce(), txs[1].Nonce) assert.Equal(t, tx1.Gas(), txs[1].GasLimit) assert.Equal(t, tx1.GasTipCap().Uint64(), txs[1].GasTipCap) assert.Equal(t, tx1.GasFeeCap().Uint64(), txs[1].GasFeeCap) assert.Equal(t, tx1.ChainId().Uint64(), txs[1].ChainID) assert.Equal(t, senderMeta.Name, txs[1].SenderName) assert.Equal(t, senderMeta.Service, txs[1].SenderService) assert.Equal(t, senderMeta.Address.String(), txs[1].SenderAddress) assert.Equal(t, senderMeta.Type, txs[1].SenderType) err = pendingTransactionOrm.UpdatePendingTransactionStatusByTxHash(context.Background(), tx1.Hash(), types.TxStatusConfirmed) assert.NoError(t, err) txs, err = pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), senderMeta.Type, 2) assert.NoError(t, err) assert.Len(t, txs, 1) err = pendingTransactionOrm.UpdateOtherTransactionsAsFailedByNonce(context.Background(), senderMeta.Address.String(), tx1.Nonce(), tx1.Hash()) assert.NoError(t, err) txs, err = pendingTransactionOrm.GetPendingOrReplacedTransactionsBySenderType(context.Background(), senderMeta.Type, 2) assert.NoError(t, err) assert.Len(t, txs, 0) status, err := pendingTransactionOrm.GetTxStatusByTxHash(context.Background(), tx0.Hash()) assert.NoError(t, err) assert.Equal(t, types.TxStatusConfirmedFailed, status) }