From c6acc1da59aa11662bdf352ab6827bc20371e06c Mon Sep 17 00:00:00 2001 From: Jernej Kos Date: Mon, 6 Jan 2020 14:00:29 +0100 Subject: [PATCH] go/oasis-test-runner: Add dynamic runtime registration tests --- go/oasis-test-runner/oasis/args.go | 5 + go/oasis-test-runner/oasis/client.go | 5 + go/oasis-test-runner/oasis/compute.go | 5 + go/oasis-test-runner/oasis/controller.go | 7 +- go/oasis-test-runner/oasis/fixture.go | 38 +-- go/oasis-test-runner/oasis/ias.go | 12 +- go/oasis-test-runner/oasis/keymanager.go | 7 +- go/oasis-test-runner/oasis/oasis.go | 16 +- go/oasis-test-runner/oasis/runtime.go | 61 ++++- go/oasis-test-runner/oasis/storage.go | 5 + go/oasis-test-runner/oasis/validator.go | 5 + go/oasis-test-runner/scenario/e2e/basic.go | 35 +++ .../scenario/e2e/runtime_dynamic.go | 224 ++++++++++++++++++ .../scenario/e2e/runtime_prune.go | 30 +-- go/oasis-test-runner/test-runner.go | 2 + go/worker/common/committee/node.go | 2 + 16 files changed, 397 insertions(+), 62 deletions(-) create mode 100644 go/oasis-test-runner/scenario/e2e/runtime_dynamic.go diff --git a/go/oasis-test-runner/oasis/args.go b/go/oasis-test-runner/oasis/args.go index c9c8c735971..c92d935f36a 100644 --- a/go/oasis-test-runner/oasis/args.go +++ b/go/oasis-test-runner/oasis/args.go @@ -34,6 +34,11 @@ type argBuilder struct { vec []string } +func (args *argBuilder) internalSocketAddress(path string) *argBuilder { + args.vec = append(args.vec, "--"+grpc.CfgAddress, "unix:"+path) + return args +} + func (args *argBuilder) debugDontBlameOasis() *argBuilder { args.vec = append(args.vec, "--"+flags.CfgDebugDontBlameOasis) return args diff --git a/go/oasis-test-runner/oasis/client.go b/go/oasis-test-runner/oasis/client.go index 9d522dde993..432958ae81d 100644 --- a/go/oasis-test-runner/oasis/client.go +++ b/go/oasis-test-runner/oasis/client.go @@ -47,6 +47,11 @@ func (client *Client) startNode() error { return nil } +// Start starts an Oasis node. +func (client *Client) Start() error { + return client.startNode() +} + // NewClient provisions a new client node and adds it to the network. func (net *Network) NewClient() (*Client, error) { clientName := fmt.Sprintf("client-%d", len(net.clients)) diff --git a/go/oasis-test-runner/oasis/compute.go b/go/oasis-test-runner/oasis/compute.go index 1dec1ef2fd7..f4bd9b6f07b 100644 --- a/go/oasis-test-runner/oasis/compute.go +++ b/go/oasis-test-runner/oasis/compute.go @@ -64,6 +64,11 @@ func (worker *Compute) ExportsPath() string { return nodeExportsPath(worker.dir) } +// Start starts an Oasis node. +func (worker *Compute) Start() error { + return worker.startNode() +} + func (worker *Compute) startNode() error { args := newArgBuilder(). debugDontBlameOasis(). diff --git a/go/oasis-test-runner/oasis/controller.go b/go/oasis-test-runner/oasis/controller.go index b6bec203951..de36d6577ae 100644 --- a/go/oasis-test-runner/oasis/controller.go +++ b/go/oasis-test-runner/oasis/controller.go @@ -6,6 +6,7 @@ import ( cmnGrpc "github.com/oasislabs/oasis-core/go/common/grpc" consensus "github.com/oasislabs/oasis-core/go/consensus/api" control "github.com/oasislabs/oasis-core/go/control/api" + registry "github.com/oasislabs/oasis-core/go/registry/api" runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api" staking "github.com/oasislabs/oasis-core/go/staking/api" ) @@ -16,8 +17,9 @@ type Controller struct { control.DebugController control.NodeController - Staking staking.Backend Consensus consensus.ClientBackend + Staking staking.Backend + Registry registry.Backend RuntimeClient runtimeClient.RuntimeClient } @@ -36,8 +38,9 @@ func NewController(socketPath string) (*Controller, error) { return &Controller{ DebugController: control.NewDebugControllerClient(conn), NodeController: control.NewNodeControllerClient(conn), - Staking: staking.NewStakingClient(conn), Consensus: consensus.NewConsensusClient(conn), + Staking: staking.NewStakingClient(conn), + Registry: registry.NewRegistryClient(conn), RuntimeClient: runtimeClient.NewRuntimeClient(conn), }, nil } diff --git a/go/oasis-test-runner/oasis/fixture.go b/go/oasis-test-runner/oasis/fixture.go index dddfec595e7..6d7d24f0917 100644 --- a/go/oasis-test-runner/oasis/fixture.go +++ b/go/oasis-test-runner/oasis/fixture.go @@ -150,7 +150,7 @@ func (f *ValidatorFixture) Create(net *Network) (*Validator, error) { } // RuntimeFixture is a runtime fixture. -type RuntimeFixture struct { +type RuntimeFixture struct { // nolint: maligned ID common.Namespace `json:"id"` Kind registry.RuntimeKind `json:"kind"` Entity int `json:"entity"` @@ -166,6 +166,8 @@ type RuntimeFixture struct { Storage registry.StorageParameters `json:"storage"` Pruner RuntimePrunerCfg `json:"pruner,omitempty"` + + ExcludeFromGenesis bool `json:"exclude_from_genesis,omitempty"` } // Create instantiates the runtime described by the fixture. @@ -188,20 +190,21 @@ func (f *RuntimeFixture) Create(netFixture *NetworkFixture, net *Network) (*Runt } return net.NewRuntime(&RuntimeCfg{ - ID: f.ID, - Kind: f.Kind, - Entity: entity, - Keymanager: km, - TEEHardware: netFixture.TEE.Hardware, - MrSigner: netFixture.TEE.MrSigner, - Compute: f.Compute, - Merge: f.Merge, - TxnScheduler: f.TxnScheduler, - Storage: f.Storage, - Binary: f.Binary, - GenesisState: f.GenesisState, - GenesisRound: f.GenesisRound, - Pruner: f.Pruner, + ID: f.ID, + Kind: f.Kind, + Entity: entity, + Keymanager: km, + TEEHardware: netFixture.TEE.Hardware, + MrSigner: netFixture.TEE.MrSigner, + Compute: f.Compute, + Merge: f.Merge, + TxnScheduler: f.TxnScheduler, + Storage: f.Storage, + Binary: f.Binary, + GenesisState: f.GenesisState, + GenesisRound: f.GenesisRound, + Pruner: f.Pruner, + ExcludeFromGenesis: f.ExcludeFromGenesis, }) } @@ -237,10 +240,12 @@ func (f *KeymanagerFixture) Create(net *Network) (*Keymanager, error) { } // StorageWorkerFixture is a storage worker fixture. -type StorageWorkerFixture struct { +type StorageWorkerFixture struct { // nolint: maligned Backend string `json:"backend"` Entity int `json:"entity"` + Restartable bool `json:"restartable"` + LogWatcherHandlerFactories []log.WatcherHandlerFactory `json:"-"` IgnoreApplies bool `json:"ignore_applies,omitempty"` @@ -255,6 +260,7 @@ func (f *StorageWorkerFixture) Create(net *Network) (*Storage, error) { return net.NewStorage(&StorageCfg{ NodeCfg: NodeCfg{ + Restartable: f.Restartable, LogWatcherHandlerFactories: f.LogWatcherHandlerFactories, }, Backend: f.Backend, diff --git a/go/oasis-test-runner/oasis/ias.go b/go/oasis-test-runner/oasis/ias.go index 39b293ce056..cea1b605064 100644 --- a/go/oasis-test-runner/oasis/ias.go +++ b/go/oasis-test-runner/oasis/ias.go @@ -16,7 +16,8 @@ var mockSPID []byte type iasProxy struct { Node - grpcPort uint16 + useRegistry bool + grpcPort uint16 } func (ias *iasProxy) tlsCertPath() string { @@ -29,9 +30,13 @@ func (ias *iasProxy) startNode() error { debugDontBlameOasis(). debugAllowTestKeys(). grpcServerPort(ias.grpcPort). - iasUseGenesis(). iasDebugMock(). iasSPID(mockSPID) + if ias.useRegistry { + args = args.internalSocketAddress(ias.net.validators[0].SocketPath()) + } else { + args = args.iasUseGenesis() + } var err error if ias.cmd, ias.exitCh, err = ias.net.startOasisNode( @@ -79,7 +84,8 @@ func (net *Network) newIASProxy() (*iasProxy, error) { net: net, dir: iasDir, }, - grpcPort: net.nextNodePort, + useRegistry: net.cfg.IASUseRegistry, + grpcPort: net.nextNodePort, } net.iasProxy.doStartNode = net.iasProxy.startNode diff --git a/go/oasis-test-runner/oasis/keymanager.go b/go/oasis-test-runner/oasis/keymanager.go index 39c4390b0e7..fd38a4ee0df 100644 --- a/go/oasis-test-runner/oasis/keymanager.go +++ b/go/oasis-test-runner/oasis/keymanager.go @@ -59,11 +59,16 @@ func (km *Keymanager) TLSCertPath() string { return nodeTLSCertPath(km.dir) } -// Exports path returns the path to the node's exports data dir. +// ExportsPath returns the path to the node's exports data dir. func (km *Keymanager) ExportsPath() string { return nodeExportsPath(km.dir) } +// Start starts an Oasis node. +func (km *Keymanager) Start() error { + return km.startNode() +} + func (km *Keymanager) provisionGenesis() error { // Provision status and policy. We can only provision this here as we need // a list of runtimes allowed to query the key manager. diff --git a/go/oasis-test-runner/oasis/oasis.go b/go/oasis-test-runner/oasis/oasis.go index aa589370d4b..6a5a3149b0c 100644 --- a/go/oasis-test-runner/oasis/oasis.go +++ b/go/oasis-test-runner/oasis/oasis.go @@ -98,6 +98,11 @@ func (n *Node) stopNode() error { return nil } +// Stop stops the node. +func (n *Node) Stop() error { + return n.stopNode() +} + // Restart kills the node, waits for it to stop, and starts it again. func (n *Node) Restart() error { if err := n.stopNode(); err != nil { @@ -179,7 +184,9 @@ type NetworkCfg struct { // nolint: maligned // DeterministicIdentities is the deterministic identities flag. DeterministicIdentities bool `json:"deterministic_identities"` - // XXX: Config for IAS proxy + // IASUseRegistry specifies whether the IAS proxy should use the registry instead of the + // genesis document for authenticating runtime IDs. + IASUseRegistry bool `json:"ias_use_registry,omitempty"` // StakingGenesis is the name of a file with a staking genesis document to use if GenesisFile isn't set. StakingGenesis string `json:"staking_genesis"` @@ -259,8 +266,13 @@ func (net *Network) ClientController() *Controller { // NumRegisterNodes returns the number of all nodes that need to register. func (net *Network) NumRegisterNodes() int { + var keyManagers int + if net.keymanager != nil { + keyManagers = 1 + } + return len(net.validators) + - 1 + // Key manager. + keyManagers + len(net.storageWorkers) + len(net.computeWorkers) + len(net.byzantine) diff --git a/go/oasis-test-runner/oasis/runtime.go b/go/oasis-test-runner/oasis/runtime.go index e7411ca0a65..cb12cf058f4 100644 --- a/go/oasis-test-runner/oasis/runtime.go +++ b/go/oasis-test-runner/oasis/runtime.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/oasislabs/oasis-core/go/common" + "github.com/oasislabs/oasis-core/go/common/cbor" "github.com/oasislabs/oasis-core/go/common/node" "github.com/oasislabs/oasis-core/go/common/sgx" cmdCommon "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common" @@ -32,6 +33,9 @@ type Runtime struct { // nolint: maligned mrSigner *sgx.MrSigner pruner RuntimePrunerCfg + + excludeFromGenesis bool + descriptor registry.Runtime } // RuntimeCfg is the Oasis runtime provisioning configuration. @@ -53,6 +57,8 @@ type RuntimeCfg struct { // nolint: maligned Storage registry.StorageParameters Pruner RuntimePrunerCfg + + ExcludeFromGenesis bool } // RuntimePrunerCfg is the pruner configuration for an Oasis runtime. @@ -69,13 +75,32 @@ func (rt *Runtime) ID() common.Namespace { } func (rt *Runtime) toGenesisArgs() []string { + if rt.excludeFromGenesis { + return []string{} + } + return []string{ "--runtime", filepath.Join(rt.dir.String(), rtDescriptorFile), } } +// ToRuntimeDescriptor returns a registry runtime descriptor for this runtime. +func (rt *Runtime) ToRuntimeDescriptor() registry.Runtime { + return rt.descriptor +} + // NewRuntime provisions a new runtime and adds it to the network. func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { + descriptor := registry.Runtime{ + ID: cfg.ID, + Kind: cfg.Kind, + TEEHardware: cfg.TEEHardware, + Compute: cfg.Compute, + Merge: cfg.Merge, + TxnScheduler: cfg.TxnScheduler, + Storage: cfg.Storage, + } + rtDir, err := net.baseDir.NewSubDir("runtime-" + cfg.ID.String()) if err != nil { net.logger.Error("failed to create runtime subdir", @@ -89,7 +114,6 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { "--" + cmdCommon.CfgDataDir, rtDir.String(), "--" + cmdRegRt.CfgID, cfg.ID.String(), "--" + cmdRegRt.CfgKind, cfg.Kind.String(), - "--" + cmdRegRt.CfgGenesisRound, strconv.FormatUint(cfg.GenesisRound, 10), } if cfg.Kind == registry.KindCompute { args = append(args, []string{ @@ -110,7 +134,13 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { }...) if cfg.GenesisState != "" { - args = append(args, "--"+cmdRegRt.CfgGenesisState, cfg.GenesisState) + args = append(args, + "--"+cmdRegRt.CfgGenesisRound, strconv.FormatUint(cfg.GenesisRound, 10), + "--"+cmdRegRt.CfgGenesisState, cfg.GenesisState, + ) + + descriptor.Genesis.Round = cfg.GenesisRound + // TODO: Support genesis state. } } var mrEnclave *sgx.MrEnclave @@ -123,11 +153,20 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { "--" + cmdRegRt.CfgTEEHardware, cfg.TEEHardware.String(), "--" + cmdRegRt.CfgVersionEnclave, mrEnclave.String() + cfg.MrSigner.String(), }...) + + descriptor.Version.TEE = cbor.Marshal(registry.VersionInfoIntelSGX{ + Enclaves: []sgx.EnclaveIdentity{ + {MrEnclave: *mrEnclave, MrSigner: *cfg.MrSigner}, + }, + }) } if cfg.Keymanager != nil { args = append(args, []string{ "--" + cmdRegRt.CfgKeyManager, cfg.Keymanager.id.String(), }...) + + descriptor.KeyManager = new(common.Namespace) + *descriptor.KeyManager = cfg.Keymanager.id } args = append(args, cfg.Entity.toGenesisArgs()...) @@ -145,14 +184,16 @@ func (net *Network) NewRuntime(cfg *RuntimeCfg) (*Runtime, error) { } rt := &Runtime{ - dir: rtDir, - id: cfg.ID, - kind: cfg.Kind, - binary: cfg.Binary, - teeHardware: cfg.TEEHardware, - mrEnclave: mrEnclave, - mrSigner: cfg.MrSigner, - pruner: cfg.Pruner, + dir: rtDir, + id: cfg.ID, + kind: cfg.Kind, + binary: cfg.Binary, + teeHardware: cfg.TEEHardware, + mrEnclave: mrEnclave, + mrSigner: cfg.MrSigner, + pruner: cfg.Pruner, + excludeFromGenesis: cfg.ExcludeFromGenesis, + descriptor: descriptor, } net.runtimes = append(net.runtimes, rt) diff --git a/go/oasis-test-runner/oasis/storage.go b/go/oasis-test-runner/oasis/storage.go index 10a5d2637e0..f3c8ebbcd3d 100644 --- a/go/oasis-test-runner/oasis/storage.go +++ b/go/oasis-test-runner/oasis/storage.go @@ -67,6 +67,11 @@ func (worker *Storage) DatabasePath() string { return filepath.Join(worker.dir.String(), database.DefaultFileName(worker.backend)) } +// Start starts an Oasis node. +func (worker *Storage) Start() error { + return worker.startNode() +} + func (worker *Storage) startNode() error { args := newArgBuilder(). debugDontBlameOasis(). diff --git a/go/oasis-test-runner/oasis/validator.go b/go/oasis-test-runner/oasis/validator.go index 6d3cddcbc27..0a0cdc608f0 100644 --- a/go/oasis-test-runner/oasis/validator.go +++ b/go/oasis-test-runner/oasis/validator.go @@ -68,6 +68,11 @@ func (val *Validator) ExportsPath() string { return nodeExportsPath(val.dir) } +// Start starts an Oasis node. +func (val *Validator) Start() error { + return val.startNode() +} + func (val *Validator) startNode() error { args := newArgBuilder(). debugDontBlameOasis(). diff --git a/go/oasis-test-runner/scenario/e2e/basic.go b/go/oasis-test-runner/scenario/e2e/basic.go index 8c7906d7b4e..e369ab9ee38 100644 --- a/go/oasis-test-runner/scenario/e2e/basic.go +++ b/go/oasis-test-runner/scenario/e2e/basic.go @@ -1,11 +1,14 @@ package e2e import ( + "context" + "fmt" "os/exec" "time" "github.com/spf13/viper" + "github.com/oasislabs/oasis-core/go/common/cbor" "github.com/oasislabs/oasis-core/go/common/node" "github.com/oasislabs/oasis-core/go/common/sgx" "github.com/oasislabs/oasis-core/go/common/sgx/ias" @@ -17,6 +20,8 @@ import ( "github.com/oasislabs/oasis-core/go/oasis-test-runner/oasis/cli" "github.com/oasislabs/oasis-core/go/oasis-test-runner/scenario" registry "github.com/oasislabs/oasis-core/go/registry/api" + runtimeClient "github.com/oasislabs/oasis-core/go/runtime/client/api" + runtimeTransaction "github.com/oasislabs/oasis-core/go/runtime/transaction" "github.com/oasislabs/oasis-core/go/storage/database" ) @@ -243,3 +248,33 @@ func (sc *basicImpl) Run(childEnv *env.Env) error { return sc.wait(childEnv, cmd, clientErrCh) } + +func (sc *basicImpl) submitRuntimeTx(ctx context.Context, key, value string) error { + c := sc.net.ClientController().RuntimeClient + + // Submit a transaction and check the result. + var rsp runtimeTransaction.TxnOutput + rawRsp, err := c.SubmitTx(ctx, &runtimeClient.SubmitTxRequest{ + RuntimeID: runtimeID, + Data: cbor.Marshal(&runtimeTransaction.TxnCall{ + Method: "insert", + Args: struct { + Key string `json:"key"` + Value string `json:"value"` + }{ + Key: key, + Value: value, + }, + }), + }) + if err != nil { + return fmt.Errorf("failed to submit runtime tx: %w", err) + } + if err = cbor.Unmarshal(rawRsp, &rsp); err != nil { + return fmt.Errorf("malformed tx output from runtime: %w", err) + } + if rsp.Error != nil { + return fmt.Errorf("runtime tx failed: %s", *rsp.Error) + } + return nil +} diff --git a/go/oasis-test-runner/scenario/e2e/runtime_dynamic.go b/go/oasis-test-runner/scenario/e2e/runtime_dynamic.go new file mode 100644 index 00000000000..427620dc437 --- /dev/null +++ b/go/oasis-test-runner/scenario/e2e/runtime_dynamic.go @@ -0,0 +1,224 @@ +package e2e + +import ( + "context" + "fmt" + "path/filepath" + "time" + + "github.com/oasislabs/oasis-core/go/common/logging" + consensus "github.com/oasislabs/oasis-core/go/consensus/api" + epochtime "github.com/oasislabs/oasis-core/go/epochtime/api" + "github.com/oasislabs/oasis-core/go/oasis-test-runner/env" + "github.com/oasislabs/oasis-core/go/oasis-test-runner/oasis" + "github.com/oasislabs/oasis-core/go/oasis-test-runner/oasis/cli" + "github.com/oasislabs/oasis-core/go/oasis-test-runner/scenario" + registry "github.com/oasislabs/oasis-core/go/registry/api" +) + +var ( + // RuntimeDynamic is the dynamic runtime registration scenario. + RuntimeDynamic scenario.Scenario = newRuntimeDynamicImpl() +) + +type runtimeDynamicImpl struct { + basicImpl + + epoch epochtime.EpochTime + + logger *logging.Logger +} + +func newRuntimeDynamicImpl() scenario.Scenario { + sc := &runtimeDynamicImpl{ + basicImpl: basicImpl{ + clientBinary: "", // We use a Go client. + }, + logger: logging.GetLogger("scenario/e2e/runtime_fees"), + } + return sc +} + +func (sc *runtimeDynamicImpl) Name() string { + return "runtime-dynamic" +} + +func (sc *runtimeDynamicImpl) Fixture() (*oasis.NetworkFixture, error) { + f, err := sc.basicImpl.Fixture() + if err != nil { + return nil, err + } + + // We need IAS proxy to use the registry as we are registering runtimes dynamically. + f.Network.IASUseRegistry = true + // Avoid unexpected blocks. + f.Network.EpochtimeMock = true + // We need runtime registration to be enabled. + // TODO: This should not be needed once this is the default (no longer debug). + f.Network.RegistryDebugAllowRuntimeRegistration = true + // Exclude all runtimes from genesis as we will register those dynamically. + for i, rt := range f.Runtimes { + // TODO: This should not be needed once dynamic keymanager policy document registration + // is supported (see oasis-core#2516). + if rt.Kind != registry.KindCompute { + continue + } + f.Runtimes[i].ExcludeFromGenesis = true + } + // All runtime nodes should be restartable as we are going to restart them. + for i := range f.StorageWorkers { + f.StorageWorkers[i].Restartable = true + } + for i := range f.ComputeWorkers { + f.ComputeWorkers[i].Restartable = true + } + + return f, nil +} + +func (sc *runtimeDynamicImpl) epochTransition(ctx context.Context) error { + sc.epoch++ + + sc.logger.Info("triggering epoch transition", + "epoch", sc.epoch, + ) + if err := sc.net.Controller().SetEpoch(ctx, sc.epoch); err != nil { + return fmt.Errorf("failed to set epoch: %w", err) + } + sc.logger.Info("epoch transition done") + return nil +} + +func (sc *runtimeDynamicImpl) Run(childEnv *env.Env) error { + if err := sc.net.Start(); err != nil { + return err + } + + ctx := context.Background() + cli := cli.New(childEnv, sc.net, sc.logger) + + sc.logger.Info("waiting for validator nodes to register", + "num_validator_nodes", len(sc.net.Validators()), + ) + if err := sc.net.Controller().WaitNodesRegistered(ctx, len(sc.net.Validators())); err != nil { + return err + } + + // Perform an initial epoch transition to make sure that the nodes can handle it even though + // there are no runtimes registered yet. + if err := sc.epochTransition(ctx); err != nil { + return err + } + + // TODO: Register a new key manager runtime and status (see oasis-core#2516). + + // Register a new compute runtime. + compRt := sc.net.Runtimes()[1].ToRuntimeDescriptor() + txPath := filepath.Join(childEnv.Dir(), "register_compute_runtime.json") + if err := cli.Registry.GenerateRegisterRuntimeTx(0, compRt, txPath, ""); err != nil { + return fmt.Errorf("failed to generate register runtime tx: %w", err) + } + if err := cli.Consensus.SubmitTx(txPath); err != nil { + return fmt.Errorf("failed to register compute runtime: %w", err) + } + + // Wait for all nodes to register. + sc.logger.Info("waiting for runtime nodes to register", + "num_nodes", sc.net.NumRegisterNodes(), + ) + if err := sc.net.Controller().WaitNodesRegistered(ctx, sc.net.NumRegisterNodes()); err != nil { + return err + } + + for i := 0; i < 5; i++ { + // Perform another epoch transition to elect compute runtime committees. + if err := sc.epochTransition(ctx); err != nil { + return err + } + + // Submit a runtime transaction. + sc.logger.Info("submitting transaction to runtime", + "seq", i, + ) + if err := sc.submitRuntimeTx(ctx, "hello", "world"); err != nil { + return err + } + + // Wait a bit between epoch transitions. + time.Sleep(1 * time.Second) + } + + // Stop all runtime nodes, so they will not re-register, causing the nodes to expire. + sc.logger.Info("stopping storage nodes") + for _, n := range sc.net.StorageWorkers() { + if err := n.Stop(); err != nil { + return fmt.Errorf("failed to stop node: %w", err) + } + } + sc.logger.Info("stopping compute nodes") + for _, n := range sc.net.ComputeWorkers() { + if err := n.Stop(); err != nil { + return fmt.Errorf("failed to stop node: %w", err) + } + } + + // Epoch transitions so nodes expire. + sc.logger.Info("performing epoch transitions so nodes expire") + for i := 0; i < 3; i++ { + if err := sc.epochTransition(ctx); err != nil { + return err + } + + // Wait a bit between epoch transitions. + time.Sleep(1 * time.Second) + } + + // Ensure that runtime got suspended. + sc.logger.Info("checking that runtime got suspended") + _, err := sc.net.Controller().Registry.GetRuntime(ctx, ®istry.NamespaceQuery{ + Height: consensus.HeightLatest, + ID: compRt.ID, + }) + switch err { + case nil: + return fmt.Errorf("runtime should be suspended but it is not") + case registry.ErrNoSuchRuntime: + // Runtime is suspended. + default: + return fmt.Errorf("unexpected error while fetching runtime: %w", err) + } + + // Start runtime nodes, make sure they register. + sc.logger.Info("starting storage nodes") + for _, n := range sc.net.StorageWorkers() { + if err := n.Start(); err != nil { + return fmt.Errorf("failed to start node: %w", err) + } + } + sc.logger.Info("starting compute nodes") + for _, n := range sc.net.ComputeWorkers() { + if err := n.Start(); err != nil { + return fmt.Errorf("failed to start node: %w", err) + } + } + + sc.logger.Info("waiting for runtime nodes to register", + "num_nodes", sc.net.NumRegisterNodes(), + ) + if err := sc.net.Controller().WaitNodesRegistered(ctx, sc.net.NumRegisterNodes()); err != nil { + return err + } + + // Epoch transition. + if err := sc.epochTransition(ctx); err != nil { + return err + } + + // Submit a runtime transaction to check whether the runtimes got resumed. + sc.logger.Info("submitting transaction to runtime") + if err := sc.submitRuntimeTx(ctx, "hello", "world"); err != nil { + return err + } + + return nil +} diff --git a/go/oasis-test-runner/scenario/e2e/runtime_prune.go b/go/oasis-test-runner/scenario/e2e/runtime_prune.go index 352c1f73b77..8c2c5921f0e 100644 --- a/go/oasis-test-runner/scenario/e2e/runtime_prune.go +++ b/go/oasis-test-runner/scenario/e2e/runtime_prune.go @@ -5,14 +5,12 @@ import ( "fmt" "time" - "github.com/oasislabs/oasis-core/go/common/cbor" "github.com/oasislabs/oasis-core/go/common/logging" "github.com/oasislabs/oasis-core/go/oasis-test-runner/env" "github.com/oasislabs/oasis-core/go/oasis-test-runner/oasis" "github.com/oasislabs/oasis-core/go/oasis-test-runner/scenario" "github.com/oasislabs/oasis-core/go/runtime/client/api" "github.com/oasislabs/oasis-core/go/runtime/history" - "github.com/oasislabs/oasis-core/go/runtime/transaction" ) var ( @@ -68,12 +66,6 @@ func (sc *runtimePruneImpl) Fixture() (*oasis.NetworkFixture, error) { return f, nil } -// keyValue is a key/value argument for the simple-keyvalue runtime. -type keyValue struct { - Key string `json:"key"` - Value string `json:"value"` -} - func (sc *runtimePruneImpl) Run(childEnv *env.Env) error { if err := sc.net.Start(); err != nil { return err @@ -102,26 +94,8 @@ func (sc *runtimePruneImpl) Run(childEnv *env.Env) error { "seq", i, ) - // Submit a transaction and check the result. - var rsp transaction.TxnOutput - rawRsp, err := c.SubmitTx(ctx, &api.SubmitTxRequest{ - RuntimeID: runtimeID, - Data: cbor.Marshal(&transaction.TxnCall{ - Method: "insert", - Args: keyValue{ - Key: "hello", - Value: "world", - }, - }), - }) - if err != nil { - return fmt.Errorf("failed to submit runtime tx: %w", err) - } - if err = cbor.Unmarshal(rawRsp, &rsp); err != nil { - return fmt.Errorf("malformed tx output from runtime: %w", err) - } - if rsp.Error != nil { - return fmt.Errorf("runtime tx failed: %s", *rsp.Error) + if err := sc.submitRuntimeTx(ctx, "hello", "world"); err != nil { + return err } } diff --git a/go/oasis-test-runner/test-runner.go b/go/oasis-test-runner/test-runner.go index 771b38bb330..f0afcfc57f0 100644 --- a/go/oasis-test-runner/test-runner.go +++ b/go/oasis-test-runner/test-runner.go @@ -51,6 +51,8 @@ func main() { _ = cmd.Register(e2e.IdentityCLI) // Runtime prune test. _ = cmd.Register(e2e.RuntimePrune) + // Runtime dynamic registration test. + _ = cmd.Register(e2e.RuntimeDynamic) // Transaction source test. _ = cmd.Register(e2e.TxSourceTransferShort) _ = cmd.RegisterNondefault(e2e.TxSourceTransfer) diff --git a/go/worker/common/committee/node.go b/go/worker/common/committee/node.go index c1e2cfae300..1787ef92f99 100644 --- a/go/worker/common/committee/node.go +++ b/go/worker/common/committee/node.go @@ -276,6 +276,8 @@ func (n *Node) worker() { return } + n.logger.Info("runtime is registered with the registry") + // Start watching roothash blocks. blocks, blocksSub, err := n.Roothash.WatchBlocks(n.Runtime.ID()) if err != nil {