github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/migrations/cadence_value_diff.go (about) 1 package migrations 2 3 import ( 4 "fmt" 5 6 "github.com/onflow/cadence/runtime/common" 7 "github.com/onflow/cadence/runtime/interpreter" 8 9 "github.com/onflow/flow-go/cmd/util/ledger/reporters" 10 "github.com/onflow/flow-go/cmd/util/ledger/util/registers" 11 "github.com/onflow/flow-go/ledger" 12 "github.com/onflow/flow-go/model/flow" 13 ) 14 15 type diffKind int 16 17 const ( 18 storageMapExistDiffKind diffKind = iota // Storage map only exists in one state 19 storageMapKeyDiffKind // Storage map keys are different 20 storageMapValueDiffKind // Storage map values are different (only with verbose logging) 21 cadenceValueDiffKind // Cadence values are different 22 cadenceValueTypeDiffKind // Cadence value types are different 23 cadenceValueStaticTypeDiffKind // Cadence value static types are different 24 ) 25 26 var diffKindString = map[diffKind]string{ 27 storageMapExistDiffKind: "storage_map_exist_diff", 28 storageMapKeyDiffKind: "storage_map_key_diff", 29 storageMapValueDiffKind: "storage_map_value_diff", 30 cadenceValueDiffKind: "cadence_value_diff", 31 cadenceValueTypeDiffKind: "cadence_value_type_diff", 32 cadenceValueStaticTypeDiffKind: "cadence_value_static_type_diff", 33 } 34 35 type diffErrorKind int 36 37 const ( 38 abortErrorKind diffErrorKind = iota 39 storageMapKeyNotImplementingStorageMapKeyDiffErrorKind 40 cadenceValueNotImplementEquatableValueDiffErrorKind 41 ) 42 43 var diffErrorKindString = map[diffErrorKind]string{ 44 abortErrorKind: "error_diff_failed", 45 storageMapKeyNotImplementingStorageMapKeyDiffErrorKind: "error_storage_map_key_not_implemeting_StorageMapKey", 46 cadenceValueNotImplementEquatableValueDiffErrorKind: "error_cadence_value_not_implementing_EquatableValue", 47 } 48 49 type diffError struct { 50 Address string 51 Kind string 52 Msg string 53 } 54 55 type diffProblem struct { 56 Address string 57 Domain string 58 Kind string 59 Msg string 60 Trace string `json:",omitempty"` 61 } 62 63 type difference struct { 64 Address string 65 Domain string 66 Kind string 67 Msg string 68 Trace string `json:",omitempty"` 69 OldValue string `json:",omitempty"` 70 NewValue string `json:",omitempty"` 71 OldValueStaticType string `json:",omitempty"` 72 NewValueStaticType string `json:",omitempty"` 73 } 74 75 type CadenceValueDiffReporter struct { 76 address common.Address 77 chainID flow.ChainID 78 reportWriter reporters.ReportWriter 79 verboseLogging bool 80 nWorkers int 81 } 82 83 func NewCadenceValueDiffReporter( 84 address common.Address, 85 chainID flow.ChainID, 86 rw reporters.ReportWriter, 87 verboseLogging bool, 88 nWorkers int, 89 ) *CadenceValueDiffReporter { 90 return &CadenceValueDiffReporter{ 91 address: address, 92 chainID: chainID, 93 reportWriter: rw, 94 verboseLogging: verboseLogging, 95 nWorkers: nWorkers, 96 } 97 } 98 99 func (dr *CadenceValueDiffReporter) newStorageRuntime( 100 payloads []*ledger.Payload, 101 ) ( 102 *InterpreterMigrationRuntime, 103 registers.Registers, 104 error, 105 ) { 106 registersByAccount, err := registers.NewByAccountFromPayloads(payloads) 107 if err != nil { 108 return nil, nil, err 109 } 110 111 // TODO: maybe make read-only again 112 runtimeInterface, err := NewInterpreterMigrationRuntime( 113 registersByAccount, 114 dr.chainID, 115 InterpreterMigrationRuntimeConfig{}, 116 ) 117 if err != nil { 118 return nil, nil, err 119 } 120 121 return runtimeInterface, registersByAccount, nil 122 } 123 124 func (dr *CadenceValueDiffReporter) DiffStates(oldPayloads, newPayloads []*ledger.Payload, domains []string) { 125 // Create all the runtime components we need for comparing Cadence values. 126 oldRuntime, oldRegs, err := dr.newStorageRuntime(oldPayloads) 127 if err != nil { 128 dr.reportWriter.Write( 129 diffError{ 130 Address: dr.address.Hex(), 131 Kind: diffErrorKindString[abortErrorKind], 132 Msg: fmt.Sprintf("failed to create runtime with old state payloads: %s", err), 133 }) 134 return 135 } 136 137 err = loadAtreeSlabsInStorage(oldRuntime.Storage, oldRegs, dr.nWorkers) 138 if err != nil { 139 dr.reportWriter.Write( 140 diffError{ 141 Address: dr.address.Hex(), 142 Kind: diffErrorKindString[abortErrorKind], 143 Msg: fmt.Sprintf("failed to preload old state atree payloads: %s", err), 144 }) 145 return 146 } 147 148 newRuntime, newRegs, err := dr.newStorageRuntime(newPayloads) 149 if err != nil { 150 dr.reportWriter.Write( 151 diffError{ 152 Address: dr.address.Hex(), 153 Kind: diffErrorKindString[abortErrorKind], 154 Msg: fmt.Sprintf("failed to create runtime with new state payloads: %s", err), 155 }) 156 return 157 } 158 159 err = loadAtreeSlabsInStorage(newRuntime.Storage, newRegs, dr.nWorkers) 160 if err != nil { 161 dr.reportWriter.Write( 162 diffError{ 163 Address: dr.address.Hex(), 164 Kind: diffErrorKindString[abortErrorKind], 165 Msg: fmt.Sprintf("failed to preload new state atree payloads: %s", err), 166 }) 167 return 168 } 169 170 // Iterate through all domains and compare cadence values. 171 for _, domain := range domains { 172 dr.diffStorageDomain(oldRuntime, newRuntime, domain) 173 } 174 } 175 176 func (dr *CadenceValueDiffReporter) diffStorageDomain( 177 oldRuntime *InterpreterMigrationRuntime, 178 newRuntime *InterpreterMigrationRuntime, 179 domain string, 180 ) { 181 oldStorageMap := oldRuntime.Storage.GetStorageMap(dr.address, domain, false) 182 newStorageMap := newRuntime.Storage.GetStorageMap(dr.address, domain, false) 183 184 if oldStorageMap == nil && newStorageMap == nil { 185 // No storage maps for this domain. 186 return 187 } 188 189 if oldStorageMap == nil && newStorageMap != nil { 190 dr.reportWriter.Write( 191 difference{ 192 Address: dr.address.Hex(), 193 Domain: domain, 194 Kind: diffKindString[storageMapExistDiffKind], 195 Msg: fmt.Sprintf( 196 "old storage map doesn't exist, new storage map has %d elements with keys %v", 197 newStorageMap.Count(), 198 getStorageMapKeys(newStorageMap), 199 ), 200 }) 201 202 return 203 } 204 205 if oldStorageMap != nil && newStorageMap == nil { 206 dr.reportWriter.Write( 207 difference{ 208 Address: dr.address.Hex(), 209 Domain: domain, 210 Kind: diffKindString[storageMapExistDiffKind], 211 Msg: fmt.Sprintf( 212 "new storage map doesn't exist, old storage map has %d elements with keys %v", 213 oldStorageMap.Count(), 214 getStorageMapKeys(oldStorageMap), 215 ), 216 }) 217 218 return 219 } 220 221 oldKeys := getStorageMapKeys(oldStorageMap) 222 newKeys := getStorageMapKeys(newStorageMap) 223 224 onlyOldKeys, onlyNewKeys, sharedKeys := diff(oldKeys, newKeys) 225 226 // Log keys only present in old storage map 227 if len(onlyOldKeys) > 0 { 228 dr.reportWriter.Write( 229 difference{ 230 Address: dr.address.Hex(), 231 Domain: domain, 232 Kind: diffKindString[storageMapKeyDiffKind], 233 Msg: fmt.Sprintf( 234 "old storage map has %d elements with keys %v, that are not present in new storge map", 235 len(onlyOldKeys), 236 onlyOldKeys, 237 ), 238 }) 239 } 240 241 // Log keys only present in new storage map 242 if len(onlyNewKeys) > 0 { 243 dr.reportWriter.Write( 244 difference{ 245 Address: dr.address.Hex(), 246 Domain: domain, 247 Kind: diffKindString[storageMapKeyDiffKind], 248 Msg: fmt.Sprintf( 249 "new storage map has %d elements with keys %v, that are not present in old storge map", 250 len(onlyNewKeys), 251 onlyNewKeys, 252 ), 253 }) 254 } 255 256 // Compare elements present in both storage maps 257 for _, key := range sharedKeys { 258 259 trace := fmt.Sprintf("%s[%v]", domain, key) 260 261 var mapKey interpreter.StorageMapKey 262 263 switch key := key.(type) { 264 case interpreter.StringAtreeValue: 265 mapKey = interpreter.StringStorageMapKey(key) 266 267 case interpreter.Uint64AtreeValue: 268 mapKey = interpreter.Uint64StorageMapKey(key) 269 270 case interpreter.StringStorageMapKey: 271 mapKey = key 272 273 case interpreter.Uint64StorageMapKey: 274 mapKey = key 275 276 default: 277 dr.reportWriter.Write( 278 diffProblem{ 279 Address: dr.address.Hex(), 280 Domain: domain, 281 Kind: diffErrorKindString[storageMapKeyNotImplementingStorageMapKeyDiffErrorKind], 282 Trace: trace, 283 Msg: fmt.Sprintf( 284 "invalid storage map key %v (%T), expected interpreter.StorageMapKey", 285 key, 286 key, 287 ), 288 }) 289 continue 290 } 291 292 oldValue := oldStorageMap.ReadValue(nil, mapKey) 293 294 newValue := newStorageMap.ReadValue(nil, mapKey) 295 296 hasDifference := dr.diffValues( 297 oldRuntime.Interpreter, 298 oldValue, 299 newRuntime.Interpreter, 300 newValue, 301 domain, 302 trace, 303 ) 304 if hasDifference { 305 if dr.verboseLogging { 306 // Log potentially large values at top level only when verbose logging is enabled. 307 dr.reportWriter.Write( 308 difference{ 309 Address: dr.address.Hex(), 310 Domain: domain, 311 Kind: diffKindString[storageMapValueDiffKind], 312 Msg: "storage map elements are different", 313 Trace: trace, 314 OldValue: oldValue.String(), 315 NewValue: newValue.String(), 316 OldValueStaticType: oldValue.StaticType(oldRuntime.Interpreter).String(), 317 NewValueStaticType: newValue.StaticType(newRuntime.Interpreter).String(), 318 }) 319 } 320 } 321 322 } 323 } 324 325 func (dr *CadenceValueDiffReporter) diffValues( 326 vInterpreter *interpreter.Interpreter, 327 v interpreter.Value, 328 otherInterpreter *interpreter.Interpreter, 329 other interpreter.Value, 330 domain string, 331 trace string, 332 ) (hasDifference bool) { 333 switch v := v.(type) { 334 case *interpreter.ArrayValue: 335 return dr.diffCadenceArrayValue(vInterpreter, v, otherInterpreter, other, domain, trace) 336 337 case *interpreter.CompositeValue: 338 return dr.diffCadenceCompositeValue(vInterpreter, v, otherInterpreter, other, domain, trace) 339 340 case *interpreter.DictionaryValue: 341 return dr.diffCadenceDictionaryValue(vInterpreter, v, otherInterpreter, other, domain, trace) 342 343 case *interpreter.SomeValue: 344 return dr.diffCadenceSomeValue(vInterpreter, v, otherInterpreter, other, domain, trace) 345 346 default: 347 oldValue, ok := v.(interpreter.EquatableValue) 348 if !ok { 349 dr.reportWriter.Write( 350 diffProblem{ 351 Address: dr.address.Hex(), 352 Domain: domain, 353 Kind: diffErrorKindString[cadenceValueNotImplementEquatableValueDiffErrorKind], 354 Trace: trace, 355 Msg: fmt.Sprintf("old value doesn't implement interpreter.EquatableValue: %s (%T)", oldValue.String(), oldValue), 356 }) 357 return true 358 } 359 360 if !oldValue.Equal(nil, interpreter.EmptyLocationRange, other) { 361 dr.reportWriter.Write( 362 difference{ 363 Address: dr.address.Hex(), 364 Domain: domain, 365 Kind: diffKindString[cadenceValueDiffKind], 366 Msg: fmt.Sprintf("values differ: %T vs %T", oldValue, other), 367 Trace: trace, 368 OldValue: v.String(), 369 NewValue: other.String(), 370 OldValueStaticType: v.StaticType(vInterpreter).String(), 371 NewValueStaticType: other.StaticType(otherInterpreter).String(), 372 }) 373 return true 374 } 375 } 376 377 return false 378 } 379 380 func (dr *CadenceValueDiffReporter) diffCadenceSomeValue( 381 vInterpreter *interpreter.Interpreter, 382 v *interpreter.SomeValue, 383 otherInterpreter *interpreter.Interpreter, 384 other interpreter.Value, 385 domain string, 386 trace string, 387 ) (hasDifference bool) { 388 otherSome, ok := other.(*interpreter.SomeValue) 389 if !ok { 390 dr.reportWriter.Write( 391 difference{ 392 Address: dr.address.Hex(), 393 Domain: domain, 394 Kind: diffKindString[cadenceValueTypeDiffKind], 395 Trace: trace, 396 Msg: fmt.Sprintf("types differ: %T != %T", v, other), 397 }) 398 return true 399 } 400 401 innerValue := v.InnerValue(vInterpreter, interpreter.EmptyLocationRange) 402 403 otherInnerValue := otherSome.InnerValue(otherInterpreter, interpreter.EmptyLocationRange) 404 405 return dr.diffValues(vInterpreter, innerValue, otherInterpreter, otherInnerValue, domain, trace) 406 } 407 408 func (dr *CadenceValueDiffReporter) diffCadenceArrayValue( 409 vInterpreter *interpreter.Interpreter, 410 v *interpreter.ArrayValue, 411 otherInterpreter *interpreter.Interpreter, 412 other interpreter.Value, 413 domain string, 414 trace string, 415 ) (hasDifference bool) { 416 otherArray, ok := other.(*interpreter.ArrayValue) 417 if !ok { 418 dr.reportWriter.Write( 419 difference{ 420 Address: dr.address.Hex(), 421 Domain: domain, 422 Kind: diffKindString[cadenceValueTypeDiffKind], 423 Trace: trace, 424 Msg: fmt.Sprintf("types differ: %T != %T", v, other), 425 }) 426 return true 427 } 428 429 if v.Type == nil && otherArray.Type != nil { 430 hasDifference = true 431 432 dr.reportWriter.Write( 433 difference{ 434 Address: dr.address.Hex(), 435 Domain: domain, 436 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 437 Trace: trace, 438 Msg: fmt.Sprintf("array static types differ: nil != %s", otherArray.Type), 439 }) 440 } 441 442 if v.Type != nil && otherArray.Type == nil { 443 hasDifference = true 444 445 dr.reportWriter.Write( 446 difference{ 447 Address: dr.address.Hex(), 448 Domain: domain, 449 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 450 Trace: trace, 451 Msg: fmt.Sprintf("array static types differ: %s != nil", v.Type), 452 }) 453 } 454 455 if v.Type != nil && otherArray.Type != nil && !v.Type.Equal(otherArray.Type) { 456 hasDifference = true 457 458 dr.reportWriter.Write( 459 difference{ 460 Address: dr.address.Hex(), 461 Domain: domain, 462 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 463 Trace: trace, 464 Msg: fmt.Sprintf("array static types differ: %s != %s", v.Type, otherArray.Type), 465 }) 466 } 467 468 count := v.Count() 469 if count != otherArray.Count() { 470 hasDifference = true 471 472 d := difference{ 473 Address: dr.address.Hex(), 474 Domain: domain, 475 Kind: diffKindString[cadenceValueDiffKind], 476 Trace: trace, 477 Msg: fmt.Sprintf("array counts differ: %d != %d", count, otherArray.Count()), 478 } 479 480 if dr.verboseLogging { 481 d.OldValue = v.String() 482 d.NewValue = other.String() 483 } 484 485 dr.reportWriter.Write(d) 486 } 487 488 // Compare array elements 489 for i := 0; i < min(count, otherArray.Count()); i++ { 490 element := v.Get(vInterpreter, interpreter.EmptyLocationRange, i) 491 otherElement := otherArray.Get(otherInterpreter, interpreter.EmptyLocationRange, i) 492 493 elementTrace := fmt.Sprintf("%s[%d]", trace, i) 494 elementHasDifference := dr.diffValues(vInterpreter, element, otherInterpreter, otherElement, domain, elementTrace) 495 if elementHasDifference { 496 hasDifference = true 497 } 498 } 499 500 return hasDifference 501 } 502 503 func (dr *CadenceValueDiffReporter) diffCadenceCompositeValue( 504 vInterpreter *interpreter.Interpreter, 505 v *interpreter.CompositeValue, 506 otherInterpreter *interpreter.Interpreter, 507 other interpreter.Value, 508 domain string, 509 trace string, 510 ) (hasDifference bool) { 511 otherComposite, ok := other.(*interpreter.CompositeValue) 512 if !ok { 513 dr.reportWriter.Write( 514 difference{ 515 Address: dr.address.Hex(), 516 Domain: domain, 517 Kind: diffKindString[cadenceValueTypeDiffKind], 518 Trace: trace, 519 Msg: fmt.Sprintf("types differ: %T != %T", v, other), 520 }) 521 return true 522 } 523 524 if !v.StaticType(vInterpreter).Equal(otherComposite.StaticType(otherInterpreter)) { 525 hasDifference = true 526 527 dr.reportWriter.Write( 528 difference{ 529 Address: dr.address.Hex(), 530 Domain: domain, 531 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 532 Trace: trace, 533 Msg: fmt.Sprintf( 534 "composite static types differ: %s != %s", 535 v.StaticType(vInterpreter), 536 otherComposite.StaticType(otherInterpreter)), 537 }) 538 } 539 540 if v.Kind != otherComposite.Kind { 541 hasDifference = true 542 543 dr.reportWriter.Write( 544 difference{ 545 Address: dr.address.Hex(), 546 Domain: domain, 547 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 548 Trace: trace, 549 Msg: fmt.Sprintf( 550 "composite kinds differ: %d != %d", 551 v.Kind, 552 otherComposite.Kind, 553 ), 554 }) 555 } 556 557 oldFieldNames := make([]string, 0, v.FieldCount()) 558 v.ForEachFieldName(func(fieldName string) bool { 559 oldFieldNames = append(oldFieldNames, fieldName) 560 return true 561 }) 562 563 newFieldNames := make([]string, 0, otherComposite.FieldCount()) 564 otherComposite.ForEachFieldName(func(fieldName string) bool { 565 newFieldNames = append(newFieldNames, fieldName) 566 return true 567 }) 568 569 onlyOldFieldNames, onlyNewFieldNames, sharedFieldNames := diff(oldFieldNames, newFieldNames) 570 571 // Log field names only present in old composite value 572 if len(onlyOldFieldNames) > 0 { 573 hasDifference = true 574 575 dr.reportWriter.Write( 576 difference{ 577 Address: dr.address.Hex(), 578 Domain: domain, 579 Kind: diffKindString[cadenceValueDiffKind], 580 Trace: trace, 581 Msg: fmt.Sprintf( 582 "old composite value has %d fields with keys %v, that are not present in new composite value", 583 len(onlyOldFieldNames), 584 onlyOldFieldNames, 585 ), 586 }) 587 } 588 589 // Log field names only present in new composite value 590 if len(onlyNewFieldNames) > 0 { 591 hasDifference = true 592 593 dr.reportWriter.Write( 594 difference{ 595 Address: dr.address.Hex(), 596 Domain: domain, 597 Kind: diffKindString[cadenceValueDiffKind], 598 Trace: trace, 599 Msg: fmt.Sprintf( 600 "new composite value has %d fields with keys %v, that are not present in old composite value", 601 len(onlyNewFieldNames), 602 onlyNewFieldNames, 603 ), 604 }) 605 } 606 607 // Compare fields in both composite values 608 for _, fieldName := range sharedFieldNames { 609 fieldValue := v.GetField(vInterpreter, interpreter.EmptyLocationRange, fieldName) 610 otherFieldValue := otherComposite.GetField(otherInterpreter, interpreter.EmptyLocationRange, fieldName) 611 612 fieldTrace := fmt.Sprintf("%s.%s", trace, fieldName) 613 fieldHasDifference := dr.diffValues(vInterpreter, fieldValue, otherInterpreter, otherFieldValue, domain, fieldTrace) 614 if fieldHasDifference { 615 hasDifference = true 616 } 617 } 618 619 return hasDifference 620 } 621 622 func (dr *CadenceValueDiffReporter) diffCadenceDictionaryValue( 623 vInterpreter *interpreter.Interpreter, 624 v *interpreter.DictionaryValue, 625 otherInterpreter *interpreter.Interpreter, 626 other interpreter.Value, 627 domain string, 628 trace string, 629 ) (hasDifference bool) { 630 otherDictionary, ok := other.(*interpreter.DictionaryValue) 631 if !ok { 632 dr.reportWriter.Write( 633 difference{ 634 Address: dr.address.Hex(), 635 Domain: domain, 636 Kind: diffKindString[cadenceValueTypeDiffKind], 637 Trace: trace, 638 Msg: fmt.Sprintf("types differ: %T != %T", v, other), 639 }) 640 return true 641 } 642 643 if !v.Type.Equal(otherDictionary.Type) { 644 hasDifference = true 645 646 dr.reportWriter.Write( 647 difference{ 648 Address: dr.address.Hex(), 649 Domain: domain, 650 Kind: diffKindString[cadenceValueStaticTypeDiffKind], 651 Trace: trace, 652 Msg: fmt.Sprintf( 653 "dict static types differ: %s != %s", 654 v.Type, 655 otherDictionary.Type), 656 }) 657 } 658 659 oldKeys := make([]interpreter.Value, 0, v.Count()) 660 v.IterateKeys(vInterpreter, interpreter.EmptyLocationRange, func(key interpreter.Value) (resume bool) { 661 oldKeys = append(oldKeys, key) 662 return true 663 }) 664 665 newKeys := make([]interpreter.Value, 0, otherDictionary.Count()) 666 otherDictionary.IterateKeys(otherInterpreter, interpreter.EmptyLocationRange, func(key interpreter.Value) (resume bool) { 667 newKeys = append(newKeys, key) 668 return true 669 }) 670 671 onlyOldKeys, onlyNewKeys, sharedKeys := diffCadenceValues(oldKeys, newKeys) 672 673 // Log keys only present in old dict value 674 if len(onlyOldKeys) > 0 { 675 hasDifference = true 676 677 dr.reportWriter.Write( 678 difference{ 679 Address: dr.address.Hex(), 680 Domain: domain, 681 Kind: diffKindString[cadenceValueDiffKind], 682 Trace: trace, 683 Msg: fmt.Sprintf( 684 "old dict value has %d elements with keys %v, that are not present in new dict value", 685 len(onlyOldKeys), 686 onlyOldKeys, 687 ), 688 }) 689 } 690 691 // Log field names only present in new composite value 692 if len(onlyNewKeys) > 0 { 693 hasDifference = true 694 695 dr.reportWriter.Write( 696 difference{ 697 Address: dr.address.Hex(), 698 Domain: domain, 699 Kind: diffKindString[cadenceValueDiffKind], 700 Trace: trace, 701 Msg: fmt.Sprintf( 702 "new dict value has %d elements with keys %v, that are not present in old dict value", 703 len(onlyNewKeys), 704 onlyNewKeys, 705 ), 706 }) 707 } 708 709 // Compare elements in both dict values 710 for _, key := range sharedKeys { 711 valueTrace := fmt.Sprintf("%s[%v]", trace, key) 712 713 oldValue, _ := v.Get(vInterpreter, interpreter.EmptyLocationRange, key) 714 715 newValue, _ := otherDictionary.Get(otherInterpreter, interpreter.EmptyLocationRange, key) 716 717 elementHasDifference := dr.diffValues(vInterpreter, oldValue, otherInterpreter, newValue, domain, valueTrace) 718 if elementHasDifference { 719 hasDifference = true 720 } 721 } 722 723 return hasDifference 724 } 725 726 func getStorageMapKeys(storageMap *interpreter.StorageMap) []any { 727 keys := make([]any, 0, storageMap.Count()) 728 729 iter := storageMap.Iterator(nil) 730 for { 731 key := iter.NextKey() 732 if key == nil { 733 break 734 } 735 keys = append(keys, key) 736 } 737 738 return keys 739 } 740 741 func diff[T comparable](old, new []T) (onlyOld, onlyNew, shared []T) { 742 onlyOld = make([]T, 0, len(old)) 743 onlyNew = make([]T, 0, len(new)) 744 shared = make([]T, 0, min(len(old), len(new))) 745 746 sharedNew := make([]bool, len(new)) 747 748 for _, o := range old { 749 found := false 750 751 for i, n := range new { 752 if o == n { 753 shared = append(shared, o) 754 found = true 755 sharedNew[i] = true 756 break 757 } 758 } 759 760 if !found { 761 onlyOld = append(onlyOld, o) 762 } 763 } 764 765 for i, shared := range sharedNew { 766 if !shared { 767 onlyNew = append(onlyNew, new[i]) 768 } 769 } 770 771 return 772 } 773 774 func diffCadenceValues(old, new []interpreter.Value) (onlyOld, onlyNew, shared []interpreter.Value) { 775 onlyOld = make([]interpreter.Value, 0, len(old)) 776 onlyNew = make([]interpreter.Value, 0, len(new)) 777 shared = make([]interpreter.Value, 0, min(len(old), len(new))) 778 779 sharedNew := make([]bool, len(new)) 780 781 for _, o := range old { 782 found := false 783 784 for i, n := range new { 785 foundShared := false 786 787 if ev, ok := o.(interpreter.EquatableValue); ok { 788 if ev.Equal(nil, interpreter.EmptyLocationRange, n) { 789 foundShared = true 790 } 791 } else { 792 if o == n { 793 foundShared = true 794 } 795 } 796 797 if foundShared { 798 shared = append(shared, o) 799 found = true 800 sharedNew[i] = true 801 break 802 } 803 } 804 805 if !found { 806 onlyOld = append(onlyOld, o) 807 } 808 } 809 810 for i, shared := range sharedNew { 811 if !shared { 812 onlyNew = append(onlyNew, new[i]) 813 } 814 } 815 816 return 817 } 818 819 func min(a, b int) int { 820 if a <= b { 821 return a 822 } 823 return b 824 }