github.com/onflow/flow-go@v0.33.17/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{}) 35 run2 := newSpockState(snapshot.MapStorageSnapshot{}) 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 111 state2 := newSpockState( 112 snapshot.MapStorageSnapshot{ 113 badRegisterId: value2, 114 }) 115 116 value, err := state1.Get(badRegisterId) 117 require.NoError(t, err) 118 require.Equal(t, value1, value) 119 120 value, err = state2.Get(badRegisterId) 121 require.NoError(t, err) 122 require.Equal(t, value2, value) 123 124 // state1 and state2 will have identical spock hash even through they read 125 // different values from the underlying storage. Merkle trie proof will 126 // ensure the underlying storage is correct / identical. 127 require.Equal( 128 t, 129 state1.Finalize().SpockSecret, 130 state2.Finalize().SpockSecret) 131 } 132 133 func TestSpockStateGetVsSetNil(t *testing.T) { 134 registerId := flow.NewRegisterID(fooOwner, "bar") 135 136 _ = testSpock( 137 t, 138 []spockTestOp{ 139 func(t *testing.T, state *spockState) { 140 err := state.Set(registerId, []byte{}) 141 require.NoError(t, err) 142 }, 143 func(t *testing.T, state *spockState) { 144 _, err := state.Get(registerId) 145 require.NoError(t, err) 146 }, 147 }) 148 } 149 150 func TestSpockStateSet(t *testing.T) { 151 otherOwner := unittest.RandomAddressFixture() 152 registerId := flow.NewRegisterID(fooOwner, "bar") 153 value := flow.RegisterValue([]byte("value")) 154 155 states := testSpock( 156 t, 157 []spockTestOp{ 158 // control experiment 159 nil, 160 // primary experiment 161 func(t *testing.T, state *spockState) { 162 err := state.Set(registerId, value) 163 require.NoError(t, err) 164 }, 165 // duplicate calls return in different spock 166 func(t *testing.T, state *spockState) { 167 err := state.Set(registerId, value) 168 require.NoError(t, err) 169 err = state.Set(registerId, value) 170 require.NoError(t, err) 171 }, 172 // Setting different register id will result in different spock 173 func(t *testing.T, state *spockState) { 174 err := state.Set(flow.NewRegisterID(fooOwner, "baR"), value) 175 require.NoError(t, err) 176 }, 177 func(t *testing.T, state *spockState) { 178 err := state.Set(flow.NewRegisterID(otherOwner, "bar"), value) 179 require.NoError(t, err) 180 }, 181 // Setting different register value will result in different spock 182 func(t *testing.T, state *spockState) { 183 err := state.Set(registerId, []byte("valuE")) 184 require.NoError(t, err) 185 }, 186 }) 187 188 // Sanity check underlying storage state is called. 189 require.Equal( 190 t, 191 map[flow.RegisterID]flow.RegisterValue{ 192 registerId: value, 193 }, 194 states[1].Finalize().WriteSet) 195 196 // Sanity check finalized state is no longer accessible. 197 err := states[1].Set(registerId, []byte("")) 198 require.ErrorContains(t, err, "cannot Set on a finalized state") 199 } 200 201 func TestSpockStateSetValueInjection(t *testing.T) { 202 registerId1 := flow.NewRegisterID(fooOwner, "injection") 203 registerId2 := flow.NewRegisterID(fooOwner, "inject") 204 205 _ = testSpock( 206 t, 207 []spockTestOp{ 208 func(t *testing.T, state *spockState) { 209 err := state.Set(registerId1, []byte{}) 210 require.NoError(t, err) 211 }, 212 func(t *testing.T, state *spockState) { 213 err := state.Set(registerId2, []byte("ion")) 214 require.NoError(t, err) 215 }, 216 }) 217 } 218 219 func TestSpockStateMerge(t *testing.T) { 220 readSet := map[flow.RegisterID]struct{}{ 221 flow.NewRegisterID(fooOwner, "bar"): struct{}{}, 222 } 223 224 states := testSpock( 225 t, 226 []spockTestOp{ 227 // control experiment 228 nil, 229 // primary experiment 230 func(t *testing.T, state *spockState) { 231 err := state.Merge( 232 &snapshot.ExecutionSnapshot{ 233 ReadSet: readSet, 234 SpockSecret: []byte("secret"), 235 }) 236 require.NoError(t, err) 237 }, 238 // duplicate calls result in different spock 239 func(t *testing.T, state *spockState) { 240 err := state.Merge( 241 &snapshot.ExecutionSnapshot{ 242 ReadSet: readSet, 243 SpockSecret: []byte("secret"), 244 }) 245 require.NoError(t, err) 246 err = state.Merge( 247 &snapshot.ExecutionSnapshot{ 248 ReadSet: readSet, 249 SpockSecret: []byte("secret"), 250 }) 251 require.NoError(t, err) 252 }, 253 // Merging execution snapshot with different spock will result in 254 // different spock 255 func(t *testing.T, state *spockState) { 256 err := state.Merge( 257 &snapshot.ExecutionSnapshot{ 258 ReadSet: readSet, 259 SpockSecret: []byte("secreT"), 260 }) 261 require.NoError(t, err) 262 }, 263 }) 264 265 // Sanity check underlying storage state is called. 266 require.Equal(t, readSet, states[1].Finalize().ReadSet) 267 268 // Sanity check finalized state is no longer accessible. 269 err := states[1].Merge(&snapshot.ExecutionSnapshot{}) 270 require.ErrorContains(t, err, "cannot Merge on a finalized state") 271 } 272 func TestSpockStateDropChanges(t *testing.T) { 273 registerId := flow.NewRegisterID(fooOwner, "read") 274 275 setup := func(t *testing.T, state *spockState) { 276 _, err := state.Get(registerId) 277 require.NoError(t, err) 278 279 err = state.Set(flow.NewRegisterID(fooOwner, "write"), []byte("blah")) 280 require.NoError(t, err) 281 } 282 283 states := testSpock( 284 t, 285 []spockTestOp{ 286 // control experiment 287 setup, 288 // primary experiment 289 func(t *testing.T, state *spockState) { 290 setup(t, state) 291 err := state.DropChanges() 292 require.NoError(t, err) 293 }, 294 // duplicate calls result in different spock 295 func(t *testing.T, state *spockState) { 296 setup(t, state) 297 err := state.DropChanges() 298 require.NoError(t, err) 299 err = state.DropChanges() 300 require.NoError(t, err) 301 }, 302 }) 303 304 // Sanity check underlying storage state is called. 305 snapshot := states[1].Finalize() 306 require.Equal( 307 t, 308 map[flow.RegisterID]struct{}{ 309 registerId: struct{}{}, 310 }, 311 snapshot.ReadSet) 312 require.Empty(t, snapshot.WriteSet) 313 314 // Sanity check finalized state is no longer accessible. 315 err := states[1].DropChanges() 316 require.ErrorContains(t, err, "cannot DropChanges on a finalized state") 317 } 318 319 func TestSpockStateRandomOps(t *testing.T) { 320 chain := []spockTestOp{ 321 nil, // control experiment 322 } 323 324 for i := 0; i < 500; i++ { 325 roll, err := rand.Uintn(4) 326 require.NoError(t, err) 327 328 switch roll { 329 case uint(0): 330 id, err := rand.Uint() 331 require.NoError(t, err) 332 333 chain = append( 334 chain, 335 chainSpockTestOps( 336 chain[len(chain)-1], 337 func(t *testing.T, state *spockState) { 338 _, err := state.Get( 339 flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id))) 340 require.NoError(t, err) 341 })) 342 case uint(1): 343 id, err := rand.Uint() 344 require.NoError(t, err) 345 346 value, err := rand.Uint() 347 require.NoError(t, err) 348 349 chain = append( 350 chain, 351 chainSpockTestOps( 352 chain[len(chain)-1], 353 func(t *testing.T, state *spockState) { 354 err := state.Set( 355 flow.NewRegisterID(flow.EmptyAddress, fmt.Sprintf("%d", id)), 356 []byte(fmt.Sprintf("%d", value))) 357 require.NoError(t, err) 358 })) 359 case uint(2): 360 spock, err := rand.Uint() 361 require.NoError(t, err) 362 363 chain = append( 364 chain, 365 chainSpockTestOps( 366 chain[len(chain)-1], 367 func(t *testing.T, state *spockState) { 368 err := state.Merge( 369 &snapshot.ExecutionSnapshot{ 370 SpockSecret: []byte(fmt.Sprintf("%d", spock)), 371 }) 372 require.NoError(t, err) 373 })) 374 case uint(3): 375 chain = append( 376 chain, 377 chainSpockTestOps( 378 chain[len(chain)-1], 379 func(t *testing.T, state *spockState) { 380 err := state.DropChanges() 381 require.NoError(t, err) 382 })) 383 default: 384 panic("Unexpected") 385 } 386 } 387 388 _ = testSpock(t, chain) 389 } 390 func TestSpockStateNewChild(t *testing.T) { 391 baseRegisterId := flow.NewRegisterID(flow.EmptyAddress, "base") 392 baseValue := flow.RegisterValue([]byte("base")) 393 394 parentOwner := unittest.RandomAddressFixture() 395 childOwner := unittest.RandomAddressFixture() 396 397 parentRegisterId1 := flow.NewRegisterID(parentOwner, "1") 398 parentValue := flow.RegisterValue([]byte("parent")) 399 400 parentRegisterId2 := flow.NewRegisterID(parentOwner, "2") 401 402 childRegisterId1 := flow.NewRegisterID(childOwner, "1") 403 childValue := flow.RegisterValue([]byte("child")) 404 405 childRegisterId2 := flow.NewRegisterID(childOwner, "2") 406 407 parent := newSpockState( 408 snapshot.MapStorageSnapshot{ 409 baseRegisterId: baseValue, 410 }) 411 412 err := parent.Set(parentRegisterId1, parentValue) 413 require.NoError(t, err) 414 415 value, err := parent.Get(parentRegisterId2) 416 require.NoError(t, err) 417 require.Nil(t, value) 418 419 child := parent.NewChild() 420 421 value, err = child.Get(baseRegisterId) 422 require.NoError(t, err) 423 require.Equal(t, value, baseValue) 424 425 value, err = child.Get(parentRegisterId1) 426 require.NoError(t, err) 427 require.Equal(t, value, parentValue) 428 429 value, err = child.Get(childRegisterId2) 430 require.NoError(t, err) 431 require.Nil(t, value) 432 433 err = child.Set(childRegisterId1, childValue) 434 require.NoError(t, err) 435 436 childSnapshot := child.Finalize() 437 require.Equal( 438 t, 439 childSnapshot.ReadSet, 440 map[flow.RegisterID]struct{}{ 441 baseRegisterId: struct{}{}, 442 parentRegisterId1: struct{}{}, 443 childRegisterId2: struct{}{}, 444 }) 445 446 require.Equal( 447 t, 448 childSnapshot.WriteSet, 449 map[flow.RegisterID]flow.RegisterValue{ 450 childRegisterId1: childValue, 451 }) 452 453 // Finalize parent without merging child to see if they are independent. 454 parentSnapshot := parent.Finalize() 455 require.Equal( 456 t, 457 parentSnapshot.ReadSet, 458 map[flow.RegisterID]struct{}{ 459 parentRegisterId2: struct{}{}, 460 }) 461 462 require.Equal( 463 t, 464 parentSnapshot.WriteSet, 465 map[flow.RegisterID]flow.RegisterValue{ 466 parentRegisterId1: parentValue, 467 }) 468 }