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.

351 lines
9.1 KiB

  1. package statedb
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "os/exec"
  7. "strconv"
  8. "github.com/hermeznetwork/hermez-node/common"
  9. "github.com/iden3/go-merkletree"
  10. "github.com/iden3/go-merkletree/db"
  11. "github.com/iden3/go-merkletree/db/pebble"
  12. )
  13. // ErrStateDBWithoutMT is used when a method that requires a MerkleTree is called in a StateDB that does not have a MerkleTree defined
  14. var ErrStateDBWithoutMT = errors.New("Can not call method to use MerkleTree in a StateDB without MerkleTree")
  15. // ErrAccountAlreadyExists is used when CreateAccount is called and the Account already exists
  16. var ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
  17. // KEYCURRENTBATCH is used as key in the db to store the current BatchNum
  18. var KEYCURRENTBATCH = []byte("currentbatch")
  19. // PATHSTATEDB defines the subpath of the StateDB
  20. const PATHSTATEDB = "/statedb"
  21. const PATHBATCHNUM = "/BatchNum"
  22. const PATHCURRENT = "/current"
  23. // StateDB represents the StateDB object
  24. type StateDB struct {
  25. path string
  26. currentBatch common.BatchNum
  27. db *pebble.PebbleStorage
  28. mt *merkletree.MerkleTree
  29. // idx holds the current Idx that the BatchBuilder is using
  30. idx common.Idx
  31. }
  32. // NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
  33. // storage
  34. func NewStateDB(path string, withMT bool, nLevels int) (*StateDB, error) {
  35. var sto *pebble.PebbleStorage
  36. var err error
  37. sto, err = pebble.NewPebbleStorage(path+PATHSTATEDB+PATHCURRENT, false)
  38. if err != nil {
  39. return nil, err
  40. }
  41. var mt *merkletree.MerkleTree = nil
  42. if withMT {
  43. mt, err = merkletree.NewMerkleTree(sto, nLevels)
  44. if err != nil {
  45. return nil, err
  46. }
  47. }
  48. sdb := &StateDB{
  49. path: path + PATHSTATEDB,
  50. db: sto,
  51. mt: mt,
  52. }
  53. // load currentBatch
  54. sdb.currentBatch, err = sdb.GetCurrentBatch()
  55. if err != nil {
  56. return nil, err
  57. }
  58. return sdb, nil
  59. }
  60. // DB returns the *pebble.PebbleStorage from the StateDB
  61. func (s *StateDB) DB() *pebble.PebbleStorage {
  62. return s.db
  63. }
  64. // GetCurrentBatch returns the current BatchNum stored in the StateDB
  65. func (s *StateDB) GetCurrentBatch() (common.BatchNum, error) {
  66. cbBytes, err := s.db.Get(KEYCURRENTBATCH)
  67. if err == db.ErrNotFound {
  68. return 0, nil
  69. }
  70. if err != nil {
  71. return 0, err
  72. }
  73. return common.BatchNumFromBytes(cbBytes)
  74. }
  75. // setCurrentBatch stores the current BatchNum in the StateDB
  76. func (s *StateDB) setCurrentBatch() error {
  77. tx, err := s.db.NewTx()
  78. if err != nil {
  79. return err
  80. }
  81. tx.Put(KEYCURRENTBATCH, s.currentBatch.Bytes())
  82. if err := tx.Commit(); err != nil {
  83. return err
  84. }
  85. return nil
  86. }
  87. // MakeCheckpoint does a checkpoint at the given batchNum in the defined path. Internally this advances & stores the current BatchNum, and then stores a Checkpoint of the current state of the StateDB.
  88. func (s *StateDB) MakeCheckpoint() error {
  89. // advance currentBatch
  90. s.currentBatch++
  91. checkpointPath := s.path + PATHBATCHNUM + strconv.Itoa(int(s.currentBatch))
  92. err := s.setCurrentBatch()
  93. if err != nil {
  94. return err
  95. }
  96. // if checkpoint BatchNum already exist in disk, delete it
  97. if _, err := os.Stat(checkpointPath); !os.IsNotExist(err) {
  98. err := os.RemoveAll(checkpointPath)
  99. if err != nil {
  100. return err
  101. }
  102. }
  103. // execute Checkpoint
  104. err = s.db.Pebble().Checkpoint(checkpointPath)
  105. if err != nil {
  106. return err
  107. }
  108. return nil
  109. }
  110. // DeleteCheckpoint removes if exist the checkpoint of the given batchNum
  111. func (s *StateDB) DeleteCheckpoint(batchNum uint64) error {
  112. checkpointPath := s.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  113. if _, err := os.Stat(checkpointPath); os.IsNotExist(err) {
  114. return fmt.Errorf("Checkpoint with batchNum %d does not exist in DB", batchNum)
  115. }
  116. return os.RemoveAll(checkpointPath)
  117. }
  118. // Reset resets the StateDB to the checkpoint at the given batchNum. Reset
  119. // does not delete the checkpoints between old current and the new current,
  120. // those checkpoints will remain in the storage, and eventually will be
  121. // deleted when MakeCheckpoint overwrites them.
  122. func (s *StateDB) Reset(batchNum uint64) error {
  123. if batchNum == 0 {
  124. s.idx = 0
  125. return nil
  126. }
  127. checkpointPath := s.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  128. currentPath := s.path + PATHCURRENT
  129. // remove 'current'
  130. err := os.RemoveAll(currentPath)
  131. if err != nil {
  132. return err
  133. }
  134. // copy 'BatchNumX' to 'current'
  135. cmd := exec.Command("cp", "-r", checkpointPath, currentPath)
  136. err = cmd.Run()
  137. if err != nil {
  138. return err
  139. }
  140. // open the new 'current'
  141. sto, err := pebble.NewPebbleStorage(currentPath, false)
  142. if err != nil {
  143. return err
  144. }
  145. s.db = sto
  146. // get currentBatch num
  147. s.currentBatch, err = s.GetCurrentBatch()
  148. if err != nil {
  149. return err
  150. }
  151. // idx is obtained from the statedb reset
  152. s.idx, err = s.getIdx()
  153. return err
  154. }
  155. // GetAccount returns the account for the given Idx
  156. func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
  157. vBytes, err := s.db.Get(idx.Bytes())
  158. if err != nil {
  159. return nil, err
  160. }
  161. accBytes, err := s.db.Get(vBytes)
  162. if err != nil {
  163. return nil, err
  164. }
  165. var b [32 * common.NLEAFELEMS]byte
  166. copy(b[:], accBytes)
  167. return common.AccountFromBytes(b)
  168. }
  169. // CreateAccount creates a new Account in the StateDB for the given Idx. If
  170. // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
  171. // MerkleTree, returning a CircomProcessorProof.
  172. func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
  173. // store at the DB the key: v, and value: leaf.Bytes()
  174. v, err := account.HashValue()
  175. if err != nil {
  176. return nil, err
  177. }
  178. accountBytes, err := account.Bytes()
  179. if err != nil {
  180. return nil, err
  181. }
  182. // store the Leaf value
  183. tx, err := s.db.NewTx()
  184. if err != nil {
  185. return nil, err
  186. }
  187. _, err = tx.Get(idx.Bytes())
  188. if err != db.ErrNotFound {
  189. return nil, ErrAccountAlreadyExists
  190. }
  191. tx.Put(v.Bytes(), accountBytes[:])
  192. tx.Put(idx.Bytes(), v.Bytes())
  193. if err := tx.Commit(); err != nil {
  194. return nil, err
  195. }
  196. if s.mt != nil {
  197. return s.mt.AddAndGetCircomProof(idx.BigInt(), v)
  198. }
  199. return nil, nil
  200. }
  201. // UpdateAccount updates the Account in the StateDB for the given Idx. If
  202. // StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
  203. // MerkleTree, returning a CircomProcessorProof.
  204. func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
  205. // store at the DB the key: v, and value: leaf.Bytes()
  206. v, err := account.HashValue()
  207. if err != nil {
  208. return nil, err
  209. }
  210. accountBytes, err := account.Bytes()
  211. if err != nil {
  212. return nil, err
  213. }
  214. tx, err := s.db.NewTx()
  215. if err != nil {
  216. return nil, err
  217. }
  218. tx.Put(v.Bytes(), accountBytes[:])
  219. tx.Put(idx.Bytes(), v.Bytes())
  220. if err := tx.Commit(); err != nil {
  221. return nil, err
  222. }
  223. if s.mt != nil {
  224. return s.mt.Update(idx.BigInt(), v)
  225. }
  226. return nil, nil
  227. }
  228. // MTGetProof returns the CircomVerifierProof for a given Idx
  229. func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
  230. if s.mt == nil {
  231. return nil, ErrStateDBWithoutMT
  232. }
  233. return s.mt.GenerateCircomVerifierProof(idx.BigInt(), s.mt.Root())
  234. }
  235. // LocalStateDB represents the local StateDB which allows to make copies from
  236. // the synchronizer StateDB, and is used by the tx-selector and the
  237. // batch-builder. LocalStateDB is an in-memory storage.
  238. type LocalStateDB struct {
  239. *StateDB
  240. synchronizerStateDB *StateDB
  241. }
  242. // NewLocalStateDB returns a new LocalStateDB connected to the given
  243. // synchronizerDB
  244. func NewLocalStateDB(path string, synchronizerDB *StateDB, withMT bool, nLevels int) (*LocalStateDB, error) {
  245. s, err := NewStateDB(path, withMT, nLevels)
  246. if err != nil {
  247. return nil, err
  248. }
  249. return &LocalStateDB{
  250. s,
  251. synchronizerDB,
  252. }, nil
  253. }
  254. // Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
  255. // gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
  256. func (l *LocalStateDB) Reset(batchNum uint64, fromSynchronizer bool) error {
  257. if batchNum == 0 {
  258. l.idx = 0
  259. return nil
  260. }
  261. synchronizerCheckpointPath := l.synchronizerStateDB.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  262. checkpointPath := l.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  263. currentPath := l.path + PATHCURRENT
  264. if fromSynchronizer {
  265. // use checkpoint from SynchronizerStateDB
  266. if _, err := os.Stat(synchronizerCheckpointPath); os.IsNotExist(err) {
  267. // if synchronizerStateDB does not have checkpoint at batchNum, return err
  268. return fmt.Errorf("Checkpoint not exist in Synchronizer")
  269. }
  270. // remove 'current'
  271. err := os.RemoveAll(currentPath)
  272. if err != nil {
  273. return err
  274. }
  275. // copy synchronizer'BatchNumX' to 'current'
  276. cmd := exec.Command("cp", "-r", synchronizerCheckpointPath, currentPath)
  277. err = cmd.Run()
  278. if err != nil {
  279. return err
  280. }
  281. // copy synchronizer-'BatchNumX' to 'BatchNumX'
  282. cmd = exec.Command("cp", "-r", synchronizerCheckpointPath, checkpointPath)
  283. err = cmd.Run()
  284. if err != nil {
  285. return err
  286. }
  287. // open the new 'current'
  288. sto, err := pebble.NewPebbleStorage(currentPath, false)
  289. if err != nil {
  290. return err
  291. }
  292. l.db = sto
  293. // get currentBatch num
  294. l.currentBatch, err = l.GetCurrentBatch()
  295. if err != nil {
  296. return err
  297. }
  298. return nil
  299. }
  300. // use checkpoint from LocalStateDB
  301. return l.StateDB.Reset(batchNum)
  302. }