elrond_rust/
account.rs

1//! Elrond accounts are based on public/private keypairs on the ed25519 curve. Addresses are derived
2//! using a bech32 encoding of the public key
3
4use bech32::{self, FromBase32, ToBase32};
5use ed25519_dalek::{PublicKey, SecretKey, Keypair, Signer};
6use super::{Result, ElrondClientError};
7use rand::rngs::OsRng;
8
9/// Representation for an address on the Elrond network. Addresses on Elrond are derived from
10/// public keys on the ed25519 curve, encoded with the Bech32 format originally created for 
11/// segwit on Bitcoin in BIP 0173.
12#[derive(Clone, Debug, PartialEq)]
13pub struct ElrondAddress {
14    inner: String
15}
16
17/// Decode a `bech32` encoded value and return an ed25519 public key if it is a valid elrond address
18fn check_elrond_address(addr_str: &str) -> Result<Option<PublicKey>> {
19    let (hrd, data) = bech32::decode(addr_str).map_err(|_| {
20        ElrondClientError::new("could not decode address from string")
21    })?;
22    if hrd == "erd" {
23        let public_key_bytes = Vec::<u8>::from_base32(&data).map_err(|_| {
24            ElrondClientError::new("could not convert base32 to bytes")
25        })?;
26        let public_key = PublicKey::from_bytes(&public_key_bytes).map_err(|_| {
27            ElrondClientError::new("bytes in bech32 are not a valid public key")
28        })?;
29        Ok(Some(public_key))
30    } else {
31        Ok(None)
32    }
33}
34
35impl ElrondAddress {
36    /// Create a new `ElrondAddress` from a string value. This will check validity.
37    pub fn new(addr_str: &str) -> Result<Self> {
38        // verify if address is valid first
39        if let Some(_pk) = check_elrond_address(addr_str)? {
40            Ok(Self { inner: addr_str.to_string() })
41        } 
42        else {
43            Err(ElrondClientError::new(
44                &format!(
45                    "'{}' is not a valid elrond address",
46                    addr_str
47                )
48            ))
49        }
50    }
51    /// Covert `ElrondAddress` to a public key
52    pub fn to_public_key(&self) -> PublicKey {
53        // this is safe as the only way to modify an inner value is via "new", which checks validity
54        check_elrond_address(&self.inner)
55            .expect("inner valid of elrond address corrupted (encoding)")
56            .expect("inner valid of elrond address corrupted (not elrond address)")
57    }
58    /// Create a new `ElrondAddress` from a ed25519 public key
59    pub fn from_public_key(public_key: &PublicKey) -> Result<Self> {
60        let inner = bech32::encode("erd", public_key.to_base32()).map_err(|_| {
61            ElrondClientError::new("could not encode public key as bech32")
62        })?;
63        Ok(Self { inner })
64    }
65    /// Get string representation of address
66    pub fn to_string(&self) -> String {
67        self.inner.clone()
68    }
69}
70
71/// An account is derived from a ed25519 keypair. New transactions are signed with the secret key and
72/// signatures can be verified with the public key. An address derived from the public key (in Bech32 
73// format) may be the recipient of other transactions.
74pub struct Account {
75    pub secret: SecretKey,
76    pub public: PublicKey,
77    pub address: ElrondAddress
78}
79
80impl Account {
81    /// Generate a new Elrond account
82    pub fn generate() -> Result<Self> {
83        let mut csprng = OsRng{};
84        let secret = SecretKey::generate(&mut csprng);
85        let public = (&secret).into();
86        let address = ElrondAddress::from_public_key(&public)?;
87        Ok(Self {
88            secret,
89            public,
90            address
91        })
92    }
93    /// Import an Elrond account from an existing secret (private key)
94    pub fn from_secret(secret: SecretKey) -> Result<Self> {
95        let public = (&secret).into();
96        let address = ElrondAddress::from_public_key(&public)?;
97        Ok(Self {
98            secret,
99            public,
100            address
101        })
102    }
103    /// Sign data with account and return signature as a hex string
104    pub fn sign(&self, data: &str) -> Result<String> {
105        let public_bytes = self.public.to_bytes();
106        let secret_bytes = self.secret.to_bytes();
107        let combined: Vec<u8> = secret_bytes.iter().chain(&public_bytes).map(|x| x.clone()).collect();
108        // ed25519_dalek requires keypair for signing, can't just use secret
109        // so perhaps save keypair and not the individual keys in the struct
110        let keypair = Keypair::from_bytes(&combined).map_err(|_|{
111            ElrondClientError::new("could not load keypair from public and secret key data")
112        })?;
113        let signature = keypair.sign(data.as_bytes()).to_bytes();
114        assert_eq!(signature.len(), 64);
115        Ok(hex::encode(signature.to_vec()))
116    }
117    /// Returns a hex string representation of the secret/private key associated with an account.
118    /// You can restore an account from this data using `Account::from_string`.
119    pub fn to_string(&self) -> String {
120        let secret_bytes = self.secret.to_bytes();
121        hex::encode(&secret_bytes.to_vec())
122    }
123    /// Load an account from a hex string representation of a secret/private key
124    pub fn from_string(hex_str: &str) -> Result<Self> {
125        let bytes = hex::decode(hex_str).map_err(|_| {
126            ElrondClientError::new("could not decode hex string")
127        })?;
128        let secret = SecretKey::from_bytes(&bytes).map_err(|_| {
129            ElrondClientError::new("hex string bytes do not encode valid secret key")
130        })?;
131        Self::from_secret(secret)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::{Account, ElrondAddress, SecretKey};
138    #[test]
139    fn generate_and_test_account() {
140        let account = Account::generate().unwrap();
141        let secret_bytes = &account.secret.to_bytes();
142        let secret_copy = SecretKey::from_bytes(secret_bytes).unwrap();
143        let account2 = Account::from_secret(secret_copy).unwrap();
144        assert_eq!(&account.secret.to_bytes(), &account2.secret.to_bytes());
145        assert_eq!(&account.public.to_bytes(), &account2.public.to_bytes());
146        assert_eq!(&account.address, &account2.address);
147    }
148
149    #[test]
150    fn validate_address(){
151        let addr_str = "erd16jats393r8rnut88yhvu5wvxxje57qzlj3tqk7n6jnf7f6cxs4uqfeh65k";
152        let address = ElrondAddress::new(addr_str).unwrap();
153        let public_key = address.to_public_key();
154        let address2 = ElrondAddress::from_public_key(&public_key).unwrap();
155        assert_eq!(address, address2);
156    }
157
158    #[test]
159    fn signing_completes(){
160        let private_key = "a4b36a5d97176618b5a7fcc9228d2fd98ee2f14ddd3d6462ae03e40eb487d15b";
161        let account = Account::from_string(private_key).unwrap();
162        let sig = account.sign("dummy data").unwrap();
163        assert_eq!(sig, "a25d1b1e24cd9299396e0c6e191b80a8e4f54e19c495955f13d6c168e5784dd642f540938333851fb1abcaa6d13f327ac2341607ad2fbb941dfdeeb6c4fcd803");
164    }
165
166    #[test]
167    fn save_and_load_account(){
168        let account = Account::generate().unwrap();
169        let account_as_string = account.to_string();
170        println!("{}", &account_as_string);
171        let account_copy = Account::from_string(&account_as_string).unwrap();
172        assert_eq!(&account.secret.to_bytes(), &account_copy.secret.to_bytes());
173        assert_eq!(&account.public.to_bytes(), &account_copy.public.to_bytes());
174        assert_eq!(account.address.to_string(), account_copy.address.to_string());
175    }
176
177    #[test]
178    fn address_from_private_key(){
179        let private_key = "a4b36a5d97176618b5a7fcc9228d2fd98ee2f14ddd3d6462ae03e40eb487d15b";
180        let account = Account::from_string(private_key).unwrap();
181        assert_eq!("erd146apxa83wr7paz3gsg07dhcpg98ascjtpg9p8l8g5rpmg6chhchq9ccvmc", &account.address.to_string());
182    }
183
184}