github.com/koko1123/flow-go-1@v0.29.6/fvm/state/transaction_state_test.go (about) 1 package state_test 2 3 import ( 4 "math" 5 "testing" 6 7 "github.com/onflow/cadence/runtime/common" 8 "github.com/stretchr/testify/require" 9 10 "github.com/koko1123/flow-go-1/fvm/meter" 11 "github.com/koko1123/flow-go-1/fvm/state" 12 "github.com/koko1123/flow-go-1/fvm/utils" 13 ) 14 15 func newTestTransactionState() *state.TransactionState { 16 return state.NewTransactionState( 17 utils.NewSimpleView(), 18 state.DefaultParameters(), 19 ) 20 } 21 22 func TestUnrestrictedNestedTransactionBasic(t *testing.T) { 23 txn := newTestTransactionState() 24 25 mainState := txn.MainTransactionId().StateForTestingOnly() 26 27 require.Equal(t, 0, txn.NumNestedTransactions()) 28 require.False(t, txn.IsParseRestricted()) 29 30 id1, err := txn.BeginNestedTransaction() 31 require.NoError(t, err) 32 33 require.Equal(t, 1, txn.NumNestedTransactions()) 34 require.False(t, txn.IsParseRestricted()) 35 36 require.True(t, txn.IsCurrent(id1)) 37 38 nestedState1 := id1.StateForTestingOnly() 39 40 id2, err := txn.BeginNestedTransaction() 41 require.NoError(t, err) 42 43 require.Equal(t, 2, txn.NumNestedTransactions()) 44 require.False(t, txn.IsParseRestricted()) 45 46 require.False(t, txn.IsCurrent(id1)) 47 require.True(t, txn.IsCurrent(id2)) 48 49 nestedState2 := id2.StateForTestingOnly() 50 51 // Ensure the values are written to the correctly nested state 52 53 addr := "address" 54 key := "key" 55 val := createByteArray(2) 56 57 err = txn.Set(addr, key, val, true) 58 require.NoError(t, err) 59 60 v, err := nestedState2.Get(addr, key, true) 61 require.NoError(t, err) 62 require.Equal(t, val, v) 63 64 v, err = nestedState1.Get(addr, key, true) 65 require.NoError(t, err) 66 require.Nil(t, v) 67 68 v, err = mainState.Get(addr, key, true) 69 require.NoError(t, err) 70 require.Nil(t, v) 71 72 // Ensure nested transactions are merged correctly 73 74 _, err = txn.Commit(id2) 75 require.NoError(t, err) 76 77 require.Equal(t, 1, txn.NumNestedTransactions()) 78 require.True(t, txn.IsCurrent(id1)) 79 80 v, err = nestedState1.Get(addr, key, true) 81 require.NoError(t, err) 82 require.Equal(t, val, v) 83 84 v, err = mainState.Get(addr, key, true) 85 require.NoError(t, err) 86 require.Nil(t, v) 87 88 _, err = txn.Commit(id1) 89 require.NoError(t, err) 90 91 require.Equal(t, 0, txn.NumNestedTransactions()) 92 require.True(t, txn.IsCurrent(txn.MainTransactionId())) 93 94 v, err = mainState.Get(addr, key, true) 95 require.NoError(t, err) 96 require.Equal(t, val, v) 97 } 98 99 func TestUnrestrictedNestedTransactionDifferentMeterParams(t *testing.T) { 100 txn := newTestTransactionState() 101 102 mainState := txn.MainTransactionId().StateForTestingOnly() 103 104 require.Equal(t, uint(math.MaxUint), mainState.TotalMemoryLimit()) 105 106 id1, err := txn.BeginNestedTransactionWithMeterParams( 107 meter.DefaultParameters().WithMemoryLimit(1)) 108 require.NoError(t, err) 109 110 nestedState1 := id1.StateForTestingOnly() 111 112 require.Equal(t, uint(1), nestedState1.TotalMemoryLimit()) 113 114 id2, err := txn.BeginNestedTransactionWithMeterParams( 115 meter.DefaultParameters().WithMemoryLimit(2)) 116 require.NoError(t, err) 117 118 nestedState2 := id2.StateForTestingOnly() 119 120 require.Equal(t, uint(2), nestedState2.TotalMemoryLimit()) 121 122 // inherits memory limit from parent 123 124 id3, err := txn.BeginNestedTransaction() 125 require.NoError(t, err) 126 127 nestedState3 := id3.StateForTestingOnly() 128 129 require.Equal(t, uint(2), nestedState3.TotalMemoryLimit()) 130 } 131 132 func TestParseRestrictedNestedTransactionBasic(t *testing.T) { 133 txn := newTestTransactionState() 134 135 mainId := txn.MainTransactionId() 136 mainState := mainId.StateForTestingOnly() 137 138 require.Equal(t, 0, txn.NumNestedTransactions()) 139 require.False(t, txn.IsParseRestricted()) 140 141 id1, err := txn.BeginNestedTransaction() 142 require.NoError(t, err) 143 144 require.Equal(t, 1, txn.NumNestedTransactions()) 145 require.False(t, txn.IsParseRestricted()) 146 147 nestedState := id1.StateForTestingOnly() 148 149 loc1 := common.AddressLocation{ 150 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 151 Name: "loc1", 152 } 153 154 restrictedId1, err := txn.BeginParseRestrictedNestedTransaction(loc1) 155 require.NoError(t, err) 156 157 require.Equal(t, 2, txn.NumNestedTransactions()) 158 require.True(t, txn.IsParseRestricted()) 159 160 restrictedNestedState1 := restrictedId1.StateForTestingOnly() 161 162 loc2 := common.AddressLocation{ 163 Address: common.MustBytesToAddress([]byte{2, 2, 2}), 164 Name: "loc2", 165 } 166 167 restrictedId2, err := txn.BeginParseRestrictedNestedTransaction(loc2) 168 require.NoError(t, err) 169 170 require.Equal(t, 3, txn.NumNestedTransactions()) 171 require.True(t, txn.IsParseRestricted()) 172 173 restrictedNestedState2 := restrictedId2.StateForTestingOnly() 174 175 // Sanity check 176 177 addr := "address" 178 key := "key" 179 180 v, err := restrictedNestedState2.Get(addr, key, true) 181 require.NoError(t, err) 182 require.Nil(t, v) 183 184 v, err = restrictedNestedState1.Get(addr, key, true) 185 require.NoError(t, err) 186 require.Nil(t, v) 187 188 v, err = nestedState.Get(addr, key, true) 189 require.NoError(t, err) 190 require.Nil(t, v) 191 192 v, err = mainState.Get(addr, key, true) 193 require.NoError(t, err) 194 require.Nil(t, v) 195 196 // Ensures attaching and committing cached nested transaction works 197 198 val := createByteArray(2) 199 200 cachedState := state.NewState( 201 utils.NewSimpleView(), 202 state.DefaultParameters(), 203 ) 204 205 err = cachedState.Set(addr, key, val, true) 206 require.NoError(t, err) 207 208 err = txn.AttachAndCommit(cachedState) 209 require.NoError(t, err) 210 211 require.Equal(t, 3, txn.NumNestedTransactions()) 212 require.True(t, txn.IsCurrent(restrictedId2)) 213 214 v, err = restrictedNestedState2.Get(addr, key, true) 215 require.NoError(t, err) 216 require.Equal(t, val, v) 217 218 v, err = restrictedNestedState1.Get(addr, key, true) 219 require.NoError(t, err) 220 require.Nil(t, v) 221 222 v, err = nestedState.Get(addr, key, true) 223 require.NoError(t, err) 224 require.Nil(t, v) 225 226 v, err = mainState.Get(addr, key, true) 227 require.NoError(t, err) 228 require.Nil(t, v) 229 230 // Ensure nested transactions are merged correctly 231 232 state, err := txn.CommitParseRestricted(loc2) 233 require.NoError(t, err) 234 require.Equal(t, restrictedNestedState2, state) 235 236 require.Equal(t, 2, txn.NumNestedTransactions()) 237 require.True(t, txn.IsCurrent(restrictedId1)) 238 239 v, err = restrictedNestedState1.Get(addr, key, true) 240 require.NoError(t, err) 241 require.Equal(t, val, v) 242 243 v, err = nestedState.Get(addr, key, true) 244 require.NoError(t, err) 245 require.Nil(t, v) 246 247 v, err = mainState.Get(addr, key, true) 248 require.NoError(t, err) 249 require.Nil(t, v) 250 251 state, err = txn.CommitParseRestricted(loc1) 252 require.NoError(t, err) 253 require.Equal(t, restrictedNestedState1, state) 254 255 require.Equal(t, 1, txn.NumNestedTransactions()) 256 require.True(t, txn.IsCurrent(id1)) 257 258 v, err = nestedState.Get(addr, key, true) 259 require.NoError(t, err) 260 require.Equal(t, val, v) 261 262 v, err = mainState.Get(addr, key, true) 263 require.NoError(t, err) 264 require.Nil(t, v) 265 266 _, err = txn.Commit(id1) 267 require.NoError(t, err) 268 269 require.Equal(t, 0, txn.NumNestedTransactions()) 270 require.True(t, txn.IsCurrent(mainId)) 271 272 v, err = mainState.Get(addr, key, true) 273 require.NoError(t, err) 274 require.Equal(t, val, v) 275 } 276 277 func TestRestartNestedTransaction(t *testing.T) { 278 txn := newTestTransactionState() 279 280 require.Equal(t, 0, txn.NumNestedTransactions()) 281 282 id, err := txn.BeginNestedTransaction() 283 require.NoError(t, err) 284 285 addr := "address" 286 key := "key" 287 val := createByteArray(2) 288 289 for i := 0; i < 10; i++ { 290 _, err := txn.BeginNestedTransaction() 291 require.NoError(t, err) 292 293 err = txn.Set(addr, key, val, true) 294 require.NoError(t, err) 295 } 296 297 loc := common.AddressLocation{ 298 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 299 Name: "loc", 300 } 301 302 for i := 0; i < 5; i++ { 303 _, err := txn.BeginParseRestrictedNestedTransaction(loc) 304 require.NoError(t, err) 305 306 err = txn.Set(addr, key, val, true) 307 require.NoError(t, err) 308 } 309 310 require.Equal(t, 16, txn.NumNestedTransactions()) 311 312 state := id.StateForTestingOnly() 313 require.Equal(t, uint64(0), state.InteractionUsed()) 314 315 // Restart will merge the meter stat, but not the view delta 316 317 err = txn.RestartNestedTransaction(id) 318 require.NoError(t, err) 319 320 require.Equal(t, 1, txn.NumNestedTransactions()) 321 require.True(t, txn.IsCurrent(id)) 322 323 require.Greater(t, state.InteractionUsed(), uint64(0)) 324 325 v, err := state.Get(addr, key, true) 326 require.NoError(t, err) 327 require.Nil(t, v) 328 } 329 330 func TestRestartNestedTransactionWithInvalidId(t *testing.T) { 331 txn := newTestTransactionState() 332 333 require.Equal(t, 0, txn.NumNestedTransactions()) 334 335 id, err := txn.BeginNestedTransaction() 336 require.NoError(t, err) 337 338 addr := "address" 339 key := "key" 340 val := createByteArray(2) 341 342 err = txn.Set(addr, key, val, true) 343 require.NoError(t, err) 344 345 var otherId state.NestedTransactionId 346 for i := 0; i < 10; i++ { 347 otherId, err = txn.BeginNestedTransaction() 348 require.NoError(t, err) 349 350 _, err = txn.Commit(otherId) 351 require.NoError(t, err) 352 } 353 354 require.True(t, txn.IsCurrent(id)) 355 356 err = txn.RestartNestedTransaction(otherId) 357 require.Error(t, err) 358 359 require.True(t, txn.IsCurrent(id)) 360 361 v, err := txn.Get(addr, key, true) 362 require.NoError(t, err) 363 require.Equal(t, val, v) 364 } 365 366 func TestUnrestrictedCannotCommitParseRestricted(t *testing.T) { 367 txn := newTestTransactionState() 368 369 loc := common.AddressLocation{ 370 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 371 Name: "loc", 372 } 373 374 id, err := txn.BeginNestedTransaction() 375 require.NoError(t, err) 376 377 require.Equal(t, 1, txn.NumNestedTransactions()) 378 require.False(t, txn.IsParseRestricted()) 379 380 _, err = txn.CommitParseRestricted(loc) 381 require.Error(t, err) 382 383 require.Equal(t, 1, txn.NumNestedTransactions()) 384 require.True(t, txn.IsCurrent(id)) 385 } 386 387 func TestUnrestrictedCannotCommitMainTransaction(t *testing.T) { 388 txn := newTestTransactionState() 389 390 id1, err := txn.BeginNestedTransaction() 391 require.NoError(t, err) 392 393 id2, err := txn.BeginNestedTransaction() 394 require.NoError(t, err) 395 396 require.Equal(t, 2, txn.NumNestedTransactions()) 397 398 _, err = txn.Commit(id1) 399 require.Error(t, err) 400 401 require.Equal(t, 2, txn.NumNestedTransactions()) 402 require.True(t, txn.IsCurrent(id2)) 403 } 404 405 func TestUnrestrictedCannotCommitUnexpectedNested(t *testing.T) { 406 txn := newTestTransactionState() 407 408 mainId := txn.MainTransactionId() 409 410 require.Equal(t, 0, txn.NumNestedTransactions()) 411 412 _, err := txn.Commit(mainId) 413 require.Error(t, err) 414 415 require.Equal(t, 0, txn.NumNestedTransactions()) 416 require.True(t, txn.IsCurrent(mainId)) 417 } 418 419 func TestParseRestrictedCannotBeginUnrestrictedNestedTransaction(t *testing.T) { 420 txn := newTestTransactionState() 421 422 loc := common.AddressLocation{ 423 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 424 Name: "loc", 425 } 426 427 id1, err := txn.BeginParseRestrictedNestedTransaction(loc) 428 require.NoError(t, err) 429 430 require.Equal(t, 1, txn.NumNestedTransactions()) 431 432 id2, err := txn.BeginNestedTransaction() 433 require.Error(t, err) 434 435 require.Equal(t, 1, txn.NumNestedTransactions()) 436 require.True(t, txn.IsCurrent(id1)) 437 require.False(t, txn.IsCurrent(id2)) 438 } 439 440 func TestParseRestrictedCannotCommitUnrestricted(t *testing.T) { 441 txn := newTestTransactionState() 442 443 loc := common.AddressLocation{ 444 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 445 Name: "loc", 446 } 447 448 id, err := txn.BeginParseRestrictedNestedTransaction(loc) 449 require.NoError(t, err) 450 451 require.Equal(t, 1, txn.NumNestedTransactions()) 452 453 _, err = txn.Commit(id) 454 require.Error(t, err) 455 456 require.Equal(t, 1, txn.NumNestedTransactions()) 457 require.True(t, txn.IsCurrent(id)) 458 } 459 460 func TestParseRestrictedCannotCommitLocationMismatch(t *testing.T) { 461 txn := newTestTransactionState() 462 463 loc := common.AddressLocation{ 464 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 465 Name: "loc", 466 } 467 468 id, err := txn.BeginParseRestrictedNestedTransaction(loc) 469 require.NoError(t, err) 470 471 require.Equal(t, 1, txn.NumNestedTransactions()) 472 473 other := common.AddressLocation{ 474 Address: common.MustBytesToAddress([]byte{1, 1, 1}), 475 Name: "other", 476 } 477 478 cacheableState, err := txn.CommitParseRestricted(other) 479 require.Error(t, err) 480 require.Nil(t, cacheableState) 481 482 require.Equal(t, 1, txn.NumNestedTransactions()) 483 require.True(t, txn.IsCurrent(id)) 484 } 485 486 func TestPauseAndResume(t *testing.T) { 487 txn := newTestTransactionState() 488 489 val, err := txn.Get("addr", "key", true) 490 require.NoError(t, err) 491 require.Nil(t, val) 492 493 id1, err := txn.BeginNestedTransaction() 494 require.NoError(t, err) 495 496 err = txn.Set("addr", "key", createByteArray(2), true) 497 require.NoError(t, err) 498 499 val, err = txn.Get("addr", "key", true) 500 require.NoError(t, err) 501 require.NotNil(t, val) 502 503 pausedState, err := txn.Pause(id1) 504 require.NoError(t, err) 505 506 val, err = txn.Get("addr", "key", true) 507 require.NoError(t, err) 508 require.Nil(t, val) 509 510 txn.Resume(pausedState) 511 512 val, err = txn.Get("addr", "key", true) 513 require.NoError(t, err) 514 require.NotNil(t, val) 515 516 err = txn.Set("addr2", "key2", createByteArray(2), true) 517 require.NoError(t, err) 518 519 _, err = txn.Commit(id1) 520 require.NoError(t, err) 521 522 val, err = txn.Get("addr2", "key2", true) 523 require.NoError(t, err) 524 require.NotNil(t, val) 525 } 526 527 func TestInvalidCommittedStateModification(t *testing.T) { 528 txn := newTestTransactionState() 529 530 id1, err := txn.BeginNestedTransaction() 531 require.NoError(t, err) 532 533 err = txn.Set("addr", "key", createByteArray(2), true) 534 require.NoError(t, err) 535 536 _, err = txn.Get("addr", "key", true) 537 require.NoError(t, err) 538 539 committedState, err := txn.Commit(id1) 540 require.NoError(t, err) 541 542 err = committedState.MergeState( 543 state.NewState(utils.NewSimpleView(), state.DefaultParameters())) 544 require.ErrorContains(t, err, "cannot MergeState on a committed state") 545 546 txn.Resume(committedState) 547 548 err = txn.Set("addr", "key", createByteArray(2), true) 549 require.ErrorContains(t, err, "cannot Set on a committed state") 550 551 _, err = txn.Get("addr", "key", true) 552 require.ErrorContains(t, err, "cannot Get on a committed state") 553 554 _, err = txn.Commit(id1) 555 require.NoError(t, err) 556 }