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