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.

377 lines
9.9 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.
  174. // MerkleTree is not affected.
  175. func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) error {
  176. // store at the DB the key: v, and value: leaf.Bytes()
  177. v, err := account.HashValue()
  178. if err != nil {
  179. return err
  180. }
  181. accountBytes, err := account.Bytes()
  182. if err != nil {
  183. return err
  184. }
  185. // store the Leaf value
  186. tx, err := s.db.NewTx()
  187. if err != nil {
  188. return err
  189. }
  190. _, err = tx.Get(idx.Bytes())
  191. if err != db.ErrNotFound {
  192. return ErrAccountAlreadyExists
  193. }
  194. tx.Put(v.Bytes(), accountBytes[:])
  195. tx.Put(idx.Bytes(), v.Bytes())
  196. return tx.Commit()
  197. }
  198. // UpdateAccount updates the Account in the StateDB for the given Idx.
  199. // MerkleTree is not affected.
  200. func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) error {
  201. // store at the DB the key: v, and value: leaf.Bytes()
  202. v, err := account.HashValue()
  203. if err != nil {
  204. return err
  205. }
  206. accountBytes, err := account.Bytes()
  207. if err != nil {
  208. return err
  209. }
  210. tx, err := s.db.NewTx()
  211. if err != nil {
  212. return err
  213. }
  214. tx.Put(v.Bytes(), accountBytes[:])
  215. tx.Put(idx.Bytes(), v.Bytes())
  216. return tx.Commit()
  217. }
  218. // MTCreateAccount creates a new Account in the StateDB for the given Idx,
  219. // and updates the MerkleTree, returning a CircomProcessorProof
  220. func (s *StateDB) MTCreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
  221. if s.mt == nil {
  222. return nil, ErrStateDBWithoutMT
  223. }
  224. err := s.CreateAccount(idx, account)
  225. if err != nil {
  226. return nil, err
  227. }
  228. v, err := account.HashValue() // already computed in s.CreateAccount, next iteration reuse first computation
  229. if err != nil {
  230. return nil, err
  231. }
  232. // Add k & v into the MT
  233. return s.mt.AddAndGetCircomProof(idx.BigInt(), v)
  234. }
  235. // MTUpdateAccount updates the Account in the StateDB for the given Idx, and
  236. // updates the MerkleTree, returning a CircomProcessorProof
  237. func (s *StateDB) MTUpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
  238. if s.mt == nil {
  239. return nil, ErrStateDBWithoutMT
  240. }
  241. err := s.UpdateAccount(idx, account)
  242. if err != nil {
  243. return nil, err
  244. }
  245. v, err := account.HashValue() // already computed in s.CreateAccount, next iteration reuse first computation
  246. if err != nil {
  247. return nil, err
  248. }
  249. // Add k & v into the MT
  250. return s.mt.Update(idx.BigInt(), v)
  251. }
  252. // MTGetProof returns the CircomVerifierProof for a given Idx
  253. func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
  254. if s.mt == nil {
  255. return nil, ErrStateDBWithoutMT
  256. }
  257. return s.mt.GenerateCircomVerifierProof(idx.BigInt(), s.mt.Root())
  258. }
  259. // LocalStateDB represents the local StateDB which allows to make copies from
  260. // the synchronizer StateDB, and is used by the tx-selector and the
  261. // batch-builder. LocalStateDB is an in-memory storage.
  262. type LocalStateDB struct {
  263. *StateDB
  264. synchronizerStateDB *StateDB
  265. }
  266. // NewLocalStateDB returns a new LocalStateDB connected to the given
  267. // synchronizerDB
  268. func NewLocalStateDB(path string, synchronizerDB *StateDB, withMT bool, nLevels int) (*LocalStateDB, error) {
  269. s, err := NewStateDB(path, withMT, nLevels)
  270. if err != nil {
  271. return nil, err
  272. }
  273. return &LocalStateDB{
  274. s,
  275. synchronizerDB,
  276. }, nil
  277. }
  278. // Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
  279. // gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
  280. func (l *LocalStateDB) Reset(batchNum uint64, fromSynchronizer bool) error {
  281. if batchNum == 0 {
  282. l.idx = 0
  283. return nil
  284. }
  285. synchronizerCheckpointPath := l.synchronizerStateDB.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  286. checkpointPath := l.path + PATHBATCHNUM + strconv.Itoa(int(batchNum))
  287. currentPath := l.path + PATHCURRENT
  288. if fromSynchronizer {
  289. // use checkpoint from SynchronizerStateDB
  290. if _, err := os.Stat(synchronizerCheckpointPath); os.IsNotExist(err) {
  291. // if synchronizerStateDB does not have checkpoint at batchNum, return err
  292. return fmt.Errorf("Checkpoint not exist in Synchronizer")
  293. }
  294. // remove 'current'
  295. err := os.RemoveAll(currentPath)
  296. if err != nil {
  297. return err
  298. }
  299. // copy synchronizer'BatchNumX' to 'current'
  300. cmd := exec.Command("cp", "-r", synchronizerCheckpointPath, currentPath)
  301. err = cmd.Run()
  302. if err != nil {
  303. return err
  304. }
  305. // copy synchronizer-'BatchNumX' to 'BatchNumX'
  306. cmd = exec.Command("cp", "-r", synchronizerCheckpointPath, checkpointPath)
  307. err = cmd.Run()
  308. if err != nil {
  309. return err
  310. }
  311. // open the new 'current'
  312. sto, err := pebble.NewPebbleStorage(currentPath, false)
  313. if err != nil {
  314. return err
  315. }
  316. l.db = sto
  317. // get currentBatch num
  318. l.currentBatch, err = l.GetCurrentBatch()
  319. if err != nil {
  320. return err
  321. }
  322. return nil
  323. }
  324. // use checkpoint from LocalStateDB
  325. return l.StateDB.Reset(batchNum)
  326. }