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 Fee: common.FeeSelector(inst.fee),
261 Amount: big.NewInt(int64(inst.amount)),
262 Type: common.TxTypeExit,
263 EthBlockNum: tc.blockNum,
264 }
265 tx.BatchNum = common.BatchNum(tc.currBatchNum) // when converted to PoolL2Tx BatchNum parameter is lost
266 testTx := L2Tx{
267 lineNum: inst.lineNum,
268 fromIdxName: inst.from,
269 toIdxName: inst.to,
270 tokenID: inst.tokenID,
271 L2Tx: tx,
272 }
273 tc.currBatchTest.l2Txs = append(tc.currBatchTest.l2Txs, testTx)
274 case common.TxTypeForceExit: // tx source: L1UserTx
275 if err := tc.checkIfTokenIsRegistered(inst); err != nil {
276 log.Error(err)
277 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
278 }
279 tx := common.L1Tx{
280 ToIdx: common.Idx(1), // as is an Exit
281 TokenID: inst.tokenID,
282 Amount: big.NewInt(int64(inst.amount)),
283 LoadAmount: big.NewInt(0),
284 Type: common.TxTypeForceExit,
285 }
286 testTx := L1Tx{
287 lineNum: inst.lineNum,
288 fromIdxName: inst.from,
289 toIdxName: inst.to,
290 L1Tx: tx,
291 }
292 if err := tc.addToL1Queue(testTx); err != nil {
293 return nil, err
294 }
295 case typeNewBatch:
296 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
297 return nil, err
298 }
299 if err = tc.setIdxs(); err != nil {
300 log.Error(err)
301 return nil, err
302 }
303 case typeNewBatchL1:
304 // for each L1UserTx of the queues[ToForgeNum], calculate the Idx
305 if err = tc.calculateIdxForL1Txs(false, tc.queues[tc.toForgeNum]); err != nil {
306 return nil, err
307 }
308 if err = tc.calculateIdxForL1Txs(true, tc.currBatchTest.l1CoordinatorTxs); err != nil {
309 return nil, err
310 }
311 tc.currBatch.L1Batch = true
312 if err = tc.setIdxs(); err != nil {
313 log.Error(err)
314 return nil, err
315 }
316 // advance batch
317 tc.toForgeNum++
318 if tc.toForgeNum == tc.openToForge {
319 tc.openToForge++
320 newQueue := []L1Tx{}
321 tc.queues = append(tc.queues, newQueue)
322 }
323 case typeNewBlock:
324 blocks = append(blocks, tc.currBlock)
325 tc.blockNum++
326 tc.currBlock = newBlock(tc.blockNum)
327 case typeAddToken:
328 newToken := common.Token{
329 EthAddr: ethCommon.BigToAddress(big.NewInt(int64(inst.tokenID * 100))), //nolint:gomnd
330 // Name: fmt.Sprintf("Token %d", inst.tokenID),
331 // Symbol: fmt.Sprintf("TK%d", inst.tokenID),
332 // Decimals: 18,
333 TokenID: inst.tokenID,
334 EthBlockNum: tc.blockNum,
335 }
336 if inst.tokenID != tc.LastRegisteredTokenID+1 {
337 return nil, fmt.Errorf("Line %d: AddToken TokenID should be sequential, expected TokenID: %d, defined TokenID: %d", inst.lineNum, tc.LastRegisteredTokenID+1, inst.tokenID)
338 }
339 tc.LastRegisteredTokenID++
340 tc.currBlock.AddedTokens = append(tc.currBlock.AddedTokens, newToken)
341 default:
342 return nil, fmt.Errorf("Line %d: Unexpected type: %s", inst.lineNum, inst.typ)
343 }
344 }
345
346 return blocks, nil
347}
348
349// calculateIdxsForL1Txs calculates new Idx for new created accounts. If
350// 'isCoordinatorTxs==true', adds the tx to tc.currBatch.L1CoordinatorTxs.
351func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error {
352 // for each batch.L1CoordinatorTxs of the queues[ToForgeNum], calculate the Idx
353 for i := 0; i < len(txs); i++ {
354 tx := txs[i]
355 if tx.L1Tx.Type == common.TxTypeCreateAccountDeposit || tx.L1Tx.Type == common.TxTypeCreateAccountDepositTransfer {
356 if tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] != nil { // if account already exists, return error
357 return fmt.Errorf("Can not create same account twice (same User & same TokenID) (this is a design property of Til)")
358 }
359 tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{
360 Idx: common.Idx(tc.idx),
361 Nonce: common.Nonce(0),
362 }
363 tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID]
364 tc.idx++
365 }
366 if isCoordinatorTxs {
367 tc.currBatch.L1CoordinatorTxs = append(tc.currBatch.L1CoordinatorTxs, tx.L1Tx)
368 }
369 }
370 return nil
371}
372
373// setIdxs sets the Idxs to the transactions of the tc.currBatch
374func (tc *Context) setIdxs() error {
375 // once Idxs are calculated, update transactions to use the new Idxs
376 for i := 0; i < len(tc.currBatchTest.l2Txs); i++ {
377 testTx := &tc.currBatchTest.l2Txs[i]
378
379 if tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID] == nil {
380 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)
381 }
382 if testTx.L2Tx.Type == common.TxTypeTransfer {
383 if _, ok := tc.l1CreatedAccounts[idxTokenIDToString(testTx.toIdxName, testTx.tokenID)]; !ok {
384 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)
385 }
386 }
387 tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce++
388 testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
389
390 // set real Idx
391 testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
392 if testTx.L2Tx.Type == common.TxTypeTransfer {
393 testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
394 }
395 // in case Type==Exit, ToIdx=1, already set at the
396 // GenerateBlocks main switch inside TxTypeExit case
397
398 nTx, err := common.NewL2Tx(&testTx.L2Tx)
399 if err != nil {
400 return fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
401 }
402 testTx.L2Tx = *nTx
403
404 tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
405 }
406
407 tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
408 tc.currBlock.Batches = append(tc.currBlock.Batches, tc.currBatch)
409 tc.currBatchNum++
410 tc.currBatch = newBatchData(tc.currBatchNum)
411 tc.currBatchTest.l1CoordinatorTxs = nil
412 tc.currBatchTest.l2Txs = nil
413 return nil
414}
415
416// addToL1Queue adds the L1Tx into the queue that is open and has space
417func (tc *Context) addToL1Queue(tx L1Tx) error {
418 if len(tc.queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
419 // if current OpenToForge queue reached its Max, move into a
420 // new queue
421 tc.openToForge++
422 newQueue := []L1Tx{}
423 tc.queues = append(tc.queues, newQueue)
424 }
425 // Fill L1UserTx specific parameters
426 tx.L1Tx.UserOrigin = true
427 toForgeL1TxsNum := int64(tc.openToForge)
428 tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
429 tx.L1Tx.EthBlockNum = tc.blockNum
430 tx.L1Tx.Position = len(tc.queues[tc.openToForge])
431
432 // When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
433 if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
434 tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
435 }
436 tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
437 tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public()
438 if tx.toIdxName == "" {
439 tx.L1Tx.ToIdx = common.Idx(0)
440 } else {
441 account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
442 if !ok {
443 return fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
444 "while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID)
445 }
446 tx.L1Tx.ToIdx = account.Idx
447 }
448 if tx.L1Tx.Type == common.TxTypeForceExit {
449 tx.L1Tx.ToIdx = common.Idx(1)
450 }
451 nTx, err := common.NewL1Tx(&tx.L1Tx)
452 if err != nil {
453 return fmt.Errorf("Line %d: %s", tx.lineNum, err.Error())
454 }
455 tx.L1Tx = *nTx
456
457 tc.queues[tc.openToForge] = append(tc.queues[tc.openToForge], tx)
458 tc.currBlock.L1UserTxs = append(tc.currBlock.L1UserTxs, tx.L1Tx)
459
460 return nil
461}
462
463func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
464 if tc.Users[tf].Accounts[inst.tokenID] == nil {
465 return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
466 }
467 return nil
468}
469func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
470 if inst.tokenID > tc.LastRegisteredTokenID {
471 return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID)
472 }
473 return nil
474}
475
476// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
477// uses the users (keys) of the Context.
478func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
479 parser := newParser(strings.NewReader(set))
480 parsedSet, err := parser.parse()
481 if err != nil {
482 return nil, err
483 }
484 if parsedSet.typ != setTypePoolL2 {
485 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ)
486 }
487
488 tc.Instructions = parsedSet.instructions
489 tc.userNames = parsedSet.users
490
491 tc.generateKeys(tc.userNames)
492
493 txs := []common.PoolL2Tx{}
494 for _, inst := range tc.Instructions {
495 switch inst.typ {
496 case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
497 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
498 log.Error(err)
499 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
500 }
501 if inst.typ == common.TxTypeTransfer {
502 // if TxTypeTransfer, need to exist the ToIdx account
503 if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
504 log.Error(err)
505 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
506 }
507 }
508 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
509 // if account of receiver does not exist, don't use
510 // ToIdx, and use only ToEthAddr & ToBJJ
511 tx := common.PoolL2Tx{
512 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
513 TokenID: inst.tokenID,
514 Amount: big.NewInt(int64(inst.amount)),
515 Fee: common.FeeSelector(inst.fee),
516 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
517 State: common.PoolL2TxStatePending,
518 Timestamp: time.Now(),
519 RqToEthAddr: common.EmptyAddr,
520 RqToBJJ: nil,
521 Type: inst.typ,
522 }
523 if tx.Type == common.TxTypeTransfer {
524 tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
525 tx.ToEthAddr = tc.Users[inst.to].Addr
526 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
527 } else if tx.Type == common.TxTypeTransferToEthAddr {
528 tx.ToIdx = common.Idx(0)
529 tx.ToEthAddr = tc.Users[inst.to].Addr
530 } else if tx.Type == common.TxTypeTransferToBJJ {
531 tx.ToIdx = common.Idx(0)
532 tx.ToEthAddr = common.FFAddr
533 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
534 }
535 nTx, err := common.NewPoolL2Tx(&tx)
536 if err != nil {
537 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
538 }
539 tx = *nTx
540 // perform signature and set it to tx.Signature
541 toSign, err := tx.HashToSign()
542 if err != nil {
543 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
544 }
545 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
546 tx.Signature = sig.Compress()
547
548 txs = append(txs, tx)
549 case common.TxTypeExit:
550 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
551 tx := common.PoolL2Tx{
552 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
553 ToIdx: common.Idx(1), // as is an Exit
554 TokenID: inst.tokenID,
555 Amount: big.NewInt(int64(inst.amount)),
556 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
557 Type: common.TxTypeExit,
558 }
559 nTx, err := common.NewPoolL2Tx(&tx)
560 if err != nil {
561 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
562 }
563 tx = *nTx
564 // perform signature and set it to tx.Signature
565 toSign, err := tx.HashToSign()
566 if err != nil {
567 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
568 }
569 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
570 tx.Signature = sig.Compress()
571 txs = append(txs, tx)
572 default:
573 return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
574 }
575 }
576
577 return txs, nil
578}
579
580// generateKeys generates BabyJubJub & Address keys for the given list of user
581// names in a deterministic way. This means, that for the same given
582// 'userNames' in a certain order, the keys will be always the same.
583func (tc *Context) generateKeys(userNames []string) {
584 for i := 1; i < len(userNames)+1; i++ {
585 if _, ok := tc.Users[userNames[i-1]]; ok {
586 // account already created
587 continue
588 }
589 // babyjubjub key
590 var sk babyjub.PrivateKey
591 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
592
593 // eth address
594 var key ecdsa.PrivateKey
595 key.D = big.NewInt(int64(i)) // only for testing
596 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
597 key.Curve = ethCrypto.S256()
598 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
599
600 u := User{
601 BJJ: &sk,
602 Addr: addr,
603 Accounts: make(map[common.TokenID]*Account),
604 }
605 tc.Users[userNames[i-1]] = &u
606 }
607}