From 003926ddfa21137c5f58f4d7d03c90b13a87c8a5 Mon Sep 17 00:00:00 2001
From: librelois <elois@ifee.fr>
Date: Sun, 1 Mar 2020 23:58:44 +0100
Subject: [PATCH] [fix] base58: handle leading zero

---
 src/bases/b58.rs    | 72 +++++++++++++++++++++++++++++++++++++--------
 src/keys/ed25519.rs | 66 +++++++++++------------------------------
 src/keys/mod.rs     | 23 +++++++--------
 src/seeds.rs        |  2 +-
 4 files changed, 87 insertions(+), 76 deletions(-)

diff --git a/src/bases/b58.rs b/src/bases/b58.rs
index 52ba871..8dec3ab 100644
--- a/src/bases/b58.rs
+++ b/src/bases/b58.rs
@@ -26,25 +26,23 @@ pub trait ToBase58 {
 /// Create an array of 32 bytes from a Base58 string.
 pub fn str_base58_to_32bytes(base58_data: &str) -> Result<([u8; 32], usize), BaseConvertionError> {
     let mut source = base58_data;
+    let mut count_leading_1 = 0;
     while !source.is_empty() && &source[0..1] == "1" {
         source = &source[1..];
+        count_leading_1 += 1;
     }
 
     match bs58::decode(source).into_vec() {
         Ok(result) => {
-            let len = result.len();
-            if len <= 32 {
-                let mut u8_array = [0; 32];
+            let mut len = result.len();
+            if len > 32 {
+                len = 32;
+            }
+            let mut u8_array = [0; 32];
 
-                u8_array[..len].clone_from_slice(&result[..len]);
+            u8_array[(32 - len)..].clone_from_slice(&result[..len]);
 
-                Ok((u8_array, len))
-            } else {
-                Err(BaseConvertionError::InvalidLength {
-                    expected: 32,
-                    found: len,
-                })
-            }
+            Ok((u8_array, count_leading_1))
         }
         Err(bs58::decode::Error::InvalidCharacter { character, index }) => {
             Err(BaseConvertionError::InvalidCharacter {
@@ -60,8 +58,56 @@ pub fn str_base58_to_32bytes(base58_data: &str) -> Result<([u8; 32], usize), Bas
 }
 
 /// Create a Base58 string from a slice of bytes.
-pub fn bytes_to_str_base58(bytes: &[u8]) -> String {
-    bs58::encode(bytes).into_string()
+pub fn bytes_to_str_base58(bytes: &[u8], mut count_leading_1: usize) -> String {
+    let mut str_base58 = String::new();
+    let bytes = &bytes[count_leading_1..];
+
+    let bytes = if count_leading_1 == 0 && !bytes.is_empty() && bytes[0] == 0 {
+        &bytes[1..]
+    } else {
+        while count_leading_1 > 0 {
+            count_leading_1 -= 1;
+            str_base58.push('1');
+        }
+        &bytes[count_leading_1..]
+    };
+
+    str_base58.push_str(&bs58::encode(bytes).into_string());
+
+    str_base58
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::*;
+
+    #[test]
+    fn test_base_58_str_with_leading_1() -> Result<(), BaseConvertionError> {
+        let base58str = "1V27SH9TiVEDs8TWFPydpRKxhvZari7wjGwQnPxMnkr";
+
+        let (bytes, count_leading_1) = str_base58_to_32bytes(base58str)?;
+
+        println!("{:?}", bytes);
+
+        assert_eq!(base58str, &bytes_to_str_base58(&bytes[..], count_leading_1),);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_base_58_str_with_43_char() -> Result<(), BaseConvertionError> {
+        let base58str = "2nV7Dv4nhTJ9dZUvRJpL34vFP9b2BkDjKWv9iBW2JaR";
+
+        let (bytes, count_leading_1) = str_base58_to_32bytes(base58str)?;
+
+        println!("{}", count_leading_1);
+        println!("{:?}", bytes);
+
+        assert_eq!(base58str, &bytes_to_str_base58(&bytes[..], count_leading_1),);
+
+        Ok(())
+    }
 }
 
 /*/// Create an array of 64bytes from a Base58 string.
diff --git a/src/keys/ed25519.rs b/src/keys/ed25519.rs
index 3074ea9..c8ee4a3 100644
--- a/src/keys/ed25519.rs
+++ b/src/keys/ed25519.rs
@@ -159,21 +159,21 @@ impl Eq for Signature {}
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
 pub struct PublicKey {
     datas: [u8; 32],
-    len: usize,
+    count_leading_zero: usize,
 }
 
 impl Default for PublicKey {
     fn default() -> Self {
         PublicKey {
             datas: [0u8; 32],
-            len: 0,
+            count_leading_zero: 32,
         }
     }
 }
 
 impl AsRef<[u8]> for PublicKey {
     fn as_ref(&self) -> &[u8] {
-        &self.datas[..self.len]
+        &self.datas[..]
     }
 }
 
@@ -187,11 +187,11 @@ impl TryFrom<&[u8]> for PublicKey {
                 found: bytes.len(),
             })
         } else {
-            let mut u8_array = [0; 32];
-            u8_array[..bytes.len()].copy_from_slice(&bytes);
+            let mut u8_array = [0; PUBKEY_SIZE_IN_BYTES];
+            u8_array[(PUBKEY_SIZE_IN_BYTES - bytes.len())..].copy_from_slice(&bytes);
             Ok(PublicKey {
                 datas: u8_array,
-                len: bytes.len(),
+                count_leading_zero: 0,
             })
         }
     }
@@ -199,13 +199,17 @@ impl TryFrom<&[u8]> for PublicKey {
 
 impl ToBase58 for PublicKey {
     fn to_base58(&self) -> String {
-        bytes_to_str_base58(self.as_ref())
+        bytes_to_str_base58(self.as_ref(), self.count_leading_zero)
     }
 }
 
 impl Display for PublicKey {
     fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
-        write!(f, "{}", bytes_to_str_base58(self.as_ref()))
+        write!(
+            f,
+            "{}",
+            bytes_to_str_base58(self.as_ref(), self.count_leading_zero)
+        )
     }
 }
 
@@ -221,8 +225,11 @@ impl super::PublicKey for PublicKey {
 
     #[inline]
     fn from_base58(base58_data: &str) -> Result<Self, BaseConvertionError> {
-        let (datas, len) = b58::str_base58_to_32bytes(base58_data)?;
-        Ok(PublicKey { datas, len })
+        let (datas, count_leading_zero) = b58::str_base58_to_32bytes(base58_data)?;
+        Ok(PublicKey {
+            datas,
+            count_leading_zero,
+        })
     }
 
     fn to_bytes_vector(&self) -> Vec<u8> {
@@ -447,13 +454,6 @@ mod tests {
         assert!(!seed.eq(&other_seed));
 
         // Test seed parsing
-        assert_eq!(
-            Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLVgth",).unwrap_err(),
-            BaseConvertionError::InvalidLength {
-                found: 35,
-                expected: 32
-            }
-        );
         /*assert_eq!(
             Seed32::from_base58("DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd",).unwrap_err(),
             BaseConvertionError::InvalidLength {
@@ -468,22 +468,6 @@ mod tests {
                 offset: 42
             }
         );
-        assert_eq!(
-            Seed32::from_base58(
-                "\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 "
-            )
-            .unwrap_err(),
-            BaseConvertionError::InvalidLength {
-                expected: 32,
-                found: 161
-            }
-        );
     }
 
     #[test]
@@ -527,22 +511,6 @@ mod tests {
                 offset: 42
             }
         );
-        assert_eq!(
-            super::PublicKey::from_base58(
-                "\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\
-                 "
-            )
-            .unwrap_err(),
-            BaseConvertionError::InvalidLength {
-                expected: 32,
-                found: 161
-            }
-        );
     }
 
     #[test]
diff --git a/src/keys/mod.rs b/src/keys/mod.rs
index 6acfdcd..53ece26 100644
--- a/src/keys/mod.rs
+++ b/src/keys/mod.rs
@@ -507,10 +507,11 @@ mod tests {
 
     #[test]
     fn pubkey() {
-        let pubkey_default = PubKey::default();
-        let pubkey_bytes = [];
-        let pubkey = PubKey::Ed25519(unwrap!(ed25519::PublicKey::try_from(&pubkey_bytes[..])));
-        assert_eq!(pubkey_default, pubkey);
+        let ed25519_pubkey_default = ed25519::PublicKey::default();
+        let pubkey_default = PubKey::Ed25519(ed25519_pubkey_default);
+        let pubkey = PubKey::Ed25519(unwrap!(ed25519::PublicKey::try_from(
+            ed25519_pubkey_default.as_ref()
+        )));
 
         let pubkey_str_b58 = "11111111111111111111111111111111".to_owned();
         assert_eq!(
@@ -519,14 +520,14 @@ mod tests {
         );
 
         assert_eq!(pubkey.size_in_bytes(), ed25519::PUBKEY_SIZE_IN_BYTES + 3);
-        assert_eq!("", &format!("{}", pubkey));
+        assert_eq!("1111111111111111111111111111111", &format!("{}", pubkey));
 
         assert_eq!(KeysAlgo::Ed25519, pubkey.algo());
         assert_eq!(KeysAlgo::Schnorr, PubKey::Schnorr().algo());
 
-        assert_eq!(pubkey_bytes.to_vec(), pubkey.to_bytes_vector());
+        assert_eq!([0u8; 32].to_vec(), pubkey.to_bytes_vector());
 
-        assert_eq!("", &pubkey.to_base58());
+        assert_eq!("1111111111111111111111111111111", &pubkey.to_base58());
 
         assert_eq!(
             Err(SigError::InvalidSig),
@@ -548,7 +549,7 @@ mod tests {
             0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
             0, 0, 0, 0,
         ];
-        let seed_str_b58 = "11111111111111111111111111111111".to_owned();
+        let seed_str_b58 = "1111111111111111111111111111111".to_owned();
         let seed = Seed32::new(seed_bytes);
 
         assert_eq!(seed_default, seed);
@@ -557,7 +558,7 @@ mod tests {
             Seed32::from_base58(&seed_str_b58).expect("Fail to parse seed !")
         );
 
-        assert_eq!(seed_str_b58, format!("{}", seed));
+        assert_eq!("1111111111111111111111111111111", format!("{}", seed));
 
         assert_eq!(seed_str_b58, seed.to_base58());
     }
@@ -725,9 +726,5 @@ mod tests {
                 23, 24, 25, 26, 27, 28, 29, 30, 31, 31
             ]),
         );
-        assert_eq!(
-            Ok(PubKey::Ed25519(ed25519::PublicKey::default())),
-            PubKey::from_bytes(&[]),
-        );
     }
 }
diff --git a/src/seeds.rs b/src/seeds.rs
index c94b9ca..c5f5563 100644
--- a/src/seeds.rs
+++ b/src/seeds.rs
@@ -38,7 +38,7 @@ impl AsRef<[u8]> for Seed32 {
 
 impl ToBase58 for Seed32 {
     fn to_base58(&self) -> String {
-        bytes_to_str_base58(&self.0[..])
+        bytes_to_str_base58(&self.0[..], 0)
     }
 }
 
-- 
GitLab