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
use std::{
    fmt::{Display, Formatter},
    marker::PhantomData,
    sync::Arc,
};

use phronesis_runtime::opaque::SessionKeys;
use parity_scale_codec::{Decode, DecodeAll, Error as DecodeError};
use sc_client_api::Backend;
use sp_application_crypto::key_types::AURA;
use sp_core::twox_128;
use sp_runtime::traits::{Block, OpaqueKeys};

use crate::{
    phron_primitives::{AccountId, PhronSessionApi, AuraId},
    BlockHash, ClientForPhron,
};

/// Trait handling connection between host code and runtime storage
pub trait RuntimeApi {
    type Error: Display;
    /// Returns aura authorities for the next session using state from block `at`
    fn next_aura_authorities(&self, at: BlockHash) -> Result<Vec<AuraId>, Self::Error>;
}

type QueuedKeys = Vec<(AccountId, SessionKeys)>;

#[derive(Clone)]
pub struct RuntimeApiImpl<C, B, BE>
where
    C: ClientForPhron<B, BE> + Send + Sync + 'static,
    C::Api: PhronSessionApi<B>,
    B: Block<Hash = BlockHash>,
    BE: Backend<B> + 'static,
{
    client: Arc<C>,
    _phantom: PhantomData<(B, BE)>,
}

impl<C, B, BE> RuntimeApiImpl<C, B, BE>
where
    C: ClientForPhron<B, BE> + Send + Sync + 'static,
    C::Api: PhronSessionApi<B>,
    B: Block<Hash = BlockHash>,
    BE: Backend<B> + 'static,
{
    pub fn new(client: Arc<C>) -> Self {
        Self {
            client,
            _phantom: PhantomData,
        }
    }

    fn read_storage<D: Decode>(
        &self,
        pallet: &str,
        item: &str,
        at_block: BlockHash,
    ) -> Result<D, ApiError> {
        let storage_key = [twox_128(pallet.as_bytes()), twox_128(item.as_bytes())].concat();

        let encoded = match self
            .client
            .storage(at_block, &sc_client_api::StorageKey(storage_key))
        {
            Ok(Some(e)) => e,
            _ => return Err(ApiError::NoStorage(pallet.to_string(), item.to_string())),
        };

        D::decode_all(&mut encoded.0.as_ref()).map_err(ApiError::DecodeError)
    }
}

#[derive(Clone, Debug)]
pub enum ApiError {
    NoStorage(String, String),
    DecodeError(DecodeError),
}

impl Display for ApiError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
        match self {
            ApiError::NoStorage(pallet, item) => write!(f, "no storage under {}.{}", pallet, item),
            ApiError::DecodeError(error) => write!(f, "decode error: {:?}", error),
        }
    }
}

impl<C, B, BE> RuntimeApi for RuntimeApiImpl<C, B, BE>
where
    C: ClientForPhron<B, BE> + Send + Sync + 'static,
    C::Api: PhronSessionApi<B>,
    B: Block<Hash = BlockHash>,
    BE: Backend<B> + 'static,
{
    type Error = ApiError;

    fn next_aura_authorities(&self, at: BlockHash) -> Result<Vec<AuraId>, Self::Error> {
        if let Ok(authorities) = self.client.runtime_api().next_session_aura_authorities(at) {
            return Ok(authorities);
        }

        let queued_keys: QueuedKeys = self.read_storage("Session", "QueuedKeys", at)?;

        Ok(queued_keys
            .into_iter()
            .filter_map(|(_, keys)| keys.get(AURA))
            .collect())
    }
}