forge_wallet 0.1.3

The rust language implementation of forge_wallet
Documentation
#[macro_use]
extern crate failure;

#[macro_use]
extern crate serde_derive;

extern crate forge_did;
extern crate forge_signer;

extern crate base58;
extern crate serde;
extern crate serde_json;

use failure::Error;
use serde_json::Value;

pub type Result<T> = ::std::result::Result<T, Error>;

pub type WalletType = forge_did::create_did::DidType;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Wallet {
    pub w_type: WalletType,
    pub sk: Vec<u8>,
    pub pk: Vec<u8>,
    pub address: String,
}

impl Wallet {
    pub fn create_default_wallet() -> Result<Wallet> {
        Self::from_wallet_type(&WalletType::default())
    }

    pub fn from_wallet_type(w_type: &WalletType) -> Result<Wallet> {
        let (sk, pk) = forge_signer::get_key_pair(w_type.key_type);
        let address = forge_did::get_did_by_pk(&pk, &Some(w_type.to_owned()))?;
        Ok(Wallet {
            w_type: w_type.to_owned(),
            sk,
            pk,
            address: forge_did::create_did::get_pure_address(&address),
        })
    }

    pub fn from_address(addr: &str) -> Result<Wallet> {
        let did_type = forge_did::create_did::DidType::from_address(addr)?;

        Ok(Wallet {
            address: addr.to_owned(),
            w_type: did_type,
            ..Default::default()
        })
    }

    pub fn from_pk(pk: &[u8], w_type: &WalletType) -> Result<Wallet> {
        let address = forge_did::get_did_by_pk(pk, &Some(w_type.to_owned()))?;
        Ok(Wallet {
            w_type: w_type.to_owned(),
            sk: Vec::new(),
            pk: pk.to_owned(),
            address: forge_did::create_did::get_pure_address(&address),
        })
    }

    pub fn from_sk(sk: &[u8], w_type: &WalletType) -> Result<Wallet> {
        let pk = forge_signer::get_pk_by_sk(sk, &w_type.key_type);
        let mut wallet = Self::from_pk(&pk, w_type)?;
        wallet.sk = sk.to_owned();
        Ok(wallet)
    }

    pub fn to_json(&self) -> Result<Value> {
        let j = serde_json::to_value(self)?;
        Ok(j)
    }

    /// all Wallet fields must be in value, serde_json::from_value will failed if lost some fields.
    pub fn from_json(j: Value) -> Result<Wallet> {
        let wallet: Wallet = match serde_json::from_value(j) {
            Ok(w) => w,
            Err(e) => bail!("wallet from json failed, err {:?}", e),
        };

        Ok(wallet)
    }

    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool> {
        if self.pk.is_empty() {
            bail!("verify failed as pk is empty");
        }
        let ret = forge_signer::verify(&self.pk, message, signature, self.w_type.key_type);

        Ok(ret)
    }

    pub fn hash(&self, message: &[u8]) -> Result<Vec<u8>> {
        use forge_did::create_did::HashType;
        use forge_hasher::{hash, HashLen};

        match self.w_type.hash_type {
            Some(HashType::Keccak) => {
                hash(message, Some(forge_hasher::HashType::Keccak), None, None)
            }
            Some(HashType::Sha3) | None => {
                hash(message, Some(forge_hasher::HashType::Sha3), None, None)
            }
            Some(HashType::Sha2) => hash(message, Some(forge_hasher::HashType::Sha2), None, None),
            Some(HashType::Keccak384) => hash(
                message,
                Some(forge_hasher::HashType::Keccak),
                Some(HashLen::Len384),
                None,
            ),
            Some(HashType::Sha3_384) => hash(
                message,
                Some(forge_hasher::HashType::Sha3),
                Some(HashLen::Len384),
                None,
            ),
            Some(HashType::Keccak512) => hash(
                message,
                Some(forge_hasher::HashType::Keccak),
                Some(HashLen::Len512),
                None,
            ),
            Some(HashType::Sha3_512) => hash(
                message,
                Some(forge_hasher::HashType::Sha3),
                Some(HashLen::Len512),
                None,
            ),
        }
    }

    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>> {
        if self.pk.is_empty() {
            bail!("sign failed as sk is empty");
        }
        let ret = forge_signer::sign(&self.sk, message, self.w_type.key_type);
        Ok(ret)
    }

    /// add lost fields
    pub fn format_wallet(&mut self) -> Result<()> {
        self.address = forge_did::create_did::get_pure_address(&self.address);

        if !self.sk.is_empty() && !self.pk.is_empty() && !self.address.is_empty()
        // && wallet.w_type.is_empty()
        {
            return Ok(());
        }
        if !self.sk.is_empty() {
            let new_wallet = Self::from_sk(&self.sk, &self.w_type)?;
            self.pk = new_wallet.pk;
            self.address = new_wallet.address;
        } else if !self.pk.is_empty() {
            self.address = Self::from_pk(&self.pk, &self.w_type)?.address;
        }
        Ok(())
    }

    /// wallet is valid or not?
    /// did_type valid?
    /// sk, pk match?
    /// address, pk match?
    /// address, did_type match?
    pub fn is_valid(wallet: &Wallet) -> bool {
        //TODO: wallet valid check?
        if !wallet.address.is_empty() {
            match forge_did::create_did::DidType::from_address(&wallet.address) {
                Ok(t) => {
                    if t != wallet.w_type {
                        return false;
                    }
                }
                Err(_) => return false,
            };
        }

        false
    }
}

#[cfg(test)]
mod test {
    use super::forge_did::create_did::{HashType, KeyType, RoleType};
    use super::WalletType;

    #[test]
    fn test_wallet() {
        let w_type = WalletType {
            role_type: Some(RoleType::Application),
            key_type: Some(KeyType::Ed25519),
            hash_type: Some(HashType::Sha3),
        };
        let sk_str  ="D67C071B6F51D2B61180B9B1AA9BE0DD0704619F0E30453AB4A592B036EDE644E4852B7091317E3622068E62A5127D1FB0D4AE2FC50213295E10652D2F0ABFC7";
        let sk = get_hex_from_str(sk_str);
        let wallet = super::Wallet::from_sk(&sk, &w_type).unwrap();

        let expect_address = "zNKtCNqYWLYWYW3gWRA1vnRykfCBZYHZvzKr".to_owned();
        assert_eq!(wallet.address, expect_address);

        let w_j = wallet.to_json().unwrap();
        let expect_j = super::Wallet::from_json(w_j).unwrap();
        assert_eq!(wallet, expect_j);
        {
            let w = super::Wallet {
                address: "abcd".to_owned(),
                sk: vec![1, 2, 3],
                ..Default::default()
            };

            let w_j = w.to_json().unwrap();
            let w_s = super::Wallet::from_json(w_j).unwrap();
            assert_eq!(w, w_s);
        }

        let message = b"hello forge rust sdk";
        let sig = wallet.sign(message).unwrap();
        assert!(wallet.verify(message, &sig).unwrap());
    }

    fn get_hex_from_str(s: &str) -> Vec<u8> {
        fn char_to_hex(ch: &char) -> u8 {
            return match ch {
                '0' => 0,
                '1' => 1,
                '2' => 2,
                '3' => 3,
                '4' => 4,
                '5' => 5,
                '6' => 6,
                '7' => 7,
                '8' => 8,
                '9' => 9,
                'A' => 10,
                'B' => 11,
                'C' => 12,
                'D' => 13,
                'E' => 14,
                'F' => 15,
                _ => unreachable!(),
            };
        }
        let mut ret = Vec::new();
        let len = s.len();
        if len < 1 {
            return ret;
        }

        let upper_s = s.to_uppercase();
        let mut tmp = 0u8;
        let mut count = 0;
        for ch in upper_s.chars() {
            if count & 0x1 != 0 {
                let high = (tmp & 0xF) << 4;
                tmp = high + char_to_hex(&ch);
                ret.push(tmp);
            } else {
                tmp = char_to_hex(&ch);
            }
            count += 1;
        }
        ret
    }
}