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
//! Wrapper to make development of ROFL components easier.
use std::sync::Arc;

use anyhow::Result;
use async_trait::async_trait;
use tokio::sync::mpsc;

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

mod client;
mod env;
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;

    /// 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>;

    /// 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
    }

    /// 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(())
    }
}