Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

ROFL instructions for Localnet #1982

Merged
merged 5 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
425 changes: 82 additions & 343 deletions docs/rofl/app.md

Large diffs are not rendered by default.

343 changes: 343 additions & 0 deletions docs/rofl/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
# Deployment on Testnet and Mainnet

As a first step we need to decide which ParaTime the ROFL app will authenticate
to. This can be any ParaTime which has the ROFL module installed. For the rest
of this section we will be using [Sapphire Testnet] which has all of the
required functionality.

## Define the Root of Trust

In the [`src/main.rs`] code update `consensus_trust_root()` to check the most
recent block of the desired network:

<!-- markdownlint-disable line-length -->
![code rust](../../examples/runtime-sdk/rofl-oracle/src/main.rs#consensus-trust-root)
<!-- markdownlint-enable line-length -->

This way, your ROFL client will sync more quickly and not want to start on any
other network or ParaTime. Read the [Consensus Trust Root] chapter to learn more
about obtaining a correct block for the root of trust.

[`src/main.rs`]: app.md#app-definition
[Consensus Trust Root]: trust-root.md

## Register the App

Before the ROFL app can authenticate it needs to be registered as an app on the
network. Anyone with enough stake can register an app. Currently, this
threshold is 10,000 TEST on Sapphire Testnet and Localnet (funded
automatically). ROFL registration on Sapphire Mainnet is yet to be enabled.

:::tip

In order to obtain TEST tokens needed for registering and running your ROFL
apps use [the faucet] or [ask on Discord]. To make things easier you should
[create or import a `secp256k1-bip44` account] that you can also use with the
Ethereum-compatible tooling like Hardhat.

<!-- markdownlint-disable line-length -->
[the faucet]: https://faucet.testnet.oasis.io/?paratime=sapphire
[ask on Discord]: https://github.com/oasisprotocol/docs/blob/main/docs/get-involved/README.md#social-media-channels
[create or import a `secp256k1-bip44` account]: https://github.com/oasisprotocol/cli/blob/master/docs/wallet.md
<!-- markdownlint-enable line-length -->

:::

Registering a ROFL app assigns it a unique app identifier in the form of:

```
rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf
```

This identifier can be used by on-chain smart contracts to ensure that they are
talking to the right app. During registration the following information is
associated with the app:

* **Administrator address.** This is the address of the account that is able to
update the app registration. During creation it defaults to the caller of the
registration transaction, but it can later be updated if needed.

* **Policy.** The policy specifies who is allowed to run instances of your ROFL
app and defines the app's _cryptographic identity_. This identity must be
proven each time the app starts through the use of remote attestation. This
ensures that all instances of your app are running the exact same code and are
running in a valid Trusted Execution Environment (TEE).

Policies can specify various parameters, but for initial registration we will
specify a very broad policy which allows anyone to run your ROFL apps. To create
a simple policy, create a file `policy.yml` with the following content:

![code yaml](../../examples/runtime-sdk/rofl-oracle/policy.yml "policy.yml")

To then register a new ROFL app run the CLI as follows:

```shell
oasis rofl create policy.yml --network testnet --paratime sapphire
```

After signing the transaction and assuming your account has enough funds to
cover the gas fees and stake required for registration, the CLI will output the
newly assigned app identifier in the following form:

```
Created ROFL application: rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf
```

You should use this identifier and replace it here in [`src/main.rs`]:

![code rust](../../examples/runtime-sdk/rofl-oracle/src/main.rs#app-id)

This is it. Before final deployment, after we have built the app binary, we will
need to update the app's registration once again to specify the app's
cryptographic identity.

## Oracle Contract Definition

:::info

While we are using [EVM-based smart contracts] in this example, the on-chain
part can be anything from a [WASM-based smart contract] to a dedicated
[runtime module].

[EVM-based smart contracts]: https://github.com/oasisprotocol/docs/blob/main/docs/dapp/sapphire/README.mdx
[WASM-based smart contract]: https://github.com/oasisprotocol/docs/blob/main/docs/dapp/cipher/README.mdx
[runtime module]: https://github.com/oasisprotocol/oasis-sdk/blob/main/docs/runtime/modules.md

:::

We have prepared a simple oracle contract for this example. You can find it by
checking out the [prepared example project] from the Oasis SDK repository. It
contains a simple [Oracle.sol] contract which collects observations from
authenticated ROFL app instances, performs trivial aggregation and stores the
final aggregated result. See the [Sapphire quickstart] chapter for more details
on building and deploying Sapphire smart contracts.

Configure the `PRIVATE_KEY` of the deployment account and the ROFL app
identifier (be sure to use the identifier that you received during
registration), then deploy the contract by running, for example:

```shell
PRIVATE_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" \
npx hardhat deploy rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf --network sapphire-testnet
```

After successful deployment you will see a message like:

```
Oracle for ROFL app rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf deployed to 0x5FbDB2315678afecb367f032d93F642f64180aa3
```

You can now proceed to building and deploying the ROFL app itself. Remember the
address where the oracle contract was deployed to as you will need it in the
next step.

<!-- markdownlint-disable line-length -->
[Oracle.sol]: https://github.com/oasisprotocol/oasis-sdk/blob/main/examples/runtime-sdk/rofl-oracle/oracle/contracts/Oracle.sol
[prepared example project]: https://github.com/oasisprotocol/oasis-sdk/tree/main/examples/runtime-sdk/rofl-oracle/oracle
[Sapphire quickstart]: https://github.com/oasisprotocol/sapphire-paratime/blob/main/docs/quickstart.mdx
<!-- markdownlint-enable line-length -->

## Configuring the Oracle Contract Address

Back in the definition of the ROFL app you will need to specify the address of
the oracle contract you deployed in the previous step. To do this, simply update
the value of the `ORACLE_CONTRACT_ADDRESS` constant defined at the top of
[`src/main.rs`] file:

<!-- markdownlint-disable line-length -->
![code rust](../../examples/runtime-sdk/rofl-oracle/src/main.rs#oracle-contract-address)
<!-- markdownlint-enable line-length -->

Make sure to use the contract address as output by the deployment script in the
previous step.

## Building the ROFL App

To build the ROFL app without hassle use the helpers provided by the Oasis CLI:

```shell
oasis rofl build sgx --network testnet --paratime sapphire
```

This will build the required app binaries using `cargo` and bundle them into
the Oasis Runtime Container (ORC) format suitable for deployment to Oasis nodes.
By default, the resulting file will be called `rofl-oracle.orc`.

:::info Reproducibility

For audit reasons it is very important that ROFL app binaries can be reproduced
from the given source code. This makes it possible to check that the right code
is actually deployed. In order to support reproducible builds, please see the
[Reproducibility chapter].

[Reproducibility chapter]: https://github.com/oasisprotocol/oasis-sdk/blob/main/docs/runtime/reproducibility.md

:::

## Updating the ROFL App Policy

Now that the app binaries are available, we need to update the policy with the
correct cryptographic identity of the app. To obtain the identity of the app
that was just built run:

```shell
oasis rofl identity rofl-oracle.orc
```

This should output something like the following:

```
0+tTmlVjUvP0eIHXH7Dld3svPppCUdKDwYxnzplndLea/8+uR7hI7CyvHEm0soNTHhzEJfk1grNoBuUqQ9eNGg==
```

This represents the cryptographic identity of the ROFL app. We now need to
update the policy to ensure that only exact instances of the built app can
successfully authenticate under our app ID. To do so, update the previously
generated `policy.yml` as follows (using your own app identity):

<!-- markdownlint-disable line-length -->
![code yaml {11-12}](../../examples/runtime-sdk/rofl-oracle/policy2.yml "policy.yml")
<!-- markdownlint-enable line-length -->

Then to update the on-chain policy, run (using _your own app identifier_ instead
of the placeholder `rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf`):

```shell
oasis rofl update rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf \
--policy policy.yml \
--admin self \
--network testnet \
--paratime sapphire
```

:::info

For those interested in the details, the specified cryptographic identity is
actually based on the MRENCLAVE and MRSIGNER pair used in Intel SGX remote
attestation. Because the ROFL protocols only use MRENCLAVE for authentication,
a random signer key is generated during build and used to sign the enclave.

:::

## Deploying the ROFL App

ROFL apps are deployed through Oasis nodes running on systems that support the
targeted TEE (e.g. Intel SGX). If you don't have a running node where you could
deploy your ROFL app, please first make sure that you have a client node with
the Sapphire Testnet runtime configured (see the [client node documentation] for
instructions on setting one up). Note that you need at least version 24.2 of
Oasis Core.

<!-- TODO: Make it really simple to spin up new nodes using checkpoints. -->
<!-- TODO: Include some reasonable pruning defaults. -->

After your node is set up, update the `runtime` section in your configuration
as follows:

```yaml
runtime:
environment: sgx # Required to ensure runtimes run in a TEE.
sgx_loader: /node/bin/oasis-core-runtime-loader
paths:
- /node/runtime/sapphire-paratime.orc
- /node/runtime/rofl-oracle.orc
```

Note the appropriate paths to both the latest Sapphire Testnet runtime and the
ROFL app bundle. Before proceeding with the rest of the chapter, please make
sure that the node is fully synchronized with Sapphire Testnet.

The node will also need to cover any transaction fees that are required to
maintain registration of the ROFL app. First, determine the address of the node
you are connecting to by running the following:

```shell
oasis-node identity show-address -a unix:/node/data/internal.sock
```

This should output an address like the following:

```
oasis1qp6tl30ljsrrqnw2awxxu2mtxk0qxyy2nymtsy90
```

You can then [transfer some tokens] to this address on Sapphire Testnet to make
sure it will have funds to pay for registration fees:

```shell
oasis account transfer 10 oasis1qp6tl30ljsrrqnw2awxxu2mtxk0qxyy2nymtsy90 \
--network testnet --paratime sapphire
```

[client node documentation]: https://github.com/oasisprotocol/docs/blob/main/docs/node/run-your-node/paratime-node.mdx
[transfer some tokens]: https://github.com/oasisprotocol/cli/blob/master/docs/account.md#transfer

## Checking That the ROFL App is Running

In order to check that the ROFL app is running and has successfully registered
on chain, you can use the following command (using _your own app identifier_
instead of the placeholder `rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf`):

```shell
oasis rofl show rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf \
--network testnet --paratime sapphire
```

This will output some information about the registered ROFL app, its policy and
its currently live instances:

```
App ID: rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf
Admin: oasis1qrydpazemvuwtnp3efm7vmfvg3tde044qg6cxwzx
Staked amount: 10000.0
Policy:
{
"quotes": {
"pcs": {
"tcb_validity_period": 30,
"min_tcb_evaluation_data_number": 16
}
},
"enclaves": [
"0+tTmlVjUvP0eIHXH7Dld3svPppCUdKDwYxnzplndLea/8+uR7hI7CyvHEm0soNTHhzEJfk1grNoBuUqQ9eNGg=="
],
"endorsements": [
{
"any": {}
}
],
"fees": 2,
"max_expiration": 3
}

=== Instances ===
- RAK: AQhV3X660/+bR8REaWYkZNR6eAysFShylhe+7Ph00PM=
Node ID: DbeoxcRwDO4Wh8bwq5rAR7wzhiB+LeYn+y7lFSGAZ7I=
Expiration: 9
```

Here you can see that a single instance of the ROFL app is running on the given
node, its public runtime attestation key (RAK) and the epoch at which its
registration will expire if not refreshed. ROFL apps must periodically refresh
their registrations to ensure they don't expire.

## Checking That the Oracle is Getting Updated

In order to check that the oracle is working, you can use the prepared
`oracle-query` task in the Hardhat project. Simply run:

```shell
npx hardhat oracle-query 0x5FbDB2315678afecb367f032d93F642f64180aa3 --network sapphire-testnet
```

And you should get an output like the following:

```
Using oracle contract deployed at 0x5FbDB2315678afecb367f032d93F642f64180aa3
ROFL app: rofl1qqn9xndja7e2pnxhttktmecvwzz0yqwxsquqyxdf
Threshold: 1
Last observation: 63990
Last update at: 656
```

That's it! Your first ROFL oracle is running!
18 changes: 17 additions & 1 deletion docs/rofl/prerequisites.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ file to your project's top-level directory as well.
To install the appropriate version of the Rust toolchain, make sure you are
in the project directory and run:

```
```shell
rustup show
```

Expand All @@ -77,13 +77,29 @@ nightly-2022-08-22-x86_64-unknown-linux-gnu (overridden by '/code/rust-toolchain
rustc 1.65.0-nightly (c0941dfb5 2022-08-21)
```

For testing ROFL binaries on Sapphire Localnet, the binaries should be compiled
for [MUSL C standard library]. You will need to add the following target to your
rust environment:

```shell
rustup target add x86_64-unknown-linux-musl
```

Additionally, you will need the MUSL wrapper for gcc. On Ubuntu/Debian systems,
you can install it by running:

```shell
sudo apt install musl-tools
```

<!-- markdownlint-disable line-length -->
[rustup]: https://rustup.rs/
[rust-upstream-rustup]: https://www.rust-lang.org/tools/install
[download `rustup-init` executable for your platform]: https://rust-lang.github.io/rustup/installation/other.html
[Rust]: https://www.rust-lang.org/
[`rust-toolchain.toml`]: https://github.com/oasisprotocol/oasis-sdk/tree/main/rust-toolchain.toml
[rust-toolchain-precedence]: https://github.com/rust-lang/rustup/blob/master/README.md#override-precedence
[MUSL C standard library]: https://musl.libc.org/
<!-- markdownlint-enable line-length -->

## SGXS Utilities
Expand Down
Loading
Loading