github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/fvm/storage/state/spock_state_test.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/onflow/flow-go/fvm/storage/snapshot" 10 "github.com/onflow/flow-go/model/flow" 11 "github.com/onflow/flow-go/utils/rand" 12 "github.com/onflow/flow-go/utils/unittest" 13 ) 14 15 type spockTestOp func(*testing.T, *spockState) 16 17 var fooOwner = unittest.RandomAddressFixture() 18 19 func chainSpockTestOps(prevOps spockTestOp, op spockTestOp) spockTestOp { 20 return func(t *testing.T, state *spockState) { 21 if prevOps != nil { 22 prevOps(t, state) 23 } 24 op(t, state) 25 } 26 } 27 28 func testSpock( 29 t *testing.T, 30 counterfactualExperiments []spockTestOp, 31 ) []*spockState { 32 resultStates := []*spockState{} 33 for _, experiment := range counterfactualExperiments { 34 run1 := newSpockState(snapshot.MapStorageSnapshot{}, DefaultSpockSecretHasher) 35 run2 := newSpockState(snapshot.MapStorageSnapshot{}, DefaultSpockSecretHasher) 36 37 if experiment != nil { 38 experiment(t, run1) 39 experiment(t, run2) 40 } 41 42 spock := run1.Finalize().SpockSecret 43 require.Equal(t, spock, run2.Finalize().SpockSecret) 44 45 for _, previous := range resultStates { 46 require.NotEqual(t, spock, previous.Finalize().SpockSecret) 47 } 48 49 resultStates = append(resultStates, run1) 50 } 51 52 return resultStates 53 } 54 55 func TestSpockStateGet(t *testing.T) { 56 otherOwner := unittest.RandomAddressFixture() 57 registerId := flow.NewRegisterID(fooOwner, "bar") 58 59 states := testSpock( 60 t, 61 []spockTestOp{ 62 // control experiment 63 nil, 64 // primary experiment 65 func(t *testing.T, state *spockState) { 66 _, err := state.Get(registerId) 67 require.NoError(t, err) 68 }, 69 // duplicate calls return in different spock 70 func(t *testing.T, state *spockState) { 71 _, err := state.Get(registerId) 72 require.NoError(t, err) 73 _, err = state.Get(registerId) 74 require.NoError(t, err) 75 }, 76 // Reading different register ids will result in different spock 77 func(t *testing.T, state *spockState) { 78 _, err := state.Get(flow.NewRegisterID(otherOwner, "bar")) 79 require.NoError(t, err) 80 }, 81 func(t *testing.T, state *spockState) { 82 _, err := state.Get(flow.NewRegisterID(fooOwner, "baR")) 83 require.NoError(t, err) 84 }, 85 }) 86 87 // Sanity check underlying storage state is called. 88 require.Equal( 89 t, 90 map[flow.RegisterID]struct{}{ 91 registerId: struct{}{}, 92 }, 93 states[1].Finalize().ReadSet) 94 95 // Sanity check finalized state is no longer accessible. 96 _, err := states[1].Get(registerId) 97 require.ErrorContains(t, err, "cannot Get on a finalized state") 98 } 99 100 func TestSpockStateGetDifferentUnderlyingStorage(t *testing.T) { 101 badRegisterId := flow.NewRegisterID(fooOwner, "bad") 102 103 value1 := flow.RegisterValue([]byte("abc")) 104 value2 := flow.RegisterValue([]byte("blah")) 105 106 state1 := newSpockState( 107 snapshot.MapStorageSnapshot{ 108 badRegisterId: value1, 109 }, 110 DefaultSpockSecretHasher, 111 ) 112 113 state2 := newSpockState( 114 snapshot.MapStorageSnapshot{ 115 badRegisterId: value2, 116 }, 117 DefaultSpockSecretHasher, 118 ) 119 120 value, err := state1.Get(badRegisterId) 121 require.NoError(t, err) 122 require.Equal(t, value1, value) 123 124 value, err = state2.Get(badRegisterId) 125 require.NoError(t, err) 126 require.Equal(t, value2, value) 127 128 // state1 and state2 will have identical spock hash even through they read 129 // different values from the underlying storage. Merkle trie proof will 130 // ensure the underlying storage is correct / identical. 131 require.Equal( 132 t, 133 state1.Finalize().SpockSecret, 134 state2.Finalize().SpockSecret) 135 } 136 137 func TestSpockStateGetVsSetNil(t *testing.T) { 138 registerId := flow.NewRegisterID(fooOwner, "bar") 139 140 _ = testSpock( 141 t, 142 []spockTestOp{ 143 func(t *testing.T, state *spockState) { 144 err := state.Set(registerId, []byte{}) 145 require.NoError(t, err) 146 }, 147 func(t *testing.T, state *spockState) { 148 _, err := state.Get(registerId) 149 require.NoError(t, err) 150 }, 151 }) 152 } 153 154 func TestSpockStateSet(t *testing.T) { 155 otherOwner := unittest.RandomAddressFixture() 156 registerId := flow.NewRegisterID(fooOwner, "bar") 157 value := flow.RegisterValue([]byte("value")) 158 159 states := testSpock( 160 t, 161 []spockTestOp{ 162 // control experiment 163 nil, 164 // primary experiment 165 func(t *testing.T, state *spockState) { 166 err := state.Set(registerId, value) 167 require.NoError(t, err) 168 }, 169 // duplicate calls return in different spock 170 func(t *testing.T, state *spockState) { 171 err := state.Set(registerId, value) 172 require.NoError(t, err) 173 err = state.Set(registerId, value) 174 require.NoError(t, err) 175 }, 176 // Setting different register id will result in different spock 177 func(t *testing.T, state *spockState) { 178 err := state.Set(flow.NewRegisterID(fooOwner, "baR"), value) 179 require.NoError(t, err) 180 }, 181 func(t *testing.T, state *spockState) { 182 err := state.Set(flow.NewRegisterID(otherOwner, "bar"), value) 183 require.NoError(t, err) 184 }, 185 // Setting different register value will result in different spock 186 func(t *testing.T, state *spockState) { 187 err := state.Set(registerId, []byte("valuE")) 188 require.NoError(t, err) 189 }, 190 }) 191 192 // Sanity check underlying storage state is called. 193 require.Equal( 194 t, 195 map[flow.RegisterID]flow.RegisterValue{ 196 registerId: value, 197 }, 198 states[1].Finalize().WriteSet) 199 200 // Sanity check finalized state is no longer accessible. 201 err := states[1].Set(registerId, []byte("")) 202 require.ErrorContains(t, err, "cannot Set on a finalized state") 203 } 204 205 func TestSpockStateSetValueInjection(t *testing.T) { 206 registerId1 := flow.NewRegisterID(fooOwner, "injection") 207 registerId2 := flow.NewRegisterID(fooOwner, "inject") 208 209 _ = testSpock( 210 t, 211 []spockTestOp{ 212 func(t *testing.T, state *spockState) { 213 err := state.Set(registerId1, []byte{}) 214 require.NoError(t, err) 215 }, 216 func(t *testing.T, state *spockState) { 217 err := state.Set(registerId2, []byte("ion")) 218 require.NoError(t, err) 219 }, 220 }) 221 } 222 223 func TestSpockStateMerge(t *testing.T) { 224 readSet := map[flow.RegisterID]struct{}{ 225 flow.NewRegisterID(fooOwner, "bar"): struct{}{}, 226 } 227 228 states := testSpock( 229 t, 230 []spockTestOp{ 231 // control experiment 232 nil, 233 // primary experiment 234 func(t *testing.T, state *spockState) { 235 err := state.Merge( 236 &snapshot.ExecutionSnapshot{ 237 ReadSet: readSet, 238 SpockSecret: []byte("secret"), 239 }) 240 require.NoError(t, err) 241 }, 242 // duplicate calls result in different spock 243 func(t *testing.T, state *spockState) { 244 err := state.Merge( 245 &snapshot.ExecutionSnapshot{ 246 ReadSet: readSet, 247 SpockSecret: []byte("secret"), 248 }) 249 require.NoError(t, err) 250 err = state.Merge( 251 &snapshot.ExecutionSnapshot{ 252 ReadSet: readSet, 253 SpockSecret: []byte("secret"), 254 }) 255 require.NoError(t, err) 256 }, 257 // Merging execution snapshot with different spock will result in 258 // different spock 259 func(t *testing.T, state *spockState) { 260 err := state.Merge( 261 &snapshot.ExecutionSnapshot{ 262 ReadSet: readSet, 263 SpockSecret: []byte("secreT"), 264 }) 265 require.NoError(t, err) 266 }, 267 }) 268 269 // Sanity check underlying storage state is called. 270 require.Equal(t, readSet, states[1].Finalize().ReadSet) 271 272 // Sanity check finalized state is no longer accessible. 273 err := states[1].Merge(&snapshot.ExecutionSnapshot{}) 274 require.ErrorContains(t, err, "cannot Merge on a finalized state") 275 } 276 func TestSpockStateDropChanges(t *testing.T) { 277 registerId := flow.NewRegisterID(fooOwner, "read") 278 279 setup := func(t *testing.T, state *spockState) { 280 _, err := state.Get(registerId) 281 require.NoError(t, err) 282 283 err = state.Set(flow.NewRegisterID(fooOwner, "write"), []byte("blah")) 284 require.NoError(t, err) 285 } 286 287 states := testSpock( 288 t, 289 []spockTestOp{ 290 // control experiment 291 setup, 292 // primary experiment 293 func(t *testing.T, state *spockState) { 294 setup(t, state) 295 err := state.DropChanges() 296 require.NoError(t, err) 297 }, 298 // duplicate calls result in different spock 299 func(t *testing.T, state *spockState) { 300 setup(t, state) 301 err := state.DropChanges() 302 require.NoError(t, err) 303 err = state.DropChanges() 304 require.NoError(t, err) 305 }, 306 }) 307 308 // Sanity check underlying storage state is called. 309 snapshot := states[1].Finalize() 310 require.Equal( 311 t, 312 map[flow.RegisterID]struct{}{ 313 registerId: struct{}{}, 314 }, 315 snapshot.ReadSet) 316 require.Empty(t, snapshot.WriteSet) 317 318 // Sanity check finalized state is no longer accessible. 319 err := states[1].DropChanges() 320 require.ErrorContains(t, err, "cannot DropChanges on a finalized state") 321 } 322 323 func TestSpockStateRandomOps(t *testing.T) { 324 chain := []spockTestOp{ 325 nil, // control experiment 326 } 327 328 for i := 0; i < 500; i++ { 329 roll, err := rand.Uintn(4) 330 require.NoError(t, err) 331 332 switch roll { 333 case uint(0): 334 id, err := rand.Uint() 335 require.NoError(t, err) 336 337 chain = append( 338 chain, 339 chainSpockTestOps( 340 chain[len(chain)-1], 341 func(t *testing.T, state *spockState) { 342 _, err := state.Get( 343 flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id))) 344 require.NoError(t, err) 345 })) 346 case uint(1): 347 id, err := rand.Uint() 348 require.NoError(t, err) 349 350 value, err := rand.Uint() 351 require.NoError(t, err) 352 353 chain = append( 354 chain, 355 chainSpockTestOps( 356 chain[len(chain)-1], 357 func(t *testing.T, state *spockState) { 358 err := state.Set( 359 flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)), 360 []byte(fmt.Sprintf("%d", value))) 361 require.NoError(t, err) 362 })) 363 case uint(2): 364 spock, err := rand.Uint() 365 require.NoError(t, err) 366 367 chain = append( 368 chain, 369 chainSpockTestOps( 370 chain[len(chain)-1], 371 func(t *testing.T, state *spockState) { 372 err := state.Merge( 373 &snapshot.ExecutionSnapshot{ 374 SpockSecret: []byte(fmt.Sprintf("%d", spock)), 375 }) 376 require.NoError(t, err) 377 })) 378 case uint(3): 379 chain = append( 380 chain, 381 chainSpockTestOps( 382 chain[len(chain)-1], 383 func(t *testing.T, state *spockState) { 384 err := state.DropChanges() 385 require.NoError(t, err) 386 })) 387 default: 388 panic("Unexpected") 389 } 390 } 391 392 _ = testSpock(t, chain) 393 } 394 func TestSpockStateNewChild(t *testing.T) { 395 baseRegisterId := flow.NewRegisterID(flow.EmptyAddress, "base") 396 baseValue := flow.RegisterValue([]byte("base")) 397 398 parentOwner := unittest.RandomAddressFixture() 399 childOwner := unittest.RandomAddressFixture() 400 401 parentRegisterId1 := flow.NewRegisterID(parentOwner, "1") 402 parentValue := flow.RegisterValue([]byte("parent")) 403 404 parentRegisterId2 := flow.NewRegisterID(parentOwner, "2") 405 406 childRegisterId1 := flow.NewRegisterID(childOwner, "1") 407 childValue := flow.RegisterValue([]byte("child")) 408 409 childRegisterId2 := flow.NewRegisterID(childOwner, "2") 410 411 parent := newSpockState( 412 snapshot.MapStorageSnapshot{ 413 baseRegisterId: baseValue, 414 }, 415 DefaultSpockSecretHasher, 416 ) 417 418 err := parent.Set(parentRegisterId1, parentValue) 419 require.NoError(t, err) 420 421 value, err := parent.Get(parentRegisterId2) 422 require.NoError(t, err) 423 require.Nil(t, value) 424 425 child := parent.NewChild() 426 427 value, err = child.Get(baseRegisterId) 428 require.NoError(t, err) 429 require.Equal(t, value, baseValue) 430 431 value, err = child.Get(parentRegisterId1) 432 require.NoError(t, err) 433 require.Equal(t, value, parentValue) 434 435 value, err = child.Get(childRegisterId2) 436 require.NoError(t, err) 437 require.Nil(t, value) 438 439 err = child.Set(childRegisterId1, childValue) 440 require.NoError(t, err) 441 442 childSnapshot := child.Finalize() 443 require.Equal( 444 t, 445 childSnapshot.ReadSet, 446 map[flow.RegisterID]struct{}{ 447 baseRegisterId: struct{}{}, 448 parentRegisterId1: struct{}{}, 449 childRegisterId2: struct{}{}, 450 }) 451 452 require.Equal( 453 t, 454 childSnapshot.WriteSet, 455 map[flow.RegisterID]flow.RegisterValue{ 456 childRegisterId1: childValue, 457 }) 458 459 // Finalize parent without merging child to see if they are independent. 460 parentSnapshot := parent.Finalize() 461 require.Equal( 462 t, 463 parentSnapshot.ReadSet, 464 map[flow.RegisterID]struct{}{ 465 parentRegisterId2: struct{}{}, 466 }) 467 468 require.Equal( 469 t, 470 parentSnapshot.WriteSet, 471 map[flow.RegisterID]flow.RegisterValue{ 472 parentRegisterId1: parentValue, 473 }) 474 }