<|start_filename|>client-sdk/go/modules/evm/types.go<|end_filename|>
package evm

// The types in this file must match the types from the evm module types
// in runtime-sdk/modules/evm/src/types.rs.

// Create is an EVM CREATE transaction.
type Create struct {
	Value    []byte `json:"value"`
	InitCode []byte `json:"init_code"`
}

// Call is an EVM CALL transaction.
type Call struct {
	Address []byte `json:"address"`
	Value   []byte `json:"value"`
	Data    []byte `json:"data"`
}

// StorageQuery queries the EVM storage.
type StorageQuery struct {
	Address []byte `json:"address"`
	Index   []byte `json:"index"`
}

// CodeQuery queries the EVM code storage.
type CodeQuery struct {
	Address []byte `json:"address"`
}

// BalanceQuery queries the EVM account balance.
type BalanceQuery struct {
	Address []byte `json:"address"`
}

// SimulateCallQuery simulates an EVM CALL.
type SimulateCallQuery struct {
	GasPrice []byte `json:"gas_price"`
	GasLimit uint64 `json:"gas_limit"`
	Caller   []byte `json:"caller"`
	Address  []byte `json:"address"`
	Value    []byte `json:"value"`
	Data     []byte `json:"data"`
}

// ModuleName is the EVM module name.
const ModuleName = "evm"

// Event is an event emitted by the EVM module.
type Event struct {
	Address []byte   `json:"address"`
	Topics  [][]byte `json:"topics"`
	Data    []byte   `json:"data"`
}


<|start_filename|>client-sdk/go/types/address.go<|end_filename|>
package types

import (
	"encoding"

	"golang.org/x/crypto/sha3"

	"github.com/oasisprotocol/oasis-core/go/common/cbor"
	"github.com/oasisprotocol/oasis-core/go/common/crypto/address"
	"github.com/oasisprotocol/oasis-core/go/common/encoding/bech32"
	staking "github.com/oasisprotocol/oasis-core/go/staking/api"

	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/ed25519"
	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/secp256k1"
	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/crypto/signature/sr25519"
)

var (
	// AddressV0Ed25519Context is the unique context for v0 Ed25519-based addresses.
	// It is shared with the consensus layer addresses on purpose.
	AddressV0Ed25519Context = staking.AddressV0Context
	// AddressV0Secp256k1EthContext is the unique context for v0 secp256k1-based addresses.
	AddressV0Secp256k1EthContext = address.NewContext("oasis-runtime-sdk/address: secp256k1eth", 0)
	// AddressV0Sr25519Context is the unique context for v0 Sr25519-based addresses.
	AddressV0Sr25519Context = address.NewContext("oasis-runtime-sdk/address: sr25519", 0)
	// AddressV0MultisigContext is the unique context for v0 multisig addresses.
	AddressV0MultisigContext = address.NewContext("oasis-runtime-sdk/address: multisig", 0)
	// AddressV0ModuleContext is the unique context for v0 module addresses.
	AddressV0ModuleContext = address.NewContext("oasis-runtime-sdk/address: module", 0)
	// AddressBech32HRP is the unique human readable part of Bech32 encoded
	// staking account addresses.
	AddressBech32HRP = staking.AddressBech32HRP

	_ encoding.BinaryMarshaler   = Address{}
	_ encoding.BinaryUnmarshaler = (*Address)(nil)
	_ encoding.TextMarshaler     = Address{}
	_ encoding.TextUnmarshaler   = (*Address)(nil)
)

// SignatureAddressSpec is information for signature-based authentication and public key-based
// address derivation.
type SignatureAddressSpec struct {
	// Ed25519 address derivation compatible with the consensus layer.
	Ed25519 *ed25519.PublicKey `json:"ed25519,omitempty"`

	// Secp256k1Eth is ethereum-compatible address derivation from Secp256k1 public keys.
	Secp256k1Eth *secp256k1.PublicKey `json:"secp256k1eth,omitempty"`

	// Sr25519 address derivation.
	Sr25519 *sr25519.PublicKey `json:"sr25519,omitempty"`
}

// PublicKey returns the public key of the authentication/address derivation specification.
func (as *SignatureAddressSpec) PublicKey() PublicKey {
	switch {
	case as.Ed25519 != nil:
		return PublicKey{PublicKey: as.Ed25519}
	case as.Secp256k1Eth != nil:
		return PublicKey{PublicKey: as.Secp256k1Eth}
	case as.Sr25519 != nil:
		return PublicKey{PublicKey: as.Sr25519}
	}
	return PublicKey{}
}

// NewSignatureAddressSpecEd25519 creates a new address specification for an Ed25519 public key.
func NewSignatureAddressSpecEd25519(pk ed25519.PublicKey) SignatureAddressSpec {
	return SignatureAddressSpec{Ed25519: &pk}
}

// NewSignatureAddressSpecSecp256k1Eth creates a new Ethereum-compatible address specification for
// an Secp256k1 public key.
func NewSignatureAddressSpecSecp256k1Eth(pk secp256k1.PublicKey) SignatureAddressSpec {
	return SignatureAddressSpec{Secp256k1Eth: &pk}
}

// NewSignatureAddressSpecSr25519 creates a new address specification for an Sr25519 public key.
func NewSignatureAddressSpecSr25519(pk sr25519.PublicKey) SignatureAddressSpec {
	return SignatureAddressSpec{Sr25519: &pk}
}

// Address is the account address.
type Address address.Address

// MarshalBinary encodes an address into binary form.
func (a Address) MarshalBinary() ([]byte, error) {
	return (address.Address)(a).MarshalBinary()
}

// UnmarshalBinary decodes a binary marshaled address.
func (a *Address) UnmarshalBinary(data []byte) error {
	return (*address.Address)(a).UnmarshalBinary(data)
}

// MarshalText encodes an address into text form.
func (a Address) MarshalText() ([]byte, error) {
	return (address.Address)(a).MarshalBech32(AddressBech32HRP)
}

// UnmarshalText decodes a text marshaled address.
func (a *Address) UnmarshalText(text []byte) error {
	return (*address.Address)(a).UnmarshalBech32(AddressBech32HRP, text)
}

// Equal compares vs another address for equality.
func (a Address) Equal(cmp Address) bool {
	return (address.Address)(a).Equal((address.Address)(cmp))
}

// String returns the string representation of an address.
func (a Address) String() string {
	bech32Addr, err := bech32.Encode(AddressBech32HRP.String(), a[:])
	if err != nil {
		return "[malformed]"
	}
	return bech32Addr
}

// NewAddress creates a new address from the given signature address specification.
func NewAddress(spec SignatureAddressSpec) (a Address) {
	var (
		ctx    address.Context
		pkData []byte
	)
	switch {
	case spec.Ed25519 != nil:
		ctx = AddressV0Ed25519Context
		pkData, _ = spec.Ed25519.MarshalBinary()
	case spec.Secp256k1Eth != nil:
		ctx = AddressV0Secp256k1EthContext
		// Use a scheme such that we can compute Secp256k1 addresses from Ethereum
		// addresses as this makes things more interoperable.
		h := sha3.NewLegacyKeccak256()
		untaggedPk, _ := spec.Secp256k1Eth.MarshalBinaryUncompressedUntagged()
		h.Write(untaggedPk)
		pkData = h.Sum(nil)[32-20:]
	case spec.Sr25519 != nil:
		ctx = AddressV0Sr25519Context
		pkData, _ = spec.Sr25519.MarshalBinary()
	default:
		panic("address: unsupported public key type")
	}
	return (Address)(address.NewAddress(ctx, pkData))
}

// NewAddressRaw creates a new address from passed address context and data.
func NewAddressRaw(ctx address.Context, data []byte) Address {
	return (Address)(address.NewAddress(ctx, data))
}

// NewAddressForModule creates a new address for a specific module and raw kind.
func NewAddressForModule(module string, kind []byte) Address {
	moduleBytes := []byte(module)
	sepBytes := []byte(".")
	data := make([]byte, 0, len(moduleBytes)+len(kind)+1)
	data = append(data,
		moduleBytes...)
	data = append(data,
		sepBytes...)
	data = append(data,
		kind...,
	)
	return (Address)(address.NewAddress(AddressV0ModuleContext, data))
}

// NewAddressFromBech32 creates a new address from the given bech-32 encoded string.
//
// Panics in case of errors -- use UnmarshalText if you want to handle errors.
func NewAddressFromBech32(data string) (a Address) {
	err := a.UnmarshalText([]byte(data))
	if err != nil {
		panic(err)
	}
	return
}

// NewAddressFromMultisig creates a new address from the given multisig configuration.
func NewAddressFromMultisig(config *MultisigConfig) Address {
	return (Address)(address.NewAddress(AddressV0MultisigContext, cbor.Marshal(config)))
}


<|start_filename|>client-sdk/go/modules/core/types.go<|end_filename|>
package core

import (
	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/types"
)

// EstimateGasQuery is the body of the core.EstimateGas query.
type EstimateGasQuery struct {
	// Caller is the address of the caller for which to do estimation. If not specified the
	// authentication information from the passed transaction is used.
	Caller *types.CallerAddress `json:"caller,omitempty"`
	// Tx is the unsigned transaction to estimate.
	Tx *types.Transaction `json:"tx"`
}


<|start_filename|>client-sdk/ts-web/rt/playground/webpack.config.js<|end_filename|>
const webpack = require('webpack');

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js',
        consensus: './src/consensus.js',
    },
    resolve: {fallback: {stream: require.resolve('stream-browserify')}},
    plugins: [
        new webpack.ProvidePlugin({
            process: 'process/browser',
            Buffer: ['buffer', 'Buffer'],
        }),
    ],
    output: {
        library: {
            name: 'playground',
            type: 'window',
            export: 'playground',
        },
    },
};


<|start_filename|>client-sdk/go/modules/consensusaccounts/types.go<|end_filename|>
package consensusaccounts

import "github.com/oasisprotocol/oasis-sdk/client-sdk/go/types"

// Deposit are the arguments for consensus.Deposit method.
type Deposit struct {
	To     *types.Address  `json:"to,omitempty"`
	Amount types.BaseUnits `json:"amount"`
}

// Withdraw are the arguments for consensus.Withdraw method.
type Withdraw struct {
	To     *types.Address  `json:"to,omitempty"`
	Amount types.BaseUnits `json:"amount"`
}

// BalanceQuery are the arguments for consensus.Balance method.
type BalanceQuery struct {
	Address types.Address `json:"address"`
}

// AccountBalance is the consensus balance in an account.
type AccountBalance struct {
	Balance types.Quantity `json:"balance"`
}

// AccountQuery are the arguments for consensus.Account method.
type AccountQuery struct {
	Address types.Address `json:"address"`
}

// ConsensusError contains error details from the consensus layer.
type ConsensusError struct {
	Module string `json:"module,omitempty"`
	Code   uint32 `json:"code,omitempty"`
}

// ModuleName is the consensus accounts module name.
const ModuleName = "consensus_accounts"

const (
	// DepositEventCode is the event code for the deposit event.
	DepositEventCode = 1
	// WithdrawEventCode is the event code for the withdraw event.
	WithdrawEventCode = 2
)

// DepositEvent is a deposit event.
type DepositEvent struct {
	From   types.Address   `json:"from"`
	Nonce  uint64          `json:"nonce"`
	To     types.Address   `json:"to"`
	Amount types.BaseUnits `json:"amount"`
	Error  *ConsensusError `json:"error,omitempty"`
}

// IsSuccess checks whether the event indicates a successful operation.
func (de *DepositEvent) IsSuccess() bool {
	return de.Error == nil
}

// WithdrawEvent is a withdraw event.
type WithdrawEvent struct {
	From   types.Address   `json:"from"`
	Nonce  uint64          `json:"nonce"`
	To     types.Address   `json:"to"`
	Amount types.BaseUnits `json:"amount"`
	Error  *ConsensusError `json:"error,omitempty"`
}

// IsSuccess checks whether the event indicates a successful operation.
func (we *WithdrawEvent) IsSuccess() bool {
	return we.Error == nil
}

// Event is a consensus account event.
type Event struct {
	Deposit  *DepositEvent
	Withdraw *WithdrawEvent
}


<|start_filename|>tests/e2e/runtime.go<|end_filename|>
package main

import (
	"context"
	"fmt"
	"path/filepath"
	"reflect"
	"runtime"
	"time"

	"google.golang.org/grpc"

	"github.com/oasisprotocol/oasis-core/go/common"
	cmnGrpc "github.com/oasisprotocol/oasis-core/go/common/grpc"
	"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/oasis-test-runner/env"
	"github.com/oasisprotocol/oasis-core/go/oasis-test-runner/log"
	"github.com/oasisprotocol/oasis-core/go/oasis-test-runner/oasis"
	"github.com/oasisprotocol/oasis-core/go/oasis-test-runner/scenario"
	"github.com/oasisprotocol/oasis-core/go/oasis-test-runner/scenario/e2e"
	registry "github.com/oasisprotocol/oasis-core/go/registry/api"
	scheduler "github.com/oasisprotocol/oasis-core/go/scheduler/api"
	"github.com/oasisprotocol/oasis-core/go/staking/api"
	"github.com/oasisprotocol/oasis-core/go/storage/database"

	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/client"
	"github.com/oasisprotocol/oasis-sdk/client-sdk/go/testing"
	"github.com/oasisprotocol/oasis-sdk/tests/e2e/txgen"
)

const (
	cfgRuntimeBinaryDirDefault = "runtime.binary_dir.default"
	cfgRuntimeLoader           = "runtime.loader"
	cfgIasMock                 = "ias.mock"

	cfgKeymanagerBinary = "keymanager.binary"
)

var (
	// RuntimeParamsDummy is a dummy instance of RuntimeScenario used to
	// register global e2e/runtime flags.
	RuntimeParamsDummy = NewRuntimeScenario("", []RunTestFunction{})

	// DefaultRuntimeLogWatcherHandlerFactories is a list of default log watcher
	// handler factories for the basic scenario.
	DefaultRuntimeLogWatcherHandlerFactories = []log.WatcherHandlerFactory{
		oasis.LogAssertNoTimeouts(),
		oasis.LogAssertNoRoundFailures(),
		oasis.LogAssertNoExecutionDiscrepancyDetected(),
	}

	runtimeID common.Namespace
	_         = runtimeID.UnmarshalHex("8000000000000000000000000000000000000000000000000000000000000000")

	keymanagerID common.Namespace
	_            = keymanagerID.UnmarshalHex("c000000000000000ffffffffffffffffffffffffffffffffffffffffffffffff")
)

// RunTestFunction is a test function.
type RunTestFunction func(*RuntimeScenario, *logging.Logger, *grpc.ClientConn, client.RuntimeClient) error

// RuntimeScenario is a base class for e2e test scenarios involving runtimes.
type RuntimeScenario struct {
	e2e.E2E

	// RuntimeName is the name of the runtime binary.
	RuntimeName string

	// RunTest is a list of test functions to run once the network is up.
	RunTest []RunTestFunction
}

// NewRuntimeScenario creates a new runtime test scenario using the given
// runtime and test functions.
func NewRuntimeScenario(runtimeName string, tests []RunTestFunction) *RuntimeScenario {
	sc := &RuntimeScenario{
		E2E:         *e2e.NewE2E(runtimeName),
		RuntimeName: runtimeName,
		RunTest:     tests,
	}
	sc.Flags.String(cfgRuntimeBinaryDirDefault, "../../target/debug", "path to the runtime binaries directory")
	sc.Flags.String(cfgRuntimeLoader, "../../../oasis-core/target/default/debug/oasis-core-runtime-loader", "path to the runtime loader")
	sc.Flags.String(cfgKeymanagerBinary, "", "path to the keymanager binary")
	sc.Flags.Bool(cfgIasMock, true, "if mock IAS service should be used")

	return sc
}

func (sc *RuntimeScenario) Clone() scenario.Scenario {
	return &RuntimeScenario{
		E2E:         sc.E2E.Clone(),
		RuntimeName: sc.RuntimeName,
		RunTest:     append(make([]RunTestFunction, 0, len(sc.RunTest)), sc.RunTest...),
	}
}

func (sc *RuntimeScenario) PreInit(childEnv *env.Env) error {
	return nil
}

func (sc *RuntimeScenario) Fixture() (*oasis.NetworkFixture, error) {
	f, err := sc.E2E.Fixture()
	if err != nil {
		return nil, err
	}

	runtimeBinary := sc.RuntimeName
	runtimeLoader, _ := sc.Flags.GetString(cfgRuntimeLoader)
	iasMock, _ := sc.Flags.GetBool(cfgIasMock)

	keymanagerPath, _ := sc.Flags.GetString(cfgKeymanagerBinary)
	usingKeymanager := len(keymanagerPath) > 0

	ff := &oasis.NetworkFixture{
		TEE: oasis.TEEFixture{
			Hardware: node.TEEHardwareInvalid,
			MrSigner: nil,
		},
		Network: oasis.NetworkCfg{
			NodeBinary:                        f.Network.NodeBinary,
			RuntimeSGXLoaderBinary:            runtimeLoader,
			DefaultLogWatcherHandlerFactories: DefaultRuntimeLogWatcherHandlerFactories,
			Consensus:                         f.Network.Consensus,
			IAS: oasis.IASCfg{
				Mock: iasMock,
			},
			StakingGenesis: &api.Genesis{
				Parameters: api.ConsensusParameters{
					MaxAllowances: 10,
				},
				TotalSupply: *quantity.NewFromUint64(200),
				Ledger: map[api.Address]*api.Account{
					api.Address(testing.Alice.Address): {
						General: api.GeneralAccount{
							Balance: *quantity.NewFromUint64(100),
							Allowances: map[api.Address]quantity.Quantity{
								api.NewRuntimeAddress(runtimeID): *quantity.NewFromUint64(100),
							},
						},
					},
					api.Address(testing.Bob.Address): {
						General: api.GeneralAccount{
							Balance: *quantity.NewFromUint64(100),
							Allowances: map[api.Address]quantity.Quantity{
								api.NewRuntimeAddress(runtimeID): *quantity.NewFromUint64(100),
							},
						},
					},
				},
			},
		},
		Entities: []oasis.EntityCfg{
			{IsDebugTestEntity: true},
			{},
		},
		Runtimes: []oasis.RuntimeFixture{
			// Key manager runtime.
			{
				ID:         keymanagerID,
				Kind:       registry.KindKeyManager,
				Entity:     0,
				Keymanager: -1,
				Binaries: map[node.TEEHardware][]string{
					node.TEEHardwareInvalid: {keymanagerPath},
				},
				AdmissionPolicy: registry.RuntimeAdmissionPolicy{
					AnyNode: &registry.AnyNodeRuntimeAdmissionPolicy{},
				},
				GovernanceModel: registry.GovernanceEntity,
			},
			// Compute runtime.
			{
				ID:         runtimeID,
				Kind:       registry.KindCompute,
				Entity:     0,
				Keymanager: -1,
				Binaries:   sc.resolveRuntimeBinaries([]string{runtimeBinary}),
				Executor: registry.ExecutorParameters{
					GroupSize:       2,
					GroupBackupSize: 1,
					RoundTimeout:    30,
					MaxMessages:     256,
				},
				TxnScheduler: registry.TxnSchedulerParameters{
					Algorithm:         registry.TxnSchedulerSimple,
					MaxBatchSize:      1000,
					MaxBatchSizeBytes: 16 * 1024 * 1024, // 16 MB.
					BatchFlushTimeout: 1 * time.Second,
					ProposerTimeout:   30,
				},
				Storage: registry.StorageParameters{
					GroupSize:               2,
					MinWriteReplication:     2,
					MaxApplyWriteLogEntries: 100_000,
					MaxApplyOps:             2,
				},
				AdmissionPolicy: registry.RuntimeAdmissionPolicy{
					AnyNode: &registry.AnyNodeRuntimeAdmissionPolicy{},
				},
				Constraints: map[scheduler.CommitteeKind]map[scheduler.Role]registry.SchedulingConstraints{
					scheduler.KindComputeExecutor: {
						scheduler.RoleWorker: {
							MinPoolSize: &registry.MinPoolSizeConstraint{
								Limit: 2,
							},
						},
						scheduler.RoleBackupWorker: {
							MinPoolSize: &registry.MinPoolSizeConstraint{
								Limit: 1,
							},
						},
					},
					scheduler.KindStorage: {
						scheduler.RoleWorker: {
							MinPoolSize: &registry.MinPoolSizeConstraint{
								Limit: 2,
							},
						},
					},
				},
				GovernanceModel: registry.GovernanceEntity,
			},
		},
		Validators: []oasis.ValidatorFixture{
			{Entity: 1, Consensus: oasis.ConsensusFixture{EnableConsensusRPCWorker: true, SupplementarySanityInterval: 1}},
			{Entity: 1, Consensus: oasis.ConsensusFixture{EnableConsensusRPCWorker: true}},
			{Entity: 1, Consensus: oasis.ConsensusFixture{EnableConsensusRPCWorker: true}},
		},
		StorageWorkers: []oasis.StorageWorkerFixture{
			{Backend: database.BackendNameBadgerDB, Entity: 1},
			{Backend: database.BackendNameBadgerDB, Entity: 1},
		},
		ComputeWorkers: []oasis.ComputeWorkerFixture{
			{Entity: 1, Runtimes: []int{1}},
			{Entity: 1, Runtimes: []int{1}},
			{Entity: 1, Runtimes: []int{1}},
		},
		Sentries: []oasis.SentryFixture{},
		Seeds:    []oasis.SeedFixture{{}},
		Clients: []oasis.ClientFixture{
			{Runtimes: []int{1}, RuntimeConfig: map[int]map[string]interface{}{
				1: {
					"allow_expensive_queries": true,
				},
			}},
		},
	}

	if usingKeymanager {
		for i := range ff.Runtimes {
			if ff.Runtimes[i].Kind == registry.KindKeyManager {
				continue
			}
			ff.Runtimes[i].Keymanager = 0
		}
		ff.KeymanagerPolicies = []oasis.KeymanagerPolicyFixture{
			{Runtime: 0, Serial: 1},
		}
		ff.Keymanagers = []oasis.KeymanagerFixture{
			{Runtime: 0, Entity: 1},
		}
	}

	return ff, nil
}

func (sc *RuntimeScenario) resolveRuntimeBinaries(runtimeBinaries []string) map[node.TEEHardware][]string {
	binaries := make(map[node.TEEHardware][]string)
	for _, tee := range []node.TEEHardware{
		node.TEEHardwareInvalid,
		node.TEEHardwareIntelSGX,
	} {
		for _, binary := range runtimeBinaries {
			binaries[tee] = append(binaries[tee], sc.resolveRuntimeBinary(binary))
		}
	}
	return binaries
}

func (sc *RuntimeScenario) resolveRuntimeBinary(runtimeBinary string) string {
	path, _ := sc.Flags.GetString(cfgRuntimeBinaryDirDefault)
	return filepath.Join(path, runtimeBinary)
}

func (sc *RuntimeScenario) waitNodesSynced() error {
	ctx := context.Background()

	checkSynced := func(n *oasis.Node) error {
		c, err := oasis.NewController(n.SocketPath())
		if err != nil {
			return fmt.Errorf("failed to create node controller: %w", err)
		}
		defer c.Close()

		if err = c.WaitSync(ctx); err != nil {
			return fmt.Errorf("failed to wait for node to sync: %w", err)
		}
		return nil
	}

	sc.Logger.Info("waiting for all nodes to sync")

	for _, n := range sc.Net.Validators() {
		if err := checkSynced(n.Node); err != nil {
			return err
		}
	}
	for _, n := range sc.Net.StorageWorkers() {
		if err := checkSynced(n.Node); err != nil {
			return err
		}
	}
	for _, n := range sc.Net.ComputeWorkers() {
		if err := checkSynced(n.Node); err != nil {
			return err
		}
	}
	for _, n := range sc.Net.Clients() {
		if err := checkSynced(n.Node); err != nil {
			return err
		}
	}

	sc.Logger.Info("nodes synced")
	return nil
}

func (sc *RuntimeScenario) Run(childEnv *env.Env) error {
	ctx := context.Background()

	// Start the test network.
	if err := sc.Net.Start(); err != nil {
		return err
	}

	// Wait for all nodes to sync.
	if err := sc.waitNodesSynced(); err != nil {
		return err
	}

	// Connect to the client node.
	clients := sc.Net.Clients()
	if len(clients) == 0 {
		return fmt.Errorf("client initialization failed")
	}

	conn, err := cmnGrpc.Dial("unix:"+clients[0].SocketPath(), grpc.WithInsecure())
	if err != nil {
		return err
	}
	rtc := client.New(conn, runtimeID)

	// Do an initial invariants check.
	if err = txgen.CheckInvariants(ctx, rtc); err != nil {
		sc.Logger.Error("initial invariants check failed", "err", err)
		return err
	}

	// Run the given tests for this runtime.
	for _, test := range sc.RunTest {
		testName := runtime.FuncForPC(reflect.ValueOf(test).Pointer()).Name()

		sc.Logger.Info("running test", "test", testName)
		if testErr := test(sc, sc.Logger, conn, rtc); testErr != nil {
			sc.Logger.Error("test failed",
				"test", testName,
				"err", testErr,
			)
			return testErr
		}
		sc.Logger.Info("test passed", "test", testName)

		// Do an invariants check after each test.
		if err = txgen.CheckInvariants(ctx, rtc); err != nil {
			sc.Logger.Error("invariants check failed after test",
				"test", testName,
				"err", err)
			return err
		}
	}

	return sc.Net.CheckLogWatchers()
}
