snapshot_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. // Tests that if a disk layer becomes stale, no active external references will
  57. // be returned with junk data. This version of the test flattens every diff layer
  58. // to check internal corner case around the bottom-most memory accumulator.
  59. func TestDiskLayerExternalInvalidationFullFlatten(t *testing.T) {
  60. // Create an empty base layer and a snapshot tree out of it
  61. base := &diskLayer{
  62. diskdb: rawdb.NewMemoryDatabase(),
  63. root: common.HexToHash("0x01"),
  64. cache: fastcache.New(1024 * 500),
  65. }
  66. snaps := &Tree{
  67. layers: map[common.Hash]snapshot{
  68. base.root: base,
  69. },
  70. }
  71. // Retrieve a reference to the base and commit a diff on top
  72. ref := snaps.Snapshot(base.root)
  73. accounts := map[common.Hash][]byte{
  74. common.HexToHash("0xa1"): randomAccount(),
  75. }
  76. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil {
  77. t.Fatalf("failed to create a diff layer: %v", err)
  78. }
  79. if n := len(snaps.layers); n != 2 {
  80. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 2)
  81. }
  82. // Commit the diff layer onto the disk and ensure it's persisted
  83. if err := snaps.Cap(common.HexToHash("0x02"), 0); err != nil {
  84. t.Fatalf("failed to merge diff layer onto disk: %v", err)
  85. }
  86. // Since the base layer was modified, ensure that data retrievald on the external reference fail
  87. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  88. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  89. }
  90. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  91. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  92. }
  93. if n := len(snaps.layers); n != 1 {
  94. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 1)
  95. fmt.Println(snaps.layers)
  96. }
  97. }
  98. // Tests that if a disk layer becomes stale, no active external references will
  99. // be returned with junk data. This version of the test retains the bottom diff
  100. // layer to check the usual mode of operation where the accumulator is retained.
  101. func TestDiskLayerExternalInvalidationPartialFlatten(t *testing.T) {
  102. // Create an empty base layer and a snapshot tree out of it
  103. base := &diskLayer{
  104. diskdb: rawdb.NewMemoryDatabase(),
  105. root: common.HexToHash("0x01"),
  106. cache: fastcache.New(1024 * 500),
  107. }
  108. snaps := &Tree{
  109. layers: map[common.Hash]snapshot{
  110. base.root: base,
  111. },
  112. }
  113. // Retrieve a reference to the base and commit two diffs on top
  114. ref := snaps.Snapshot(base.root)
  115. accounts := map[common.Hash][]byte{
  116. common.HexToHash("0xa1"): randomAccount(),
  117. }
  118. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil {
  119. t.Fatalf("failed to create a diff layer: %v", err)
  120. }
  121. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil {
  122. t.Fatalf("failed to create a diff layer: %v", err)
  123. }
  124. if n := len(snaps.layers); n != 3 {
  125. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
  126. }
  127. // Commit the diff layer onto the disk and ensure it's persisted
  128. defer func(memcap uint64) { aggregatorMemoryLimit = memcap }(aggregatorMemoryLimit)
  129. aggregatorMemoryLimit = 0
  130. if err := snaps.Cap(common.HexToHash("0x03"), 2); err != nil {
  131. t.Fatalf("failed to merge diff layer onto disk: %v", err)
  132. }
  133. // Since the base layer was modified, ensure that data retrievald on the external reference fail
  134. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  135. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  136. }
  137. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  138. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  139. }
  140. if n := len(snaps.layers); n != 2 {
  141. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
  142. fmt.Println(snaps.layers)
  143. }
  144. }
  145. // Tests that if a diff layer becomes stale, no active external references will
  146. // be returned with junk data. This version of the test flattens every diff layer
  147. // to check internal corner case around the bottom-most memory accumulator.
  148. func TestDiffLayerExternalInvalidationFullFlatten(t *testing.T) {
  149. // Create an empty base layer and a snapshot tree out of it
  150. base := &diskLayer{
  151. diskdb: rawdb.NewMemoryDatabase(),
  152. root: common.HexToHash("0x01"),
  153. cache: fastcache.New(1024 * 500),
  154. }
  155. snaps := &Tree{
  156. layers: map[common.Hash]snapshot{
  157. base.root: base,
  158. },
  159. }
  160. // Commit two diffs on top and retrieve a reference to the bottommost
  161. accounts := map[common.Hash][]byte{
  162. common.HexToHash("0xa1"): randomAccount(),
  163. }
  164. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil {
  165. t.Fatalf("failed to create a diff layer: %v", err)
  166. }
  167. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil {
  168. t.Fatalf("failed to create a diff layer: %v", err)
  169. }
  170. if n := len(snaps.layers); n != 3 {
  171. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 3)
  172. }
  173. ref := snaps.Snapshot(common.HexToHash("0x02"))
  174. // Flatten the diff layer into the bottom accumulator
  175. if err := snaps.Cap(common.HexToHash("0x03"), 1); err != nil {
  176. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  177. }
  178. // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
  179. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  180. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  181. }
  182. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  183. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  184. }
  185. if n := len(snaps.layers); n != 2 {
  186. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 2)
  187. fmt.Println(snaps.layers)
  188. }
  189. }
  190. // Tests that if a diff layer becomes stale, no active external references will
  191. // be returned with junk data. This version of the test retains the bottom diff
  192. // layer to check the usual mode of operation where the accumulator is retained.
  193. func TestDiffLayerExternalInvalidationPartialFlatten(t *testing.T) {
  194. // Create an empty base layer and a snapshot tree out of it
  195. base := &diskLayer{
  196. diskdb: rawdb.NewMemoryDatabase(),
  197. root: common.HexToHash("0x01"),
  198. cache: fastcache.New(1024 * 500),
  199. }
  200. snaps := &Tree{
  201. layers: map[common.Hash]snapshot{
  202. base.root: base,
  203. },
  204. }
  205. // Commit three diffs on top and retrieve a reference to the bottommost
  206. accounts := map[common.Hash][]byte{
  207. common.HexToHash("0xa1"): randomAccount(),
  208. }
  209. if err := snaps.Update(common.HexToHash("0x02"), common.HexToHash("0x01"), accounts, nil); err != nil {
  210. t.Fatalf("failed to create a diff layer: %v", err)
  211. }
  212. if err := snaps.Update(common.HexToHash("0x03"), common.HexToHash("0x02"), accounts, nil); err != nil {
  213. t.Fatalf("failed to create a diff layer: %v", err)
  214. }
  215. if err := snaps.Update(common.HexToHash("0x04"), common.HexToHash("0x03"), accounts, nil); err != nil {
  216. t.Fatalf("failed to create a diff layer: %v", err)
  217. }
  218. if n := len(snaps.layers); n != 4 {
  219. t.Errorf("pre-cap layer count mismatch: have %d, want %d", n, 4)
  220. }
  221. ref := snaps.Snapshot(common.HexToHash("0x02"))
  222. // Doing a Cap operation with many allowed layers should be a no-op
  223. exp := len(snaps.layers)
  224. if err := snaps.Cap(common.HexToHash("0x04"), 2000); err != nil {
  225. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  226. }
  227. if got := len(snaps.layers); got != exp {
  228. t.Errorf("layers modified, got %d exp %d", got, exp)
  229. }
  230. // Flatten the diff layer into the bottom accumulator
  231. if err := snaps.Cap(common.HexToHash("0x04"), 2); err != nil {
  232. t.Fatalf("failed to flatten diff layer into accumulator: %v", err)
  233. }
  234. // Since the accumulator diff layer was modified, ensure that data retrievald on the external reference fail
  235. if acc, err := ref.Account(common.HexToHash("0x01")); err != ErrSnapshotStale {
  236. t.Errorf("stale reference returned account: %#x (err: %v)", acc, err)
  237. }
  238. if slot, err := ref.Storage(common.HexToHash("0xa1"), common.HexToHash("0xb1")); err != ErrSnapshotStale {
  239. t.Errorf("stale reference returned storage slot: %#x (err: %v)", slot, err)
  240. }
  241. if n := len(snaps.layers); n != 3 {
  242. t.Errorf("post-cap layer count mismatch: have %d, want %d", n, 3)
  243. fmt.Println(snaps.layers)
  244. }
  245. }
  246. // TestPostCapBasicDataAccess tests some functionality regarding capping/flattening.
  247. func TestPostCapBasicDataAccess(t *testing.T) {
  248. // setAccount is a helper to construct a random account entry and assign it to
  249. // an account slot in a snapshot
  250. setAccount := func(accKey string) map[common.Hash][]byte {
  251. return map[common.Hash][]byte{
  252. common.HexToHash(accKey): randomAccount(),
  253. }
  254. }
  255. // Create a starting base layer and a snapshot tree out of it
  256. base := &diskLayer{
  257. diskdb: rawdb.NewMemoryDatabase(),
  258. root: common.HexToHash("0x01"),
  259. cache: fastcache.New(1024 * 500),
  260. }
  261. snaps := &Tree{
  262. layers: map[common.Hash]snapshot{
  263. base.root: base,
  264. },
  265. }
  266. // The lowest difflayer
  267. snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), setAccount("0xa1"), nil)
  268. snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), setAccount("0xa2"), nil)
  269. snaps.Update(common.HexToHash("0xb2"), common.HexToHash("0xa1"), setAccount("0xb2"), nil)
  270. snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), setAccount("0xa3"), nil)
  271. snaps.Update(common.HexToHash("0xb3"), common.HexToHash("0xb2"), setAccount("0xb3"), nil)
  272. // checkExist verifies if an account exiss in a snapshot
  273. checkExist := func(layer *diffLayer, key string) error {
  274. if data, _ := layer.Account(common.HexToHash(key)); data == nil {
  275. return fmt.Errorf("expected %x to exist, got nil", common.HexToHash(key))
  276. }
  277. return nil
  278. }
  279. // shouldErr checks that an account access errors as expected
  280. shouldErr := func(layer *diffLayer, key string) error {
  281. if data, err := layer.Account(common.HexToHash(key)); err == nil {
  282. return fmt.Errorf("expected error, got data %x", data)
  283. }
  284. return nil
  285. }
  286. // check basics
  287. snap := snaps.Snapshot(common.HexToHash("0xb3")).(*diffLayer)
  288. if err := checkExist(snap, "0xa1"); err != nil {
  289. t.Error(err)
  290. }
  291. if err := checkExist(snap, "0xb2"); err != nil {
  292. t.Error(err)
  293. }
  294. if err := checkExist(snap, "0xb3"); err != nil {
  295. t.Error(err)
  296. }
  297. // Cap to a bad root should fail
  298. if err := snaps.Cap(common.HexToHash("0x1337"), 0); err == nil {
  299. t.Errorf("expected error, got none")
  300. }
  301. // Now, merge the a-chain
  302. snaps.Cap(common.HexToHash("0xa3"), 0)
  303. // At this point, a2 got merged into a1. Thus, a1 is now modified, and as a1 is
  304. // the parent of b2, b2 should no longer be able to iterate into parent.
  305. // These should still be accessible
  306. if err := checkExist(snap, "0xb2"); err != nil {
  307. t.Error(err)
  308. }
  309. if err := checkExist(snap, "0xb3"); err != nil {
  310. t.Error(err)
  311. }
  312. // But these would need iteration into the modified parent
  313. if err := shouldErr(snap, "0xa1"); err != nil {
  314. t.Error(err)
  315. }
  316. if err := shouldErr(snap, "0xa2"); err != nil {
  317. t.Error(err)
  318. }
  319. if err := shouldErr(snap, "0xa3"); err != nil {
  320. t.Error(err)
  321. }
  322. // Now, merge it again, just for fun. It should now error, since a3
  323. // is a disk layer
  324. if err := snaps.Cap(common.HexToHash("0xa3"), 0); err == nil {
  325. t.Error("expected error capping the disk layer, got none")
  326. }
  327. }