hermez-node/db/statedb/txprocessors.go
db/statedb/txprocessors.goRaw
1package statedb
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "math/big"
8
9 ethCommon "github.com/ethereum/go-ethereum/common"
10 "github.com/hermeznetwork/hermez-node/common"
11 "github.com/iden3/go-iden3-crypto/poseidon"
12 "github.com/iden3/go-merkletree"
13 "github.com/iden3/go-merkletree/db"
14 "github.com/iden3/go-merkletree/db/memory"
15)
16
17var (
18 // keyidx is used as key in the db to store the current Idx
19 keyidx = []byte("idx")
20
21 ffAddr = ethCommon.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff")
22)
23
24func (s *StateDB) resetZKInputs() {
25 s.zki = nil
26 s.i = 0
27}
28
29// ProcessTxs process the given L1Txs & L2Txs applying the needed updates to
30// the StateDB depending on the transaction Type. Returns the common.ZKInputs
31// to generate the SnarkProof later used by the BatchBuilder, and if
32// cmpExitTree is set to true, returns common.ExitTreeLeaf that is later used
33// by the Synchronizer to update the HistoryDB.
34func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) (*common.ZKInputs, []*common.ExitInfo, error) {
35 var err error
36 var exitTree *merkletree.MerkleTree
37 exits := make(map[common.Idx]common.Account)
38
39 if s.zki != nil {
40 return nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong ans is not empty")
41 }
42 defer s.resetZKInputs()
43
44 nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs)
45 if nTx == 0 {
46 return nil, nil, nil // TBD if return an error in the case of no Txs to process
47 }
48
49 if cmpZKInputs {
50 s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function
51 }
52
53 // TBD if ExitTree is only in memory or stored in disk, for the moment
54 // only needed in memory
55 exitTree, err = merkletree.NewMerkleTree(memory.NewMemoryStorage(), s.mt.MaxLevels())
56 if err != nil {
57 return nil, nil, err
58 }
59
60 // assumption: l1usertx are sorted by L1Tx.Position
61 for _, tx := range l1usertxs {
62 exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx)
63 if err != nil {
64 return nil, nil, err
65 }
66 if exitIdx != nil && cmpExitTree {
67 exits[*exitIdx] = *exitAccount
68 }
69 if s.zki != nil {
70 s.i++
71 }
72 }
73 for _, tx := range l1coordinatortxs {
74 exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx)
75 if err != nil {
76 return nil, nil, err
77 }
78 if exitIdx != nil && cmpExitTree {
79 exits[*exitIdx] = *exitAccount
80 }
81 if s.zki != nil {
82 s.i++
83 }
84 }
85 for _, tx := range l2txs {
86 exitIdx, exitAccount, err := s.processL2Tx(exitTree, tx)
87 if err != nil {
88 return nil, nil, err
89 }
90 if exitIdx != nil && cmpExitTree {
91 exits[*exitIdx] = *exitAccount
92 }
93 if s.zki != nil {
94 s.i++
95 }
96 }
97
98 if !cmpExitTree && !cmpZKInputs {
99 return nil, nil, nil
100 }
101
102 // once all txs processed (exitTree root frozen), for each leaf
103 // generate common.ExitInfo data
104 var exitInfos []*common.ExitInfo
105 for exitIdx, exitAccount := range exits {
106 // 0. generate MerkleProof
107 p, err := exitTree.GenerateCircomVerifierProof(exitIdx.BigInt(), nil)
108 if err != nil {
109 return nil, nil, err
110 }
111 // 1. compute nullifier
112 exitAccStateValue, err := exitAccount.HashValue()
113 if err != nil {
114 return nil, nil, err
115 }
116 nullifier, err := poseidon.Hash([]*big.Int{
117 exitAccStateValue,
118 big.NewInt(int64(s.currentBatch)),
119 exitTree.Root().BigInt(),
120 })
121 if err != nil {
122 return nil, nil, err
123 }
124 // 2. generate common.ExitInfo
125 ei := &common.ExitInfo{
126 AccountIdx: exitIdx,
127 MerkleProof: p,
128 Nullifier: nullifier,
129 Balance: exitAccount.Balance,
130 }
131 exitInfos = append(exitInfos, ei)
132 }
133 if !cmpZKInputs {
134 return nil, exitInfos, nil
135 }
136
137 // compute last ZKInputs parameters
138 s.zki.OldLastIdx = (s.idx - 1).BigInt()
139 s.zki.OldStateRoot = s.mt.Root().BigInt()
140 s.zki.GlobalChainID = big.NewInt(0) // TODO, 0: ethereum, get this from config file
141 // zki.FeeIdxs = ? // TODO, this will be get from the config file
142 tokenIDs, err := s.getTokenIDsBigInt(l1usertxs, l1coordinatortxs, l2txs)
143 if err != nil {
144 return nil, nil, err
145 }
146 s.zki.FeePlanTokens = tokenIDs
147
148 // s.zki.ISInitStateRootFee = s.mt.Root().BigInt()
149 // compute fees
150
151 // once fees are computed
152
153 // return exitInfos, so Synchronizer will be able to store it into
154 // HistoryDB for the concrete BatchNum
155 return s.zki, exitInfos, nil
156}
157
158// getTokenIDsBigInt returns the list of TokenIDs in *big.Int format
159func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) ([]*big.Int, error) {
160 tokenIDs := make(map[common.TokenID]bool)
161 for i := 0; i < len(l1usertxs); i++ {
162 tokenIDs[l1usertxs[i].TokenID] = true
163 }
164 for i := 0; i < len(l1coordinatortxs); i++ {
165 tokenIDs[l1coordinatortxs[i].TokenID] = true
166 }
167 for i := 0; i < len(l2txs); i++ {
168 // as L2Tx does not have parameter TokenID, get it from the
169 // AccountsDB (in the StateDB)
170 acc, err := s.GetAccount(l2txs[i].ToIdx)
171 if err != nil {
172 return nil, err
173 }
174 tokenIDs[acc.TokenID] = true
175 }
176 var tBI []*big.Int
177 for t := range tokenIDs {
178 tBI = append(tBI, t.BigInt())
179 }
180 return tBI, nil
181}
182
183// processL1Tx process the given L1Tx applying the needed updates to the
184// StateDB depending on the transaction Type.
185func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, error) {
186 // ZKInputs
187 if s.zki != nil {
188 // Txs
189 // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready
190 s.zki.FromIdx[s.i] = tx.FromIdx.BigInt()
191 s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()
192 s.zki.OnChain[s.i] = big.NewInt(1)
193
194 // L1Txs
195 s.zki.LoadAmountF[s.i] = tx.LoadAmount
196 s.zki.FromEthAddr[s.i] = common.EthAddrToBigInt(tx.FromEthAddr)
197 if tx.FromBJJ != nil {
198 s.zki.FromBJJCompressed[s.i] = common.BJJCompressedTo256BigInts(tx.FromBJJ.Compress())
199 }
200
201 // Intermediate States
202 s.zki.ISOnChain[s.i] = big.NewInt(1)
203 }
204
205 switch tx.Type {
206 case common.TxTypeForceTransfer, common.TxTypeTransfer:
207 // go to the MT account of sender and receiver, and update balance
208 // & nonce
209 err := s.applyTransfer(tx.Tx())
210 if err != nil {
211 return nil, nil, err
212 }
213 case common.TxTypeCreateAccountDeposit:
214 // add new account to the MT, update balance of the MT account
215 err := s.applyCreateAccount(tx)
216 if err != nil {
217 return nil, nil, err
218 }
219
220 if s.zki != nil {
221 s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account
222 s.zki.NewAccount[s.i] = big.NewInt(1)
223 }
224 case common.TxTypeDeposit:
225 // update balance of the MT account
226 err := s.applyDeposit(tx, false)
227 if err != nil {
228 return nil, nil, err
229 }
230 case common.TxTypeDepositTransfer:
231 // update balance in MT account, update balance & nonce of sender
232 // & receiver
233 err := s.applyDeposit(tx, true)
234 if err != nil {
235 return nil, nil, err
236 }
237 case common.TxTypeCreateAccountDepositTransfer:
238 // add new account to the merkletree, update balance in MT account,
239 // update balance & nonce of sender & receiver
240 err := s.applyCreateAccount(tx)
241 if err != nil {
242 return nil, nil, err
243 }
244 err = s.applyTransfer(tx.Tx())
245 if err != nil {
246 return nil, nil, err
247 }
248
249 if s.zki != nil {
250 s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account
251 s.zki.NewAccount[s.i] = big.NewInt(1)
252 }
253 case common.TxTypeExit:
254 // execute exit flow
255 exitAccount, err := s.applyExit(exitTree, tx.Tx())
256 if err != nil {
257 return nil, nil, err
258 }
259 return &tx.FromIdx, exitAccount, nil
260 default:
261 }
262
263 return nil, nil, nil
264}
265
266// processL2Tx process the given L2Tx applying the needed updates to
267// the StateDB depending on the transaction Type.
268func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, error) {
269 // ZKInputs
270 if s.zki != nil {
271 // Txs
272 // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready
273 // s.zki.TxCompressedDataV2[s.i] = tx.TxCompressedDataV2() // uncomment once L2Tx.TxCompressedDataV2 is ready
274 s.zki.FromIdx[s.i] = tx.FromIdx.BigInt()
275 s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()
276
277 // fill AuxToIdx if needed
278 if tx.ToIdx == common.Idx(0) {
279 // Idx not set in the Tx, get it from DB through ToEthAddr or ToBJJ
280 var idx common.Idx
281 if !bytes.Equal(tx.ToEthAddr.Bytes(), ffAddr.Bytes()) {
282 idx = s.getIdxByEthAddr(tx.ToEthAddr)
283 if idx == common.Idx(0) {
284 return nil, nil, fmt.Errorf("Idx can not be found for given tx.FromEthAddr")
285 }
286 } else {
287 idx = s.getIdxByBJJ(tx.ToBJJ)
288 if idx == common.Idx(0) {
289 return nil, nil, fmt.Errorf("Idx can not be found for given tx.FromBJJ")
290 }
291 }
292 s.zki.AuxToIdx[s.i] = idx.BigInt()
293 }
294 s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y
295 s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(tx.ToEthAddr)
296
297 s.zki.OnChain[s.i] = big.NewInt(0)
298 s.zki.NewAccount[s.i] = big.NewInt(0)
299
300 // L2Txs
301 // s.zki.RqOffset[s.i] = // TODO
302 // s.zki.RqTxCompressedDataV2[s.i] = // TODO
303 // s.zki.RqToEthAddr[s.i] = common.EthAddrToBigInt(tx.RqToEthAddr) // TODO
304 // s.zki.RqToBJJAy[s.i] = tx.ToBJJ.Y // TODO
305 s.zki.S[s.i] = tx.Signature.S
306 s.zki.R8x[s.i] = tx.Signature.R8.X
307 s.zki.R8y[s.i] = tx.Signature.R8.Y
308 }
309
310 switch tx.Type {
311 case common.TxTypeTransfer:
312 // go to the MT account of sender and receiver, and update
313 // balance & nonce
314 err := s.applyTransfer(tx.Tx())
315 if err != nil {
316 return nil, nil, err
317 }
318 case common.TxTypeExit:
319 // execute exit flow
320 exitAccount, err := s.applyExit(exitTree, tx.Tx())
321 if err != nil {
322 return nil, nil, err
323 }
324 return &tx.FromIdx, exitAccount, nil
325 default:
326 }
327 return nil, nil, nil
328}
329
330// applyCreateAccount creates a new account in the account of the depositer, it
331// stores the deposit value
332func (s *StateDB) applyCreateAccount(tx *common.L1Tx) error {
333 account := &common.Account{
334 TokenID: tx.TokenID,
335 Nonce: 0,
336 Balance: tx.LoadAmount,
337 PublicKey: tx.FromBJJ,
338 EthAddr: tx.FromEthAddr,
339 }
340
341 _, err := s.CreateAccount(common.Idx(s.idx+1), account)
342 if err != nil {
343 return err
344 }
345
346 s.idx = s.idx + 1
347 return s.setIdx(s.idx)
348}
349
350// applyDeposit updates the balance in the account of the depositer, if
351// andTransfer parameter is set to true, the method will also apply the
352// Transfer of the L1Tx/DepositTransfer
353func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error {
354 // deposit the tx.LoadAmount into the sender account
355 accSender, err := s.GetAccount(tx.FromIdx)
356 if err != nil {
357 return err
358 }
359 accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount)
360
361 // in case that the tx is a L1Tx>DepositTransfer
362 if transfer {
363 accReceiver, err := s.GetAccount(tx.ToIdx)
364 if err != nil {
365 return err
366 }
367 // subtract amount to the sender
368 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
369 // add amount to the receiver
370 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
371 // update receiver account in localStateDB
372 _, err = s.UpdateAccount(tx.ToIdx, accReceiver)
373 if err != nil {
374 return err
375 }
376 }
377 // update sender account in localStateDB
378 _, err = s.UpdateAccount(tx.FromIdx, accSender)
379 if err != nil {
380 return err
381 }
382 return nil
383}
384
385// applyTransfer updates the balance & nonce in the account of the sender, and
386// the balance in the account of the receiver
387func (s *StateDB) applyTransfer(tx *common.Tx) error {
388 // get sender and receiver accounts from localStateDB
389 accSender, err := s.GetAccount(tx.FromIdx)
390 if err != nil {
391 return err
392 }
393 accReceiver, err := s.GetAccount(tx.ToIdx)
394 if err != nil {
395 return err
396 }
397
398 // increment nonce
399 accSender.Nonce++
400
401 // subtract amount to the sender
402 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
403 // add amount to the receiver
404 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
405
406 // update receiver account in localStateDB
407 _, err = s.UpdateAccount(tx.ToIdx, accReceiver)
408 if err != nil {
409 return err
410 }
411 // update sender account in localStateDB
412 _, err = s.UpdateAccount(tx.FromIdx, accSender)
413 if err != nil {
414 return err
415 }
416
417 return nil
418}
419
420func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*common.Account, error) {
421 // 0. subtract tx.Amount from current Account in StateMT
422 // add the tx.Amount into the Account (tx.FromIdx) in the ExitMT
423 acc, err := s.GetAccount(tx.FromIdx)
424 if err != nil {
425 return nil, err
426 }
427 acc.Balance = new(big.Int).Sub(acc.Balance, tx.Amount)
428 _, err = s.UpdateAccount(tx.FromIdx, acc)
429 if err != nil {
430 return nil, err
431 }
432
433 exitAccount, err := getAccountInTreeDB(exitTree.DB(), tx.FromIdx)
434 if err == db.ErrNotFound {
435 // 1a. if idx does not exist in exitTree:
436 // add new leaf 'ExitTreeLeaf', where ExitTreeLeaf.Balance = exitAmount (exitAmount=tx.Amount)
437 exitAccount := &common.Account{
438 TokenID: acc.TokenID,
439 Nonce: common.Nonce(1),
440 Balance: tx.Amount,
441 PublicKey: acc.PublicKey,
442 EthAddr: acc.EthAddr,
443 }
444 _, err = createAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
445 return exitAccount, err
446 } else if err != nil {
447 return exitAccount, err
448 }
449
450 // 1b. if idx already exist in exitTree:
451 // update account, where account.Balance += exitAmount
452 exitAccount.Balance = new(big.Int).Add(exitAccount.Balance, tx.Amount)
453 _, err = updateAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
454 return exitAccount, err
455}
456
457// getIdx returns the stored Idx from the localStateDB, which is the last Idx
458// used for an Account in the localStateDB.
459func (s *StateDB) getIdx() (common.Idx, error) {
460 idxBytes, err := s.DB().Get(keyidx)
461 if err == db.ErrNotFound {
462 return 0, nil
463 }
464 if err != nil {
465 return 0, err
466 }
467 return common.IdxFromBytes(idxBytes[:4])
468}
469
470// setIdx stores Idx in the localStateDB
471func (s *StateDB) setIdx(idx common.Idx) error {
472 tx, err := s.DB().NewTx()
473 if err != nil {
474 return err
475 }
476 err = tx.Put(keyidx, idx.Bytes())
477 if err != nil {
478 return err
479 }
480 if err := tx.Commit(); err != nil {
481 return err
482 }
483 return nil
484}