From 78360b9d107c1e59aa5d52e28b88eb60890872cb Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 14 Aug 2024 12:57:59 -0700 Subject: [PATCH 01/21] Support a white list for type-requirement removals --- cmd/util/ledger/migrations/cadence.go | 57 +++- .../staged_contracts_migration_test.go | 275 ++++++++++++++++++ 2 files changed, 328 insertions(+), 4 deletions(-) diff --git a/cmd/util/ledger/migrations/cadence.go b/cmd/util/ledger/migrations/cadence.go index 8b795b1082d..9073da83ff2 100644 --- a/cmd/util/ledger/migrations/cadence.go +++ b/cmd/util/ledger/migrations/cadence.go @@ -41,6 +41,28 @@ func NewInterfaceTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRu } } +var typeRequirementRemovals = []struct { + address string + contractName string + typeName string +}{ + { + address: "e93c412c964bdf40", + contractName: "TiblesProducer", + typeName: "Producer", + }, + { + address: "e93c412c964bdf40", + contractName: "TiblesProducer", + typeName: "Minter", + }, + { + address: "e93c412c964bdf40", + contractName: "TiblesProducer", + typeName: "ContentLocation", + }, +} + func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRules { systemContracts := systemcontracts.SystemContractsForChain(chainID) @@ -51,11 +73,23 @@ func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRu oldNonFungibleTokenCollectionCompositeType, newNonFungibleTokenCollectionType := nonFungibleTokenCompositeToInterfaceRule(systemContracts, "Collection") - return StaticTypeMigrationRules{ + rules := StaticTypeMigrationRules{ oldFungibleTokenVaultCompositeType.ID(): newFungibleTokenVaultType, oldNonFungibleTokenNFTCompositeType.ID(): newNonFungibleTokenNFTType, oldNonFungibleTokenCollectionCompositeType.ID(): newNonFungibleTokenCollectionType, } + + for _, typeRemoval := range typeRequirementRemovals { + oldType, newType := compositeToInterfaceRule( + mustHexToAddress(typeRemoval.address), + typeRemoval.contractName, + typeRemoval.typeName, + ) + + rules[oldType.ID()] = newType + } + + return rules } func NewCadence1InterfaceStaticTypeConverter(chainID flow.ChainID) statictypes.InterfaceTypeConverterFunc { @@ -77,11 +111,26 @@ func nonFungibleTokenCompositeToInterfaceRule( ) { contract := systemContracts.NonFungibleToken - qualifiedIdentifier := fmt.Sprintf("%s.%s", contract.Name, identifier) + return compositeToInterfaceRule( + common.Address(contract.Address), + contract.Name, + identifier, + ) +} + +func compositeToInterfaceRule( + address common.Address, + contractName string, + typeName string, +) ( + *interpreter.CompositeStaticType, + *interpreter.IntersectionStaticType, +) { + qualifiedIdentifier := fmt.Sprintf("%s.%s", contractName, typeName) location := common.AddressLocation{ - Address: common.Address(contract.Address), - Name: contract.Name, + Address: address, + Name: contractName, } nftTypeID := location.TypeID(nil, qualifiedIdentifier) diff --git a/cmd/util/ledger/migrations/staged_contracts_migration_test.go b/cmd/util/ledger/migrations/staged_contracts_migration_test.go index bb253248893..44aedf57fb4 100644 --- a/cmd/util/ledger/migrations/staged_contracts_migration_test.go +++ b/cmd/util/ledger/migrations/staged_contracts_migration_test.go @@ -2407,3 +2407,278 @@ func TestContractUpdateFailureEntry_MarshalJSON(t *testing.T) { string(actual), ) } + +func TestTypeRequirementRemoval(t *testing.T) { + t.Parallel() + + const chainID = flow.Testnet + + t.Run("TiblesProducer", func(t *testing.T) { + t.Parallel() + + tiblesAddress := mustHexToAddress("e93c412c964bdf40") + + oldCode := ` + pub contract interface TiblesProducer { + + pub struct ContentLocation {} + pub struct interface IContentLocation {} + + pub resource interface IContent { + access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} + pub fun getMetadata(contentId: String): {String: AnyStruct}? + } + + pub resource interface IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource Producer: IContent, IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource interface IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + + pub resource Minter: IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + } + ` + + newCode := ` + access(all) contract interface TiblesProducer { + + access(all) struct interface ContentLocation {} + access(all) struct interface IContentLocation {} + + access(all) resource interface IContent { + access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}} + access(all) fun getMetadata(contentId: String): {String: AnyStruct}? + } + + access(all) resource interface IProducer { + access(contract) let minters: @{String: {Minter}} + } + + access(all) resource interface Producer: IContent, IProducer { + access(contract) let minters: @{String: {Minter}} + } + + access(all) resource interface IMinter { + access(all) let id: String + access(all) var lastMintNumber: UInt32 + access(all) let contentCapability: Capability + access(all) fun mintNext() + } + + access(all) resource interface Minter: IMinter { + access(all) let id: String + access(all) var lastMintNumber: UInt32 + access(all) let contentCapability: Capability + access(all) fun mintNext() + } + } + ` + + stagedContracts := []StagedContract{ + { + Address: tiblesAddress, + Contract: Contract{ + Name: "TiblesProducer", + Code: []byte(newCode), + }, + }, + } + + logWriter := &logWriter{} + log := zerolog.New(logWriter) + + rwf := &testReportWriterFactory{} + + options := StagedContractsMigrationOptions{ + ChainID: chainID, + VerboseErrorOutput: true, + } + + migration := NewStagedContractsMigration("test", "test", log, rwf, options). + WithStagedContractUpdates(stagedContracts). + WithContractUpdateValidation() + + registersByAccount, err := registersForStagedContracts( + StagedContract{ + Address: tiblesAddress, + Contract: Contract{ + Name: "TiblesProducer", + Code: []byte(oldCode), + }, + }, + ) + require.NoError(t, err) + + err = migration.InitMigration(log, registersByAccount, 1) + require.NoError(t, err) + + owner := string(tiblesAddress[:]) + accountRegisters := registersByAccount.AccountRegisters(owner) + + err = migration.MigrateAccount( + context.Background(), + tiblesAddress, + accountRegisters, + ) + require.NoError(t, err) + + err = migration.Close() + require.NoError(t, err) + + require.Empty(t, logWriter.logs) + + require.Equal(t, 1, accountRegisters.Count()) + assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, "TiblesProducer")) + }) + + t.Run("different address", func(t *testing.T) { + t.Parallel() + + addressGenerator := chainID.Chain().NewAddressGenerator() + randomAddress, err := addressGenerator.NextAddress() + require.NoError(t, err) + + oldCode := ` + pub contract interface TiblesProducer { + + pub struct ContentLocation {} + pub struct interface IContentLocation {} + + pub resource interface IContent { + access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} + pub fun getMetadata(contentId: String): {String: AnyStruct}? + } + + pub resource interface IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource Producer: IContent, IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource interface IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + + pub resource Minter: IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + } + ` + + newCode := ` + access(all) contract interface TiblesProducer { + + access(all) struct interface ContentLocation {} + access(all) struct interface IContentLocation {} + + access(all) resource interface IContent { + access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}} + access(all) fun getMetadata(contentId: String): {String: AnyStruct}? + } + + access(all) resource interface IProducer { + access(contract) let minters: @{String: {Minter}} + } + + access(all) resource interface Producer: IContent, IProducer { + access(contract) let minters: @{String: {Minter}} + } + + access(all) resource interface IMinter { + access(all) let id: String + access(all) var lastMintNumber: UInt32 + access(all) let contentCapability: Capability + access(all) fun mintNext() + } + + access(all) resource interface Minter: IMinter { + access(all) let id: String + access(all) var lastMintNumber: UInt32 + access(all) let contentCapability: Capability + access(all) fun mintNext() + } + } + ` + + stagedContracts := []StagedContract{ + { + Address: common.Address(randomAddress), + Contract: Contract{ + Name: "TiblesProducer", + Code: []byte(newCode), + }, + }, + } + + logWriter := &logWriter{} + log := zerolog.New(logWriter) + + rwf := &testReportWriterFactory{} + + options := StagedContractsMigrationOptions{ + ChainID: chainID, + VerboseErrorOutput: true, + } + + migration := NewStagedContractsMigration("test", "test", log, rwf, options). + WithStagedContractUpdates(stagedContracts). + WithContractUpdateValidation() + + registersByAccount, err := registersForStagedContracts( + StagedContract{ + Address: common.Address(randomAddress), + Contract: Contract{ + Name: "TiblesProducer", + Code: []byte(oldCode), + }, + }, + ) + require.NoError(t, err) + + err = migration.InitMigration(log, registersByAccount, 1) + require.NoError(t, err) + + owner := string(randomAddress[:]) + accountRegisters := registersByAccount.AccountRegisters(owner) + + err = migration.MigrateAccount( + context.Background(), + common.Address(randomAddress), + accountRegisters, + ) + require.NoError(t, err) + + err = migration.Close() + require.NoError(t, err) + + require.Len(t, logWriter.logs, 1) + errorLog := logWriter.logs[0] + require.Contains(t, errorLog, "incompatible type annotations. expected `Minter`, found `{Minter}") + require.Contains(t, errorLog, "incompatible type annotations. expected `TiblesProducer.ContentLocation`, found `{TiblesProducer.ContentLocation}") + + require.Equal(t, 1, accountRegisters.Count()) + assert.Equal(t, oldCode, contractCode(t, registersByAccount, owner, "TiblesProducer")) + }) +} From a7c38d0d793418a7b8e312fbd51be88bbd035a80 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 14 Aug 2024 13:29:52 -0700 Subject: [PATCH 02/21] Add storage migration test --- .../cadence_values_migration_test.go | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 97f0bffa2a7..59466aaacf8 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2692,6 +2692,156 @@ func TestStorageCapConsInferredBorrowTypeEntry_MarshalJSON(t *testing.T) { ) } +func TestTypeRequirementRemovalMigration(t *testing.T) { + t.Parallel() + + rwf := &testReportWriterFactory{} + + logWriter := &writer{} + logger := zerolog.New(logWriter).Level(zerolog.ErrorLevel) + + const nWorker = 2 + + const chainID = flow.Emulator + + payloads, err := newBootstrapPayloads(chainID) + require.NoError(t, err) + + registersByAccount, err := registers.NewByAccountFromPayloads(payloads) + require.NoError(t, err) + + storedAddress := common.Address(chainID.Chain().ServiceAddress()) + tiblesAddress := mustHexToAddress("e93c412c964bdf40") + + // Store a contract in `addressC`. + + migrationRuntime, err := NewInterpreterMigrationRuntime( + registersByAccount, + chainID, + InterpreterMigrationRuntimeConfig{}, + ) + require.NoError(t, err) + + storage := migrationRuntime.Storage + storageDomain := common.PathDomainStorage.Identifier() + + storageMap := storage.GetStorageMap( + storedAddress, + storageDomain, + true, + ) + + // Store a value with the actual `TiblesProducer.Minter` type + storageMap.WriteValue( + migrationRuntime.Interpreter, + interpreter.StringStorageMapKey("a"), + interpreter.NewTypeValue( + nil, + interpreter.NewCompositeStaticTypeComputeTypeID( + nil, + common.AddressLocation{ + Name: "TiblesProducer", + Address: tiblesAddress, + }, + "TiblesProducer.Minter", + ), + ), + ) + + // Store a value with a random `TiblesProducer.Minter` type (different address) + storageMap.WriteValue( + migrationRuntime.Interpreter, + interpreter.StringStorageMapKey("b"), + interpreter.NewTypeValue( + nil, + interpreter.NewCompositeStaticTypeComputeTypeID( + nil, + common.AddressLocation{ + Name: "TiblesProducer", + Address: storedAddress, + }, + "TiblesProducer.Minter", + ), + ), + ) + + // Commit + + err = storage.NondeterministicCommit(migrationRuntime.Interpreter, false) + require.NoError(t, err) + + // finalize the transaction + result, err := migrationRuntime.TransactionState.FinalizeMainTransaction() + require.NoError(t, err) + + // Merge the changes into the registers + + expectedAddresses := map[flow.Address]struct{}{ + flow.Address(storedAddress): {}, + } + + err = registers.ApplyChanges( + registersByAccount, + result.WriteSet, + expectedAddresses, + logger, + ) + require.NoError(t, err) + + // Migrate + + // TODO: EVM contract is not deployed in snapshot yet, so can't update it + const evmContractChange = EVMContractChangeNone + + const burnerContractChange = BurnerContractChangeUpdate + + migrations := NewCadence1Migrations( + logger, + t.TempDir(), + rwf, + Options{ + NWorker: nWorker, + ChainID: chainID, + EVMContractChange: evmContractChange, + BurnerContractChange: burnerContractChange, + VerboseErrorOutput: true, + }, + ) + + for _, migration := range migrations { + err = migration.Migrate(registersByAccount) + require.NoError( + t, + err, + "migration `%s` failed, logs: %v", + migration.Name, + logWriter.logs, + ) + } + + // Check account + + _, err = runScript( + chainID, + registersByAccount, + fmt.Sprintf( + //language=Cadence + ` + access(all) + fun main() { + let storage = getAuthAccount(%s).storage + assert(storage.copy(from: /storage/a)!.identifier == "{A.%s.TiblesProducer.Minter}") + assert(storage.copy(from: /storage/b)!.identifier == "A.%s.TiblesProducer.Minter") + } + `, + storedAddress.HexWithPrefix(), + tiblesAddress.Hex(), + storedAddress.Hex(), + ), + ) + require.NoError(t, err) +} + func runScript(chainID flow.ChainID, registersByAccount *registers.ByAccount, script string) (cadence.Value, error) { options := computation.DefaultFVMOptions(chainID, false, false) options = append(options, From a817d1c73497936150634fd74b2ff70610bb8977 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Mon, 19 Aug 2024 11:21:52 -0700 Subject: [PATCH 03/21] capture random and coinbase as part of tx executed events --- fvm/evm/events/events.go | 19 +++++++++++++++++++ fvm/evm/events/events_test.go | 14 ++++++++++++-- fvm/evm/handler/handler.go | 20 ++++++++++++++++++-- fvm/evm/stdlib/contract.cdc | 6 +++++- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index b59f34bd41c..8fce3d9fdb3 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -34,6 +34,8 @@ type transactionEvent struct { Payload []byte // transaction RLP-encoded payload Result *types.Result // transaction execution result BlockHeight uint64 + Random gethCommon.Hash + Coinbase *types.Address } // NewTransactionEvent creates a new transaction event with the given parameters @@ -44,13 +46,21 @@ func NewTransactionEvent( result *types.Result, payload []byte, blockHeight uint64, + random gethCommon.Hash, + coinbase types.Address, ) *Event { + var cb *types.Address + if coinbase != types.EmptyAddress { + cb = &coinbase + } return &Event{ Etype: EventTypeTransactionExecuted, Payload: &transactionEvent{ BlockHeight: blockHeight, Payload: payload, Result: result, + Random: random, + Coinbase: cb, }, } } @@ -70,6 +80,11 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error deployedAddress = cadence.String(p.Result.DeployedContractAddress.String()) } + coinbaseAddress := cadence.String("") + if p.Coinbase != nil { + coinbaseAddress = cadence.String(p.Coinbase.String()) + } + errorMsg := "" if p.Result.VMError != nil { errorMsg = p.Result.VMError.Error() @@ -95,6 +110,8 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), + hashToCadenceArrayValue(p.Random), + coinbaseAddress, }).WithType(eventType), nil } @@ -161,6 +178,8 @@ type TransactionEventPayload struct { ErrorMessage string `cadence:"errorMessage"` ReturnedData []byte `cadence:"returnedData"` PrecompiledCalls []byte `cadence:"precompiledCalls"` + Random gethCommon.Hash `cadence:"random"` + Coinbase string `cadence:"coinbase"` } // DecodeTransactionEventPayload decodes Cadence event into transaction event payload. diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index e30819ca31e..4883f02c109 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -83,6 +83,8 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) + random := testutils.RandomCommonHash(t) + coinbase := testutils.RandomAddress(t) data := "000000000000000000000000000000000000000000000000000000000000002a" dataBytes, err := hex.DecodeString(data) require.NoError(t, err) @@ -112,7 +114,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { - event := events.NewTransactionEvent(txResult, txBytes, blockHeight) + event := events.NewTransactionEvent(txResult, txBytes, blockHeight, random, coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -120,6 +122,8 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) + assert.Equal(t, tep.Random, random) + assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) assert.Equal(t, types.ErrorCode(tep.ErrorCode), types.ExecutionErrCodeOutOfGas) @@ -154,7 +158,11 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { t.Run("evm.TransactionExecuted with non-failed status", func(t *testing.T) { txResult.VMError = nil - event := events.NewTransactionEvent(txResult, txBytes, blockHeight) + event := events.NewTransactionEvent(txResult, + txBytes, + blockHeight, + random, + coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -162,6 +170,8 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) + assert.Equal(t, tep.Random, random) + assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) assert.Equal(t, types.ErrCodeNoError, types.ErrorCode(tep.ErrorCode)) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index c8e0338778c..b9b152fb9c0 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -274,6 +274,8 @@ func (h *ContractHandler) batchRun( r, rlpEncodedTxs[i], bp.Height, + ctx.Random, + ctx.GasFeeCollector, )) if err != nil { return nil, err @@ -395,7 +397,15 @@ func (h *ContractHandler) run( } // step 9 - emit transaction event - err = h.emitEvent(events.NewTransactionEvent(res, rlpEncodedTx, bp.Height)) + err = h.emitEvent( + events.NewTransactionEvent( + res, + rlpEncodedTx, + bp.Height, + ctx.Random, + ctx.GasFeeCollector, + )) + if err != nil { return nil, err } @@ -607,7 +617,13 @@ func (h *ContractHandler) executeAndHandleCall( return nil, err } err = h.emitEvent( - events.NewTransactionEvent(res, encoded, bp.Height), + events.NewTransactionEvent( + res, + encoded, + bp.Height, + ctx.Random, + ctx.GasFeeCollector, + ), ) if err != nil { return nil, err diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index e5a075fcc13..3a989f68bcd 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -71,7 +71,11 @@ contract EVM { /// precompiled contracts (e.g. Cadence Arch) during the transaction execution. /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. - precompiledCalls: [UInt8] + precompiledCalls: [UInt8], + /// random value used for PREVRANDO opcode + random: [UInt8; 32], + /// coinbase value used for COINBASE opcode (if empty no coinbase is set) + coinbase: String ) access(all) From 569ae2c7589e5b8f7a8f9b03c4e116bcb15c50a4 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Mon, 19 Aug 2024 13:52:50 -0700 Subject: [PATCH 04/21] Extract the type requirements from old code --- cmd/util/ledger/migrations/cadence.go | 89 +++-- .../cadence_values_migration_test.go | 62 +++- .../change_contract_code_migration.go | 7 +- .../change_contract_code_migration_test.go | 72 +++- .../migrations/staged_contracts_migration.go | 27 +- .../staged_contracts_migration_test.go | 332 +++++++++++++++--- .../migrations/static_type_migration.go | 16 +- .../migrations/type_requirements_extractor.go | 158 +++++++++ 8 files changed, 651 insertions(+), 112 deletions(-) create mode 100644 cmd/util/ledger/migrations/type_requirements_extractor.go diff --git a/cmd/util/ledger/migrations/cadence.go b/cmd/util/ledger/migrations/cadence.go index 9073da83ff2..a46e993f659 100644 --- a/cmd/util/ledger/migrations/cadence.go +++ b/cmd/util/ledger/migrations/cadence.go @@ -41,29 +41,10 @@ func NewInterfaceTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRu } } -var typeRequirementRemovals = []struct { - address string - contractName string - typeName string -}{ - { - address: "e93c412c964bdf40", - contractName: "TiblesProducer", - typeName: "Producer", - }, - { - address: "e93c412c964bdf40", - contractName: "TiblesProducer", - typeName: "Minter", - }, - { - address: "e93c412c964bdf40", - contractName: "TiblesProducer", - typeName: "ContentLocation", - }, -} - -func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRules { +func NewCompositeTypeConversionRules( + chainID flow.ChainID, + legacyTypeRequirements *LegacyTypeRequirements, +) StaticTypeMigrationRules { systemContracts := systemcontracts.SystemContractsForChain(chainID) oldFungibleTokenVaultCompositeType, newFungibleTokenVaultType := @@ -79,11 +60,11 @@ func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRu oldNonFungibleTokenCollectionCompositeType.ID(): newNonFungibleTokenCollectionType, } - for _, typeRemoval := range typeRequirementRemovals { + for _, typeRequirement := range legacyTypeRequirements.typeRequirements { oldType, newType := compositeToInterfaceRule( - mustHexToAddress(typeRemoval.address), - typeRemoval.contractName, - typeRemoval.typeName, + typeRequirement.Address, + typeRequirement.ContractName, + typeRequirement.TypeName, ) rules[oldType.ID()] = newType @@ -93,13 +74,22 @@ func NewCompositeTypeConversionRules(chainID flow.ChainID) StaticTypeMigrationRu } func NewCadence1InterfaceStaticTypeConverter(chainID flow.ChainID) statictypes.InterfaceTypeConverterFunc { - rules := NewInterfaceTypeConversionRules(chainID) - return NewStaticTypeMigration[*interpreter.InterfaceStaticType](rules) + return NewStaticTypeMigration[*interpreter.InterfaceStaticType]( + func() StaticTypeMigrationRules { + return NewInterfaceTypeConversionRules(chainID) + }, + ) } -func NewCadence1CompositeStaticTypeConverter(chainID flow.ChainID) statictypes.CompositeTypeConverterFunc { - rules := NewCompositeTypeConversionRules(chainID) - return NewStaticTypeMigration[*interpreter.CompositeStaticType](rules) +func NewCadence1CompositeStaticTypeConverter( + chainID flow.ChainID, + legacyTypeRequirements *LegacyTypeRequirements, +) statictypes.CompositeTypeConverterFunc { + return NewStaticTypeMigration[*interpreter.CompositeStaticType]( + func() StaticTypeMigrationRules { + return NewCompositeTypeConversionRules(chainID, legacyTypeRequirements) + }, + ) } func nonFungibleTokenCompositeToInterfaceRule( @@ -437,6 +427,7 @@ func NewCadence1ValueMigrations( log zerolog.Logger, rwf reporters.ReportWriterFactory, importantLocations map[common.AddressLocation]struct{}, + legacyTypeRequirements *LegacyTypeRequirements, opts Options, ) (migs []NamedMigration) { @@ -493,7 +484,7 @@ func NewCadence1ValueMigrations( rwf, errorMessageHandler, programs, - NewCadence1CompositeStaticTypeConverter(opts.ChainID), + NewCadence1CompositeStaticTypeConverter(opts.ChainID, legacyTypeRequirements), NewCadence1InterfaceStaticTypeConverter(opts.ChainID), storageDomainCapabilities, opts, @@ -584,10 +575,11 @@ const stagedContractUpdateMigrationName = "staged-contracts-update-migration" func NewCadence1ContractsMigrations( log zerolog.Logger, rwf reporters.ReportWriterFactory, + importantLocations map[common.AddressLocation]struct{}, + legacyTypeRequirements *LegacyTypeRequirements, opts Options, ) ( migs []NamedMigration, - importantLocations map[common.AddressLocation]struct{}, ) { stagedContractsMigrationOptions := StagedContractsMigrationOptions{ @@ -602,9 +594,10 @@ func NewCadence1ContractsMigrations( } var systemContractsMigration *StagedContractsMigration - systemContractsMigration, importantLocations = NewSystemContractsMigration( + systemContractsMigration = NewSystemContractsMigration( log, rwf, + importantLocations, systemContractsMigrationOptions, ) @@ -613,6 +606,7 @@ func NewCadence1ContractsMigrations( "staged-contracts-migration", log, rwf, + legacyTypeRequirements, stagedContractsMigrationOptions, ).WithContractUpdateValidation(). WithStagedContractUpdates(opts.StagedContracts) @@ -670,7 +664,7 @@ func NewCadence1ContractsMigrations( }, ) - return migs, importantLocations + return migs } var testnetAccountsWithBrokenSlabReferences = func() map[common.Address]struct{} { @@ -797,9 +791,29 @@ func NewCadence1Migrations( } } - cadence1ContractsMigrations, importantLocations := NewCadence1ContractsMigrations( + importantLocations := make(map[common.AddressLocation]struct{}) + legacyTypeRequirements := &LegacyTypeRequirements{} + + cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( + log, + rwf, + importantLocations, + legacyTypeRequirements, + ) + + migs = append( + migs, + NamedMigration{ + Name: "extract-type-requirements", + Migrate: cadenceTypeRequirementsExtractor, + }, + ) + + cadence1ContractsMigrations := NewCadence1ContractsMigrations( log, rwf, + importantLocations, + legacyTypeRequirements, opts, ) @@ -814,6 +828,7 @@ func NewCadence1Migrations( log, rwf, importantLocations, + legacyTypeRequirements, opts, )..., ) diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 59466aaacf8..0b1c3b73d47 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2702,7 +2702,7 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { const nWorker = 2 - const chainID = flow.Emulator + const chainID = flow.Testnet payloads, err := newBootstrapPayloads(chainID) require.NoError(t, err) @@ -2731,6 +2731,8 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { true, ) + contractName := "TiblesProducer" + // Store a value with the actual `TiblesProducer.Minter` type storageMap.WriteValue( migrationRuntime.Interpreter, @@ -2740,7 +2742,7 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { interpreter.NewCompositeStaticTypeComputeTypeID( nil, common.AddressLocation{ - Name: "TiblesProducer", + Name: contractName, Address: tiblesAddress, }, "TiblesProducer.Minter", @@ -2757,7 +2759,7 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { interpreter.NewCompositeStaticTypeComputeTypeID( nil, common.AddressLocation{ - Name: "TiblesProducer", + Name: contractName, Address: storedAddress, }, "TiblesProducer.Minter", @@ -2788,6 +2790,60 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { ) require.NoError(t, err) + // Set contract code + + oldCode := ` + pub contract interface TiblesProducer { + + pub struct ContentLocation {} + pub struct interface IContentLocation {} + + pub resource interface IContent { + access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} + pub fun getMetadata(contentId: String): {String: AnyStruct}? + } + + pub resource interface IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource Producer: IContent, IProducer { + access(contract) let minters: @{String: Minter} + } + + pub resource interface IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + + pub resource Minter: IMinter { + pub let id: String + pub var lastMintNumber: UInt32 + pub let contentCapability: Capability + pub fun mintNext() + } + } + ` + + err = registersByAccount.Set( + string(tiblesAddress[:]), + flow.ContractKey(contractName), + []byte(oldCode), + ) + require.NoError(t, err) + + encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) + require.NoError(t, err) + + err = registersByAccount.Set( + string(tiblesAddress[:]), + flow.ContractNamesKey, + encodedContractNames, + ) + require.NoError(t, err) + // Migrate // TODO: EVM contract is not deployed in snapshot yet, so can't update it diff --git a/cmd/util/ledger/migrations/change_contract_code_migration.go b/cmd/util/ledger/migrations/change_contract_code_migration.go index a809228f467..6deefd278fd 100644 --- a/cmd/util/ledger/migrations/change_contract_code_migration.go +++ b/cmd/util/ledger/migrations/change_contract_code_migration.go @@ -286,25 +286,24 @@ type SystemContractsMigrationOptions struct { func NewSystemContractsMigration( log zerolog.Logger, rwf reporters.ReportWriterFactory, + locations map[common.AddressLocation]struct{}, options SystemContractsMigrationOptions, ) ( migration *StagedContractsMigration, - locations map[common.AddressLocation]struct{}, ) { migration = NewStagedContractsMigration( "SystemContractsMigration", "system-contracts-migration", log, rwf, + &LegacyTypeRequirements{}, // This is empty for system contracts options.StagedContractsMigrationOptions, ) - locations = make(map[common.AddressLocation]struct{}) - for _, change := range SystemContractChanges(options.ChainID, options) { migration.registerContractChange(change) locations[change.AddressLocation()] = struct{}{} } - return migration, locations + return migration } diff --git a/cmd/util/ledger/migrations/change_contract_code_migration_test.go b/cmd/util/ledger/migrations/change_contract_code_migration_test.go index 0e89df23f0a..e9bf4c7d0bd 100644 --- a/cmd/util/ledger/migrations/change_contract_code_migration_test.go +++ b/cmd/util/ledger/migrations/change_contract_code_migration_test.go @@ -81,7 +81,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options) + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ) registersByAccount := registers.NewByAccount() @@ -113,7 +120,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options) + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ) registersByAccount, err := registersForStagedContracts( StagedContract{ @@ -165,7 +179,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address1), @@ -226,7 +247,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address1), @@ -298,7 +326,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address1), @@ -377,7 +412,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address1), @@ -452,7 +494,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address1), @@ -514,7 +563,14 @@ func TestChangeContractCodeMigration(t *testing.T) { ChainID: flow.Emulator, VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates([]StagedContract{ { Address: common.Address(address2), diff --git a/cmd/util/ledger/migrations/staged_contracts_migration.go b/cmd/util/ledger/migrations/staged_contracts_migration.go index 72987e7adf3..54725538726 100644 --- a/cmd/util/ledger/migrations/staged_contracts_migration.go +++ b/cmd/util/ledger/migrations/staged_contracts_migration.go @@ -42,6 +42,7 @@ type StagedContractsMigration struct { contractNamesProvider stdlib.AccountContractNamesProvider reporter reporters.ReportWriter verboseErrorOutput bool + legacyTypeRequirements *LegacyTypeRequirements } type StagedContract struct { @@ -73,22 +74,23 @@ func NewStagedContractsMigration( reporterName string, log zerolog.Logger, rwf reporters.ReportWriterFactory, + legacyTypeRequirements *LegacyTypeRequirements, options StagedContractsMigrationOptions, ) *StagedContractsMigration { return &StagedContractsMigration{ - name: name, - log: log, - chainID: options.ChainID, - stagedContracts: map[common.Address]map[string]Contract{}, - contractsByLocation: map[common.Location][]byte{}, - reporter: rwf.ReportWriter(reporterName), - verboseErrorOutput: options.VerboseErrorOutput, + name: name, + log: log, + chainID: options.ChainID, + legacyTypeRequirements: legacyTypeRequirements, + stagedContracts: map[common.Address]map[string]Contract{}, + contractsByLocation: map[common.Location][]byte{}, + reporter: rwf.ReportWriter(reporterName), + verboseErrorOutput: options.VerboseErrorOutput, } } func (m *StagedContractsMigration) WithContractUpdateValidation() *StagedContractsMigration { m.enableUpdateValidation = true - m.userDefinedTypeChangeCheckFunc = NewUserDefinedTypeChangeCheckerFunc(m.chainID) return m } @@ -179,6 +181,12 @@ func (m *StagedContractsMigration) InitMigration( m.contractAdditionHandler = mr.ContractAdditionHandler m.contractNamesProvider = mr.ContractNamesProvider + // `legacyTypeRequirements` are populated by a previous migration. + // So it should only be used once the previous migrations are complete. + if m.enableUpdateValidation { + m.userDefinedTypeChangeCheckFunc = NewUserDefinedTypeChangeCheckerFunc(m.chainID, m.legacyTypeRequirements) + } + return nil } @@ -646,11 +654,12 @@ func StagedContractsFromCSV(path string) ([]StagedContract, error) { func NewUserDefinedTypeChangeCheckerFunc( chainID flow.ChainID, + legacyTypeRequirements *LegacyTypeRequirements, ) func(oldTypeID common.TypeID, newTypeID common.TypeID) (checked, valid bool) { typeChangeRules := map[common.TypeID]common.TypeID{} - compositeTypeRules := NewCompositeTypeConversionRules(chainID) + compositeTypeRules := NewCompositeTypeConversionRules(chainID, legacyTypeRequirements) for typeID, newStaticType := range compositeTypeRules { typeChangeRules[typeID] = newStaticType.ID() } diff --git a/cmd/util/ledger/migrations/staged_contracts_migration_test.go b/cmd/util/ledger/migrations/staged_contracts_migration_test.go index 44aedf57fb4..972177e634f 100644 --- a/cmd/util/ledger/migrations/staged_contracts_migration_test.go +++ b/cmd/util/ledger/migrations/staged_contracts_migration_test.go @@ -96,7 +96,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates(stagedContracts) registersByAccount, err := registersForStagedContracts( @@ -161,7 +168,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation() migration.WithStagedContractUpdates(stagedContracts) @@ -228,7 +242,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -309,7 +330,14 @@ func TestStagedContractsMigration(t *testing.T) { } const reporterName = "test" - migration := NewStagedContractsMigration("test", reporterName, log, rwf, options). + migration := NewStagedContractsMigration( + "test", + reporterName, + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -405,7 +433,14 @@ func TestStagedContractsMigration(t *testing.T) { } const reporterName = "test" - migration := NewStagedContractsMigration("test", reporterName, log, rwf, options). + migration := NewStagedContractsMigration( + "test", + reporterName, + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates(stagedContracts) registersByAccount, err := registersForStagedContracts( @@ -514,7 +549,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates(stagedContracts) registersByAccount, err := registersForStagedContracts( @@ -581,7 +623,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates(stagedContracts) // NOTE: no payloads @@ -736,7 +785,14 @@ func TestStagedContractsMigration(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options) + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ) accountOwner := string(accountAddress[:]) @@ -846,7 +902,14 @@ func TestStagedContractsWithImports(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithStagedContractUpdates(stagedContracts) registersByAccount, err := registersForStagedContracts( @@ -945,7 +1008,14 @@ func TestStagedContractsWithImports(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1060,7 +1130,14 @@ func TestStagedContractsWithImports(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1180,7 +1257,14 @@ func TestStagedContractsWithImports(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1431,7 +1515,14 @@ func TestStagedContractsWithUpdateValidator(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1548,7 +1639,14 @@ func TestStagedContractsWithUpdateValidator(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1654,7 +1752,14 @@ func TestStagedContractsWithUpdateValidator(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1786,7 +1891,14 @@ func TestStagedContractConformanceChanges(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -1919,7 +2031,14 @@ func TestStagedContractConformanceChanges(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -2189,7 +2308,14 @@ func TestStagedContractsUpdateValidationErrors(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -2305,7 +2431,14 @@ func TestStagedContractsUpdateValidationErrors(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + &LegacyTypeRequirements{}, + options, + ). WithContractUpdateValidation(). WithStagedContractUpdates(stagedContracts) @@ -2413,7 +2546,7 @@ func TestTypeRequirementRemoval(t *testing.T) { const chainID = flow.Testnet - t.Run("TiblesProducer", func(t *testing.T) { + t.Run("TiblesProducer contract", func(t *testing.T) { t.Parallel() tiblesAddress := mustHexToAddress("e93c412c964bdf40") @@ -2488,11 +2621,13 @@ func TestTypeRequirementRemoval(t *testing.T) { } ` + contractName := "TiblesProducer" + stagedContracts := []StagedContract{ { Address: tiblesAddress, Contract: Contract{ - Name: "TiblesProducer", + Name: contractName, Code: []byte(newCode), }, }, @@ -2508,21 +2643,75 @@ func TestTypeRequirementRemoval(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). - WithStagedContractUpdates(stagedContracts). - WithContractUpdateValidation() - registersByAccount, err := registersForStagedContracts( StagedContract{ Address: tiblesAddress, Contract: Contract{ - Name: "TiblesProducer", + Name: contractName, Code: []byte(oldCode), }, }, ) require.NoError(t, err) + encodedContractNames, err := environment.EncodeContractNames([]string{contractName}) + require.NoError(t, err) + + err = registersByAccount.Set( + string(tiblesAddress[:]), + flow.ContractNamesKey, + encodedContractNames, + ) + require.NoError(t, err) + + // Run type-requirement extractor + + legacyTypeRequirements := &LegacyTypeRequirements{} + + cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( + log, + rwf, + nil, + legacyTypeRequirements, + ) + err = cadenceTypeRequirementsExtractor(registersByAccount) + require.NoError(t, err) + + require.Equal( + t, + []TypeRequirement{ + { + Address: tiblesAddress, + ContractName: contractName, + TypeName: "ContentLocation", + }, + { + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Producer", + }, + { + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Minter", + }, + }, + legacyTypeRequirements.typeRequirements, + ) + + // Run staged contract migration + + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + legacyTypeRequirements, + options, + ). + WithStagedContractUpdates(stagedContracts). + WithContractUpdateValidation() + err = migration.InitMigration(log, registersByAccount, 1) require.NoError(t, err) @@ -2541,11 +2730,11 @@ func TestTypeRequirementRemoval(t *testing.T) { require.Empty(t, logWriter.logs) - require.Equal(t, 1, accountRegisters.Count()) - assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, "TiblesProducer")) + require.Equal(t, 2, accountRegisters.Count()) + assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, contractName)) }) - t.Run("different address", func(t *testing.T) { + t.Run("random contract", func(t *testing.T) { t.Parallel() addressGenerator := chainID.Chain().NewAddressGenerator() @@ -2553,13 +2742,13 @@ func TestTypeRequirementRemoval(t *testing.T) { require.NoError(t, err) oldCode := ` - pub contract interface TiblesProducer { + pub contract interface Foo { pub struct ContentLocation {} pub struct interface IContentLocation {} pub resource interface IContent { - access(contract) let contentIdsToPaths: {String: TiblesProducer.ContentLocation} + access(contract) let contentIdsToPaths: {String: Foo.ContentLocation} pub fun getMetadata(contentId: String): {String: AnyStruct}? } @@ -2588,13 +2777,13 @@ func TestTypeRequirementRemoval(t *testing.T) { ` newCode := ` - access(all) contract interface TiblesProducer { + access(all) contract interface Foo { access(all) struct interface ContentLocation {} access(all) struct interface IContentLocation {} access(all) resource interface IContent { - access(contract) let contentIdsToPaths: {String: {TiblesProducer.ContentLocation}} + access(contract) let contentIdsToPaths: {String: {Foo.ContentLocation}} access(all) fun getMetadata(contentId: String): {String: AnyStruct}? } @@ -2622,11 +2811,13 @@ func TestTypeRequirementRemoval(t *testing.T) { } ` + fooContractName := "Foo" + stagedContracts := []StagedContract{ { Address: common.Address(randomAddress), Contract: Contract{ - Name: "TiblesProducer", + Name: fooContractName, Code: []byte(newCode), }, }, @@ -2642,21 +2833,75 @@ func TestTypeRequirementRemoval(t *testing.T) { VerboseErrorOutput: true, } - migration := NewStagedContractsMigration("test", "test", log, rwf, options). - WithStagedContractUpdates(stagedContracts). - WithContractUpdateValidation() - registersByAccount, err := registersForStagedContracts( StagedContract{ Address: common.Address(randomAddress), Contract: Contract{ - Name: "TiblesProducer", + Name: fooContractName, Code: []byte(oldCode), }, }, ) require.NoError(t, err) + encodedContractNames, err := environment.EncodeContractNames([]string{fooContractName}) + require.NoError(t, err) + + err = registersByAccount.Set( + string(randomAddress[:]), + flow.ContractNamesKey, + encodedContractNames, + ) + require.NoError(t, err) + + // Run type-requirement extractor + + legacyTypeRequirements := &LegacyTypeRequirements{} + + cadenceTypeRequirementsExtractor := NewTypeRequirementsExtractingMigration( + log, + rwf, + nil, + legacyTypeRequirements, + ) + err = cadenceTypeRequirementsExtractor(registersByAccount) + require.NoError(t, err) + + require.Equal( + t, + []TypeRequirement{ + { + Address: common.Address(randomAddress), + ContractName: fooContractName, + TypeName: "ContentLocation", + }, + { + Address: common.Address(randomAddress), + ContractName: fooContractName, + TypeName: "Producer", + }, + { + Address: common.Address(randomAddress), + ContractName: fooContractName, + TypeName: "Minter", + }, + }, + legacyTypeRequirements.typeRequirements, + ) + + // Run staged contract migration + + migration := NewStagedContractsMigration( + "test", + "test", + log, + rwf, + legacyTypeRequirements, + options, + ). + WithStagedContractUpdates(stagedContracts). + WithContractUpdateValidation() + err = migration.InitMigration(log, registersByAccount, 1) require.NoError(t, err) @@ -2673,12 +2918,9 @@ func TestTypeRequirementRemoval(t *testing.T) { err = migration.Close() require.NoError(t, err) - require.Len(t, logWriter.logs, 1) - errorLog := logWriter.logs[0] - require.Contains(t, errorLog, "incompatible type annotations. expected `Minter`, found `{Minter}") - require.Contains(t, errorLog, "incompatible type annotations. expected `TiblesProducer.ContentLocation`, found `{TiblesProducer.ContentLocation}") + require.Empty(t, logWriter.logs) - require.Equal(t, 1, accountRegisters.Count()) - assert.Equal(t, oldCode, contractCode(t, registersByAccount, owner, "TiblesProducer")) + require.Equal(t, 2, accountRegisters.Count()) + assert.Equal(t, newCode, contractCode(t, registersByAccount, owner, fooContractName)) }) } diff --git a/cmd/util/ledger/migrations/static_type_migration.go b/cmd/util/ledger/migrations/static_type_migration.go index 11c7e5c95a8..ec8cd6577a1 100644 --- a/cmd/util/ledger/migrations/static_type_migration.go +++ b/cmd/util/ledger/migrations/static_type_migration.go @@ -8,18 +8,22 @@ import ( type StaticTypeMigrationRules map[common.TypeID]interpreter.StaticType func NewStaticTypeMigration[T interpreter.StaticType]( - rules StaticTypeMigrationRules, + rulesGetter func() StaticTypeMigrationRules, ) func(staticType T) interpreter.StaticType { - // Returning `nil` form the callback indicates the type wasn't converted. + var rules StaticTypeMigrationRules - if rules == nil { - return func(original T) interpreter.StaticType { + return func(original T) interpreter.StaticType { + // Initialize only once + if rules == nil { + rules = rulesGetter() + } + + // Returning `nil` form the callback indicates the type wasn't converted. + if rules == nil { return nil } - } - return func(original T) interpreter.StaticType { if replacement, ok := rules[original.ID()]; ok { return replacement } diff --git a/cmd/util/ledger/migrations/type_requirements_extractor.go b/cmd/util/ledger/migrations/type_requirements_extractor.go new file mode 100644 index 00000000000..2caeed2d6b1 --- /dev/null +++ b/cmd/util/ledger/migrations/type_requirements_extractor.go @@ -0,0 +1,158 @@ +package migrations + +import ( + "bytes" + "fmt" + "github.com/onflow/cadence/runtime/common" + "github.com/onflow/cadence/runtime/old_parser" + "github.com/rs/zerolog" + "sort" + + "github.com/onflow/flow-go/cmd/util/ledger/reporters" + "github.com/onflow/flow-go/cmd/util/ledger/util/registers" + "github.com/onflow/flow-go/fvm/environment" + "github.com/onflow/flow-go/model/flow" +) + +const typeRequirementExtractingReporterName = "type-requirements-extracting" + +type LegacyTypeRequirements struct { + typeRequirements []TypeRequirement +} + +type TypeRequirement struct { + Address common.Address + ContractName string + TypeName string +} + +func NewTypeRequirementsExtractingMigration( + log zerolog.Logger, + rwf reporters.ReportWriterFactory, + importantLocations map[common.AddressLocation]struct{}, + legacyTypeRequirements *LegacyTypeRequirements, +) RegistersMigration { + return func(registersByAccount *registers.ByAccount) error { + + reporter := rwf.ReportWriter(typeRequirementExtractingReporterName) + defer reporter.Close() + + // Gather all contracts + + log.Info().Msg("Gathering contracts ...") + + contracts := make([]AddressContract, 0, contractCountEstimate) + + err := registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { + owner := accountRegisters.Owner() + + encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) + if err != nil { + return err + } + + contractNames, err := environment.DecodeContractNames(encodedContractNames) + if err != nil { + return err + } + + for _, contractName := range contractNames { + + contractKey := flow.ContractKey(contractName) + + code, err := accountRegisters.Get(owner, contractKey) + if err != nil { + return err + } + + if len(bytes.TrimSpace(code)) == 0 { + continue + } + + address := common.Address([]byte(owner)) + location := common.AddressLocation{ + Address: address, + Name: contractName, + } + + if _, isSystemContract := importantLocations[location]; isSystemContract { + // System contracts have their own type-changing rules. + // So do not add them here. + continue + } + + contracts = append( + contracts, + AddressContract{ + location: location, + code: code, + }, + ) + } + + return nil + }) + if err != nil { + return fmt.Errorf("failed to get contracts of accounts: %w", err) + } + + sort.Slice(contracts, func(i, j int) bool { + a := contracts[i] + b := contracts[j] + return a.location.ID() < b.location.ID() + }) + + log.Info().Msgf("Gathered all contracts (%d)", len(contracts)) + + // Check all contracts + + for _, contract := range contracts { + extractTypeRequirements( + contract, + log, + reporter, + legacyTypeRequirements, + ) + } + + return nil + } +} + +func extractTypeRequirements( + contract AddressContract, + log zerolog.Logger, + reporter reporters.ReportWriter, + legacyTypeRequirements *LegacyTypeRequirements, +) { + + // must be parsed with the old parser. + program, err := old_parser.ParseProgram( + nil, + contract.code, + old_parser.Config{}, + ) + + if err != nil { + // If the old contract cannot be parsed, then ignore + return + } + + contractInterface := program.SoleContractInterfaceDeclaration() + if contractInterface == nil { + // Type requirements can only reside inside contract interfaces. + // Ignore all other programs. + return + } + + for _, composites := range contractInterface.DeclarationMembers().Composites() { + legacyTypeRequirements.typeRequirements = append( + legacyTypeRequirements.typeRequirements, + TypeRequirement{ + Address: contract.location.Address, + ContractName: contractInterface.Identifier.Identifier, + TypeName: composites.Identifier.Identifier, + }, + ) + } +} From d63575afb053550a023c205a0ccc19e6676611fa Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 08:59:03 -0700 Subject: [PATCH 05/21] move random to block levle --- fvm/evm/handler/blockstore.go | 8 +++ fvm/evm/handler/handler.go | 7 +-- fvm/evm/types/block.go | 95 +++++++++++++++++++++++++++++++++-- fvm/evm/types/block_test.go | 52 ++++++++++++++++++- 4 files changed, 152 insertions(+), 10 deletions(-) diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index c1dcf77cb49..9c6fc128d99 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -76,11 +76,19 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { // expect timestamps in unix seconds so we convert here timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second)) + // read a random value for block proposal + rand := gethCommon.Hash{} + err = bs.backend.ReadRandom(rand[:]) + if err != nil { + return nil, err + } + blockProposal := types.NewBlockProposal( parentHash, lastExecutedBlock.Height+1, timestamp, lastExecutedBlock.TotalSupply, + rand, ) return blockProposal, nil } diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index b9b152fb9c0..c92239fc085 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -517,11 +517,6 @@ func (h *ContractHandler) getBlockContext() (types.BlockContext, error) { if err != nil { return types.BlockContext{}, err } - rand := gethCommon.Hash{} - err = h.backend.ReadRandom(rand[:]) - if err != nil { - return types.BlockContext{}, err - } return types.BlockContext{ ChainID: types.EVMChainIDFromFlowChainID(h.flowChainID), @@ -534,7 +529,7 @@ func (h *ContractHandler) getBlockContext() (types.BlockContext, error) { return hash }, ExtraPrecompiledContracts: h.precompiledContracts, - Random: rand, + Random: bp.Prevrandao, Tracer: h.tracer.TxTracer(), TxCountSoFar: uint(len(bp.TxHashes)), TotalGasUsedSoFar: bp.TotalGasUsed, diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index bd2ab4e8408..dbbf3f71bdc 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -44,8 +44,11 @@ type Block struct { // values as node values. Proofs are still compatible but might require an extra hashing step. TransactionHashRoot gethCommon.Hash - // stores gas used by all transactions included in the block. + // TotalGasUsed stores gas used by all transactions included in the block. TotalGasUsed uint64 + + // Prevrandao is the value returned for Prevrandao + Prevrandao gethCommon.Hash } // ToBytes encodes the block into bytes @@ -65,6 +68,7 @@ func NewBlock( height uint64, timestamp uint64, totalSupply *big.Int, + prevrandao gethCommon.Hash, ) *Block { return &Block{ ParentBlockHash: parentBlockHash, @@ -73,13 +77,21 @@ func NewBlock( TotalSupply: totalSupply, ReceiptRoot: gethTypes.EmptyReceiptsHash, TransactionHashRoot: gethTypes.EmptyRootHash, + Prevrandao: prevrandao, } } // NewBlockFromBytes constructs a new block from encoded data func NewBlockFromBytes(encoded []byte) (*Block, error) { res := &Block{} - return res, gethRLP.DecodeBytes(encoded, res) + err := gethRLP.DecodeBytes(encoded, res) + if err != nil { + res = decodeBlockBreakingChanges(encoded) + if res == nil { + return nil, err + } + } + return res, nil } // GenesisTimestamp returns the block time stamp for EVM genesis block @@ -104,6 +116,7 @@ func GenesisBlock(chainID flow.ChainID) *Block { ReceiptRoot: gethTypes.EmptyRootHash, TransactionHashRoot: gethTypes.EmptyRootHash, TotalGasUsed: 0, + Prevrandao: gethCommon.Hash{}, } } @@ -182,7 +195,14 @@ func (b *BlockProposal) ToBytes() ([]byte, error) { // NewBlockProposalFromBytes constructs a new block proposal from encoded data func NewBlockProposalFromBytes(encoded []byte) (*BlockProposal, error) { res := &BlockProposal{} - return res, gethRLP.DecodeBytes(encoded, res) + err := gethRLP.DecodeBytes(encoded, res) + if err != nil { + res = decodeBlockProposalBreakingChanges(encoded) + if res == nil { + return nil, err + } + } + return res, nil } func NewBlockProposal( @@ -190,6 +210,7 @@ func NewBlockProposal( height uint64, timestamp uint64, totalSupply *big.Int, + prevrandao gethCommon.Hash, ) *BlockProposal { return &BlockProposal{ Block: Block{ @@ -198,6 +219,7 @@ func NewBlockProposal( Timestamp: timestamp, TotalSupply: totalSupply, ReceiptRoot: gethTypes.EmptyRootHash, + Prevrandao: prevrandao, }, Receipts: make([]LightReceipt, 0), TxHashes: make([]gethCommon.Hash, 0), @@ -217,3 +239,70 @@ func (t TransactionHashes) EncodeIndex(index int, buffer *bytes.Buffer) { func (t TransactionHashes) RootHash() gethCommon.Hash { return gethTypes.DeriveSha(t, gethTrie.NewStackTrie(nil)) } + +// Below block type section, defines earlier block types, +// this is being used to decode blocks that were stored +// before block type changes. It allows us to still decode +// a block that would otherwise be invalid if decoded into +// latest version of the above Block type. + +// before adding Prevrandao to block +type BlockV0 struct { + ParentBlockHash gethCommon.Hash + Height uint64 + Timestamp uint64 + TotalSupply *big.Int + ReceiptRoot gethCommon.Hash + TransactionHashRoot gethCommon.Hash + TotalGasUsed uint64 +} + +type BlockProposalV0 struct { + BlockV0 + Receipts []LightReceipt + TxHashes TransactionHashes +} + +// decodeBlockBreakingChanges will try to decode the bytes into all +// previous versions of block type, if it succeeds it will return the +// migrated block, otherwise it will return nil. +func decodeBlockBreakingChanges(encoded []byte) *Block { + b0 := &BlockV0{} + if err := gethRLP.DecodeBytes(encoded, b0); err == nil { + return &Block{ + ParentBlockHash: b0.ParentBlockHash, + Height: b0.Height, + Timestamp: b0.Timestamp, + TotalSupply: b0.TotalSupply, + ReceiptRoot: b0.ReceiptRoot, + TransactionHashRoot: b0.TransactionHashRoot, + TotalGasUsed: b0.TotalGasUsed, + Prevrandao: gethCommon.Hash{}, + } + } + return nil +} + +// decodeBlockProposalBreakingChanges will try to decode the bytes into all +// previous versions of block proposal type, if it succeeds it will return the +// migrated block, otherwise it will return nil. +func decodeBlockProposalBreakingChanges(encoded []byte) *BlockProposal { + bp0 := &BlockProposalV0{} + if err := gethRLP.DecodeBytes(encoded, bp0); err == nil { + return &BlockProposal{ + Block: Block{ + ParentBlockHash: bp0.ParentBlockHash, + Height: bp0.Height, + Timestamp: bp0.Timestamp, + TotalSupply: bp0.TotalSupply, + ReceiptRoot: bp0.ReceiptRoot, + TransactionHashRoot: bp0.TransactionHashRoot, + TotalGasUsed: bp0.TotalGasUsed, + Prevrandao: gethCommon.Hash{}, + }, + Receipts: bp0.Receipts, + TxHashes: bp0.TxHashes, + } + } + return nil +} diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index aa9ee1d260c..7aac62197a5 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -6,6 +6,7 @@ import ( gethCommon "github.com/onflow/go-ethereum/common" gethTypes "github.com/onflow/go-ethereum/core/types" + gethRLP "github.com/onflow/go-ethereum/rlp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -53,7 +54,7 @@ func Test_BlockHash(t *testing.T) { } func Test_BlockProposal(t *testing.T) { - bp := NewBlockProposal(gethCommon.Hash{1}, 1, 0, nil) + bp := NewBlockProposal(gethCommon.Hash{1}, 1, 0, nil, gethCommon.Hash{1, 2, 3}) bp.AppendTransaction(nil) require.Empty(t, bp.TxHashes) @@ -76,3 +77,52 @@ func Test_BlockProposal(t *testing.T) { bp.PopulateRoots() require.NotEqual(t, gethTypes.EmptyReceiptsHash, bp.ReceiptRoot) } + +func Test_DecodeHistoricBlocks(t *testing.T) { + bv0 := BlockV0{ + ParentBlockHash: GenesisBlockHash(flow.Previewnet.Chain().ChainID()), + Height: 1, + Timestamp: 2, + TotalSupply: big.NewInt(3), + ReceiptRoot: gethCommon.Hash{0x04}, + TransactionHashRoot: gethCommon.Hash{0x05}, + TotalGasUsed: 6, + } + b0, err := gethRLP.EncodeToBytes(bv0) + require.NoError(t, err) + + b := decodeBlockBreakingChanges(b0) + require.Equal(t, b.ParentBlockHash, bv0.ParentBlockHash) + require.Equal(t, b.Height, bv0.Height) + require.Equal(t, b.Timestamp, bv0.Timestamp) + require.Equal(t, b.TotalSupply.Uint64(), bv0.TotalSupply.Uint64()) + require.Equal(t, b.ReceiptRoot, bv0.ReceiptRoot) + require.Equal(t, b.TransactionHashRoot, bv0.TransactionHashRoot) + require.Equal(t, b.TotalGasUsed, bv0.TotalGasUsed) + require.Empty(t, b.Prevrandao) + + bpv0 := BlockProposalV0{ + BlockV0: bv0, + Receipts: []LightReceipt{ + {CumulativeGasUsed: 10}, + {CumulativeGasUsed: 2}, + }, + TxHashes: []gethCommon.Hash{{1, 2}, {3, 4}, {5, 6}}, + } + + bp0, err := gethRLP.EncodeToBytes(bpv0) + require.NoError(t, err) + + bp := decodeBlockProposalBreakingChanges(bp0) + require.Equal(t, bp.ParentBlockHash, bpv0.ParentBlockHash) + require.Equal(t, bp.Height, bpv0.Height) + require.Equal(t, bp.Timestamp, bpv0.Timestamp) + require.Equal(t, bp.TotalSupply.Uint64(), bpv0.TotalSupply.Uint64()) + require.Equal(t, bp.ReceiptRoot, bpv0.ReceiptRoot) + require.Equal(t, bp.TransactionHashRoot, bpv0.TransactionHashRoot) + require.Equal(t, bp.TotalGasUsed, bpv0.TotalGasUsed) + require.Empty(t, bp.Prevrandao) + require.Len(t, bp.Receipts, 2) + require.Len(t, bp.TxHashes, 3) + +} From 769f171854a05aca5ceacb5eeef9a23aba45f5b8 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 09:39:17 -0700 Subject: [PATCH 06/21] move random to block level --- fvm/evm/events/events.go | 7 +++---- fvm/evm/events/events_test.go | 8 +++----- fvm/evm/stdlib/contract.cdc | 4 ++-- fvm/evm/types/block_test.go | 8 ++++---- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 8fce3d9fdb3..a18f40461d3 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -42,11 +42,11 @@ type transactionEvent struct { // - result: the result of the transaction execution // - payload: the RLP-encoded payload of the transaction // - blockHeight: the height of the block where the transaction is included +// - coinbase: address of the coinbase, where the fees are collected func NewTransactionEvent( result *types.Result, payload []byte, blockHeight uint64, - random gethCommon.Hash, coinbase types.Address, ) *Event { var cb *types.Address @@ -59,7 +59,6 @@ func NewTransactionEvent( BlockHeight: blockHeight, Payload: payload, Result: result, - Random: random, Coinbase: cb, }, } @@ -110,7 +109,6 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), - hashToCadenceArrayValue(p.Random), coinbaseAddress, }).WithType(eventType), nil } @@ -144,6 +142,7 @@ func (p *blockEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error) { hashToCadenceArrayValue(p.ParentBlockHash), hashToCadenceArrayValue(p.ReceiptRoot), hashToCadenceArrayValue(p.TransactionHashRoot), + hashToCadenceArrayValue(p.Prevrandao), }).WithType(eventType), nil } @@ -156,6 +155,7 @@ type BlockEventPayload struct { ParentBlockHash gethCommon.Hash `cadence:"parentHash"` ReceiptRoot gethCommon.Hash `cadence:"receiptRoot"` TransactionHashRoot gethCommon.Hash `cadence:"transactionHashRoot"` + Prevrandao gethCommon.Hash `cadence:"prevrandao"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. @@ -178,7 +178,6 @@ type TransactionEventPayload struct { ErrorMessage string `cadence:"errorMessage"` ReturnedData []byte `cadence:"returnedData"` PrecompiledCalls []byte `cadence:"precompiledCalls"` - Random gethCommon.Hash `cadence:"random"` Coinbase string `cadence:"coinbase"` } diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 4883f02c109..ad49e240010 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -34,6 +34,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { ReceiptRoot: gethCommon.Hash{}, TotalGasUsed: 15, TransactionHashRoot: gethCommon.HexToHash("0x70b67ce6710355acf8d69b2ea013d34e212bc4824926c5d26f189c1ca9667246"), + Prevrandao: testutils.RandomCommonHash(t), } event := events.NewBlockEvent(block) @@ -55,6 +56,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, bep.ParentBlockHash, block.ParentBlockHash) assert.Equal(t, bep.ReceiptRoot, block.ReceiptRoot) assert.Equal(t, bep.TransactionHashRoot, block.TransactionHashRoot) + assert.Equal(t, bep.Prevrandao, block.Prevrandao) v, err := ccf.Encode(ev) require.NoError(t, err) @@ -83,7 +85,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) - random := testutils.RandomCommonHash(t) coinbase := testutils.RandomAddress(t) data := "000000000000000000000000000000000000000000000000000000000000002a" dataBytes, err := hex.DecodeString(data) @@ -114,7 +115,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { - event := events.NewTransactionEvent(txResult, txBytes, blockHeight, random, coinbase) + event := events.NewTransactionEvent(txResult, txBytes, blockHeight, coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -122,7 +123,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Random, random) assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) @@ -161,7 +161,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { event := events.NewTransactionEvent(txResult, txBytes, blockHeight, - random, coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -170,7 +169,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Random, random) assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 3a989f68bcd..2f0824aa43f 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -34,6 +34,8 @@ contract EVM { receiptRoot: [UInt8; 32], // root hash of all the transaction hashes transactionHashRoot: [UInt8; 32], + /// value returned for PREVRANDAO opcode + prevrandao: [UInt8; 32], ) /// Transaction executed event is emitted every time a transaction @@ -72,8 +74,6 @@ contract EVM { /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. precompiledCalls: [UInt8], - /// random value used for PREVRANDO opcode - random: [UInt8; 32], /// coinbase value used for COINBASE opcode (if empty no coinbase is set) coinbase: String ) diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 7aac62197a5..592e144adac 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -80,13 +80,13 @@ func Test_BlockProposal(t *testing.T) { func Test_DecodeHistoricBlocks(t *testing.T) { bv0 := BlockV0{ - ParentBlockHash: GenesisBlockHash(flow.Previewnet.Chain().ChainID()), + ParentBlockHash: gethCommon.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), Height: 1, Timestamp: 2, TotalSupply: big.NewInt(3), ReceiptRoot: gethCommon.Hash{0x04}, TransactionHashRoot: gethCommon.Hash{0x05}, - TotalGasUsed: 6, + TotalGasUsed: 0, } b0, err := gethRLP.EncodeToBytes(bv0) require.NoError(t, err) @@ -113,7 +113,8 @@ func Test_DecodeHistoricBlocks(t *testing.T) { bp0, err := gethRLP.EncodeToBytes(bpv0) require.NoError(t, err) - bp := decodeBlockProposalBreakingChanges(bp0) + bp, err := NewBlockProposalFromBytes(bp0) + require.NoError(t, err) require.Equal(t, bp.ParentBlockHash, bpv0.ParentBlockHash) require.Equal(t, bp.Height, bpv0.Height) require.Equal(t, bp.Timestamp, bpv0.Timestamp) @@ -124,5 +125,4 @@ func Test_DecodeHistoricBlocks(t *testing.T) { require.Empty(t, bp.Prevrandao) require.Len(t, bp.Receipts, 2) require.Len(t, bp.TxHashes, 3) - } From 9aeb6f927f61ef031eb0dfa39707cc642d7265cc Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 09:42:51 -0700 Subject: [PATCH 07/21] remove random changes --- fvm/evm/events/events.go | 5 ----- fvm/evm/events/events_test.go | 6 +----- fvm/evm/handler/handler.go | 3 --- fvm/evm/stdlib/contract.cdc | 2 -- 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 8fce3d9fdb3..efe23fe18ef 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -34,7 +34,6 @@ type transactionEvent struct { Payload []byte // transaction RLP-encoded payload Result *types.Result // transaction execution result BlockHeight uint64 - Random gethCommon.Hash Coinbase *types.Address } @@ -46,7 +45,6 @@ func NewTransactionEvent( result *types.Result, payload []byte, blockHeight uint64, - random gethCommon.Hash, coinbase types.Address, ) *Event { var cb *types.Address @@ -59,7 +57,6 @@ func NewTransactionEvent( BlockHeight: blockHeight, Payload: payload, Result: result, - Random: random, Coinbase: cb, }, } @@ -110,7 +107,6 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), - hashToCadenceArrayValue(p.Random), coinbaseAddress, }).WithType(eventType), nil } @@ -178,7 +174,6 @@ type TransactionEventPayload struct { ErrorMessage string `cadence:"errorMessage"` ReturnedData []byte `cadence:"returnedData"` PrecompiledCalls []byte `cadence:"precompiledCalls"` - Random gethCommon.Hash `cadence:"random"` Coinbase string `cadence:"coinbase"` } diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 4883f02c109..18af126150b 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -83,7 +83,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) - random := testutils.RandomCommonHash(t) coinbase := testutils.RandomAddress(t) data := "000000000000000000000000000000000000000000000000000000000000002a" dataBytes, err := hex.DecodeString(data) @@ -114,7 +113,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { - event := events.NewTransactionEvent(txResult, txBytes, blockHeight, random, coinbase) + event := events.NewTransactionEvent(txResult, txBytes, blockHeight, coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -122,7 +121,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Random, random) assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) @@ -161,7 +159,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { event := events.NewTransactionEvent(txResult, txBytes, blockHeight, - random, coinbase) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -170,7 +167,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Random, random) assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index b9b152fb9c0..91f51a34986 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -274,7 +274,6 @@ func (h *ContractHandler) batchRun( r, rlpEncodedTxs[i], bp.Height, - ctx.Random, ctx.GasFeeCollector, )) if err != nil { @@ -402,7 +401,6 @@ func (h *ContractHandler) run( res, rlpEncodedTx, bp.Height, - ctx.Random, ctx.GasFeeCollector, )) @@ -621,7 +619,6 @@ func (h *ContractHandler) executeAndHandleCall( res, encoded, bp.Height, - ctx.Random, ctx.GasFeeCollector, ), ) diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 3a989f68bcd..dcdf3fef39f 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -72,8 +72,6 @@ contract EVM { /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. precompiledCalls: [UInt8], - /// random value used for PREVRANDO opcode - random: [UInt8; 32], /// coinbase value used for COINBASE opcode (if empty no coinbase is set) coinbase: String ) From 67e5fc7e4ee4fc8359a6ad4f4aa838eb1f71f36e Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 09:47:06 -0700 Subject: [PATCH 08/21] state commitment update due to evm contract change --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index f2ec6d76b67..b3dde6d51f1 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("1383e01cdba9bb1df08d413f89c3a252f1081f2ebb0c58850676194a01cfc4c4") + expectedStateCommitmentBytes, _ := hex.DecodeString("523c035e307c09118e7597a5822cc21d637e0c8ad53b960b52e3d42f45db2e49") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index ea7278b1e6e..266ad1b9ad9 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "f0eeeadd231fcc1668d4bcc8df8488a35e126600e3c4b88b4120b35e4ea9ee8c" +const GenesisStateCommitmentHex = "5fff8035b9b8627a38ac70e1de4bc8e66a0c92efbfd928694d632e98f60023fd" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "b41ab049fc2f2f5f419357f2e4dc9d2181a18f419e3b2e96d7f908511d5e0aa1" + return "458a02e4cc371e31f81c13e41c335902b8597728a9af147cc16329efd66922e6" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "922fca1ecf717e724328d24a9e3cb147426973405fe0d7ba87f67e0f5347887b" + return "d230aab0eb12865331d069dc836845b1994fa813a0a627d53d577f4f5bf2898b" } From 646e6ec29593224b77aba28ce55c631745f833fb Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 09:56:04 -0700 Subject: [PATCH 09/21] state commit update due to evm contract changes --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index b3dde6d51f1..5a9c1e2a598 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("523c035e307c09118e7597a5822cc21d637e0c8ad53b960b52e3d42f45db2e49") + expectedStateCommitmentBytes, _ := hex.DecodeString("e2bcb22077471e73b5079b254a026f6ae77761c51113657cae17009eb62094c3") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index 266ad1b9ad9..ec947e62ebe 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "5fff8035b9b8627a38ac70e1de4bc8e66a0c92efbfd928694d632e98f60023fd" +const GenesisStateCommitmentHex = "a0525520a0684f6686e3a4f3284974d856f1feba785f0aca4c70a8c22061a034" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "458a02e4cc371e31f81c13e41c335902b8597728a9af147cc16329efd66922e6" + return "559d1bca7c92f64c0cb31b0fd4d98c76b56ac6ac5624ce0a23cd5df5ac8fbb46" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "d230aab0eb12865331d069dc836845b1994fa813a0a627d53d577f4f5bf2898b" + return "96d0f183fd93175bc945f17dd92ed79201a5f11e67bf341d66d819fc93757064" } From 811a03575eebaa13c6825eab59dcb8514cfcf358 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 10:33:52 -0700 Subject: [PATCH 10/21] use flow block hash as prevrandao value --- fvm/evm/handler/blockstore.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index 9c6fc128d99..d670bfb935f 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -17,9 +17,10 @@ const ( ) type BlockStore struct { - chainID flow.ChainID - backend types.Backend - rootAddress flow.Address + chainID flow.ChainID + backend types.Backend + rootAddress flow.Address + cachedBlockProposal *types.BlockProposal } var _ types.BlockStore = &BlockStore{} @@ -76,19 +77,15 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { // expect timestamps in unix seconds so we convert here timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second)) - // read a random value for block proposal - rand := gethCommon.Hash{} - err = bs.backend.ReadRandom(rand[:]) - if err != nil { - return nil, err - } + // Use flow block hash as input for prevrandao + prevrandao := gethCommon.BytesToHash(cadenceBlock.Hash[:]) blockProposal := types.NewBlockProposal( parentHash, lastExecutedBlock.Height+1, timestamp, lastExecutedBlock.TotalSupply, - rand, + prevrandao, ) return blockProposal, nil } From fad0375b1224c2229aa1ae3dd45661b64043e100 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 10:53:56 -0700 Subject: [PATCH 11/21] revert changes --- fvm/evm/handler/blockstore.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index d670bfb935f..ee35e877dfb 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -17,10 +17,9 @@ const ( ) type BlockStore struct { - chainID flow.ChainID - backend types.Backend - rootAddress flow.Address - cachedBlockProposal *types.BlockProposal + chainID flow.ChainID + backend types.Backend + rootAddress flow.Address } var _ types.BlockStore = &BlockStore{} @@ -77,8 +76,12 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { // expect timestamps in unix seconds so we convert here timestamp := uint64(cadenceBlock.Timestamp / int64(time.Second)) - // Use flow block hash as input for prevrandao - prevrandao := gethCommon.BytesToHash(cadenceBlock.Hash[:]) + // read a random value for block proposal + prevrandao := gethCommon.Hash{} + err = bs.backend.ReadRandom(prevrandao[:]) + if err != nil { + return nil, err + } blockProposal := types.NewBlockProposal( parentHash, @@ -87,6 +90,13 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { lastExecutedBlock.TotalSupply, prevrandao, ) + + // store it + err = bs.UpdateBlockProposal(blockProposal) + if err != nil { + return nil, err + } + return blockProposal, nil } From 9c4f38b33f1fad1eab92aee5c502766b5d3bf7da Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Tue, 20 Aug 2024 11:29:59 -0700 Subject: [PATCH 12/21] Add reporting --- cmd/util/ledger/migrations/cadence.go | 3 +- .../cadence_values_migration_test.go | 28 +++++++++ .../migrations/type_requirements_extractor.go | 60 +++++++++++++++---- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/cmd/util/ledger/migrations/cadence.go b/cmd/util/ledger/migrations/cadence.go index a46e993f659..d78538b8d9a 100644 --- a/cmd/util/ledger/migrations/cadence.go +++ b/cmd/util/ledger/migrations/cadence.go @@ -593,8 +593,7 @@ func NewCadence1ContractsMigrations( Burner: opts.BurnerContractChange, } - var systemContractsMigration *StagedContractsMigration - systemContractsMigration = NewSystemContractsMigration( + systemContractsMigration := NewSystemContractsMigration( log, rwf, importantLocations, diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 0b1c3b73d47..324c19809b4 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2875,6 +2875,34 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { ) } + // Check reporters + + reporter := rwf.reportWriters[typeRequirementExtractingReporterName] + require.NotNil(t, reporter) + require.Len(t, reporter.entries, 3) + + assert.Equal( + t, + []any{ + typeRequirementRemovalEntry{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "ContentLocation", + }, + typeRequirementRemovalEntry{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Producer", + }, + typeRequirementRemovalEntry{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Minter", + }, + }, + reporter.entries, + ) + // Check account _, err = runScript( diff --git a/cmd/util/ledger/migrations/type_requirements_extractor.go b/cmd/util/ledger/migrations/type_requirements_extractor.go index 2caeed2d6b1..f81a063dd15 100644 --- a/cmd/util/ledger/migrations/type_requirements_extractor.go +++ b/cmd/util/ledger/migrations/type_requirements_extractor.go @@ -2,11 +2,14 @@ package migrations import ( "bytes" + "encoding/json" "fmt" + "sort" + + "github.com/rs/zerolog" + "github.com/onflow/cadence/runtime/common" "github.com/onflow/cadence/runtime/old_parser" - "github.com/rs/zerolog" - "sort" "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" @@ -146,13 +149,50 @@ func extractTypeRequirements( } for _, composites := range contractInterface.DeclarationMembers().Composites() { - legacyTypeRequirements.typeRequirements = append( - legacyTypeRequirements.typeRequirements, - TypeRequirement{ - Address: contract.location.Address, - ContractName: contractInterface.Identifier.Identifier, - TypeName: composites.Identifier.Identifier, - }, - ) + typeRequirement := TypeRequirement{ + Address: contract.location.Address, + ContractName: contractInterface.Identifier.Identifier, + TypeName: composites.Identifier.Identifier, + } + + legacyTypeRequirements.typeRequirements = append(legacyTypeRequirements.typeRequirements, typeRequirement) + + reporter.Write(typeRequirementRemovalEntry{ + Address: typeRequirement.Address, + ContractName: typeRequirement.ContractName, + TypeName: typeRequirement.TypeName, + }) } + + log.Info().Msgf("Collected %d type-requirements", len(legacyTypeRequirements.typeRequirements)) +} + +// cadenceValueMigrationFailureEntry + +type typeRequirementRemovalEntry struct { + Address common.Address + ContractName string + TypeName string +} + +var _ valueMigrationReportEntry = typeRequirementRemovalEntry{} + +func (e typeRequirementRemovalEntry) accountAddress() common.Address { + return e.Address +} + +var _ json.Marshaler = typeRequirementRemovalEntry{} + +func (e typeRequirementRemovalEntry) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Kind string `json:"kind"` + AccountAddress string `json:"account_address"` + ContractName string `json:"contract_name"` + TypeName string `json:"type_name"` + }{ + Kind: "cadence-type-requirement-remove", + AccountAddress: e.Address.HexWithPrefix(), + ContractName: e.ContractName, + TypeName: e.TypeName, + }) } From 12680aa23e0122b256b8b21c1980309b01a37d10 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 15:00:32 -0700 Subject: [PATCH 13/21] construct block proposal and persist it after last block commit --- fvm/evm/handler/blockstore.go | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index ee35e877dfb..a081ea61e3f 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -47,7 +47,10 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { if len(data) != 0 { return types.NewBlockProposalFromBytes(data) } + return bs.constructBlockProposal() +} +func (bs *BlockStore) constructBlockProposal() (*types.BlockProposal, error) { // if available construct a new one cadenceHeight, err := bs.backend.GetCurrentBlockHeight() if err != nil { @@ -91,12 +94,6 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { prevrandao, ) - // store it - err = bs.UpdateBlockProposal(blockProposal) - if err != nil { - return nil, err - } - return blockProposal, nil } @@ -114,14 +111,6 @@ func (bs *BlockStore) UpdateBlockProposal(bp *types.BlockProposal) error { ) } -func (bs *BlockStore) ResetBlockProposal() error { - return bs.backend.SetValue( - bs.rootAddress[:], - []byte(BlockStoreLatestBlockProposalKey), - nil, - ) -} - // CommitBlockProposal commits the block proposal to the chain func (bs *BlockStore) CommitBlockProposal(bp *types.BlockProposal) error { bp.PopulateRoots() @@ -150,7 +139,12 @@ func (bs *BlockStore) CommitBlockProposal(bp *types.BlockProposal) error { return err } - err = bs.ResetBlockProposal() + // construct a new block proposal and store + newBP, err := bs.constructBlockProposal() + if err != nil { + return err + } + err = bs.UpdateBlockProposal(newBP) if err != nil { return err } From 3fadde16bfbbaababae103080e77d9afaf3fc9f9 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 15:10:19 -0700 Subject: [PATCH 14/21] store block proposal after creation --- fvm/evm/handler/blockstore.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fvm/evm/handler/blockstore.go b/fvm/evm/handler/blockstore.go index a081ea61e3f..a8c5975f193 100644 --- a/fvm/evm/handler/blockstore.go +++ b/fvm/evm/handler/blockstore.go @@ -47,7 +47,16 @@ func (bs *BlockStore) BlockProposal() (*types.BlockProposal, error) { if len(data) != 0 { return types.NewBlockProposalFromBytes(data) } - return bs.constructBlockProposal() + bp, err := bs.constructBlockProposal() + if err != nil { + return nil, err + } + // store block proposal + err = bs.UpdateBlockProposal(bp) + if err != nil { + return nil, err + } + return bp, nil } func (bs *BlockStore) constructBlockProposal() (*types.BlockProposal, error) { From 48983fca79b85b4f1ba144b9e969b244295f2bbb Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 19:58:53 -0700 Subject: [PATCH 15/21] rename to match ETH name --- fvm/evm/events/events.go | 2 +- fvm/evm/events/events_test.go | 4 ++-- fvm/evm/handler/handler.go | 2 +- fvm/evm/types/block.go | 18 +++++++++--------- fvm/evm/types/block_test.go | 4 ++-- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 258d315d2b8..6197ad62434 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -141,7 +141,7 @@ func (p *blockEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error) { hashToCadenceArrayValue(p.ParentBlockHash), hashToCadenceArrayValue(p.ReceiptRoot), hashToCadenceArrayValue(p.TransactionHashRoot), - hashToCadenceArrayValue(p.Prevrandao), + hashToCadenceArrayValue(p.PrevRandao), }).WithType(eventType), nil } diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index ad49e240010..2de1ee1e8a2 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -34,7 +34,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { ReceiptRoot: gethCommon.Hash{}, TotalGasUsed: 15, TransactionHashRoot: gethCommon.HexToHash("0x70b67ce6710355acf8d69b2ea013d34e212bc4824926c5d26f189c1ca9667246"), - Prevrandao: testutils.RandomCommonHash(t), + PrevRandao: testutils.RandomCommonHash(t), } event := events.NewBlockEvent(block) @@ -56,7 +56,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, bep.ParentBlockHash, block.ParentBlockHash) assert.Equal(t, bep.ReceiptRoot, block.ReceiptRoot) assert.Equal(t, bep.TransactionHashRoot, block.TransactionHashRoot) - assert.Equal(t, bep.Prevrandao, block.Prevrandao) + assert.Equal(t, bep.Prevrandao, block.PrevRandao) v, err := ccf.Encode(ev) require.NoError(t, err) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 4c62d514719..8460beffc1d 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -527,7 +527,7 @@ func (h *ContractHandler) getBlockContext() (types.BlockContext, error) { return hash }, ExtraPrecompiledContracts: h.precompiledContracts, - Random: bp.Prevrandao, + Random: bp.PrevRandao, Tracer: h.tracer.TxTracer(), TxCountSoFar: uint(len(bp.TxHashes)), TotalGasUsedSoFar: bp.TotalGasUsed, diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index dbbf3f71bdc..14ae3b6a389 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -47,8 +47,8 @@ type Block struct { // TotalGasUsed stores gas used by all transactions included in the block. TotalGasUsed uint64 - // Prevrandao is the value returned for Prevrandao - Prevrandao gethCommon.Hash + // PrevRandao is the value returned for Prevrandao opcode + PrevRandao gethCommon.Hash } // ToBytes encodes the block into bytes @@ -68,7 +68,7 @@ func NewBlock( height uint64, timestamp uint64, totalSupply *big.Int, - prevrandao gethCommon.Hash, + prevRandao gethCommon.Hash, ) *Block { return &Block{ ParentBlockHash: parentBlockHash, @@ -77,7 +77,7 @@ func NewBlock( TotalSupply: totalSupply, ReceiptRoot: gethTypes.EmptyReceiptsHash, TransactionHashRoot: gethTypes.EmptyRootHash, - Prevrandao: prevrandao, + PrevRandao: prevRandao, } } @@ -116,7 +116,7 @@ func GenesisBlock(chainID flow.ChainID) *Block { ReceiptRoot: gethTypes.EmptyRootHash, TransactionHashRoot: gethTypes.EmptyRootHash, TotalGasUsed: 0, - Prevrandao: gethCommon.Hash{}, + PrevRandao: gethCommon.Hash{}, } } @@ -210,7 +210,7 @@ func NewBlockProposal( height uint64, timestamp uint64, totalSupply *big.Int, - prevrandao gethCommon.Hash, + prevRandao gethCommon.Hash, ) *BlockProposal { return &BlockProposal{ Block: Block{ @@ -219,7 +219,7 @@ func NewBlockProposal( Timestamp: timestamp, TotalSupply: totalSupply, ReceiptRoot: gethTypes.EmptyRootHash, - Prevrandao: prevrandao, + PrevRandao: prevRandao, }, Receipts: make([]LightReceipt, 0), TxHashes: make([]gethCommon.Hash, 0), @@ -277,7 +277,7 @@ func decodeBlockBreakingChanges(encoded []byte) *Block { ReceiptRoot: b0.ReceiptRoot, TransactionHashRoot: b0.TransactionHashRoot, TotalGasUsed: b0.TotalGasUsed, - Prevrandao: gethCommon.Hash{}, + PrevRandao: gethCommon.Hash{}, } } return nil @@ -298,7 +298,7 @@ func decodeBlockProposalBreakingChanges(encoded []byte) *BlockProposal { ReceiptRoot: bp0.ReceiptRoot, TransactionHashRoot: bp0.TransactionHashRoot, TotalGasUsed: bp0.TotalGasUsed, - Prevrandao: gethCommon.Hash{}, + PrevRandao: gethCommon.Hash{}, }, Receipts: bp0.Receipts, TxHashes: bp0.TxHashes, diff --git a/fvm/evm/types/block_test.go b/fvm/evm/types/block_test.go index 592e144adac..2ccb0ddcd65 100644 --- a/fvm/evm/types/block_test.go +++ b/fvm/evm/types/block_test.go @@ -99,7 +99,7 @@ func Test_DecodeHistoricBlocks(t *testing.T) { require.Equal(t, b.ReceiptRoot, bv0.ReceiptRoot) require.Equal(t, b.TransactionHashRoot, bv0.TransactionHashRoot) require.Equal(t, b.TotalGasUsed, bv0.TotalGasUsed) - require.Empty(t, b.Prevrandao) + require.Empty(t, b.PrevRandao) bpv0 := BlockProposalV0{ BlockV0: bv0, @@ -122,7 +122,7 @@ func Test_DecodeHistoricBlocks(t *testing.T) { require.Equal(t, bp.ReceiptRoot, bpv0.ReceiptRoot) require.Equal(t, bp.TransactionHashRoot, bpv0.TransactionHashRoot) require.Equal(t, bp.TotalGasUsed, bpv0.TotalGasUsed) - require.Empty(t, bp.Prevrandao) + require.Empty(t, bp.PrevRandao) require.Len(t, bp.Receipts, 2) require.Len(t, bp.TxHashes, 3) } From cfa0f13b38158a7c4abf6c652bbc6f26ba5db58c Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 20:02:25 -0700 Subject: [PATCH 16/21] typo --- fvm/evm/events/events.go | 2 +- fvm/evm/events/events_test.go | 2 +- fvm/evm/types/block.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index 6197ad62434..e1aafaa890f 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -154,7 +154,7 @@ type BlockEventPayload struct { ParentBlockHash gethCommon.Hash `cadence:"parentHash"` ReceiptRoot gethCommon.Hash `cadence:"receiptRoot"` TransactionHashRoot gethCommon.Hash `cadence:"transactionHashRoot"` - Prevrandao gethCommon.Hash `cadence:"prevrandao"` + PrevRandao gethCommon.Hash `cadence:"prevRandao"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 2de1ee1e8a2..49e2bf90a20 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -56,7 +56,7 @@ func TestEVMBlockExecutedEventCCFEncodingDecoding(t *testing.T) { assert.Equal(t, bep.ParentBlockHash, block.ParentBlockHash) assert.Equal(t, bep.ReceiptRoot, block.ReceiptRoot) assert.Equal(t, bep.TransactionHashRoot, block.TransactionHashRoot) - assert.Equal(t, bep.Prevrandao, block.PrevRandao) + assert.Equal(t, bep.PrevRandao, block.PrevRandao) v, err := ccf.Encode(ev) require.NoError(t, err) diff --git a/fvm/evm/types/block.go b/fvm/evm/types/block.go index 14ae3b6a389..458e2961bc3 100644 --- a/fvm/evm/types/block.go +++ b/fvm/evm/types/block.go @@ -47,7 +47,7 @@ type Block struct { // TotalGasUsed stores gas used by all transactions included in the block. TotalGasUsed uint64 - // PrevRandao is the value returned for Prevrandao opcode + // PrevRandao is the value returned for block.prevrandao opcode PrevRandao gethCommon.Hash } @@ -246,7 +246,7 @@ func (t TransactionHashes) RootHash() gethCommon.Hash { // a block that would otherwise be invalid if decoded into // latest version of the above Block type. -// before adding Prevrandao to block +// before adding PrevRandao to the block type BlockV0 struct { ParentBlockHash gethCommon.Hash Height uint64 From ee0dd1d9a93efb08ae1fda8b54e6d1414b8e73f9 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 22:12:22 -0700 Subject: [PATCH 17/21] remove unwanted changes --- fvm/evm/events/events.go | 17 +---------------- fvm/evm/events/events_test.go | 10 ++-------- fvm/evm/handler/handler.go | 3 --- fvm/evm/stdlib/contract.cdc | 4 +--- 4 files changed, 4 insertions(+), 30 deletions(-) diff --git a/fvm/evm/events/events.go b/fvm/evm/events/events.go index e1aafaa890f..edda7c78253 100644 --- a/fvm/evm/events/events.go +++ b/fvm/evm/events/events.go @@ -34,31 +34,23 @@ type transactionEvent struct { Payload []byte // transaction RLP-encoded payload Result *types.Result // transaction execution result BlockHeight uint64 - Coinbase *types.Address } // NewTransactionEvent creates a new transaction event with the given parameters // - result: the result of the transaction execution // - payload: the RLP-encoded payload of the transaction // - blockHeight: the height of the block where the transaction is included -// - coinbase: address of the coinbase, where the fees are collected func NewTransactionEvent( result *types.Result, payload []byte, blockHeight uint64, - coinbase types.Address, ) *Event { - var cb *types.Address - if coinbase != types.EmptyAddress { - cb = &coinbase - } return &Event{ Etype: EventTypeTransactionExecuted, Payload: &transactionEvent{ BlockHeight: blockHeight, Payload: payload, Result: result, - Coinbase: cb, }, } } @@ -78,11 +70,6 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error deployedAddress = cadence.String(p.Result.DeployedContractAddress.String()) } - coinbaseAddress := cadence.String("") - if p.Coinbase != nil { - coinbaseAddress = cadence.String(p.Coinbase.String()) - } - errorMsg := "" if p.Result.VMError != nil { errorMsg = p.Result.VMError.Error() @@ -108,7 +95,6 @@ func (p *transactionEvent) ToCadence(chainID flow.ChainID) (cadence.Event, error cadence.NewUInt64(p.BlockHeight), bytesToCadenceUInt8ArrayValue(p.Result.ReturnedData), bytesToCadenceUInt8ArrayValue(p.Result.PrecompiledCalls), - coinbaseAddress, }).WithType(eventType), nil } @@ -154,7 +140,7 @@ type BlockEventPayload struct { ParentBlockHash gethCommon.Hash `cadence:"parentHash"` ReceiptRoot gethCommon.Hash `cadence:"receiptRoot"` TransactionHashRoot gethCommon.Hash `cadence:"transactionHashRoot"` - PrevRandao gethCommon.Hash `cadence:"prevRandao"` + PrevRandao gethCommon.Hash `cadence:"prevrandao"` } // DecodeBlockEventPayload decodes Cadence event into block event payload. @@ -177,7 +163,6 @@ type TransactionEventPayload struct { ErrorMessage string `cadence:"errorMessage"` ReturnedData []byte `cadence:"returnedData"` PrecompiledCalls []byte `cadence:"precompiledCalls"` - Coinbase string `cadence:"coinbase"` } // DecodeTransactionEventPayload decodes Cadence event into transaction event payload. diff --git a/fvm/evm/events/events_test.go b/fvm/evm/events/events_test.go index 49e2bf90a20..4b5d3420f6b 100644 --- a/fvm/evm/events/events_test.go +++ b/fvm/evm/events/events_test.go @@ -85,7 +85,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) txHash := testutils.RandomCommonHash(t) blockHash := testutils.RandomCommonHash(t) - coinbase := testutils.RandomAddress(t) data := "000000000000000000000000000000000000000000000000000000000000002a" dataBytes, err := hex.DecodeString(data) require.NoError(t, err) @@ -115,7 +114,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { } t.Run("evm.TransactionExecuted with failed status", func(t *testing.T) { - event := events.NewTransactionEvent(txResult, txBytes, blockHeight, coinbase) + event := events.NewTransactionEvent(txResult, txBytes, blockHeight) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -123,7 +122,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) assert.Equal(t, types.ErrorCode(tep.ErrorCode), types.ExecutionErrCodeOutOfGas) @@ -158,10 +156,7 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { t.Run("evm.TransactionExecuted with non-failed status", func(t *testing.T) { txResult.VMError = nil - event := events.NewTransactionEvent(txResult, - txBytes, - blockHeight, - coinbase) + event := events.NewTransactionEvent(txResult, txBytes, blockHeight) ev, err := event.Payload.ToCadence(flow.Emulator) require.NoError(t, err) @@ -169,7 +164,6 @@ func TestEVMTransactionExecutedEventCCFEncodingDecoding(t *testing.T) { require.NoError(t, err) assert.Equal(t, tep.BlockHeight, blockHeight) - assert.Equal(t, tep.Coinbase, coinbase.String()) assert.Equal(t, tep.Hash, txHash) assert.Equal(t, tep.Payload, txBytes) assert.Equal(t, types.ErrCodeNoError, types.ErrorCode(tep.ErrorCode)) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index 8460beffc1d..a9c8b53bcc6 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -274,7 +274,6 @@ func (h *ContractHandler) batchRun( r, rlpEncodedTxs[i], bp.Height, - ctx.GasFeeCollector, )) if err != nil { return nil, err @@ -401,7 +400,6 @@ func (h *ContractHandler) run( res, rlpEncodedTx, bp.Height, - ctx.GasFeeCollector, )) if err != nil { @@ -614,7 +612,6 @@ func (h *ContractHandler) executeAndHandleCall( res, encoded, bp.Height, - ctx.GasFeeCollector, ), ) if err != nil { diff --git a/fvm/evm/stdlib/contract.cdc b/fvm/evm/stdlib/contract.cdc index 2f0824aa43f..21ed5a0af78 100644 --- a/fvm/evm/stdlib/contract.cdc +++ b/fvm/evm/stdlib/contract.cdc @@ -73,9 +73,7 @@ contract EVM { /// precompiled contracts (e.g. Cadence Arch) during the transaction execution. /// This data helps to replay the transactions without the need to /// have access to the full cadence state data. - precompiledCalls: [UInt8], - /// coinbase value used for COINBASE opcode (if empty no coinbase is set) - coinbase: String + precompiledCalls: [UInt8] ) access(all) From ef61bc14a139163d8e1a60cb5120fd1da554ad37 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 22:14:19 -0700 Subject: [PATCH 18/21] state commit update due to evm contract change --- engine/execution/state/bootstrap/bootstrap_test.go | 2 +- utils/unittest/execution_state.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/execution/state/bootstrap/bootstrap_test.go b/engine/execution/state/bootstrap/bootstrap_test.go index 5a9c1e2a598..6fa5ef66470 100644 --- a/engine/execution/state/bootstrap/bootstrap_test.go +++ b/engine/execution/state/bootstrap/bootstrap_test.go @@ -53,7 +53,7 @@ func TestBootstrapLedger(t *testing.T) { } func TestBootstrapLedger_ZeroTokenSupply(t *testing.T) { - expectedStateCommitmentBytes, _ := hex.DecodeString("e2bcb22077471e73b5079b254a026f6ae77761c51113657cae17009eb62094c3") + expectedStateCommitmentBytes, _ := hex.DecodeString("e8b4b48a5c4eb510e28ecc9623271d53ef9915c98d333939b448516fa25e5a8f") expectedStateCommitment, err := flow.ToStateCommitment(expectedStateCommitmentBytes) require.NoError(t, err) diff --git a/utils/unittest/execution_state.go b/utils/unittest/execution_state.go index ec947e62ebe..ee6da7b082d 100644 --- a/utils/unittest/execution_state.go +++ b/utils/unittest/execution_state.go @@ -23,7 +23,7 @@ const ServiceAccountPrivateKeySignAlgo = crypto.ECDSAP256 const ServiceAccountPrivateKeyHashAlgo = hash.SHA2_256 // Pre-calculated state commitment with root account with the above private key -const GenesisStateCommitmentHex = "a0525520a0684f6686e3a4f3284974d856f1feba785f0aca4c70a8c22061a034" +const GenesisStateCommitmentHex = "ba479ddabd34159a9d6326ea78c659aa6dd71db63791714bdccdbea859ba1b8e" var GenesisStateCommitment flow.StateCommitment @@ -87,10 +87,10 @@ func genesisCommitHexByChainID(chainID flow.ChainID) string { return GenesisStateCommitmentHex } if chainID == flow.Testnet { - return "559d1bca7c92f64c0cb31b0fd4d98c76b56ac6ac5624ce0a23cd5df5ac8fbb46" + return "9485c620254319da8ea93978909d7ed8327e9dd1f4cb9ec74c816919166b5a2c" } if chainID == flow.Sandboxnet { return "e1c08b17f9e5896f03fe28dd37ca396c19b26628161506924fbf785834646ea1" } - return "96d0f183fd93175bc945f17dd92ed79201a5f11e67bf341d66d819fc93757064" + return "66df5ceb8ca13532a5004bb6014677b0ff72199a5ea42388d5e0947368739c94" } From 581e12c1554e9dafc71a0bfec07495c006b12b24 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Tue, 20 Aug 2024 22:17:53 -0700 Subject: [PATCH 19/21] clean up --- fvm/evm/handler/handler.go | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/fvm/evm/handler/handler.go b/fvm/evm/handler/handler.go index a9c8b53bcc6..89dbeddb0f5 100644 --- a/fvm/evm/handler/handler.go +++ b/fvm/evm/handler/handler.go @@ -396,11 +396,8 @@ func (h *ContractHandler) run( // step 9 - emit transaction event err = h.emitEvent( - events.NewTransactionEvent( - res, - rlpEncodedTx, - bp.Height, - )) + events.NewTransactionEvent(res, rlpEncodedTx, bp.Height), + ) if err != nil { return nil, err @@ -608,11 +605,7 @@ func (h *ContractHandler) executeAndHandleCall( return nil, err } err = h.emitEvent( - events.NewTransactionEvent( - res, - encoded, - bp.Height, - ), + events.NewTransactionEvent(res, encoded, bp.Height), ) if err != nil { return nil, err From 5c4a34aa173cc07ee99d7eb654af60f7cf898db8 Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 21 Aug 2024 08:33:36 -0700 Subject: [PATCH 20/21] Make linter happy --- cmd/util/ledger/migrations/type_requirements_extractor.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/util/ledger/migrations/type_requirements_extractor.go b/cmd/util/ledger/migrations/type_requirements_extractor.go index f81a063dd15..82d2c527217 100644 --- a/cmd/util/ledger/migrations/type_requirements_extractor.go +++ b/cmd/util/ledger/migrations/type_requirements_extractor.go @@ -158,9 +158,7 @@ func extractTypeRequirements( legacyTypeRequirements.typeRequirements = append(legacyTypeRequirements.typeRequirements, typeRequirement) reporter.Write(typeRequirementRemovalEntry{ - Address: typeRequirement.Address, - ContractName: typeRequirement.ContractName, - TypeName: typeRequirement.TypeName, + TypeRequirement: typeRequirement, }) } @@ -170,9 +168,7 @@ func extractTypeRequirements( // cadenceValueMigrationFailureEntry type typeRequirementRemovalEntry struct { - Address common.Address - ContractName string - TypeName string + TypeRequirement } var _ valueMigrationReportEntry = typeRequirementRemovalEntry{} From 4f94b136d8292e1fdaf45064f4df6bc89304b6ff Mon Sep 17 00:00:00 2001 From: Supun Setunga Date: Wed, 21 Aug 2024 10:01:32 -0700 Subject: [PATCH 21/21] Refactor code --- .../cadence_values_migration_test.go | 24 ++-- .../migrations/contract_checking_migration.go | 129 +++++++++--------- .../migrations/static_type_migration.go | 6 + .../migrations/type_requirements_extractor.go | 77 ++--------- 4 files changed, 98 insertions(+), 138 deletions(-) diff --git a/cmd/util/ledger/migrations/cadence_values_migration_test.go b/cmd/util/ledger/migrations/cadence_values_migration_test.go index 324c19809b4..ba94f31aa13 100644 --- a/cmd/util/ledger/migrations/cadence_values_migration_test.go +++ b/cmd/util/ledger/migrations/cadence_values_migration_test.go @@ -2885,19 +2885,25 @@ func TestTypeRequirementRemovalMigration(t *testing.T) { t, []any{ typeRequirementRemovalEntry{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "ContentLocation", + TypeRequirement{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "ContentLocation", + }, }, typeRequirementRemovalEntry{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Producer", + TypeRequirement{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Producer", + }, }, typeRequirementRemovalEntry{ - Address: tiblesAddress, - ContractName: contractName, - TypeName: "Minter", + TypeRequirement{ + Address: tiblesAddress, + ContractName: contractName, + TypeName: "Minter", + }, }, }, reporter.entries, diff --git a/cmd/util/ledger/migrations/contract_checking_migration.go b/cmd/util/ledger/migrations/contract_checking_migration.go index 5f9b9fe524b..5140cc4a424 100644 --- a/cmd/util/ledger/migrations/contract_checking_migration.go +++ b/cmd/util/ledger/migrations/contract_checking_migration.go @@ -51,70 +51,15 @@ func NewContractCheckingMigration( return fmt.Errorf("failed to create interpreter migration runtime: %w", err) } - // Gather all contracts - - log.Info().Msg("Gathering contracts ...") - - contractsForPrettyPrinting := make(map[common.Location][]byte, contractCountEstimate) - - contracts := make([]AddressContract, 0, contractCountEstimate) - - err = registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { - owner := accountRegisters.Owner() - - encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) - if err != nil { - return err - } - - contractNames, err := environment.DecodeContractNames(encodedContractNames) - if err != nil { - return err - } - - for _, contractName := range contractNames { - - contractKey := flow.ContractKey(contractName) - - code, err := accountRegisters.Get(owner, contractKey) - if err != nil { - return err - } - - if len(bytes.TrimSpace(code)) == 0 { - continue - } - - address := common.Address([]byte(owner)) - location := common.AddressLocation{ - Address: address, - Name: contractName, - } - - contracts = append( - contracts, - AddressContract{ - location: location, - code: code, - }, - ) - - contractsForPrettyPrinting[location] = code - } - - return nil - }) + contracts, err := gatherContractsFromRegisters(registersByAccount, log) if err != nil { - return fmt.Errorf("failed to get contracts of accounts: %w", err) + return err } - sort.Slice(contracts, func(i, j int) bool { - a := contracts[i] - b := contracts[j] - return a.location.ID() < b.location.ID() - }) - - log.Info().Msgf("Gathered all contracts (%d)", len(contracts)) + contractsForPrettyPrinting := make(map[common.Location][]byte, len(contracts)) + for _, contract := range contracts { + contractsForPrettyPrinting[contract.location] = contract.code + } // Check all contracts @@ -135,6 +80,68 @@ func NewContractCheckingMigration( } } +func gatherContractsFromRegisters(registersByAccount *registers.ByAccount, log zerolog.Logger) ([]AddressContract, error) { + log.Info().Msg("Gathering contracts ...") + + contracts := make([]AddressContract, 0, contractCountEstimate) + + err := registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { + owner := accountRegisters.Owner() + + encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) + if err != nil { + return err + } + + contractNames, err := environment.DecodeContractNames(encodedContractNames) + if err != nil { + return err + } + + for _, contractName := range contractNames { + + contractKey := flow.ContractKey(contractName) + + code, err := accountRegisters.Get(owner, contractKey) + if err != nil { + return err + } + + if len(bytes.TrimSpace(code)) == 0 { + continue + } + + address := common.Address([]byte(owner)) + location := common.AddressLocation{ + Address: address, + Name: contractName, + } + + contracts = append( + contracts, + AddressContract{ + location: location, + code: code, + }, + ) + } + + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to get contracts of accounts: %w", err) + } + + sort.Slice(contracts, func(i, j int) bool { + a := contracts[i] + b := contracts[j] + return a.location.ID() < b.location.ID() + }) + + log.Info().Msgf("Gathered all contracts (%d)", len(contracts)) + return contracts, nil +} + func checkContract( contract AddressContract, log zerolog.Logger, diff --git a/cmd/util/ledger/migrations/static_type_migration.go b/cmd/util/ledger/migrations/static_type_migration.go index ec8cd6577a1..c34164cd755 100644 --- a/cmd/util/ledger/migrations/static_type_migration.go +++ b/cmd/util/ledger/migrations/static_type_migration.go @@ -7,6 +7,12 @@ import ( type StaticTypeMigrationRules map[common.TypeID]interpreter.StaticType +// NewStaticTypeMigration returns a type converter function. +// Accepts a `rulesGetter` which return +// This is because this constructor is called at the time of constructing the migrations (e.g: Cadence value migration), +// but the rules can only be finalized after running previous TypeRequirementsExtractingMigration migration. +// i.e: the LegacyTypeRequirements list used by NewCompositeTypeConversionRules is lazily populated. +// So we need to delay the construction of the rules, until after the execution of previous migration. func NewStaticTypeMigration[T interpreter.StaticType]( rulesGetter func() StaticTypeMigrationRules, ) func(staticType T) interpreter.StaticType { diff --git a/cmd/util/ledger/migrations/type_requirements_extractor.go b/cmd/util/ledger/migrations/type_requirements_extractor.go index 82d2c527217..d4965e8eb87 100644 --- a/cmd/util/ledger/migrations/type_requirements_extractor.go +++ b/cmd/util/ledger/migrations/type_requirements_extractor.go @@ -1,10 +1,7 @@ package migrations import ( - "bytes" "encoding/json" - "fmt" - "sort" "github.com/rs/zerolog" @@ -13,8 +10,6 @@ import ( "github.com/onflow/flow-go/cmd/util/ledger/reporters" "github.com/onflow/flow-go/cmd/util/ledger/util/registers" - "github.com/onflow/flow-go/fvm/environment" - "github.com/onflow/flow-go/model/flow" ) const typeRequirementExtractingReporterName = "type-requirements-extracting" @@ -42,74 +37,20 @@ func NewTypeRequirementsExtractingMigration( // Gather all contracts - log.Info().Msg("Gathering contracts ...") - - contracts := make([]AddressContract, 0, contractCountEstimate) - - err := registersByAccount.ForEachAccount(func(accountRegisters *registers.AccountRegisters) error { - owner := accountRegisters.Owner() - - encodedContractNames, err := accountRegisters.Get(owner, flow.ContractNamesKey) - if err != nil { - return err - } - - contractNames, err := environment.DecodeContractNames(encodedContractNames) - if err != nil { - return err - } - - for _, contractName := range contractNames { - - contractKey := flow.ContractKey(contractName) - - code, err := accountRegisters.Get(owner, contractKey) - if err != nil { - return err - } - - if len(bytes.TrimSpace(code)) == 0 { - continue - } - - address := common.Address([]byte(owner)) - location := common.AddressLocation{ - Address: address, - Name: contractName, - } - - if _, isSystemContract := importantLocations[location]; isSystemContract { - // System contracts have their own type-changing rules. - // So do not add them here. - continue - } - - contracts = append( - contracts, - AddressContract{ - location: location, - code: code, - }, - ) - } - - return nil - }) + contracts, err := gatherContractsFromRegisters(registersByAccount, log) if err != nil { - return fmt.Errorf("failed to get contracts of accounts: %w", err) + return err } - sort.Slice(contracts, func(i, j int) bool { - a := contracts[i] - b := contracts[j] - return a.location.ID() < b.location.ID() - }) - - log.Info().Msgf("Gathered all contracts (%d)", len(contracts)) - - // Check all contracts + // Extract type requirements from all contracts for _, contract := range contracts { + if _, isSystemContract := importantLocations[contract.location]; isSystemContract { + // System contracts have their own type-changing rules. + // So do not add them here. + continue + } + extractTypeRequirements( contract, log,