github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/cmd/util/ledger/migrations/cadence_values_migration.go (about) 1 package migrations 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "io" 8 "sync" 9 10 "errors" 11 12 "github.com/onflow/cadence/migrations" 13 "github.com/onflow/cadence/migrations/capcons" 14 "github.com/onflow/cadence/migrations/entitlements" 15 "github.com/onflow/cadence/migrations/statictypes" 16 "github.com/onflow/cadence/migrations/string_normalization" 17 "github.com/onflow/cadence/runtime" 18 "github.com/onflow/cadence/runtime/common" 19 cadenceErrors "github.com/onflow/cadence/runtime/errors" 20 "github.com/onflow/cadence/runtime/interpreter" 21 "github.com/rs/zerolog" 22 23 "github.com/onflow/flow-go/cmd/util/ledger/reporters" 24 "github.com/onflow/flow-go/cmd/util/ledger/util" 25 "github.com/onflow/flow-go/cmd/util/ledger/util/registers" 26 "github.com/onflow/flow-go/fvm/environment" 27 "github.com/onflow/flow-go/fvm/tracing" 28 "github.com/onflow/flow-go/ledger" 29 "github.com/onflow/flow-go/model/flow" 30 ) 31 32 type CadenceBaseMigration struct { 33 name string 34 log zerolog.Logger 35 reporter reporters.ReportWriter 36 diffReporter reporters.ReportWriter 37 logVerboseDiff bool 38 verboseErrorOutput bool 39 checkStorageHealthBeforeMigration bool 40 valueMigrations func( 41 inter *interpreter.Interpreter, 42 accounts environment.Accounts, 43 reporter *cadenceValueMigrationReporter, 44 ) []migrations.ValueMigration 45 interpreterMigrationRuntimeConfig InterpreterMigrationRuntimeConfig 46 errorMessageHandler *errorMessageHandler 47 programs map[runtime.Location]*interpreter.Program 48 chainID flow.ChainID 49 nWorkers int 50 } 51 52 var _ AccountBasedMigration = (*CadenceBaseMigration)(nil) 53 var _ io.Closer = (*CadenceBaseMigration)(nil) 54 55 func (m *CadenceBaseMigration) Close() error { 56 // Close the report writer so it flushes to file. 57 m.reporter.Close() 58 59 if m.diffReporter != nil { 60 m.diffReporter.Close() 61 } 62 63 return nil 64 } 65 66 func (m *CadenceBaseMigration) InitMigration( 67 log zerolog.Logger, 68 _ *registers.ByAccount, 69 nWorkers int, 70 ) error { 71 m.log = log.With().Str("migration", m.name).Logger() 72 m.nWorkers = nWorkers 73 74 // During the migration, we only provide already checked programs, 75 // no parsing/checking of contracts is expected. 76 77 m.interpreterMigrationRuntimeConfig = InterpreterMigrationRuntimeConfig{ 78 GetOrLoadProgram: func( 79 location runtime.Location, 80 _ func() (*interpreter.Program, error), 81 ) (*interpreter.Program, error) { 82 program, ok := m.programs[location] 83 if !ok { 84 return nil, fmt.Errorf("program not found: %s", location) 85 } 86 return program, nil 87 }, 88 GetCode: func(_ common.AddressLocation) ([]byte, error) { 89 return nil, fmt.Errorf("unexpected call to GetCode") 90 }, 91 GetContractNames: func(address flow.Address) ([]string, error) { 92 return nil, fmt.Errorf("unexpected call to GetContractNames") 93 }, 94 } 95 96 return nil 97 } 98 99 func (m *CadenceBaseMigration) MigrateAccount( 100 _ context.Context, 101 address common.Address, 102 accountRegisters *registers.AccountRegisters, 103 ) error { 104 var oldPayloadsForDiff []*ledger.Payload 105 if m.diffReporter != nil { 106 oldPayloadsForDiff = accountRegisters.Payloads() 107 } 108 109 // Create all the runtime components we need for the migration 110 migrationRuntime, err := NewInterpreterMigrationRuntime( 111 accountRegisters, 112 m.chainID, 113 m.interpreterMigrationRuntimeConfig, 114 ) 115 if err != nil { 116 return fmt.Errorf("failed to create interpreter migration runtime: %w", err) 117 } 118 119 storage := migrationRuntime.Storage 120 121 // Check storage health before migration, if enabled. 122 var storageHealthErrorBefore error 123 if m.checkStorageHealthBeforeMigration { 124 125 storageHealthErrorBefore = checkStorageHealth(address, storage, accountRegisters, m.nWorkers) 126 if storageHealthErrorBefore != nil { 127 m.log.Warn(). 128 Err(storageHealthErrorBefore). 129 Str("account", address.HexWithPrefix()). 130 Msg("storage health check before migration failed") 131 } 132 } 133 134 migration, err := migrations.NewStorageMigration( 135 migrationRuntime.Interpreter, 136 storage, 137 m.name, 138 address, 139 ) 140 if err != nil { 141 return fmt.Errorf("failed to create storage migration: %w", err) 142 } 143 144 reporter := newValueMigrationReporter( 145 m.reporter, 146 m.log, 147 m.errorMessageHandler, 148 m.verboseErrorOutput, 149 ) 150 151 valueMigrations := m.valueMigrations( 152 migrationRuntime.Interpreter, 153 migrationRuntime.Accounts, 154 reporter, 155 ) 156 157 migration.Migrate( 158 migration.NewValueMigrationsPathMigrator( 159 reporter, 160 valueMigrations..., 161 ), 162 ) 163 164 err = migration.Commit() 165 if err != nil { 166 return fmt.Errorf("failed to commit changes: %w", err) 167 } 168 169 // Check storage health after migration. 170 // If the storage health check failed before the migration, we don't need to check it again. 171 if storageHealthErrorBefore == nil { 172 storageHealthErrorAfter := storage.CheckHealth() 173 if storageHealthErrorAfter != nil { 174 m.log.Err(storageHealthErrorAfter). 175 Str("account", address.HexWithPrefix()). 176 Msg("storage health check after migration failed") 177 } 178 } 179 180 // finalize the transaction 181 result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() 182 if err != nil { 183 return fmt.Errorf("failed to finalize main transaction: %w", err) 184 } 185 186 // Merge the changes into the registers 187 expectedAddresses := map[flow.Address]struct{}{ 188 flow.Address(address): {}, 189 } 190 191 err = registers.ApplyChanges( 192 accountRegisters, 193 result.WriteSet, 194 expectedAddresses, 195 m.log, 196 ) 197 if err != nil { 198 return fmt.Errorf("failed to apply changes: %w", err) 199 } 200 201 if m.diffReporter != nil { 202 newPayloadsForDiff := accountRegisters.Payloads() 203 204 accountDiffReporter := NewCadenceValueDiffReporter( 205 address, 206 m.chainID, 207 m.diffReporter, 208 m.logVerboseDiff, 209 m.nWorkers, 210 ) 211 212 accountDiffReporter.DiffStates( 213 oldPayloadsForDiff, 214 newPayloadsForDiff, 215 allStorageMapDomains, 216 ) 217 } 218 219 return nil 220 } 221 222 const cadenceValueMigrationReporterName = "cadence-value-migration" 223 224 // NewCadence1ValueMigration creates a new CadenceBaseMigration 225 // which runs some of the Cadence value migrations (static types, entitlements, strings) 226 func NewCadence1ValueMigration( 227 rwf reporters.ReportWriterFactory, 228 errorMessageHandler *errorMessageHandler, 229 programs map[runtime.Location]*interpreter.Program, 230 compositeTypeConverter statictypes.CompositeTypeConverterFunc, 231 interfaceTypeConverter statictypes.InterfaceTypeConverterFunc, 232 staticTypeCache migrations.StaticTypeCache, 233 opts Options, 234 ) *CadenceBaseMigration { 235 236 var diffReporter reporters.ReportWriter 237 if opts.DiffMigrations { 238 diffReporter = rwf.ReportWriter("cadence-value-migration-diff") 239 } 240 241 return &CadenceBaseMigration{ 242 name: "cadence_value_migration", 243 reporter: rwf.ReportWriter(cadenceValueMigrationReporterName), 244 diffReporter: diffReporter, 245 logVerboseDiff: opts.LogVerboseDiff, 246 verboseErrorOutput: opts.VerboseErrorOutput, 247 checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, 248 valueMigrations: func( 249 inter *interpreter.Interpreter, 250 _ environment.Accounts, 251 reporter *cadenceValueMigrationReporter, 252 ) []migrations.ValueMigration { 253 return []migrations.ValueMigration{ 254 statictypes.NewStaticTypeMigration(). 255 WithCompositeTypeConverter(compositeTypeConverter). 256 WithInterfaceTypeConverter(interfaceTypeConverter), 257 entitlements.NewEntitlementsMigrationWithCache(inter, staticTypeCache), 258 string_normalization.NewStringNormalizingMigration(), 259 } 260 }, 261 errorMessageHandler: errorMessageHandler, 262 programs: programs, 263 chainID: opts.ChainID, 264 } 265 } 266 267 // NewCadence1LinkValueMigration creates a new CadenceBaseMigration 268 // which migrates links to capability controllers. 269 // It populates the given map with the IDs of the capability controller it issues. 270 func NewCadence1LinkValueMigration( 271 rwf reporters.ReportWriterFactory, 272 errorMessageHandler *errorMessageHandler, 273 programs map[runtime.Location]*interpreter.Program, 274 capabilityMapping *capcons.CapabilityMapping, 275 opts Options, 276 ) *CadenceBaseMigration { 277 var diffReporter reporters.ReportWriter 278 if opts.DiffMigrations { 279 diffReporter = rwf.ReportWriter("cadence-link-value-migration-diff") 280 } 281 282 return &CadenceBaseMigration{ 283 name: "cadence_link_value_migration", 284 reporter: rwf.ReportWriter("cadence-link-value-migration"), 285 diffReporter: diffReporter, 286 logVerboseDiff: opts.LogVerboseDiff, 287 verboseErrorOutput: opts.VerboseErrorOutput, 288 checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, 289 valueMigrations: func( 290 _ *interpreter.Interpreter, 291 accounts environment.Accounts, 292 reporter *cadenceValueMigrationReporter, 293 ) []migrations.ValueMigration { 294 idGenerator := environment.NewAccountLocalIDGenerator( 295 tracing.NewMockTracerSpan(), 296 util.NopMeter{}, 297 accounts, 298 ) 299 return []migrations.ValueMigration{ 300 &capcons.LinkValueMigration{ 301 CapabilityMapping: capabilityMapping, 302 AccountIDGenerator: idGenerator, 303 Reporter: reporter, 304 }, 305 } 306 }, 307 errorMessageHandler: errorMessageHandler, 308 programs: programs, 309 chainID: opts.ChainID, 310 } 311 } 312 313 // NewCadence1CapabilityValueMigration creates a new CadenceBaseMigration 314 // which migrates path capability values to ID capability values. 315 // It requires a map the IDs of the capability controllers, 316 // generated by the link value migration. 317 func NewCadence1CapabilityValueMigration( 318 rwf reporters.ReportWriterFactory, 319 errorMessageHandler *errorMessageHandler, 320 programs map[runtime.Location]*interpreter.Program, 321 capabilityMapping *capcons.CapabilityMapping, 322 opts Options, 323 ) *CadenceBaseMigration { 324 var diffReporter reporters.ReportWriter 325 if opts.DiffMigrations { 326 diffReporter = rwf.ReportWriter("cadence-capability-value-migration-diff") 327 } 328 329 return &CadenceBaseMigration{ 330 name: "cadence_capability_value_migration", 331 reporter: rwf.ReportWriter("cadence-capability-value-migration"), 332 diffReporter: diffReporter, 333 logVerboseDiff: opts.LogVerboseDiff, 334 verboseErrorOutput: opts.VerboseErrorOutput, 335 checkStorageHealthBeforeMigration: opts.CheckStorageHealthBeforeMigration, 336 valueMigrations: func( 337 _ *interpreter.Interpreter, 338 _ environment.Accounts, 339 reporter *cadenceValueMigrationReporter, 340 ) []migrations.ValueMigration { 341 return []migrations.ValueMigration{ 342 &capcons.CapabilityValueMigration{ 343 CapabilityMapping: capabilityMapping, 344 Reporter: reporter, 345 }, 346 } 347 }, 348 errorMessageHandler: errorMessageHandler, 349 programs: programs, 350 chainID: opts.ChainID, 351 } 352 } 353 354 // errorMessageHandler formats error messages from errors. 355 // It only reports program loading errors once. 356 type errorMessageHandler struct { 357 // common.Location -> struct{} 358 reportedProgramLoadingErrors sync.Map 359 } 360 361 func (t *errorMessageHandler) FormatError(err error) (message string, showStack bool) { 362 363 // Only report program loading errors once, 364 // omit full error message for subsequent occurrences 365 366 var programLoadingError environment.ProgramLoadingError 367 if errors.As(err, &programLoadingError) { 368 location := programLoadingError.Location 369 _, ok := t.reportedProgramLoadingErrors.LoadOrStore(location, struct{}{}) 370 if ok { 371 return "error getting program", false 372 } 373 374 return err.Error(), false 375 } 376 377 return err.Error(), true 378 } 379 380 // cadenceValueMigrationReporter is the reporter for cadence value migrations 381 type cadenceValueMigrationReporter struct { 382 reportWriter reporters.ReportWriter 383 log zerolog.Logger 384 errorMessageHandler *errorMessageHandler 385 verboseErrorOutput bool 386 } 387 388 var _ capcons.LinkMigrationReporter = &cadenceValueMigrationReporter{} 389 var _ capcons.CapabilityMigrationReporter = &cadenceValueMigrationReporter{} 390 var _ migrations.Reporter = &cadenceValueMigrationReporter{} 391 392 func newValueMigrationReporter( 393 reportWriter reporters.ReportWriter, 394 log zerolog.Logger, 395 errorMessageHandler *errorMessageHandler, 396 verboseErrorOutput bool, 397 ) *cadenceValueMigrationReporter { 398 return &cadenceValueMigrationReporter{ 399 reportWriter: reportWriter, 400 log: log, 401 errorMessageHandler: errorMessageHandler, 402 verboseErrorOutput: verboseErrorOutput, 403 } 404 } 405 406 func (t *cadenceValueMigrationReporter) Migrated( 407 storageKey interpreter.StorageKey, 408 storageMapKey interpreter.StorageMapKey, 409 migration string, 410 ) { 411 t.reportWriter.Write(cadenceValueMigrationEntry{ 412 StorageKey: storageKey, 413 StorageMapKey: storageMapKey, 414 Migration: migration, 415 }) 416 } 417 418 func (t *cadenceValueMigrationReporter) Error(err error) { 419 420 var migrationErr migrations.StorageMigrationError 421 422 if !errors.As(err, &migrationErr) { 423 panic(cadenceErrors.NewUnreachableError()) 424 } 425 426 message, showStack := t.errorMessageHandler.FormatError(migrationErr.Err) 427 428 storageKey := migrationErr.StorageKey 429 storageMapKey := migrationErr.StorageMapKey 430 migration := migrationErr.Migration 431 432 if showStack && len(migrationErr.Stack) > 0 { 433 message = fmt.Sprintf("%s\n%s", message, migrationErr.Stack) 434 } 435 436 if t.verboseErrorOutput { 437 t.reportWriter.Write(cadenceValueMigrationFailureEntry{ 438 StorageKey: storageKey, 439 StorageMapKey: storageMapKey, 440 Migration: migration, 441 Message: message, 442 }) 443 } 444 } 445 446 func (t *cadenceValueMigrationReporter) MigratedPathCapability( 447 accountAddress common.Address, 448 addressPath interpreter.AddressPath, 449 borrowType *interpreter.ReferenceStaticType, 450 ) { 451 t.reportWriter.Write(capabilityMigrationEntry{ 452 AccountAddress: accountAddress, 453 AddressPath: addressPath, 454 BorrowType: borrowType, 455 }) 456 } 457 458 func (t *cadenceValueMigrationReporter) MissingCapabilityID( 459 accountAddress common.Address, 460 addressPath interpreter.AddressPath, 461 ) { 462 t.reportWriter.Write(capabilityMissingCapabilityIDEntry{ 463 AccountAddress: accountAddress, 464 AddressPath: addressPath, 465 }) 466 } 467 468 func (t *cadenceValueMigrationReporter) MigratedLink( 469 accountAddressPath interpreter.AddressPath, 470 capabilityID interpreter.UInt64Value, 471 ) { 472 t.reportWriter.Write(linkMigrationEntry{ 473 AccountAddressPath: accountAddressPath, 474 CapabilityID: uint64(capabilityID), 475 }) 476 } 477 478 func (t *cadenceValueMigrationReporter) CyclicLink(err capcons.CyclicLinkError) { 479 t.reportWriter.Write(err) 480 } 481 482 func (t *cadenceValueMigrationReporter) MissingTarget(accountAddressPath interpreter.AddressPath) { 483 t.reportWriter.Write(linkMissingTargetEntry{ 484 AddressPath: accountAddressPath, 485 }) 486 } 487 488 func (t *cadenceValueMigrationReporter) DictionaryKeyConflict(accountAddressPath interpreter.AddressPath) { 489 t.reportWriter.Write(dictionaryKeyConflictEntry{ 490 AddressPath: accountAddressPath, 491 }) 492 } 493 494 type valueMigrationReportEntry interface { 495 accountAddress() common.Address 496 } 497 498 // cadenceValueMigrationReportEntry 499 500 type cadenceValueMigrationEntry struct { 501 StorageKey interpreter.StorageKey 502 StorageMapKey interpreter.StorageMapKey 503 Migration string 504 } 505 506 var _ valueMigrationReportEntry = cadenceValueMigrationEntry{} 507 508 func (e cadenceValueMigrationEntry) accountAddress() common.Address { 509 return e.StorageKey.Address 510 } 511 512 var _ json.Marshaler = cadenceValueMigrationEntry{} 513 514 func (e cadenceValueMigrationEntry) MarshalJSON() ([]byte, error) { 515 return json.Marshal(struct { 516 Kind string `json:"kind"` 517 AccountAddress string `json:"account_address"` 518 StorageDomain string `json:"domain"` 519 Key string `json:"key"` 520 Migration string `json:"migration"` 521 }{ 522 Kind: "cadence-value-migration-success", 523 AccountAddress: e.StorageKey.Address.HexWithPrefix(), 524 StorageDomain: e.StorageKey.Key, 525 Key: fmt.Sprintf("%s", e.StorageMapKey), 526 Migration: e.Migration, 527 }) 528 } 529 530 // cadenceValueMigrationFailureEntry 531 532 type cadenceValueMigrationFailureEntry struct { 533 StorageKey interpreter.StorageKey 534 StorageMapKey interpreter.StorageMapKey 535 Migration string 536 Message string 537 } 538 539 var _ valueMigrationReportEntry = cadenceValueMigrationFailureEntry{} 540 541 func (e cadenceValueMigrationFailureEntry) accountAddress() common.Address { 542 return e.StorageKey.Address 543 } 544 545 var _ json.Marshaler = cadenceValueMigrationFailureEntry{} 546 547 func (e cadenceValueMigrationFailureEntry) MarshalJSON() ([]byte, error) { 548 return json.Marshal(struct { 549 Kind string `json:"kind"` 550 AccountAddress string `json:"account_address"` 551 StorageDomain string `json:"domain"` 552 Key string `json:"key"` 553 Migration string `json:"migration"` 554 Message string `json:"message"` 555 }{ 556 Kind: "cadence-value-migration-failure", 557 AccountAddress: e.StorageKey.Address.HexWithPrefix(), 558 StorageDomain: e.StorageKey.Key, 559 Key: fmt.Sprintf("%s", e.StorageMapKey), 560 Migration: e.Migration, 561 Message: e.Message, 562 }) 563 } 564 565 // linkMigrationEntry 566 567 type linkMigrationEntry struct { 568 AccountAddressPath interpreter.AddressPath 569 CapabilityID uint64 570 } 571 572 var _ valueMigrationReportEntry = linkMigrationEntry{} 573 574 func (e linkMigrationEntry) accountAddress() common.Address { 575 return e.AccountAddressPath.Address 576 } 577 578 var _ json.Marshaler = linkMigrationEntry{} 579 580 func (e linkMigrationEntry) MarshalJSON() ([]byte, error) { 581 return json.Marshal(struct { 582 Kind string `json:"kind"` 583 AccountAddress string `json:"account_address"` 584 Path string `json:"path"` 585 CapabilityID uint64 `json:"capability_id"` 586 }{ 587 Kind: "link-migration-success", 588 AccountAddress: e.AccountAddressPath.Address.HexWithPrefix(), 589 Path: e.AccountAddressPath.Path.String(), 590 CapabilityID: e.CapabilityID, 591 }) 592 } 593 594 // capabilityMigrationEntry 595 596 type capabilityMigrationEntry struct { 597 AccountAddress common.Address 598 AddressPath interpreter.AddressPath 599 BorrowType *interpreter.ReferenceStaticType 600 } 601 602 var _ valueMigrationReportEntry = capabilityMigrationEntry{} 603 604 func (e capabilityMigrationEntry) accountAddress() common.Address { 605 return e.AccountAddress 606 } 607 608 var _ json.Marshaler = capabilityMigrationEntry{} 609 610 func (e capabilityMigrationEntry) MarshalJSON() ([]byte, error) { 611 return json.Marshal(struct { 612 Kind string `json:"kind"` 613 AccountAddress string `json:"account_address"` 614 Address string `json:"address"` 615 Path string `json:"path"` 616 BorrowType string `json:"borrow_type"` 617 }{ 618 Kind: "capability-migration-success", 619 AccountAddress: e.AccountAddress.HexWithPrefix(), 620 Address: e.AddressPath.Address.HexWithPrefix(), 621 Path: e.AddressPath.Path.String(), 622 BorrowType: string(e.BorrowType.ID()), 623 }) 624 } 625 626 // capabilityMissingCapabilityIDEntry 627 628 type capabilityMissingCapabilityIDEntry struct { 629 AccountAddress common.Address 630 AddressPath interpreter.AddressPath 631 } 632 633 var _ valueMigrationReportEntry = capabilityMissingCapabilityIDEntry{} 634 635 func (e capabilityMissingCapabilityIDEntry) accountAddress() common.Address { 636 return e.AccountAddress 637 } 638 639 var _ json.Marshaler = capabilityMissingCapabilityIDEntry{} 640 641 func (e capabilityMissingCapabilityIDEntry) MarshalJSON() ([]byte, error) { 642 return json.Marshal(struct { 643 Kind string `json:"kind"` 644 AccountAddress string `json:"account_address"` 645 Address string `json:"address"` 646 Path string `json:"path"` 647 }{ 648 Kind: "capability-missing-capability-id", 649 AccountAddress: e.AccountAddress.HexWithPrefix(), 650 Address: e.AddressPath.Address.HexWithPrefix(), 651 Path: e.AddressPath.Path.String(), 652 }) 653 } 654 655 // linkMissingTargetEntry 656 657 type linkMissingTargetEntry struct { 658 AddressPath interpreter.AddressPath 659 } 660 661 var _ valueMigrationReportEntry = linkMissingTargetEntry{} 662 663 func (e linkMissingTargetEntry) accountAddress() common.Address { 664 return e.AddressPath.Address 665 } 666 667 var _ json.Marshaler = linkMissingTargetEntry{} 668 669 func (e linkMissingTargetEntry) MarshalJSON() ([]byte, error) { 670 return json.Marshal(struct { 671 Kind string `json:"kind"` 672 AccountAddress string `json:"account_address"` 673 Path string `json:"path"` 674 }{ 675 Kind: "link-missing-target", 676 AccountAddress: e.AddressPath.Address.HexWithPrefix(), 677 Path: e.AddressPath.Path.String(), 678 }) 679 } 680 681 // dictionaryKeyConflictEntry 682 683 type dictionaryKeyConflictEntry struct { 684 AddressPath interpreter.AddressPath 685 } 686 687 var _ json.Marshaler = dictionaryKeyConflictEntry{} 688 689 func (e dictionaryKeyConflictEntry) MarshalJSON() ([]byte, error) { 690 return json.Marshal(struct { 691 Kind string `json:"kind"` 692 AccountAddress string `json:"account_address"` 693 Path string `json:"path"` 694 }{ 695 Kind: "dictionary-key-conflict", 696 AccountAddress: e.AddressPath.Address.HexWithPrefix(), 697 Path: e.AddressPath.Path.String(), 698 }) 699 }