You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

541 lines
16 KiB

Fix eth events query and sync inconsistent state - kvdb - Fix path in Last when doing `setNew` - Only close if db != nil, and after closing, always set db to nil - This will avoid a panic in the case where the db is closed but there's an error soon after, and a future call tries to close again. This is because pebble.Close() will panic if the db is already closed. - Avoid calling pebble methods when a the Storage interface already implements that method (like Close). - statedb - In test, avoid calling KVDB method if the same method is available for the StateDB (like MakeCheckpoint, CurrentBatch). - eth - In *EventByBlock methods, take blockHash as input argument and use it when querying the event logs. Previously the blockHash was only taken from the logs results *only if* there was any log. This caused the following issue: if there was no logs, it was not possible to know if the result was from the expected block or an uncle block! By querying logs by blockHash we make sure that even if there are no logs, they are from the right block. - Note that now the function can either be called with a blockNum or blockHash, but not both at the same time. - sync - If there's an error during call to Sync call resetState, which internally resets the stateDB to avoid stale checkpoints (and a corresponding invalid increase in the StateDB batchNum). - During a Sync, after very batch processed, make sure that the StateDB currentBatch corresponds to the batchNum in the smart contract log/event.
3 years ago
Update coordinator to work better under real net - cli / node - Update handler of SIGINT so that after 3 SIGINTs, the process terminates unconditionally - coordinator - Store stats without pointer - In all functions that send a variable via channel, check for context done to avoid deadlock (due to no process reading from the channel, which has no queue) when the node is stopped. - Abstract `canForge` so that it can be used outside of the `Coordinator` - In `canForge` check the blockNumber in current and next slot. - Update tests due to smart contract changes in slot handling, and minimum bid defaults - TxManager - Add consts, vars and stats to allow evaluating `canForge` - Add `canForge` method (not used yet) - Store batch and nonces status (last success and last pending) - Track nonces internally instead of relying on the ethereum node (this is required to work with ganache when there are pending txs) - Handle the (common) case of the receipt not being found after the tx is sent. - Don't start the main loop until we get an initial messae fo the stats and vars (so that in the loop the stats and vars are set to synchronizer values) - When a tx fails, check and discard all the failed transactions before sending the message to stop the pipeline. This will avoid sending consecutive messages of stop the pipeline when multiple txs are detected to be failed consecutively. Also, future txs of the same pipeline after a discarded txs are discarded, and their nonces reused. - Robust handling of nonces: - If geth returns nonce is too low, increase it - If geth returns nonce too hight, decrease it - If geth returns underpriced, increase gas price - If geth returns replace underpriced, increase gas price - Add support for resending transactions after a timeout - Store `BatchInfos` in a queue - Pipeline - When an error is found, stop forging batches and send a message to the coordinator to stop the pipeline with information of the failed batch number so that in a restart, non-failed batches are not repated. - When doing a reset of the stateDB, if possible reset from the local checkpoint instead of resetting from the synchronizer. This allows resetting from a batch that is valid but not yet sent / synced. - Every time a pipeline is started, assign it a number from a counter. This allows the TxManager to ignore batches from stopped pipelines, via a message sent by the coordinator. - Avoid forging when we haven't reached the rollup genesis block number. - Add config parameter `StartSlotBlocksDelay`: StartSlotBlocksDelay is the number of blocks of delay to wait before starting the pipeline when we reach a slot in which we can forge. - When detecting a reorg, only reset the pipeline if the batch from which the pipeline started changed and wasn't sent by us. - Add config parameter `ScheduleBatchBlocksAheadCheck`: ScheduleBatchBlocksAheadCheck is the number of blocks ahead in which the forger address is checked to be allowed to forge (apart from checking the next block), used to decide when to stop scheduling new batches (by stopping the pipeline). For example, if we are at block 10 and ScheduleBatchBlocksAheadCheck is 5, eventhough at block 11 we canForge, the pipeline will be stopped if we can't forge at block 15. This value should be the expected number of blocks it takes between scheduling a batch and having it mined. - Add config parameter `SendBatchBlocksMarginCheck`: SendBatchBlocksMarginCheck is the number of margin blocks ahead in which the coordinator is also checked to be allowed to forge, apart from the next block; used to decide when to stop sending batches to the smart contract. For example, if we are at block 10 and SendBatchBlocksMarginCheck is 5, eventhough at block 11 we canForge, the batch will be discarded if we can't forge at block 15. - Add config parameter `TxResendTimeout`: TxResendTimeout is the timeout after which a non-mined ethereum transaction will be resent (reusing the nonce) with a newly calculated gas price - Add config parameter `MaxGasPrice`: MaxGasPrice is the maximum gas price allowed for ethereum transactions - Add config parameter `NoReuseNonce`: NoReuseNonce disables reusing nonces of pending transactions for new replacement transactions. This is useful for testing with Ganache. - Extend BatchInfo with more useful information for debugging - eth / ethereum client - Add necessary methods to create the auth object for transactions manually so that we can set the nonce, gas price, gas limit, etc manually - Update `RollupForgeBatch` to take an auth object as input (so that the coordinator can set parameters manually) - synchronizer - In stats, add `NextSlot` - In stats, store full last batch instead of just last batch number - Instead of calculating a nextSlot from scratch every time, update the current struct (only updating the forger info if we are Synced) - Afer every processed batch, check that the calculated StateDB MTRoot matches the StateRoot found in the forgeBatch event.
3 years ago
Update coordinator to work better under real net - cli / node - Update handler of SIGINT so that after 3 SIGINTs, the process terminates unconditionally - coordinator - Store stats without pointer - In all functions that send a variable via channel, check for context done to avoid deadlock (due to no process reading from the channel, which has no queue) when the node is stopped. - Abstract `canForge` so that it can be used outside of the `Coordinator` - In `canForge` check the blockNumber in current and next slot. - Update tests due to smart contract changes in slot handling, and minimum bid defaults - TxManager - Add consts, vars and stats to allow evaluating `canForge` - Add `canForge` method (not used yet) - Store batch and nonces status (last success and last pending) - Track nonces internally instead of relying on the ethereum node (this is required to work with ganache when there are pending txs) - Handle the (common) case of the receipt not being found after the tx is sent. - Don't start the main loop until we get an initial messae fo the stats and vars (so that in the loop the stats and vars are set to synchronizer values) - When a tx fails, check and discard all the failed transactions before sending the message to stop the pipeline. This will avoid sending consecutive messages of stop the pipeline when multiple txs are detected to be failed consecutively. Also, future txs of the same pipeline after a discarded txs are discarded, and their nonces reused. - Robust handling of nonces: - If geth returns nonce is too low, increase it - If geth returns nonce too hight, decrease it - If geth returns underpriced, increase gas price - If geth returns replace underpriced, increase gas price - Add support for resending transactions after a timeout - Store `BatchInfos` in a queue - Pipeline - When an error is found, stop forging batches and send a message to the coordinator to stop the pipeline with information of the failed batch number so that in a restart, non-failed batches are not repated. - When doing a reset of the stateDB, if possible reset from the local checkpoint instead of resetting from the synchronizer. This allows resetting from a batch that is valid but not yet sent / synced. - Every time a pipeline is started, assign it a number from a counter. This allows the TxManager to ignore batches from stopped pipelines, via a message sent by the coordinator. - Avoid forging when we haven't reached the rollup genesis block number. - Add config parameter `StartSlotBlocksDelay`: StartSlotBlocksDelay is the number of blocks of delay to wait before starting the pipeline when we reach a slot in which we can forge. - When detecting a reorg, only reset the pipeline if the batch from which the pipeline started changed and wasn't sent by us. - Add config parameter `ScheduleBatchBlocksAheadCheck`: ScheduleBatchBlocksAheadCheck is the number of blocks ahead in which the forger address is checked to be allowed to forge (apart from checking the next block), used to decide when to stop scheduling new batches (by stopping the pipeline). For example, if we are at block 10 and ScheduleBatchBlocksAheadCheck is 5, eventhough at block 11 we canForge, the pipeline will be stopped if we can't forge at block 15. This value should be the expected number of blocks it takes between scheduling a batch and having it mined. - Add config parameter `SendBatchBlocksMarginCheck`: SendBatchBlocksMarginCheck is the number of margin blocks ahead in which the coordinator is also checked to be allowed to forge, apart from the next block; used to decide when to stop sending batches to the smart contract. For example, if we are at block 10 and SendBatchBlocksMarginCheck is 5, eventhough at block 11 we canForge, the batch will be discarded if we can't forge at block 15. - Add config parameter `TxResendTimeout`: TxResendTimeout is the timeout after which a non-mined ethereum transaction will be resent (reusing the nonce) with a newly calculated gas price - Add config parameter `MaxGasPrice`: MaxGasPrice is the maximum gas price allowed for ethereum transactions - Add config parameter `NoReuseNonce`: NoReuseNonce disables reusing nonces of pending transactions for new replacement transactions. This is useful for testing with Ganache. - Extend BatchInfo with more useful information for debugging - eth / ethereum client - Add necessary methods to create the auth object for transactions manually so that we can set the nonce, gas price, gas limit, etc manually - Update `RollupForgeBatch` to take an auth object as input (so that the coordinator can set parameters manually) - synchronizer - In stats, add `NextSlot` - In stats, store full last batch instead of just last batch number - Instead of calculating a nextSlot from scratch every time, update the current struct (only updating the forger info if we are Synced) - Afer every processed batch, check that the calculated StateDB MTRoot matches the StateRoot found in the forgeBatch event.
3 years ago
Fix eth events query and sync inconsistent state - kvdb - Fix path in Last when doing `setNew` - Only close if db != nil, and after closing, always set db to nil - This will avoid a panic in the case where the db is closed but there's an error soon after, and a future call tries to close again. This is because pebble.Close() will panic if the db is already closed. - Avoid calling pebble methods when a the Storage interface already implements that method (like Close). - statedb - In test, avoid calling KVDB method if the same method is available for the StateDB (like MakeCheckpoint, CurrentBatch). - eth - In *EventByBlock methods, take blockHash as input argument and use it when querying the event logs. Previously the blockHash was only taken from the logs results *only if* there was any log. This caused the following issue: if there was no logs, it was not possible to know if the result was from the expected block or an uncle block! By querying logs by blockHash we make sure that even if there are no logs, they are from the right block. - Note that now the function can either be called with a blockNum or blockHash, but not both at the same time. - sync - If there's an error during call to Sync call resetState, which internally resets the stateDB to avoid stale checkpoints (and a corresponding invalid increase in the StateDB batchNum). - During a Sync, after very batch processed, make sure that the StateDB currentBatch corresponds to the batchNum in the smart contract log/event.
3 years ago
Fix eth events query and sync inconsistent state - kvdb - Fix path in Last when doing `setNew` - Only close if db != nil, and after closing, always set db to nil - This will avoid a panic in the case where the db is closed but there's an error soon after, and a future call tries to close again. This is because pebble.Close() will panic if the db is already closed. - Avoid calling pebble methods when a the Storage interface already implements that method (like Close). - statedb - In test, avoid calling KVDB method if the same method is available for the StateDB (like MakeCheckpoint, CurrentBatch). - eth - In *EventByBlock methods, take blockHash as input argument and use it when querying the event logs. Previously the blockHash was only taken from the logs results *only if* there was any log. This caused the following issue: if there was no logs, it was not possible to know if the result was from the expected block or an uncle block! By querying logs by blockHash we make sure that even if there are no logs, they are from the right block. - Note that now the function can either be called with a blockNum or blockHash, but not both at the same time. - sync - If there's an error during call to Sync call resetState, which internally resets the stateDB to avoid stale checkpoints (and a corresponding invalid increase in the StateDB batchNum). - During a Sync, after very batch processed, make sure that the StateDB currentBatch corresponds to the batchNum in the smart contract log/event.
3 years ago
  1. package statedb
  2. import (
  3. "errors"
  4. "fmt"
  5. "math/big"
  6. "github.com/hermeznetwork/hermez-node/common"
  7. "github.com/hermeznetwork/hermez-node/db/kvdb"
  8. "github.com/hermeznetwork/hermez-node/log"
  9. "github.com/hermeznetwork/tracerr"
  10. "github.com/iden3/go-merkletree"
  11. "github.com/iden3/go-merkletree/db"
  12. "github.com/iden3/go-merkletree/db/pebble"
  13. )
  14. var (
  15. // ErrStateDBWithoutMT is used when a method that requires a MerkleTree
  16. // is called in a StateDB that does not have a MerkleTree defined
  17. ErrStateDBWithoutMT = errors.New(
  18. "Can not call method to use MerkleTree in a StateDB without MerkleTree")
  19. // ErrAccountAlreadyExists is used when CreateAccount is called and the
  20. // Account already exists
  21. ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
  22. // ErrIdxNotFound is used when trying to get the Idx from EthAddr or
  23. // EthAddr&ToBJJ
  24. ErrIdxNotFound = errors.New("Idx can not be found")
  25. // ErrGetIdxNoCase is used when trying to get the Idx from EthAddr &
  26. // BJJ with not compatible combination
  27. ErrGetIdxNoCase = errors.New(
  28. "Can not get Idx due unexpected combination of ethereum Address & BabyJubJub PublicKey")
  29. // PrefixKeyIdx is the key prefix for idx in the db
  30. PrefixKeyIdx = []byte("i:")
  31. // PrefixKeyAccHash is the key prefix for account hash in the db
  32. PrefixKeyAccHash = []byte("h:")
  33. // PrefixKeyMT is the key prefix for merkle tree in the db
  34. PrefixKeyMT = []byte("m:")
  35. // PrefixKeyAddr is the key prefix for address in the db
  36. PrefixKeyAddr = []byte("a:")
  37. // PrefixKeyAddrBJJ is the key prefix for address-babyjubjub in the db
  38. PrefixKeyAddrBJJ = []byte("ab:")
  39. )
  40. const (
  41. // TypeSynchronizer defines a StateDB used by the Synchronizer, that
  42. // generates the ExitTree when processing the txs
  43. TypeSynchronizer = "synchronizer"
  44. // TypeTxSelector defines a StateDB used by the TxSelector, without
  45. // computing ExitTree neither the ZKInputs
  46. TypeTxSelector = "txselector"
  47. // TypeBatchBuilder defines a StateDB used by the BatchBuilder, that
  48. // generates the ExitTree and the ZKInput when processing the txs
  49. TypeBatchBuilder = "batchbuilder"
  50. // MaxNLevels is the maximum value of NLevels for the merkle tree,
  51. // which comes from the fact that AccountIdx has 48 bits.
  52. MaxNLevels = 48
  53. )
  54. // TypeStateDB determines the type of StateDB
  55. type TypeStateDB string
  56. // Config of the StateDB
  57. type Config struct {
  58. // Path where the checkpoints will be stored
  59. Path string
  60. // Keep is the number of old checkpoints to keep. If 0, all
  61. // checkpoints are kept.
  62. Keep int
  63. // NoLast skips having an opened DB with a checkpoint to the last
  64. // batchNum for thread-safe reads.
  65. NoLast bool
  66. // Type of StateDB (
  67. Type TypeStateDB
  68. // NLevels is the number of merkle tree levels in case the Type uses a
  69. // merkle tree. If the Type doesn't use a merkle tree, NLevels should
  70. // be 0.
  71. NLevels int
  72. // At every checkpoint, check that there are no gaps between the
  73. // checkpoints
  74. noGapsCheck bool
  75. }
  76. // StateDB represents the StateDB object
  77. type StateDB struct {
  78. cfg Config
  79. db *kvdb.KVDB
  80. MT *merkletree.MerkleTree
  81. }
  82. // Last offers a subset of view methods of the StateDB that can be
  83. // called via the LastRead method of StateDB in a thread-safe manner to obtain
  84. // a consistent view to the last batch of the StateDB.
  85. type Last struct {
  86. db db.Storage
  87. }
  88. // GetAccount returns the account for the given Idx
  89. func (s *Last) GetAccount(idx common.Idx) (*common.Account, error) {
  90. return GetAccountInTreeDB(s.db, idx)
  91. }
  92. // GetCurrentBatch returns the current BatchNum stored in Last.db
  93. func (s *Last) GetCurrentBatch() (common.BatchNum, error) {
  94. cbBytes, err := s.db.Get(kvdb.KeyCurrentBatch)
  95. if tracerr.Unwrap(err) == db.ErrNotFound {
  96. return 0, nil
  97. } else if err != nil {
  98. return 0, tracerr.Wrap(err)
  99. }
  100. return common.BatchNumFromBytes(cbBytes)
  101. }
  102. // DB returns the underlying storage of Last
  103. func (s *Last) DB() db.Storage {
  104. return s.db
  105. }
  106. // GetAccounts returns all the accounts in the db. Use for debugging pruposes
  107. // only.
  108. func (s *Last) GetAccounts() ([]common.Account, error) {
  109. return getAccounts(s.db)
  110. }
  111. // NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
  112. // storage. Checkpoints older than the value defined by `keep` will be
  113. // deleted.
  114. // func NewStateDB(pathDB string, keep int, typ TypeStateDB, nLevels int) (*StateDB, error) {
  115. func NewStateDB(cfg Config) (*StateDB, error) {
  116. var kv *kvdb.KVDB
  117. var err error
  118. kv, err = kvdb.NewKVDB(kvdb.Config{Path: cfg.Path, Keep: cfg.Keep,
  119. NoGapsCheck: cfg.noGapsCheck, NoLast: cfg.NoLast})
  120. if err != nil {
  121. return nil, tracerr.Wrap(err)
  122. }
  123. var mt *merkletree.MerkleTree = nil
  124. if cfg.Type == TypeSynchronizer || cfg.Type == TypeBatchBuilder {
  125. mt, err = merkletree.NewMerkleTree(kv.StorageWithPrefix(PrefixKeyMT), cfg.NLevels)
  126. if err != nil {
  127. return nil, tracerr.Wrap(err)
  128. }
  129. }
  130. if cfg.Type == TypeTxSelector && cfg.NLevels != 0 {
  131. return nil, tracerr.Wrap(
  132. fmt.Errorf("invalid StateDB parameters: StateDB type==TypeStateDB can not have nLevels!=0"))
  133. }
  134. return &StateDB{
  135. cfg: cfg,
  136. db: kv,
  137. MT: mt,
  138. }, nil
  139. }
  140. // Type returns the StateDB configured Type
  141. func (s *StateDB) Type() TypeStateDB {
  142. return s.cfg.Type
  143. }
  144. // LastRead is a thread-safe method to query the last checkpoint of the StateDB
  145. // via the Last type methods
  146. func (s *StateDB) LastRead(fn func(sdbLast *Last) error) error {
  147. return s.db.LastRead(
  148. func(db *pebble.Storage) error {
  149. return fn(&Last{
  150. db: db,
  151. })
  152. },
  153. )
  154. }
  155. // LastGetAccount is a thread-safe method to query an account in the last
  156. // checkpoint of the StateDB.
  157. func (s *StateDB) LastGetAccount(idx common.Idx) (*common.Account, error) {
  158. var account *common.Account
  159. if err := s.LastRead(func(sdb *Last) error {
  160. var err error
  161. account, err = sdb.GetAccount(idx)
  162. return err
  163. }); err != nil {
  164. return nil, tracerr.Wrap(err)
  165. }
  166. return account, nil
  167. }
  168. // LastGetCurrentBatch is a thread-safe method to get the current BatchNum in
  169. // the last checkpoint of the StateDB.
  170. func (s *StateDB) LastGetCurrentBatch() (common.BatchNum, error) {
  171. var batchNum common.BatchNum
  172. if err := s.LastRead(func(sdb *Last) error {
  173. var err error
  174. batchNum, err = sdb.GetCurrentBatch()
  175. return err
  176. }); err != nil {
  177. return 0, tracerr.Wrap(err)
  178. }
  179. return batchNum, nil
  180. }
  181. // LastMTGetRoot returns the root of the underlying Merkle Tree in the last
  182. // checkpoint of the StateDB.
  183. func (s *StateDB) LastMTGetRoot() (*big.Int, error) {
  184. var root *big.Int
  185. if err := s.LastRead(func(sdb *Last) error {
  186. mt, err := merkletree.NewMerkleTree(sdb.DB().WithPrefix(PrefixKeyMT), s.cfg.NLevels)
  187. if err != nil {
  188. return tracerr.Wrap(err)
  189. }
  190. root = mt.Root().BigInt()
  191. return nil
  192. }); err != nil {
  193. return nil, tracerr.Wrap(err)
  194. }
  195. return root, nil
  196. }
  197. // MakeCheckpoint does a checkpoint at the given batchNum in the defined path.
  198. // Internally this advances & stores the current BatchNum, and then stores a
  199. // Checkpoint of the current state of the StateDB.
  200. func (s *StateDB) MakeCheckpoint() error {
  201. log.Debugw("Making StateDB checkpoint", "batch", s.CurrentBatch()+1, "type", s.cfg.Type)
  202. return s.db.MakeCheckpoint()
  203. }
  204. // DeleteOldCheckpoints deletes old checkpoints when there are more than
  205. // `cfg.keep` checkpoints
  206. func (s *StateDB) DeleteOldCheckpoints() error {
  207. return s.db.DeleteOldCheckpoints()
  208. }
  209. // CurrentBatch returns the current in-memory CurrentBatch of the StateDB.db
  210. func (s *StateDB) CurrentBatch() common.BatchNum {
  211. return s.db.CurrentBatch
  212. }
  213. // CurrentIdx returns the current in-memory CurrentIdx of the StateDB.db
  214. func (s *StateDB) CurrentIdx() common.Idx {
  215. return s.db.CurrentIdx
  216. }
  217. // getCurrentBatch returns the current BatchNum stored in the StateDB.db
  218. func (s *StateDB) getCurrentBatch() (common.BatchNum, error) {
  219. return s.db.GetCurrentBatch()
  220. }
  221. // GetCurrentIdx returns the stored Idx from the localStateDB, which is the
  222. // last Idx used for an Account in the localStateDB.
  223. func (s *StateDB) GetCurrentIdx() (common.Idx, error) {
  224. return s.db.GetCurrentIdx()
  225. }
  226. // SetCurrentIdx stores Idx in the StateDB
  227. func (s *StateDB) SetCurrentIdx(idx common.Idx) error {
  228. return s.db.SetCurrentIdx(idx)
  229. }
  230. // Reset resets the StateDB to the checkpoint at the given batchNum. Reset
  231. // does not delete the checkpoints between old current and the new current,
  232. // those checkpoints will remain in the storage, and eventually will be
  233. // deleted when MakeCheckpoint overwrites them.
  234. func (s *StateDB) Reset(batchNum common.BatchNum) error {
  235. log.Debugw("Making StateDB Reset", "batch", batchNum, "type", s.cfg.Type)
  236. if err := s.db.Reset(batchNum); err != nil {
  237. return tracerr.Wrap(err)
  238. }
  239. if s.MT != nil {
  240. // open the MT for the current s.db
  241. mt, err := merkletree.NewMerkleTree(s.db.StorageWithPrefix(PrefixKeyMT), s.MT.MaxLevels())
  242. if err != nil {
  243. return tracerr.Wrap(err)
  244. }
  245. s.MT = mt
  246. }
  247. return nil
  248. }
  249. // GetAccount returns the account for the given Idx
  250. func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
  251. return GetAccountInTreeDB(s.db.DB(), idx)
  252. }
  253. func accountsIter(db db.Storage, fn func(a *common.Account) (bool, error)) error {
  254. idxDB := db.WithPrefix(PrefixKeyIdx)
  255. if err := idxDB.Iterate(func(k []byte, v []byte) (bool, error) {
  256. idx, err := common.IdxFromBytes(k)
  257. if err != nil {
  258. return false, tracerr.Wrap(err)
  259. }
  260. acc, err := GetAccountInTreeDB(db, idx)
  261. if err != nil {
  262. return false, tracerr.Wrap(err)
  263. }
  264. ok, err := fn(acc)
  265. if err != nil {
  266. return false, tracerr.Wrap(err)
  267. }
  268. return ok, nil
  269. }); err != nil {
  270. return tracerr.Wrap(err)
  271. }
  272. return nil
  273. }
  274. func getAccounts(db db.Storage) ([]common.Account, error) {
  275. accs := []common.Account{}
  276. if err := accountsIter(
  277. db,
  278. func(a *common.Account) (bool, error) {
  279. accs = append(accs, *a)
  280. return true, nil
  281. },
  282. ); err != nil {
  283. return nil, tracerr.Wrap(err)
  284. }
  285. return accs, nil
  286. }
  287. // TestGetAccounts returns all the accounts in the db. Use only in tests.
  288. // Outside tests getting all the accounts is discouraged because it's an
  289. // expensive operation, but if you must do it, use `LastRead()` method to get a
  290. // thread-safe and consistent view of the stateDB.
  291. func (s *StateDB) TestGetAccounts() ([]common.Account, error) {
  292. return getAccounts(s.db.DB())
  293. }
  294. // GetAccountInTreeDB is abstracted from StateDB to be used from StateDB and
  295. // from ExitTree. GetAccount returns the account for the given Idx
  296. func GetAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
  297. idxBytes, err := idx.Bytes()
  298. if err != nil {
  299. return nil, tracerr.Wrap(err)
  300. }
  301. vBytes, err := sto.Get(append(PrefixKeyIdx, idxBytes[:]...))
  302. if err != nil {
  303. return nil, tracerr.Wrap(err)
  304. }
  305. accBytes, err := sto.Get(append(PrefixKeyAccHash, vBytes...))
  306. if err != nil {
  307. return nil, tracerr.Wrap(err)
  308. }
  309. var b [32 * common.NLeafElems]byte
  310. copy(b[:], accBytes)
  311. account, err := common.AccountFromBytes(b)
  312. if err != nil {
  313. return nil, tracerr.Wrap(err)
  314. }
  315. account.Idx = idx
  316. return account, nil
  317. }
  318. // CreateAccount creates a new Account in the StateDB for the given Idx. If
  319. // StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
  320. // MerkleTree, returning a CircomProcessorProof.
  321. func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (
  322. *merkletree.CircomProcessorProof, error) {
  323. cpp, err := CreateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
  324. if err != nil {
  325. return cpp, tracerr.Wrap(err)
  326. }
  327. // store idx by EthAddr & BJJ
  328. err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.BJJ, account.TokenID)
  329. return cpp, tracerr.Wrap(err)
  330. }
  331. // CreateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
  332. // from ExitTree. Creates a new Account in the StateDB for the given Idx. If
  333. // StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
  334. // MerkleTree, returning a CircomProcessorProof.
  335. func CreateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx,
  336. account *common.Account) (*merkletree.CircomProcessorProof, error) {
  337. // store at the DB the key: v, and value: leaf.Bytes()
  338. v, err := account.HashValue()
  339. if err != nil {
  340. return nil, tracerr.Wrap(err)
  341. }
  342. accountBytes, err := account.Bytes()
  343. if err != nil {
  344. return nil, tracerr.Wrap(err)
  345. }
  346. // store the Leaf value
  347. tx, err := sto.NewTx()
  348. if err != nil {
  349. return nil, tracerr.Wrap(err)
  350. }
  351. idxBytes, err := idx.Bytes()
  352. if err != nil {
  353. return nil, tracerr.Wrap(err)
  354. }
  355. _, err = tx.Get(append(PrefixKeyIdx, idxBytes[:]...))
  356. if tracerr.Unwrap(err) != db.ErrNotFound {
  357. return nil, tracerr.Wrap(ErrAccountAlreadyExists)
  358. }
  359. err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
  360. if err != nil {
  361. return nil, tracerr.Wrap(err)
  362. }
  363. err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
  364. if err != nil {
  365. return nil, tracerr.Wrap(err)
  366. }
  367. if err := tx.Commit(); err != nil {
  368. return nil, tracerr.Wrap(err)
  369. }
  370. if mt != nil {
  371. return mt.AddAndGetCircomProof(idx.BigInt(), v)
  372. }
  373. return nil, nil
  374. }
  375. // UpdateAccount updates the Account in the StateDB for the given Idx. If
  376. // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
  377. // MerkleTree, returning a CircomProcessorProof.
  378. func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (
  379. *merkletree.CircomProcessorProof, error) {
  380. return UpdateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
  381. }
  382. // UpdateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
  383. // from ExitTree. Updates the Account in the StateDB for the given Idx. If
  384. // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
  385. // MerkleTree, returning a CircomProcessorProof.
  386. func UpdateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx,
  387. account *common.Account) (*merkletree.CircomProcessorProof, error) {
  388. // store at the DB the key: v, and value: account.Bytes()
  389. v, err := account.HashValue()
  390. if err != nil {
  391. return nil, tracerr.Wrap(err)
  392. }
  393. accountBytes, err := account.Bytes()
  394. if err != nil {
  395. return nil, tracerr.Wrap(err)
  396. }
  397. tx, err := sto.NewTx()
  398. if err != nil {
  399. return nil, tracerr.Wrap(err)
  400. }
  401. err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
  402. if err != nil {
  403. return nil, tracerr.Wrap(err)
  404. }
  405. idxBytes, err := idx.Bytes()
  406. if err != nil {
  407. return nil, tracerr.Wrap(err)
  408. }
  409. err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
  410. if err != nil {
  411. return nil, tracerr.Wrap(err)
  412. }
  413. if err := tx.Commit(); err != nil {
  414. return nil, tracerr.Wrap(err)
  415. }
  416. if mt != nil {
  417. proof, err := mt.Update(idx.BigInt(), v)
  418. return proof, tracerr.Wrap(err)
  419. }
  420. return nil, nil
  421. }
  422. // MTGetProof returns the CircomVerifierProof for a given Idx
  423. func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
  424. if s.MT == nil {
  425. return nil, tracerr.Wrap(ErrStateDBWithoutMT)
  426. }
  427. p, err := s.MT.GenerateSCVerifierProof(idx.BigInt(), s.MT.Root())
  428. if err != nil {
  429. return nil, tracerr.Wrap(err)
  430. }
  431. return p, nil
  432. }
  433. // Close the StateDB
  434. func (s *StateDB) Close() {
  435. s.db.Close()
  436. }
  437. // LocalStateDB represents the local StateDB which allows to make copies from
  438. // the synchronizer StateDB, and is used by the tx-selector and the
  439. // batch-builder. LocalStateDB is an in-memory storage.
  440. type LocalStateDB struct {
  441. *StateDB
  442. synchronizerStateDB *StateDB
  443. }
  444. // NewLocalStateDB returns a new LocalStateDB connected to the given
  445. // synchronizerDB. Checkpoints older than the value defined by `keep` will be
  446. // deleted.
  447. func NewLocalStateDB(cfg Config, synchronizerDB *StateDB) (*LocalStateDB, error) {
  448. cfg.noGapsCheck = true
  449. cfg.NoLast = true
  450. s, err := NewStateDB(cfg)
  451. if err != nil {
  452. return nil, tracerr.Wrap(err)
  453. }
  454. return &LocalStateDB{
  455. s,
  456. synchronizerDB,
  457. }, nil
  458. }
  459. // CheckpointExists returns true if the checkpoint exists
  460. func (l *LocalStateDB) CheckpointExists(batchNum common.BatchNum) (bool, error) {
  461. return l.db.CheckpointExists(batchNum)
  462. }
  463. // Reset performs a reset in the LocalStateDB. If fromSynchronizer is true, it
  464. // gets the state from LocalStateDB.synchronizerStateDB for the given batchNum.
  465. // If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
  466. func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
  467. if fromSynchronizer {
  468. log.Debugw("Making StateDB ResetFromSynchronizer", "batch", batchNum, "type", l.cfg.Type)
  469. if err := l.db.ResetFromSynchronizer(batchNum, l.synchronizerStateDB.db); err != nil {
  470. return tracerr.Wrap(err)
  471. }
  472. // open the MT for the current s.db
  473. if l.MT != nil {
  474. mt, err := merkletree.NewMerkleTree(l.db.StorageWithPrefix(PrefixKeyMT),
  475. l.MT.MaxLevels())
  476. if err != nil {
  477. return tracerr.Wrap(err)
  478. }
  479. l.MT = mt
  480. }
  481. return nil
  482. }
  483. // use checkpoint from LocalStateDB
  484. return l.StateDB.Reset(batchNum)
  485. }