From 875ce1ae974a75144bd04d12e263cd0cc517e6f8 Mon Sep 17 00:00:00 2001 From: ptrus Date: Thu, 29 Apr 2021 11:14:10 +0200 Subject: [PATCH 1/2] go/cmd/registry: simplify register runtime command Update gen_register runtime command to accept runtime descriptor path instead of messing with the arguments. --- .changelog/3903.breaking.md | 8 + go/oasis-node/cmd/registry/runtime/runtime.go | 525 +----------------- go/oasis-test-runner/oasis/cli/registry.go | 182 +----- go/oasis-test-runner/oasis/runtime.go | 69 ++- .../scenario/e2e/registry_cli.go | 7 +- .../scenario/e2e/runtime/history_reindex.go | 3 +- .../e2e/runtime/keymanager_upgrade.go | 5 +- .../scenario/e2e/runtime/runtime_dynamic.go | 5 +- .../scenario/e2e/runtime/runtime_upgrade.go | 3 +- 9 files changed, 103 insertions(+), 704 deletions(-) create mode 100644 .changelog/3903.breaking.md diff --git a/.changelog/3903.breaking.md b/.changelog/3903.breaking.md new file mode 100644 index 00000000000..ea02a1f033b --- /dev/null +++ b/.changelog/3903.breaking.md @@ -0,0 +1,8 @@ +go/cmd/registry: simplify register runtime command + +The `registry runtime gen_register` command now accepts a JSON runtime +descriptor, which should simplify generating runtime registration +transactions. +The `registy runtime init_genesis` command is removed as runtime +descriptors in genesis are not singed since version 21.0+ making the command +obsolete. diff --git a/go/oasis-node/cmd/registry/runtime/runtime.go b/go/oasis-node/cmd/registry/runtime/runtime.go index 26003d03bcf..d4a7d25cbf6 100644 --- a/go/oasis-node/cmd/registry/runtime/runtime.go +++ b/go/oasis-node/cmd/registry/runtime/runtime.go @@ -7,24 +7,13 @@ import ( "fmt" "io/ioutil" "os" - "path/filepath" - "strings" - "time" "github.com/spf13/cobra" flag "github.com/spf13/pflag" "github.com/spf13/viper" "google.golang.org/grpc" - "github.com/oasisprotocol/oasis-core/go/common" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" - "github.com/oasisprotocol/oasis-core/go/common/crypto/signature" "github.com/oasisprotocol/oasis-core/go/common/logging" - "github.com/oasisprotocol/oasis-core/go/common/node" - "github.com/oasisprotocol/oasis-core/go/common/quantity" - "github.com/oasisprotocol/oasis-core/go/common/sgx" - "github.com/oasisprotocol/oasis-core/go/common/version" consensus "github.com/oasisprotocol/oasis-core/go/consensus/api" cmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common" cmdConsensus "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/consensus" @@ -33,84 +22,17 @@ import ( cmdGrpc "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/grpc" cmdSigner "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/signer" registry "github.com/oasisprotocol/oasis-core/go/registry/api" - scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api" - staking "github.com/oasisprotocol/oasis-core/go/staking/api" - storage "github.com/oasisprotocol/oasis-core/go/storage/api" - "github.com/oasisprotocol/oasis-core/go/storage/mkvs" ) const ( - CfgID = "runtime.id" - CfgTEEHardware = "runtime.tee_hardware" - CfgGenesisState = "runtime.genesis.state" - CfgGenesisRound = "runtime.genesis.round" - CfgKind = "runtime.kind" - CfgKeyManager = "runtime.keymanager" - cfgOutput = "runtime.genesis.file" - CfgVersion = "runtime.version" - CfgVersionEnclave = "runtime.version.enclave" - CfgGovernanceModel = "runtime.governance_model" - - // Executor committee flags. - CfgExecutorGroupSize = "runtime.executor.group_size" - CfgExecutorGroupBackupSize = "runtime.executor.group_backup_size" - CfgExecutorAllowedStragglers = "runtime.executor.allowed_stragglers" - CfgExecutorRoundTimeout = "runtime.executor.round_timeout" - CfgExecutorMaxMessages = "runtime.executor.max_messages" - - // Storage committee flags. - CfgStorageGroupSize = "runtime.storage.group_size" - CfgStorageMinWriteReplication = "runtime.storage.min_write_replication" - CfgStorageMaxApplyWriteLogEntries = "runtime.storage.max_apply_write_log_entries" - CfgStorageMaxApplyOps = "runtime.storage.max_apply_ops" - CfgStorageCheckpointInterval = "runtime.storage.checkpoint_interval" - CfgStorageCheckpointNumKept = "runtime.storage.checkpoint_num_kept" - CfgStorageCheckpointChunkSize = "runtime.storage.checkpoint_chunk_size" - - // Transaction scheduler flags. - CfgTxnSchedulerAlgorithm = "runtime.txn_scheduler.algorithm" - CfgTxnSchedulerBatchFlushTimeout = "runtime.txn_scheduler.flush_timeout" - CfgTxnSchedulerMaxBatchSize = "runtime.txn_scheduler.max_batch_size" - CfgTxnSchedulerMaxBatchSizeBytes = "runtime.txn_scheduler.max_batch_size_bytes" - CfgTxnSchedulerProposerTimeout = "runtime.txn_scheduler.proposer_timeout" - - // Admission policy flags. - CfgAdmissionPolicy = "runtime.admission_policy" - CfgAdmissionPolicyEntityWhitelist = "runtime.admission_policy_entity_whitelist" - AdmissionPolicyNameAnyNode = "any-node" - AdmissionPolicyNameEntityWhitelist = "entity-whitelist" - - // Constraints flags. - cfgConstraints = "runtime.constraints" - cfgConstraintsMinPoolSize = "min_pool_size" - cfgConstraintsValidatorSet = "validator_set" - cfgConstraintsMaxNodes = "max_nodes" - - CfgConstraintsExecutorWorkerMinPoolSize = "runtime.constraints.executor.worker.min_pool_size" - CfgConstraintsExecutorWorkerValidatorSet = "runtime.constraints.executor.worker.validator_set" - CfgConstraintsExecutorWorkerMaxNodes = "runtime.constraints.executor.worker.max_nodes" - - CfgConstraintsExecutorBackupWorkerMinPoolSize = "runtime.constraints.executor.backup-worker.min_pool_size" - CfgConstraintsExecutorBackupWorkerValidatorSet = "runtime.constraints.executor.backup-worker.validator_set" - CfgConstraintsExecutorBackupWorkerMaxNodes = "runtime.constraints.executor.backup-worker.max_nodes" - - CfgConstraintsStorageWorkerMinPoolSize = "runtime.constraints.storage.worker.min_pool_size" - CfgConstraintsStorageWorkerValidatorSet = "runtime.constraints.storage.worker.validator_set" - CfgConstraintsStorageWorkerMaxNodes = "runtime.constraints.storage.worker.max_nodes" - - // Staking parameters flags. - CfgStakingThreshold = "runtime.staking.threshold" - CfgStakingSlashing = "runtime.staking.slashing" + // CfgRuntimeDescriptor is the flag to specify the path to runtime descriptor. + CfgRuntimeDescriptor = "runtime.descriptor" // List runtimes flags. CfgIncludeSuspended = "include_suspended" - - runtimeGenesisFilename = "runtime_genesis.json" ) var ( - outputFlags = flag.NewFlagSet("", flag.ContinueOnError) - runtimeFlags = flag.NewFlagSet("", flag.ContinueOnError) runtimeListFlags = flag.NewFlagSet("", flag.ContinueOnError) registerFlags = flag.NewFlagSet("", flag.ContinueOnError) @@ -119,12 +41,6 @@ var ( Short: "runtime registry backend utilities", } - initGenesisCmd = &cobra.Command{ - Use: "init_genesis", - Short: "initialize a runtime for genesis", - Run: doInitGenesis, - } - registerCmd = &cobra.Command{ Use: "gen_register", Short: "generate a register runtime transaction", @@ -138,20 +54,6 @@ var ( } logger = logging.GetLogger("cmd/registry/runtime") - - cfgConstraintsAll = []string{ - CfgConstraintsExecutorWorkerMinPoolSize, - CfgConstraintsExecutorWorkerValidatorSet, - CfgConstraintsExecutorWorkerMaxNodes, - - CfgConstraintsExecutorBackupWorkerMinPoolSize, - CfgConstraintsExecutorBackupWorkerValidatorSet, - CfgConstraintsExecutorBackupWorkerMaxNodes, - - CfgConstraintsStorageWorkerMinPoolSize, - CfgConstraintsStorageWorkerValidatorSet, - CfgConstraintsStorageWorkerMaxNodes, - } ) func doConnect(cmd *cobra.Command) (*grpc.ClientConn, registry.Backend) { @@ -167,59 +69,45 @@ func doConnect(cmd *cobra.Command) (*grpc.ClientConn, registry.Backend) { return conn, client } -func doInitGenesis(cmd *cobra.Command, args []string) { +func doGenRegister(cmd *cobra.Command, args []string) { if err := cmdCommon.Init(); err != nil { cmdCommon.EarlyLogAndExit(err) } - dataDir, err := cmdCommon.DataDirOrPwd() + genesis := cmdConsensus.InitGenesis() + cmdConsensus.AssertTxFileOK() + + fileBytes, err := ioutil.ReadFile(viper.GetString(CfgRuntimeDescriptor)) if err != nil { - logger.Error("failed to query data directory", + logger.Error("failed to read runtime descriptor", "err", err, ) os.Exit(1) } - rt, err := runtimeFromFlags() - if err != nil { - logger.Error("failed to parse runtime from flags", + var rt registry.Runtime + if err = json.Unmarshal(fileBytes, &rt); err != nil { + logger.Error("can't parse runtime descriptor", "err", err, ) os.Exit(1) } - // Write out the runtime registration. - b, _ := json.Marshal(rt) - if err = ioutil.WriteFile(filepath.Join(dataDir, viper.GetString(cfgOutput)), b, 0o600); err != nil { - logger.Error("failed to write runtime genesis registration", + if err = rt.ValidateBasic(true); err != nil { + logger.Error("runtime descriptor is not valid", "err", err, ) os.Exit(1) } - - logger.Info("generated runtime", - "runtime", rt.ID, - ) -} - -func doGenRegister(cmd *cobra.Command, args []string) { - if err := cmdCommon.Init(); err != nil { - cmdCommon.EarlyLogAndExit(err) - } - - genesis := cmdConsensus.InitGenesis() - cmdConsensus.AssertTxFileOK() - - rt, err := runtimeFromFlags() - if err != nil { - logger.Info("failed to parse runtime from flags", + if err = rt.Genesis.SanityCheck(false); err != nil { + logger.Error("runtime descriptor genesis sanity check failure", "err", err, ) os.Exit(1) } nonce, fee := cmdConsensus.GetTxNonceAndFee() - tx := registry.NewRegisterRuntimeTx(nonce, fee, rt) + tx := registry.NewRegisterRuntimeTx(nonce, fee, &rt) cmdConsensus.SignAndSaveTx(cmdContext.GetCtxWithGenesisInfo(genesis), tx, nil) } @@ -258,319 +146,9 @@ func doList(cmd *cobra.Command, args []string) { } } -func runtimeFromFlags() (*registry.Runtime, error) { // nolint: gocyclo - var id common.Namespace - if err := id.UnmarshalHex(viper.GetString(CfgID)); err != nil { - logger.Error("failed to parse runtime ID", - "err", err, - ) - return nil, err - } - - var teeHardware node.TEEHardware - s := viper.GetString(CfgTEEHardware) - if err := teeHardware.FromString(s); err != nil { - logger.Error("invalid TEE hardware", - CfgTEEHardware, s, - ) - return nil, fmt.Errorf("invalid TEE hardware") - } - - _, signer, err := cmdCommon.LoadEntitySigner() - if err != nil { - logger.Error("failed to load owning entity's signer", - "err", err, - ) - return nil, err - } - - var ( - kmID *common.Namespace - kind registry.RuntimeKind - ) - s = viper.GetString(CfgKind) - if err = kind.FromString(s); err != nil { - logger.Error("invalid runtime kind", - CfgKind, s, - ) - return nil, fmt.Errorf("invalid runtime kind") - } - switch kind { - case registry.KindCompute: - if viper.GetString(CfgKeyManager) != "" { - var tmpKmID common.Namespace - if err = tmpKmID.UnmarshalHex(viper.GetString(CfgKeyManager)); err != nil { - logger.Error("failed to parse key manager ID", - "err", err, - ) - return nil, err - } - kmID = &tmpKmID - } - if id.IsKeyManager() { - logger.Error("runtime ID has the key manager flag set", - "id", id, - ) - return nil, fmt.Errorf("invalid runtime flags") - } - case registry.KindKeyManager: - // Key managers don't have their own key manager. - if !id.IsKeyManager() { - logger.Error("runtime ID does not have the key manager flag set", - "id", id, - ) - return nil, fmt.Errorf("invalid runtime flags") - } - case registry.KindInvalid: - return nil, fmt.Errorf("cannot create runtime with invalid kind") - } - - // TODO: Support root upload when registering. - gen := registry.RuntimeGenesis{} - gen.Round = viper.GetUint64(CfgGenesisRound) - switch state := viper.GetString(CfgGenesisState); state { - case "": - gen.StateRoot.Empty() - default: - var b []byte - b, err = ioutil.ReadFile(state) - if err != nil { - logger.Error("failed to load runtime genesis storage state", - "err", err, - "filename", state, - ) - return nil, err - } - - var log storage.WriteLog - if err = json.Unmarshal(b, &log); err != nil { - logger.Error("failed to parse runtime genesis storage state", - "err", err, - "filename", state, - ) - return nil, err - } - - // Use in-memory MKVS tree to calculate the new root. - tree := mkvs.New(nil, nil, storage.RootTypeState) - ctx := context.Background() - for _, logEntry := range log { - err = tree.Insert(ctx, logEntry.Key, logEntry.Value) - if err != nil { - logger.Error("failed to apply runtime genesis storage state", - "err", err, - "filename", state, - ) - return nil, err - } - } - - var newRoot hash.Hash - _, newRoot, err = tree.Commit(ctx, id, gen.Round) - if err != nil { - logger.Error("failed to apply runtime genesis storage state", - "err", err, - "filename", state, - ) - return nil, err - } - - gen.StateRoot = newRoot - gen.State = log - } - - var govModel registry.RuntimeGovernanceModel - if err = govModel.UnmarshalText([]byte(strings.ToLower(viper.GetString(CfgGovernanceModel)))); err != nil { - logger.Error("invalid runtime governance model specified") - return nil, err - } - - rt := ®istry.Runtime{ - Versioned: cbor.NewVersioned(registry.LatestRuntimeDescriptorVersion), - ID: id, - EntityID: signer.Public(), - Genesis: gen, - Kind: kind, - TEEHardware: teeHardware, - Version: registry.VersionInfo{ - Version: version.FromU64(viper.GetUint64(CfgVersion)), - }, - KeyManager: kmID, - Executor: registry.ExecutorParameters{ - GroupSize: uint16(viper.GetUint64(CfgExecutorGroupSize)), - GroupBackupSize: uint16(viper.GetUint64(CfgExecutorGroupBackupSize)), - AllowedStragglers: uint16(viper.GetUint64(CfgExecutorAllowedStragglers)), - RoundTimeout: viper.GetInt64(CfgExecutorRoundTimeout), - MaxMessages: viper.GetUint32(CfgExecutorMaxMessages), - }, - TxnScheduler: registry.TxnSchedulerParameters{ - Algorithm: viper.GetString(CfgTxnSchedulerAlgorithm), - BatchFlushTimeout: viper.GetDuration(CfgTxnSchedulerBatchFlushTimeout), - MaxBatchSize: viper.GetUint64(CfgTxnSchedulerMaxBatchSize), - MaxBatchSizeBytes: uint64(viper.GetSizeInBytes(CfgTxnSchedulerMaxBatchSizeBytes)), - ProposerTimeout: viper.GetInt64(CfgTxnSchedulerProposerTimeout), - }, - Storage: registry.StorageParameters{ - GroupSize: uint16(viper.GetUint64(CfgStorageGroupSize)), - MinWriteReplication: uint16(viper.GetUint64(CfgStorageMinWriteReplication)), - MaxApplyWriteLogEntries: viper.GetUint64(CfgStorageMaxApplyWriteLogEntries), - MaxApplyOps: viper.GetUint64(CfgStorageMaxApplyOps), - CheckpointInterval: viper.GetUint64(CfgStorageCheckpointInterval), - CheckpointNumKept: viper.GetUint64(CfgStorageCheckpointNumKept), - CheckpointChunkSize: uint64(viper.GetSizeInBytes(CfgStorageCheckpointChunkSize)), - }, - GovernanceModel: govModel, - } - if teeHardware == node.TEEHardwareIntelSGX { - var cs sgx.Constraints - for _, v := range viper.GetStringSlice(CfgVersionEnclave) { - var enclaveID sgx.EnclaveIdentity - if err = enclaveID.UnmarshalHex(v); err != nil { - logger.Error("failed to parse SGX enclave identity", - "err", err, - ) - return nil, err - } - cs.Enclaves = append(cs.Enclaves, enclaveID) - } - rt.Version.TEE = cbor.Marshal(cs) - } - switch sap := viper.GetString(CfgAdmissionPolicy); sap { - case AdmissionPolicyNameAnyNode: - rt.AdmissionPolicy.AnyNode = ®istry.AnyNodeRuntimeAdmissionPolicy{} - case AdmissionPolicyNameEntityWhitelist: - entities := make(map[signature.PublicKey]registry.EntityWhitelistConfig) - for _, se := range viper.GetStringSlice(CfgAdmissionPolicyEntityWhitelist) { - var e signature.PublicKey - if err = e.UnmarshalText([]byte(se)); err != nil { - logger.Error("failed to parse entity ID", - "err", err, - CfgAdmissionPolicyEntityWhitelist, se, - ) - return nil, fmt.Errorf("entity whitelist runtime admission policy parse entity ID: %w", err) - } - entities[e] = registry.EntityWhitelistConfig{ - MaxNodes: make(map[node.RolesMask]uint16), - } - // TODO: Handle the clusterfuck of parsing the nested map from - // command-line arguments sometime later. As it is now, it's - // configured as unlimited nodes of any role for the given entity. - } - rt.AdmissionPolicy.EntityWhitelist = ®istry.EntityWhitelistRuntimeAdmissionPolicy{ - Entities: entities, - } - default: - logger.Error("invalid runtime admission policy", - CfgAdmissionPolicy, sap, - ) - return nil, fmt.Errorf("invalid runtime admission policy") - } - - // Constraints. - // TODO: Hack needed because viper's Sub doesn't work correctly. - var haveConstraints bool - cc := viper.New() - for _, key := range cfgConstraintsAll { - if viper.IsSet(key) { - cc.Set(key[len(cfgConstraints)+1:], viper.Get(key)) - haveConstraints = true - } - } - - if haveConstraints { - rt.Constraints = make(map[scheduler.CommitteeKind]map[scheduler.Role]registry.SchedulingConstraints) - - for _, kind := range []scheduler.CommitteeKind{scheduler.KindComputeExecutor, scheduler.KindStorage} { - kindText, _ := kind.MarshalText() - ce := cc.Sub(string(kindText)) - if ce == nil { - continue - } - - cs := make(map[scheduler.Role]registry.SchedulingConstraints) - rt.Constraints[kind] = cs - - for _, role := range []scheduler.Role{scheduler.RoleWorker, scheduler.RoleBackupWorker} { - roleText, _ := role.MarshalText() - cw := ce.Sub(string(roleText)) - if cw == nil { - continue - } - - var sc registry.SchedulingConstraints - if minPoolSize := cw.GetUint64(cfgConstraintsMinPoolSize); minPoolSize > 0 { - sc.MinPoolSize = ®istry.MinPoolSizeConstraint{ - Limit: uint16(minPoolSize), - } - } - if validatorSet := cw.GetBool(cfgConstraintsValidatorSet); validatorSet { - sc.ValidatorSet = ®istry.ValidatorSetConstraint{} - } - if maxNodes := cw.GetUint64(cfgConstraintsMaxNodes); maxNodes > 0 { - sc.MaxNodes = ®istry.MaxNodesConstraint{ - Limit: uint16(maxNodes), - } - } - cs[role] = sc - } - } - } - - // Staking parameters. - if th := viper.GetStringMapString(CfgStakingThreshold); th != nil { - rt.Staking.Thresholds = make(map[staking.ThresholdKind]quantity.Quantity) - for kindRaw, valueRaw := range th { - var ( - kind staking.ThresholdKind - value quantity.Quantity - ) - - if err = kind.UnmarshalText([]byte(kindRaw)); err != nil { - return nil, fmt.Errorf("staking: bad threshold kind (%s): %w", kindRaw, err) - } - if err = value.UnmarshalText([]byte(valueRaw)); err != nil { - return nil, fmt.Errorf("staking: bad threshold value (%s): %w", valueRaw, err) - } - - if _, ok := rt.Staking.Thresholds[kind]; ok { - return nil, fmt.Errorf("staking: duplicate value for threshold '%s'", kind) - } - rt.Staking.Thresholds[kind] = value - } - } - if sl := viper.GetStringMapString(CfgStakingSlashing); sl != nil { - rt.Staking.Slashing = make(map[staking.SlashReason]staking.Slash) - for reasonRaw, valueRaw := range sl { - var ( - reason staking.SlashReason - value quantity.Quantity - ) - - if err = reason.UnmarshalText([]byte(reasonRaw)); err != nil { - return nil, fmt.Errorf("staking: bad slash raeson (%s): %w", reasonRaw, err) - } - if err = value.UnmarshalText([]byte(valueRaw)); err != nil { - return nil, fmt.Errorf("staking: bad slash value (%s): %w", valueRaw, err) - } - - if _, ok := rt.Staking.Slashing[reason]; ok { - return nil, fmt.Errorf("staking: duplicate value for reason '%s'", reason) - } - rt.Staking.Slashing[reason] = staking.Slash{Amount: value} - } - } - // Validate descriptor. - if err = rt.ValidateBasic(true); err != nil { - return nil, fmt.Errorf("invalid runtime descriptor: %w", err) - } - - return rt, nil -} - // Register registers the runtime sub-command and all of it's children. func Register(parentCmd *cobra.Command) { for _, v := range []*cobra.Command{ - initGenesisCmd, registerCmd, listCmd, } { @@ -578,88 +156,25 @@ func Register(parentCmd *cobra.Command) { } for _, v := range []*cobra.Command{ - initGenesisCmd, registerCmd, } { v.Flags().AddFlagSet(cmdFlags.DebugTestEntityFlags) } - initGenesisCmd.Flags().AddFlagSet(runtimeFlags) - initGenesisCmd.Flags().AddFlagSet(outputFlags) - listCmd.Flags().AddFlagSet(cmdGrpc.ClientFlags) listCmd.Flags().AddFlagSet(cmdFlags.VerboseFlags) listCmd.Flags().AddFlagSet(runtimeListFlags) registerCmd.Flags().AddFlagSet(registerFlags) - registerCmd.Flags().AddFlagSet(runtimeFlags) - parentCmd.AddCommand(runtimeCmd) } func init() { - outputFlags.String(cfgOutput, runtimeGenesisFilename, "File name of the document to be written under datadir") - _ = viper.BindPFlags(outputFlags) - - runtimeFlags.String(CfgID, "", "Runtime ID") - runtimeFlags.String(CfgTEEHardware, "invalid", "Type of TEE hardware. Supported values are \"invalid\" and \"intel-sgx\"") - runtimeFlags.String(CfgGenesisState, "", "Runtime state at genesis") - runtimeFlags.Uint64(CfgGenesisRound, 0, "Runtime round at genesis") - runtimeFlags.String(CfgKeyManager, "", "Key Manager Runtime ID") - runtimeFlags.String(CfgKind, "compute", "Kind of runtime. Supported values are \"compute\" and \"keymanager\"") - runtimeFlags.String(CfgVersion, "", "Runtime version. Value is 64-bit hex e.g. 0x0000000100020003 for 1.2.3") - runtimeFlags.StringSlice(CfgVersionEnclave, nil, "Runtime TEE enclave version(s)") - runtimeFlags.String(CfgGovernanceModel, "entity", "Runtime governance model (entity or runtime or consensus)") - - // Init Executor committee flags. - runtimeFlags.Uint16(CfgExecutorGroupSize, 1, "Number of workers in the runtime executor group/committee") - runtimeFlags.Uint16(CfgExecutorGroupBackupSize, 0, "Number of backup workers in the runtime executor group/committee") - runtimeFlags.Uint16(CfgExecutorAllowedStragglers, 0, "Number of stragglers allowed per round in the runtime executor group") - runtimeFlags.Int64(CfgExecutorRoundTimeout, 5, "Executor committee round timeout for this runtime (in consensus blocks)") - runtimeFlags.Uint32(CfgExecutorMaxMessages, 32, "Maximum number of runtime messages that can be emitted in a round") - - // Init Transaction scheduler flags. - runtimeFlags.String(CfgTxnSchedulerAlgorithm, registry.TxnSchedulerSimple, "Transaction scheduling algorithm") - runtimeFlags.Duration(CfgTxnSchedulerBatchFlushTimeout, 1*time.Second, "Maximum amount of time to wait for a scheduled batch") - runtimeFlags.Uint64(CfgTxnSchedulerMaxBatchSize, 1000, "Maximum size of a batch of runtime requests") - runtimeFlags.String(CfgTxnSchedulerMaxBatchSizeBytes, "16mb", "Maximum size (in bytes) of a batch of runtime requests") - runtimeFlags.Int64(CfgTxnSchedulerProposerTimeout, 5, "Timeout (in consensus blocks) before a round can be timeouted due to proposer not proposing") - - // Init Storage committee flags. - runtimeFlags.Uint16(CfgStorageGroupSize, 1, "Number of storage nodes for the runtime") - runtimeFlags.Uint16(CfgStorageMinWriteReplication, 1, "Minimum required storage write replication") - runtimeFlags.Uint64(CfgStorageMaxApplyWriteLogEntries, 100_000, "Maximum number of write log entries") - runtimeFlags.Uint64(CfgStorageMaxApplyOps, 2, "Maximum number of apply operations in a batch") - runtimeFlags.Uint64(CfgStorageCheckpointInterval, 10_000, "Storage checkpoint interval (in rounds)") - runtimeFlags.Uint64(CfgStorageCheckpointNumKept, 2, "Number of storage checkpoints to keep") - runtimeFlags.String(CfgStorageCheckpointChunkSize, "8mb", "Storage checkpoint chunk size") - - // Init Admission policy flags. - runtimeFlags.String(CfgAdmissionPolicy, "", "What type of node admission policy to have") - runtimeFlags.StringSlice(CfgAdmissionPolicyEntityWhitelist, nil, "For entity whitelist node admission policies, the IDs (hex) of the entities in the whitelist") - - // Init constraints flags. - runtimeFlags.Uint16(CfgConstraintsExecutorWorkerMinPoolSize, 1, "Minimum required candidate primary compute node pool size (should be >= GroupSize)") - runtimeFlags.Bool(CfgConstraintsExecutorWorkerValidatorSet, false, "Validator set membership constraint for primary compute nodes") - runtimeFlags.Uint16(CfgConstraintsExecutorWorkerMaxNodes, 0, "Maximum elected nodes per entity for primary compute nodes") - - runtimeFlags.Uint16(CfgConstraintsExecutorBackupWorkerMinPoolSize, 1, "Minimum required candidate backup compute node pool size (should be >= GroupBackupSize)") - runtimeFlags.Bool(CfgConstraintsExecutorBackupWorkerValidatorSet, false, "Validator set membership constraint for backup compute nodes") - runtimeFlags.Uint16(CfgConstraintsExecutorBackupWorkerMaxNodes, 0, "Maximum elected nodes per entity for backup compute nodes") - - runtimeFlags.Uint16(CfgConstraintsStorageWorkerMinPoolSize, 1, "Minimum required candidate storage node pool size (should be >= GroupSize)") - runtimeFlags.Bool(CfgConstraintsStorageWorkerValidatorSet, false, "Validator set membership constraint for storage nodes") - runtimeFlags.Uint16(CfgConstraintsStorageWorkerMaxNodes, 0, "Maximum elected nodes per entity for storage nodes") - - // Init Staking flags. - runtimeFlags.StringToString(CfgStakingThreshold, nil, "Additional staking threshold for this runtime (=)") - runtimeFlags.StringToString(CfgStakingSlashing, nil, "Staking slashing parameter for this runtime (=)") - - _ = viper.BindPFlags(runtimeFlags) - runtimeFlags.AddFlagSet(cmdSigner.Flags) - runtimeFlags.AddFlagSet(cmdSigner.CLIFlags) - + registerFlags.String(CfgRuntimeDescriptor, "", "Path to the runtime descriptor") + _ = viper.BindPFlags(registerFlags) + registerFlags.AddFlagSet(cmdSigner.Flags) + registerFlags.AddFlagSet(cmdSigner.CLIFlags) registerFlags.AddFlagSet(cmdFlags.DebugTestEntityFlags) registerFlags.AddFlagSet(cmdConsensus.TxFlags) registerFlags.AddFlagSet(cmdFlags.AssumeYesFlag) diff --git a/go/oasis-test-runner/oasis/cli/registry.go b/go/oasis-test-runner/oasis/cli/registry.go index 5b234050289..50aee25b4af 100644 --- a/go/oasis-test-runner/oasis/cli/registry.go +++ b/go/oasis-test-runner/oasis/cli/registry.go @@ -1,18 +1,17 @@ package cli import ( + "encoding/json" "fmt" + "io/ioutil" + "path/filepath" "strconv" - "github.com/oasisprotocol/oasis-core/go/common/cbor" - "github.com/oasisprotocol/oasis-core/go/common/node" - "github.com/oasisprotocol/oasis-core/go/common/sgx" cmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common" "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/consensus" "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common/flags" cmdRegRt "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/registry/runtime" registry "github.com/oasisprotocol/oasis-core/go/registry/api" - scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api" ) // RegistryHelpers contains the oasis-node registry CLI helpers. @@ -20,164 +19,37 @@ type RegistryHelpers struct { *helpersBase } -func (r *RegistryHelpers) runRegistryRuntimeSubcommand( - cmd string, +// GenerateRegisterRuntimeTx is a wrapper for "registry runtime gen_register" subcommand. +func (r *RegistryHelpers) GenerateRegisterRuntimeTx( + baseDir string, runtime registry.Runtime, - genesisStateFile string, - extraArgs ...string, + nonce uint64, + txPath string, ) error { - args := []string{ - "registry", "runtime", cmd, - "--" + cmdRegRt.CfgID, runtime.ID.String(), - "--" + cmdRegRt.CfgTEEHardware, runtime.TEEHardware.String(), - "--" + cmdRegRt.CfgKind, runtime.Kind.String(), - "--" + cmdRegRt.CfgVersion, runtime.Version.Version.String(), - "--" + cmdRegRt.CfgGovernanceModel, runtime.GovernanceModel.String(), - } - args = append(args, extraArgs...) - - switch runtime.TEEHardware { - case node.TEEHardwareInvalid: - case node.TEEHardwareIntelSGX: - var cs sgx.Constraints - if err := cbor.Unmarshal(runtime.Version.TEE, &cs); err != nil { - return fmt.Errorf("failed to unmarshal Intel SGX TEE version: %w", err) - } - - for _, e := range cs.Enclaves { - args = append(args, - "--"+cmdRegRt.CfgVersionEnclave, e.String(), - ) - } - default: - return fmt.Errorf("unsupported TEE hardware: %s", runtime.TEEHardware) - } - - if runtime.Kind == registry.KindCompute { - args = append(args, - "--"+cmdRegRt.CfgGenesisState, genesisStateFile, - "--"+cmdRegRt.CfgGenesisRound, strconv.FormatUint(runtime.Genesis.Round, 10), - "--"+cmdRegRt.CfgExecutorGroupSize, strconv.FormatUint(uint64(runtime.Executor.GroupSize), 10), - "--"+cmdRegRt.CfgExecutorGroupBackupSize, strconv.FormatUint(uint64(runtime.Executor.GroupBackupSize), 10), - "--"+cmdRegRt.CfgExecutorAllowedStragglers, strconv.FormatUint(uint64(runtime.Executor.AllowedStragglers), 10), - "--"+cmdRegRt.CfgExecutorRoundTimeout, strconv.FormatInt(runtime.Executor.RoundTimeout, 10), - "--"+cmdRegRt.CfgExecutorMaxMessages, strconv.FormatUint(uint64(runtime.Executor.MaxMessages), 10), - "--"+cmdRegRt.CfgStorageGroupSize, strconv.FormatUint(uint64(runtime.Storage.GroupSize), 10), - "--"+cmdRegRt.CfgStorageMinWriteReplication, strconv.FormatUint(uint64(runtime.Storage.MinWriteReplication), 10), - "--"+cmdRegRt.CfgStorageMaxApplyWriteLogEntries, strconv.FormatUint(runtime.Storage.MaxApplyWriteLogEntries, 10), - "--"+cmdRegRt.CfgStorageMaxApplyOps, strconv.FormatUint(runtime.Storage.MaxApplyOps, 10), - "--"+cmdRegRt.CfgStorageCheckpointInterval, strconv.FormatUint(runtime.Storage.CheckpointInterval, 10), - "--"+cmdRegRt.CfgStorageCheckpointNumKept, strconv.FormatUint(runtime.Storage.CheckpointNumKept, 10), - "--"+cmdRegRt.CfgStorageCheckpointChunkSize, strconv.FormatUint(runtime.Storage.CheckpointChunkSize, 10), - "--"+cmdRegRt.CfgTxnSchedulerAlgorithm, runtime.TxnScheduler.Algorithm, - "--"+cmdRegRt.CfgTxnSchedulerBatchFlushTimeout, runtime.TxnScheduler.BatchFlushTimeout.String(), - "--"+cmdRegRt.CfgTxnSchedulerMaxBatchSize, strconv.FormatUint(runtime.TxnScheduler.MaxBatchSize, 10), - "--"+cmdRegRt.CfgTxnSchedulerMaxBatchSizeBytes, strconv.FormatUint(runtime.TxnScheduler.MaxBatchSizeBytes, 10), - "--"+cmdRegRt.CfgTxnSchedulerProposerTimeout, strconv.FormatInt(runtime.TxnScheduler.ProposerTimeout, 10), - ) - - if runtime.Constraints != nil { - if cs := runtime.Constraints[scheduler.KindComputeExecutor]; cs != nil { - if mps := cs[scheduler.RoleWorker].MinPoolSize; mps != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorWorkerMinPoolSize, strconv.FormatUint(uint64(mps.Limit), 10)) - } - if vs := cs[scheduler.RoleWorker].ValidatorSet; vs != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorWorkerValidatorSet) - } - if mn := cs[scheduler.RoleWorker].MaxNodes; mn != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorWorkerMaxNodes, strconv.FormatUint(uint64(mn.Limit), 10)) - } - - if mps := cs[scheduler.RoleBackupWorker].MinPoolSize; mps != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorBackupWorkerMinPoolSize, strconv.FormatUint(uint64(mps.Limit), 10)) - } - if vs := cs[scheduler.RoleBackupWorker].ValidatorSet; vs != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorBackupWorkerValidatorSet) - } - if mn := cs[scheduler.RoleBackupWorker].MaxNodes; mn != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsExecutorBackupWorkerMaxNodes, strconv.FormatUint(uint64(mn.Limit), 10)) - } - } - if cs := runtime.Constraints[scheduler.KindStorage]; cs != nil { - if mps := cs[scheduler.RoleWorker].MinPoolSize; mps != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsStorageWorkerMinPoolSize, strconv.FormatUint(uint64(mps.Limit), 10)) - } - if vs := cs[scheduler.RoleWorker].ValidatorSet; vs != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsStorageWorkerValidatorSet) - } - if mn := cs[scheduler.RoleWorker].MaxNodes; mn != nil { - args = append(args, "--"+cmdRegRt.CfgConstraintsStorageWorkerMaxNodes, strconv.FormatUint(uint64(mn.Limit), 10)) - } - } - } - } - if runtime.KeyManager != nil { - args = append(args, "--"+cmdRegRt.CfgKeyManager, runtime.KeyManager.String()) - } - - if runtime.AdmissionPolicy.AnyNode != nil { - args = append(args, - "--"+cmdRegRt.CfgAdmissionPolicy, cmdRegRt.AdmissionPolicyNameAnyNode, - ) - } else if runtime.AdmissionPolicy.EntityWhitelist != nil { - args = append(args, - "--"+cmdRegRt.CfgAdmissionPolicy, cmdRegRt.AdmissionPolicyNameEntityWhitelist, - ) - for e := range runtime.AdmissionPolicy.EntityWhitelist.Entities { - args = append(args, - "--"+cmdRegRt.CfgAdmissionPolicyEntityWhitelist, e.String(), - ) - } - } else { - return fmt.Errorf("invalid admission policy") - } - - for kind, value := range runtime.Staking.Thresholds { - kindRaw, _ := kind.MarshalText() - valueRaw, _ := value.MarshalText() + r.logger.Info("generating register runtime tx") - args = append(args, - "--"+cmdRegRt.CfgStakingThreshold, fmt.Sprintf("%s=%s", string(kindRaw), string(valueRaw)), - ) + // Save runtime descriptor into a temp file. + rtDescPath := filepath.Join(baseDir, fmt.Sprintf("registry_runtime_register_descriptor-%s.json", runtime.ID)) + rtDescStr, _ := json.Marshal(runtime) + if err := ioutil.WriteFile(rtDescPath, rtDescStr, 0o600); err != nil { + return fmt.Errorf("failed to write runtime descriptor to file: %w", err) } - for reason, value := range runtime.Staking.Slashing { - reasonRaw, _ := reason.MarshalText() - valueRaw, _ := value.Amount.MarshalText() - args = append(args, - "--"+cmdRegRt.CfgStakingSlashing, fmt.Sprintf("%s=%s", string(reasonRaw), string(valueRaw)), - ) + args := []string{ + "registry", "runtime", "gen_register", + "--" + cmdRegRt.CfgRuntimeDescriptor, rtDescPath, + "--" + consensus.CfgTxNonce, strconv.FormatUint(nonce, 10), + "--" + consensus.CfgTxFile, txPath, + "--" + consensus.CfgTxFeeAmount, strconv.Itoa(0), // TODO: Make fee configurable. + "--" + consensus.CfgTxFeeGas, strconv.Itoa(10000), // TODO: Make fee configurable. + "--" + flags.CfgDebugDontBlameOasis, + "--" + cmdCommon.CfgDebugAllowTestKeys, + "--" + flags.CfgDebugTestEntity, + "--" + flags.CfgGenesisFile, r.cfg.GenesisFile, } - if out, err := r.runSubCommandWithOutput("registry-runtime-"+cmd, args); err != nil { - return fmt.Errorf("failed to run 'registry runtime %s': error: %w output: %s", cmd, err, out.String()) + if out, err := r.runSubCommandWithOutput("registry-runtime-gen_register", args); err != nil { + return fmt.Errorf("failed to run 'registry runtime gen_register': error: %w output: %s", err, out.String()) } - return nil } - -// GenerateRegisterRuntimeTx is a wrapper for "registry runtime gen_register" subcommand. -func (r *RegistryHelpers) GenerateRegisterRuntimeTx( - nonce uint64, - runtime registry.Runtime, - txPath, genesisStateFile string, -) error { - r.logger.Info("generating register runtime tx") - - // Generate a runtime register transaction file with debug test entity. - return r.runRegistryRuntimeSubcommand("gen_register", runtime, genesisStateFile, - "--"+consensus.CfgTxNonce, strconv.FormatUint(nonce, 10), - "--"+consensus.CfgTxFile, txPath, - "--"+consensus.CfgTxFeeAmount, strconv.Itoa(0), // TODO: Make fee configurable. - "--"+consensus.CfgTxFeeGas, strconv.Itoa(10000), // TODO: Make fee configurable. - "--"+flags.CfgDebugDontBlameOasis, - "--"+cmdCommon.CfgDebugAllowTestKeys, - "--"+flags.CfgDebugTestEntity, - "--"+flags.CfgGenesisFile, r.cfg.GenesisFile, - ) -} - -// InitGenesis is a wrapper for "registry runtime init_genesis" subcommand. -func (r *RegistryHelpers) InitGenesis(runtime registry.Runtime, genesisStateFile string, extraArgs ...string) error { - return r.runRegistryRuntimeSubcommand("init_genesis", runtime, genesisStateFile, extraArgs...) -} diff --git a/go/oasis-test-runner/oasis/runtime.go b/go/oasis-test-runner/oasis/runtime.go index 2d1cfd7e867..9f5ea7bce04 100644 --- a/go/oasis-test-runner/oasis/runtime.go +++ b/go/oasis-test-runner/oasis/runtime.go @@ -1,6 +1,7 @@ package oasis import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -10,19 +11,18 @@ import ( "github.com/oasisprotocol/oasis-core/go/common" "github.com/oasisprotocol/oasis-core/go/common/cbor" + "github.com/oasisprotocol/oasis-core/go/common/crypto/hash" "github.com/oasisprotocol/oasis-core/go/common/node" "github.com/oasisprotocol/oasis-core/go/common/sgx" - cmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common" "github.com/oasisprotocol/oasis-core/go/oasis-test-runner/env" - "github.com/oasisprotocol/oasis-core/go/oasis-test-runner/oasis/cli" registry "github.com/oasisprotocol/oasis-core/go/registry/api" scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api" storage "github.com/oasisprotocol/oasis-core/go/storage/api" + "github.com/oasisprotocol/oasis-core/go/storage/mkvs" ) const ( rtDescriptorFile = "runtime_genesis.json" - rtStateFile = "runtime_genesis_state.json" ) // Runtime is an Oasis runtime. @@ -117,11 +117,6 @@ func (rt *Runtime) ToRuntimeDescriptor() registry.Runtime { return rt.descriptor } -// GetGenesisStatePath returns the path to the runtime genesis state file (if any). -func (rt *Runtime) GetGenesisStatePath() string { - return rt.genesisState -} - // NewRuntime provisions a new runtime and adds it to the network. func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { descriptor := registry.Runtime{ @@ -138,6 +133,7 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { Staking: cfg.Staking, GovernanceModel: cfg.GovernanceModel, } + descriptor.Genesis.StateRoot.Empty() rtDir, err := net.baseDir.NewSubDir("runtime-" + cfg.ID.String()) if err != nil { @@ -151,19 +147,42 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { if cfg.GenesisState != nil && genesisStatePath != "" { return nil, fmt.Errorf("oasis/runtime: inline genesis state and file genesis state set") } - if cfg.GenesisState != nil || genesisStatePath != "" { + + log := cfg.GenesisState + + if genesisStatePath != "" { + var b []byte + b, err = ioutil.ReadFile(genesisStatePath) + if err != nil { + return nil, fmt.Errorf("failed to load runtime genesis storage state: %w", err) + } + + if err = json.Unmarshal(b, &log); err != nil { + return nil, fmt.Errorf("failed to parse runtime genesis storage state: %w", err) + } + } + if log != nil { descriptor.Genesis.Round = cfg.GenesisRound - if cfg.GenesisState != nil { - genesisStatePath = filepath.Join(rtDir.String(), rtStateFile) - var b []byte - if b, err = json.Marshal(cfg.GenesisState); err != nil { - return nil, fmt.Errorf("oasis/runtime: failed to serialize runtime genesis state: %w", err) - } - if err = ioutil.WriteFile(genesisStatePath, b, 0o600); err != nil { - return nil, fmt.Errorf("oasis/runtime: failed to write runtime genesis file: %w", err) + // Use in-memory MKVS tree to calculate the new root. + tree := mkvs.New(nil, nil, storage.RootTypeState) + ctx := context.Background() + for _, logEntry := range log { + err = tree.Insert(ctx, logEntry.Key, logEntry.Value) + if err != nil { + return nil, fmt.Errorf("failed to apply runtime genesis storage state: %w", err) } } + + var newRoot hash.Hash + _, newRoot, err = tree.Commit(ctx, descriptor.ID, descriptor.Genesis.Round) + if err != nil { + return nil, fmt.Errorf("failed to apply runtime genesis storage state: %w", err) + } + + descriptor.Genesis.StateRoot = newRoot + descriptor.Genesis.State = log } + var mrEnclaves []*sgx.MrEnclave if cfg.TEEHardware == node.TEEHardwareIntelSGX { enclaveIdentities := []sgx.EnclaveIdentity{} @@ -184,17 +203,11 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { *descriptor.KeyManager = cfg.Keymanager.id } - // Provision a runtime descriptor suitable for the genesis block. - cli := cli.New(net.env, net, net.logger) - extraArgs := append([]string{}, - "--"+cmdCommon.CfgDataDir, rtDir.String(), - ) - extraArgs = append(extraArgs, cfg.Entity.toGenesisArgs()...) - if err = cli.Registry.InitGenesis(descriptor, genesisStatePath, extraArgs...); err != nil { - net.logger.Error("failed to provision runtime", - "err", err, - ) - return nil, fmt.Errorf("oasis/runtime: failed to provision runtime: %w", err) + // Save runtime descriptor into file. + rtDescStr, _ := json.Marshal(descriptor) + path := filepath.Join(rtDir.String(), rtDescriptorFile) + if err := ioutil.WriteFile(path, rtDescStr, 0o600); err != nil { + return nil, fmt.Errorf("failed to write runtime descriptor to file: %w", err) } rt := &Runtime{ diff --git a/go/oasis-test-runner/scenario/e2e/registry_cli.go b/go/oasis-test-runner/scenario/e2e/registry_cli.go index 2ac405f67ee..e712b777554 100644 --- a/go/oasis-test-runner/scenario/e2e/registry_cli.go +++ b/go/oasis-test-runner/scenario/e2e/registry_cli.go @@ -681,12 +681,7 @@ func (sc *registryCLIImpl) testRuntime(ctx context.Context, childEnv *env.Env, c // Generate register runtime transaction. registerTxPath := filepath.Join(childEnv.Dir(), "registry_runtime_register.json") - genesisStatePath := filepath.Join(childEnv.Dir(), "registry_runtime_register_genesis_state.json") - genesisStateStr, _ := json.Marshal(testRuntime.Genesis.State) - if err = ioutil.WriteFile(genesisStatePath, genesisStateStr, 0o600); err != nil { - return err - } - if err = cli.Registry.GenerateRegisterRuntimeTx(0, testRuntime, registerTxPath, genesisStatePath); err != nil { + if err = cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), testRuntime, 0, registerTxPath); err != nil { return fmt.Errorf("failed to generate runtime register tx: %w", err) } diff --git a/go/oasis-test-runner/scenario/e2e/runtime/history_reindex.go b/go/oasis-test-runner/scenario/e2e/runtime/history_reindex.go index 93a928f7e91..0fa8d3b9c0b 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/history_reindex.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/history_reindex.go @@ -135,9 +135,8 @@ func (sc *historyReindexImpl) Run(childEnv *env.Env) error { // Register runtime. compRt := sc.Net.Runtimes()[rtIdx] - compRtDesc := compRt.ToRuntimeDescriptor() txPath := filepath.Join(childEnv.Dir(), "register_compute_runtime.json") - if err := cli.Registry.GenerateRegisterRuntimeTx(0, compRtDesc, txPath, compRt.GetGenesisStatePath()); err != nil { + if err := cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), compRt.ToRuntimeDescriptor(), 0, txPath); err != nil { return fmt.Errorf("failed to generate register compute runtime tx: %w", err) } if err := cli.Consensus.SubmitTx(txPath); err != nil { diff --git a/go/oasis-test-runner/scenario/e2e/runtime/keymanager_upgrade.go b/go/oasis-test-runner/scenario/e2e/runtime/keymanager_upgrade.go index a9680b18e6c..25089d19120 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/keymanager_upgrade.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/keymanager_upgrade.go @@ -254,9 +254,8 @@ func (sc *kmUpgradeImpl) Run(childEnv *env.Env) error { // Update runtime to include the new enclave identity. sc.Logger.Info("updating keymanager runtime descriptor") newRt := sc.Net.Runtimes()[2] - kmRtDesc := newRt.ToRuntimeDescriptor() - kmTxPath := filepath.Join(childEnv.Dir(), "register_update_km_runtime.json") - if err = cli.Registry.GenerateRegisterRuntimeTx(sc.nonce, kmRtDesc, kmTxPath, ""); err != nil { + kmTxPath := filepath.Join(childEnv.Dir(), "register_km_runtime.json") + if err = cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), newRt.ToRuntimeDescriptor(), sc.nonce, kmTxPath); err != nil { return fmt.Errorf("failed to generate register KM runtime tx: %w", err) } sc.nonce++ diff --git a/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go b/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go index 03962c50594..01b881e1d16 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/runtime_dynamic.go @@ -138,9 +138,8 @@ func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { // nolint: gocyclo // Register a new keymanager runtime. kmRt := sc.Net.Runtimes()[0] - kmRtDesc := kmRt.ToRuntimeDescriptor() kmTxPath := filepath.Join(childEnv.Dir(), "register_km_runtime.json") - if err := cli.Registry.GenerateRegisterRuntimeTx(nonce, kmRtDesc, kmTxPath, ""); err != nil { + if err := cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), kmRt.ToRuntimeDescriptor(), nonce, kmTxPath); err != nil { return fmt.Errorf("failed to generate register KM runtime tx: %w", err) } nonce++ @@ -224,7 +223,7 @@ func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { // nolint: gocyclo compRt := sc.Net.Runtimes()[1] compRtDesc := compRt.ToRuntimeDescriptor() txPath := filepath.Join(childEnv.Dir(), "register_compute_runtime.json") - if err := cli.Registry.GenerateRegisterRuntimeTx(nonce, compRtDesc, txPath, compRt.GetGenesisStatePath()); err != nil { + if err := cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), compRtDesc, nonce, txPath); err != nil { return fmt.Errorf("failed to generate register compute runtime tx: %w", err) } nonce++ diff --git a/go/oasis-test-runner/scenario/e2e/runtime/runtime_upgrade.go b/go/oasis-test-runner/scenario/e2e/runtime/runtime_upgrade.go index 80bc2513c59..145d48555a6 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime/runtime_upgrade.go +++ b/go/oasis-test-runner/scenario/e2e/runtime/runtime_upgrade.go @@ -238,9 +238,8 @@ func (sc *runtimeUpgradeImpl) Run(childEnv *env.Env) error { // Update runtime to include the new enclave identity. sc.Logger.Info("updating runtime descriptor") newRt := sc.Net.Runtimes()[len(sc.Net.Runtimes())-1] - newRtDesc := newRt.ToRuntimeDescriptor() newTxPath := filepath.Join(childEnv.Dir(), "register_update_compute_runtime.json") - if err = cli.Registry.GenerateRegisterRuntimeTx(sc.nonce, newRtDesc, newTxPath, ""); err != nil { + if err = cli.Registry.GenerateRegisterRuntimeTx(childEnv.Dir(), newRt.ToRuntimeDescriptor(), sc.nonce, newTxPath); err != nil { return fmt.Errorf("failed to generate register compute runtime tx: %w", err) } sc.nonce++ From 03a70329aec45549d08217939ab01a0513ca0f16 Mon Sep 17 00:00:00 2001 From: ptrus Date: Thu, 29 Apr 2021 11:29:35 +0200 Subject: [PATCH 2/2] docs/deploying-a-runtime: update docs --- docs/setup/deploying-a-runtime.md | 208 ++++++++++++++++++------------ 1 file changed, 129 insertions(+), 79 deletions(-) diff --git a/docs/setup/deploying-a-runtime.md b/docs/setup/deploying-a-runtime.md index b8d7b98c0f1..5f7d4becd65 100644 --- a/docs/setup/deploying-a-runtime.md +++ b/docs/setup/deploying-a-runtime.md @@ -2,8 +2,8 @@ Before proceeding, make sure to look at the [prerequisites] required for running an Oasis Core environment followed by [build instructions] for the respective -environment (non-SGX or SGX), using the [`oasis-net-runner`] and see -[runtime documentation] for a general documentation on runtimes. +environment (non-SGX or SGX), using the [`oasis-net-runner`] and see [runtime +documentation] for a general documentation on runtimes. These instructions will show how to register and deploy a runtime node on a local development network. @@ -19,6 +19,7 @@ Use the [`oasis-net-runner`] to provision a validator node network without any registered runtimes. + ``` mkdir /tmp/runtime-example @@ -31,11 +32,12 @@ oasis-net-runner \ --fixture.default.fund_entities \ --fixture.default.num_entities 2 ``` + -The following steps should be run in a separate terminal window. -To simplify the instructions set up an `ADDR` environment variable -pointing to the UNIX socket exposed by the started node: +The following steps should be run in a separate terminal window. To simplify the +instructions set up an `ADDR` environment variable pointing to the UNIX socket +exposed by the started node: ``` export ADDR=unix:/tmp/runtime-example/net-runner/network/validator-0/internal.sock @@ -50,11 +52,13 @@ oasis-node registry entity list -a $ADDR -v Should give output similar to: + ``` {"v":2,"id":"JTUtHd4XYQjh//e6eYU7Pa/XMFG88WE+jixvceIfWrk=","nodes":["LQu4ZtFg8OJ0MC4M4QMeUR7Is6Xt4A/CW+PK/7TPiH0="]} {"v":2,"id":"+MJpnSTzc11dNI5emMa+asCJH5cxBiBCcpbYE4XBdso="} {"v":2,"id":"TqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TU="} ``` + In following steps we will register and run the [simple-keyvalue] runtime on the @@ -66,51 +70,102 @@ network. To generate and sign a runtime registration transaction that will initialize and register the runtime we will use the `registry runtime gen_register` command. -When initializing a runtime we need to specify various runtime parameters. To -list all of the available parameters with short descriptions run: -`registry runtime gen_register --help` subcommand. - -``` -oasis-node registry runtime gen_register --help -``` +When initializing a runtime we need to provide the runtime descriptor. -For additional information about runtimes and parameters see the -[runtime documentation] and [code reference]. +For additional information about runtimes and parameters see the [runtime +documentation] and [code reference]. Before generating the registration transaction, gather the following data and set up environment variables to simplify instructions. - `ENTITY_DIR` - Path to the entity directory created when starting the -development network. This entity will be the runtime owner. The genesis used in -the provisioning initial network step funds the all entities in entities. In -the following instructions we will be using the `entity-2` entity (located in -`/tmp/runtime-example/net-runner/network/entity-2/` directory). + development network. This entity will be the runtime owner. The genesis used + in the provisioning initial network step funds the all entities in entities. + In the following instructions we will be using the `entity-2` entity (located + in `/tmp/runtime-example/net-runner/network/entity-2/` directory). - `ENTITY_ID` - ID of the entity that will be the owner of the runtime. You can -get the entity ID from `$ENTITY_DIR/entity.json` file. + get the entity ID from `$ENTITY_DIR/entity.json` file. - `GENESIS_JSON` - Path to the genesis.json file used in the development -network. (defaults to: `/tmp/runtime-example/net-runner/network/genesis.json`). + network. (defaults to: + `/tmp/runtime-example/net-runner/network/genesis.json`). - `RUNTIME_ID` - See [runtime identifiers] on how to choose a runtime -identifier. In this example we use - `8000000000000000000000000000000000000000000000000000000001234567`, which is a -test identifier and will not work outside local tests. + identifier. In this example we use + `gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEjRWc=`, which is the `base64` encoded: + `8000000000000000000000000000000000000000000000000000000001234567` - a test + identifier that will not work outside local tests. - `RUNTIME_GENESIS_JSON` - Path to the runtime genesis state file. The runtime -used in this example does not use a genesis file. -- `NONCE` - Entity account nonce. If you followed the guide, nonce `0` -would be the initial nonce to use for the entity. Note: make sure to keep -updating the nonce when generating new transactions. To query for current -account nonce value use [stake account info] CLI. + used in this example does not use a genesis file. +- `NONCE` - Entity account nonce. If you followed the guide, nonce `0` would be + the initial nonce to use for the entity. Note: make sure to keep updating the + nonce when generating new transactions. To query for current account nonce + value use [stake account info] CLI. ``` export ENTITY_DIR=/tmp/runtime-example/net-runner/network/entity-2/ export ENTITY_ID=+MJpnSTzc11dNI5emMa+asCJH5cxBiBCcpbYE4XBdso= export GENESIS_JSON=/tmp/runtime-example/net-runner/network/genesis.json -export RUNTIME_ID=8000000000000000000000000000000000000000000000000000000001234567 -export RUNTIME_GENESIS_JSON="" +export RUNTIME_ID=gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEjRWc= +export RUNTIME_DESCRIPTOR=/tmp/runtime-example/runtime_descriptor.json export NONCE=0 ``` +Prepare a runtime descriptor: + +``` +cat << EOF > "${RUNTIME_DESCRIPTOR}" +{ + "v": 2, + "id": "${RUNTIME_ID}", + "entity_id": "${ENTITY_ID}", + "genesis": { + "state_root": "xnK40e9W7Sirh8NiLFEUBpvdOte4+XN0mNDAHs7wlno=", + "state": null, + "storage_receipts": null, + "round": 0 + }, + "kind": 1, + "tee_hardware": 0, + "versions": { + "version": {} + }, + "executor": { + "group_size": 1, + "group_backup_size": 0, + "allowed_stragglers": 0, + "round_timeout": 5, + "max_messages": 32 + }, + "txn_scheduler": { + "algorithm": "simple", + "batch_flush_timeout": 1000000000, + "max_batch_size": 1000, + "max_batch_size_bytes": 16777216, + "propose_batch_timeout": 5 + }, + "storage": { + "group_size": 1, + "min_write_replication": 1, + "max_apply_write_log_entries": 100000, + "max_apply_ops": 2, + "checkpoint_interval": 10000, + "checkpoint_num_kept": 2, + "checkpoint_chunk_size": 8388608 + }, + "admission_policy": { + "entity_whitelist": { + "entities": { + "${ENTITY_ID}": {} + } + } + }, + "staking": {}, + "governance_model": "entity" +} +EOF +``` + [runtime identifiers]: ../runtime/identifiers.md -[stake account info]: ../oasis-node/cli.md#info +[stake account info]: ../oasis-node/cli.md#info ``` oasis-node registry runtime gen_register \ @@ -121,13 +176,7 @@ oasis-node registry runtime gen_register \ --genesis.file $GENESIS_JSON \ --signer.backend file \ --signer.dir $ENTITY_DIR \ - --runtime.id $RUNTIME_ID \ - --runtime.kind compute \ - --runtime.genesis.state "$RUNTIME_GENESIS_JSON" \ - --runtime.executor.group_size 1 \ - --runtime.storage.group_size 1 \ - --runtime.admission_policy entity-whitelist \ - --runtime.admission_policy_entity_whitelist $ENTITY_ID \ + --runtime.descriptor /tmp/runtime-example/runtime-descriptor.json --debug.dont_blame_oasis \ --debug.allow_test_keys ``` @@ -136,17 +185,18 @@ After confirmation, this command outputs a signed transaction in the `/tmp/runtime-example/register_runtime.tx` file. In the next step we will submit the transaction to complete the runtime registration. -{% hint style="warning" %} -**WARNING** +{% hint style="warning" %} **WARNING** -When registering a runtime on a _non-development_ network you will likely -want to modify default parameters. Additionally, since we are running this on -a debug network, we had to enable the `debug.dont_blame_oasis` and -`debug.allow_test_keys` flags. -{% endhint %} +When registering a runtime on a _non-development_ network you will likely want +to modify default parameters. Additionally, since we are running this on a debug +network, we had to enable the `debug.dont_blame_oasis` and +`debug.allow_test_keys` flags. {% endhint %} -[code reference]: https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/registry/api?tab=doc#Runtime + +[code reference]: + https://pkg.go.dev/github.com/oasisprotocol/oasis-core/go/registry/api?tab=doc#Runtime + ## Submitting the Runtime Register Transaction @@ -173,15 +223,15 @@ oasis-node registry runtime list \ Should give output similar to + ``` {"v":2,"id":"gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEjRWc=","entity_id":"+MJpnSTzc11dNI5emMa+asCJH5cxBiBCcpbYE4XBdso=","genesis":{"state_root":"xnK40e9W7Sirh8NiLFEUBpvdOte4+XN0mNDAHs7wlno=","state":null,"storage_receipts":null,"round":0},"kind":1,"tee_hardware":0,"versions":{"version":{}},"executor":{"group_size":1,"group_backup_size":0,"allowed_stragglers":0,"round_timeout":5,"max_messages":32},"txn_scheduler":{"algorithm":"simple","batch_flush_timeout":1000000000,"max_batch_size":1000,"max_batch_size_bytes":16777216,"propose_batch_timeout":5},"storage":{"group_size":1,"min_write_replication":1,"max_apply_write_log_entries":100000,"max_apply_ops":2,"checkpoint_interval":10000,"checkpoint_num_kept":2,"checkpoint_chunk_size":8388608},"admission_policy":{"entity_whitelist":{"entities":{"+MJpnSTzc11dNI5emMa+asCJH5cxBiBCcpbYE4XBdso=":{}}}},"staking":{},"governance_model":"entity"} ``` + -{% hint style="info" %} -Since we did not setup any runtime nodes, the runtime will get [suspended] until -nodes for the runtime register. -{% endhint %} +{% hint style="info" %} Since we did not setup any runtime nodes, the runtime +will get [suspended] until nodes for the runtime register. {% endhint %} In the next step we will setup and run a runtime node. @@ -192,23 +242,22 @@ In the next step we will setup and run a runtime node. We will now run a node that will act as a compute, storage and client node for the runtime. -{% hint style="info" %} -In a real word scenario there would be multiple nodes running the runtime, -each likely serving as a single type only. -{% endhint %} +{% hint style="info" %} In a real word scenario there would be multiple nodes +running the runtime, each likely serving as a single type only. {% endhint %} Before running the node, gather the following data parameters and set up environment variables to simplify instructions. -- `RUNTIME_BINARY` - Path to the runtime binary that will be run on the node. -We will use the [simple-keyvalue] runtime. If you followed the -[build instructions] the built binary is available at -`./target/default/debug/simple-keyvalue`. +- `RUNTIME_BINARY` - Path to the runtime binary that will be run on the node. We + will use the [simple-keyvalue] runtime. If you followed the [build + instructions] the built binary is available at + `./target/default/debug/simple-keyvalue`. - `SEED_NODE_ADDRESS` - Address of the seed node in the development network. -Seed node address can be seen in the `oasis-net-runner` logs, when the network -is initially provisioned. + Seed node address can be seen in the `oasis-net-runner` logs, when the network + is initially provisioned. + ``` export RUNTIME_BINARY=/workdir/target/default/debug/simple-keyvalue export SEED_NODE_ADDRESS=@127.0.0.1:20000 @@ -238,19 +287,17 @@ oasis-node \ --debug.dont_blame_oasis \ --debug.allow_test_keys ``` + -{% hint style="danger" %} -**WARNING** +{% hint style="danger" %} **WARNING** This also enables unsafe debug-only flags which must never be used in a -production setting as they may result in node compromise. -{% endhint %} +production setting as they may result in node compromise. {% endhint %} -{% hint style="info" %} -When running a runtime node in a production setting, the `worker.p2p.addresses` -and `worker.client.addresses` flags need to be configured as well. -{% endhint %} +{% hint style="info" %} When running a runtime node in a production setting, the +`worker.p2p.addresses` and `worker.client.addresses` flags need to be configured +as well. {% endhint %} Following steps should be run in a new terminal window. @@ -261,8 +308,8 @@ need to update the entity information in registry, to include the started node. Before proceeding, gather the runtime node id and store it in a variable. If you followed above instructions, the node id can be seen in -`/tmp/runtime-example/runtime-node/identity_pub.pem` (or using the [node -control status command]). +`/tmp/runtime-example/runtime-node/identity_pub.pem` (or using the [node control +status command]). Update the entity and generate a transaction that will update the registry state. @@ -300,6 +347,7 @@ Confirm the entity in the registry has been updated by querying the registry state: + ``` oasis-node registry entity list -a $ADDR -v @@ -307,10 +355,11 @@ oasis-node registry entity list -a $ADDR -v {"v":1,"id":"+MJpnSTzc11dNI5emMa+asCJH5cxBiBCcpbYE4XBdso=","nodes":["vWUfSmjrHSlN5tSSO3/Qynzx+R/UlwPV9u+lnodQ00c="]} {"v":1,"id":"TqUyj5Q+9vZtqu10yw6Zw7HEX3Ywe0JQA9vHyzY47TU=","allow_entity_signed_nodes":true} ``` + -Node is now able to register and the runtime should get resumed, make sure -this happens by querying the registry for runtimes: +Node is now able to register and the runtime should get resumed, make sure this +happens by querying the registry for runtimes: ``` # Ensure node is registered @@ -320,19 +369,18 @@ oasis-node registry node list -a $ADDR -v | grep "$NODE_ID" oasis-node registry runtime list -a $ADDR -v ``` -{% hint style="info" %} -You might need to wait few seconds for an epoch transition so that the node -is registered and runtime gets resumed. +{% hint style="info" %} You might need to wait few seconds for an epoch +transition so that the node is registered and runtime gets resumed. {% endhint %} [node control status command]: ../oasis-node/cli.md#status ## Testing the Runtime -Now that the runtime node is running, is registered, and runtime is resumed, -we can test the runtime by submitting runtime transactions. -For that we use the [simple-keyvalue-client] binary which tests the -functionality of the `simple-keyvalue` runtime. +Now that the runtime node is running, is registered, and runtime is resumed, we +can test the runtime by submitting runtime transactions. For that we use the +[simple-keyvalue-client] binary which tests the functionality of the +`simple-keyvalue` runtime. If you followed [build instructions] the built client binary is available at `target/default/debug/simple-keyvalue-client`. @@ -340,6 +388,7 @@ If you followed [build instructions] the built client binary is available at Run the test client: + ``` ./target/default/debug/simple-keyvalue-client \ --node-address unix:/tmp/runtime-example/runtime-node/internal.sock \ @@ -357,6 +406,7 @@ Getting latest block... ... Simple key/value client finished. ``` + [simple-keyvalue-client]: ../../tests/clients/simple-keyvalue