hermez-node/test/til/txs.go
test/til/txs.goRaw
1package til
2
3import (
4 "crypto/ecdsa"
5 "fmt"
6 "math/big"
7 "strconv"
8 "strings"
9 "time"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 ethCrypto "github.com/ethereum/go-ethereum/crypto"
13 "github.com/hermeznetwork/hermez-node/common"
14 "github.com/hermeznetwork/hermez-node/log"
15 "github.com/iden3/go-iden3-crypto/babyjub"
16)
17
18func newBatchData(batchNum int) common.BatchData {
19 return common.BatchData{
20 L1CoordinatorTxs: []common.L1Tx{},
21 L2Txs: []common.L2Tx{},
22 Batch: common.Batch{
23 BatchNum: common.BatchNum(batchNum),
24 StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0)},
25 }
26}
27
28func newBlock(blockNum int64) common.BlockData {
29 return common.BlockData{
30 Block: common.Block{
31 EthBlockNum: blockNum,
32 },
33 Rollup: common.RollupData{
34 L1UserTxs: []common.L1Tx{},
35 },
36 }
37}
38
39// Context contains the data of the test
40type Context struct {
41 Instructions []instruction
42 userNames []string
43 Users map[string]*User
44 LastRegisteredTokenID common.TokenID
45 l1CreatedAccounts map[string]*Account
46
47 // rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch
48 rollupConstMaxL1UserTx int
49
50 idx int
51 currBlock common.BlockData
52 currBatch common.BatchData
53 currBatchNum int
54 Queues [][]L1Tx
55 ToForgeNum int
56 openToForge int
57 currBatchTest struct {
58 l1CoordinatorTxs []L1Tx
59 l2Txs []L2Tx
60 }
61 blockNum int64
62}
63
64// NewContext returns a new Context
65func NewContext(rollupConstMaxL1UserTx int) *Context {
66 currBatchNum := 1 // The protocol defines the first batchNum to be 1
67 return &Context{
68 Users: make(map[string]*User),
69 l1CreatedAccounts: make(map[string]*Account),
70 LastRegisteredTokenID: 0,
71
72 rollupConstMaxL1UserTx: rollupConstMaxL1UserTx,
73 idx: common.UserThreshold,
74 // We use some placeholder values for StateRoot and ExitTree
75 // because these values will never be nil
76 currBlock: newBlock(2), //nolint:gomnd
77 currBatch: newBatchData(currBatchNum),
78 currBatchNum: currBatchNum,
79 // start with 2 queues, one for toForge, and the other for openToForge
80 Queues: make([][]L1Tx, 2),
81 ToForgeNum: 0,
82 openToForge: 1,
83 //nolint:gomnd
84 blockNum: 2, // rollup genesis blockNum
85 }
86}
87
88// Account contains the data related to the account for a specific TokenID of a User
89type Account struct {
90 Idx common.Idx
91 Nonce common.Nonce
92}
93
94// User contains the data related to a testing user
95type User struct {
96 BJJ *babyjub.PrivateKey
97 Addr ethCommon.Address
98 Accounts map[common.TokenID]*Account
99}
100
101// L1Tx is the data structure used internally for transaction test generation,
102// which contains a common.L1Tx data plus some intermediate data for the
103// transaction generation.
104type L1Tx struct {
105 lineNum int
106 fromIdxName string
107 toIdxName string
108
109 L1Tx common.L1Tx
110}
111
112// L2Tx is the data structure used internally for transaction test generation,
113// which contains a common.L2Tx data plus some intermediate data for the
114// transaction generation.
115type L2Tx struct {
116 lineNum int
117 fromIdxName string
118 toIdxName string
119 tokenID common.TokenID
120 L2Tx common.L2Tx
121}
122
123// GenerateBlocks returns an array of BlockData for a given set. It uses the
124// users (keys & nonces) of the Context.
125func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) {
126 parser := newParser(strings.NewReader(set))
127 parsedSet, err := parser.parse()
128 if err != nil {
129 return nil, err
130 }
131 if parsedSet.typ != setTypeBlockchain {
132 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypeBlockchain, parsedSet.typ)
133 }
134
135 tc.Instructions = parsedSet.instructions
136 tc.userNames = parsedSet.users
137
138 tc.generateKeys(tc.userNames)
139
140 var blocks []common.BlockData
141 for _, inst := range parsedSet.instructions {
142 switch inst.typ {
143 case txTypeCreateAccountDepositCoordinator: // tx source: L1CoordinatorTx
144 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
145 log.Error(err)
146 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
147 }
148 tx := common.L1Tx{
149 FromEthAddr: tc.Users[inst.from].Addr,
150 FromBJJ: tc.Users[inst.from].BJJ.Public(),
151 TokenID: inst.tokenID,
152 Amount: big.NewInt(0),
153 LoadAmount: big.NewInt(int64(inst.loadAmount)),
154 Type: common.TxTypeCreateAccountDeposit, // as txTypeCreateAccountDepositCoordinator is not valid oustide Til package
155 }
156 testTx := L1Tx{
157 lineNum: inst.lineNum,
158 fromIdxName: inst.from,
159 L1Tx: tx,
160 }
161
162 tc.currBatchTest.l1CoordinatorTxs = append(tc.currBatchTest.l1CoordinatorTxs, testTx)
163 case common.TxTypeCreateAccountDeposit, common.TxTypeCreateAccountDepositTransfer: // tx source: L1UserTx
164 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
165 log.Error(err)
166 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
167 }
168 tx := common.L1Tx{
169 FromEthAddr: tc.Users[inst.from].Addr,
170 FromBJJ: tc.Users[inst.from].BJJ.Public(),
171 TokenID: inst.tokenID,
172 Amount: big.NewInt(0),
173 LoadAmount: big.NewInt(int64(inst.loadAmount)),
174 Type: inst.typ,
175 }
176 if inst.typ == common.TxTypeCreateAccountDepositTransfer {
177 tx.Amount = big.NewInt(int64(inst.amount))
178 }
179 testTx := L1Tx{
180 lineNum: inst.lineNum,
181 fromIdxName: inst.from,
182 toIdxName: inst.to,
183 L1Tx: tx,
184 }
185 if err := tc.addToL1Queue(testTx); err != nil {
186 return nil, err
187 }
188 case common.TxTypeDeposit, common.TxTypeDepositTransfer: // tx source: L1UserTx
189 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
190 log.Error(err)
191 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
192 }
193 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
194 log.Error(err)
195 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
196 }
197 tx := common.L1Tx{
198 TokenID: inst.tokenID,
199 Amount: big.NewInt(0),
200 LoadAmount: big.NewInt(int64(inst.loadAmount)),
201 Type: inst.typ,
202 }
203 if inst.typ == common.TxTypeDepositTransfer {
204 tx.Amount = big.NewInt(int64(inst.amount))
205 }
206 testTx := L1Tx{
207 lineNum: inst.lineNum,
208 fromIdxName: inst.from,
209 toIdxName: inst.to,
210 L1Tx: tx,
211 }
212 if err := tc.addToL1Queue(testTx); err != nil {
213 return nil, err
214 }
215 case common.TxTypeTransfer: // L2Tx
216 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
217 log.Error(err)
218 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
219 }
220 tx := common.L2Tx{
221 Amount: big.NewInt(int64(inst.amount)),
222 Fee: common.FeeSelector(inst.fee),
223 Type: common.TxTypeTransfer,
224 EthBlockNum: tc.blockNum,
225 }
226 tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
227 testTx := L2Tx{
228 lineNum: inst.lineNum,
229 fromIdxName: inst.from,
230 toIdxName: inst.to,
231 tokenID: inst.tokenID,
232 L2Tx: tx,
233 }
234 tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
235 case common.TxTypeForceTransfer: // tx source: L1UserTx
236 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
237 log.Error(err)
238 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
239 }
240 tx := common.L1Tx{
241 TokenID: inst.tokenID,
242 Amount: big.NewInt(int64(inst.amount)),
243 LoadAmount: big.NewInt(0),
244 Type: common.TxTypeForceTransfer,
245 }
246 testTx := L1Tx{
247 lineNum: inst.lineNum,
248 fromIdxName: inst.from,
249 toIdxName: inst.to,
250 L1Tx: tx,
251 }
252 if err := tc.addToL1Queue(testTx); err != nil {
253 return nil, err
254 }
255 case common.TxTypeExit: // tx source: L2Tx
256 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
257 log.Error(err)
258 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
259 }
260 tx := common.L2Tx{
261 ToIdx: common.Idx(1), // as is an Exit
262 Fee: common.FeeSelector(inst.fee),
263 Amount: big.NewInt(int64(inst.amount)),
264 Type: common.TxTypeExit,
265 EthBlockNum: tc.blockNum,
266 }
267 tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
268 testTx := L2Tx{
269 lineNum: inst.lineNum,
270 fromIdxName: inst.from,
271 toIdxName: inst.to,
272 tokenID: inst.tokenID,
273 L2Tx: tx,
274 }
275 tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
276 case common.TxTypeForceExit: // tx source: L1UserTx
277 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
278 log.Error(err)
279 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
280 }
281 tx := common.L1Tx{
282 ToIdx: common.Idx(1), // as is an Exit
283 TokenID: inst.tokenID,
284 Amount: big.NewInt(int64(inst.amount)),
285 LoadAmount: big.NewInt(0),
286 Type: common.TxTypeForceExit,
287 }
288 testTx := L1Tx{
289 lineNum: inst.lineNum,
290 fromIdxName: inst.from,
291 toIdxName: inst.to,
292 L1Tx: tx,
293 }
294 if err := tc.addToL1Queue(testTx); err != nil {
295 return nil, err
296 }
297 case typeNewBatch:
298 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
299 return nil, err
300 }
301 if err = tc.setIdxs(); err != nil {
302 log.Error(err)
303 return nil, err
304 }
305 case typeNewBatchL1:
306 // for each L1UserTx of the Queues[ToForgeNum], calculate the Idx
307 if err = tc.calculateIdxForL1Txs(false, tc.Queues[tc.ToForgeNum]); err != nil {
308 return nil, err
309 }
310 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
311 return nil, err
312 }
313 tc.currBatch.L1Batch = true
314 if err = tc.setIdxs(); err != nil {
315 log.Error(err)
316 return nil, err
317 }
318 toForgeL1TxsNum := int64(tc.openToForge)
319 tc.currBatch.Batch.ForgeL1TxsNum = &toForgeL1TxsNum
320 // advance batch
321 tc.ToForgeNum++
322 if tc.ToForgeNum == tc.openToForge {
323 tc.openToForge++
324 newQueue := []L1Tx{}
325 tc.Queues = append(tc.Queues, newQueue)
326 }
327 case typeNewBlock:
328 blocks = append(blocks, tc.currBlock)
329 tc.blockNum++
330 tc.currBlock = newBlock(tc.blockNum)
331 case typeAddToken:
332 newToken := common.Token{
333 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.tokenID * 100))), //nolint:gomnd
334 // Name: fmt.Sprintf("Token %d", inst.tokenID),
335 // Symbol: fmt.Sprintf("TK%d", inst.tokenID),
336 // Decimals: 18,
337 TokenID: inst.tokenID,
338 EthBlockNum: tc.blockNum,
339 }
340 if inst.tokenID != tc.LastRegisteredTokenID+1 {
341 return nil, fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.LastRegisteredTokenID+1, inst.tokenID)
342 }
343 tc.LastRegisteredTokenID++
344 tc.currBlock.Rollup.AddedTokens = append(tc.currBlock.Rollup.AddedTokens, newToken)
345 default:
346 return nil, fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)
347 }
348 }
349
350 return blocks, nil
351}
352
353// calculateIdxsForL1Txs calculates new Idx for new created accounts. If
354// 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
355func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
356 // for each batch.L1CoordinatorTxs of the Queues[ToForgeNum], calculate the Idx
357 for i := 0; i < len(txs); i++ {
358 tx := txs[i]
359 if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
360 if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
361 return fmt.Errorf("Can not create same account twice (same User (%s) & same TokenID (%d)) (this is a design property of Til)", tx.fromIdxName, tx.L1Tx.TokenID)
362 }
363 tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
364 Idx: common.Idx(tc.idx),
365 Nonce: common.Nonce(0),
366 }
367 tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
368 tc.idx++
369 }
370 if isCoordinatorTxs {
371 tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
372 }
373 }
374 return nil
375}
376
377// setIdxs sets the Idxs to the transactions of the tc.currBatch
378func (tc *Context) setIdxs() error {
379 // once Idxs are calculated, update transactions to use the new Idxs
380 for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
381 testTx := &tc.currBatchTest.l2Txs[i]
382
383 if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
384 return fmt.Errorf("Line %d: %s from User %s for TokenID %d while account not created yet", testTx.lineNum, testTx.L2Tx.Type, testTx.fromIdxName, testTx.tokenID)
385 }
386 if testTx.L2Tx.Type == common.TxTypeTransfer {
387 if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
388 return fmt.Errorf("Line %d: Can not create Transfer for a non existing account. Batch %d, ToIdx name: %s, TokenID: %d", testTx.lineNum, tc.currBatchNum, testTx.toIdxName, testTx.tokenID)
389 }
390 }
391 tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
392 // next line is commented to avoid Blockchain L2Txs to have
393 // Nonce different from 0, as from Blockchain those
394 // transactions will come without Nonce
395 // testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
396
397 // set real Idx
398 testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
399 if testTx.L2Tx.Type == common.TxTypeTransfer {
400 testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
401 }
402 // in case Type==Exit, ToIdx=1, already set at the
403 // GenerateBlocks main switch inside TxTypeExit case
404
405 nTx, err := common.NewL2Tx(&testTx.L2Tx)
406 if err != nil {
407 return fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
408 }
409 testTx.L2Tx = *nTx
410
411 tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
412 }
413
414 tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
415 tc.currBlock.Rollup.Batches = append(tc.currBlock.Rollup.Batches, tc.currBatch)
416 tc.currBatchNum++
417 tc.currBatch = newBatchData(tc.currBatchNum)
418 tc.currBatchTest.l1CoordinatorTxs = nil
419 tc.currBatchTest.l2Txs = nil
420 return nil
421}
422
423// addToL1Queue adds the L1Tx into the queue that is open and has space
424func (tc *Context) addToL1Queue(tx L1Tx) error {
425 if len(tc.Queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
426 // if current OpenToForge queue reached its Max, move into a
427 // new queue
428 tc.openToForge++
429 newQueue := []L1Tx{}
430 tc.Queues = append(tc.Queues, newQueue)
431 }
432 // Fill L1UserTx specific parameters
433 tx.L1Tx.UserOrigin = true
434 toForgeL1TxsNum := int64(tc.openToForge)
435 tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
436 tx.L1Tx.EthBlockNum = tc.blockNum
437 tx.L1Tx.Position = len(tc.Queues[tc.openToForge])
438
439 // When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
440 if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
441 tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
442 }
443 tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
444 tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public()
445 if tx.toIdxName == "" {
446 tx.L1Tx.ToIdx = common.Idx(0)
447 } else {
448 account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
449 if !ok {
450 return fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
451 "while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID)
452 }
453 tx.L1Tx.ToIdx = account.Idx
454 }
455 if tx.L1Tx.Type == common.TxTypeForceExit {
456 tx.L1Tx.ToIdx = common.Idx(1)
457 }
458 nTx, err := common.NewL1Tx(&tx.L1Tx)
459 if err != nil {
460 return fmt.Errorf("Line %d: %s", tx.lineNum, err.Error())
461 }
462 tx.L1Tx = *nTx
463
464 tc.Queues[tc.openToForge] = append(tc.Queues[tc.openToForge], tx)
465 tc.currBlock.Rollup.L1UserTxs = append(tc.currBlock.Rollup.L1UserTxs, tx.L1Tx)
466
467 return nil
468}
469
470func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
471 if tc.Users[tf].Accounts[inst.tokenID] == nil {
472 return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
473 }
474 return nil
475}
476func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
477 if inst.tokenID > tc.LastRegisteredTokenID {
478 return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID)
479 }
480 return nil
481}
482
483// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
484// uses the users (keys) of the Context.
485func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
486 parser := newParser(strings.NewReader(set))
487 parsedSet, err := parser.parse()
488 if err != nil {
489 return nil, err
490 }
491 if parsedSet.typ != setTypePoolL2 {
492 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ)
493 }
494
495 tc.Instructions = parsedSet.instructions
496 tc.userNames = parsedSet.users
497
498 tc.generateKeys(tc.userNames)
499
500 txs := []common.PoolL2Tx{}
501 for _, inst := range tc.Instructions {
502 switch inst.typ {
503 case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
504 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
505 log.Error(err)
506 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
507 }
508 if inst.typ == common.TxTypeTransfer {
509 // if TxTypeTransfer, need to exist the ToIdx account
510 if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
511 log.Error(err)
512 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
513 }
514 }
515 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
516 // if account of receiver does not exist, don't use
517 // ToIdx, and use only ToEthAddr & ToBJJ
518 tx := common.PoolL2Tx{
519 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
520 TokenID: inst.tokenID,
521 Amount: big.NewInt(int64(inst.amount)),
522 Fee: common.FeeSelector(inst.fee),
523 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
524 State: common.PoolL2TxStatePending,
525 Timestamp: time.Now(),
526 RqToEthAddr: common.EmptyAddr,
527 RqToBJJ: nil,
528 Type: inst.typ,
529 }
530 if tx.Type == common.TxTypeTransfer {
531 tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
532 tx.ToEthAddr = tc.Users[inst.to].Addr
533 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
534 } else if tx.Type == common.TxTypeTransferToEthAddr {
535 tx.ToIdx = common.Idx(0)
536 tx.ToEthAddr = tc.Users[inst.to].Addr
537 } else if tx.Type == common.TxTypeTransferToBJJ {
538 tx.ToIdx = common.Idx(0)
539 tx.ToEthAddr = common.FFAddr
540 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
541 }
542 nTx, err := common.NewPoolL2Tx(&tx)
543 if err != nil {
544 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
545 }
546 tx = *nTx
547 // perform signature and set it to tx.Signature
548 toSign, err := tx.HashToSign()
549 if err != nil {
550 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
551 }
552 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
553 tx.Signature = sig.Compress()
554
555 txs = append(txs, tx)
556 case common.TxTypeExit:
557 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
558 tx := common.PoolL2Tx{
559 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
560 ToIdx: common.Idx(1), // as is an Exit
561 Fee: common.FeeSelector(inst.fee),
562 TokenID: inst.tokenID,
563 Amount: big.NewInt(int64(inst.amount)),
564 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
565 Type: common.TxTypeExit,
566 }
567 nTx, err := common.NewPoolL2Tx(&tx)
568 if err != nil {
569 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
570 }
571 tx = *nTx
572 // perform signature and set it to tx.Signature
573 toSign, err := tx.HashToSign()
574 if err != nil {
575 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
576 }
577 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
578 tx.Signature = sig.Compress()
579 txs = append(txs, tx)
580 default:
581 return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
582 }
583 }
584
585 return txs, nil
586}
587
588// generateKeys generates BabyJubJub & Address keys for the given list of user
589// names in a deterministic way. This means, that for the same given
590// 'userNames' in a certain order, the keys will be always the same.
591func (tc *Context) generateKeys(userNames []string) {
592 for i := 1; i < len(userNames)+1; i++ {
593 if _, ok := tc.Users[userNames[i-1]]; ok {
594 // account already created
595 continue
596 }
597 // babyjubjub key
598 var sk babyjub.PrivateKey
599 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
600
601 // eth address
602 var key ecdsa.PrivateKey
603 key.D = big.NewInt(int64(i)) // only for testing
604 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
605 key.Curve = ethCrypto.S256()
606 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
607
608 u := User{
609 BJJ: &sk,
610 Addr: addr,
611 Accounts: make(map[common.TokenID]*Account),
612 }
613 tc.Users[userNames[i-1]] = &u
614 }
615}
616
617// L1TxsToCommonL1Txs converts an array of []til.L1Tx to []common.L1Tx
618func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx {
619 var r []common.L1Tx
620 for i := 0; i < len(l1); i++ {
621 r = append(r, l1[i].L1Tx)
622 }
623 return r
624}