diff --git a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs
index e45e4d2b10d..856c54172a1 100644
--- a/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs
+++ b/rust/tw_any_coin/tests/tw_any_address_ffi_tests.rs
@@ -34,7 +34,7 @@ fn test_any_address_derive() {
// TODO match `CoinType` when it's generated.
let expected_address = match coin.blockchain {
- BlockchainType::Aptos => "",
+ BlockchainType::Aptos => "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4",
// By default, Bitcoin will return a P2PKH address.
BlockchainType::Bitcoin => "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X",
BlockchainType::Ethereum => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309",
@@ -111,9 +111,8 @@ fn test_any_address_is_valid_coin() {
"0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b",
"eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b",
"19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c",
- "777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb",
"0x777821c78442e17d82c3d7a371f42de7189e4248e529fe6eee6bca40ddbb",
- "eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175" // Too short automatically padded
+ "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175"
],
BlockchainType::Bitcoin => vec![
"1MrZNGN7mfWZiZNQttrzHjfw72jnJC2JNx",
diff --git a/rust/tw_aptos/src/address.rs b/rust/tw_aptos/src/address.rs
index 080c28a9d97..17e699a923b 100644
--- a/rust/tw_aptos/src/address.rs
+++ b/rust/tw_aptos/src/address.rs
@@ -9,13 +9,33 @@ use std::str::FromStr;
use move_core_types::account_address::{AccountAddress, AccountAddressParseError};
use tw_coin_entry::coin_entry::CoinAddress;
use tw_coin_entry::error::AddressError;
+use tw_keypair::ed25519;
use tw_memory::Data;
+use tw_hash::sha3::sha3_256;
+
+
+#[derive(Debug)]
+#[repr(u8)]
+pub enum Scheme {
+ Ed25519 = 0,
+}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Address {
addr: AccountAddress,
}
+impl Address {
+ /// Initializes an address with a `ed25519` public key.
+ pub fn with_ed25519_pubkey(pubkey: &ed25519::sha512::PublicKey) -> Result
{
+ let mut to_hash = pubkey.as_slice().to_vec();
+ to_hash.push(Scheme::Ed25519 as u8);
+ let hashed = sha3_256(to_hash.as_slice());
+ let addr = AccountAddress::from_bytes(hashed).map_err(from_account_error)?;
+ Ok(Address{addr})
+ }
+}
+
impl Display for Address {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.addr.to_hex_literal())
@@ -38,7 +58,52 @@ impl FromStr for Address {
type Err = AddressError;
fn from_str(s: &str) -> Result {
- let addr = AccountAddress::from_str(s).map_err(from_account_error)?;
+ const NUM_CHARS: usize = AccountAddress::LENGTH * 2;
+ let mut has_0x = false;
+ let mut working = s.trim();
+
+ // Checks if it has a 0x at the beginning, which is okay
+ if working.starts_with("0x") {
+ has_0x = true;
+ working = &working[2..];
+ }
+
+ if working.len() > NUM_CHARS {
+ return Err(AddressError::InvalidInput)
+ } else if !has_0x && working.len() < NUM_CHARS {
+ return Err(AddressError::InvalidInput)
+ }
+
+ if !working.chars().all(|c| char::is_ascii_hexdigit(&c)) {
+ return Err(AddressError::InvalidInput)
+ }
+
+ let addr = if has_0x {
+ AccountAddress::from_hex_literal(s.trim())
+ } else {
+ AccountAddress::from_str(s.trim())
+ }.map_err(from_account_error)?;
+
Ok(Address{addr})
}
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use tw_keypair::ed25519::sha512::PrivateKey;
+
+ #[test]
+ fn test_from_public_key() {
+ let private = PrivateKey::try_from(
+ "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5",
+ )
+ .unwrap();
+ let public = private.public();
+ let addr = Address::with_ed25519_pubkey(&public);
+ assert_eq!(
+ addr.unwrap().to_string(),
+ "0x9006fa46f038224e8004bdda97f2e7a60c2c3d135bce7cb15541e5c0aae907a4"
+ );
+ }
}
\ No newline at end of file
diff --git a/rust/tw_aptos/src/entry.rs b/rust/tw_aptos/src/entry.rs
index d8a348f4571..a01e5417662 100644
--- a/rust/tw_aptos/src/entry.rs
+++ b/rust/tw_aptos/src/entry.rs
@@ -9,7 +9,7 @@ use std::str::FromStr;
use tw_coin_entry::coin_context::CoinContext;
use tw_coin_entry::coin_entry::{CoinEntry, PublicKeyBytes, SignatureBytes};
use tw_coin_entry::derivation::Derivation;
-use tw_coin_entry::error::{AddressResult};
+use tw_coin_entry::error::{AddressError, AddressResult};
use tw_coin_entry::modules::json_signer::NoJsonSigner;
use tw_coin_entry::modules::message_signer::NoMessageSigner;
use tw_coin_entry::modules::plan_builder::NoPlanBuilder;
@@ -51,7 +51,10 @@ impl CoinEntry for AptosEntry {
_derivation: Derivation,
_prefix: Option,
) -> AddressResult {
- todo!()
+ let public_key = public_key
+ .to_ed25519()
+ .ok_or(AddressError::PublicKeyTypeMismatch)?;
+ Address::with_ed25519_pubkey(public_key)
}
#[inline]
diff --git a/rust/tw_keypair/src/tw/public.rs b/rust/tw_keypair/src/tw/public.rs
index e8a31f0fc20..46cb0e4f7e9 100644
--- a/rust/tw_keypair/src/tw/public.rs
+++ b/rust/tw_keypair/src/tw/public.rs
@@ -135,4 +135,13 @@ impl PublicKey {
_ => None,
}
}
+
+ pub fn to_ed25519(&self) -> Option<&ed25519::sha512::PublicKey> {
+ match self {
+ PublicKey::Ed25519(ed25519) => {
+ Some(ed25519)
+ },
+ _ => None,
+ }
+ }
}