|
package coordinator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"os"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
ethCommon "github.com/ethereum/go-ethereum/common"
|
|
"github.com/hermeznetwork/hermez-node/batchbuilder"
|
|
"github.com/hermeznetwork/hermez-node/common"
|
|
dbUtils "github.com/hermeznetwork/hermez-node/db"
|
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
|
"github.com/hermeznetwork/hermez-node/db/l2db"
|
|
"github.com/hermeznetwork/hermez-node/db/statedb"
|
|
"github.com/hermeznetwork/hermez-node/eth"
|
|
"github.com/hermeznetwork/hermez-node/log"
|
|
"github.com/hermeznetwork/hermez-node/prover"
|
|
"github.com/hermeznetwork/hermez-node/synchronizer"
|
|
"github.com/hermeznetwork/hermez-node/test"
|
|
"github.com/hermeznetwork/hermez-node/txprocessor"
|
|
"github.com/hermeznetwork/hermez-node/txselector"
|
|
"github.com/hermeznetwork/tracerr"
|
|
"github.com/iden3/go-iden3-crypto/babyjub"
|
|
"github.com/iden3/go-merkletree/db/pebble"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var deleteme = []string{}
|
|
|
|
func pebbleMakeCheckpoint(source, dest string) error {
|
|
// Remove dest folder (if it exists) before doing the checkpoint
|
|
if _, err := os.Stat(dest); !os.IsNotExist(err) {
|
|
err := os.RemoveAll(dest)
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
} else if err != nil && !os.IsNotExist(err) {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
|
|
sto, err := pebble.NewPebbleStorage(source, false)
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
defer func() {
|
|
errClose := sto.Pebble().Close()
|
|
if errClose != nil {
|
|
log.Errorw("Pebble.Close", "err", errClose)
|
|
}
|
|
}()
|
|
|
|
// execute Checkpoint
|
|
err = sto.Pebble().Checkpoint(dest)
|
|
if err != nil {
|
|
return tracerr.Wrap(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
exitVal := m.Run()
|
|
for _, dir := range deleteme {
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
os.Exit(exitVal)
|
|
}
|
|
|
|
var syncDBPath string
|
|
var txSelDBPath string
|
|
var batchBuilderDBPath string
|
|
|
|
type modules struct {
|
|
historyDB *historydb.HistoryDB
|
|
l2DB *l2db.L2DB
|
|
txSelector *txselector.TxSelector
|
|
batchBuilder *batchbuilder.BatchBuilder
|
|
stateDB *statedb.StateDB
|
|
}
|
|
|
|
var maxL1Txs uint64 = 256
|
|
var maxTxs uint64 = 376
|
|
var nLevels uint32 = 32 //nolint:deadcode,unused
|
|
var maxFeeTxs uint32 = 64 //nolint:deadcode,varcheck
|
|
var chainID uint16 = 0
|
|
|
|
func newTestModules(t *testing.T) modules {
|
|
var err error
|
|
syncDBPath, err = ioutil.TempDir("", "tmpSyncDB")
|
|
require.NoError(t, err)
|
|
deleteme = append(deleteme, syncDBPath)
|
|
syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: syncDBPath, Keep: 128,
|
|
Type: statedb.TypeSynchronizer, NLevels: 48})
|
|
assert.NoError(t, err)
|
|
|
|
pass := os.Getenv("POSTGRES_PASS")
|
|
db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
|
|
require.NoError(t, err)
|
|
test.WipeDB(db)
|
|
l2DB := l2db.NewL2DB(db, db, 10, 100, 0.0, 24*time.Hour, nil)
|
|
historyDB := historydb.NewHistoryDB(db, db, nil)
|
|
|
|
txSelDBPath, err = ioutil.TempDir("", "tmpTxSelDB")
|
|
require.NoError(t, err)
|
|
deleteme = append(deleteme, txSelDBPath)
|
|
|
|
var bjj babyjub.PublicKeyComp
|
|
err = bjj.UnmarshalText([]byte("c433f7a696b7aa3a5224efb3993baf0ccd9e92eecee0c29a3f6c8208a9e81d9e"))
|
|
require.NoError(t, err)
|
|
coordAccount := &txselector.CoordAccount{ // TODO TMP
|
|
Addr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"),
|
|
BJJ: bjj,
|
|
AccountCreationAuth: nil,
|
|
}
|
|
txSelector, err := txselector.NewTxSelector(coordAccount, txSelDBPath, syncStateDB, l2DB)
|
|
assert.NoError(t, err)
|
|
|
|
batchBuilderDBPath, err = ioutil.TempDir("", "tmpBatchBuilderDB")
|
|
require.NoError(t, err)
|
|
deleteme = append(deleteme, batchBuilderDBPath)
|
|
batchBuilder, err := batchbuilder.NewBatchBuilder(batchBuilderDBPath, syncStateDB, 0,
|
|
uint64(nLevels))
|
|
assert.NoError(t, err)
|
|
|
|
return modules{
|
|
historyDB: historyDB,
|
|
l2DB: l2DB,
|
|
txSelector: txSelector,
|
|
batchBuilder: batchBuilder,
|
|
stateDB: syncStateDB,
|
|
}
|
|
}
|
|
|
|
type timer struct {
|
|
time int64
|
|
}
|
|
|
|
func (t *timer) Time() int64 {
|
|
currentTime := t.time
|
|
t.time++
|
|
return currentTime
|
|
}
|
|
|
|
var bidder = ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f")
|
|
var forger = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
|
|
|
|
func newTestCoordinator(t *testing.T, forgerAddr ethCommon.Address, ethClient *test.Client,
|
|
ethClientSetup *test.ClientSetup, modules modules) *Coordinator {
|
|
debugBatchPath, err := ioutil.TempDir("", "tmpDebugBatch")
|
|
require.NoError(t, err)
|
|
deleteme = append(deleteme, debugBatchPath)
|
|
|
|
conf := Config{
|
|
ForgerAddress: forgerAddr,
|
|
ConfirmBlocks: 5,
|
|
L1BatchTimeoutPerc: 0.5,
|
|
EthClientAttempts: 5,
|
|
SyncRetryInterval: 400 * time.Microsecond,
|
|
EthClientAttemptsDelay: 100 * time.Millisecond,
|
|
TxManagerCheckInterval: 300 * time.Millisecond,
|
|
DebugBatchPath: debugBatchPath,
|
|
MustForgeAtSlotDeadline: true,
|
|
Purger: PurgerCfg{
|
|
PurgeBatchDelay: 10,
|
|
PurgeBlockDelay: 10,
|
|
InvalidateBatchDelay: 4,
|
|
InvalidateBlockDelay: 4,
|
|
},
|
|
TxProcessorConfig: txprocessor.Config{
|
|
NLevels: nLevels,
|
|
MaxFeeTx: maxFeeTxs,
|
|
MaxTx: uint32(maxTxs),
|
|
MaxL1Tx: uint32(maxL1Txs),
|
|
ChainID: chainID,
|
|
},
|
|
VerifierIdx: 0,
|
|
}
|
|
|
|
serverProofs := []prover.Client{
|
|
&prover.MockClient{Delay: 300 * time.Millisecond},
|
|
&prover.MockClient{Delay: 400 * time.Millisecond},
|
|
}
|
|
|
|
scConsts := &synchronizer.SCConsts{
|
|
Rollup: *ethClientSetup.RollupConstants,
|
|
Auction: *ethClientSetup.AuctionConstants,
|
|
WDelayer: *ethClientSetup.WDelayerConstants,
|
|
}
|
|
initSCVars := &synchronizer.SCVariables{
|
|
Rollup: *ethClientSetup.RollupVariables,
|
|
Auction: *ethClientSetup.AuctionVariables,
|
|
WDelayer: *ethClientSetup.WDelayerVariables,
|
|
}
|
|
coord, err := NewCoordinator(conf, modules.historyDB, modules.l2DB, modules.txSelector,
|
|
modules.batchBuilder, serverProofs, ethClient, scConsts, initSCVars)
|
|
require.NoError(t, err)
|
|
return coord
|
|
}
|
|
|
|
func newTestSynchronizer(t *testing.T, ethClient *test.Client, ethClientSetup *test.ClientSetup,
|
|
modules modules) *synchronizer.Synchronizer {
|
|
sync, err := synchronizer.NewSynchronizer(ethClient, modules.historyDB, modules.l2DB, modules.stateDB,
|
|
synchronizer.Config{
|
|
StatsRefreshPeriod: 0 * time.Second,
|
|
})
|
|
require.NoError(t, err)
|
|
return sync
|
|
}
|
|
|
|
// TestCoordinatorFlow is a test where the coordinator is stared (which means
|
|
// that goroutines are spawned), and ethereum blocks are mined via the
|
|
// test.Client to simulate starting and stopping forging times. This test
|
|
// works without a synchronizer, and no l2txs are inserted in the pool, so all
|
|
// the batches are forged empty. The purpose of this test is to manually
|
|
// observe via the logs that nothing crashes and that the coordinator starts
|
|
// and stops forging at the right blocks.
|
|
func TestCoordinatorFlow(t *testing.T) {
|
|
if os.Getenv("TEST_COORD_FLOW") == "" {
|
|
return
|
|
}
|
|
ethClientSetup := test.NewClientSetupExample()
|
|
ethClientSetup.ChainID = big.NewInt(int64(chainID))
|
|
var timer timer
|
|
ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
|
|
modules := newTestModules(t)
|
|
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
|
|
|
|
// Bid for slot 2 and 4
|
|
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
|
|
require.NoError(t, err)
|
|
bid, ok := new(big.Int).SetString("11000000000000000000", 10)
|
|
if !ok {
|
|
panic("bad bid")
|
|
}
|
|
_, err = ethClient.AuctionBidSimple(3, bid)
|
|
require.NoError(t, err)
|
|
_, err = ethClient.AuctionBidSimple(5, bid)
|
|
require.NoError(t, err)
|
|
|
|
coord.Start()
|
|
ctx := context.Background()
|
|
time.Sleep(1 * time.Second)
|
|
|
|
waitForSlot := func(slot int64) {
|
|
for {
|
|
blockNum, err := ethClient.EthLastBlock()
|
|
require.NoError(t, err)
|
|
nextBlockSlot, err := ethClient.AuctionGetSlotNumber(blockNum + 1)
|
|
require.NoError(t, err)
|
|
if nextBlockSlot == slot {
|
|
break
|
|
}
|
|
ethClient.CtlMineBlock()
|
|
time.Sleep(100 * time.Millisecond)
|
|
var stats synchronizer.Stats
|
|
stats.Eth.LastBlock = *ethClient.CtlLastBlock()
|
|
stats.Sync.LastBlock = stats.Eth.LastBlock
|
|
stats.Eth.LastBatchNum = ethClient.CtlLastForgedBatch()
|
|
stats.Sync.LastBatch.BatchNum = common.BatchNum(stats.Eth.LastBatchNum)
|
|
canForge, err := ethClient.AuctionCanForge(forger, blockNum+1)
|
|
require.NoError(t, err)
|
|
var slot common.Slot
|
|
slotNum := ethClientSetup.AuctionConstants.SlotNum(blockNum + 1)
|
|
slot.StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
(slotNum)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
slot.EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
(slotNum+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
if canForge {
|
|
slot.Forger = forger
|
|
}
|
|
stats.Sync.Auction.CurrentSlot = slot
|
|
|
|
// Copy stateDB to synchronizer if there was a new batch
|
|
source := fmt.Sprintf("%v/BatchNum%v", batchBuilderDBPath, stats.Sync.LastBatch)
|
|
dest := fmt.Sprintf("%v/BatchNum%v", syncDBPath, stats.Sync.LastBatch)
|
|
if stats.Sync.LastBatch.BatchNum != 0 {
|
|
if _, err := os.Stat(dest); os.IsNotExist(err) {
|
|
log.Infow("Making pebble checkpoint for sync",
|
|
"source", source, "dest", dest)
|
|
err = pebbleMakeCheckpoint(source, dest)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
coord.SendMsg(ctx, MsgSyncBlock{
|
|
Stats: stats,
|
|
})
|
|
}
|
|
}
|
|
|
|
// NOTE: With the current test, the coordinator will enter in forge
|
|
// time before the bidded slot because no one else is forging in the
|
|
// other slots before the slot deadline.
|
|
// simulate forgeSequence time
|
|
waitForSlot(2)
|
|
log.Info("~~~ simulate entering in forge time")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// simulate going out from forgeSequence
|
|
waitForSlot(3)
|
|
log.Info("~~~ simulate going out from forge time")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// simulate entering forgeSequence time again
|
|
waitForSlot(4)
|
|
log.Info("~~~ simulate entering in forge time again")
|
|
time.Sleep(2 * time.Second)
|
|
|
|
// simulate stopping forgerLoop by channel
|
|
log.Info("~~~ simulate stopping forgerLoop by closing coordinator stopch")
|
|
coord.Stop()
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
|
|
func TestCoordinatorStartStop(t *testing.T) {
|
|
ethClientSetup := test.NewClientSetupExample()
|
|
ethClientSetup.ChainID = big.NewInt(int64(chainID))
|
|
var timer timer
|
|
ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
|
|
modules := newTestModules(t)
|
|
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
|
|
coord.Start()
|
|
coord.Stop()
|
|
}
|
|
|
|
func TestCoordCanForge(t *testing.T) {
|
|
ethClientSetup := test.NewClientSetupExample()
|
|
ethClientSetup.ChainID = big.NewInt(int64(chainID))
|
|
bootForger := ethClientSetup.AuctionVariables.BootCoordinator
|
|
|
|
var timer timer
|
|
ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
|
|
modules := newTestModules(t)
|
|
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
|
|
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
|
|
require.NoError(t, err)
|
|
bid, ok := new(big.Int).SetString("12000000000000000000", 10)
|
|
if !ok {
|
|
panic("bad bid")
|
|
}
|
|
_, err = ethClient.AuctionBidSimple(3, bid)
|
|
require.NoError(t, err)
|
|
|
|
modules2 := newTestModules(t)
|
|
bootCoord := newTestCoordinator(t, bootForger, ethClient, ethClientSetup, modules2)
|
|
|
|
assert.Equal(t, forger, coord.cfg.ForgerAddress)
|
|
assert.Equal(t, bootForger, bootCoord.cfg.ForgerAddress)
|
|
ethBootCoord, err := ethClient.AuctionGetBootCoordinator()
|
|
require.NoError(t, err)
|
|
assert.Equal(t, &bootForger, ethBootCoord)
|
|
|
|
var stats synchronizer.Stats
|
|
|
|
slots := [4]common.Slot{}
|
|
for i := 0; i < 4; i++ {
|
|
slots[i].StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(i)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
slots[i].EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(i+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
// Only slot 3 has Coordinator winner, the rest are BootCoordinator
|
|
if i == 3 {
|
|
slots[i].Forger = forger
|
|
} else {
|
|
slots[i].Forger = bootForger
|
|
}
|
|
}
|
|
|
|
// Slot 0. No bid, so the winner is the boot coordinator
|
|
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
|
|
stats.Sync.LastBlock = stats.Eth.LastBlock
|
|
stats.Sync.Auction.CurrentSlot = slots[0]
|
|
coord.stats = stats
|
|
bootCoord.stats = stats
|
|
assert.Equal(t, false, coord.canForge())
|
|
assert.Equal(t, true, bootCoord.canForge())
|
|
|
|
// Slot 0. No bid, and we reach the deadline, so anyone can forge
|
|
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(ethClientSetup.AuctionVariables.SlotDeadline)
|
|
stats.Sync.LastBlock = stats.Eth.LastBlock
|
|
stats.Sync.Auction.CurrentSlot = slots[0]
|
|
coord.stats = stats
|
|
bootCoord.stats = stats
|
|
assert.Equal(t, true, coord.canForge())
|
|
assert.Equal(t, true, bootCoord.canForge())
|
|
|
|
// Anyone can forge but the node MustForgeAtSlotDeadline as set as false
|
|
coord.cfg.MustForgeAtSlotDeadline = false
|
|
assert.Equal(t, false, coord.canForge())
|
|
|
|
// Slot 3. coordinator bid, so the winner is the coordinator
|
|
stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
3*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
stats.Sync.LastBlock = stats.Eth.LastBlock
|
|
stats.Sync.Auction.CurrentSlot = slots[3]
|
|
coord.stats = stats
|
|
bootCoord.stats = stats
|
|
assert.Equal(t, true, coord.canForge())
|
|
assert.Equal(t, false, bootCoord.canForge())
|
|
}
|
|
|
|
func TestCoordHandleMsgSyncBlock(t *testing.T) {
|
|
ethClientSetup := test.NewClientSetupExample()
|
|
ethClientSetup.ChainID = big.NewInt(int64(chainID))
|
|
bootForger := ethClientSetup.AuctionVariables.BootCoordinator
|
|
|
|
var timer timer
|
|
ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
|
|
modules := newTestModules(t)
|
|
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
|
|
_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
|
|
require.NoError(t, err)
|
|
bid, ok := new(big.Int).SetString("11000000000000000000", 10)
|
|
if !ok {
|
|
panic("bad bid")
|
|
}
|
|
_, err = ethClient.AuctionBidSimple(3, bid)
|
|
require.NoError(t, err)
|
|
|
|
slots := [4]common.Slot{}
|
|
for i := 0; i < 4; i++ {
|
|
slots[i].StartBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(i)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
slots[i].EndBlock = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(i+1)*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
// Only slot 3 has Coordinator winner, the rest are BootCoordinator
|
|
if i == 3 {
|
|
slots[i].Forger = forger
|
|
} else {
|
|
slots[i].Forger = bootForger
|
|
}
|
|
}
|
|
|
|
var msg MsgSyncBlock
|
|
coord.stats = msg.Stats
|
|
ctx := context.Background()
|
|
|
|
// Slot 0. No bid, so the winner is the boot coordinator
|
|
// pipelineStarted: false -> false
|
|
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
|
|
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
|
|
coord.stats.Sync.Auction.CurrentSlot = slots[0]
|
|
assert.Equal(t, false, coord.canForge())
|
|
msg.Stats = coord.stats
|
|
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
|
|
assert.Nil(t, coord.pipeline)
|
|
|
|
// Slot 0. No bid, and we reach the deadline, so anyone can forge
|
|
// pipelineStarted: false -> true
|
|
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(ethClientSetup.AuctionVariables.SlotDeadline)
|
|
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
|
|
coord.stats.Sync.Auction.CurrentSlot = slots[0]
|
|
assert.Equal(t, true, coord.canForge())
|
|
msg.Stats = coord.stats
|
|
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
|
|
assert.NotNil(t, coord.pipeline)
|
|
|
|
// Slot 0. No bid, and we reach the deadline, so anyone can forge
|
|
// pipelineStarted: true -> true
|
|
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
|
|
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
|
|
coord.stats.Sync.Auction.CurrentSlot = slots[0]
|
|
assert.Equal(t, true, coord.canForge())
|
|
msg.Stats = coord.stats
|
|
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
|
|
assert.NotNil(t, coord.pipeline)
|
|
|
|
// Slot 1. No bid, so the winner is the boot coordinator
|
|
// pipelineStarted: true -> false
|
|
coord.stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
|
|
1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
|
|
coord.stats.Sync.LastBlock = coord.stats.Eth.LastBlock
|
|
coord.stats.Sync.Auction.CurrentSlot = slots[1]
|
|
assert.Equal(t, false, coord.canForge())
|
|
msg.Stats = coord.stats
|
|
require.NoError(t, coord.handleMsgSyncBlock(ctx, &msg))
|
|
assert.Nil(t, coord.pipeline)
|
|
}
|
|
|
|
// ethAddTokens adds the tokens from the blocks to the blockchain
|
|
func ethAddTokens(blocks []common.BlockData, client *test.Client) {
|
|
for _, block := range blocks {
|
|
for _, token := range block.Rollup.AddedTokens {
|
|
consts := eth.ERC20Consts{
|
|
Name: fmt.Sprintf("Token %d", token.TokenID),
|
|
Symbol: fmt.Sprintf("TK%d", token.TokenID),
|
|
Decimals: 18,
|
|
}
|
|
// tokenConsts[token.TokenID] = consts
|
|
client.CtlAddERC20(token.EthAddr, consts)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCoordinatorStress(t *testing.T) {
|
|
if os.Getenv("TEST_COORD_STRESS") == "" {
|
|
return
|
|
}
|
|
log.Info("Begin Test Coord Stress")
|
|
ethClientSetup := test.NewClientSetupExample()
|
|
ethClientSetup.ChainID = big.NewInt(int64(chainID))
|
|
var timer timer
|
|
ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
|
|
modules := newTestModules(t)
|
|
coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
|
|
syn := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
|
|
|
|
coord.Start()
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
var wg sync.WaitGroup
|
|
|
|
// Synchronizer loop
|
|
wg.Add(1)
|
|
go func() {
|
|
for {
|
|
blockData, _, err := syn.Sync(ctx, nil)
|
|
if ctx.Err() != nil {
|
|
wg.Done()
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
if blockData != nil {
|
|
stats := syn.Stats()
|
|
coord.SendMsg(ctx, MsgSyncBlock{
|
|
Stats: *stats,
|
|
Batches: blockData.Rollup.Batches,
|
|
Vars: synchronizer.SCVariablesPtr{
|
|
Rollup: blockData.Rollup.Vars,
|
|
Auction: blockData.Auction.Vars,
|
|
WDelayer: blockData.WDelayer.Vars,
|
|
},
|
|
})
|
|
} else {
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Blockchain mining loop
|
|
wg.Add(1)
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
wg.Done()
|
|
return
|
|
case <-time.After(1 * time.Second):
|
|
ethClient.CtlMineBlock()
|
|
}
|
|
}
|
|
}()
|
|
|
|
time.Sleep(600 * time.Second)
|
|
|
|
cancel()
|
|
wg.Wait()
|
|
coord.Stop()
|
|
}
|
|
|
|
// TODO: Test Reorg
|
|
// TODO: Test TxMonitor
|
|
// TODO: Test forgeBatch
|
|
// TODO: Test waitServerProof
|
|
// TODO: Test handleReorg
|