arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/batchbuilder/batchbuilder.go
batchbuilder/batchbuilder.goRaw
  1package batchbuilder
  2
  3import (
  4	"encoding/binary"
  5	"math/big"
  6
  7	ethCommon "github.com/ethereum/go-ethereum/common"
  8	"github.com/hermeznetwork/hermez-node/common"
  9	"github.com/hermeznetwork/hermez-node/db/statedb"
 10	"github.com/iden3/go-merkletree/db"
 11)
 12
 13// KEYIDX is used as key in the db to store the current Idx
 14var KEYIDX = []byte("idx")
 15
 16// ConfigCircuit contains the circuit configuration
 17type ConfigCircuit struct {
 18	TxsMax       uint64
 19	L1TxsMax     uint64
 20	SMTLevelsMax uint64
 21}
 22
 23// BatchBuilder implements the batch builder type, which contains the
 24// functionalities
 25type BatchBuilder struct {
 26	// idx holds the current Idx that the BatchBuilder is using
 27	idx            uint64
 28	localStateDB   *statedb.LocalStateDB
 29	configCircuits []ConfigCircuit
 30}
 31
 32// ConfigBatch contains the batch configuration
 33type ConfigBatch struct {
 34	ForgerAddress ethCommon.Address
 35}
 36
 37// NewBatchBuilder constructs a new BatchBuilder, and executes the bb.Reset
 38// method
 39func NewBatchBuilder(dbpath string, synchronizerStateDB *statedb.StateDB, configCircuits []ConfigCircuit, batchNum uint64, nLevels uint64) (*BatchBuilder, error) {
 40	localStateDB, err := statedb.NewLocalStateDB(dbpath, synchronizerStateDB, true, int(nLevels))
 41	if err != nil {
 42		return nil, err
 43	}
 44
 45	bb := BatchBuilder{
 46		localStateDB:   localStateDB,
 47		configCircuits: configCircuits,
 48	}
 49
 50	err = bb.Reset(batchNum, true)
 51	return &bb, err
 52}
 53
 54// Reset tells the BatchBuilder to reset it's internal state to the required
 55// `batchNum`.  If `fromSynchronizer` is true, the BatchBuilder must take a
 56// copy of the rollup state from the Synchronizer at that `batchNum`, otherwise
 57// it can just roll back the internal copy.
 58func (bb *BatchBuilder) Reset(batchNum uint64, fromSynchronizer bool) error {
 59	if batchNum == 0 {
 60		bb.idx = 0
 61		return nil
 62	}
 63	err := bb.localStateDB.Reset(batchNum, fromSynchronizer)
 64	if err != nil {
 65		return err
 66	}
 67	// idx is obtained from the statedb reset
 68	bb.idx, err = bb.getIdx()
 69	return err
 70}
 71
 72// BuildBatch takes the transactions and returns the common.ZKInputs of the next batch
 73func (bb *BatchBuilder) BuildBatch(configBatch ConfigBatch, l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) {
 74	for _, tx := range l1usertxs {
 75		err := bb.processL1Tx(tx)
 76		if err != nil {
 77			return nil, err
 78		}
 79	}
 80	for _, tx := range l1coordinatortxs {
 81		err := bb.processL1Tx(tx)
 82		if err != nil {
 83			return nil, err
 84		}
 85	}
 86	for _, tx := range l2txs {
 87		switch tx.Type {
 88		case common.TxTypeTransfer:
 89			// go to the MT account of sender and receiver, and update
 90			// balance & nonce
 91			err := bb.applyTransfer(tx.Tx)
 92			if err != nil {
 93				return nil, err
 94			}
 95		case common.TxTypeExit:
 96			// execute exit flow
 97		default:
 98		}
 99	}
100
101	return nil, nil
102}
103
104func (bb *BatchBuilder) processL1Tx(tx common.L1Tx) 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 := bb.applyTransfer(tx.Tx)
110		if err != nil {
111			return err
112		}
113	case common.TxTypeCreateAccountDeposit:
114		// add new account to the MT, update balance of the MT account
115		err := bb.applyCreateAccount(tx)
116		if err != nil {
117			return err
118		}
119	case common.TxTypeDeposit: // TODO check if this type will ever exist, or will be TxTypeDepositAndTransfer with transfer 0 value
120		// update balance of the MT account
121		err := bb.applyDeposit(tx, false)
122		if err != nil {
123			return err
124		}
125	case common.TxTypeDepositAndTransfer:
126		// update balance in MT account, update balance & nonce of sender
127		// & receiver
128		err := bb.applyDeposit(tx, true)
129		if err != nil {
130			return err
131		}
132	case common.TxTypeCreateAccountDepositAndTransfer:
133		// add new account to the merkletree, update balance in MT account,
134		// update balance & nonce of sender & receiver
135		err := bb.applyCreateAccount(tx)
136		if err != nil {
137			return err
138		}
139		err = bb.applyTransfer(tx.Tx)
140		if err != nil {
141			return err
142		}
143	case common.TxTypeExit:
144		// execute exit flow
145	default:
146	}
147
148	return nil
149}
150
151// applyCreateAccount creates a new account in the account of the depositer, it
152// stores the deposit value
153func (bb *BatchBuilder) applyCreateAccount(tx common.L1Tx) error {
154	account := &common.Account{
155		TokenID:   tx.TokenID,
156		Nonce:     0,
157		Balance:   tx.LoadAmount,
158		PublicKey: &tx.FromBJJ,
159		EthAddr:   tx.FromEthAddr,
160	}
161
162	err := bb.localStateDB.CreateAccount(common.Idx(bb.idx+1), account)
163	if err != nil {
164		return err
165	}
166
167	bb.idx = bb.idx + 1
168	return bb.setIdx(bb.idx)
169}
170
171// applyDeposit updates the balance in the account of the depositer, if
172// andTransfer parameter is set to true, the method will also apply the
173// Transfer of the L1Tx/DepositAndTransfer
174func (bb *BatchBuilder) applyDeposit(tx common.L1Tx, transfer bool) error {
175	// deposit the tx.LoadAmount into the sender account
176	accSender, err := bb.localStateDB.GetAccount(tx.FromIdx)
177	if err != nil {
178		return err
179	}
180	accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount)
181
182	// in case that the tx is a L1Tx>DepositAndTransfer
183	if transfer {
184		accReceiver, err := bb.localStateDB.GetAccount(tx.ToIdx)
185		if err != nil {
186			return err
187		}
188		// substract amount to the sender
189		accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Tx.Amount)
190		// add amount to the receiver
191		accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Tx.Amount)
192		// update receiver account in localStateDB
193		err = bb.localStateDB.UpdateAccount(tx.ToIdx, accReceiver)
194		if err != nil {
195			return err
196		}
197	}
198	// update sender account in localStateDB
199	err = bb.localStateDB.UpdateAccount(tx.FromIdx, accSender)
200	if err != nil {
201		return err
202	}
203	return nil
204}
205
206// applyTransfer updates the balance & nonce in the account of the sender, and
207// the balance in the account of the receiver
208func (bb *BatchBuilder) applyTransfer(tx common.Tx) error {
209	// get sender and receiver accounts from localStateDB
210	accSender, err := bb.localStateDB.GetAccount(tx.FromIdx)
211	if err != nil {
212		return err
213	}
214	accReceiver, err := bb.localStateDB.GetAccount(tx.ToIdx)
215	if err != nil {
216		return err
217	}
218
219	// substract amount to the sender
220	accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount)
221	// add amount to the receiver
222	accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount)
223
224	// update receiver account in localStateDB
225	err = bb.localStateDB.UpdateAccount(tx.ToIdx, accReceiver)
226	if err != nil {
227		return err
228	}
229	// update sender account in localStateDB
230	err = bb.localStateDB.UpdateAccount(tx.FromIdx, accSender)
231	if err != nil {
232		return err
233	}
234
235	return nil
236}
237
238// getIdx returns the stored Idx from the localStateDB, which is the last Idx used for an Account in the localStateDB.
239func (bb *BatchBuilder) getIdx() (uint64, error) {
240	idxBytes, err := bb.localStateDB.DB().Get(KEYIDX)
241	if err == db.ErrNotFound {
242		return 0, nil
243	}
244	if err != nil {
245		return 0, err
246	}
247	idx := binary.LittleEndian.Uint64(idxBytes[:8])
248	return idx, nil
249}
250
251// setIdx stores Idx in the localStateDB
252func (bb *BatchBuilder) setIdx(idx uint64) error {
253	tx, err := bb.localStateDB.DB().NewTx()
254	if err != nil {
255		return err
256	}
257	var idxBytes [8]byte
258	binary.LittleEndian.PutUint64(idxBytes[:], idx)
259	tx.Put(KEYIDX, idxBytes[:])
260	if err := tx.Commit(); err != nil {
261		return err
262	}
263	return nil
264}