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.Amount)
190 // add amount to the receiver
191 accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, 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}