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