snapshot_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package snapshot
  17. import (
  18. "fmt"
  19. "math/big"
  20. "math/rand"
  21. "testing"
  22. "github.com/VictoriaMetrics/fastcache"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/core/rawdb"
  25. "github.com/ethereum/go-ethereum/rlp"
  26. )
  27. // randomHash generates a random blob of data and returns it as a hash.
  28. func randomHash() common.Hash {
  29. var hash common.Hash
  30. if n, err := rand.Read(hash[:]); n != common.HashLength || err != nil {
  31. panic(err)
  32. }
  33. return hash
  34. }
  35. // randomAccount generates a random account and returns it RLP encoded.
  36. func randomAccount() []byte {
  37. root := randomHash()
  38. a := Account{
  39. Balance: big.NewInt(rand.Int63()),
  40. Nonce: rand.Uint64(),
  41. Root: root[:],
  42. CodeHash: emptyCode[:],
  43. }
  44. data, _ := rlp.EncodeToBytes(a)
  45. return data
  46. }
  47. // randomAccountSet generates a set of random accounts with the given strings as
  48. // the account address hashes.
  49. func randomAccountSet(hashes ...string) map[common.Hash][]byte {
  50. accounts := make(map[common.Hash][]byte)
  51. for _, hash := range hashes {
  52. accounts[common.HexToHash(hash)] = randomAccount()
  53. }
  54. return accounts
  55. }
  56. // randomStorageSet generates a set of random slots with the given strings as
  57. // the slot addresses.
  58. func randomStorageSet(accounts []string, hashes [][]string, nilStorage [][]string) map[common.Hash]map[common.Hash][]byte {
  59. storages := make(map[common.Hash]map[common.Hash][]byte)
  60. for index, account := range accounts {
  61. storages[common.HexToHash(account)] = make(map[common.Hash][]byte)
  62. if index < len(hashes) {
  63. hashes := hashes[index]
  64. for _, hash := range hashes {
  65. storages[common.HexToHash(account)][common.HexToHash(hash)] = randomHash().Bytes()
  66. }
  67. }
  68. if index < len(nilStorage) {
  69. nils := nilStorage[index]
  70. for _, hash := range nils {
  71. storages[common.HexToHash(account)][common.HexToHash(hash)] = nil
  72. }
  73. }
  74. }
  75. return storages
  76. }
  77. // Tests that if a disk layer becomes stale, no active external references will
  78. // be returned with junk data. This version of the test flattens every diff layer
  79. // to check internal corner case around the bottom-most memory accumulator.
  80. func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
  81. // Create an empty base layer and a snapshot tree out of it
  82. base := &diskLayer{
  83. diskdb: rawdb.NewMemoryDatabase(),
  84. root: common.HexToHash("0x01"),
  85. cache: fastcache.New(1024 * 500),
  86. }
  87. snaps := &Tree{
  88. layers: map[common.Hash]snapshot{
  89. base.root: base,
  90. },
  91. }
  92. // Retrieve a reference to the base and commit a diff on top
  93. ref := snaps.Snapshot(base.root)
  94. accounts := map[common.Hash][]byte{
  95. common.HexToHash("0xa1"): randomAccount(),
  96. }
  97. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
  98. t.Fatalf("failed to create a diff layer: %v", err)
  99. }
  100. if n := len(snaps.layers); n != 2 {
  101. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 2)
  102. }
  103. // Commit the diff layer onto the disk and ensure it's persisted
  104. if err := snaps.Cap(common.HexToHash("0x02"), 0); err != nil {
  105. t.Fatalf("failed to merge diff layer onto disk: %v", err)
  106. }
  107. // Since the base layer was modified, ensure that data retrieval on the external reference fail
  108. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  109. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  110. }
  111. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  112. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  113. }
  114. if n := len(snaps.layers); n != 1 {
  115. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 1)
  116. fmt.Println(snaps.layers)
  117. }
  118. }
  119. // Tests that if a disk layer becomes stale, no active external references will
  120. // be returned with junk data. This version of the test retains the bottom diff
  121. // layer to check the usual mode of operation where the accumulator is retained.
  122. func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
  123. // Create an empty base layer and a snapshot tree out of it
  124. base := &diskLayer{
  125. diskdb: rawdb.NewMemoryDatabase(),
  126. root: common.HexToHash("0x01"),
  127. cache: fastcache.New(1024 * 500),
  128. }
  129. snaps := &Tree{
  130. layers: map[common.Hash]snapshot{
  131. base.root: base,
  132. },
  133. }
  134. // Retrieve a reference to the base and commit two diffs on top
  135. ref := snaps.Snapshot(base.root)
  136. accounts := map[common.Hash][]byte{
  137. common.HexToHash("0xa1"): randomAccount(),
  138. }
  139. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
  140. t.Fatalf("failed to create a diff layer: %v", err)
  141. }
  142. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
  143. t.Fatalf("failed to create a diff layer: %v", err)
  144. }
  145. if n := len(snaps.layers); n != 3 {
  146. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
  147. }
  148. // Commit the diff layer onto the disk and ensure it's persisted
  149. defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit)
  150. aggregatorMemoryLimit = 0
  151. if err := snaps.Cap(common.HexToHash("0x03"), 2); err != nil {
  152. t.Fatalf("failed to merge diff layer onto disk: %v", err)
  153. }
  154. // Since the base layer was modified, ensure that data retrievald on the external reference fail
  155. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  156. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  157. }
  158. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  159. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  160. }
  161. if n := len(snaps.layers); n != 2 {
  162. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
  163. fmt.Println(snaps.layers)
  164. }
  165. }
  166. // Tests that if a diff layer becomes stale, no active external references will
  167. // be returned with junk data. This version of the test flattens every diff layer
  168. // to check internal corner case around the bottom-most memory accumulator.
  169. func TestDiffLayerExternalInvalidationFullFlatten(t *testing.T) {
  170. // Create an empty base layer and a snapshot tree out of it
  171. base := &diskLayer{
  172. diskdb: rawdb.NewMemoryDatabase(),
  173. root: common.HexToHash("0x01"),
  174. cache: fastcache.New(1024 * 500),
  175. }
  176. snaps := &Tree{
  177. layers: map[common.Hash]snapshot{
  178. base.root: base,
  179. },
  180. }
  181. // Commit two diffs on top and retrieve a reference to the bottommost
  182. accounts := map[common.Hash][]byte{
  183. common.HexToHash("0xa1"): randomAccount(),
  184. }
  185. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
  186. t.Fatalf("failed to create a diff layer: %v", err)
  187. }
  188. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
  189. t.Fatalf("failed to create a diff layer: %v", err)
  190. }
  191. if n := len(snaps.layers); n != 3 {
  192. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
  193. }
  194. ref := snaps.Snapshot(common.HexToHash("0x02"))
  195. // Flatten the diff layer into the bottom accumulator
  196. if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil {
  197. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  198. }
  199. // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
  200. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  201. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  202. }
  203. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  204. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  205. }
  206. if n := len(snaps.layers); n != 2 {
  207. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
  208. fmt.Println(snaps.layers)
  209. }
  210. }
  211. // Tests that if a diff layer becomes stale, no active external references will
  212. // be returned with junk data. This version of the test retains the bottom diff
  213. // layer to check the usual mode of operation where the accumulator is retained.
  214. func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
  215. // Create an empty base layer and a snapshot tree out of it
  216. base := &diskLayer{
  217. diskdb: rawdb.NewMemoryDatabase(),
  218. root: common.HexToHash("0x01"),
  219. cache: fastcache.New(1024 * 500),
  220. }
  221. snaps := &Tree{
  222. layers: map[common.Hash]snapshot{
  223. base.root: base,
  224. },
  225. }
  226. // Commit three diffs on top and retrieve a reference to the bottommost
  227. accounts := map[common.Hash][]byte{
  228. common.HexToHash("0xa1"): randomAccount(),
  229. }
  230. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), nil, accounts, nil); err != nil {
  231. t.Fatalf("failed to create a diff layer: %v", err)
  232. }
  233. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), nil, accounts, nil); err != nil {
  234. t.Fatalf("failed to create a diff layer: %v", err)
  235. }
  236. if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), nil, accounts, nil); err != nil {
  237. t.Fatalf("failed to create a diff layer: %v", err)
  238. }
  239. if n := len(snaps.layers); n != 4 {
  240. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 4)
  241. }
  242. ref := snaps.Snapshot(common.HexToHash("0x02"))
  243. // Doing a Cap operation with many allowed layers should be a no-op
  244. exp := len(snaps.layers)
  245. if err := snaps.Cap(common.HexToHash("0x04"), 2000); err != nil {
  246. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  247. }
  248. if got := len(snaps.layers); got != exp {
  249. t.Errorf("layers modified, got %d exp %d", got, exp)
  250. }
  251. // Flatten the diff layer into the bottom accumulator
  252. if err := snaps.Cap(common.HexToHash("0x04"), 2); err != nil {
  253. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  254. }
  255. // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
  256. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  257. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  258. }
  259. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  260. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  261. }
  262. if n := len(snaps.layers); n != 3 {
  263. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 3)
  264. fmt.Println(snaps.layers)
  265. }
  266. }
  267. // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening.
  268. func TestPostCapBasicDataAccess(t *testing.T) {
  269. // setAccount is a helper to construct a random account entry and assign it to
  270. // an account slot in a snapshot
  271. setAccount := func(accKey string) map[common.Hash][]byte {
  272. return map[common.Hash][]byte{
  273. common.HexToHash(accKey): randomAccount(),
  274. }
  275. }
  276. // Create a starting base layer and a snapshot tree out of it
  277. base := &diskLayer{
  278. diskdb: rawdb.NewMemoryDatabase(),
  279. root: common.HexToHash("0x01"),
  280. cache: fastcache.New(1024 * 500),
  281. }
  282. snaps := &Tree{
  283. layers: map[common.Hash]snapshot{
  284. base.root: base,
  285. },
  286. }
  287. // The lowest difflayer
  288. snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
  289. snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
  290. snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), nil, setAccount("0xb2"), nil)
  291. snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
  292. snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), nil, setAccount("0xb3"), nil)
  293. // checkExist verifies if an account exiss in a snapshot
  294. checkExist := func(layer *diffLayer, key string) error {
  295. if data, _ := layer.Account(common.HexToHash(key)); data == nil {
  296. return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key))
  297. }
  298. return nil
  299. }
  300. // shouldErr checks that an account access errors as expected
  301. shouldErr := func(layer *diffLayer, key string) error {
  302. if data, err := layer.Account(common.HexToHash(key)); err == nil {
  303. return fmt.Errorf("expected error, got data %x", data)
  304. }
  305. return nil
  306. }
  307. // check basics
  308. snap := snaps.Snapshot(common.HexToHash("0xb3")).(*diffLayer)
  309. if err := checkExist(snap, "0xa1"); err != nil {
  310. t.Error(err)
  311. }
  312. if err := checkExist(snap, "0xb2"); err != nil {
  313. t.Error(err)
  314. }
  315. if err := checkExist(snap, "0xb3"); err != nil {
  316. t.Error(err)
  317. }
  318. // Cap to a bad root should fail
  319. if err := snaps.Cap(common.HexToHash("0x1337"), 0); err == nil {
  320. t.Errorf("expected error, got none")
  321. }
  322. // Now, merge the a-chain
  323. snaps.Cap(common.HexToHash("0xa3"), 0)
  324. // At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is
  325. // the parent of b2, b2 should no longer be able to iterate into parent.
  326. // These should still be accessible
  327. if err := checkExist(snap, "0xb2"); err != nil {
  328. t.Error(err)
  329. }
  330. if err := checkExist(snap, "0xb3"); err != nil {
  331. t.Error(err)
  332. }
  333. // But these would need iteration into the modified parent
  334. if err := shouldErr(snap, "0xa1"); err != nil {
  335. t.Error(err)
  336. }
  337. if err := shouldErr(snap, "0xa2"); err != nil {
  338. t.Error(err)
  339. }
  340. if err := shouldErr(snap, "0xa3"); err != nil {
  341. t.Error(err)
  342. }
  343. // Now, merge it again, just for fun. It should now error, since a3
  344. // is a disk layer
  345. if err := snaps.Cap(common.HexToHash("0xa3"), 0); err == nil {
  346. t.Error("expected error capping the disk layer, got none")
  347. }
  348. }