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 // next line is commented to avoid Blockchain L2Txs to have
389 // Nonce different from 0, as from Blockchain those
390 // transactions will come without Nonce
391 // testTx.L2Tx.Nonce = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Nonce
392
393 // set real Idx
394 testTx.L2Tx.FromIdx = tc.Users[testTx.fromIdxName].Accounts[testTx.tokenID].Idx
395 if testTx.L2Tx.Type == common.TxTypeTransfer {
396 testTx.L2Tx.ToIdx = tc.Users[testTx.toIdxName].Accounts[testTx.tokenID].Idx
397 }
398 // in case Type==Exit, ToIdx=1, already set at the
399 // GenerateBlocks main switch inside TxTypeExit case
400
401 nTx, err := common.NewL2Tx(&testTx.L2Tx)
402 if err != nil {
403 return fmt.Errorf("Line %d: %s", testTx.lineNum, err.Error())
404 }
405 testTx.L2Tx = *nTx
406
407 tc.currBatch.L2Txs = append(tc.currBatch.L2Txs, testTx.L2Tx)
408 }
409
410 tc.currBatch.Batch.LastIdx = int64(tc.idx - 1) // `-1` because tc.idx is the next available idx
411 tc.currBlock.Batches = append(tc.currBlock.Batches, tc.currBatch)
412 tc.currBatchNum++
413 tc.currBatch = newBatchData(tc.currBatchNum)
414 tc.currBatchTest.l1CoordinatorTxs = nil
415 tc.currBatchTest.l2Txs = nil
416 return nil
417}
418
419// addToL1Queue adds the L1Tx into the queue that is open and has space
420func (tc *Context) addToL1Queue(tx L1Tx) error {
421 if len(tc.queues[tc.openToForge]) >= tc.rollupConstMaxL1UserTx {
422 // if current OpenToForge queue reached its Max, move into a
423 // new queue
424 tc.openToForge++
425 newQueue := []L1Tx{}
426 tc.queues = append(tc.queues, newQueue)
427 }
428 // Fill L1UserTx specific parameters
429 tx.L1Tx.UserOrigin = true
430 toForgeL1TxsNum := int64(tc.openToForge)
431 tx.L1Tx.ToForgeL1TxsNum = &toForgeL1TxsNum
432 tx.L1Tx.EthBlockNum = tc.blockNum
433 tx.L1Tx.Position = len(tc.queues[tc.openToForge])
434
435 // When an L1UserTx is generated, all idxs must be available (except when idx == 0 or idx == 1)
436 if tx.L1Tx.Type != common.TxTypeCreateAccountDeposit && tx.L1Tx.Type != common.TxTypeCreateAccountDepositTransfer {
437 tx.L1Tx.FromIdx = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID].Idx
438 }
439 tx.L1Tx.FromEthAddr = tc.Users[tx.fromIdxName].Addr
440 tx.L1Tx.FromBJJ = tc.Users[tx.fromIdxName].BJJ.Public()
441 if tx.toIdxName == "" {
442 tx.L1Tx.ToIdx = common.Idx(0)
443 } else {
444 account, ok := tc.Users[tx.toIdxName].Accounts[tx.L1Tx.TokenID]
445 if !ok {
446 return fmt.Errorf("Line %d: Transfer to User: %s, for TokenID: %d, "+
447 "while account not created yet", tx.lineNum, tx.toIdxName, tx.L1Tx.TokenID)
448 }
449 tx.L1Tx.ToIdx = account.Idx
450 }
451 if tx.L1Tx.Type == common.TxTypeForceExit {
452 tx.L1Tx.ToIdx = common.Idx(1)
453 }
454 nTx, err := common.NewL1Tx(&tx.L1Tx)
455 if err != nil {
456 return fmt.Errorf("Line %d: %s", tx.lineNum, err.Error())
457 }
458 tx.L1Tx = *nTx
459
460 tc.queues[tc.openToForge] = append(tc.queues[tc.openToForge], tx)
461 tc.currBlock.L1UserTxs = append(tc.currBlock.L1UserTxs, tx.L1Tx)
462
463 return nil
464}
465
466func (tc *Context) checkIfAccountExists(tf string, inst instruction) error {
467 if tc.Users[tf].Accounts[inst.tokenID] == nil {
468 return fmt.Errorf("%s at User: %s, for TokenID: %d, while account not created yet", inst.typ, tf, inst.tokenID)
469 }
470 return nil
471}
472func (tc *Context) checkIfTokenIsRegistered(inst instruction) error {
473 if inst.tokenID > tc.LastRegisteredTokenID {
474 return fmt.Errorf("Can not process %s: TokenID %d not registered, last registered TokenID: %d", inst.typ, inst.tokenID, tc.LastRegisteredTokenID)
475 }
476 return nil
477}
478
479// GeneratePoolL2Txs returns an array of common.PoolL2Tx from a given set. It
480// uses the users (keys) of the Context.
481func (tc *Context) GeneratePoolL2Txs(set string) ([]common.PoolL2Tx, error) {
482 parser := newParser(strings.NewReader(set))
483 parsedSet, err := parser.parse()
484 if err != nil {
485 return nil, err
486 }
487 if parsedSet.typ != setTypePoolL2 {
488 return nil, fmt.Errorf("Expected set type: %s, found: %s", setTypePoolL2, parsedSet.typ)
489 }
490
491 tc.Instructions = parsedSet.instructions
492 tc.userNames = parsedSet.users
493
494 tc.generateKeys(tc.userNames)
495
496 txs := []common.PoolL2Tx{}
497 for _, inst := range tc.Instructions {
498 switch inst.typ {
499 case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ:
500 if err := tc.checkIfAccountExists(inst.from, inst); err != nil {
501 log.Error(err)
502 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
503 }
504 if inst.typ == common.TxTypeTransfer {
505 // if TxTypeTransfer, need to exist the ToIdx account
506 if err := tc.checkIfAccountExists(inst.to, inst); err != nil {
507 log.Error(err)
508 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
509 }
510 }
511 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
512 // if account of receiver does not exist, don't use
513 // ToIdx, and use only ToEthAddr & ToBJJ
514 tx := common.PoolL2Tx{
515 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
516 TokenID: inst.tokenID,
517 Amount: big.NewInt(int64(inst.amount)),
518 Fee: common.FeeSelector(inst.fee),
519 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
520 State: common.PoolL2TxStatePending,
521 Timestamp: time.Now(),
522 RqToEthAddr: common.EmptyAddr,
523 RqToBJJ: nil,
524 Type: inst.typ,
525 }
526 if tx.Type == common.TxTypeTransfer {
527 tx.ToIdx = tc.Users[inst.to].Accounts[inst.tokenID].Idx
528 tx.ToEthAddr = tc.Users[inst.to].Addr
529 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
530 } else if tx.Type == common.TxTypeTransferToEthAddr {
531 tx.ToIdx = common.Idx(0)
532 tx.ToEthAddr = tc.Users[inst.to].Addr
533 } else if tx.Type == common.TxTypeTransferToBJJ {
534 tx.ToIdx = common.Idx(0)
535 tx.ToEthAddr = common.FFAddr
536 tx.ToBJJ = tc.Users[inst.to].BJJ.Public()
537 }
538 nTx, err := common.NewPoolL2Tx(&tx)
539 if err != nil {
540 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
541 }
542 tx = *nTx
543 // perform signature and set it to tx.Signature
544 toSign, err := tx.HashToSign()
545 if err != nil {
546 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
547 }
548 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
549 tx.Signature = sig.Compress()
550
551 txs = append(txs, tx)
552 case common.TxTypeExit:
553 tc.Users[inst.from].Accounts[inst.tokenID].Nonce++
554 tx := common.PoolL2Tx{
555 FromIdx: tc.Users[inst.from].Accounts[inst.tokenID].Idx,
556 ToIdx: common.Idx(1), // as is an Exit
557 TokenID: inst.tokenID,
558 Amount: big.NewInt(int64(inst.amount)),
559 Nonce: tc.Users[inst.from].Accounts[inst.tokenID].Nonce,
560 Type: common.TxTypeExit,
561 }
562 nTx, err := common.NewPoolL2Tx(&tx)
563 if err != nil {
564 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
565 }
566 tx = *nTx
567 // perform signature and set it to tx.Signature
568 toSign, err := tx.HashToSign()
569 if err != nil {
570 return nil, fmt.Errorf("Line %d: %s", inst.lineNum, err.Error())
571 }
572 sig := tc.Users[inst.from].BJJ.SignPoseidon(toSign)
573 tx.Signature = sig.Compress()
574 txs = append(txs, tx)
575 default:
576 return nil, fmt.Errorf("Line %d: instruction type unrecognized: %s", inst.lineNum, inst.typ)
577 }
578 }
579
580 return txs, nil
581}
582
583// generateKeys generates BabyJubJub & Address keys for the given list of user
584// names in a deterministic way. This means, that for the same given
585// 'userNames' in a certain order, the keys will be always the same.
586func (tc *Context) generateKeys(userNames []string) {
587 for i := 1; i < len(userNames)+1; i++ {
588 if _, ok := tc.Users[userNames[i-1]]; ok {
589 // account already created
590 continue
591 }
592 // babyjubjub key
593 var sk babyjub.PrivateKey
594 copy(sk[:], []byte(strconv.Itoa(i))) // only for testing
595
596 // eth address
597 var key ecdsa.PrivateKey
598 key.D = big.NewInt(int64(i)) // only for testing
599 key.PublicKey.X, key.PublicKey.Y = ethCrypto.S256().ScalarBaseMult(key.D.Bytes())
600 key.Curve = ethCrypto.S256()
601 addr := ethCrypto.PubkeyToAddress(key.PublicKey)
602
603 u := User{
604 BJJ: &sk,
605 Addr: addr,
606 Accounts: make(map[common.TokenID]*Account),
607 }
608 tc.Users[userNames[i-1]] = &u
609 }
610}