hermez-node/coordinator/pipeline_test.go
coordinator/pipeline_test.goRaw
1package coordinator
2
3import (
4 "context"
5 "fmt"
6 "io/ioutil"
7 "math/big"
8 "os"
9 "testing"
10
11 ethKeystore "github.com/ethereum/go-ethereum/accounts/keystore"
12 ethCommon "github.com/ethereum/go-ethereum/common"
13 "github.com/ethereum/go-ethereum/crypto"
14 "github.com/ethereum/go-ethereum/ethclient"
15 "github.com/hermeznetwork/hermez-node/common"
16 "github.com/hermeznetwork/hermez-node/db/historydb"
17 "github.com/hermeznetwork/hermez-node/db/statedb"
18 "github.com/hermeznetwork/hermez-node/eth"
19 "github.com/hermeznetwork/hermez-node/prover"
20 "github.com/hermeznetwork/hermez-node/synchronizer"
21 "github.com/hermeznetwork/hermez-node/test"
22 "github.com/hermeznetwork/hermez-node/test/til"
23 "github.com/iden3/go-merkletree"
24 "github.com/stretchr/testify/assert"
25 "github.com/stretchr/testify/require"
26)
27
28func newBigInt(s string) *big.Int {
29 v, ok := new(big.Int).SetString(s, 10)
30 if !ok {
31 panic(fmt.Errorf("Can't set big.Int from %s", s))
32 }
33 return v
34}
35
36func TestPipelineShouldL1L2Batch(t *testing.T) {
37 ethClientSetup := test.NewClientSetupExample()
38 ethClientSetup.ChainID = big.NewInt(int64(chainID))
39
40 var timer timer
41 ctx := context.Background()
42 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
43 modules := newTestModules(t)
44 var stats synchronizer.Stats
45 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
46 pipeline, err := coord.newPipeline(ctx)
47 require.NoError(t, err)
48 pipeline.vars = coord.vars
49
50 // Check that the parameters are the ones we expect and use in this test
51 require.Equal(t, 0.5, pipeline.cfg.L1BatchTimeoutPerc)
52 require.Equal(t, int64(10), ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout)
53 l1BatchTimeoutPerc := pipeline.cfg.L1BatchTimeoutPerc
54 l1BatchTimeout := ethClientSetup.RollupVariables.ForgeL1L2BatchTimeout
55
56 startBlock := int64(100)
57 // Empty batchInfo to pass to shouldL1L2Batch() which sets debug information
58 batchInfo := BatchInfo{}
59
60 //
61 // No scheduled L1Batch
62 //
63
64 // Last L1Batch was a long time ago
65 stats.Eth.LastBlock.Num = startBlock
66 stats.Sync.LastBlock = stats.Eth.LastBlock
67 stats.Sync.LastL1BatchBlock = 0
68 pipeline.stats = stats
69 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
70
71 stats.Sync.LastL1BatchBlock = startBlock
72
73 // We are are one block before the timeout range * 0.5
74 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
75 stats.Sync.LastBlock = stats.Eth.LastBlock
76 pipeline.stats = stats
77 assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
78
79 // We are are at timeout range * 0.5
80 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
81 stats.Sync.LastBlock = stats.Eth.LastBlock
82 pipeline.stats = stats
83 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
84
85 //
86 // Scheduled L1Batch
87 //
88 pipeline.state.lastScheduledL1BatchBlockNum = startBlock
89 stats.Sync.LastL1BatchBlock = startBlock - 10
90
91 // We are are one block before the timeout range * 0.5
92 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc) - 1
93 stats.Sync.LastBlock = stats.Eth.LastBlock
94 pipeline.stats = stats
95 assert.Equal(t, false, pipeline.shouldL1L2Batch(&batchInfo))
96
97 // We are are at timeout range * 0.5
98 stats.Eth.LastBlock.Num = startBlock - 1 + int64(float64(l1BatchTimeout-1)*l1BatchTimeoutPerc)
99 stats.Sync.LastBlock = stats.Eth.LastBlock
100 pipeline.stats = stats
101 assert.Equal(t, true, pipeline.shouldL1L2Batch(&batchInfo))
102}
103
104const testTokensLen = 3
105const testUsersLen = 4
106
107func preloadSync(t *testing.T, ethClient *test.Client, sync *synchronizer.Synchronizer,
108 historyDB *historydb.HistoryDB, stateDB *statedb.StateDB) *til.Context {
109 // Create a set with `testTokensLen` tokens and for each token
110 // `testUsersLen` accounts.
111 var set []til.Instruction
112 // set = append(set, til.Instruction{Typ: "Blockchain"})
113 for tokenID := 1; tokenID < testTokensLen; tokenID++ {
114 set = append(set, til.Instruction{
115 Typ: til.TypeAddToken,
116 TokenID: common.TokenID(tokenID),
117 })
118 }
119 depositAmount, ok := new(big.Int).SetString("10225000000000000000000000000000000", 10)
120 require.True(t, ok)
121 for tokenID := 0; tokenID < testTokensLen; tokenID++ {
122 for user := 0; user < testUsersLen; user++ {
123 set = append(set, til.Instruction{
124 Typ: common.TxTypeCreateAccountDeposit,
125 TokenID: common.TokenID(tokenID),
126 DepositAmount: depositAmount,
127 From: fmt.Sprintf("User%d", user),
128 })
129 }
130 }
131 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
132 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
133 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
134
135 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
136 blocks, err := tc.GenerateBlocksFromInstructions(set)
137 require.NoError(t, err)
138 require.NotNil(t, blocks)
139 // Set StateRoots for batches manually (til doesn't set it)
140 blocks[0].Rollup.Batches[0].Batch.StateRoot =
141 newBigInt("0")
142 blocks[0].Rollup.Batches[1].Batch.StateRoot =
143 newBigInt("10941365282189107056349764238909072001483688090878331371699519307087372995595")
144
145 ethAddTokens(blocks, ethClient)
146 err = ethClient.CtlAddBlocks(blocks)
147 require.NoError(t, err)
148
149 ctx := context.Background()
150 for {
151 syncBlock, discards, err := sync.Sync(ctx, nil)
152 require.NoError(t, err)
153 require.Nil(t, discards)
154 if syncBlock == nil {
155 break
156 }
157 }
158 dbTokens, err := historyDB.GetAllTokens()
159 require.Nil(t, err)
160 require.Equal(t, testTokensLen, len(dbTokens))
161
162 dbAccounts, err := historyDB.GetAllAccounts()
163 require.Nil(t, err)
164 require.Equal(t, testTokensLen*testUsersLen, len(dbAccounts))
165
166 sdbAccounts, err := stateDB.TestGetAccounts()
167 require.Nil(t, err)
168 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
169
170 return tc
171}
172
173func TestPipelineForgeBatchWithTxs(t *testing.T) {
174 ethClientSetup := test.NewClientSetupExample()
175 ethClientSetup.ChainID = big.NewInt(int64(chainID))
176
177 var timer timer
178 ctx := context.Background()
179 ethClient := test.NewClient(true, &timer, &bidder, ethClientSetup)
180 modules := newTestModules(t)
181 coord := newTestCoordinator(t, forger, ethClient, ethClientSetup, modules)
182 sync := newTestSynchronizer(t, ethClient, ethClientSetup, modules)
183
184 // preload the synchronier (via the test ethClient) some tokens and
185 // users with positive balances
186 tilCtx := preloadSync(t, ethClient, sync, modules.historyDB, modules.stateDB)
187 syncStats := sync.Stats()
188 batchNum := syncStats.Sync.LastBatch.BatchNum
189 syncSCVars := sync.SCVars()
190
191 pipeline, err := coord.newPipeline(ctx)
192 require.NoError(t, err)
193
194 // Insert some l2txs in the Pool
195 setPool := `
196Type: PoolL2
197
198PoolTransfer(0) User0-User1: 100 (126)
199PoolTransfer(0) User1-User2: 200 (126)
200PoolTransfer(0) User2-User3: 300 (126)
201 `
202 l2txs, err := tilCtx.GeneratePoolL2Txs(setPool)
203 require.NoError(t, err)
204 for _, tx := range l2txs {
205 err := modules.l2DB.AddTxTest(&tx) //nolint:gosec
206 require.NoError(t, err)
207 }
208
209 err = pipeline.reset(batchNum, syncStats, &synchronizer.SCVariables{
210 Rollup: *syncSCVars.Rollup,
211 Auction: *syncSCVars.Auction,
212 WDelayer: *syncSCVars.WDelayer,
213 })
214 require.NoError(t, err)
215 // Sanity check
216 sdbAccounts, err := pipeline.txSelector.LocalAccountsDB().TestGetAccounts()
217 require.Nil(t, err)
218 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
219
220 // Sanity check
221 sdbAccounts, err = pipeline.batchBuilder.LocalStateDB().TestGetAccounts()
222 require.Nil(t, err)
223 require.Equal(t, testTokensLen*testUsersLen, len(sdbAccounts))
224
225 // Sanity check
226 require.Equal(t, modules.stateDB.MT.Root(),
227 pipeline.batchBuilder.LocalStateDB().MT.Root())
228
229 batchNum++
230
231 batchInfo, err := pipeline.forgeBatch(batchNum)
232 require.NoError(t, err)
233 assert.Equal(t, 3, len(batchInfo.L2Txs))
234
235 batchNum++
236 batchInfo, err = pipeline.forgeBatch(batchNum)
237 require.NoError(t, err)
238 assert.Equal(t, 0, len(batchInfo.L2Txs))
239}
240
241func TestEthRollupForgeBatch(t *testing.T) {
242 if os.Getenv("TEST_ROLLUP_FORGE_BATCH") == "" {
243 return
244 }
245 const web3URL = "http://localhost:8545"
246 const password = "test"
247 addr := ethCommon.HexToAddress("0xb4124ceb3451635dacedd11767f004d8a28c6ee7")
248 sk, err := crypto.HexToECDSA(
249 "a8a54b2d8197bc0b19bb8a084031be71835580a01e70a45a13babd16c9bc1563")
250 require.NoError(t, err)
251 rollupAddr := ethCommon.HexToAddress("0x8EEaea23686c319133a7cC110b840d1591d9AeE0")
252 pathKeystore, err := ioutil.TempDir("", "tmpKeystore")
253 require.NoError(t, err)
254 deleteme = append(deleteme, pathKeystore)
255 ctx := context.Background()
256 batchInfo := &BatchInfo{}
257 proofClient := &prover.MockClient{}
258 chainID := uint16(0)
259
260 ethClient, err := ethclient.Dial(web3URL)
261 require.NoError(t, err)
262 ethCfg := eth.EthereumConfig{
263 CallGasLimit: 300000,
264 GasPriceDiv: 100,
265 }
266 scryptN := ethKeystore.LightScryptN
267 scryptP := ethKeystore.LightScryptP
268 keyStore := ethKeystore.NewKeyStore(pathKeystore,
269 scryptN, scryptP)
270 account, err := keyStore.ImportECDSA(sk, password)
271 require.NoError(t, err)
272 require.Equal(t, account.Address, addr)
273 err = keyStore.Unlock(account, password)
274 require.NoError(t, err)
275
276 client, err := eth.NewClient(ethClient, &account, keyStore, ð.ClientConfig{
277 Ethereum: ethCfg,
278 Rollup: eth.RollupConfig{
279 Address: rollupAddr,
280 },
281 Auction: eth.AuctionConfig{
282 Address: ethCommon.Address{},
283 TokenHEZ: eth.TokenConfig{
284 Address: ethCommon.Address{},
285 Name: "HEZ",
286 },
287 },
288 WDelayer: eth.WDelayerConfig{
289 Address: ethCommon.Address{},
290 },
291 })
292 require.NoError(t, err)
293
294 zkInputs := common.NewZKInputs(chainID, 100, 24, 512, 32, big.NewInt(1))
295 zkInputs.Metadata.NewStateRootRaw = &merkletree.Hash{1}
296 zkInputs.Metadata.NewExitRootRaw = &merkletree.Hash{2}
297 batchInfo.ZKInputs = zkInputs
298 err = proofClient.CalculateProof(ctx, batchInfo.ZKInputs)
299 require.NoError(t, err)
300
301 proof, pubInputs, err := proofClient.GetProof(ctx)
302 require.NoError(t, err)
303 batchInfo.Proof = proof
304 batchInfo.PublicInputs = pubInputs
305
306 batchInfo.ForgeBatchArgs = prepareForgeBatchArgs(batchInfo)
307 auth, err := client.NewAuth()
308 require.NoError(t, err)
309 _, err = client.RollupForgeBatch(batchInfo.ForgeBatchArgs, auth)
310 require.NoError(t, err)
311 batchInfo.Proof = proof
312}