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
//! Storage implementation
use ceres_runtime::{Error, Metadata, Result, Runtime};
use ceres_std::BTreeMap;
use etc::{Etc, FileSystem, Meta};
use sled::Db;
use std::{cell::RefCell, fs, path::PathBuf, process, rc::Rc};

/// A ceres storage implementation using sled
#[derive(Clone)]
pub struct Storage(pub Db);

impl Storage {
    fn quit() {
        println!(
            "The following required arguments were not provided: \n\t\t\
             <*.contract | name | code-hash>"
        );
        process::exit(1);
    }

    /// New storage
    pub fn new() -> crate::Result<Self> {
        let etc =
            Etc::new(&dirs::home_dir().ok_or(crate::Error::Custom("Could not find home dir"))?)?;

        let storage = Storage(sled::open(etc.open(".ceres/contracts")?.real_path()?)?);
        Ok(storage)
    }

    /// Contract instance
    ///
    /// * From path of `*.contract`
    /// * From name of `*.contract`
    /// * From code_hash of `*.contract`
    pub fn rt(&mut self, contract: &str) -> crate::Result<Runtime> {
        let if_path = PathBuf::from(contract);
        let storage = Rc::new(RefCell::new(self.clone()));
        Ok(if if_path.exists() {
            let source = fs::read(if_path)?;
            let rt = Runtime::from_contract_and_storage(&source, storage)?;
            self.0.insert(
                &rt.metadata.contract.name,
                bincode::serialize(&rt.metadata.clone())?,
            )?;
            rt
        } else if let Ok(Some(contract)) = if contract.is_empty() {
            let mut recent = None;
            for c in self.0.iter() {
                let (k, v) = c?;
                if k.len() != 32 {
                    recent = Some(Ok(Some(v)));
                    break;
                }
            }

            if let Some(r) = recent {
                r
            } else {
                return Err(crate::Error::ParseContractFailed(
                    "Get recent contract failed".to_string(),
                ));
            }
        } else {
            self.0.get(contract.as_bytes())
        } {
            Runtime::from_metadata_and_storage(
                bincode::deserialize::<Metadata>(&contract)?,
                storage,
            )?
        } else {
            Self::quit();

            // NOTE:
            //
            // Unreachable error
            return Err(crate::Error::ParseContractFailed(contract.to_string()));
        })
    }
}

impl ceres_runtime::Storage for Storage {
    fn set(&mut self, code_hash: [u8; 32], data: BTreeMap<[u8; 32], Vec<u8>>) -> Result<()> {
        self.0
            .insert(
                &code_hash,
                bincode::serialize(&data).map_err(|_| Error::Custom {
                    err: "Serialize failed",
                })?,
            )
            .map_err(|_| Error::InsertContractFailed)?;
        Ok(())
    }

    fn get(&self, code_hash: [u8; 32]) -> Option<BTreeMap<[u8; 32], Vec<u8>>> {
        bincode::deserialize(&self.0.get(&code_hash).ok()??).ok()
    }

    fn new_state(&self) -> BTreeMap<[u8; 32], Vec<u8>> {
        BTreeMap::new()
    }
}