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.

651 lines
18 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
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
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
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. "encoding/hex"
  4. "fmt"
  5. "io/ioutil"
  6. "math/big"
  7. "os"
  8. "strings"
  9. "testing"
  10. ethCommon "github.com/ethereum/go-ethereum/common"
  11. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  12. "github.com/hermeznetwork/hermez-node/common"
  13. "github.com/hermeznetwork/hermez-node/log"
  14. "github.com/hermeznetwork/tracerr"
  15. "github.com/iden3/go-iden3-crypto/babyjub"
  16. "github.com/iden3/go-merkletree/db"
  17. "github.com/stretchr/testify/assert"
  18. "github.com/stretchr/testify/require"
  19. )
  20. func newAccount(t *testing.T, i int) *common.Account {
  21. var sk babyjub.PrivateKey
  22. _, err := hex.Decode(sk[:],
  23. []byte("0001020304050607080900010203040506070809000102030405060708090001"))
  24. require.NoError(t, err)
  25. pk := sk.Public()
  26. key, err := ethCrypto.GenerateKey()
  27. require.NoError(t, err)
  28. address := ethCrypto.PubkeyToAddress(key.PublicKey)
  29. return &common.Account{
  30. Idx: common.Idx(256 + i),
  31. TokenID: common.TokenID(i),
  32. Nonce: common.Nonce(i),
  33. Balance: big.NewInt(1000),
  34. BJJ: pk.Compress(),
  35. EthAddr: address,
  36. }
  37. }
  38. func TestNewStateDBIntermediateState(t *testing.T) {
  39. dir, err := ioutil.TempDir("", "tmpdb")
  40. require.NoError(t, err)
  41. defer require.NoError(t, os.RemoveAll(dir))
  42. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeTxSelector, NLevels: 0})
  43. require.NoError(t, err)
  44. // test values
  45. k0 := []byte("testkey0")
  46. k1 := []byte("testkey1")
  47. v0 := []byte("testvalue0")
  48. v1 := []byte("testvalue1")
  49. // store some data
  50. tx, err := sdb.db.DB().NewTx()
  51. require.NoError(t, err)
  52. err = tx.Put(k0, v0)
  53. require.NoError(t, err)
  54. err = tx.Commit()
  55. require.NoError(t, err)
  56. v, err := sdb.db.DB().Get(k0)
  57. require.NoError(t, err)
  58. assert.Equal(t, v0, v)
  59. // k0 not yet in last
  60. err = sdb.LastRead(func(sdb *Last) error {
  61. _, err := sdb.DB().Get(k0)
  62. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  63. return nil
  64. })
  65. require.NoError(t, err)
  66. // Close PebbleDB before creating a new StateDB
  67. sdb.Close()
  68. // call NewStateDB which should get the db at the last checkpoint state
  69. // executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
  70. sdb, err = NewStateDB(Config{Path: dir, Keep: 128, Type: TypeTxSelector, NLevels: 0})
  71. require.NoError(t, err)
  72. v, err = sdb.db.DB().Get(k0)
  73. assert.NotNil(t, err)
  74. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  75. assert.Nil(t, v)
  76. // k0 not in last
  77. err = sdb.LastRead(func(sdb *Last) error {
  78. _, err := sdb.DB().Get(k0)
  79. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  80. return nil
  81. })
  82. require.NoError(t, err)
  83. // store the same data from the beginning that has ben lost since last NewStateDB
  84. tx, err = sdb.db.DB().NewTx()
  85. require.NoError(t, err)
  86. err = tx.Put(k0, v0)
  87. require.NoError(t, err)
  88. err = tx.Commit()
  89. require.NoError(t, err)
  90. v, err = sdb.db.DB().Get(k0)
  91. require.NoError(t, err)
  92. assert.Equal(t, v0, v)
  93. // k0 yet not in last
  94. err = sdb.LastRead(func(sdb *Last) error {
  95. _, err := sdb.DB().Get(k0)
  96. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  97. return nil
  98. })
  99. require.NoError(t, err)
  100. // make checkpoints with the current state
  101. bn, err := sdb.getCurrentBatch()
  102. require.NoError(t, err)
  103. assert.Equal(t, common.BatchNum(0), bn)
  104. err = sdb.MakeCheckpoint()
  105. require.NoError(t, err)
  106. bn, err = sdb.getCurrentBatch()
  107. require.NoError(t, err)
  108. assert.Equal(t, common.BatchNum(1), bn)
  109. // k0 in last
  110. err = sdb.LastRead(func(sdb *Last) error {
  111. v, err := sdb.DB().Get(k0)
  112. require.NoError(t, err)
  113. assert.Equal(t, v0, v)
  114. return nil
  115. })
  116. require.NoError(t, err)
  117. // write more data
  118. tx, err = sdb.db.DB().NewTx()
  119. require.NoError(t, err)
  120. err = tx.Put(k1, v1)
  121. require.NoError(t, err)
  122. err = tx.Put(k0, v1) // overwrite k0 with v1
  123. require.NoError(t, err)
  124. err = tx.Commit()
  125. require.NoError(t, err)
  126. v, err = sdb.db.DB().Get(k1)
  127. require.NoError(t, err)
  128. assert.Equal(t, v1, v)
  129. err = sdb.LastRead(func(sdb *Last) error {
  130. v, err := sdb.DB().Get(k0)
  131. require.NoError(t, err)
  132. assert.Equal(t, v0, v)
  133. return nil
  134. })
  135. require.NoError(t, err)
  136. // Close PebbleDB before creating a new StateDB
  137. sdb.Close()
  138. // call NewStateDB which should get the db at the last checkpoint state
  139. // executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
  140. sdb, err = NewStateDB(Config{Path: dir, Keep: 128, Type: TypeTxSelector, NLevels: 0})
  141. require.NoError(t, err)
  142. bn, err = sdb.getCurrentBatch()
  143. require.NoError(t, err)
  144. assert.Equal(t, common.BatchNum(1), bn)
  145. // we closed the db without doing a checkpoint after overwriting k0, so
  146. // it's back to v0
  147. v, err = sdb.db.DB().Get(k0)
  148. require.NoError(t, err)
  149. assert.Equal(t, v0, v)
  150. v, err = sdb.db.DB().Get(k1)
  151. assert.NotNil(t, err)
  152. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  153. assert.Nil(t, v)
  154. }
  155. func TestStateDBWithoutMT(t *testing.T) {
  156. dir, err := ioutil.TempDir("", "tmpdb")
  157. require.NoError(t, err)
  158. defer require.NoError(t, os.RemoveAll(dir))
  159. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeTxSelector, NLevels: 0})
  160. require.NoError(t, err)
  161. // create test accounts
  162. var accounts []*common.Account
  163. for i := 0; i < 4; i++ {
  164. accounts = append(accounts, newAccount(t, i))
  165. }
  166. // get non-existing account, expecting an error
  167. unexistingAccount := common.Idx(1)
  168. _, err = sdb.GetAccount(unexistingAccount)
  169. assert.NotNil(t, err)
  170. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  171. // add test accounts
  172. for i := 0; i < len(accounts); i++ {
  173. _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
  174. require.NoError(t, err)
  175. }
  176. for i := 0; i < len(accounts); i++ {
  177. existingAccount := accounts[i].Idx
  178. accGetted, err := sdb.GetAccount(existingAccount)
  179. require.NoError(t, err)
  180. assert.Equal(t, accounts[i], accGetted)
  181. }
  182. // try already existing idx and get error
  183. existingAccount := common.Idx(256)
  184. _, err = sdb.GetAccount(existingAccount) // check that exist
  185. require.NoError(t, err)
  186. _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
  187. assert.NotNil(t, err)
  188. assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
  189. // update accounts
  190. for i := 0; i < len(accounts); i++ {
  191. accounts[i].Nonce = accounts[i].Nonce + 1
  192. existingAccount = common.Idx(i)
  193. _, err = sdb.UpdateAccount(existingAccount, accounts[i])
  194. require.NoError(t, err)
  195. }
  196. _, err = sdb.MTGetProof(common.Idx(1))
  197. assert.NotNil(t, err)
  198. assert.Equal(t, ErrStateDBWithoutMT, tracerr.Unwrap(err))
  199. }
  200. func TestStateDBWithMT(t *testing.T) {
  201. dir, err := ioutil.TempDir("", "tmpdb")
  202. require.NoError(t, err)
  203. defer require.NoError(t, os.RemoveAll(dir))
  204. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeSynchronizer, NLevels: 32})
  205. require.NoError(t, err)
  206. // create test accounts
  207. var accounts []*common.Account
  208. for i := 0; i < 20; i++ {
  209. accounts = append(accounts, newAccount(t, i))
  210. }
  211. // get non-existing account, expecting an error
  212. _, err = sdb.GetAccount(common.Idx(1))
  213. assert.NotNil(t, err)
  214. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  215. // add test accounts
  216. for i := 0; i < len(accounts); i++ {
  217. _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
  218. require.NoError(t, err)
  219. }
  220. for i := 0; i < len(accounts); i++ {
  221. accGetted, err := sdb.GetAccount(accounts[i].Idx)
  222. require.NoError(t, err)
  223. assert.Equal(t, accounts[i], accGetted)
  224. }
  225. // try already existing idx and get error
  226. _, err = sdb.GetAccount(common.Idx(256)) // check that exist
  227. require.NoError(t, err)
  228. _, err = sdb.CreateAccount(common.Idx(256), accounts[1]) // check that can not be created twice
  229. assert.NotNil(t, err)
  230. assert.Equal(t, ErrAccountAlreadyExists, tracerr.Unwrap(err))
  231. _, err = sdb.MTGetProof(common.Idx(256))
  232. require.NoError(t, err)
  233. // update accounts
  234. for i := 0; i < len(accounts); i++ {
  235. accounts[i].Nonce = accounts[i].Nonce + 1
  236. _, err = sdb.UpdateAccount(accounts[i].Idx, accounts[i])
  237. require.NoError(t, err)
  238. }
  239. a, err := sdb.GetAccount(common.Idx(256)) // check that account value has been updated
  240. require.NoError(t, err)
  241. assert.Equal(t, accounts[0].Nonce, a.Nonce)
  242. }
  243. // TestCheckpoints performs almost the same test than kvdb/kvdb_test.go
  244. // TestCheckpoints, but over the StateDB
  245. func TestCheckpoints(t *testing.T) {
  246. dir, err := ioutil.TempDir("", "sdb")
  247. require.NoError(t, err)
  248. defer require.NoError(t, os.RemoveAll(dir))
  249. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeSynchronizer, NLevels: 32})
  250. require.NoError(t, err)
  251. err = sdb.Reset(0)
  252. require.NoError(t, err)
  253. // create test accounts
  254. var accounts []*common.Account
  255. for i := 0; i < 10; i++ {
  256. accounts = append(accounts, newAccount(t, i))
  257. }
  258. // add test accounts
  259. for i := 0; i < len(accounts); i++ {
  260. _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
  261. require.NoError(t, err)
  262. }
  263. // account doesn't exist in Last checkpoint
  264. _, err = sdb.LastGetAccount(accounts[0].Idx)
  265. assert.Equal(t, db.ErrNotFound, tracerr.Unwrap(err))
  266. // do checkpoints and check that currentBatch is correct
  267. err = sdb.MakeCheckpoint()
  268. require.NoError(t, err)
  269. cb, err := sdb.getCurrentBatch()
  270. require.NoError(t, err)
  271. assert.Equal(t, common.BatchNum(1), cb)
  272. // account exists in Last checkpoint
  273. accCur, err := sdb.GetAccount(accounts[0].Idx)
  274. require.NoError(t, err)
  275. accLast, err := sdb.LastGetAccount(accounts[0].Idx)
  276. require.NoError(t, err)
  277. assert.Equal(t, accounts[0], accLast)
  278. assert.Equal(t, accCur, accLast)
  279. for i := 1; i < 10; i++ {
  280. err = sdb.MakeCheckpoint()
  281. require.NoError(t, err)
  282. cb, err = sdb.getCurrentBatch()
  283. require.NoError(t, err)
  284. assert.Equal(t, common.BatchNum(i+1), cb)
  285. }
  286. // printCheckpoints(t, sdb.cfg.Path)
  287. // reset checkpoint
  288. err = sdb.Reset(3)
  289. require.NoError(t, err)
  290. // check that reset can be repeated (as there exist the 'current' and
  291. // 'BatchNum3', from where the 'current' is a copy)
  292. err = sdb.Reset(3)
  293. require.NoError(t, err)
  294. // check that currentBatch is as expected after Reset
  295. cb, err = sdb.getCurrentBatch()
  296. require.NoError(t, err)
  297. assert.Equal(t, common.BatchNum(3), cb)
  298. // advance one checkpoint and check that currentBatch is fine
  299. err = sdb.MakeCheckpoint()
  300. require.NoError(t, err)
  301. cb, err = sdb.getCurrentBatch()
  302. require.NoError(t, err)
  303. assert.Equal(t, common.BatchNum(4), cb)
  304. err = sdb.db.DeleteCheckpoint(common.BatchNum(1))
  305. require.NoError(t, err)
  306. err = sdb.db.DeleteCheckpoint(common.BatchNum(2))
  307. require.NoError(t, err)
  308. err = sdb.db.DeleteCheckpoint(common.BatchNum(1)) // does not exist, should return err
  309. assert.NotNil(t, err)
  310. err = sdb.db.DeleteCheckpoint(common.BatchNum(2)) // does not exist, should return err
  311. assert.NotNil(t, err)
  312. // Create a LocalStateDB from the initial StateDB
  313. dirLocal, err := ioutil.TempDir("", "ldb")
  314. require.NoError(t, err)
  315. defer require.NoError(t, os.RemoveAll(dirLocal))
  316. ldb, err := NewLocalStateDB(Config{Path: dirLocal, Keep: 128, Type: TypeBatchBuilder,
  317. NLevels: 32}, sdb)
  318. require.NoError(t, err)
  319. // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
  320. err = ldb.Reset(4, true)
  321. require.NoError(t, err)
  322. // check that currentBatch is 4 after the Reset
  323. cb, err = ldb.getCurrentBatch()
  324. require.NoError(t, err)
  325. assert.Equal(t, common.BatchNum(4), cb)
  326. // advance one checkpoint in ldb
  327. err = ldb.MakeCheckpoint()
  328. require.NoError(t, err)
  329. cb, err = ldb.getCurrentBatch()
  330. require.NoError(t, err)
  331. assert.Equal(t, common.BatchNum(5), cb)
  332. // Create a 2nd LocalStateDB from the initial StateDB
  333. dirLocal2, err := ioutil.TempDir("", "ldb2")
  334. require.NoError(t, err)
  335. defer require.NoError(t, os.RemoveAll(dirLocal2))
  336. ldb2, err := NewLocalStateDB(Config{Path: dirLocal2, Keep: 128, Type: TypeBatchBuilder,
  337. NLevels: 32}, sdb)
  338. require.NoError(t, err)
  339. // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
  340. err = ldb2.Reset(4, true)
  341. require.NoError(t, err)
  342. // check that currentBatch is 4 after the Reset
  343. cb = ldb2.CurrentBatch()
  344. assert.Equal(t, common.BatchNum(4), cb)
  345. // advance one checkpoint in ldb2
  346. err = ldb2.MakeCheckpoint()
  347. require.NoError(t, err)
  348. cb = ldb2.CurrentBatch()
  349. assert.Equal(t, common.BatchNum(5), cb)
  350. debug := false
  351. if debug {
  352. printCheckpoints(t, sdb.cfg.Path)
  353. printCheckpoints(t, ldb.cfg.Path)
  354. printCheckpoints(t, ldb2.cfg.Path)
  355. }
  356. }
  357. func TestStateDBGetAccounts(t *testing.T) {
  358. dir, err := ioutil.TempDir("", "tmpdb")
  359. require.NoError(t, err)
  360. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeTxSelector, NLevels: 0})
  361. require.NoError(t, err)
  362. // create test accounts
  363. var accounts []common.Account
  364. for i := 0; i < 16; i++ {
  365. account := newAccount(t, i)
  366. accounts = append(accounts, *account)
  367. }
  368. // add test accounts
  369. for i := range accounts {
  370. _, err = sdb.CreateAccount(accounts[i].Idx, &accounts[i])
  371. require.NoError(t, err)
  372. }
  373. dbAccounts, err := sdb.TestGetAccounts()
  374. require.NoError(t, err)
  375. assert.Equal(t, accounts, dbAccounts)
  376. }
  377. func printCheckpoints(t *testing.T, path string) {
  378. files, err := ioutil.ReadDir(path)
  379. require.NoError(t, err)
  380. fmt.Println(path)
  381. for _, f := range files {
  382. fmt.Println(" " + f.Name())
  383. }
  384. }
  385. func bigFromStr(h string, u int) *big.Int {
  386. if u == 16 {
  387. h = strings.TrimPrefix(h, "0x")
  388. }
  389. b, ok := new(big.Int).SetString(h, u)
  390. if !ok {
  391. panic("bigFromStr err")
  392. }
  393. return b
  394. }
  395. func TestCheckAccountsTreeTestVectors(t *testing.T) {
  396. dir, err := ioutil.TempDir("", "tmpdb")
  397. require.NoError(t, err)
  398. defer require.NoError(t, os.RemoveAll(dir))
  399. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeSynchronizer, NLevels: 32})
  400. require.NoError(t, err)
  401. ay0 := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1))
  402. // test value from js version (compatibility-canary)
  403. assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
  404. (hex.EncodeToString(ay0.Bytes())))
  405. bjjPoint0Comp := babyjub.PackSignY(true, ay0)
  406. bjj0 := babyjub.PublicKeyComp(bjjPoint0Comp)
  407. ay1 := bigFromStr("00", 16)
  408. bjjPoint1Comp := babyjub.PackSignY(false, ay1)
  409. bjj1 := babyjub.PublicKeyComp(bjjPoint1Comp)
  410. ay2 := bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16)
  411. bjjPoint2Comp := babyjub.PackSignY(false, ay2)
  412. bjj2 := babyjub.PublicKeyComp(bjjPoint2Comp)
  413. ay3 := bigFromStr("0x10", 16) // 0x10=16
  414. bjjPoint3Comp := babyjub.PackSignY(false, ay3)
  415. require.NoError(t, err)
  416. bjj3 := babyjub.PublicKeyComp(bjjPoint3Comp)
  417. accounts := []*common.Account{
  418. {
  419. Idx: 1,
  420. TokenID: 0xFFFFFFFF,
  421. BJJ: bjj0,
  422. EthAddr: ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"),
  423. Nonce: common.Nonce(0xFFFFFFFFFF),
  424. Balance: bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16),
  425. },
  426. {
  427. Idx: 100,
  428. TokenID: 0,
  429. BJJ: bjj1,
  430. EthAddr: ethCommon.HexToAddress("0x00"),
  431. Nonce: common.Nonce(0),
  432. Balance: bigFromStr("0", 10),
  433. },
  434. {
  435. Idx: 0xFFFFFFFFFFFF,
  436. TokenID: 3,
  437. BJJ: bjj2,
  438. EthAddr: ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"),
  439. Nonce: common.Nonce(129),
  440. Balance: bigFromStr("42000000000000000000", 10),
  441. },
  442. {
  443. Idx: 10000,
  444. TokenID: 1000,
  445. BJJ: bjj3,
  446. EthAddr: ethCommon.HexToAddress("0x64"),
  447. Nonce: common.Nonce(1900),
  448. Balance: bigFromStr("14000000000000000000", 10),
  449. },
  450. }
  451. for i := 0; i < len(accounts); i++ {
  452. _, err = accounts[i].HashValue()
  453. require.NoError(t, err)
  454. _, err = sdb.CreateAccount(accounts[i].Idx, accounts[i])
  455. if err != nil {
  456. log.Error(err)
  457. }
  458. require.NoError(t, err)
  459. }
  460. // root value generated by js version:
  461. assert.Equal(t,
  462. "17298264051379321456969039521810887093935433569451713402227686942080129181291",
  463. sdb.MT.Root().BigInt().String())
  464. }
  465. // TestListCheckpoints performs almost the same test than kvdb/kvdb_test.go
  466. // TestListCheckpoints, but over the StateDB
  467. func TestListCheckpoints(t *testing.T) {
  468. dir, err := ioutil.TempDir("", "tmpdb")
  469. require.NoError(t, err)
  470. defer require.NoError(t, os.RemoveAll(dir))
  471. sdb, err := NewStateDB(Config{Path: dir, Keep: 128, Type: TypeSynchronizer, NLevels: 32})
  472. require.NoError(t, err)
  473. numCheckpoints := 16
  474. // do checkpoints
  475. for i := 0; i < numCheckpoints; i++ {
  476. err = sdb.MakeCheckpoint()
  477. require.NoError(t, err)
  478. }
  479. list, err := sdb.db.ListCheckpoints()
  480. require.NoError(t, err)
  481. assert.Equal(t, numCheckpoints, len(list))
  482. assert.Equal(t, 1, list[0])
  483. assert.Equal(t, numCheckpoints, list[len(list)-1])
  484. numReset := 10
  485. err = sdb.Reset(common.BatchNum(numReset))
  486. require.NoError(t, err)
  487. list, err = sdb.db.ListCheckpoints()
  488. require.NoError(t, err)
  489. assert.Equal(t, numReset, len(list))
  490. assert.Equal(t, 1, list[0])
  491. assert.Equal(t, numReset, list[len(list)-1])
  492. }
  493. // TestDeleteOldCheckpoints performs almost the same test than
  494. // kvdb/kvdb_test.go TestDeleteOldCheckpoints, but over the StateDB
  495. func TestDeleteOldCheckpoints(t *testing.T) {
  496. dir, err := ioutil.TempDir("", "tmpdb")
  497. require.NoError(t, err)
  498. defer require.NoError(t, os.RemoveAll(dir))
  499. keep := 16
  500. sdb, err := NewStateDB(Config{Path: dir, Keep: keep, Type: TypeSynchronizer, NLevels: 32})
  501. require.NoError(t, err)
  502. numCheckpoints := 32
  503. // do checkpoints and check that we never have more than `keep`
  504. // checkpoints
  505. for i := 0; i < numCheckpoints; i++ {
  506. err = sdb.MakeCheckpoint()
  507. require.NoError(t, err)
  508. checkpoints, err := sdb.db.ListCheckpoints()
  509. require.NoError(t, err)
  510. assert.LessOrEqual(t, len(checkpoints), keep)
  511. }
  512. }
  513. func TestCurrentIdx(t *testing.T) {
  514. dir, err := ioutil.TempDir("", "tmpdb")
  515. require.NoError(t, err)
  516. defer require.NoError(t, os.RemoveAll(dir))
  517. keep := 16
  518. sdb, err := NewStateDB(Config{Path: dir, Keep: keep, Type: TypeSynchronizer, NLevels: 32})
  519. require.NoError(t, err)
  520. idx := sdb.CurrentIdx()
  521. assert.Equal(t, common.Idx(255), idx)
  522. sdb.Close()
  523. sdb, err = NewStateDB(Config{Path: dir, Keep: keep, Type: TypeSynchronizer, NLevels: 32})
  524. require.NoError(t, err)
  525. idx = sdb.CurrentIdx()
  526. assert.Equal(t, common.Idx(255), idx)
  527. err = sdb.MakeCheckpoint()
  528. require.NoError(t, err)
  529. idx = sdb.CurrentIdx()
  530. assert.Equal(t, common.Idx(255), idx)
  531. sdb.Close()
  532. sdb, err = NewStateDB(Config{Path: dir, Keep: keep, Type: TypeSynchronizer, NLevels: 32})
  533. require.NoError(t, err)
  534. idx = sdb.CurrentIdx()
  535. assert.Equal(t, common.Idx(255), idx)
  536. }
  537. func TestResetFromBadCheckpoint(t *testing.T) {
  538. dir, err := ioutil.TempDir("", "tmpdb")
  539. require.NoError(t, err)
  540. defer require.NoError(t, os.RemoveAll(dir))
  541. keep := 16
  542. sdb, err := NewStateDB(Config{Path: dir, Keep: keep, Type: TypeSynchronizer, NLevels: 32})
  543. require.NoError(t, err)
  544. err = sdb.MakeCheckpoint()
  545. require.NoError(t, err)
  546. err = sdb.MakeCheckpoint()
  547. require.NoError(t, err)
  548. err = sdb.MakeCheckpoint()
  549. require.NoError(t, err)
  550. // reset from a checkpoint that doesn't exist
  551. err = sdb.Reset(10)
  552. require.Error(t, err)
  553. }