hermez-node/db/statedb/txprocessors.go
db/statedb/txprocessors.goRaw
1package statedb
2
3import (
4 "errors"
5 "math/big"
6
7 "github.com/hermeznetwork/hermez-node/common"
8 "github.com/hermeznetwork/hermez-node/log"
9 "github.com/iden3/go-iden3-crypto/babyjub"
10 "github.com/iden3/go-merkletree"
11 "github.com/iden3/go-merkletree/db"
12 "github.com/iden3/go-merkletree/db/memory"
13)
14
15var (
16 // keyidx is used as key in the db to store the current Idx
17 keyidx = []byte("idx")
18)
19
20func (s *StateDB) resetZKInputs() {
21 s.zki = nil
22 s.i = 0
23}
24
25type processedExit struct {
26 exit bool
27 newExit bool
28 idx common.Idx
29 acc common.Account
30}
31
32// ProcessTxs process the given L1Txs & L2Txs applying the needed updates to
33// the StateDB depending on the transaction Type. Returns the common.ZKInputs
34// to generate the SnarkProof later used by the BatchBuilder, and if
35// cmpExitTree is set to true, returns common.ExitTreeLeaf that is later used
36// by the Synchronizer to update the HistoryDB.
37func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) (*common.ZKInputs, []*common.ExitInfo, error) {
38 var err error
39 var exitTree *merkletree.MerkleTree
40
41 if s.zki != nil {
42 return nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong and it's not empty")
43 }
44 defer s.resetZKInputs()
45
46 nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs)
47 if nTx == 0 {
48 // TODO return ZKInputs of batch without txs
49 return nil, nil, nil
50 }
51 exits := make([]processedExit, nTx)
52
53 if cmpZKInputs {
54 s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function, taken from config file/coordinator call
55 s.zki.OldLastIdx = (s.idx - 1).BigInt()
56 s.zki.OldStateRoot = s.mt.Root().BigInt()
57 }
58
59 // TBD if ExitTree is only in memory or stored in disk, for the moment
60 // only needed in memory
61 if cmpExitTree {
62 exitTree, err = merkletree.NewMerkleTree(memory.NewMemoryStorage(), s.mt.MaxLevels())
63 if err != nil {
64 return nil, nil, err
65 }
66 }
67
68 // assumption: l1usertx are sorted by L1Tx.Position
69 for _, tx := range l1usertxs {
70 exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx)
71 if err != nil {
72 return nil, nil, err
73 }
74 if exitIdx != nil && cmpExitTree {
75 exits[s.i] = processedExit{
76 exit: true,
77 newExit: newExit,
78 idx: *exitIdx,
79 acc: *exitAccount,
80 }
81 }
82 if s.zki != nil {
83 s.i++
84 }
85 }
86 for _, tx := range l1coordinatortxs {
87 exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx)
88 if err != nil {
89 return nil, nil, err
90 }
91 if exitIdx != nil {
92 log.Error("Unexpected Exit in L1CoordinatorTx")
93 }
94 if exitIdx != nil && cmpExitTree {
95 exits[s.i] = processedExit{
96 exit: true,
97 newExit: newExit,
98 idx: *exitIdx,
99 acc: *exitAccount,
100 }
101 }
102 if s.zki != nil {
103 s.i++
104 }
105 }
106 for _, tx := range l2txs {
107 exitIdx, exitAccount, newExit, err := s.processL2Tx(exitTree, tx)
108 if err != nil {
109 return nil, nil, err
110 }
111 if exitIdx != nil && cmpExitTree {
112 exits[s.i] = processedExit{
113 exit: true,
114 newExit: newExit,
115 idx: *exitIdx,
116 acc: *exitAccount,
117 }
118 }
119 if s.zki != nil {
120 s.i++
121 }
122 }
123
124 if !cmpExitTree && !cmpZKInputs {
125 return nil, nil, nil
126 }
127
128 // once all txs processed (exitTree root frozen), for each Exit,
129 // generate common.ExitInfo data
130 var exitInfos []*common.ExitInfo
131 for i := 0; i < nTx; i++ {
132 if !exits[i].exit {
133 continue
134 }
135 exitIdx := exits[i].idx
136 exitAccount := exits[i].acc
137
138 // 0. generate MerkleProof
139 p, err := exitTree.GenerateCircomVerifierProof(exitIdx.BigInt(), nil)
140 if err != nil {
141 return nil, nil, err
142 }
143 // 1. generate common.ExitInfo
144 ei := &common.ExitInfo{
145 AccountIdx: exitIdx,
146 MerkleProof: p,
147 Balance: exitAccount.Balance,
148 }
149 exitInfos = append(exitInfos, ei)
150
151 if s.zki != nil {
152 s.zki.TokenID2[i] = exitAccount.TokenID.BigInt()
153 s.zki.Nonce2[i] = exitAccount.Nonce.BigInt()
154 if babyjub.PointCoordSign(exitAccount.PublicKey.X) {
155 s.zki.Sign2[i] = big.NewInt(1)
156 }
157 s.zki.Ay2[i] = exitAccount.PublicKey.Y
158 s.zki.Balance2[i] = exitAccount.Balance
159 s.zki.EthAddr2[i] = common.EthAddrToBigInt(exitAccount.EthAddr)
160 s.zki.Siblings2[i] = p.Siblings
161 if exits[i].newExit {
162 s.zki.NewExit[i] = big.NewInt(1)
163 }
164 if p.IsOld0 {
165 s.zki.IsOld0_2[i] = big.NewInt(1)
166 }
167 s.zki.OldKey2[i] = p.OldKey.BigInt()
168 s.zki.OldValue2[i] = p.OldValue.BigInt()
169 }
170 }
171 if !cmpZKInputs {
172 return nil, exitInfos, nil
173 }
174
175 // compute last ZKInputs parameters
176 s.zki.GlobalChainID = big.NewInt(0) // TODO, 0: ethereum, this will be get from config file
177 // zki.FeeIdxs = ? // TODO, this will be get from the config file
178 tokenIDs, err := s.getTokenIDsBigInt(l1usertxs, l1coordinatortxs, l2txs)
179 if err != nil {
180 return nil, nil, err
181 }
182 s.zki.FeePlanTokens = tokenIDs
183
184 // s.zki.ISInitStateRootFee = s.mt.Root().BigInt()
185
186 // TODO once the Node Config sets the Accounts where to send the Fees
187 // compute fees & update ZKInputs
188
189 // return exitInfos, so Synchronizer will be able to store it into
190 // HistoryDB for the concrete BatchNum
191 return s.zki, exitInfos, nil
192}
193
194// getTokenIDsBigInt returns the list of TokenIDs in *big.Int format
195func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) ([]*big.Int, error) {
196 tokenIDs := make(map[common.TokenID]bool)
197 for i := 0; i < len(l1usertxs); i++ {
198 tokenIDs[l1usertxs[i].TokenID] = true
199 }
200 for i := 0; i < len(l1coordinatortxs); i++ {
201 tokenIDs[l1coordinatortxs[i].TokenID] = true
202 }
203 for i := 0; i < len(l2txs); i++ {
204 // as L2Tx does not have parameter TokenID, get it from the
205 // AccountsDB (in the StateDB)
206 acc, err := s.GetAccount(l2txs[i].ToIdx)
207 if err != nil {
208 return nil, err
209 }
210 tokenIDs[acc.TokenID] = true
211 }
212 var tBI []*big.Int
213 for t := range tokenIDs {
214 tBI = append(tBI, t.BigInt())
215 }
216 return tBI, nil
217}
218
219// processL1Tx process the given L1Tx applying the needed updates to the
220// StateDB depending on the transaction Type. It returns the 3 parameters
221// related to the Exit (in case of): Idx, ExitAccount, boolean determining if
222// the Exit created a new Leaf in the ExitTree.
223func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, bool, error) {
224 // ZKInputs
225 if s.zki != nil {
226 // Txs
227 // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready
228 s.zki.FromIdx[s.i] = tx.FromIdx.BigInt()
229 s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()
230 s.zki.OnChain[s.i] = big.NewInt(1)
231
232 // L1Txs
233 s.zki.LoadAmountF[s.i] = tx.LoadAmount
234 s.zki.FromEthAddr[s.i] = common.EthAddrToBigInt(tx.FromEthAddr)
235 if tx.FromBJJ != nil {
236 s.zki.FromBJJCompressed[s.i] = BJJCompressedTo256BigInts(tx.FromBJJ.Compress())
237 }
238
239 // Intermediate States
240 s.zki.ISOnChain[s.i] = big.NewInt(1)
241 }
242
243 switch tx.Type {
244 case common.TxTypeForceTransfer, common.TxTypeTransfer:
245 // go to the MT account of sender and receiver, and update balance
246 // & nonce
247 err := s.applyTransfer(tx.Tx(), 0) // 0 for the parameter toIdx, as at L1Tx ToIdx can only be 0 in the Deposit type case.
248 if err != nil {
249 return nil, nil, false, err
250 }
251 case common.TxTypeCreateAccountDeposit:
252 // add new account to the MT, update balance of the MT account
253 err := s.applyCreateAccount(tx)
254 if err != nil {
255 return nil, nil, false, err
256 }
257
258 if s.zki != nil {
259 s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account
260 s.zki.NewAccount[s.i] = big.NewInt(1)
261 }
262 case common.TxTypeDeposit:
263 // update balance of the MT account
264 err := s.applyDeposit(tx, false)
265 if err != nil {
266 return nil, nil, false, err
267 }
268 case common.TxTypeDepositTransfer:
269 // update balance in MT account, update balance & nonce of sender
270 // & receiver
271 err := s.applyDeposit(tx, true)
272 if err != nil {
273 return nil, nil, false, err
274 }
275 case common.TxTypeCreateAccountDepositTransfer:
276 // add new account to the merkletree, update balance in MT account,
277 // update balance & nonce of sender & receiver
278 err := s.applyCreateAccountDepositTransfer(tx)
279 if err != nil {
280 return nil, nil, false, err
281 }
282
283 if s.zki != nil {
284 s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account
285 s.zki.NewAccount[s.i] = big.NewInt(1)
286 }
287 case common.TxTypeExit:
288 // execute exit flow
289 exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx())
290 if err != nil {
291 return nil, nil, false, err
292 }
293 return &tx.FromIdx, exitAccount, newExit, nil
294 default:
295 }
296
297 return nil, nil, false, nil
298}
299
300// processL2Tx process the given L2Tx applying the needed updates to the
301// StateDB depending on the transaction Type. It returns the 3 parameters
302// related to the Exit (in case of): Idx, ExitAccount, boolean determining if
303// the Exit created a new Leaf in the ExitTree.
304func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) {
305 var err error
306 var auxToIdx common.Idx
307 // if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ
308 if tx.ToIdx == common.Idx(0) {
309 auxToIdx, err = s.GetIdxByEthAddrBJJ(tx.ToEthAddr, tx.ToBJJ)
310 if err != nil {
311 log.Error(err)
312 return nil, nil, false, err
313 }
314 }
315
316 // ZKInputs
317 if s.zki != nil {
318 // Txs
319 // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready
320 // s.zki.TxCompressedDataV2[s.i] = tx.TxCompressedDataV2() // uncomment once L2Tx.TxCompressedDataV2 is ready
321 s.zki.FromIdx[s.i] = tx.FromIdx.BigInt()
322 s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()
323
324 // fill AuxToIdx if needed
325 if tx.ToIdx == common.Idx(0) {
326 // use toIdx that can have been filled by tx.ToIdx or
327 // if tx.Idx==0 (this case), toIdx is filled by the Idx
328 // from db by ToEthAddr&ToBJJ
329 s.zki.AuxToIdx[s.i] = auxToIdx.BigInt()
330 }
331
332 s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y
333 s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(tx.ToEthAddr)
334
335 s.zki.OnChain[s.i] = big.NewInt(0)
336 s.zki.NewAccount[s.i] = big.NewInt(0)
337
338 // L2Txs
339 // s.zki.RqOffset[s.i] = // TODO Rq once TxSelector is ready
340 // s.zki.RqTxCompressedDataV2[s.i] = // TODO
341 // s.zki.RqToEthAddr[s.i] = common.EthAddrToBigInt(tx.RqToEthAddr) // TODO
342 // s.zki.RqToBJJAy[s.i] = tx.ToBJJ.Y // TODO
343 s.zki.S[s.i] = tx.Signature.S
344 s.zki.R8x[s.i] = tx.Signature.R8.X
345 s.zki.R8y[s.i] = tx.Signature.R8.Y
346 }
347
348 switch tx.Type {
349 case common.TxTypeTransfer:
350 // go to the MT account of sender and receiver, and update
351 // balance & nonce
352 err = s.applyTransfer(tx.Tx(), auxToIdx)
353 if err != nil {
354 return nil, nil, false, err
355 }
356 case common.TxTypeExit:
357 // execute exit flow
358 exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx())
359 if err != nil {
360 return nil, nil, false, err
361 }
362 return &tx.FromIdx, exitAccount, newExit, nil
363 default:
364 }
365 return nil, nil, false, nil
366}
367
368// applyCreateAccount creates a new account in the account of the depositer, it
369// stores the deposit value
370func (s *StateDB) applyCreateAccount(tx *common.L1Tx) error {
371 account := &common.Account{
372 TokenID: tx.TokenID,
373 Nonce: 0,
374 Balance: tx.LoadAmount,
375 PublicKey: tx.FromBJJ,
376 EthAddr: tx.FromEthAddr,
377 }
378
379 p, err := s.CreateAccount(common.Idx(s.idx+1), account)
380 if err != nil {
381 return err
382 }
383 if s.zki != nil {
384 s.zki.TokenID1[s.i] = tx.TokenID.BigInt()
385 s.zki.Nonce1[s.i] = big.NewInt(0)
386 if babyjub.PointCoordSign(tx.FromBJJ.X) {
387 s.zki.Sign1[s.i] = big.NewInt(1)
388 }
389 s.zki.Ay1[s.i] = tx.FromBJJ.Y
390 s.zki.Balance1[s.i] = tx.LoadAmount
391 s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(tx.FromEthAddr)
392 s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings)
393 if p.IsOld0 {
394 s.zki.IsOld0_1[s.i] = big.NewInt(1)
395 }
396 s.zki.OldKey1[s.i] = p.OldKey.BigInt()
397 s.zki.OldValue1[s.i] = p.OldValue.BigInt()
398 }
399
400 s.idx = s.idx + 1
401 return s.setIdx(s.idx)
402}
403
404// applyDeposit updates the balance in the account of the depositer, if
405// andTransfer parameter is set to true, the method will also apply the
406// Transfer of the L1Tx/DepositTransfer
407func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error {
408 // deposit the tx.LoadAmount into the sender account
409 accSender, err := s.GetAccount(tx.FromIdx)
410 if err != nil {
411 return err
412 }
413 accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount)
414
415 // in case that the tx is a L1Tx>DepositTransfer
416 var accReceiver *common.Account
417 if transfer {
418 accReceiver, err = s.GetAccount(tx.ToIdx)
419 if err != nil {
420 return err
421 }
422 // subtract amount to the sender
423 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
424 // add amount to the receiver
425 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
426 }
427 // update sender account in localStateDB
428 p, err := s.UpdateAccount(tx.FromIdx, accSender)
429 if err != nil {
430 return err
431 }
432 if s.zki != nil {
433 s.zki.TokenID1[s.i] = accSender.TokenID.BigInt()
434 s.zki.Nonce1[s.i] = accSender.Nonce.BigInt()
435 if babyjub.PointCoordSign(accSender.PublicKey.X) {
436 s.zki.Sign1[s.i] = big.NewInt(1)
437 }
438 s.zki.Ay1[s.i] = accSender.PublicKey.Y
439 s.zki.Balance1[s.i] = accSender.Balance
440 s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(accSender.EthAddr)
441 s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings)
442 // IsOld0_1, OldKey1, OldValue1 not needed as this is not an insert
443 }
444
445 // this is done after updating Sender Account (depositer)
446 if transfer {
447 // update receiver account in localStateDB
448 p, err := s.UpdateAccount(tx.ToIdx, accReceiver)
449 if err != nil {
450 return err
451 }
452 if s.zki != nil {
453 s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt()
454 s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt()
455 if babyjub.PointCoordSign(accReceiver.PublicKey.X) {
456 s.zki.Sign2[s.i] = big.NewInt(1)
457 }
458 s.zki.Ay2[s.i] = accReceiver.PublicKey.Y
459 s.zki.Balance2[s.i] = accReceiver.Balance
460 s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr)
461 s.zki.Siblings2[s.i] = siblingsToZKInputFormat(p.Siblings)
462 // IsOld0_2, OldKey2, OldValue2 not needed as this is not an insert
463 }
464 }
465
466 return nil
467}
468
469// applyTransfer updates the balance & nonce in the account of the sender, and
470// the balance in the account of the receiver.
471// Parameter 'toIdx' should be at 0 if the tx already has tx.ToIdx!=0, if
472// tx.ToIdx==0, then toIdx!=0, and will be used the toIdx parameter as Idx of
473// the receiver. This parameter is used when the tx.ToIdx is not specified and
474// the real ToIdx is found trhrough the ToEthAddr or ToBJJ.
475func (s *StateDB) applyTransfer(tx *common.Tx, auxToIdx common.Idx) error {
476 if auxToIdx == 0 {
477 auxToIdx = tx.ToIdx
478 }
479 // get sender and receiver accounts from localStateDB
480 accSender, err := s.GetAccount(tx.FromIdx)
481 if err != nil {
482 return err
483 }
484 accReceiver, err := s.GetAccount(auxToIdx)
485 if err != nil {
486 return err
487 }
488
489 // increment nonce
490 accSender.Nonce++
491
492 // subtract amount to the sender
493 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
494 // add amount to the receiver
495 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
496
497 // update sender account in localStateDB
498 pSender, err := s.UpdateAccount(tx.FromIdx, accSender)
499 if err != nil {
500 return err
501 }
502 if s.zki != nil {
503 s.zki.TokenID1[s.i] = accSender.TokenID.BigInt()
504 s.zki.Nonce1[s.i] = accSender.Nonce.BigInt()
505 if babyjub.PointCoordSign(accSender.PublicKey.X) {
506 s.zki.Sign1[s.i] = big.NewInt(1)
507 }
508 s.zki.Ay1[s.i] = accSender.PublicKey.Y
509 s.zki.Balance1[s.i] = accSender.Balance
510 s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(accSender.EthAddr)
511 s.zki.Siblings1[s.i] = siblingsToZKInputFormat(pSender.Siblings)
512 }
513
514 // update receiver account in localStateDB
515 pReceiver, err := s.UpdateAccount(auxToIdx, accReceiver)
516 if err != nil {
517 return err
518 }
519 if s.zki != nil {
520 s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt()
521 s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt()
522 if babyjub.PointCoordSign(accReceiver.PublicKey.X) {
523 s.zki.Sign2[s.i] = big.NewInt(1)
524 }
525 s.zki.Ay2[s.i] = accReceiver.PublicKey.Y
526 s.zki.Balance2[s.i] = accReceiver.Balance
527 s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr)
528 s.zki.Siblings2[s.i] = siblingsToZKInputFormat(pReceiver.Siblings)
529 }
530
531 return nil
532}
533
534// applyCreateAccountDepositTransfer, in a single tx, creates a new account,
535// makes a deposit, and performs a transfer to another account
536func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error {
537 accSender := &common.Account{
538 TokenID: tx.TokenID,
539 Nonce: 0,
540 Balance: tx.LoadAmount,
541 PublicKey: tx.FromBJJ,
542 EthAddr: tx.FromEthAddr,
543 }
544 accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount)
545 accReceiver, err := s.GetAccount(tx.ToIdx)
546 if err != nil {
547 return err
548 }
549 // subtract amount to the sender
550 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
551 // add amount to the receiver
552 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
553
554 // create Account of the Sender
555 p, err := s.CreateAccount(common.Idx(s.idx+1), accSender)
556 if err != nil {
557 return err
558 }
559 if s.zki != nil {
560 s.zki.TokenID1[s.i] = tx.TokenID.BigInt()
561 s.zki.Nonce1[s.i] = big.NewInt(0)
562 if babyjub.PointCoordSign(tx.FromBJJ.X) {
563 s.zki.Sign1[s.i] = big.NewInt(1)
564 }
565 s.zki.Ay1[s.i] = tx.FromBJJ.Y
566 s.zki.Balance1[s.i] = tx.LoadAmount
567 s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(tx.FromEthAddr)
568 s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings)
569 if p.IsOld0 {
570 s.zki.IsOld0_1[s.i] = big.NewInt(1)
571 }
572 s.zki.OldKey1[s.i] = p.OldKey.BigInt()
573 s.zki.OldValue1[s.i] = p.OldValue.BigInt()
574 }
575
576 // update receiver account in localStateDB
577 p, err = s.UpdateAccount(tx.ToIdx, accReceiver)
578 if err != nil {
579 return err
580 }
581 if s.zki != nil {
582 s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt()
583 s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt()
584 if babyjub.PointCoordSign(accReceiver.PublicKey.X) {
585 s.zki.Sign2[s.i] = big.NewInt(1)
586 }
587 s.zki.Ay2[s.i] = accReceiver.PublicKey.Y
588 s.zki.Balance2[s.i] = accReceiver.Balance
589 s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr)
590 s.zki.Siblings2[s.i] = siblingsToZKInputFormat(p.Siblings)
591 }
592
593 s.idx = s.idx + 1
594 return s.setIdx(s.idx)
595}
596
597// It returns the ExitAccount and a boolean determining if the Exit created a
598// new Leaf in the ExitTree.
599func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*common.Account, bool, error) {
600 // 0. subtract tx.Amount from current Account in StateMT
601 // add the tx.Amount into the Account (tx.FromIdx) in the ExitMT
602 acc, err := s.GetAccount(tx.FromIdx)
603 if err != nil {
604 return nil, false, err
605 }
606 acc.Balance = new(big.Int).Sub(acc.Balance, tx.Amount)
607 p, err := s.UpdateAccount(tx.FromIdx, acc)
608 if err != nil {
609 return nil, false, err
610 }
611 if s.zki != nil {
612 s.zki.TokenID1[s.i] = acc.TokenID.BigInt()
613 s.zki.Nonce1[s.i] = acc.Nonce.BigInt()
614 if babyjub.PointCoordSign(acc.PublicKey.X) {
615 s.zki.Sign1[s.i] = big.NewInt(1)
616 }
617 s.zki.Ay1[s.i] = acc.PublicKey.Y
618 s.zki.Balance1[s.i] = acc.Balance
619 s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(acc.EthAddr)
620 s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings)
621 }
622
623 if exitTree == nil {
624 return nil, false, nil
625 }
626 exitAccount, err := getAccountInTreeDB(exitTree.DB(), tx.FromIdx)
627 if err == db.ErrNotFound {
628 // 1a. if idx does not exist in exitTree:
629 // add new leaf 'ExitTreeLeaf', where ExitTreeLeaf.Balance = exitAmount (exitAmount=tx.Amount)
630 exitAccount := &common.Account{
631 TokenID: acc.TokenID,
632 Nonce: common.Nonce(1),
633 Balance: tx.Amount,
634 PublicKey: acc.PublicKey,
635 EthAddr: acc.EthAddr,
636 }
637 _, err = createAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
638 return exitAccount, true, err
639 } else if err != nil {
640 return exitAccount, false, err
641 }
642
643 // 1b. if idx already exist in exitTree:
644 // update account, where account.Balance += exitAmount
645 exitAccount.Balance = new(big.Int).Add(exitAccount.Balance, tx.Amount)
646 _, err = updateAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
647 return exitAccount, false, err
648}
649
650// getIdx returns the stored Idx from the localStateDB, which is the last Idx
651// used for an Account in the localStateDB.
652func (s *StateDB) getIdx() (common.Idx, error) {
653 idxBytes, err := s.DB().Get(keyidx)
654 if err == db.ErrNotFound {
655 return 0, nil
656 }
657 if err != nil {
658 return 0, err
659 }
660 return common.IdxFromBytes(idxBytes[:4])
661}
662
663// setIdx stores Idx in the localStateDB
664func (s *StateDB) setIdx(idx common.Idx) error {
665 tx, err := s.DB().NewTx()
666 if err != nil {
667 return err
668 }
669 idxBytes, err := idx.Bytes()
670 if err != nil {
671 return err
672 }
673 err = tx.Put(keyidx, idxBytes[:])
674 if err != nil {
675 return err
676 }
677 if err := tx.Commit(); err != nil {
678 return err
679 }
680 return nil
681}