mirror of
https://github.com/scroll-tech/scroll.git
synced 2026-01-09 14:08:03 -05:00
872 lines
34 KiB
Go
872 lines
34 KiB
Go
package test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/scroll-tech/go-ethereum/log"
|
|
"github.com/scroll-tech/go-ethereum/params"
|
|
"github.com/stretchr/testify/assert"
|
|
"gorm.io/gorm"
|
|
|
|
"scroll-tech/common/testcontainers"
|
|
"scroll-tech/common/types"
|
|
"scroll-tech/common/types/encoding"
|
|
"scroll-tech/common/types/message"
|
|
"scroll-tech/common/version"
|
|
"scroll-tech/database/migrate"
|
|
|
|
"scroll-tech/coordinator/internal/config"
|
|
"scroll-tech/coordinator/internal/controller/api"
|
|
"scroll-tech/coordinator/internal/controller/cron"
|
|
"scroll-tech/coordinator/internal/orm"
|
|
"scroll-tech/coordinator/internal/route"
|
|
)
|
|
|
|
const (
|
|
forkNumberFour = 4
|
|
forkNumberThree = 3
|
|
forkNumberTwo = 2
|
|
forkNumberOne = 1
|
|
)
|
|
|
|
var (
|
|
conf *config.Config
|
|
|
|
testApps *testcontainers.TestcontainerApps
|
|
|
|
db *gorm.DB
|
|
l2BlockOrm *orm.L2Block
|
|
chunkOrm *orm.Chunk
|
|
batchOrm *orm.Batch
|
|
proverTaskOrm *orm.ProverTask
|
|
proverBlockListOrm *orm.ProverBlockList
|
|
|
|
block1 *encoding.Block
|
|
block2 *encoding.Block
|
|
|
|
chunk *encoding.Chunk
|
|
hardForkChunk1 *encoding.Chunk
|
|
hardForkChunk2 *encoding.Chunk
|
|
|
|
batch *encoding.Batch
|
|
hardForkBatch1 *encoding.Batch
|
|
hardForkBatch2 *encoding.Batch
|
|
|
|
tokenTimeout int
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
defer func() {
|
|
if testApps != nil {
|
|
testApps.Free()
|
|
}
|
|
}()
|
|
m.Run()
|
|
}
|
|
|
|
func randomURL() string {
|
|
id, _ := rand.Int(rand.Reader, big.NewInt(2000-1))
|
|
return fmt.Sprintf("localhost:%d", 10000+2000+id.Int64())
|
|
}
|
|
|
|
func setupCoordinator(t *testing.T, proversPerSession uint8, coordinatorURL string, nameForkMap map[string]int64) (*cron.Collector, *http.Server) {
|
|
var err error
|
|
db, err = testApps.GetGormDBClient()
|
|
|
|
assert.NoError(t, err)
|
|
sqlDB, err := db.DB()
|
|
assert.NoError(t, err)
|
|
assert.NoError(t, migrate.ResetDB(sqlDB))
|
|
|
|
tokenTimeout = 6
|
|
conf = &config.Config{
|
|
L2: &config.L2{
|
|
ChainID: 111,
|
|
},
|
|
ProverManager: &config.ProverManager{
|
|
ProversPerSession: proversPerSession,
|
|
Verifier: &config.VerifierConfig{
|
|
MockMode: true,
|
|
},
|
|
BatchCollectionTimeSec: 10,
|
|
ChunkCollectionTimeSec: 10,
|
|
MaxVerifierWorkers: 10,
|
|
SessionAttempts: 5,
|
|
MinProverVersion: version.Version,
|
|
},
|
|
Auth: &config.Auth{
|
|
ChallengeExpireDurationSec: tokenTimeout,
|
|
LoginExpireDurationSec: tokenTimeout,
|
|
},
|
|
}
|
|
|
|
var chainConf params.ChainConfig
|
|
for forkName, forkNumber := range nameForkMap {
|
|
switch forkName {
|
|
case "shanghai":
|
|
chainConf.ShanghaiBlock = big.NewInt(forkNumber)
|
|
case "bernoulli":
|
|
chainConf.BernoulliBlock = big.NewInt(forkNumber)
|
|
case "london":
|
|
chainConf.LondonBlock = big.NewInt(forkNumber)
|
|
case "istanbul":
|
|
chainConf.IstanbulBlock = big.NewInt(forkNumber)
|
|
case "homestead":
|
|
chainConf.HomesteadBlock = big.NewInt(forkNumber)
|
|
case "eip155":
|
|
chainConf.EIP155Block = big.NewInt(forkNumber)
|
|
}
|
|
}
|
|
|
|
proofCollector := cron.NewCollector(context.Background(), db, conf, nil)
|
|
|
|
router := gin.New()
|
|
api.InitController(conf, &chainConf, db, nil)
|
|
route.Route(router, conf, nil)
|
|
srv := &http.Server{
|
|
Addr: coordinatorURL,
|
|
Handler: router,
|
|
}
|
|
go func() {
|
|
runErr := srv.ListenAndServe()
|
|
if runErr != nil && !errors.Is(runErr, http.ErrServerClosed) {
|
|
assert.NoError(t, runErr)
|
|
}
|
|
}()
|
|
time.Sleep(time.Second * 2)
|
|
|
|
return proofCollector, srv
|
|
}
|
|
|
|
func setEnv(t *testing.T) {
|
|
var err error
|
|
|
|
version.Version = "v4.1.98"
|
|
|
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.LogfmtFormat()))
|
|
glogger.Verbosity(log.LvlInfo)
|
|
log.Root().SetHandler(glogger)
|
|
|
|
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 = orm.NewBatch(db)
|
|
chunkOrm = orm.NewChunk(db)
|
|
l2BlockOrm = orm.NewL2Block(db)
|
|
proverTaskOrm = orm.NewProverTask(db)
|
|
proverBlockListOrm = orm.NewProverBlockList(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)
|
|
|
|
chunk = &encoding.Chunk{Blocks: []*encoding.Block{block1, block2}}
|
|
hardForkChunk1 = &encoding.Chunk{Blocks: []*encoding.Block{block1}}
|
|
hardForkChunk2 = &encoding.Chunk{Blocks: []*encoding.Block{block2}}
|
|
|
|
assert.NoError(t, err)
|
|
|
|
batch = &encoding.Batch{Chunks: []*encoding.Chunk{chunk}}
|
|
hardForkBatch1 = &encoding.Batch{Index: 1, Chunks: []*encoding.Chunk{hardForkChunk1}}
|
|
hardForkBatch2 = &encoding.Batch{Index: 2, Chunks: []*encoding.Chunk{hardForkChunk2}}
|
|
}
|
|
|
|
func TestApis(t *testing.T) {
|
|
// Set up the test environment.
|
|
setEnv(t)
|
|
|
|
t.Run("TestHandshake", testHandshake)
|
|
t.Run("TestFailedHandshake", testFailedHandshake)
|
|
t.Run("TestGetTaskBlocked", testGetTaskBlocked)
|
|
t.Run("TestOutdatedProverVersion", testOutdatedProverVersion)
|
|
t.Run("TestValidProof", testValidProof)
|
|
t.Run("TestInvalidProof", testInvalidProof)
|
|
t.Run("TestProofGeneratedFailed", testProofGeneratedFailed)
|
|
t.Run("TestTimeoutProof", testTimeoutProof)
|
|
t.Run("TestHardFork", testHardForkAssignTask)
|
|
}
|
|
|
|
func testHandshake(t *testing.T) {
|
|
// Setup coordinator and http server.
|
|
coordinatorURL := randomURL()
|
|
proofCollector, httpHandler := setupCoordinator(t, 1, coordinatorURL, map[string]int64{"homestead": forkNumberOne})
|
|
defer func() {
|
|
proofCollector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
chunkProver := newMockProver(t, "prover_chunk_test", coordinatorURL, message.ProofTypeChunk, version.Version)
|
|
assert.True(t, chunkProver.healthCheckSuccess(t))
|
|
}
|
|
|
|
func testFailedHandshake(t *testing.T) {
|
|
// Setup coordinator and http server.
|
|
coordinatorURL := randomURL()
|
|
proofCollector, httpHandler := setupCoordinator(t, 1, coordinatorURL, map[string]int64{"homestead": forkNumberOne})
|
|
defer func() {
|
|
proofCollector.Stop()
|
|
}()
|
|
|
|
// Try to perform handshake without token
|
|
chunkProver := newMockProver(t, "prover_chunk_test", coordinatorURL, message.ProofTypeChunk, version.Version)
|
|
assert.True(t, chunkProver.healthCheckSuccess(t))
|
|
|
|
// Try to perform handshake with server shutdown
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
time.Sleep(time.Second)
|
|
batchProver := newMockProver(t, "prover_batch_test", coordinatorURL, message.ProofTypeBatch, version.Version)
|
|
assert.True(t, batchProver.healthCheckFailure(t))
|
|
}
|
|
|
|
func testGetTaskBlocked(t *testing.T) {
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, map[string]int64{"homestead": forkNumberOne})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
chunkProver := newMockProver(t, "prover_chunk_test", coordinatorURL, message.ProofTypeChunk, version.Version)
|
|
assert.True(t, chunkProver.healthCheckSuccess(t))
|
|
|
|
batchProver := newMockProver(t, "prover_batch_test", coordinatorURL, message.ProofTypeBatch, version.Version)
|
|
assert.True(t, batchProver.healthCheckSuccess(t))
|
|
|
|
err := proverBlockListOrm.InsertProverPublicKey(context.Background(), chunkProver.proverName, chunkProver.publicKey())
|
|
assert.NoError(t, err)
|
|
|
|
expectedErr := fmt.Errorf("return prover task err:check prover task parameter failed, error:public key %s is blocked from fetching tasks. ProverName: %s, ProverVersion: %s", chunkProver.publicKey(), chunkProver.proverName, chunkProver.proverVersion)
|
|
code, errMsg := chunkProver.tryGetProverTask(t, message.ProofTypeChunk, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorGetTaskFailure, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
|
|
expectedErr = fmt.Errorf("get empty prover task")
|
|
code, errMsg = batchProver.tryGetProverTask(t, message.ProofTypeBatch, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorEmptyProofData, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
|
|
err = proverBlockListOrm.InsertProverPublicKey(context.Background(), batchProver.proverName, batchProver.publicKey())
|
|
assert.NoError(t, err)
|
|
|
|
err = proverBlockListOrm.DeleteProverPublicKey(context.Background(), chunkProver.publicKey())
|
|
assert.NoError(t, err)
|
|
|
|
expectedErr = fmt.Errorf("get empty prover task")
|
|
code, errMsg = chunkProver.tryGetProverTask(t, message.ProofTypeChunk, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorEmptyProofData, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
|
|
expectedErr = fmt.Errorf("return prover task err:check prover task parameter failed, error:public key %s is blocked from fetching tasks. ProverName: %s, ProverVersion: %s", batchProver.publicKey(), batchProver.proverName, batchProver.proverVersion)
|
|
code, errMsg = batchProver.tryGetProverTask(t, message.ProofTypeBatch, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorGetTaskFailure, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
}
|
|
|
|
func testOutdatedProverVersion(t *testing.T) {
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, map[string]int64{"homestead": forkNumberOne})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
chunkProver := newMockProver(t, "prover_chunk_test", coordinatorURL, message.ProofTypeChunk, "v1.0.0")
|
|
assert.True(t, chunkProver.healthCheckSuccess(t))
|
|
|
|
batchProver := newMockProver(t, "prover_batch_test", coordinatorURL, message.ProofTypeBatch, "v1.999.999")
|
|
assert.True(t, chunkProver.healthCheckSuccess(t))
|
|
|
|
expectedErr := fmt.Errorf("return prover task err:check prover task parameter failed, error:incompatible prover version. please upgrade your prover, minimum allowed version: %s, actual version: %s", version.Version, chunkProver.proverVersion)
|
|
code, errMsg := chunkProver.tryGetProverTask(t, message.ProofTypeChunk, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorGetTaskFailure, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
|
|
expectedErr = fmt.Errorf("return prover task err:check prover task parameter failed, error:incompatible prover version. please upgrade your prover, minimum allowed version: %s, actual version: %s", version.Version, batchProver.proverVersion)
|
|
code, errMsg = batchProver.tryGetProverTask(t, message.ProofTypeBatch, "homestead")
|
|
assert.Equal(t, types.ErrCoordinatorGetTaskFailure, code)
|
|
assert.Equal(t, expectedErr, fmt.Errorf(errMsg))
|
|
}
|
|
|
|
func testHardForkAssignTask(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
proofType message.ProofType
|
|
forkNumbers map[string]int64
|
|
proverForkNames []string
|
|
exceptTaskNumber int
|
|
exceptGetTaskErrCodes []int
|
|
exceptGetTaskErrMsgs []string
|
|
}{
|
|
{ // hard fork 4, prover 4 block [2-3]
|
|
name: "noTaskForkChunkProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"bernoulli": forkNumberFour},
|
|
exceptTaskNumber: 0,
|
|
proverForkNames: []string{"bernoulli", "bernoulli"},
|
|
exceptGetTaskErrCodes: []int{types.ErrCoordinatorEmptyProofData, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"get empty prover task", "get empty prover task"},
|
|
},
|
|
{
|
|
name: "noTaskForkBatchProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"bernoulli": forkNumberFour},
|
|
exceptTaskNumber: 0,
|
|
proverForkNames: []string{"bernoulli", "bernoulli"},
|
|
exceptGetTaskErrCodes: []int{types.ErrCoordinatorEmptyProofData, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"get empty prover task", "get empty prover task"},
|
|
},
|
|
{ // hard fork 1, prover 1 block [2-3]
|
|
name: "noTaskForkChunkProverVersionLessThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "homestead": forkNumberOne},
|
|
exceptTaskNumber: 0,
|
|
proverForkNames: []string{"homestead", "homestead"},
|
|
exceptGetTaskErrCodes: []int{types.ErrCoordinatorEmptyProofData, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"get empty prover task", "get empty prover task"},
|
|
},
|
|
{
|
|
name: "noTaskForkBatchProverVersionLessThanHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "homestead": forkNumberOne},
|
|
exceptTaskNumber: 0,
|
|
proverForkNames: []string{"homestead", "homestead"},
|
|
exceptGetTaskErrCodes: []int{types.ErrCoordinatorEmptyProofData, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"get empty prover task", "get empty prover task"},
|
|
},
|
|
{
|
|
name: "noTaskForkBatchProverVersionLessThanHardForkProverNumberEqual0",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"shanghai": forkNumberOne, "london": forkNumberThree},
|
|
exceptTaskNumber: 0,
|
|
proverForkNames: []string{"", ""},
|
|
exceptGetTaskErrCodes: []int{types.ErrCoordinatorEmptyProofData, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"get empty prover task", "get empty prover task"},
|
|
},
|
|
{ // hard fork 3, prover 3 block [2-3]
|
|
name: "oneTaskForkChunkProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"london": forkNumberThree},
|
|
exceptTaskNumber: 1,
|
|
proverForkNames: []string{"london", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"", "get empty prover task"},
|
|
},
|
|
{
|
|
name: "oneTaskForkBatchProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"london": forkNumberThree},
|
|
exceptTaskNumber: 1,
|
|
proverForkNames: []string{"london", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"", "get empty prover task"},
|
|
},
|
|
{ // hard fork 2, prover 2 block [2-3]
|
|
name: "oneTaskForkChunkProverVersionLessThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 1,
|
|
proverForkNames: []string{"istanbul", "istanbul"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"", "get empty prover task"},
|
|
},
|
|
{
|
|
name: "oneTaskForkBatchProverVersionLessThanHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 1,
|
|
proverForkNames: []string{"istanbul", "istanbul"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"", "get empty prover task"},
|
|
},
|
|
{ // hard fork 2, prover 2 block [2-3]
|
|
name: "twoTaskForkChunkProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"istanbul", "istanbul"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{
|
|
name: "twoTaskForkBatchProverVersionLargeOrEqualThanHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"istanbul", "istanbul"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{ // hard fork 4, prover 3 block [2-3]
|
|
name: "twoTaskForkChunkProverVersionLessThanHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"bernoulli": forkNumberFour, "istanbul": forkNumberTwo},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"istanbul", "istanbul"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{ // hard fork 3, prover1:2 prover2:3 block [2-3]
|
|
name: "twoTaskForkChunkProverVersionMiddleHardFork",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"istanbul", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{
|
|
name: "twoTaskForkBatchProverVersionMiddleHardFork",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"istanbul": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"istanbul", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{ // hard fork 3, prover1:2 prover2:3 block [2-3]
|
|
name: "twoTaskForkChunkProverVersionMiddleHardForkProverNumberEqual0",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"shanghai": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{
|
|
name: "twoTaskForkBatchProverVersionMiddleHardForkProverNumberEqual0",
|
|
proofType: message.ProofTypeBatch,
|
|
forkNumbers: map[string]int64{"shanghai": forkNumberTwo, "london": forkNumberThree},
|
|
exceptTaskNumber: 2,
|
|
proverForkNames: []string{"", "london"},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.Success},
|
|
exceptGetTaskErrMsgs: []string{"", ""},
|
|
},
|
|
{ // hard fork 2, prover 2 block [2-3]
|
|
name: "oneTaskForkChunkProverVersionLessThanHardForkProverNumberEqual0",
|
|
proofType: message.ProofTypeChunk,
|
|
forkNumbers: map[string]int64{"shanghai": forkNumberOne, "london": forkNumberThree},
|
|
exceptTaskNumber: 1,
|
|
proverForkNames: []string{"", ""},
|
|
exceptGetTaskErrCodes: []int{types.Success, types.ErrCoordinatorEmptyProofData},
|
|
exceptGetTaskErrMsgs: []string{"", "get empty prover task"},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, tt.forkNumbers)
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
chunkProof := &message.ChunkProof{
|
|
StorageTrace: []byte("testStorageTrace"),
|
|
Protocol: []byte("testProtocol"),
|
|
Proof: []byte("testProof"),
|
|
Instances: []byte("testInstance"),
|
|
Vk: []byte("testVk"),
|
|
ChunkInfo: nil,
|
|
}
|
|
|
|
// the insert block number is 2 and 3
|
|
// chunk1 batch1 contains block number 2
|
|
// chunk2 batch2 contains block number 3
|
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
|
|
assert.NoError(t, err)
|
|
|
|
dbHardForkChunk1, err := chunkOrm.InsertChunk(context.Background(), hardForkChunk1)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 0, 2, dbHardForkChunk1.Hash)
|
|
assert.NoError(t, err)
|
|
err = chunkOrm.UpdateProofAndProvingStatusByHash(context.Background(), dbHardForkChunk1.Hash, chunkProof, types.ProvingTaskUnassigned, 1)
|
|
assert.NoError(t, err)
|
|
dbHardForkBatch1, err := batchOrm.InsertBatch(context.Background(), hardForkBatch1)
|
|
assert.NoError(t, err)
|
|
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, dbHardForkBatch1.Hash)
|
|
assert.NoError(t, err)
|
|
err = batchOrm.UpdateChunkProofsStatusByBatchHash(context.Background(), dbHardForkBatch1.Hash, types.ChunkProofsStatusReady)
|
|
assert.NoError(t, err)
|
|
|
|
dbHardForkChunk2, err := chunkOrm.InsertChunk(context.Background(), hardForkChunk2)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 3, 100, dbHardForkChunk2.Hash)
|
|
assert.NoError(t, err)
|
|
err = chunkOrm.UpdateProofAndProvingStatusByHash(context.Background(), dbHardForkChunk2.Hash, chunkProof, types.ProvingTaskUnassigned, 1)
|
|
assert.NoError(t, err)
|
|
dbHardForkBatch2, err := batchOrm.InsertBatch(context.Background(), hardForkBatch2)
|
|
assert.NoError(t, err)
|
|
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 1, 1, dbHardForkBatch2.Hash)
|
|
assert.NoError(t, err)
|
|
err = batchOrm.UpdateChunkProofsStatusByBatchHash(context.Background(), dbHardForkBatch2.Hash, types.ChunkProofsStatusReady)
|
|
assert.NoError(t, err)
|
|
|
|
getTaskNumber := 0
|
|
for i := 0; i < 2; i++ {
|
|
mockProver := newMockProver(t, fmt.Sprintf("mock_prover_%d", i), coordinatorURL, tt.proofType, version.Version)
|
|
proverTask, errCode, errMsg := mockProver.getProverTask(t, tt.proofType, tt.proverForkNames[i])
|
|
assert.Equal(t, tt.exceptGetTaskErrCodes[i], errCode)
|
|
assert.Equal(t, tt.exceptGetTaskErrMsgs[i], errMsg)
|
|
if errCode != types.Success {
|
|
continue
|
|
}
|
|
getTaskNumber++
|
|
mockProver.submitProof(t, proverTask, verifiedSuccess, types.Success, tt.proverForkNames[i])
|
|
}
|
|
assert.Equal(t, getTaskNumber, tt.exceptTaskNumber)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testValidProof(t *testing.T) {
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, map[string]int64{"istanbul": forkNumberTwo})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
|
|
assert.NoError(t, err)
|
|
dbChunk, err := chunkOrm.InsertChunk(context.Background(), chunk)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 0, 100, dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batch, err := batchOrm.InsertBatch(context.Background(), batch)
|
|
assert.NoError(t, err)
|
|
err = chunkOrm.UpdateBatchHashInRange(context.Background(), 0, 0, batch.Hash)
|
|
assert.NoError(t, err)
|
|
|
|
// create mock provers.
|
|
provers := make([]*mockProver, 2)
|
|
for i := 0; i < len(provers); i++ {
|
|
var proofType message.ProofType
|
|
if i%2 == 0 {
|
|
proofType = message.ProofTypeChunk
|
|
} else {
|
|
proofType = message.ProofTypeBatch
|
|
}
|
|
|
|
provers[i] = newMockProver(t, "prover_test"+strconv.Itoa(i), coordinatorURL, proofType, version.Version)
|
|
|
|
proofStatus := verifiedSuccess
|
|
proverTask, errCode, errMsg := provers[i].getProverTask(t, proofType, "istanbul")
|
|
assert.Equal(t, errCode, types.Success)
|
|
assert.Equal(t, errMsg, "")
|
|
assert.NotNil(t, proverTask)
|
|
provers[i].submitProof(t, proverTask, proofStatus, types.Success, "istanbul")
|
|
}
|
|
|
|
// verify proof status
|
|
var (
|
|
tick = time.Tick(1500 * time.Millisecond)
|
|
tickStop = time.Tick(time.Minute)
|
|
)
|
|
|
|
var (
|
|
chunkProofStatus types.ProvingStatus
|
|
batchProofStatus types.ProvingStatus
|
|
chunkActiveAttempts int16
|
|
chunkMaxAttempts int16
|
|
batchActiveAttempts int16
|
|
batchMaxAttempts int16
|
|
)
|
|
|
|
for {
|
|
select {
|
|
case <-tick:
|
|
chunkProofStatus, err = chunkOrm.GetProvingStatusByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batchProofStatus, err = batchOrm.GetProvingStatusByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
if chunkProofStatus == types.ProvingTaskVerified && batchProofStatus == types.ProvingTaskVerified {
|
|
return
|
|
}
|
|
|
|
chunkActiveAttempts, chunkMaxAttempts, err = chunkOrm.GetAttemptsByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(chunkMaxAttempts))
|
|
assert.Equal(t, 0, int(chunkActiveAttempts))
|
|
|
|
batchActiveAttempts, batchMaxAttempts, err = batchOrm.GetAttemptsByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(batchMaxAttempts))
|
|
assert.Equal(t, 0, int(batchActiveAttempts))
|
|
|
|
case <-tickStop:
|
|
t.Error("failed to check proof status", "chunkProofStatus", chunkProofStatus.String(), "batchProofStatus", batchProofStatus.String())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testInvalidProof(t *testing.T) {
|
|
// Setup coordinator and ws server.
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, map[string]int64{"istanbul": forkNumberTwo})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
|
|
assert.NoError(t, err)
|
|
dbChunk, err := chunkOrm.InsertChunk(context.Background(), chunk)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 0, 100, dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batch, err := batchOrm.InsertBatch(context.Background(), batch)
|
|
assert.NoError(t, err)
|
|
err = batchOrm.UpdateChunkProofsStatusByBatchHash(context.Background(), batch.Hash, types.ChunkProofsStatusReady)
|
|
assert.NoError(t, err)
|
|
|
|
proofType := message.ProofTypeBatch
|
|
provingStatus := verifiedFailed
|
|
expectErrCode := types.ErrCoordinatorHandleZkProofFailure
|
|
prover := newMockProver(t, "prover_test", coordinatorURL, proofType, version.Version)
|
|
proverTask, errCode, errMsg := prover.getProverTask(t, proofType, "istanbul")
|
|
assert.NotNil(t, proverTask)
|
|
assert.Equal(t, errCode, types.Success)
|
|
assert.Equal(t, errMsg, "")
|
|
prover.submitProof(t, proverTask, provingStatus, expectErrCode, "istanbul")
|
|
|
|
// verify proof status
|
|
var (
|
|
tick = time.Tick(1500 * time.Millisecond)
|
|
tickStop = time.Tick(time.Minute)
|
|
batchProofStatus types.ProvingStatus
|
|
batchActiveAttempts int16
|
|
batchMaxAttempts int16
|
|
)
|
|
|
|
for {
|
|
select {
|
|
case <-tick:
|
|
batchProofStatus, err = batchOrm.GetProvingStatusByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
if batchProofStatus == types.ProvingTaskAssigned {
|
|
return
|
|
}
|
|
batchActiveAttempts, batchMaxAttempts, err = batchOrm.GetAttemptsByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(batchMaxAttempts))
|
|
assert.Equal(t, 0, int(batchActiveAttempts))
|
|
case <-tickStop:
|
|
t.Error("failed to check proof status", "batchProofStatus", batchProofStatus.String())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testProofGeneratedFailed(t *testing.T) {
|
|
// Setup coordinator and ws server.
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 3, coordinatorURL, map[string]int64{"istanbul": forkNumberTwo})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
|
|
assert.NoError(t, err)
|
|
dbChunk, err := chunkOrm.InsertChunk(context.Background(), chunk)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 0, 100, dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batch, err := batchOrm.InsertBatch(context.Background(), batch)
|
|
assert.NoError(t, err)
|
|
err = batchOrm.UpdateChunkProofsStatusByBatchHash(context.Background(), batch.Hash, types.ChunkProofsStatusReady)
|
|
assert.NoError(t, err)
|
|
|
|
// create mock provers.
|
|
provers := make([]*mockProver, 2)
|
|
for i := 0; i < len(provers); i++ {
|
|
var proofType message.ProofType
|
|
if i%2 == 0 {
|
|
proofType = message.ProofTypeChunk
|
|
} else {
|
|
proofType = message.ProofTypeBatch
|
|
}
|
|
provers[i] = newMockProver(t, "prover_test"+strconv.Itoa(i), coordinatorURL, proofType, version.Version)
|
|
proverTask, errCode, errMsg := provers[i].getProverTask(t, proofType, "istanbul")
|
|
assert.NotNil(t, proverTask)
|
|
assert.Equal(t, errCode, types.Success)
|
|
assert.Equal(t, errMsg, "")
|
|
provers[i].submitProof(t, proverTask, generatedFailed, types.ErrCoordinatorHandleZkProofFailure, "istanbul")
|
|
}
|
|
|
|
// verify proof status
|
|
var (
|
|
tick = time.Tick(1500 * time.Millisecond)
|
|
tickStop = time.Tick(time.Minute)
|
|
)
|
|
|
|
var (
|
|
chunkProofStatus types.ProvingStatus
|
|
batchProofStatus types.ProvingStatus
|
|
chunkProverTaskProvingStatus types.ProverProveStatus
|
|
batchProverTaskProvingStatus types.ProverProveStatus
|
|
chunkActiveAttempts int16
|
|
chunkMaxAttempts int16
|
|
batchActiveAttempts int16
|
|
batchMaxAttempts int16
|
|
)
|
|
|
|
for {
|
|
select {
|
|
case <-tick:
|
|
chunkProofStatus, err = chunkOrm.GetProvingStatusByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batchProofStatus, err = batchOrm.GetProvingStatusByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
if chunkProofStatus == types.ProvingTaskAssigned && batchProofStatus == types.ProvingTaskAssigned {
|
|
return
|
|
}
|
|
|
|
chunkActiveAttempts, chunkMaxAttempts, err = chunkOrm.GetAttemptsByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(chunkMaxAttempts))
|
|
assert.Equal(t, 0, int(chunkActiveAttempts))
|
|
|
|
batchActiveAttempts, batchMaxAttempts, err = batchOrm.GetAttemptsByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(batchMaxAttempts))
|
|
assert.Equal(t, 0, int(batchActiveAttempts))
|
|
|
|
chunkProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), message.ProofTypeChunk, dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batchProverTaskProvingStatus, err = proverTaskOrm.GetProvingStatusByTaskID(context.Background(), message.ProofTypeBatch, batch.Hash)
|
|
assert.NoError(t, err)
|
|
if chunkProverTaskProvingStatus == types.ProverProofInvalid && batchProverTaskProvingStatus == types.ProverProofInvalid {
|
|
return
|
|
}
|
|
case <-tickStop:
|
|
t.Error("failed to check proof status", "chunkProofStatus", chunkProofStatus.String(), "batchProofStatus", batchProofStatus.String())
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func testTimeoutProof(t *testing.T) {
|
|
// Setup coordinator and ws server.
|
|
coordinatorURL := randomURL()
|
|
collector, httpHandler := setupCoordinator(t, 1, coordinatorURL, map[string]int64{"istanbul": forkNumberTwo})
|
|
defer func() {
|
|
collector.Stop()
|
|
assert.NoError(t, httpHandler.Shutdown(context.Background()))
|
|
}()
|
|
|
|
var (
|
|
chunkActiveAttempts int16
|
|
chunkMaxAttempts int16
|
|
batchActiveAttempts int16
|
|
batchMaxAttempts int16
|
|
)
|
|
|
|
err := l2BlockOrm.InsertL2Blocks(context.Background(), []*encoding.Block{block1, block2})
|
|
assert.NoError(t, err)
|
|
dbChunk, err := chunkOrm.InsertChunk(context.Background(), chunk)
|
|
assert.NoError(t, err)
|
|
err = l2BlockOrm.UpdateChunkHashInRange(context.Background(), 0, 100, dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
batch, err := batchOrm.InsertBatch(context.Background(), batch)
|
|
assert.NoError(t, err)
|
|
err = batchOrm.UpdateChunkProofsStatusByBatchHash(context.Background(), batch.Hash, types.ChunkProofsStatusReady)
|
|
assert.NoError(t, err)
|
|
|
|
// create first chunk & batch mock prover, that will not send any proof.
|
|
chunkProver1 := newMockProver(t, "prover_test"+strconv.Itoa(0), coordinatorURL, message.ProofTypeChunk, version.Version)
|
|
proverChunkTask, errChunkCode, errChunkMsg := chunkProver1.getProverTask(t, message.ProofTypeChunk, "istanbul")
|
|
assert.NotNil(t, proverChunkTask)
|
|
assert.Equal(t, errChunkCode, types.Success)
|
|
assert.Equal(t, errChunkMsg, "")
|
|
|
|
batchProver1 := newMockProver(t, "prover_test"+strconv.Itoa(1), coordinatorURL, message.ProofTypeBatch, version.Version)
|
|
proverBatchTask, errBatchCode, errBatchMsg := batchProver1.getProverTask(t, message.ProofTypeBatch, "istanbul")
|
|
assert.NotNil(t, proverBatchTask)
|
|
assert.Equal(t, errBatchCode, types.Success)
|
|
assert.Equal(t, errBatchMsg, "")
|
|
|
|
// verify proof status, it should be assigned, because prover didn't send any proof
|
|
chunkProofStatus, err := chunkOrm.GetProvingStatusByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, chunkProofStatus, types.ProvingTaskAssigned)
|
|
|
|
batchProofStatus, err := batchOrm.GetProvingStatusByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, batchProofStatus, types.ProvingTaskAssigned)
|
|
|
|
chunkActiveAttempts, chunkMaxAttempts, err = chunkOrm.GetAttemptsByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(chunkMaxAttempts))
|
|
assert.Equal(t, 1, int(chunkActiveAttempts))
|
|
|
|
batchActiveAttempts, batchMaxAttempts, err = batchOrm.GetAttemptsByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 1, int(batchMaxAttempts))
|
|
assert.Equal(t, 1, int(batchActiveAttempts))
|
|
|
|
// wait coordinator to reset the prover task proving status
|
|
time.Sleep(time.Duration(conf.ProverManager.BatchCollectionTimeSec*2) * time.Second)
|
|
|
|
// create second mock prover, that will send valid proof.
|
|
chunkProver2 := newMockProver(t, "prover_test"+strconv.Itoa(2), coordinatorURL, message.ProofTypeChunk, version.Version)
|
|
proverChunkTask2, chunkTask2ErrCode, chunkTask2ErrMsg := chunkProver2.getProverTask(t, message.ProofTypeChunk, "istanbul")
|
|
assert.NotNil(t, proverChunkTask2)
|
|
assert.Equal(t, chunkTask2ErrCode, types.Success)
|
|
assert.Equal(t, chunkTask2ErrMsg, "")
|
|
chunkProver2.submitProof(t, proverChunkTask2, verifiedSuccess, types.Success, "istanbul")
|
|
|
|
batchProver2 := newMockProver(t, "prover_test"+strconv.Itoa(3), coordinatorURL, message.ProofTypeBatch, version.Version)
|
|
proverBatchTask2, batchTask2ErrCode, batchTask2ErrMsg := batchProver2.getProverTask(t, message.ProofTypeBatch, "istanbul")
|
|
assert.NotNil(t, proverBatchTask2)
|
|
assert.Equal(t, batchTask2ErrCode, types.Success)
|
|
assert.Equal(t, batchTask2ErrMsg, "")
|
|
batchProver2.submitProof(t, proverBatchTask2, verifiedSuccess, types.Success, "istanbul")
|
|
|
|
// verify proof status, it should be verified now, because second prover sent valid proof
|
|
chunkProofStatus2, err := chunkOrm.GetProvingStatusByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, chunkProofStatus2, types.ProvingTaskVerified)
|
|
|
|
batchProofStatus2, err := batchOrm.GetProvingStatusByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, batchProofStatus2, types.ProvingTaskVerified)
|
|
|
|
chunkActiveAttempts, chunkMaxAttempts, err = chunkOrm.GetAttemptsByHash(context.Background(), dbChunk.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, int(chunkMaxAttempts))
|
|
assert.Equal(t, 0, int(chunkActiveAttempts))
|
|
|
|
batchActiveAttempts, batchMaxAttempts, err = batchOrm.GetAttemptsByHash(context.Background(), batch.Hash)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 2, int(batchMaxAttempts))
|
|
assert.Equal(t, 0, int(batchActiveAttempts))
|
|
}
|