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/historydb"
14 "github.com/hermeznetwork/hermez-node/db/l2db"
15 "github.com/hermeznetwork/hermez-node/db/statedb"
16 "github.com/hermeznetwork/hermez-node/log"
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
23func newTestModules(t *testing.T) (*txselector.TxSelector, *batchbuilder.BatchBuilder) { // FUTURE once Synchronizer is ready, should return it also
24 nLevels := 32
25
26 synchDBPath, err := ioutil.TempDir("", "tmpSynchDB")
27 require.Nil(t, err)
28 synchSdb, err := statedb.NewStateDB(synchDBPath, statedb.TypeSynchronizer, nLevels)
29 assert.Nil(t, err)
30
31 // txselDBPath, err := ioutil.TempDir("", "tmpTxSelDB")
32 // require.Nil(t, err)
33 // bbDBPath, err := ioutil.TempDir("", "tmpBBDB")
34 // require.Nil(t, err)
35 // txselSdb, err := statedb.NewLocalStateDB(txselDBPath, synchSdb, statedb.TypeTxSelector, nLevels)
36 // assert.Nil(t, err)
37 // bbSdb, err := statedb.NewLocalStateDB(bbDBPath, synchSdb, statedb.TypeBatchBuilder, nLevels)
38 // assert.Nil(t, err)
39
40 pass := os.Getenv("POSTGRES_PASS")
41 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
42 require.Nil(t, err)
43 l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
44
45 txselDir, err := ioutil.TempDir("", "tmpTxSelDB")
46 require.Nil(t, err)
47 txsel, err := txselector.NewTxSelector(txselDir, synchSdb, l2DB, 10, 10, 10)
48 assert.Nil(t, err)
49
50 bbDir, err := ioutil.TempDir("", "tmpBatchBuilderDB")
51 require.Nil(t, err)
52 bb, err := batchbuilder.NewBatchBuilder(bbDir, synchSdb, nil, 0, uint64(nLevels))
53 assert.Nil(t, err)
54
55 // l1Txs, coordinatorL1Txs, poolL2Txs := test.GenerateTestTxsFromSet(t, test.SetTest0)
56
57 return txsel, bb
58}
59
60// CoordNode is an example of a Node that handles the goroutines for the coordinator
61type CoordNode struct {
62 c *Coordinator
63 stopForge chan bool
64 stopGetProofCallForge chan bool
65 stopForgeCallConfirm chan bool
66}
67
68func NewCoordNode(c *Coordinator) *CoordNode {
69 return &CoordNode{
70 c: c,
71 }
72}
73
74func (cn *CoordNode) Start() {
75 log.Debugw("Starting CoordNode...")
76 cn.stopForge = make(chan bool)
77 cn.stopGetProofCallForge = make(chan bool)
78 cn.stopForgeCallConfirm = make(chan bool)
79 queueSize := 8
80 batchCh0 := make(chan *BatchInfo, queueSize)
81 batchCh1 := make(chan *BatchInfo, queueSize)
82
83 go func() {
84 for {
85 select {
86 case <-cn.stopForge:
87 return
88 default:
89 if forge, err := cn.c.ForgeLoopFn(batchCh0, cn.stopForge); err == ErrStop {
90 return
91 } else if err != nil {
92 log.Errorw("CoordNode ForgeLoopFn", "error", err)
93 time.Sleep(200 * time.Millisecond) // Avoid overflowing log with errors
94 } else if !forge {
95 time.Sleep(200 * time.Millisecond)
96 }
97 }
98 }
99 }()
100 go func() {
101 for {
102 select {
103 case <-cn.stopGetProofCallForge:
104 return
105 default:
106 if err := cn.c.GetProofCallForgeLoopFn(
107 batchCh0, batchCh1, cn.stopGetProofCallForge); err == ErrStop {
108 return
109 } else if err != nil {
110 log.Errorw("CoordNode GetProofCallForgeLoopFn", "error", err)
111 }
112 }
113 }
114 }()
115 go func() {
116 for {
117 select {
118 case <-cn.stopForgeCallConfirm:
119 return
120 default:
121 if err := cn.c.ForgeCallConfirmLoopFn(
122 batchCh1, cn.stopForgeCallConfirm); err == ErrStop {
123 return
124 } else if err != nil {
125 log.Errorw("CoordNode ForgeCallConfirmLoopFn", "error", err)
126 }
127 }
128 }
129 }()
130}
131
132func (cn *CoordNode) Stop() {
133 log.Debugw("Stopping CoordNode...")
134 cn.stopForge <- true
135 cn.stopGetProofCallForge <- true
136 cn.stopForgeCallConfirm <- true
137}
138
139type timer struct {
140 time int64
141}
142
143func (t *timer) Time() int64 {
144 currentTime := t.time
145 t.time++
146 return currentTime
147}
148
149func waitForSlot(t *testing.T, c *test.Client, slot int64) {
150 for {
151 blockNum, err := c.EthCurrentBlock()
152 require.Nil(t, err)
153 nextBlockSlot, err := c.AuctionGetSlotNumber(blockNum + 1)
154 require.Nil(t, err)
155 if nextBlockSlot == slot {
156 break
157 }
158 c.CtlMineBlock()
159 }
160}
161
162func TestCoordinator(t *testing.T) {
163 txsel, bb := newTestModules(t)
164
165 conf := Config{}
166 hdb := &historydb.HistoryDB{}
167 serverProofs := []ServerProofInterface{&ServerProofMock{}, &ServerProofMock{}}
168
169 var timer timer
170 ethClientSetup := test.NewClientSetupExample()
171 addr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6")
172 ethClient := test.NewClient(true, &timer, &addr, ethClientSetup)
173
174 // Bid for slot 2 and 4
175 _, err := ethClient.AuctionRegisterCoordinator(addr, "https://foo.bar")
176 require.Nil(t, err)
177 _, err = ethClient.AuctionBid(2, big.NewInt(9999), addr)
178 require.Nil(t, err)
179 _, err = ethClient.AuctionBid(4, big.NewInt(9999), addr)
180 require.Nil(t, err)
181
182 c := NewCoordinator(conf, hdb, txsel, bb, serverProofs, ethClient)
183 cn := NewCoordNode(c)
184 cn.Start()
185 time.Sleep(1 * time.Second)
186
187 // simulate forgeSequence time
188 waitForSlot(t, ethClient, 2)
189 log.Info("simulate entering in forge time")
190 time.Sleep(1 * time.Second)
191
192 // simulate going out from forgeSequence
193 waitForSlot(t, ethClient, 3)
194 log.Info("simulate going out from forge time")
195 time.Sleep(1 * time.Second)
196
197 // simulate entering forgeSequence time again
198 waitForSlot(t, ethClient, 4)
199 log.Info("simulate entering in forge time again")
200 time.Sleep(2 * time.Second)
201
202 // simulate stopping forgerLoop by channel
203 log.Info("simulate stopping forgerLoop by closing coordinator stopch")
204 cn.Stop()
205 time.Sleep(1 * time.Second)
206}