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