casper-engine-grpc-server 0.6.3

Wasm execution engine for Casper smart contracts.
Documentation
use std::{
    collections::BTreeMap,
    convert::{TryFrom, TryInto},
    mem,
};

use casper_execution_engine::shared::account::{Account, ActionThresholds, AssociatedKeys};
use casper_types::account::{AccountHash, Weight};

use super::NamedKeyMap;
use crate::engine_server::{
    mappings::{self, ParsingError},
    state::{self, Account_AssociatedKey, NamedKey},
};

impl From<Account> for state::Account {
    fn from(mut account: Account) -> Self {
        let mut pb_account = state::Account::new();

        pb_account.public_key = account.account_hash().as_bytes().to_vec();

        let named_keys = mem::replace(account.named_keys_mut(), BTreeMap::new());
        let pb_named_keys: Vec<NamedKey> = NamedKeyMap::new(named_keys).into();
        pb_account.set_named_keys(pb_named_keys.into());

        pb_account.set_main_purse(account.main_purse().into());

        let associated_keys: Vec<Account_AssociatedKey> =
            account.associated_keys().map(Into::into).collect();
        pb_account.set_associated_keys(associated_keys.into());

        {
            let deployment = u32::from(account.action_thresholds().deployment().value());
            let key_management = u32::from(account.action_thresholds().key_management().value());
            let pb_action_thresholds = pb_account.mut_action_thresholds();
            pb_action_thresholds.set_deployment_threshold(deployment);
            pb_action_thresholds.set_key_management_threshold(key_management)
        }

        pb_account
    }
}

impl TryFrom<state::Account> for Account {
    type Error = ParsingError;

    fn try_from(pb_account: state::Account) -> Result<Self, Self::Error> {
        let account_hash =
            mappings::vec_to_array(pb_account.public_key, "Protobuf Account::AccountHash")?;

        let named_keys: NamedKeyMap = pb_account.named_keys.into_vec().try_into()?;

        let main_purse = {
            let pb_uref = pb_account
                .main_purse
                .into_option()
                .ok_or_else(|| ParsingError::from("Protobuf Account missing MainPurse field"))?;
            pb_uref.try_into()?
        };

        let associated_keys = {
            let mut associated_keys = AssociatedKeys::default();
            for pb_associated_key in pb_account.associated_keys.into_vec() {
                let (key, weight) = pb_associated_key.try_into()?;
                associated_keys.add_key(key, weight).map_err(|error| {
                    ParsingError(format!(
                        "Error parsing Protobuf Account::AssociatedKeys: {:?}",
                        error
                    ))
                })?;
            }
            associated_keys
        };

        let action_thresholds = {
            let pb_action_thresholds =
                pb_account.action_thresholds.into_option().ok_or_else(|| {
                    ParsingError::from("Protobuf Account missing ActionThresholds field")
                })?;

            ActionThresholds::new(
                weight_from(
                    pb_action_thresholds.deployment_threshold,
                    "Protobuf DeploymentThreshold",
                )?,
                weight_from(
                    pb_action_thresholds.key_management_threshold,
                    "Protobuf KeyManagementThreshold",
                )?,
            )
            .map_err(ParsingError::from)?
        };

        let account = Account::new(
            AccountHash::new(account_hash),
            named_keys.into_inner(),
            main_purse,
            associated_keys,
            action_thresholds,
        );
        Ok(account)
    }
}

impl From<(&AccountHash, &Weight)> for Account_AssociatedKey {
    fn from((account_hash, weight): (&AccountHash, &Weight)) -> Self {
        let mut pb_associated_key = Account_AssociatedKey::new();
        pb_associated_key.public_key = account_hash.as_bytes().to_vec();
        pb_associated_key.set_weight(weight.value().into());
        pb_associated_key
    }
}

impl TryFrom<Account_AssociatedKey> for (AccountHash, Weight) {
    type Error = ParsingError;

    fn try_from(pb_associated_key: Account_AssociatedKey) -> Result<Self, Self::Error> {
        let account_hash = AccountHash::new(mappings::vec_to_array(
            pb_associated_key.public_key,
            "Protobuf Account::AssociatedKey",
        )?);

        let weight = weight_from(pb_associated_key.weight, "Protobuf AssociatedKey::Weight")?;

        Ok((account_hash, weight))
    }
}

fn weight_from(value: u32, value_name: &str) -> Result<Weight, ParsingError> {
    let weight = u8::try_from(value).map_err(|_| {
        ParsingError(format!(
            "Unable to convert {} to u8 while parsing {}",
            value, value_name
        ))
    })?;
    Ok(Weight::new(weight))
}

#[cfg(test)]
mod tests {
    use proptest::proptest;

    use casper_execution_engine::shared::account::gens;

    use super::*;
    use crate::engine_server::mappings::test_utils;

    proptest! {
        #[test]
        fn round_trip(account in gens::account_arb()) {
            test_utils::protobuf_round_trip::<Account, state::Account>(account);
        }
    }
}