Skip to content

Commit

Permalink
feat: add public key encoder
Browse files Browse the repository at this point in the history
  • Loading branch information
XuNeal committed Dec 29, 2023
1 parent 197357b commit 5b11a85
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 146 deletions.
103 changes: 46 additions & 57 deletions token-core/tcx-eos/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,30 @@ use bitcoin::util::base58;
use std::str::FromStr;
use tcx_common::{ripemd160, CommonError};
use tcx_constants::CoinInfo;
use tcx_keystore::{Address, Result};
use tcx_keystore::{keystore::PublicKeyEncoder, Address, Result};
use tcx_primitive::TypedPublicKey;

#[derive(PartialEq, Eq, Clone)]
pub struct EosAddress {
pubkey_bytes: Vec<u8>,
checksum: Vec<u8>,
}
pub struct EosAddress {}

impl Address for EosAddress {
fn from_public_key(public_key: &TypedPublicKey, _coin: &CoinInfo) -> Result<Self> {
#[derive(PartialEq, Eq, Clone)]
pub struct EosPublicKeyEncoder {}

impl PublicKeyEncoder for EosPublicKeyEncoder {
fn encode(public_key: &TypedPublicKey, _coin_info: &CoinInfo) -> Result<String> {
let pubkey_bytes = public_key.to_bytes();
let hashed_bytes = ripemd160(&pubkey_bytes);
let checksum = hashed_bytes[..4].to_vec();
let mut bytes = vec![];
bytes.extend_from_slice(&pubkey_bytes);
bytes.extend_from_slice(&checksum);
Ok(format!("EOS{}", base58::encode_slice(&bytes)))
}
}

Ok(EosAddress {
pubkey_bytes,
checksum,
})
impl Address for EosAddress {
fn from_public_key(_public_key: &TypedPublicKey, _coin: &CoinInfo) -> Result<Self> {
Ok(EosAddress {})
}

fn is_valid(address: &str, _coin: &CoinInfo) -> bool {
Expand All @@ -32,70 +37,54 @@ impl Address for EosAddress {
impl FromStr for EosAddress {
type Err = failure::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if let Some(s) = s.strip_prefix("EOS") {
let s = &s[3..];
let bytes = base58::from(s).map_err(|_| CommonError::InvalidAddress)?;
let checksum = bytes[bytes.len() - 4..].to_vec();
let pubkey_bytes = bytes[..bytes.len() - 4].to_vec();

let hashed_bytes = ripemd160(&pubkey_bytes);
let expected_checksum = hashed_bytes[..4].to_vec();
if checksum != expected_checksum {
return Err(CommonError::InvalidAddressChecksum.into());
}

Ok(EosAddress {
pubkey_bytes,
checksum,
})
} else {
Err(CommonError::InvalidAddress.into())
}
Ok(EosAddress {})
}
}

impl ToString for EosAddress {
fn to_string(&self) -> String {
let mut bytes = vec![];
bytes.extend_from_slice(&self.pubkey_bytes);
bytes.extend_from_slice(&self.checksum);
format!("EOS{}", base58::encode_slice(&bytes))
"".to_string()
}
}

#[cfg(test)]
mod tests {
use std::str::FromStr;

use crate::address::EosAddress;
use crate::address::EosPublicKeyEncoder;
use tcx_common::FromHex;
use tcx_constants::CoinInfo;
use tcx_keystore::Address;
use tcx_keystore::{Address, PublicKeyEncoder};
use tcx_primitive::{PublicKey, Secp256k1PublicKey, TypedPublicKey};

#[test]
fn test_is_valid() {
fn test_encode_public_key() {
let tests = [
"EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF",
"EOS5varo7aGmCFQw77DNiiWUj3YQA7ZmWUMC4NDDXeeaeEAXk436S",
"EOS6w47YkvVGLzvKeozV5ZK34QApCmALrwoH2Dwhnirs5TZ9mg5io",
"EOS5varo7aGmCFQw77DNiiWUj3YQA7ZmWUMC4NDDXeeaeEAXk436S",
(
"0x037b5253c24ce2a293566f9e066051366cda5073e4a43b25f07c990d7c9ac0aab5",
"EOS7mYcbf9BumHjUUCPoXh2nxzkipQZDQCZC7EmRq8cwB1exEYHfy",
),
(
"0x03f6a261f3b4d7c24014f2026b09ad409076c566b39b99b8e0a7196f391caec508",
"EOS8hrUMSKjQZK4QsXLfAVTwmmD4nTfF9Lb11ZQN3zVYrdxhApgFr",
),
(
"0x03844a01522a26156df32b587d80df60d76072480e299c6d3241c7b2b929c07625",
"EOS7qVgE5PF58jAV7HmFPNEAEdyZmE4UBasTRa7AZ1AhJGxYovBwc",
),
(
"0x035cda3171ba9107ec3f398c8e33b17803fd9d6a815ee5d544a71759455396319c",
"EOS7Y8KPZQDMhDWjHaM3nWRWwYSoP75KpSatFbanKRPRaUKDWi2UA",
),
];

for i in tests {
assert!(EosAddress::is_valid(i, &CoinInfo::default()));
let bytes = Vec::from_0x_hex(i.0).unwrap();
let k1_pub_key = Secp256k1PublicKey::from_slice(&bytes).unwrap();
let typed_pub_key = TypedPublicKey::Secp256k1(k1_pub_key);
assert_eq!(
EosPublicKeyEncoder::encode(&typed_pub_key, &CoinInfo::default()).unwrap(),
i.1
);
}
}

#[test]
fn test_invalid_address_checksum() {
let address = "EOS5varo7aGmCFQw77DNiiWUj3YQA7ZmWUMC4NDDXeeaeEAXk436R";
let addr = EosAddress::from_str(address);
assert_eq!(addr.err().unwrap().to_string(), "invalid_address_checksum");
}

#[test]
fn test_invalid_address() {
let address = "TEST5varo7aGmCFQw77DNiiWUj3YQA7ZmWUMC4NDDXeeaeEAXk436S";
let addr = EosAddress::from_str(address);
assert_eq!(addr.err().unwrap().to_string(), "invalid_address");
}
}
1 change: 1 addition & 0 deletions token-core/tcx-eos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod eos {
pub type MessageInput = crate::transaction::EosMessageInput;

pub type MessageOutput = crate::transaction::EosMessageOutput;
pub type PubKeyEncoder = crate::address::EosPublicKeyEncoder;
}

pub fn encode_eos_wif(private_key_bytes: &[u8]) -> Result<String> {
Expand Down
37 changes: 33 additions & 4 deletions token-core/tcx-keystore/src/keystore/hd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ impl HdKeystore {
network: coin_info.network.to_string(),
ext_pub_key,
seg_wit: coin_info.seg_wit.to_string(),
public_key: public_key.to_bytes().to_hex(),
public_key: public_key,
};

Ok(account)
Expand All @@ -230,7 +230,9 @@ mod tests {
use crate::Source;
use bitcoin_hashes::hex::ToHex;
use std::string::ToString;
use tcx_common::FromHex;
use tcx_constants::{CurveType, TEST_MNEMONIC, TEST_PASSWORD};
use tcx_primitive::{PublicKey, Secp256k1PublicKey, TypedPublicKey};

// A mnemonic word separated by a full-width or half-width space
static MNEMONIC_WITH_WHITESPACE: &'static str =
Expand Down Expand Up @@ -358,6 +360,15 @@ mod tests {

let acc = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();

let k1_pub_key = Secp256k1PublicKey::from_slice(
&Vec::from_hex_auto(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
)
.unwrap(),
)
.unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);

let expected = Account {
address: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string(),
derivation_path: "m/44'/0'/0'/0/0".to_string(),
Expand All @@ -366,7 +377,7 @@ mod tests {
seg_wit: "NONE".to_string(),
curve: CurveType::SECP256k1,
coin: "BITCOIN".to_string(),
public_key: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string()
public_key
};

assert_eq!(acc, expected);
Expand Down Expand Up @@ -457,6 +468,16 @@ mod tests {

let acc = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();

let k1_pub_key = Secp256k1PublicKey::from_slice(
&Vec::from_hex_auto(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
)
.unwrap(),
)
.unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);

//TODO: why btc address is publickey
let expected = Account {
address: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string(),
derivation_path: "m/44'/0'/0'/0/0".to_string(),
Expand All @@ -465,7 +486,7 @@ mod tests {
seg_wit: "NONE".to_string(),
curve: CurveType::SECP256k1,
coin: "BITCOIN".to_string(),
public_key: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string()
public_key
};

assert_eq!(acc, expected);
Expand Down Expand Up @@ -514,6 +535,14 @@ mod tests {

let acc = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();

let k1_pub_key = Secp256k1PublicKey::from_slice(
&Vec::from_hex_auto(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
)
.unwrap(),
)
.unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);
let expected = Account {
address: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string(),
derivation_path: "m/44'/0'/0'/0/0".to_string(),
Expand All @@ -522,7 +551,7 @@ mod tests {
seg_wit: "NONE".to_string(),
curve: CurveType::SECP256k1,
coin: "BITCOIN".to_string(),
public_key: "026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868".to_string()
public_key
};

assert_eq!(acc, expected);
Expand Down
53 changes: 32 additions & 21 deletions token-core/tcx-keystore/src/keystore/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ fn transform_mnemonic_error(err: failure::Error) -> Error {
}

/// Account that presents one blockchain wallet on a fixtures
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
#[derive(Debug, Clone, PartialEq)]
pub struct Account {
pub address: String,
pub derivation_path: String,
Expand All @@ -82,7 +81,11 @@ pub struct Account {
pub network: String,
pub seg_wit: String,
pub ext_pub_key: String,
pub public_key: String,
pub public_key: TypedPublicKey,
}

pub trait PublicKeyEncoder: Sized + Clone + PartialEq + Eq {
fn encode(public_key: &TypedPublicKey, coin_info: &CoinInfo) -> Result<String>;
}

/// Chain address interface, for encapsulate derivation
Expand Down Expand Up @@ -340,7 +343,7 @@ impl Keystore {
network: coin_info.network.to_string(),
seg_wit: coin_info.seg_wit.to_string(),
ext_pub_key: xpub.to_string(),
public_key: typed_pk.to_bytes().to_0x_hex(),
public_key: typed_pk,
};
Ok(account)
}
Expand Down Expand Up @@ -512,7 +515,9 @@ pub(crate) mod tests {
coin_info_from_param, CoinInfo, CurveType, TEST_MNEMONIC, TEST_PASSWORD, TEST_PRIVATE_KEY,
};
use tcx_crypto::Key;
use tcx_primitive::{Ss58Codec, TypedDeterministicPublicKey, TypedPublicKey};
use tcx_primitive::{
PublicKey, Secp256k1PublicKey, Ss58Codec, TypedDeterministicPublicKey, TypedPublicKey,
};

#[derive(Clone, PartialEq, Eq)]
pub(crate) struct MockAddress(Vec<u8>);
Expand Down Expand Up @@ -866,19 +871,22 @@ pub(crate) mod tests {
seg_wit: "NONE".to_string(),
};

let k1_pub_key = Secp256k1PublicKey::from_slice(
&Vec::from_hex_auto(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
)
.unwrap(),
)
.unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);

let account = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();
assert_eq!(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
account.public_key
);
assert_eq!(public_key, account.public_key);
assert_eq!(account.curve, CurveType::SECP256k1);

let accounts = keystore.derive_coins::<MockAddress>(&[coin_info]).unwrap();
assert_eq!(accounts.len(), 1);
assert_eq!(
"026b5b6a9d041bc5187e0b34f9e496436c7bff261c6c1b5f3c06b433c61394b868",
accounts[0].public_key
);
assert_eq!(public_key, accounts[0].public_key);
assert_eq!(accounts[0].curve, CurveType::SECP256k1);

let public_key = keystore
Expand Down Expand Up @@ -928,19 +936,22 @@ pub(crate) mod tests {
seg_wit: "NONE".to_string(),
};

let k1_pub_key = Secp256k1PublicKey::from_slice(
&Vec::from_hex_auto(
"0280c98b8ea7cab630defb0c09a4295c2193cdee016c1d5b9b0cb18572b9c370fe",
)
.unwrap(),
)
.unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);

let account = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();
assert_eq!(
"0280c98b8ea7cab630defb0c09a4295c2193cdee016c1d5b9b0cb18572b9c370fe",
account.public_key
);
assert_eq!(public_key, account.public_key);
assert_eq!(account.curve, CurveType::SECP256k1);

let accounts = keystore.derive_coins::<MockAddress>(&[coin_info]).unwrap();
assert_eq!(accounts.len(), 1);
assert_eq!(
"0280c98b8ea7cab630defb0c09a4295c2193cdee016c1d5b9b0cb18572b9c370fe",
accounts[0].public_key
);
assert_eq!(public_key, accounts[0].public_key);
assert_eq!(accounts[0].curve, CurveType::SECP256k1);

let public_key = keystore
Expand Down
9 changes: 7 additions & 2 deletions token-core/tcx-keystore/src/keystore/private.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl PrivateKeystore {
network: coin.network.to_string(),
seg_wit: coin.seg_wit.to_string(),
ext_pub_key: "".to_string(),
public_key: pub_key.to_bytes().to_hex(),
public_key: pub_key,
};

Ok(acc)
Expand All @@ -153,6 +153,7 @@ mod tests {
use tcx_common::FromHex;
use tcx_constants::{CoinInfo, CurveType, TEST_MNEMONIC, TEST_PASSWORD, TEST_PRIVATE_KEY};
use tcx_crypto::Key;
use tcx_primitive::{PublicKey, Secp256k1PublicKey, TypedPublicKey};

#[test]
fn test_from_private_key() {
Expand Down Expand Up @@ -247,7 +248,11 @@ mod tests {

for (i, coin_info) in coin_infos.iter().enumerate() {
let acc = keystore.derive_coin::<MockAddress>(&coin_info).unwrap();
assert_eq!(acc.public_key, excepts[i]);

let k1_pub_key =
Secp256k1PublicKey::from_slice(&Vec::from_hex_auto(excepts[i]).unwrap()).unwrap();
let public_key = TypedPublicKey::Secp256k1(k1_pub_key);
assert_eq!(acc.public_key, public_key);
}
}
}
2 changes: 1 addition & 1 deletion token-core/tcx-keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod signer;
pub use keystore::{
fingerprint_from_mnemonic, fingerprint_from_private_key, fingerprint_from_seed,
mnemonic_to_seed, Account, Address, HdKeystore, Keystore, KeystoreGuard, Metadata,
PrivateKeystore, Source,
PrivateKeystore, PublicKeyEncoder, Source,
};

pub use signer::{HashSigner, MessageSigner, SignatureParameters, Signer, TransactionSigner};
Expand Down
2 changes: 1 addition & 1 deletion token-core/tcx-primitive/src/bls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::ecc::{KeyError, PrivateKey as TraitPrivateKey, PublicKey as TraitPubl
use crate::Result;
use blst::min_pk::{PublicKey, SecretKey};

#[derive(Clone)]
#[derive(Clone, Debug, PartialEq)]
pub struct BLSPublicKey(PublicKey);

#[derive(Clone)]
Expand Down
Loading

0 comments on commit 5b11a85

Please sign in to comment.