use std::convert::From;
use std::result::Result;
use std::error::Error;
use std::{fmt, str};
use std::str::FromStr;
use rust_base58::{ToBase58, FromBase58};
use rust_base58::base58::FromBase58Error;
#[cfg(feature = "json-types")]
use rustc_serialize::json;
pub const WALLET_ADDRESS_LEN: usize = 7;
#[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Clone, Copy, RustcEncodable, RustcDecodable)]
pub struct WalletAddress {
address: [u8; WALLET_ADDRESS_LEN],
}
impl WalletAddress {
pub fn from_data(addr: [u8; WALLET_ADDRESS_LEN]) -> WalletAddress {
assert_eq!(addr[0],
0x00,
"the provided address is not a correct Fractal Global wallet address, its \
first byt should be 0x00");
WalletAddress { address: addr }
}
pub fn get_raw(&self) -> &[u8] {
&self.address
}
}
impl From<[u8; WALLET_ADDRESS_LEN]> for WalletAddress {
fn from(other: [u8; WALLET_ADDRESS_LEN]) -> WalletAddress {
WalletAddress { address: other }
}
}
impl FromStr for WalletAddress {
type Err = WalletAddressParseError;
fn from_str(s: &str) -> Result<WalletAddress, WalletAddressParseError> {
if &s[0..2] != "fr" {
return Err(WalletAddressParseError::new(s,
"the address does not start with \"fr\"",
None));
}
let bytes = match s[2..].from_base58() {
Ok(b) => b,
Err(FromBase58Error::InvalidBase58Byte(c, i)) => {
let new_error = FromBase58Error::InvalidBase58Byte(c, i + 2);
return Err(WalletAddressParseError::new(s,
&format!("the address is not a valid \
base-58 encoded string: {}",
new_error),
Some(new_error)));
}
};
if bytes[0] != 0x00 {
return Err(WalletAddressParseError::new(s,
"the first byte of the address is not 0x00",
None));
}
let mut checksum = [0u8; 2];
for byte in &bytes[..WALLET_ADDRESS_LEN] {
checksum[0] ^= *byte;
checksum[1] ^= checksum[0];
}
if checksum[0] != bytes[WALLET_ADDRESS_LEN] ||
checksum[1] != bytes[WALLET_ADDRESS_LEN + 1] {
Err(WalletAddressParseError::new(s, "checksum fail", None))
} else {
let mut address = [0u8; WALLET_ADDRESS_LEN];
address.clone_from_slice(&bytes[..WALLET_ADDRESS_LEN]);
Ok(WalletAddress::from_data(address))
}
}
}
#[cfg(feature = "json-types")]
impl json::ToJson for WalletAddress {
fn to_json(&self) -> json::Json {
json::Json::String(format!("{}", self))
}
}
impl fmt::Display for WalletAddress {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut arr = [0u8; WALLET_ADDRESS_LEN + 2];
arr[0..WALLET_ADDRESS_LEN].clone_from_slice(&self.address);
for byte in &self.address {
arr[WALLET_ADDRESS_LEN] ^= *byte;
arr[WALLET_ADDRESS_LEN + 1] ^= arr[WALLET_ADDRESS_LEN];
}
write!(f, "fr{}", arr.to_base58())
}
}
#[derive(Debug)]
pub struct WalletAddressParseError {
description: String,
cause: Option<FromBase58Error>,
}
impl WalletAddressParseError {
fn new<S: AsRef<str>>(wallet_address: S,
error: S,
cause: Option<FromBase58Error>)
-> WalletAddressParseError {
WalletAddressParseError {
description: format!("the wallet address {:?} is not a valid Fractal Global wallet \
address, {}",
wallet_address.as_ref(),
error.as_ref()),
cause: cause,
}
}
}
impl fmt::Display for WalletAddressParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.description)
}
}
impl Error for WalletAddressParseError {
fn description(&self) -> &str {
&self.description
}
fn cause(&self) -> Option<&Error> {
None
}
}