diff --git a/README.md b/README.md index fe312bc8..027dc97e 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,8 @@ and if you plan to run the emulator with Docker you must use the environment var | `--start-block-height` | `FLOW_STARTBLOCKHEIGHT` | `0` | Start block height to use when starting the network using 'testnet' or 'mainnet' as the chain-id | | `--evm-enabled` | `FLOW_EVMENABLED` | `false` | Enable evm support | +| `--legacy-upgrade` | `FLOW_LEGACYUPGRADE` | `false` | Enable upgrading of legacy contracts | + ## Running the emulator with the Flow CLI The emulator is bundled with the [Flow CLI](https://docs.onflow.org/flow-cli), a command-line interface for working with Flow. diff --git a/cmd/emulator/start/start.go b/cmd/emulator/start/start.go index d14d7dc0..7a4aef9a 100644 --- a/cmd/emulator/start/start.go +++ b/cmd/emulator/start/start.go @@ -39,41 +39,42 @@ import ( ) type Config struct { - Port int `default:"3569" flag:"port,p" info:"port to run RPC server"` - DebuggerPort int `default:"2345" flag:"debugger-port" info:"port to run the Debugger (Debug Adapter Protocol)"` - RestPort int `default:"8888" flag:"rest-port" info:"port to run the REST API"` - AdminPort int `default:"8080" flag:"admin-port" info:"port to run the admin API"` - Verbose bool `default:"false" flag:"verbose,v" info:"enable verbose logging"` - LogFormat string `default:"text" flag:"log-format" info:"logging output format. Valid values (text, JSON)"` - BlockTime time.Duration `flag:"block-time,b" info:"time between sealed blocks, e.g. '300ms', '-1.5h' or '2h45m'. Valid units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'"` - ServicePrivateKey string `flag:"service-priv-key" info:"service account private key"` - ServicePublicKey string `flag:"service-pub-key" info:"service account public key"` - ServiceKeySigAlgo string `default:"ECDSA_P256" flag:"service-sig-algo" info:"service account key signature algorithm"` - ServiceKeyHashAlgo string `default:"SHA3_256" flag:"service-hash-algo" info:"service account key hash algorithm"` - Init bool `default:"false" flag:"init" info:"whether to initialize a new account profile"` - GRPCDebug bool `default:"false" flag:"grpc-debug" info:"enable gRPC server reflection for debugging with grpc_cli"` - RESTDebug bool `default:"false" flag:"rest-debug" info:"enable REST API debugging output"` - Persist bool `default:"false" flag:"persist" info:"enable persistent storage"` - Snapshot bool `default:"false" flag:"snapshot" info:"enable snapshots for emulator"` - DBPath string `default:"./flowdb" flag:"dbpath" info:"path to database directory"` - SimpleAddresses bool `default:"false" flag:"simple-addresses" info:"use sequential addresses starting with 0x01"` - TokenSupply string `default:"1000000000.0" flag:"token-supply" info:"initial FLOW token supply"` - TransactionExpiry int `default:"10" flag:"transaction-expiry" info:"transaction expiry, measured in blocks"` - StorageLimitEnabled bool `default:"true" flag:"storage-limit" info:"enable account storage limit"` - StorageMBPerFLOW string `flag:"storage-per-flow" info:"the MB amount of storage capacity an account has per 1 FLOW token it has. e.g. '100.0'. The default is taken from the current version of flow-go"` - MinimumAccountBalance string `flag:"min-account-balance" info:"The minimum account balance of an account. This is also the cost of creating one account. e.g. '0.001'. The default is taken from the current version of flow-go"` - TransactionFeesEnabled bool `default:"false" flag:"transaction-fees" info:"enable transaction fees"` - TransactionMaxGasLimit int `default:"9999" flag:"transaction-max-gas-limit" info:"maximum gas limit for transactions"` - ScriptGasLimit int `default:"100000" flag:"script-gas-limit" info:"gas limit for scripts"` - Contracts bool `default:"false" flag:"contracts" info:"deploy common contracts when emulator starts"` - ContractRemovalEnabled bool `default:"true" flag:"contract-removal" info:"allow removal of already deployed contracts, used for updating during development"` - SkipTxValidation bool `default:"false" flag:"skip-tx-validation" info:"skip verification of transaction signatures and sequence numbers"` - Host string `default:"" flag:"host" info:"host to listen on for emulator GRPC/REST/Admin servers (default: all interfaces)"` - ChainID string `default:"emulator" flag:"chain-id" info:"chain to emulate for address generation. Valid values are: 'emulator', 'testnet', 'mainnet'"` - RedisURL string `default:"" flag:"redis-url" info:"redis-server URL for persisting redis storage backend ( redis://[[username:]password@]host[:port][/database] ) "` - SqliteURL string `default:"" flag:"sqlite-url" info:"sqlite db URL for persisting sqlite storage backend "` - CoverageReportingEnabled bool `default:"false" flag:"coverage-reporting" info:"enable Cadence code coverage reporting"` - EVMEnabled bool `default:"false" flag:"evm-enabled" info:"enable EVM support"` + Port int `default:"3569" flag:"port,p" info:"port to run RPC server"` + DebuggerPort int `default:"2345" flag:"debugger-port" info:"port to run the Debugger (Debug Adapter Protocol)"` + RestPort int `default:"8888" flag:"rest-port" info:"port to run the REST API"` + AdminPort int `default:"8080" flag:"admin-port" info:"port to run the admin API"` + Verbose bool `default:"false" flag:"verbose,v" info:"enable verbose logging"` + LogFormat string `default:"text" flag:"log-format" info:"logging output format. Valid values (text, JSON)"` + BlockTime time.Duration `flag:"block-time,b" info:"time between sealed blocks, e.g. '300ms', '-1.5h' or '2h45m'. Valid units are 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h'"` + ServicePrivateKey string `flag:"service-priv-key" info:"service account private key"` + ServicePublicKey string `flag:"service-pub-key" info:"service account public key"` + ServiceKeySigAlgo string `default:"ECDSA_P256" flag:"service-sig-algo" info:"service account key signature algorithm"` + ServiceKeyHashAlgo string `default:"SHA3_256" flag:"service-hash-algo" info:"service account key hash algorithm"` + Init bool `default:"false" flag:"init" info:"whether to initialize a new account profile"` + GRPCDebug bool `default:"false" flag:"grpc-debug" info:"enable gRPC server reflection for debugging with grpc_cli"` + RESTDebug bool `default:"false" flag:"rest-debug" info:"enable REST API debugging output"` + Persist bool `default:"false" flag:"persist" info:"enable persistent storage"` + Snapshot bool `default:"false" flag:"snapshot" info:"enable snapshots for emulator"` + DBPath string `default:"./flowdb" flag:"dbpath" info:"path to database directory"` + SimpleAddresses bool `default:"false" flag:"simple-addresses" info:"use sequential addresses starting with 0x01"` + TokenSupply string `default:"1000000000.0" flag:"token-supply" info:"initial FLOW token supply"` + TransactionExpiry int `default:"10" flag:"transaction-expiry" info:"transaction expiry, measured in blocks"` + StorageLimitEnabled bool `default:"true" flag:"storage-limit" info:"enable account storage limit"` + StorageMBPerFLOW string `flag:"storage-per-flow" info:"the MB amount of storage capacity an account has per 1 FLOW token it has. e.g. '100.0'. The default is taken from the current version of flow-go"` + MinimumAccountBalance string `flag:"min-account-balance" info:"The minimum account balance of an account. This is also the cost of creating one account. e.g. '0.001'. The default is taken from the current version of flow-go"` + TransactionFeesEnabled bool `default:"false" flag:"transaction-fees" info:"enable transaction fees"` + TransactionMaxGasLimit int `default:"9999" flag:"transaction-max-gas-limit" info:"maximum gas limit for transactions"` + ScriptGasLimit int `default:"100000" flag:"script-gas-limit" info:"gas limit for scripts"` + Contracts bool `default:"false" flag:"contracts" info:"deploy common contracts when emulator starts"` + ContractRemovalEnabled bool `default:"true" flag:"contract-removal" info:"allow removal of already deployed contracts, used for updating during development"` + SkipTxValidation bool `default:"false" flag:"skip-tx-validation" info:"skip verification of transaction signatures and sequence numbers"` + Host string `default:"" flag:"host" info:"host to listen on for emulator GRPC/REST/Admin servers (default: all interfaces)"` + ChainID string `default:"emulator" flag:"chain-id" info:"chain to emulate for address generation. Valid values are: 'emulator', 'testnet', 'mainnet'"` + RedisURL string `default:"" flag:"redis-url" info:"redis-server URL for persisting redis storage backend ( redis://[[username:]password@]host[:port][/database] ) "` + SqliteURL string `default:"" flag:"sqlite-url" info:"sqlite db URL for persisting sqlite storage backend "` + CoverageReportingEnabled bool `default:"false" flag:"coverage-reporting" info:"enable Cadence code coverage reporting"` + LegacyContractUpgradeEnabled bool `default:"false" flag:"legacy-upgrade" info:"enable Cadence legacy contract upgrade"` + EVMEnabled bool `default:"false" flag:"evm-enabled" info:"enable EVM support"` // todo temporarily disabled until remote register endpoint is re-enabled // StartBlockHeight uint64 `default:"0" flag:"start-block-height" info:"block height to start the emulator at. only valid when forking Mainnet or Testnet"` } @@ -174,33 +175,34 @@ func Cmd(getServiceKey serviceKeyFunc) *cobra.Command { RESTPort: conf.RestPort, RESTDebug: conf.RESTDebug, // TODO: allow headers to be parsed from environment - HTTPHeaders: nil, - BlockTime: conf.BlockTime, - ServicePublicKey: servicePublicKey, - ServicePrivateKey: servicePrivateKey, - ServiceKeySigAlgo: serviceKeySigAlgo, - ServiceKeyHashAlgo: serviceKeyHashAlgo, - Persist: conf.Persist, - Snapshot: conf.Snapshot, - DBPath: conf.DBPath, - GenesisTokenSupply: parseCadenceUFix64(conf.TokenSupply, "token-supply"), - TransactionMaxGasLimit: uint64(conf.TransactionMaxGasLimit), - ScriptGasLimit: uint64(conf.ScriptGasLimit), - TransactionExpiry: uint(conf.TransactionExpiry), - StorageLimitEnabled: conf.StorageLimitEnabled, - StorageMBPerFLOW: storageMBPerFLOW, - MinimumStorageReservation: minimumStorageReservation, - TransactionFeesEnabled: conf.TransactionFeesEnabled, - WithContracts: conf.Contracts, - SkipTransactionValidation: conf.SkipTxValidation, - SimpleAddressesEnabled: conf.SimpleAddresses, - Host: conf.Host, - ChainID: flowChainID, - RedisURL: conf.RedisURL, - ContractRemovalEnabled: conf.ContractRemovalEnabled, - SqliteURL: conf.SqliteURL, - CoverageReportingEnabled: conf.CoverageReportingEnabled, - EVMEnabled: conf.EVMEnabled, + HTTPHeaders: nil, + BlockTime: conf.BlockTime, + ServicePublicKey: servicePublicKey, + ServicePrivateKey: servicePrivateKey, + ServiceKeySigAlgo: serviceKeySigAlgo, + ServiceKeyHashAlgo: serviceKeyHashAlgo, + Persist: conf.Persist, + Snapshot: conf.Snapshot, + DBPath: conf.DBPath, + GenesisTokenSupply: parseCadenceUFix64(conf.TokenSupply, "token-supply"), + TransactionMaxGasLimit: uint64(conf.TransactionMaxGasLimit), + ScriptGasLimit: uint64(conf.ScriptGasLimit), + TransactionExpiry: uint(conf.TransactionExpiry), + StorageLimitEnabled: conf.StorageLimitEnabled, + StorageMBPerFLOW: storageMBPerFLOW, + MinimumStorageReservation: minimumStorageReservation, + TransactionFeesEnabled: conf.TransactionFeesEnabled, + WithContracts: conf.Contracts, + SkipTransactionValidation: conf.SkipTxValidation, + SimpleAddressesEnabled: conf.SimpleAddresses, + Host: conf.Host, + ChainID: flowChainID, + RedisURL: conf.RedisURL, + ContractRemovalEnabled: conf.ContractRemovalEnabled, + SqliteURL: conf.SqliteURL, + CoverageReportingEnabled: conf.CoverageReportingEnabled, + LegacyContractUpgradeEnabled: conf.LegacyContractUpgradeEnabled, + EVMEnabled: conf.EVMEnabled, // todo temporarily disabled until remote register endpoint is re-enabled // StartBlockHeight: conf.StartBlockHeight, } diff --git a/emulator/blockchain.go b/emulator/blockchain.go index ee70ba47..d8a40e8d 100644 --- a/emulator/blockchain.go +++ b/emulator/blockchain.go @@ -176,6 +176,13 @@ func WithSimpleAddresses() Option { } } +// WithLegacyUpgradeEnabled enables parsing of old contracts for legacy upgrade purposes +func WithLegacyUpgradeEnabled() Option { + return func(c *config) { + c.LegacyContractUpgradeEnabled = true + } +} + // WithGenesisTokenSupply sets the genesis token supply. func WithGenesisTokenSupply(supply cadence.UFix64) Option { return func(c *config) { @@ -358,6 +365,7 @@ type config struct { StorageLimitEnabled bool TransactionFeesEnabled bool ContractRemovalEnabled bool + LegacyContractUpgradeEnabled bool EVMEnabled bool MinimumStorageReservation cadence.UFix64 StorageMBPerFLOW cadence.UFix64 @@ -471,6 +479,10 @@ func (b *Blockchain) Ping() error { return nil } +func (b *Blockchain) Runtime() runtime.Runtime { + return b.coverageReportedRuntime +} + func (b *Blockchain) GetChain() flowgo.Chain { return b.vmCtx.Chain } @@ -579,9 +591,10 @@ func configureFVM(blockchain *Blockchain, conf config, blocks *blocks) (*fvm.Vir cadenceLogger := conf.Logger.Hook(CadenceHook{MainLogger: &conf.ServerLogger}).Level(zerolog.DebugLevel) runtimeConfig := runtime.Config{ - Debugger: blockchain.debugger, - AttachmentsEnabled: true, - CoverageReport: conf.CoverageReport, + Debugger: blockchain.debugger, + AttachmentsEnabled: true, + LegacyContractUpgradeEnabled: conf.LegacyContractUpgradeEnabled, + CoverageReport: conf.CoverageReport, } coverageReportedRuntime := &CoverageReportedRuntime{ Runtime: runtime.NewInterpreterRuntime(runtimeConfig), diff --git a/go.mod b/go.mod index b709cf75..8c24b330 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/improbable-eng/grpc-web v0.15.0 github.com/logrusorgru/aurora v2.0.3+incompatible - github.com/onflow/cadence v1.0.0-M3 + github.com/onflow/cadence v1.0.0-M4 github.com/onflow/crypto v0.25.0 github.com/onflow/flow-core-contracts/lib/go/templates v0.15.1-0.20240125214229-b7a95136dd0d github.com/onflow/flow-go v0.33.2-0.20240126002816-f0770a716d61 diff --git a/go.sum b/go.sum index ca3d33a5..b9b1f440 100644 --- a/go.sum +++ b/go.sum @@ -1973,8 +1973,9 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f h1:Z8/PgTqOgOg02MTRpTBYO2k16FE6z4wEOtaC2WBR9Xo= github.com/onflow/atree v0.6.1-0.20230711151834-86040b30171f/go.mod h1:xvP61FoOs95K7IYdIYRnNcYQGf4nbF/uuJ0tHf4DRuM= -github.com/onflow/cadence v1.0.0-M3 h1:bSydJise9pU4aALloUKv/EWmDLITRlbBpuG8OPBydZM= github.com/onflow/cadence v1.0.0-M3/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= +github.com/onflow/cadence v1.0.0-M4 h1:/nt3j7vpYDxuI0ghIgAJrb2R01ijvJYZLAkKt+zbpTY= +github.com/onflow/cadence v1.0.0-M4/go.mod h1:odXGZZ/wGNA5mwT8bC9v8u8EXACHllB2ABSZK65TGL8= github.com/onflow/crypto v0.25.0 h1:BeWbLsh3ZD13Ej+Uky6kg1PL1ZIVBDVX+2MVBNwqddg= github.com/onflow/crypto v0.25.0/go.mod h1:C8FbaX0x8y+FxWjbkHy0Q4EASCDR9bSPWZqlpCLYyVI= github.com/onflow/flow-core-contracts/lib/go/contracts v0.15.1-0.20240125214229-b7a95136dd0d h1:Afcfk/9jAQZ1v5PLGdP68FG/0yPPM60fn9Eq8ChBGS0= diff --git a/server/server.go b/server/server.go index 30bf20e0..30b711cb 100644 --- a/server/server.go +++ b/server/server.go @@ -136,6 +136,8 @@ type Config struct { SqliteURL string // CoverageReportingEnabled enables/disables Cadence code coverage reporting. CoverageReportingEnabled bool + // LegacyUpgradeEnabled enables/disables Cadence legacy contracts upgrades + LegacyContractUpgradeEnabled bool // StartBlockHeight is the height at which to start the emulator. StartBlockHeight uint64 } @@ -407,6 +409,13 @@ func configureBlockchain(logger *zerolog.Logger, conf *Config, store storage.Sto ) } + if conf.LegacyContractUpgradeEnabled { + options = append( + options, + emulator.WithLegacyUpgradeEnabled(), + ) + } + emulatedBlockchain, err := emulator.New(options...) if err != nil { return nil, err diff --git a/server/server_test.go b/server/server_test.go index d9d1d54b..b9404bd4 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -24,6 +24,7 @@ import ( "os" "testing" + "github.com/onflow/flow-emulator/emulator" "github.com/rs/zerolog" "github.com/stretchr/testify/require" ) @@ -79,6 +80,22 @@ func TestPersistenceWithSnapshotFlag(t *testing.T) { require.True(t, os.IsNotExist(err), "DB should not exist") } +func TestLegacyUpgradeFlag(t *testing.T) { + logger := zerolog.Nop() + + conf := &Config{LegacyContractUpgradeEnabled: true} + server := NewEmulatorServer(&logger, conf) + defer server.Stop() + + require.NotNil(t, server) + require.True(t, server.config.LegacyContractUpgradeEnabled) + + e := server.Emulator() + + require.IsType(t, &emulator.Blockchain{}, e) + require.True(t, e.(*emulator.Blockchain).Runtime().Config().LegacyContractUpgradeEnabled) +} + func TestExecuteScript(t *testing.T) { logger := zerolog.Nop()