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