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