waves-rust 0.2.6

A Rust library for interacting with the Waves blockchain. Supports node interaction, offline transaction signing and creating addresses and keys.
Documentation
use crate::error::Error::InvalidBytesLength;
use crate::error::{Error, Result};
use crate::model::account::Address;
use crate::model::ByteString;
use crate::util::Base58;
use std::fmt;

#[derive(Clone, Eq, PartialEq, Hash)]
pub struct PublicKey {
    bytes: Vec<u8>,
}

impl fmt::Debug for PublicKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "PublicKey {{ {} }}", self.encoded())
    }
}

impl std::str::FromStr for PublicKey {
    type Err = Error;

    fn from_str(base58string: &str) -> Result<PublicKey> {
        let bytes = Base58::decode(base58string)?;
        Ok(PublicKey { bytes })
    }
}

impl PublicKey {
    pub fn from_bytes(bytes: &[u8]) -> Result<PublicKey> {
        if bytes.len() != 32 {
            Err(InvalidBytesLength {
                expected_len: 32,
                actual_len: bytes.len(),
            })?;
        }
        Ok(PublicKey {
            bytes: Vec::from(bytes),
        })
    }

    pub fn from_string(base58string: &str) -> Result<PublicKey> {
        let bytes = Base58::decode(base58string)?;
        Ok(PublicKey { bytes })
    }

    pub fn address(&self, chain_id: u8) -> Result<Address> {
        Address::from_public_key(chain_id, self)
    }
}

impl ByteString for PublicKey {
    fn bytes(&self) -> Vec<u8> {
        self.bytes.clone()
    }

    fn encoded(&self) -> String {
        Base58::encode(&self.bytes, false)
    }

    fn encoded_with_prefix(&self) -> String {
        Base58::encode(&self.bytes, true)
    }
}

impl TryFrom<&str> for PublicKey {
    type Error = Error;

    fn try_from(value: &str) -> Result<Self> {
        PublicKey::from_string(value)
    }
}

impl TryFrom<String> for PublicKey {
    type Error = Error;

    fn try_from(value: String) -> Result<Self> {
        PublicKey::from_string(&value)
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use crate::error::Error::InvalidBytesLength;
    use crate::error::Result;
    use crate::model::account::{PrivateKey, PublicKey};
    use crate::model::ByteString;
    use crate::util::{Base58, Crypto};

    #[test]
    fn test_public_key_from_bytes() -> Result<()> {
        let seed_phrase = "blame vacant regret company chase trip grant funny brisk innocent";

        let expected_public_key_from_nonce_0 = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB";
        let expected_public_key_from_nonce_128 = "DTvCW1nzFr7mHrHkGf1apstRfwPp4yYL19YvjjLEAPBh";
        let expected_public_key_from_nonce_255 = "esjbpqVWSg8iCaPYQA3SoxZo3oUkdRJSi9tKLoqKQoC";

        assert_eq!(
            PublicKey::from_bytes(&public_key_bytes(seed_phrase, 0))?.encoded(),
            expected_public_key_from_nonce_0
        );
        assert_eq!(
            PublicKey::from_bytes(&public_key_bytes(seed_phrase, 128))?.encoded(),
            expected_public_key_from_nonce_128
        );
        assert_eq!(
            PublicKey::from_bytes(&public_key_bytes(seed_phrase, 255))?.encoded(),
            expected_public_key_from_nonce_255
        );
        Ok(())
    }

    fn public_key_bytes(seed_phrase: &str, nonce: u8) -> Vec<u8> {
        let bytes = PrivateKey::from_seed(seed_phrase, nonce)
            .expect("failed to get private ket from seed phrase")
            .bytes();
        println!("{}", Base58::encode(&bytes.to_vec(), false));
        Crypto::get_public_key(&bytes)
    }

    #[test]
    fn test_invalid_bytes_len_for_public_key() -> Result<()> {
        let public_key = PublicKey::from_bytes(&[]);
        match public_key {
            Ok(_) => panic!("expected error"),
            Err(err) => match err {
                InvalidBytesLength { .. } => Ok(()),
                _ => panic!("expected InvalidBytesLength error"),
            },
        }
    }

    #[test]
    fn test_public_key_from_str() -> Result<()> {
        let expected_str = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB";
        let public_key: PublicKey = expected_str.try_into()?;
        assert_eq!(expected_str, public_key.encoded());
        Ok(())
    }

    #[test]
    fn test_public_key_from_string() -> Result<()> {
        let expected_string = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB".to_owned();
        let public_key: PublicKey = expected_string.clone().try_into()?;
        assert_eq!(expected_string, public_key.encoded());
        Ok(())
    }

    #[test]
    fn test_public_key_std_from_str() -> Result<()> {
        let expected_string = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB".to_owned();
        let public_key = PublicKey::from_str(&expected_string)?;
        assert_eq!(expected_string, public_key.encoded());
        Ok(())
    }

    #[test]
    fn test_byte_string_for_public_key() -> Result<()> {
        let public_key: PublicKey = "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB".try_into()?;
        let expected_bytes = vec![
            113, 40, 188, 13, 166, 104, 24, 229, 65, 157, 176, 205, 96, 187, 101, 62, 170, 97, 253,
            32, 117, 73, 107, 139, 119, 67, 237, 157, 117, 22, 27, 36,
        ];
        assert_eq!(expected_bytes, public_key.bytes());
        assert_eq!(
            "8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB",
            public_key.encoded()
        );
        assert_eq!(
            "base58:8cj6YzvQPhSHGvnjupNTW8zrADTT8CMAAd2xTuej84gB",
            public_key.encoded_with_prefix()
        );
        Ok(())
    }
}