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
//! Ceres Runtime
use crate::{method::InkMethod, Error, Result};
use ceres_executor::{Executor, Memory};
use ceres_sandbox::{RuntimeInterfaces, Sandbox, Transaction};
use ceres_std::{Rc, String, ToString, Vec};
use ceres_support::{
    convert, traits,
    types::{self, Metadata},
};
use core::cell::RefCell;

/// Ceres Runtime
pub struct Runtime {
    pub sandbox: Sandbox,
    pub metadata: Metadata,
    pub cache: Rc<RefCell<dyn traits::Frame<Memory>>>,
}

impl Runtime {
    /// Create runtime from contract
    pub fn contract(contract: &[u8], ri: Option<impl RuntimeInterfaces>) -> Result<Runtime> {
        let meta = serde_json::from_str::<Metadata>(&String::from_utf8_lossy(contract))
            .map_err(|_| Error::DecodeContractFailed)?;

        Self::new(meta, types::Cache::default(), ri)
    }

    /// Create runtime from contract
    pub fn from_contract(
        contract: &[u8],
        cache: impl traits::Frame<Memory> + 'static,
        ri: Option<impl RuntimeInterfaces>,
    ) -> Result<Runtime> {
        let meta = serde_json::from_slice::<Metadata>(&contract)
            .map_err(|_| Error::DecodeContractFailed)?;

        Self::new(meta, cache, ri)
    }

    /// Create runtime from metadata and storage
    pub fn from_metadata(
        meta: Metadata,
        cache: impl traits::Frame<Memory> + 'static,
        ri: Option<impl RuntimeInterfaces>,
    ) -> Result<Runtime> {
        Self::new(meta, cache, ri)
    }

    /// Load contract to cache
    pub fn load(&mut self, b: &[u8]) -> Result<[u8; 32]> {
        self.load_metadata(
            &serde_json::from_slice::<Metadata>(b).map_err(|_| Error::DecodeContractFailed)?,
        )
    }

    /// Load metadata to cache
    pub fn load_metadata(&mut self, meta: &Metadata) -> Result<[u8; 32]> {
        Ok(self.sandbox.load_metadata(meta)?)
    }

    /// New runtime
    pub fn new(
        metadata: Metadata,
        cache: impl traits::Frame<Memory> + 'static,
        ri: Option<impl RuntimeInterfaces>,
    ) -> Result<Runtime> {
        // generate seal calls
        let seal_calls = ceres_seal::pallet_contracts(ri);

        // wrap cache
        let cache = Rc::new(RefCell::new(cache));

        // Create Sandbox and Builder
        let mut sandbox = Sandbox::new(cache.clone(), seal_calls.clone());
        let code_hash = sandbox.load_metadata(&metadata)?;
        sandbox.prepare(code_hash)?;

        // Construct runtime
        Ok(Runtime {
            sandbox,
            metadata,
            cache,
        })
    }

    /// Deploy contract
    pub fn deploy(
        &mut self,
        method: &str,
        args: Vec<Vec<u8>>,
        tx: Option<Transaction>,
    ) -> Result<Option<Vec<u8>>> {
        self.invoke(InkMethod::Deploy, method, args, tx)
    }

    /// Call contract
    pub fn call(
        &mut self,
        method: &str,
        args: Vec<Vec<u8>>,
        tx: Option<Transaction>,
    ) -> Result<Option<Vec<u8>>> {
        self.invoke(InkMethod::Call, method, args, tx)
    }

    // Invoke (ink) method
    pub fn invoke(
        &mut self,
        method: InkMethod,
        inner_method: &str,
        args: Vec<Vec<u8>>,
        tx: Option<Transaction>,
    ) -> Result<Option<Vec<u8>>> {
        // construct transaction
        if let Some(tx) = tx {
            self.sandbox.tx = tx;
        }

        // set input
        self.sandbox.input = Some(method.parse(&self.metadata, inner_method, args)?);

        // execute
        let hash = self
            .cache
            .borrow()
            .active()
            .ok_or(ceres_executor::Error::CodeNotFound)?;
        Executor::new(
            convert::to_storage_key(&hash[..]).ok_or(ceres_executor::Error::CodeNotFound)?,
            &mut self.sandbox,
        )?
        .invoke(&method.to_string(), &[], &mut self.sandbox)
        .map_err(|error| Error::CallContractFailed { error })?;

        // flush data
        self.cache
            .borrow_mut()
            .flush()
            .ok_or(Error::FlushDataFailed)?;
        Ok(self.sandbox.ret.take())
    }
}