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