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