hermez-node/db/statedb/txprocessors.go
db/statedb/txprocessors.goRaw
1package statedb
2
3import (
4 "math/big"
5
6 "github.com/hermeznetwork/hermez-node/common"
7 "github.com/iden3/go-iden3-crypto/poseidon"
8 "github.com/iden3/go-merkletree"
9 "github.com/iden3/go-merkletree/db"
10 "github.com/iden3/go-merkletree/db/memory"
11)
12
13// keyidx is used as key in the db to store the current Idx
14var keyidx = []byte("idx")
15
16// FUTURE This will be used from common once pending PR is merged
17type ExitInfo struct {
18 Idx *common.Idx
19 Proof *merkletree.CircomVerifierProof
20 Nullifier *big.Int
21 Balance *big.Int
22}
23
24// ProcessTxs process the given L1Txs & L2Txs applying the needed updates to
25// the StateDB depending on the transaction Type. Returns the common.ZKInputs
26// to generate the SnarkProof later used by the BatchBuilder, and if
27// cmpExitTree is set to true, returns common.ExitTreeLeaf that is later used
28// by the Synchronizer to update the HistoryDB.
29func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.L2Tx) (*common.ZKInputs, []*ExitInfo, error) {
30 var err error
31 var exitTree *merkletree.MerkleTree
32 exits := make(map[common.Idx]common.Account)
33 if cmpExitTree {
34 // TBD if ExitTree is only in memory or stored in disk, for the moment
35 // only needed in memory
36 exitTree, err = merkletree.NewMerkleTree(memory.NewMemoryStorage(), s.mt.MaxLevels())
37 if err != nil {
38 return nil, nil, err
39 }
40 }
41
42 for _, tx := range l1usertxs {
43 exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx)
44 if err != nil {
45 return nil, nil, err
46 }
47 if exitIdx != nil && cmpExitTree {
48 exits[*exitIdx] = *exitAccount
49 }
50 }
51 for _, tx := range l1coordinatortxs {
52 exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx)
53 if err != nil {
54 return nil, nil, err
55 }
56 if exitIdx != nil && cmpExitTree {
57 exits[*exitIdx] = *exitAccount
58 }
59 }
60 for _, tx := range l2txs {
61 exitIdx, exitAccount, err := s.processL2Tx(exitTree, tx)
62 if err != nil {
63 return nil, nil, err
64 }
65 if exitIdx != nil && cmpExitTree {
66 exits[*exitIdx] = *exitAccount
67 }
68 }
69
70 if !cmpExitTree {
71 return nil, nil, nil
72 }
73
74 // once all txs processed (exitTree root frozen), for each leaf
75 // generate ExitInfo data
76 var exitInfos []*ExitInfo
77 for exitIdx, exitAccount := range exits {
78 // 0. generate MerkleProof
79 p, err := exitTree.GenerateCircomVerifierProof(exitIdx.BigInt(), nil)
80 if err != nil {
81 return nil, nil, err
82 }
83 // 1. compute nullifier
84 exitAccStateValue, err := exitAccount.HashValue()
85 if err != nil {
86 return nil, nil, err
87 }
88 nullifier, err := poseidon.Hash([]*big.Int{
89 exitAccStateValue,
90 big.NewInt(int64(s.currentBatch)),
91 exitTree.Root().BigInt(),
92 })
93 if err != nil {
94 return nil, nil, err
95 }
96 // 2. generate ExitInfo
97 ei := &ExitInfo{
98 Idx: &exitIdx,
99 Proof: p,
100 Nullifier: nullifier,
101 Balance: exitAccount.Balance,
102 }
103 exitInfos = append(exitInfos, ei)
104 }
105
106 // return exitInfos, so Synchronizer will be able to store it into
107 // HistoryDB for the concrete BatchNum
108 return nil, exitInfos, nil
109}
110
111// processL2Tx process the given L2Tx applying the needed updates to
112// the StateDB depending on the transaction Type.
113func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.L2Tx) (*common.Idx, *common.Account, error) {
114 switch tx.Type {
115 case common.TxTypeTransfer:
116 // go to the MT account of sender and receiver, and update
117 // balance & nonce
118 err := s.applyTransfer(tx.Tx())
119 if err != nil {
120 return nil, nil, err
121 }
122 case common.TxTypeExit:
123 // execute exit flow
124 exitAccount, err := s.applyExit(exitTree, tx.Tx())
125 if err != nil {
126 return nil, nil, err
127 }
128 return &tx.FromIdx, exitAccount, nil
129 default:
130 }
131 return nil, nil, nil
132}
133
134// processL1Tx process the given L1Tx applying the needed updates to the
135// StateDB depending on the transaction Type.
136func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, error) {
137 switch tx.Type {
138 case common.TxTypeForceTransfer, common.TxTypeTransfer:
139 // go to the MT account of sender and receiver, and update balance
140 // & nonce
141 err := s.applyTransfer(tx.Tx())
142 if err != nil {
143 return nil, nil, err
144 }
145 case common.TxTypeCreateAccountDeposit:
146 // add new account to the MT, update balance of the MT account
147 err := s.applyCreateAccount(tx)
148 if err != nil {
149 return nil, nil, err
150 }
151 case common.TxTypeDeposit:
152 // update balance of the MT account
153 err := s.applyDeposit(tx, false)
154 if err != nil {
155 return nil, nil, err
156 }
157 case common.TxTypeDepositTransfer:
158 // update balance in MT account, update balance & nonce of sender
159 // & receiver
160 err := s.applyDeposit(tx, true)
161 if err != nil {
162 return nil, nil, err
163 }
164 case common.TxTypeCreateAccountDepositTransfer:
165 // add new account to the merkletree, update balance in MT account,
166 // update balance & nonce of sender & receiver
167 err := s.applyCreateAccount(tx)
168 if err != nil {
169 return nil, nil, err
170 }
171 err = s.applyTransfer(tx.Tx())
172 if err != nil {
173 return nil, nil, err
174 }
175 case common.TxTypeExit:
176 // execute exit flow
177 exitAccount, err := s.applyExit(exitTree, tx.Tx())
178 if err != nil {
179 return nil, nil, err
180 }
181 return &tx.FromIdx, exitAccount, nil
182 default:
183 }
184
185 return nil, nil, nil
186}
187
188// applyCreateAccount creates a new account in the account of the depositer, it
189// stores the deposit value
190func (s *StateDB) applyCreateAccount(tx *common.L1Tx) error {
191 account := &common.Account{
192 TokenID: tx.TokenID,
193 Nonce: 0,
194 Balance: tx.LoadAmount,
195 PublicKey: tx.FromBJJ,
196 EthAddr: tx.FromEthAddr,
197 }
198
199 _, err := s.CreateAccount(common.Idx(s.idx+1), account)
200 if err != nil {
201 return err
202 }
203
204 s.idx = s.idx + 1
205 return s.setIdx(s.idx)
206}
207
208// applyDeposit updates the balance in the account of the depositer, if
209// andTransfer parameter is set to true, the method will also apply the
210// Transfer of the L1Tx/DepositTransfer
211func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error {
212 // deposit the tx.LoadAmount into the sender account
213 accSender, err := s.GetAccount(tx.FromIdx)
214 if err != nil {
215 return err
216 }
217 accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount)
218
219 // in case that the tx is a L1Tx>DepositTransfer
220 if transfer {
221 accReceiver, err := s.GetAccount(tx.ToIdx)
222 if err != nil {
223 return err
224 }
225 // substract amount to the sender
226 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
227 // add amount to the receiver
228 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
229 // update receiver account in localStateDB
230 _, err = s.UpdateAccount(tx.ToIdx, accReceiver)
231 if err != nil {
232 return err
233 }
234 }
235 // update sender account in localStateDB
236 _, err = s.UpdateAccount(tx.FromIdx, accSender)
237 if err != nil {
238 return err
239 }
240 return nil
241}
242
243// applyTransfer updates the balance & nonce in the account of the sender, and
244// the balance in the account of the receiver
245func (s *StateDB) applyTransfer(tx *common.Tx) error {
246 // get sender and receiver accounts from localStateDB
247 accSender, err := s.GetAccount(tx.FromIdx)
248 if err != nil {
249 return err
250 }
251 accReceiver, err := s.GetAccount(tx.ToIdx)
252 if err != nil {
253 return err
254 }
255
256 // increment nonce
257 accSender.Nonce++
258
259 // substract amount to the sender
260 accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
261 // add amount to the receiver
262 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
263
264 // update receiver account in localStateDB
265 _, err = s.UpdateAccount(tx.ToIdx, accReceiver)
266 if err != nil {
267 return err
268 }
269 // update sender account in localStateDB
270 _, err = s.UpdateAccount(tx.FromIdx, accSender)
271 if err != nil {
272 return err
273 }
274
275 return nil
276}
277
278func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*common.Account, error) {
279 // 0. substract tx.Amount from current Account in StateMT
280 // add the tx.Amount into the Account (tx.FromIdx) in the ExitMT
281 acc, err := s.GetAccount(tx.FromIdx)
282 if err != nil {
283 return nil, err
284 }
285 acc.Balance = new(big.Int).Sub(acc.Balance, tx.Amount)
286 _, err = s.UpdateAccount(tx.FromIdx, acc)
287 if err != nil {
288 return nil, err
289 }
290
291 exitAccount, err := getAccountInTreeDB(exitTree.DB(), tx.FromIdx)
292 if err == db.ErrNotFound {
293 // 1a. if idx does not exist in exitTree:
294 // add new leaf 'ExitTreeLeaf', where ExitTreeLeaf.Balance = exitAmount (exitAmount=tx.Amount)
295 exitAccount := &common.Account{
296 TokenID: acc.TokenID,
297 Nonce: common.Nonce(1),
298 Balance: tx.Amount,
299 PublicKey: acc.PublicKey,
300 EthAddr: acc.EthAddr,
301 }
302 _, err = createAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
303 return exitAccount, err
304 } else if err != nil {
305 return exitAccount, err
306 }
307
308 // 1b. if idx already exist in exitTree:
309 // update account, where account.Balance += exitAmount
310 exitAccount.Balance = new(big.Int).Add(exitAccount.Balance, tx.Amount)
311 _, err = updateAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount)
312 return exitAccount, err
313}
314
315// getIdx returns the stored Idx from the localStateDB, which is the last Idx
316// used for an Account in the localStateDB.
317func (s *StateDB) getIdx() (common.Idx, error) {
318 idxBytes, err := s.DB().Get(keyidx)
319 if err == db.ErrNotFound {
320 return 0, nil
321 }
322 if err != nil {
323 return 0, err
324 }
325 return common.IdxFromBytes(idxBytes[:4])
326}
327
328// setIdx stores Idx in the localStateDB
329func (s *StateDB) setIdx(idx common.Idx) error {
330 tx, err := s.DB().NewTx()
331 if err != nil {
332 return err
333 }
334 tx.Put(keyidx, idx.Bytes())
335 if err := tx.Commit(); err != nil {
336 return err
337 }
338 return nil
339}