arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/coordinator/coordinator_test.go
coordinator/coordinator_test.goRaw
  1package coordinator
  2
  3import (
  4	"io/ioutil"
  5	"math/big"
  6	"os"
  7	"testing"
  8	"time"
  9
 10	ethCommon "github.com/ethereum/go-ethereum/common"
 11	"github.com/hermeznetwork/hermez-node/batchbuilder"
 12	dbUtils "github.com/hermeznetwork/hermez-node/db"
 13	"github.com/hermeznetwork/hermez-node/db/l2db"
 14	"github.com/hermeznetwork/hermez-node/db/statedb"
 15	"github.com/hermeznetwork/hermez-node/log"
 16	"github.com/hermeznetwork/hermez-node/synchronizer"
 17	"github.com/hermeznetwork/hermez-node/test"
 18	"github.com/hermeznetwork/hermez-node/txselector"
 19	"github.com/stretchr/testify/assert"
 20	"github.com/stretchr/testify/require"
 21)
 22
 23var deleteme = []string{}
 24
 25func TestMain(m *testing.M) {
 26	exitVal := m.Run()
 27	for _, dir := range deleteme {
 28		if err := os.RemoveAll(dir); err != nil {
 29			panic(err)
 30		}
 31	}
 32	os.Exit(exitVal)
 33}
 34
 35func newTestModules(t *testing.T) (*txselector.TxSelector, *batchbuilder.BatchBuilder) { // FUTURE once Synchronizer is ready, should return it also
 36	nLevels := 32
 37
 38	syncDBPath, err := ioutil.TempDir("", "tmpSyncDB")
 39	require.Nil(t, err)
 40	deleteme = append(deleteme, syncDBPath)
 41	syncSdb, err := statedb.NewStateDB(syncDBPath, statedb.TypeSynchronizer, nLevels)
 42	assert.Nil(t, err)
 43
 44	pass := os.Getenv("POSTGRES_PASS")
 45	db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 46	require.Nil(t, err)
 47	l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
 48
 49	txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
 50	require.Nil(t, err)
 51	deleteme = append(deleteme, txselDir)
 52	txsel, err := txselector.NewTxSelector(txselDir, syncSdb, l2DB, 10, 10, 10)
 53	assert.Nil(t, err)
 54
 55	bbDir, err := ioutil.TempDir("", "tmpBatchBuilderDB")
 56	require.Nil(t, err)
 57	deleteme = append(deleteme, bbDir)
 58	bb, err := batchbuilder.NewBatchBuilder(bbDir, syncSdb, nil, 0, uint64(nLevels))
 59	assert.Nil(t, err)
 60
 61	// l1Txs, coordinatorL1Txs, poolL2Txs := test.GenerateTestTxsFromSet(t, test.SetTest0)
 62
 63	return txsel, bb
 64}
 65
 66type timer struct {
 67	time int64
 68}
 69
 70func (t *timer) Time() int64 {
 71	currentTime := t.time
 72	t.time++
 73	return currentTime
 74}
 75
 76var bidder = ethCommon.HexToAddress("0x6b175474e89094c44da98b954eedeac495271d0f")
 77var forger = ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
 78
 79func newTestCoordinator(t *testing.T, forgerAddr ethCommon.Address, ethClient *test.Client, ethClientSetup *test.ClientSetup) *Coordinator {
 80	txsel, bb := newTestModules(t)
 81
 82	debugBatchPath, err := ioutil.TempDir("", "tmpDebugBatch")
 83	require.Nil(t, err)
 84	deleteme = append(deleteme, debugBatchPath)
 85
 86	conf := Config{
 87		ForgerAddress:          forgerAddr,
 88		ConfirmBlocks:          5,
 89		L1BatchTimeoutPerc:     0.5,
 90		EthClientAttempts:      5,
 91		EthClientAttemptsDelay: 100 * time.Millisecond,
 92		TxManagerCheckInterval: 500 * time.Millisecond,
 93		DebugBatchPath:         debugBatchPath,
 94	}
 95	serverProofs := []ServerProofInterface{&ServerProofMock{}, &ServerProofMock{}}
 96
 97	scConsts := &synchronizer.SCConsts{
 98		Rollup:   *ethClientSetup.RollupConstants,
 99		Auction:  *ethClientSetup.AuctionConstants,
100		WDelayer: *ethClientSetup.WDelayerConstants,
101	}
102	initSCVars := &synchronizer.SCVariables{
103		Rollup:   *ethClientSetup.RollupVariables,
104		Auction:  *ethClientSetup.AuctionVariables,
105		WDelayer: *ethClientSetup.WDelayerVariables,
106	}
107	coord, err := NewCoordinator(conf, nil, txsel, bb, serverProofs, ethClient, scConsts, initSCVars)
108	require.Nil(t, err)
109	return coord
110}
111
112func TestCoordinatorFlow(t *testing.T) {
113	if os.Getenv("TEST_COORD_FLOW") == "" {
114		return
115	}
116	ethClientSetup := test.NewClientSetupExample()
117	var timer timer
118	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
119	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
120
121	// Bid for slot 2 and 4
122	_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
123	require.Nil(t, err)
124	_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
125	require.Nil(t, err)
126	_, err = ethClient.AuctionBidSimple(4, big.NewInt(9999))
127	require.Nil(t, err)
128
129	coord.Start()
130	time.Sleep(1 * time.Second)
131
132	waitForSlot := func(slot int64) {
133		for {
134			blockNum, err := ethClient.EthLastBlock()
135			require.Nil(t, err)
136			nextBlockSlot, err := ethClient.AuctionGetSlotNumber(blockNum + 1)
137			require.Nil(t, err)
138			if nextBlockSlot == slot {
139				break
140			}
141			ethClient.CtlMineBlock()
142			time.Sleep(100 * time.Millisecond)
143			var stats synchronizer.Stats
144			stats.Eth.LastBlock = *ethClient.CtlLastBlock()
145			stats.Sync.LastBlock = *ethClient.CtlLastBlock()
146			canForge, err := ethClient.AuctionCanForge(forger, blockNum+1)
147			require.Nil(t, err)
148			if canForge {
149				// fmt.Println("DBG canForge")
150				stats.Sync.Auction.CurrentSlot.Forger = forger
151			}
152			coord.SendMsg(MsgSyncStats{
153				Stats: stats,
154			})
155		}
156	}
157
158	// NOTE: With the current test, the coordinator will enter in forge
159	// time before the bidded slot because no one else is forging in the
160	// other slots before the slot deadline.
161	// simulate forgeSequence time
162	waitForSlot(2)
163	log.Info("~~~ simulate entering in forge time")
164	time.Sleep(1 * time.Second)
165
166	// simulate going out from forgeSequence
167	waitForSlot(3)
168	log.Info("~~~ simulate going out from forge time")
169	time.Sleep(1 * time.Second)
170
171	// simulate entering forgeSequence time again
172	waitForSlot(4)
173	log.Info("~~~ simulate entering in forge time again")
174	time.Sleep(2 * time.Second)
175
176	// simulate stopping forgerLoop by channel
177	log.Info("~~~ simulate stopping forgerLoop by closing coordinator stopch")
178	coord.Stop()
179	time.Sleep(1 * time.Second)
180}
181
182func TestCoordinatorStartStop(t *testing.T) {
183	ethClientSetup := test.NewClientSetupExample()
184	var timer timer
185	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
186	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
187	coord.Start()
188	coord.Stop()
189}
190
191func TestCoordCanForge(t *testing.T) {
192	ethClientSetup := test.NewClientSetupExample()
193	bootForger := ethClientSetup.AuctionVariables.BootCoordinator
194
195	var timer timer
196	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
197	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
198	_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
199	require.Nil(t, err)
200	_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
201	require.Nil(t, err)
202
203	bootCoord := newTestCoordinator(t, bootForger, ethClient, ethClientSetup)
204
205	assert.Equal(t, forger, coord.cfg.ForgerAddress)
206	assert.Equal(t, bootForger, bootCoord.cfg.ForgerAddress)
207	ethBootCoord, err := ethClient.AuctionGetBootCoordinator()
208	require.Nil(t, err)
209	assert.Equal(t, &bootForger, ethBootCoord)
210
211	var stats synchronizer.Stats
212
213	// Slot 0.  No bid, so the winner is the boot coordinator
214	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
215	stats.Sync.LastBlock = stats.Eth.LastBlock
216	stats.Sync.Auction.CurrentSlot.Forger = bootForger
217	assert.Equal(t, false, coord.canForge(&stats))
218	assert.Equal(t, true, bootCoord.canForge(&stats))
219
220	// Slot 0.  No bid, and we reach the deadline, so anyone can forge
221	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
222		int64(ethClientSetup.AuctionVariables.SlotDeadline)
223	stats.Sync.LastBlock = stats.Eth.LastBlock
224	stats.Sync.Auction.CurrentSlot.Forger = bootForger
225	assert.Equal(t, true, coord.canForge(&stats))
226	assert.Equal(t, true, bootCoord.canForge(&stats))
227
228	// Slot 1. coordinator bid, so the winner is the coordinator
229	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
230		1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
231	stats.Sync.LastBlock = stats.Eth.LastBlock
232	stats.Sync.Auction.CurrentSlot.Forger = forger
233	assert.Equal(t, true, coord.canForge(&stats))
234	assert.Equal(t, false, bootCoord.canForge(&stats))
235}
236
237func TestCoordHandleMsgSyncStats(t *testing.T) {
238	ethClientSetup := test.NewClientSetupExample()
239	bootForger := ethClientSetup.AuctionVariables.BootCoordinator
240
241	var timer timer
242	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
243	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
244	_, err := ethClient.AuctionSetCoordinator(forger, "https://foo.bar")
245	require.Nil(t, err)
246	_, err = ethClient.AuctionBidSimple(2, big.NewInt(9999))
247	require.Nil(t, err)
248
249	var stats synchronizer.Stats
250
251	// Slot 0.  No bid, so the winner is the boot coordinator
252	// pipelineStarted: false -> false
253	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum
254	stats.Sync.LastBlock = stats.Eth.LastBlock
255	stats.Sync.Auction.CurrentSlot.Forger = bootForger
256	assert.Equal(t, false, coord.canForge(&stats))
257	require.Nil(t, coord.handleMsgSyncStats(&stats))
258	assert.Nil(t, coord.pipeline)
259
260	// Slot 0.  No bid, and we reach the deadline, so anyone can forge
261	// pipelineStarted: false -> true
262	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
263		int64(ethClientSetup.AuctionVariables.SlotDeadline)
264	stats.Sync.LastBlock = stats.Eth.LastBlock
265	stats.Sync.Auction.CurrentSlot.Forger = bootForger
266	assert.Equal(t, true, coord.canForge(&stats))
267	require.Nil(t, coord.handleMsgSyncStats(&stats))
268	assert.NotNil(t, coord.pipeline)
269
270	// Slot 0.  No bid, and we reach the deadline, so anyone can forge
271	// pipelineStarted: true -> true
272	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
273		int64(ethClientSetup.AuctionVariables.SlotDeadline) + 1
274	stats.Sync.LastBlock = stats.Eth.LastBlock
275	stats.Sync.Auction.CurrentSlot.Forger = bootForger
276	assert.Equal(t, true, coord.canForge(&stats))
277	require.Nil(t, coord.handleMsgSyncStats(&stats))
278	assert.NotNil(t, coord.pipeline)
279
280	// Slot 0. No bid, so the winner is the boot coordinator
281	// pipelineStarted: true -> false
282	stats.Eth.LastBlock.Num = ethClientSetup.AuctionConstants.GenesisBlockNum +
283		1*int64(ethClientSetup.AuctionConstants.BlocksPerSlot)
284	stats.Sync.LastBlock = stats.Eth.LastBlock
285	stats.Sync.Auction.CurrentSlot.Forger = bootForger
286	assert.Equal(t, false, coord.canForge(&stats))
287	require.Nil(t, coord.handleMsgSyncStats(&stats))
288	assert.Nil(t, coord.pipeline)
289}
290
291func TestPipelineShouldL1L2Batch(t *testing.T) {
292	ethClientSetup := test.NewClientSetupExample()
293
294	var timer timer
295	ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
296	coord := newTestCoordinator(t, forger, ethClient, ethClientSetup)
297	pipeline := coord.newPipeline()
298	pipeline.vars = coord.vars
299
300	// Check that the parameters are the ones we expect and use in this test
301	require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
302	require.Equal(t, int64(9), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
303	l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
304	l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
305
306	var stats synchronizer.Stats
307
308	startBlock := int64(100)
309
310	//
311	// No scheduled L1Batch
312	//
313
314	// Last L1Batch was a long time ago
315	stats.Eth.LastBlock.Num = startBlock
316	stats.Sync.LastBlock = stats.Eth.LastBlock
317	stats.Sync.LastL1BatchBlock = 0
318	pipeline.stats = stats
319	assert.Equal(t, true, pipeline.shouldL1L2Batch())
320
321	stats.Sync.LastL1BatchBlock = startBlock
322
323	// We are are one block before the timeout range * 0.5
324	stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
325	stats.Sync.LastBlock = stats.Eth.LastBlock
326	pipeline.stats = stats
327	assert.Equal(t, false, pipeline.shouldL1L2Batch())
328
329	// We are are at timeout range * 0.5
330	stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
331	stats.Sync.LastBlock = stats.Eth.LastBlock
332	pipeline.stats = stats
333	assert.Equal(t, true, pipeline.shouldL1L2Batch())
334
335	//
336	// Scheduled L1Batch
337	//
338	pipeline.lastScheduledL1BatchBlockNum = startBlock
339	stats.Sync.LastL1BatchBlock = startBlock - 10
340
341	// We are are one block before the timeout range * 0.5
342	stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc) - 1
343	stats.Sync.LastBlock = stats.Eth.LastBlock
344	pipeline.stats = stats
345	assert.Equal(t, false, pipeline.shouldL1L2Batch())
346
347	// We are are at timeout range * 0.5
348	stats.Eth.LastBlock.Num = startBlock + int64(float64(l1BatchTimeout)*l1BatchTimeoutPerc)
349	stats.Sync.LastBlock = stats.Eth.LastBlock
350	pipeline.stats = stats
351	assert.Equal(t, true, pipeline.shouldL1L2Batch())
352}
353
354// TODO: Test Reorg
355// TODO: Test Pipeline
356// TODO: Test TxMonitor