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.

355 lines
9.2 KiB

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