From df2464ef88816aa52976f963624547f9b1e3c0a1 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Wed, 15 Jan 2025 19:25:25 +0100 Subject: [PATCH 1/6] move execution parameter to separate account --- fvm/executionParameters.go | 6 ++-- fvm/systemcontracts/system_contracts.go | 39 ++++++++++++++++--------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/fvm/executionParameters.go b/fvm/executionParameters.go index ac0ec6223f0..bb3f2075318 100644 --- a/fvm/executionParameters.go +++ b/fvm/executionParameters.go @@ -19,6 +19,7 @@ import ( "github.com/onflow/flow-go/fvm/storage" "github.com/onflow/flow-go/fvm/storage/derived" "github.com/onflow/flow-go/fvm/storage/state" + "github.com/onflow/flow-go/fvm/systemcontracts" ) func ProcedureStateParameters( @@ -143,10 +144,11 @@ func (computer ExecutionParametersComputer) getExecutionParameters() ( derived.StateExecutionParameters, error, ) { + sc := systemcontracts.SystemContractsForChain(computer.ctx.Chain.ChainID()) + // Check that the service account exists because all the settings are // stored in it - serviceAddress := computer.ctx.Chain.ServiceAddress() - service := common.Address(serviceAddress) + service := common.Address(sc.ExecutionParametersAccount.Address) env := environment.NewScriptEnv( context.Background(), diff --git a/fvm/systemcontracts/system_contracts.go b/fvm/systemcontracts/system_contracts.go index 7fd321d88bc..8773ce4b0ed 100644 --- a/fvm/systemcontracts/system_contracts.go +++ b/fvm/systemcontracts/system_contracts.go @@ -45,7 +45,8 @@ const ( ContractNameBurner = "Burner" // AccountNameEVMStorage is not a contract, but a special account that is used to store EVM state - AccountNameEVMStorage = "EVMStorageAccount" + AccountNameEVMStorage = "EVMStorageAccount" + AccountNameExecutionParametersAccount = "ExecutionParametersAccount" // Unqualified names of service events (not including address prefix or contract name) @@ -148,10 +149,11 @@ type SystemContracts struct { DKG SystemContract // service account related contracts - FlowServiceAccount SystemContract - NodeVersionBeacon SystemContract - RandomBeaconHistory SystemContract - FlowStorageFees SystemContract + FlowServiceAccount SystemContract + NodeVersionBeacon SystemContract + RandomBeaconHistory SystemContract + FlowStorageFees SystemContract + ExecutionParametersAccount SystemContract // token related contracts FlowFees SystemContract @@ -331,16 +333,26 @@ func init() { } } + executionParametersAccountFunc := func(chain flow.ChainID) flow.Address { + switch chain { + case flow.Testnet: + return flow.HexToAddress("b118676e2f11145f") + default: + return serviceAddressFunc(chain) + } + } + contractAddressFunc = map[string]func(id flow.ChainID) flow.Address{ ContractNameIDTableStaking: epochAddressFunc, ContractNameEpoch: epochAddressFunc, ContractNameClusterQC: epochAddressFunc, ContractNameDKG: epochAddressFunc, - ContractNameNodeVersionBeacon: serviceAddressFunc, - ContractNameRandomBeaconHistory: serviceAddressFunc, - ContractNameServiceAccount: serviceAddressFunc, - ContractNameStorageFees: serviceAddressFunc, + ContractNameNodeVersionBeacon: serviceAddressFunc, + ContractNameRandomBeaconHistory: serviceAddressFunc, + ContractNameServiceAccount: serviceAddressFunc, + ContractNameStorageFees: serviceAddressFunc, + AccountNameExecutionParametersAccount: executionParametersAccountFunc, ContractNameFlowFees: nthAddressFunc(FlowFeesAccountIndex), ContractNameFungibleToken: nthAddressFunc(FungibleTokenAccountIndex), @@ -392,10 +404,11 @@ func init() { ClusterQC: addressOfContract(ContractNameClusterQC), DKG: addressOfContract(ContractNameDKG), - FlowServiceAccount: addressOfContract(ContractNameServiceAccount), - NodeVersionBeacon: addressOfContract(ContractNameNodeVersionBeacon), - RandomBeaconHistory: addressOfContract(ContractNameRandomBeaconHistory), - FlowStorageFees: addressOfContract(ContractNameStorageFees), + FlowServiceAccount: addressOfContract(ContractNameServiceAccount), + NodeVersionBeacon: addressOfContract(ContractNameNodeVersionBeacon), + RandomBeaconHistory: addressOfContract(ContractNameRandomBeaconHistory), + FlowStorageFees: addressOfContract(ContractNameStorageFees), + ExecutionParametersAccount: addressOfContract(AccountNameExecutionParametersAccount), FlowFees: addressOfContract(ContractNameFlowFees), FlowToken: addressOfContract(ContractNameFlowToken), From ea646a62eb095db4ac924e722aabc6df62b4bb97 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Wed, 15 Jan 2025 22:04:28 +0100 Subject: [PATCH 2/6] cleanup --- fvm/executionParameters.go | 7 +++++-- fvm/systemcontracts/system_contracts.go | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/fvm/executionParameters.go b/fvm/executionParameters.go index bb3f2075318..3c4fa8defc7 100644 --- a/fvm/executionParameters.go +++ b/fvm/executionParameters.go @@ -146,8 +146,11 @@ func (computer ExecutionParametersComputer) getExecutionParameters() ( ) { sc := systemcontracts.SystemContractsForChain(computer.ctx.Chain.ChainID()) - // Check that the service account exists because all the settings are - // stored in it + // The execution parameters are stored in the ExecutionParametersAccount. This is + // just the service account for all networks except mainnet and testnet. + // For mainnet and testnet, the execution parameters are stored in a separate + // account, so that they are separated from the frequently changing data on the + // service account. service := common.Address(sc.ExecutionParametersAccount.Address) env := environment.NewScriptEnv( diff --git a/fvm/systemcontracts/system_contracts.go b/fvm/systemcontracts/system_contracts.go index 8773ce4b0ed..b25a36cbf58 100644 --- a/fvm/systemcontracts/system_contracts.go +++ b/fvm/systemcontracts/system_contracts.go @@ -45,7 +45,10 @@ const ( ContractNameBurner = "Burner" // AccountNameEVMStorage is not a contract, but a special account that is used to store EVM state - AccountNameEVMStorage = "EVMStorageAccount" + AccountNameEVMStorage = "EVMStorageAccount" + // AccountNameExecutionParametersAccount is not a contract, but a special account that is used to store execution parameters + // This is generally just the service account. For mainnet and testnet, it is a separate account, + // in order to separate it away from the frequently changing data on the service account. AccountNameExecutionParametersAccount = "ExecutionParametersAccount" // Unqualified names of service events (not including address prefix or contract name) @@ -100,6 +103,11 @@ var ( evmStorageAddressTestnet = flow.HexToAddress("1a54ed2be7552821") // evmStorageAddressMainnet is the address of the EVM state storage contract on Mainnet evmStorageAddressMainnet = flow.HexToAddress("d421a63faae318f9") + + // executionParametersAddressTestnet is the address of the Execution Parameters contract on Testnet + executionParametersAddressTestnet = flow.HexToAddress("00000000000") + // executionParametersAddressMainnet is the address of the Execution Parameters contract on Mainnet + executionParametersAddressMainnet = flow.HexToAddress("00000000000") ) // SystemContract represents a system contract on a particular chain. @@ -335,8 +343,10 @@ func init() { executionParametersAccountFunc := func(chain flow.ChainID) flow.Address { switch chain { + case flow.Mainnet: + return executionParametersAddressMainnet case flow.Testnet: - return flow.HexToAddress("b118676e2f11145f") + return executionParametersAddressTestnet default: return serviceAddressFunc(chain) } From db82d0d89d6bf82acb6a4f19a4e0a11077a30cc2 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Thu, 16 Jan 2025 15:44:32 +0100 Subject: [PATCH 3/6] set testnet account --- fvm/systemcontracts/system_contracts.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fvm/systemcontracts/system_contracts.go b/fvm/systemcontracts/system_contracts.go index b25a36cbf58..dfcc44524d1 100644 --- a/fvm/systemcontracts/system_contracts.go +++ b/fvm/systemcontracts/system_contracts.go @@ -105,9 +105,10 @@ var ( evmStorageAddressMainnet = flow.HexToAddress("d421a63faae318f9") // executionParametersAddressTestnet is the address of the Execution Parameters contract on Testnet - executionParametersAddressTestnet = flow.HexToAddress("00000000000") + executionParametersAddressTestnet = flow.HexToAddress("a71b77409f5acbc1") // executionParametersAddressMainnet is the address of the Execution Parameters contract on Mainnet - executionParametersAddressMainnet = flow.HexToAddress("00000000000") + // TODO: update this address once the Execution Parameters contract is deployed on Mainnet + executionParametersAddressMainnet = flow.HexToAddress("TODO") ) // SystemContract represents a system contract on a particular chain. From 7dbf7c575ac987f419ecc4c175d661f75cedda8b Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Thu, 16 Jan 2025 18:13:57 +0100 Subject: [PATCH 4/6] fix tests --- .../derived_data_invalidator_test.go | 2 +- fvm/fvm_test.go | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/fvm/environment/derived_data_invalidator_test.go b/fvm/environment/derived_data_invalidator_test.go index 2eed1cf36fe..7c7038948f7 100644 --- a/fvm/environment/derived_data_invalidator_test.go +++ b/fvm/environment/derived_data_invalidator_test.go @@ -244,7 +244,7 @@ func TestMeterParamOverridesUpdated(t *testing.T) { snapshotTree := snapshot.NewSnapshotTree(nil) - ctx := fvm.NewContext(fvm.WithChain(flow.Testnet.Chain())) + ctx := fvm.NewContext(fvm.WithChain(flow.Emulator.Chain())) vm := fvm.NewVirtualMachine() executionSnapshot, _, err := vm.Run( diff --git a/fvm/fvm_test.go b/fvm/fvm_test.go index e621d0ec2b5..44bbb371ca8 100644 --- a/fvm/fvm_test.go +++ b/fvm/fvm_test.go @@ -1080,7 +1080,11 @@ func TestTransactionFeeDeduction(t *testing.T) { func TestSettingExecutionWeights(t *testing.T) { + // change the chain so that the metering settings are read from the service account + chain := flow.Emulator.Chain() + t.Run("transaction should fail with high weights", newVMTest().withBootstrapProcedureOptions( + fvm.WithMinimumStorageReservation(fvm.DefaultMinimumStorageReservation), fvm.WithAccountCreationFee(fvm.DefaultAccountCreationFee), fvm.WithStorageMBPerFLOW(fvm.DefaultStorageMBPerFLOW), @@ -1089,6 +1093,8 @@ func TestSettingExecutionWeights(t *testing.T) { common.ComputationKindLoop: 100_000 << meter.MeterExecutionInternalPrecisionBytes, }, ), + ).withContextOptions( + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { @@ -1137,6 +1143,7 @@ func TestSettingExecutionWeights(t *testing.T) { ), ).withContextOptions( fvm.WithMemoryLimit(10_000_000_000), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { // Create an account private key. @@ -1187,6 +1194,7 @@ func TestSettingExecutionWeights(t *testing.T) { ), ).withContextOptions( fvm.WithMemoryLimit(10_000_000_000), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { @@ -1231,6 +1239,8 @@ func TestSettingExecutionWeights(t *testing.T) { fvm.WithExecutionMemoryWeights( memoryWeights, ), + ).withContextOptions( + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { privateKeys, err := testutil.GenerateAccountPrivateKeys(1) @@ -1298,6 +1308,8 @@ func TestSettingExecutionWeights(t *testing.T) { environment.ComputationKindCreateAccount: (fvm.DefaultComputationLimit + 1) << meter.MeterExecutionInternalPrecisionBytes, }, ), + ).withContextOptions( + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { txBody := flow.NewTransactionBody(). @@ -1334,6 +1346,8 @@ func TestSettingExecutionWeights(t *testing.T) { environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes, }, ), + ).withContextOptions( + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { @@ -1371,6 +1385,8 @@ func TestSettingExecutionWeights(t *testing.T) { environment.ComputationKindCreateAccount: 100_000_000 << meter.MeterExecutionInternalPrecisionBytes, }, ), + ).withContextOptions( + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { txBody := flow.NewTransactionBody(). @@ -1414,6 +1430,7 @@ func TestSettingExecutionWeights(t *testing.T) { fvm.WithAccountStorageLimit(true), fvm.WithTransactionFeesEnabled(true), fvm.WithMemoryLimit(math.MaxUint64), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { // Use the maximum amount of computation so that the transaction still passes. @@ -1507,6 +1524,7 @@ func TestSettingExecutionWeights(t *testing.T) { fvm.WithAccountStorageLimit(true), fvm.WithTransactionFeesEnabled(true), fvm.WithMemoryLimit(math.MaxUint64), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { // Create an account private key. @@ -2162,6 +2180,8 @@ func TestScriptExecutionLimit(t *testing.T) { t.Parallel() + chain := flow.Emulator.Chain() + script := fvm.Script([]byte(` access(all) fun main() { var s: Int256 = 1024102410241024 @@ -2203,6 +2223,7 @@ func TestScriptExecutionLimit(t *testing.T) { fvm.WithTransactionFeesEnabled(true), fvm.WithAccountStorageLimit(true), fvm.WithComputationLimit(10000), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { scriptCtx := fvm.NewContextFromParent(ctx) @@ -2225,6 +2246,7 @@ func TestScriptExecutionLimit(t *testing.T) { fvm.WithTransactionFeesEnabled(true), fvm.WithAccountStorageLimit(true), fvm.WithComputationLimit(20000), + fvm.WithChain(chain), ).run( func(t *testing.T, vm fvm.VM, chain flow.Chain, ctx fvm.Context, snapshotTree snapshot.SnapshotTree) { scriptCtx := fvm.NewContextFromParent(ctx) From 61c2b347c58f2c421539b4ffa9f0f228e7580cb3 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Fri, 17 Jan 2025 15:44:49 +0100 Subject: [PATCH 5/6] add missing mocks --- .../mock/snapshot_execution_subset.go | 87 +++++++++++++++++++ .../snapshot_execution_subset_provider.go | 49 +++++++++++ 2 files changed, 136 insertions(+) create mode 100644 state/protocol/mock/snapshot_execution_subset.go create mode 100644 state/protocol/mock/snapshot_execution_subset_provider.go diff --git a/state/protocol/mock/snapshot_execution_subset.go b/state/protocol/mock/snapshot_execution_subset.go new file mode 100644 index 00000000000..1950f9adace --- /dev/null +++ b/state/protocol/mock/snapshot_execution_subset.go @@ -0,0 +1,87 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" +) + +// SnapshotExecutionSubset is an autogenerated mock type for the SnapshotExecutionSubset type +type SnapshotExecutionSubset struct { + mock.Mock +} + +// RandomSource provides a mock function with given fields: +func (_m *SnapshotExecutionSubset) RandomSource() ([]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RandomSource") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// VersionBeacon provides a mock function with given fields: +func (_m *SnapshotExecutionSubset) VersionBeacon() (*flow.SealedVersionBeacon, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for VersionBeacon") + } + + var r0 *flow.SealedVersionBeacon + var r1 error + if rf, ok := ret.Get(0).(func() (*flow.SealedVersionBeacon, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *flow.SealedVersionBeacon); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*flow.SealedVersionBeacon) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewSnapshotExecutionSubset creates a new instance of SnapshotExecutionSubset. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSnapshotExecutionSubset(t interface { + mock.TestingT + Cleanup(func()) +}) *SnapshotExecutionSubset { + mock := &SnapshotExecutionSubset{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/state/protocol/mock/snapshot_execution_subset_provider.go b/state/protocol/mock/snapshot_execution_subset_provider.go new file mode 100644 index 00000000000..445c6daa7f5 --- /dev/null +++ b/state/protocol/mock/snapshot_execution_subset_provider.go @@ -0,0 +1,49 @@ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package mock + +import ( + flow "github.com/onflow/flow-go/model/flow" + mock "github.com/stretchr/testify/mock" + + protocol "github.com/onflow/flow-go/state/protocol" +) + +// SnapshotExecutionSubsetProvider is an autogenerated mock type for the SnapshotExecutionSubsetProvider type +type SnapshotExecutionSubsetProvider struct { + mock.Mock +} + +// AtBlockID provides a mock function with given fields: blockID +func (_m *SnapshotExecutionSubsetProvider) AtBlockID(blockID flow.Identifier) protocol.SnapshotExecutionSubset { + ret := _m.Called(blockID) + + if len(ret) == 0 { + panic("no return value specified for AtBlockID") + } + + var r0 protocol.SnapshotExecutionSubset + if rf, ok := ret.Get(0).(func(flow.Identifier) protocol.SnapshotExecutionSubset); ok { + r0 = rf(blockID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(protocol.SnapshotExecutionSubset) + } + } + + return r0 +} + +// NewSnapshotExecutionSubsetProvider creates a new instance of SnapshotExecutionSubsetProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSnapshotExecutionSubsetProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *SnapshotExecutionSubsetProvider { + mock := &SnapshotExecutionSubsetProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 25b9987c9497ae8bb9a851e2d8009071bfa560d5 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Tue, 21 Jan 2025 18:48:53 +0100 Subject: [PATCH 6/6] update accounts --- fvm/systemcontracts/system_contracts.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fvm/systemcontracts/system_contracts.go b/fvm/systemcontracts/system_contracts.go index dfcc44524d1..ba5e7854724 100644 --- a/fvm/systemcontracts/system_contracts.go +++ b/fvm/systemcontracts/system_contracts.go @@ -105,10 +105,9 @@ var ( evmStorageAddressMainnet = flow.HexToAddress("d421a63faae318f9") // executionParametersAddressTestnet is the address of the Execution Parameters contract on Testnet - executionParametersAddressTestnet = flow.HexToAddress("a71b77409f5acbc1") + executionParametersAddressTestnet = flow.HexToAddress("6997a2f2cf57b73a") // executionParametersAddressMainnet is the address of the Execution Parameters contract on Mainnet - // TODO: update this address once the Execution Parameters contract is deployed on Mainnet - executionParametersAddressMainnet = flow.HexToAddress("TODO") + executionParametersAddressMainnet = flow.HexToAddress("f426ff57ee8f6110") ) // SystemContract represents a system contract on a particular chain.