From 142633b8756b1f321e7e859709ec3865b8ef2aaf Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Thu, 8 Aug 2024 22:50:03 -0700 Subject: [PATCH 1/3] Add reproducer --- .../cadence_values_migration_test.go | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 4a9aba89541..62e88f04d30 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2182,7 +2182,9 @@ func TestCapabilityMigration(t *testing.T) { interpreter.PrimitiveStaticTypeAnyStruct, ) - capabilityValue := &interpreter.PathCapabilityValue{ + // Store a capability with storage path + + capabilityFoo := &interpreter.PathCapabilityValue{ BorrowType: borrowType, Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "foo"), @@ -2194,7 +2196,23 @@ func TestCapabilityMigration(t *testing.T) { storageMap.WriteValue( runtime.Interpreter, storageMapKey, - capabilityValue, + capabilityFoo, + ) + + // Store another capability with storage path, but without a borrow type. + + capabilityBar := &interpreter.PathCapabilityValue{ + Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"), + + // Important: Capability must be for a different address, + // compared to where the capability is stored. + Address: interpreter.AddressValue(addressB), + } + + storageMap.WriteValue( + runtime.Interpreter, + storageMapKey, + capabilityBar, ) err = storage.NondeterministicCommit(runtime.Interpreter, false) From 5dae66bcdf878eaf6bbbfece9ad2821c418ad5b7 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 9 Aug 2024 11:46:30 -0700 Subject: [PATCH 2/3] Report and skip storage caps with no borrow type --- cmd/util/ledger/migrations/cadence.go | 42 ++++--- .../migrations/cadence_values_migration.go | 72 +++++++++-- .../cadence_values_migration_test.go | 117 +++++++++++++++--- 3 files changed, 185 insertions(+), 46 deletions(-) diff --git a/cmd/util/ledger/migrations/cadence.go b/cmd/util/ledger/migrations/cadence.go index 393a6ad3bb5..9ad27a8f699 100644 --- a/cmd/util/ledger/migrations/cadence.go +++ b/cmd/util/ledger/migrations/cadence.go @@ -5,12 +5,13 @@ import ( _ "embed" "fmt" + "github.com/rs/zerolog" + "github.com/onflow/cadence/migrations/capcons" "github.com/onflow/cadence/migrations/statictypes" "github.com/onflow/cadence/runtime" "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/interpreter" - "github.com/rs/zerolog" "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util" @@ -228,16 +229,22 @@ type IssueStorageCapConMigration struct { programs map[runtime.Location]*interpreter.Program mapping *capcons.CapabilityMapping reporter reporters.ReportWriter + logVerboseDiff bool + verboseErrorOutput bool + errorMessageHandler *errorMessageHandler + log zerolog.Logger } const issueStorageCapConMigrationReporterName = "cadence-storage-capcon-issue-migration" func NewIssueStorageCapConMigration( rwf reporters.ReportWriterFactory, + errorMessageHandler *errorMessageHandler, chainID flow.ChainID, storageDomainCapabilities *capcons.AccountsCapabilities, programs map[runtime.Location]*interpreter.Program, capabilityMapping *capcons.CapabilityMapping, + opts Options, ) *IssueStorageCapConMigration { return &IssueStorageCapConMigration{ name: "cadence_storage_cap_con_issue_migration", @@ -246,14 +253,19 @@ func NewIssueStorageCapConMigration( accountsCapabilities: storageDomainCapabilities, programs: programs, mapping: capabilityMapping, + logVerboseDiff: opts.LogVerboseDiff, + verboseErrorOutput: opts.VerboseErrorOutput, + errorMessageHandler: errorMessageHandler, } } func (m *IssueStorageCapConMigration) InitMigration( - _ zerolog.Logger, + log zerolog.Logger, _ *registers.ByAccount, _ int, ) error { + m.log = log.With().Str("migration", m.name).Logger() + // During the migration, we only provide already checked programs, // no parsing/checking of contracts is expected. @@ -309,30 +321,22 @@ func (m *IssueStorageCapConMigration) MigrateAccount( idGenerator: idGenerator, } + reporter := newValueMigrationReporter( + m.reporter, + m.log, + m.errorMessageHandler, + m.verboseErrorOutput, + ) + capcons.IssueAccountCapabilities( migrationRuntime.Interpreter, + reporter, address, accountCapabilities, handler, m.mapping, ) - // It would be ideal to do the reporting inside `IssueAccountCapabilities` function above. - // However, that doesn't have the access to the reporter. So doing it here. - for _, capability := range accountCapabilities.Capabilities { - id, _, _ := m.mapping.Get(interpreter.AddressPath{ - Address: address, - Path: capability.Path, - }) - - m.reporter.Write(storageCapconIssuedEntry{ - AccountAddress: address, - Path: capability.Path, - BorrowType: capability.BorrowType, - CapabilityID: id, - }) - } - return nil } @@ -409,10 +413,12 @@ func NewCadence1ValueMigrations( func(opts Options) (string, AccountBasedMigration) { migration := NewIssueStorageCapConMigration( rwf, + errorMessageHandler, opts.ChainID, storageDomainCapabilities, programs, capabilityMapping, + opts, ) return migration.name, migration diff --git a/cmd/util/ledger/migrations/cadence_values_migration.go b/cmd/util/ledger/migrations/cadence_values_migration.go index 53eebf77a14..c484d34cb35 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration.go +++ b/cmd/util/ledger/migrations/cadence_values_migration.go @@ -449,6 +449,7 @@ type cadenceValueMigrationReporter struct { var _ capcons.LinkMigrationReporter = &cadenceValueMigrationReporter{} var _ capcons.CapabilityMigrationReporter = &cadenceValueMigrationReporter{} +var _ capcons.StorageCapabilityMigrationReporter = &cadenceValueMigrationReporter{} var _ migrations.Reporter = &cadenceValueMigrationReporter{} func newValueMigrationReporter( @@ -529,6 +530,30 @@ func (t *cadenceValueMigrationReporter) MissingCapabilityID( }) } +func (t *cadenceValueMigrationReporter) MissingBorrowType( + accountAddress common.Address, + addressPath interpreter.AddressPath, +) { + t.reportWriter.Write(storageCapConsMissingBorrowTypeEntry{ + AccountAddress: accountAddress, + AddressPath: addressPath, + }) +} + +func (t *cadenceValueMigrationReporter) IssuedStorageCapabilityController( + accountAddress common.Address, + addressPath interpreter.AddressPath, + borrowType *interpreter.ReferenceStaticType, + capabilityID interpreter.UInt64Value, +) { + t.reportWriter.Write(storageCapConIssuedEntry{ + AccountAddress: accountAddress, + AddressPath: addressPath, + BorrowType: borrowType, + CapabilityID: capabilityID, + }) +} + func (t *cadenceValueMigrationReporter) MigratedLink( accountAddressPath interpreter.AddressPath, capabilityID interpreter.UInt64Value, @@ -801,35 +826,66 @@ func (e dictionaryKeyConflictEntry) MarshalJSON() ([]byte, error) { }) } -// storageCapconIssuedEntry +// storageCapConIssuedEntry -type storageCapconIssuedEntry struct { +type storageCapConIssuedEntry struct { AccountAddress common.Address - Path interpreter.PathValue + AddressPath interpreter.AddressPath BorrowType interpreter.StaticType CapabilityID interpreter.UInt64Value } -var _ valueMigrationReportEntry = storageCapconIssuedEntry{} +var _ valueMigrationReportEntry = storageCapConIssuedEntry{} -func (e storageCapconIssuedEntry) accountAddress() common.Address { +func (e storageCapConIssuedEntry) accountAddress() common.Address { return e.AccountAddress } -var _ json.Marshaler = storageCapconIssuedEntry{} +var _ json.Marshaler = storageCapConIssuedEntry{} -func (e storageCapconIssuedEntry) MarshalJSON() ([]byte, error) { +func (e storageCapConIssuedEntry) MarshalJSON() ([]byte, error) { return json.Marshal(struct { Kind string `json:"kind"` AccountAddress string `json:"account_address"` + Address string `json:"address"` Path string `json:"path"` BorrowType string `json:"borrow_type"` CapabilityID string `json:"capability_id"` }{ Kind: "storage-capcon-issued", AccountAddress: e.AccountAddress.HexWithPrefix(), - Path: e.Path.String(), + Address: e.AddressPath.Address.HexWithPrefix(), + Path: e.AddressPath.Path.String(), BorrowType: string(e.BorrowType.ID()), CapabilityID: e.CapabilityID.String(), }) } + +// StorageCapConMissingBorrowType + +type storageCapConsMissingBorrowTypeEntry struct { + AccountAddress common.Address + AddressPath interpreter.AddressPath +} + +var _ valueMigrationReportEntry = storageCapConsMissingBorrowTypeEntry{} + +func (e storageCapConsMissingBorrowTypeEntry) accountAddress() common.Address { + return e.AccountAddress +} + +var _ json.Marshaler = storageCapConsMissingBorrowTypeEntry{} + +func (e storageCapConsMissingBorrowTypeEntry) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Kind string `json:"kind"` + AccountAddress string `json:"account_address"` + Address string `json:"address"` + Path string `json:"path"` + }{ + Kind: "storage-capcon-missing-borrow-type", + AccountAddress: e.AccountAddress.HexWithPrefix(), + Address: e.AddressPath.Address.HexWithPrefix(), + Path: e.AddressPath.Path.String(), + }) +} diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 62e88f04d30..41cfb014803 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2167,7 +2167,6 @@ func TestCapabilityMigration(t *testing.T) { require.NoError(t, err) storage := runtime.Storage - storageMapKey := interpreter.StringStorageMapKey("test") storageDomain := common.PathDomainStorage.Identifier() storageMap := storage.GetStorageMap( @@ -2184,6 +2183,8 @@ func TestCapabilityMigration(t *testing.T) { // Store a capability with storage path + fooCapStorageMapKey := interpreter.StringStorageMapKey("fooCap") + capabilityFoo := &interpreter.PathCapabilityValue{ BorrowType: borrowType, Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "foo"), @@ -2195,12 +2196,14 @@ func TestCapabilityMigration(t *testing.T) { storageMap.WriteValue( runtime.Interpreter, - storageMapKey, + fooCapStorageMapKey, capabilityFoo, ) // Store another capability with storage path, but without a borrow type. + barCapStorageMapKey := interpreter.StringStorageMapKey("barCap") + capabilityBar := &interpreter.PathCapabilityValue{ Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"), @@ -2211,7 +2214,7 @@ func TestCapabilityMigration(t *testing.T) { storageMap.WriteValue( runtime.Interpreter, - storageMapKey, + barCapStorageMapKey, capabilityBar, ) @@ -2269,11 +2272,18 @@ func TestCapabilityMigration(t *testing.T) { reporter := rwf.reportWriters[capabilityValueMigrationReporterName] require.NotNil(t, reporter) - require.Len(t, reporter.entries, 2) + require.Len(t, reporter.entries, 3) require.Equal( t, []any{ + capabilityMissingCapabilityIDEntry{ + AccountAddress: addressA, + AddressPath: interpreter.AddressPath{ + Address: addressB, + Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"), + }, + }, capabilityMigrationEntry{ AccountAddress: addressA, AddressPath: interpreter.AddressPath{ @@ -2288,7 +2298,7 @@ func TestCapabilityMigration(t *testing.T) { Key: storageDomain, Address: addressA, }, - StorageMapKey: storageMapKey, + StorageMapKey: fooCapStorageMapKey, Migration: "CapabilityValueMigration", }, }, @@ -2297,28 +2307,95 @@ func TestCapabilityMigration(t *testing.T) { issueStorageCapConReporter := rwf.reportWriters[issueStorageCapConMigrationReporterName] require.NotNil(t, issueStorageCapConReporter) - require.Len(t, issueStorageCapConReporter.entries, 1) + require.Len(t, issueStorageCapConReporter.entries, 2) + require.Equal( + t, + []any{ + storageCapConsMissingBorrowTypeEntry{ + AccountAddress: addressB, + AddressPath: interpreter.AddressPath{ + Address: addressB, + Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "bar"), + }, + }, + storageCapConIssuedEntry{ + AccountAddress: addressB, + AddressPath: interpreter.AddressPath{ + Address: addressB, + Path: interpreter.NewUnmeteredPathValue(common.PathDomainStorage, "foo"), + }, + BorrowType: borrowType, + CapabilityID: 3, + }, + }, + issueStorageCapConReporter.entries, + ) +} - entry := issueStorageCapConReporter.entries[0] +func TestStorageCapConIssuedEntry_MarshalJSON(t *testing.T) { - require.IsType(t, storageCapconIssuedEntry{}, entry) - storageCapconIssued := entry.(storageCapconIssuedEntry) + t.Parallel() - actual, err := storageCapconIssued.MarshalJSON() + e := storageCapConIssuedEntry{ + AccountAddress: common.MustBytesToAddress([]byte{0x2}), + AddressPath: interpreter.AddressPath{ + Address: common.MustBytesToAddress([]byte{0x1}), + Path: interpreter.PathValue{ + Domain: common.PathDomainStorage, + Identifier: "test", + }, + }, + BorrowType: interpreter.NewReferenceStaticType( + nil, + interpreter.UnauthorizedAccess, + interpreter.PrimitiveStaticTypeInt, + ), + CapabilityID: 3, + } + + actual, err := e.MarshalJSON() require.NoError(t, err) require.JSONEq(t, //language=JSON - fmt.Sprintf( - `{ - "kind": "storage-capcon-issued", - "account_address": "%s", - "path": "/storage/foo", - "borrow_type": "&AnyStruct", - "capability_id": "3" - }`, - addressB.HexWithPrefix(), - ), + `{ + "kind": "storage-capcon-issued", + "account_address": "0x0000000000000002", + "address": "0x0000000000000001", + "path": "/storage/test", + "borrow_type": "&Int", + "capability_id": "3" + }`, + string(actual), + ) +} + +func TestStorageCapConsMissingBorrowTypeEntry_MarshalJSON(t *testing.T) { + + t.Parallel() + + e := storageCapConsMissingBorrowTypeEntry{ + AccountAddress: common.MustBytesToAddress([]byte{0x2}), + AddressPath: interpreter.AddressPath{ + Address: common.MustBytesToAddress([]byte{0x1}), + Path: interpreter.PathValue{ + Domain: common.PathDomainStorage, + Identifier: "test", + }, + }, + } + + actual, err := e.MarshalJSON() + require.NoError(t, err) + + require.JSONEq(t, + //language=JSON + `{ + "kind": "storage-capcon-missing-borrow-type", + "account_address": "0x0000000000000002", + "address": "0x0000000000000001", + "path": "/storage/test" + }`, string(actual), ) } From d3ea857ac1f83a0ec8a91f020a0c4c8f1b1ac128 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Fri, 9 Aug 2024 13:05:28 -0700 Subject: [PATCH 3/3] Update to cadence v1.0.0-preview.44 --- go.mod | 2 +- go.sum | 4 ++-- insecure/go.mod | 2 +- insecure/go.sum | 4 ++-- integration/go.mod | 2 +- integration/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index f27670305ef..1a24448c28d 100644 --- a/go.mod +++ b/go.mod @@ -48,7 +48,7 @@ require ( github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multihash v0.2.3 github.com/onflow/atree v0.8.0-rc.5 - github.com/onflow/cadence v1.0.0-preview.43 + github.com/onflow/cadence v1.0.0-preview.44 github.com/onflow/crypto v0.25.1 github.com/onflow/flow v0.3.4 github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 diff --git a/go.sum b/go.sum index c623d5db725..e50b11b40b1 100644 --- a/go.sum +++ b/go.sum @@ -2167,8 +2167,8 @@ github.com/onflow/atree v0.8.0-rc.5/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/ github.com/onflow/boxo v0.0.0-20240201202436-f2477b92f483 h1:LpiQhTAfM9CAmNVEs0n//cBBgCg+vJSiIxTHYUklZ84= github.com/onflow/boxo v0.0.0-20240201202436-f2477b92f483/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.43 h1:Bnc+uNYMeE3Mj8wTH5vpT+d9wo9toYmwsucZhYbZtGE= -github.com/onflow/cadence v1.0.0-preview.43/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= +github.com/onflow/cadence v1.0.0-preview.44 h1:APQ8DzIS6kX4FMZsaYoucbvF/PPwjHEerr+aGRDcPz4= +github.com/onflow/cadence v1.0.0-preview.44/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= github.com/onflow/crypto v0.25.1 h1:0txy2PKPMM873JbpxQNbJmuOJtD56bfs48RQfm0ts5A= github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= diff --git a/insecure/go.mod b/insecure/go.mod index 59715ff84d1..4a5a8fb5d2f 100644 --- a/insecure/go.mod +++ b/insecure/go.mod @@ -202,7 +202,7 @@ require ( github.com/multiformats/go-varint v0.0.7 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onflow/atree v0.8.0-rc.5 // indirect - github.com/onflow/cadence v1.0.0-preview.43 // indirect + github.com/onflow/cadence v1.0.0-preview.44 // indirect github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 // indirect github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 // indirect github.com/onflow/flow-ft/lib/go/contracts v1.0.0 // indirect diff --git a/insecure/go.sum b/insecure/go.sum index 8d8524f7468..bb81e58ac7f 100644 --- a/insecure/go.sum +++ b/insecure/go.sum @@ -2157,8 +2157,8 @@ github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs github.com/onflow/atree v0.8.0-rc.5 h1:1sU+c6UfDzq/EjM8nTw4EI8GvEMarcxkWkJKy6piFSY= github.com/onflow/atree v0.8.0-rc.5/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.43 h1:Bnc+uNYMeE3Mj8wTH5vpT+d9wo9toYmwsucZhYbZtGE= -github.com/onflow/cadence v1.0.0-preview.43/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= +github.com/onflow/cadence v1.0.0-preview.44 h1:APQ8DzIS6kX4FMZsaYoucbvF/PPwjHEerr+aGRDcPz4= +github.com/onflow/cadence v1.0.0-preview.44/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= github.com/onflow/crypto v0.25.1 h1:0txy2PKPMM873JbpxQNbJmuOJtD56bfs48RQfm0ts5A= github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= diff --git a/integration/go.mod b/integration/go.mod index 28b396fa94a..0658515807c 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -20,7 +20,7 @@ require ( github.com/ipfs/go-ds-badger2 v0.1.3 github.com/ipfs/go-ds-pebble v0.3.1 github.com/libp2p/go-libp2p v0.32.2 - github.com/onflow/cadence v1.0.0-preview.43 + github.com/onflow/cadence v1.0.0-preview.44 github.com/onflow/crypto v0.25.1 github.com/onflow/flow-core-contracts/lib/go/contracts v1.3.1 github.com/onflow/flow-core-contracts/lib/go/templates v1.3.1 diff --git a/integration/go.sum b/integration/go.sum index fe04f457ff0..b8039a7b298 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -2141,8 +2141,8 @@ github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs github.com/onflow/atree v0.8.0-rc.5 h1:1sU+c6UfDzq/EjM8nTw4EI8GvEMarcxkWkJKy6piFSY= github.com/onflow/atree v0.8.0-rc.5/go.mod h1:yccR+LR7xc1Jdic0mrjocbHvUD7lnVvg8/Ct1AA5zBo= github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= -github.com/onflow/cadence v1.0.0-preview.43 h1:Bnc+uNYMeE3Mj8wTH5vpT+d9wo9toYmwsucZhYbZtGE= -github.com/onflow/cadence v1.0.0-preview.43/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= +github.com/onflow/cadence v1.0.0-preview.44 h1:APQ8DzIS6kX4FMZsaYoucbvF/PPwjHEerr+aGRDcPz4= +github.com/onflow/cadence v1.0.0-preview.44/go.mod h1:BCoenp1TYp+SmG7FGWStjehvvzcvNQ3xvpK5rkthq3Y= github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= github.com/onflow/crypto v0.25.1 h1:0txy2PKPMM873JbpxQNbJmuOJtD56bfs48RQfm0ts5A= github.com/onflow/crypto v0.25.1/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI=