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