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.

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