1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Wrapper to make development of ROFL components easier.
use std::{collections::BTreeMap, sync::Arc};

use anyhow::Result;
use async_trait::async_trait;
use base64::prelude::*;
use tokio::sync::mpsc;

use crate::{
    core::{
        app,
        common::version,
        config::Config,
        consensus::{roothash, verifier::TrustRoot},
        dispatcher::{PostInitState, PreInitState},
        start_runtime,
    },
    crypto,
    types::transaction,
};

pub mod client;
mod env;
pub mod init;
mod notifier;
pub mod prelude;
mod processor;
mod registration;

pub use crate::modules::rofl::app_id::AppId;
pub use client::Client;
pub use env::Environment;

/// ROFL component application.
#[allow(unused_variables)]
#[async_trait]
pub trait App: Send + Sync + 'static {
    /// ROFL application version.
    const VERSION: version::Version;

    /// Identifier of the application (used for registrations).
    fn id() -> AppId {
        // By default we fetch the application identifier from the build-time environment.
        #[allow(clippy::option_env_unwrap)]
        AppId::from_bech32(
            option_env!("ROFL_APP_ID").expect("Override App::id or specify ROFL_APP_ID."),
        )
        .expect("Corrupted ROFL_APP_ID (must be Bech32-encoded ROFL app ID).")
    }

    /// Return the consensus layer trust root for this runtime; if `None`, consensus layer integrity
    /// verification will not be performed.
    fn consensus_trust_root() -> Option<TrustRoot> {
        // By default we fetch the trust root from the build-time environment.
        option_env!("ROFL_CONSENSUS_TRUST_ROOT").map(|raw_trust_root| {
            // Parse from base64-encoded CBOR.
            cbor::from_slice(
                &BASE64_STANDARD
                    .decode(raw_trust_root)
                    .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR)."),
            )
            .expect("Corrupted ROFL_CONSENSUS_TRUST_ROOT (must be Base64-encoded CBOR).")
        })
    }

    /// Create a new unsigned transaction.
    fn new_transaction<B>(&self, method: &str, body: B) -> transaction::Transaction
    where
        B: cbor::Encode,
    {
        let mut tx = transaction::Transaction::new(method, body);
        // Make the ROFL module resolve the payer for all of our transactions.
        tx.set_fee_proxy("rofl", Self::id().as_ref());
        tx
    }

    /// Fetches custom app instance metadata that is included in its on-chain registration.
    ///
    /// This method is called before each registration refresh. Returning an error will not block
    /// registration, rather it will result in the metadata being cleared.
    async fn get_metadata(
        self: Arc<Self>,
        env: Environment<Self>,
    ) -> Result<BTreeMap<String, String>>
    where
        Self: Sized,
    {
        Ok(BTreeMap::new())
    }

    /// Custom post-registration initialization. It runs before any image-specific scripts are
    /// called by the runtime so it can be used to do things like set up custom storage after
    /// successful registration.
    ///
    /// Until this function completes, no further initialization will happen.
    async fn post_registration_init(self: Arc<Self>, env: Environment<Self>)
    where
        Self: Sized,
    {
        // Default implementation just runs the trivial initialization.
        init::post_registration_init();
    }

    /// Main application processing loop.
    async fn run(self: Arc<Self>, env: Environment<Self>)
    where
        Self: Sized,
    {
        // Default implementation does nothing.
    }

    /// Logic that runs on each runtime block. Only one of these will run concurrently.
    async fn on_runtime_block(self: Arc<Self>, env: Environment<Self>, round: u64)
    where
        Self: Sized,
    {
        // Default implementation does nothing.
    }

    /// Start the application.
    fn start(self)
    where
        Self: Sized,
    {
        start_runtime(
            Box::new(|state: PreInitState<'_>| -> PostInitState {
                // Fetch host information and configure domain separation context.
                let hi = state.protocol.get_host_info();
                crypto::signature::context::set_chain_context(
                    hi.runtime_id,
                    &hi.consensus_chain_context,
                );

                PostInitState {
                    app: Some(Box::new(AppWrapper::new(self, &state))),
                    ..Default::default()
                }
            }),
            Config {
                version: Self::VERSION,
                trust_root: Self::consensus_trust_root(),
                ..Default::default()
            },
        );
    }
}

struct AppWrapper {
    cmdq: mpsc::Sender<processor::Command>,
}

impl AppWrapper {
    fn new<A>(app: A, state: &PreInitState<'_>) -> Self
    where
        A: App,
    {
        Self {
            cmdq: processor::Processor::start(app, state),
        }
    }
}

#[async_trait]
impl app::App for AppWrapper {
    async fn on_runtime_block(&self, blk: &roothash::AnnotatedBlock) -> Result<()> {
        self.cmdq
            .send(processor::Command::ProcessRuntimeBlock(blk.clone()))
            .await?;
        Ok(())
    }

    async fn on_runtime_event(
        &self,
        _blk: &roothash::AnnotatedBlock,
        _tags: &[Vec<u8>],
    ) -> Result<()> {
        Ok(())
    }
}