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
143
144
145
146
147
148
149
150
151
152
153
154
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.get_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);
        }
    }
}