Explorar o código

util/address: make Address type

ghassmo %!s(int64=4) %!d(string=hai) anos
pai
achega
bfcd69254c
Modificáronse 4 ficheiros con 134 adicións e 34 borrados
  1. 12 34
      src/crypto/keypair.rs
  2. 6 0
      src/error.rs
  3. 113 0
      src/util/address.rs
  4. 3 0
      src/util/mod.rs

+ 12 - 34
src/crypto/keypair.rs

@@ -7,11 +7,11 @@ use pasta_curves::{
     pallas,
 };
 use rand::RngCore;
-use sha2::Digest;
 
 use crate::{
     crypto::{constants::OrchardFixedBases, util::mod_r_p},
     serial::{Decodable, Encodable, ReadExt, WriteExt},
+    util::Address,
     Error, Result,
 };
 
@@ -24,12 +24,12 @@ pub struct Keypair {
 impl Keypair {
     pub fn new(secret: SecretKey) -> Self {
         let public = PublicKey::from_secret(secret);
-        Keypair { secret, public }
+        Self { secret, public }
     }
 
     pub fn random(mut rng: impl RngCore) -> Self {
         let secret = SecretKey::random(&mut rng);
-        Keypair::new(secret)
+        Self::new(secret)
     }
 }
 
@@ -39,7 +39,7 @@ pub struct SecretKey(pub pallas::Base);
 impl SecretKey {
     pub fn random(mut rng: impl RngCore) -> Self {
         let x = pallas::Base::random(&mut rng);
-        SecretKey(x)
+        Self(x)
     }
 
     pub fn to_bytes(self) -> [u8; 32] {
@@ -53,12 +53,12 @@ pub struct PublicKey(pub pallas::Point);
 impl PublicKey {
     pub fn random(mut rng: impl RngCore) -> Self {
         let p = pallas::Point::random(&mut rng);
-        PublicKey(p)
+        Self(p)
     }
 
     pub fn from_secret(s: SecretKey) -> Self {
         let p = OrchardFixedBases::NullifierK.generator() * mod_r_p(s.0);
-        PublicKey(p)
+        Self(p)
     }
 
     pub fn to_bytes(self) -> [u8; 32] {
@@ -66,34 +66,12 @@ impl PublicKey {
     }
 }
 
-impl std::fmt::Display for PublicKey {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        // sha256
-        let mut hasher = sha2::Sha256::new();
-        hasher.update(self.to_bytes());
-        let hash = hasher.finalize();
-
-        // ripemd160
-        let mut hasher = ripemd160::Ripemd160::new();
-        hasher.update(hash);
-        let mut hash = hasher.finalize().to_vec();
-
-        // add version
-        let mut payload = vec![0x00_u8];
-
-        // add public key hash
-        payload.append(&mut hash);
-
-        // hash the payload + version
-        let mut hasher = sha2::Sha256::new();
-        hasher.update(payload.clone());
-        let payload_hash = hasher.finalize().to_vec();
-
-        payload.append(&mut payload_hash[0..4].to_vec());
-
-        // base58 encoding
-        let address: String = bs58::encode(payload).into_string();
-        write!(f, "{}", address)
+impl From<Address> for PublicKey {
+    fn from(address: Address) -> Self {
+        let mut bytes = [0u8; 32];
+        bytes.copy_from_slice(&address.0[1..33]);
+        let publickey = pallas::Point::from_bytes(&bytes).unwrap();
+        Self(publickey)
     }
 }
 

+ 6 - 0
src/error.rs

@@ -149,6 +149,12 @@ pub enum Error {
     AsyncChannelSenderError,
     #[error(transparent)]
     AsyncChannelReceiverError(#[from] async_channel::RecvError),
+
+    /// Address
+    #[error("Error converting Address to PublicKey")]
+    AddressToPublicKeyError,
+    #[error("Invalid Address")]
+    InvalidAddress,
 }
 
 impl From<zeromq::ZmqError> for Error {

+ 113 - 0
src/util/address.rs

@@ -0,0 +1,113 @@
+use std::io;
+
+use sha2::Digest;
+
+use crate::{
+    crypto::keypair::PublicKey,
+    serial::{Decodable, Encodable, ReadExt, WriteExt},
+    Error, Result,
+};
+
+enum AddressType {
+    Payment = 0,
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct Address(pub [u8; 37]);
+
+impl Address {
+    pub fn from_str(address: String) -> Result<Self> {
+        let bytes = bs58::decode(&address).into_vec();
+        if bytes.is_ok() && Self::is_valid_address(bytes.as_ref().unwrap().clone()) {
+            let mut bytes_arr = [0u8; 37];
+            bytes_arr.copy_from_slice(bytes.unwrap().as_slice());
+            Ok(Self(bytes_arr))
+        } else {
+            return Err(Error::InvalidAddress)
+        }
+    }
+
+    fn is_valid_address(address: Vec<u8>) -> bool {
+        if address.starts_with(&[AddressType::Payment as u8]) && address.len() == 37 {
+            // hash the version + publickey to check the checksum
+            let mut hasher = sha2::Sha256::new();
+            hasher.update(address[..33].to_vec());
+            let payload_hash = hasher.finalize().to_vec();
+
+            payload_hash[..4] == address[33..]
+        } else {
+            false
+        }
+    }
+}
+
+impl std::fmt::Display for Address {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        // base58 encoding
+        let address: String = bs58::encode(self.0).into_string();
+        write!(f, "{}", address)
+    }
+}
+
+impl From<PublicKey> for Address {
+    fn from(publickey: PublicKey) -> Self {
+        let mut publickey = publickey.to_bytes().to_vec();
+
+        // add version
+        let mut address = vec![AddressType::Payment as u8];
+
+        // add public key
+        address.append(&mut publickey);
+
+        // hash the version + publickey
+        let mut hasher = sha2::Sha256::new();
+        hasher.update(address.clone());
+        let payload_hash = hasher.finalize().to_vec();
+
+        // add the 4 first bytes from the hash as checksum
+        address.append(&mut payload_hash[..4].to_vec());
+
+        let mut payment_address = [0u8; 37];
+        payment_address.copy_from_slice(address.as_slice());
+
+        Self(payment_address)
+    }
+}
+
+impl Encodable for Address {
+    fn encode<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        s.write_slice(&self.0)?;
+        Ok(37)
+    }
+}
+
+impl Decodable for Address {
+    fn decode<D: io::Read>(mut d: D) -> Result<Self> {
+        let mut bytes = [0u8; 37];
+        d.read_slice(&mut bytes)?;
+        Ok(Self(bytes))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rand::rngs::OsRng;
+
+    use super::*;
+    use crate::crypto::keypair::{Keypair, PublicKey};
+
+    #[test]
+    fn test_address() -> Result<()> {
+        // from/to PublicKey
+        let keypair = Keypair::random(&mut OsRng);
+        let address = Address::from(keypair.public);
+        assert_eq!(keypair.public, PublicKey::from(address));
+
+        // from/to string
+        let address_str = address.to_string();
+        let from_str = Address::from_str(address_str.clone())?;
+        assert_eq!(from_str, address);
+
+        Ok(())
+    }
+}

+ 3 - 0
src/util/mod.rs

@@ -1,3 +1,4 @@
+pub mod address;
 pub mod loader;
 pub mod net_name;
 pub mod parse;
@@ -9,3 +10,5 @@ pub use net_name::NetworkName;
 pub use parse::{assign_id, decode_base10, encode_base10, generate_id, generate_id2};
 pub use path::{expand_path, join_config_path};
 pub use token_list::{DrkTokenList, TokenList};
+
+pub use address::Address;