Browse Source

[sdk/crypto] added shareaddress

ertosns 2 năm trước cách đây
mục cha
commit
ebf902d10d

+ 0 - 6
src/sdk/src/crypto/constants.rs

@@ -42,11 +42,5 @@ pub(crate) const L_ORCHARD_SCALAR: usize = 255;
 /// $\ell_\mathsf{value}$
 pub(crate) const L_VALUE: usize = 64;
 
-/// main-net wallet import format prefix
-pub const MAINNET_ADDRS_PREFIX: [u8; 1] = [0x80];
-
-/// test-net wallet import format prefix
-pub const TESTNET_ADDRS_PREFIX: [u8; 1] = [0x01];
-
 /// WIF checksum length
 pub const WIF_CHECKSUM_LEN: usize = 4;

+ 3 - 3
src/sdk/src/crypto/mod.rs

@@ -77,9 +77,9 @@ pub mod pasta_prelude {
     };
 }
 
-/// Wallet Import Format
-pub mod wif;
-pub use wif::Wif;
+/// Sharing address encoded as wallet import format
+pub mod share_address;
+pub use share_address::{ShareAddress, ShareAddressType};
 
 #[macro_export]
 macro_rules! fp_from_bs58 {

+ 215 - 0
src/sdk/src/crypto/share_address.rs

@@ -0,0 +1,215 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+use std::convert::TryInto;
+
+use pasta_curves::group::ff::PrimeField;
+use sha2::{Digest, Sha256};
+
+use crate::{
+    crypto::{PublicKey, SecretKey},
+    error::ContractError,
+    pasta::pallas,
+};
+use std::fmt;
+
+fn double_sha256(input: &[u8]) -> [u8; 32] {
+    let mut hasher = Sha256::new();
+    hasher.update(input);
+    let first = hasher.finalize();
+
+    let mut hasher = Sha256::new();
+    hasher.update(first);
+    hasher.finalize().into()
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct ShareAddress {
+    pub raw: [u8; 32],
+    pub prefix: ShareAddressType,
+    full_address: Option<bool>,
+}
+
+#[derive(Clone, Debug, Copy, PartialEq)]
+pub enum ShareAddressType {
+    SecretKey = 1,
+    PublicKey = 2,
+    DaoBulla = 3,
+}
+
+/// Wallet import format <https://en.bitcoin.it/wiki/Wallet_import_format>
+/// Encodes pallas-curve base field secret key to base58 string.
+impl ShareAddress {
+    /// Initialize `ShareAddress` from `pallas::Base`
+    pub fn from_field(
+        field_element: &pallas::Base,
+        prefix: ShareAddressType,
+        with_checksum: Option<bool>,
+    ) -> Self {
+        // address to bytes
+        let address: [u8; 32] = field_element.to_repr();
+        // address prefix
+        ShareAddress { raw: address, prefix, full_address: with_checksum }
+    }
+    /// concatenate prefix with raw bytes
+    pub fn extended_address(&self) -> [u8; 33] {
+        // create extended address
+        let mut extended_addrs: [u8; 33] = [0; 33];
+        let (extended_addrs_prefix, extended_addrs_main) = extended_addrs.split_at_mut(1);
+        extended_addrs_prefix.copy_from_slice(&[self.prefix as u8]);
+        extended_addrs_main.copy_from_slice(&self.raw);
+        extended_addrs
+    }
+    /// extended address checksum
+    pub fn checksum(&self) -> [u8; 4] {
+        let extended_addrs: [u8; 33] = self.extended_address();
+        // extended address checksum
+        let checksum: [u8; 4] = double_sha256(&extended_addrs)[0..4].try_into().unwrap();
+        checksum
+    }
+}
+
+impl fmt::Display for ShareAddress {
+    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+        let extended_addrs = self.extended_address();
+        match self.full_address {
+            Some(_) => {
+                let checksum = self.checksum();
+                let mut full_address: [u8; 37] = [0; 37];
+                let (full_address_left, full_address_right) =
+                    full_address.split_at_mut(extended_addrs.len());
+                full_address_left.copy_from_slice(&extended_addrs);
+                full_address_right.copy_from_slice(&checksum);
+                let base58 = bs58::encode(full_address).into_string();
+                fmt.write_str(&base58)?;
+            }
+            None => {
+                let base58 = bs58::encode(extended_addrs).into_string();
+                fmt.write_str(&base58)?;
+            }
+        }
+        Ok(())
+    }
+}
+
+/// convert decode `ShareAddress` string
+impl TryFrom<std::string::String> for ShareAddress {
+    type Error = String;
+    fn try_from(share_address_str: String) -> Result<Self, Self::Error> {
+        let full_address: Vec<u8> = bs58::decode(share_address_str).into_vec().unwrap();
+
+        if full_address.len() != 37 && full_address.len() != 33 {
+            return Err("invalid share address".to_string())
+        }
+        let is_full_address = if full_address.len() == 37 { Some(true) } else { None };
+        // extract prefix
+        let prefix: u8 = full_address[0];
+        // get address
+        let addrs: [u8; 32] = full_address[1..33].try_into().expect("slice with incorrect length");
+        // get extended address
+        let extended_addrs: [u8; 33] =
+            full_address[0..33].try_into().expect("slice with incorrect length");
+        if full_address.len() == 37 {
+            // get checksum
+            let wif_checksum: [u8; 4] =
+                full_address[33..37].try_into().expect("slice with incorrect length");
+            // validate checksum
+            let checksum: [u8; 4] = double_sha256(&extended_addrs)[0..4].try_into().unwrap();
+            assert!(wif_checksum == checksum);
+        }
+        if prefix != ShareAddressType::SecretKey as u8 {
+            return Err("wrong prefix".to_string())
+        }
+        Ok(ShareAddress {
+            raw: addrs,
+            prefix: ShareAddressType::SecretKey,
+            full_address: is_full_address,
+        })
+    }
+}
+
+impl TryInto<SecretKey> for ShareAddress {
+    type Error = String;
+    fn try_into(self) -> Result<SecretKey, Self::Error> {
+        if self.prefix != ShareAddressType::SecretKey {
+            return Err("wrong prefix".to_string())
+        }
+        let sk: Result<SecretKey, ContractError> = SecretKey::from_bytes(self.raw);
+        Ok(sk.unwrap())
+    }
+}
+
+impl TryInto<PublicKey> for ShareAddress {
+    type Error = String;
+    fn try_into(self) -> Result<PublicKey, Self::Error> {
+        if self.prefix != ShareAddressType::PublicKey {
+            return Err("wrong prefix".to_string())
+        }
+        let sk: Result<PublicKey, ContractError> = PublicKey::from_bytes(self.raw);
+        Ok(sk.unwrap())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_sk_share_address() {
+        let sk_bytes: [u8; 32] = [0; 32];
+        let sk = SecretKey::from_bytes(sk_bytes).unwrap();
+        let share_address =
+            ShareAddress::from_field(&sk.inner(), ShareAddressType::SecretKey, Some(true));
+        let share_address_str = share_address.to_string();
+        let share_address_rhs = match ShareAddress::try_from(share_address_str) {
+            Err(why) => panic!("{:?}", why),
+            Ok(value) => value,
+        };
+        assert!(share_address == share_address_rhs);
+    }
+    #[test]
+    fn test_sk_mini_share_addres() {
+        let sk_bytes: [u8; 32] = [0; 32];
+        let sk = SecretKey::from_bytes(sk_bytes).unwrap();
+        let mini_share_address =
+            ShareAddress::from_field(&sk.inner(), ShareAddressType::SecretKey, None);
+        let share_address_str = mini_share_address.to_string();
+        let share_address_rhs = match ShareAddress::try_from(share_address_str) {
+            Err(why) => panic!("{:?}", why),
+            Ok(value) => value,
+        };
+        assert!(mini_share_address == share_address_rhs);
+    }
+    #[test]
+    fn test_try_from_str() {
+        let res = ShareAddress::try_from("abc".to_string());
+        assert!(res.is_err());
+    }
+    #[test]
+    fn test_sk_tryfrom() {
+        let sk_bytes: [u8; 32] = [0; 32];
+        let sk = SecretKey::from_bytes(sk_bytes).unwrap();
+        let share_address =
+            ShareAddress::from_field(&sk.inner(), ShareAddressType::SecretKey, Some(true));
+        let sk_rhs = match share_address.try_into() {
+            Err(why) => panic!("{:?}", why),
+            Ok(value) => value,
+        };
+        assert!(sk == sk_rhs);
+    }
+}