Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emulator Feature: Bootstrap with default contracts #88

Merged
merged 14 commits into from
Dec 8, 2021
Merged
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ and if you plan to run the emulator with Docker you must use the environment var
| `--verbose`, `-v` | `FLOW_VERBOSE` | `false` | Enable verbose logging (useful for debugging) |
| `--log-format` | `FLOW_LOGFORMAT` | `text` | Output log format (valid values `text`, `JSON`) |
| `--block-time`, `-b` | `FLOW_BLOCKTIME` | `0` | Time between sealed blocks. Valid units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h` |
| `--include-helpful-contracts` | `FLOW_INCLUDEHELPFULCONTRACTS` | `false` | Deploy some helpful contracts like [NFT](https://github.com/onflow/flow-nft/blob/master/contracts/NonFungibleToken.cdc) and an [NFT Marketplace](https://github.com/onflow/nft-storefront), when the emulator starts |
| `--service-priv-key` | `FLOW_SERVICEPRIVATEKEY` | random | Private key used for the [service account](https://docs.onflow.org/flow-token/concepts/#flow-service-account) |
| `--service-pub-key` | `FLOW_SERVICEPUBLICKEY` | random | Public key used for the [service account](https://docs.onflow.org/flow-token/concepts/#flow-service-account) |
| `--service-sig-algo` | `FLOW_SERVICEKEYSIGALGO` | `ECDSA_P256` | Service account key [signature algorithm](https://docs.onflow.org/cadence/language/crypto/#signing-algorithms) |
Expand Down
47 changes: 25 additions & 22 deletions cmd/emulator/start/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,28 +38,29 @@ import (
)

type Config struct {
Port int `default:"3569" flag:"port,p" info:"port to run RPC server"`
HTTPPort int `default:"8080" flag:"http-port" info:"port to run HTTP server"`
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 `flag:"service-sig-algo" info:"service account key signature algorithm"`
ServiceKeyHashAlgo string `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"`
Persist bool `default:"false" flag:"persist" info:"enable persistent storage"`
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"`
Port int `default:"3569" flag:"port,p" info:"port to run RPC server"`
HTTPPort int `default:"8080" flag:"http-port" info:"port to run HTTP server"`
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"`
Persist bool `default:"false" flag:"persist" info:"enable persistent storage"`
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"`
IncludeHelpfulContracts bool `default:"false" flag:"include-helpful-contracts" info:"deploy some helpful NFT contracts when emulator starts"`
}

const EnvPrefix = "FLOW"
Expand Down Expand Up @@ -153,6 +154,7 @@ func Cmd(getServiceKey serviceKeyFunc) *cobra.Command {
HTTPHeaders: nil,
BlockTime: conf.BlockTime,
ServicePublicKey: servicePublicKey,
ServicePrivateKey: servicePrivateKey,
ServiceKeySigAlgo: serviceKeySigAlgo,
ServiceKeyHashAlgo: serviceKeyHashAlgo,
Persist: conf.Persist,
Expand All @@ -165,6 +167,7 @@ func Cmd(getServiceKey serviceKeyFunc) *cobra.Command {
StorageMBPerFLOW: storageMBPerFLOW,
MinimumStorageReservation: minimumStorageReservation,
TransactionFeesEnabled: conf.TransactionFeesEnabled,
IncludeHelpfulContracts: conf.IncludeHelpfulContracts,
}

emu := server.NewEmulatorServer(logger, serverConf)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/onflow/flow-go v0.21.3
github.com/onflow/flow-go-sdk v0.21.0
github.com/onflow/flow-go/crypto v0.18.0
github.com/onflow/flow-nft/lib/go/contracts v0.0.0-20210915191154-12ee8c507a0e // indirect
github.com/onflow/flow/protobuf/go/flow v0.2.2
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.10.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,8 @@ github.com/onflow/flow-go-sdk v0.21.0/go.mod h1:2xhtzwRAeItwbHQzHiIK2gPgLDw1hNPa
github.com/onflow/flow-go/crypto v0.12.0/go.mod h1:oXuvU0Dr4lHKgye6nHEFbBXIWNv+dBQUzoVW5Go38+o=
github.com/onflow/flow-go/crypto v0.18.0 h1:2tucSdxmld/08LfIfLxGfw+8QuSNFiHAUojX5AFvq6k=
github.com/onflow/flow-go/crypto v0.18.0/go.mod h1:3Ah843iPyjIL+7nH9EillV3OWNptrL+DrQUbVKgg2E4=
github.com/onflow/flow-nft/lib/go/contracts v0.0.0-20210915191154-12ee8c507a0e h1:svZJ1NydwvNGfkJfJXIeECcKecnYC1CtC/BMzdfEI0U=
github.com/onflow/flow-nft/lib/go/contracts v0.0.0-20210915191154-12ee8c507a0e/go.mod h1:epgW8P53PDpHaqBQCmMgJqdet4h7ONaoIL3kVD/nnzU=
github.com/onflow/flow/protobuf/go/flow v0.1.9/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
github.com/onflow/flow/protobuf/go/flow v0.2.0/go.mod h1:kRugbzZjwQqvevJhrnnCFMJZNmoSJmxlKt6hTGXZojM=
github.com/onflow/flow/protobuf/go/flow v0.2.2 h1:EVhA0w3lu+BG7RK39ojIJVghLH998iP7YC0V/Op0KnU=
Expand Down
65 changes: 65 additions & 0 deletions server/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package server

import (
"fmt"
"io/ioutil"
"path/filepath"
"regexp"

emulator "github.com/onflow/flow-emulator"
"github.com/onflow/flow-go-sdk"
"github.com/onflow/flow-go-sdk/templates"
"github.com/onflow/flow-go/fvm"
"github.com/onflow/flow-nft/lib/go/contracts"
)

var baseContractsPath = "./server/contracts/"

func deployContracts(conf *Config, b *emulator.Blockchain) map[string]flow.Address {
nftAddress, _ := deployContract("NonFungibleToken", contracts.NonFungibleToken(), b)
exampleNFT, _ := deployContract("ExampleNFT", contracts.ExampleNFT(nftAddress.Hex()), b)

nftStorefrontContract := loadContract("NFTStorefront.cdc", map[string]flow.Address{
"FungibleToken": flow.HexToAddress(fvm.FlowTokenAddress(b.GetChain()).Hex()),
"NonFungibleToken": nftAddress,
})

nftStorefront, _ := deployContract("NFTStorefront", nftStorefrontContract, b)

addresses := map[string]flow.Address{
"NonFungibleToken": nftAddress,
"ExampleNFT": exampleNFT,
"NFTStorefront": nftStorefront,
sideninja marked this conversation as resolved.
Show resolved Hide resolved
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use emulator instance SendTransaction function here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh!!! will give that a try -- right now i'm just using the raw blockchain methods :P

image

Copy link
Contributor

@sideninja sideninja Nov 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah you are right sorry, no method like that on the blockchain. Maybe anyhow extract to some helper.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, okay! I think this'll do it (now it actually checks the service account before printing, to make sure!)

image

return addresses
}

func loadContract(name string, replacements map[string]flow.Address) []byte {
code := string(readFile(filepath.Join(baseContractsPath, name)))
for name, realAddress := range replacements {
placeholder := regexp.MustCompile(fmt.Sprintf(`"[^"\s].*/%s.cdc"`, name))
code = placeholder.ReplaceAllString(code, fmt.Sprintf("0x%s", realAddress.String()))
}
return []byte(code)
}

func deployContract(name string, contract []byte, b *emulator.Blockchain) (flow.Address, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the intention to have contracts deployed to different accounts, else you can deploy all at once.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, interesting!! How do you deploy all at once?

Haha I thought it was all right to have different accounts for each one, but they could also go ito the same account... the bigger thing will probably be.. how do you control the accounts once you've deployed the contracts?

  • actually it might be a good idea to make them all deploy from the service acct, since that's what you have the keys for, right?

Copy link
Contributor

@sideninja sideninja Nov 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think that it would be better to deploy on the service account not just because of the keys but also there are for sure a lot of tests/projects, unfortunately, depending on the sequence of addresses used for testing so if you will "use" those addresses to do these deployments we might break something for them. You can deploy them using a single transaction as contracts can be passed as an array, see here: https://pkg.go.dev/github.com/onflow/flow-go-sdk/templates#CreateAccount

So maybe better change this and then we should be all good.

return b.CreateAccount(
nil,
[]templates.Contract{
{
Name: name,
Source: string(contract),
},
},
)
}

func readFile(path string) []byte {
contents, err := ioutil.ReadFile(path)
if err != nil {
panic(err)
}
return contents
}
Loading