use sha2::Digest;
use std::fmt::{self, Display};
use thruput_xdr::contract::ContractEntrypointID;
pub mod remote;
pub mod semver;
#[derive(Debug)]
pub struct NormalizedMethodSignature {
method_name: String,
}
impl NormalizedMethodSignature {
#[cfg(feature = "syn")]
pub fn new_from_signature(method: &syn::Signature) -> Self {
Self {
method_name: method.ident.to_string(),
}
}
pub fn new(method_name: &str) -> Self {
Self {
method_name: method_name.to_string(),
}
}
pub fn get_signature_string(&self) -> String {
self.method_name.clone()
}
}
impl Display for NormalizedMethodSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.method_name.as_str())
}
}
#[derive(Debug)]
pub struct EntrypointSignature {
pub interface_name: Option<String>,
pub version_str: String,
pub method_sig: NormalizedMethodSignature,
}
impl EntrypointSignature {
#[cfg(feature = "syn")]
pub fn new(
interface_name: Option<&str>,
version_str: &str,
method: &syn::Signature,
) -> Self {
Self {
interface_name: interface_name.map(|s| s.to_string()),
version_str: version_str.to_string(),
method_sig: NormalizedMethodSignature::new_from_signature(method),
}
}
pub fn id(&self) -> ContractEntrypointID {
contract_entrypoint_id(
self.interface_name.as_deref(),
self.version_str.as_str(),
&self.method_sig,
)
}
}
impl Display for EntrypointSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}<{}>::{}",
self.interface_name
.as_deref()
.unwrap_or("_unnamed_interface"),
self.version_str,
self.method_sig,
)
}
}
pub const UNNAMED_INTERFACE: &str = "_unnamed_interface";
fn entrypoint_id_preimage<'a>(
interface_name: Option<&'a str>,
version_str: &str,
method: &NormalizedMethodSignature,
) -> (&'a str, String, String) {
(
interface_name.unwrap_or(UNNAMED_INTERFACE),
semver::major_version_from_string(version_str),
method.get_signature_string(),
)
}
pub fn contract_entrypoint_id(
interface_name: Option<&str>,
version_str: &str,
method: &NormalizedMethodSignature,
) -> ContractEntrypointID {
let (interface_name, major_version, method_sig) =
entrypoint_id_preimage(interface_name, version_str, method);
let mut hasher = sha2::Sha256::new();
hasher.update(interface_name.as_bytes());
hasher.update(b"::");
hasher.update(major_version.as_bytes());
hasher.update(b"::");
hasher.update(method_sig.as_bytes());
let result = hasher.finalize();
let entrypoint_id: [u8; 8] = result[0..8].try_into().unwrap();
ContractEntrypointID::from(entrypoint_id)
}
pub fn interface_id(
interface_name: Option<&str>,
version_str: &str,
) -> [u8; 8] {
let interface_name = interface_name.unwrap_or(UNNAMED_INTERFACE);
let major_version = semver::major_version_from_string(version_str);
let mut hasher = sha2::Sha256::new();
hasher.update(b"thruput-interface");
hasher.update(b"::");
hasher.update(interface_name.as_bytes());
hasher.update(b"::");
hasher.update(major_version.as_bytes());
let result = hasher.finalize();
result[0..8].try_into().unwrap()
}
pub fn entrypoint_export_name_for(
interface_name: Option<&str>,
version_str: &str,
method: &NormalizedMethodSignature,
) -> String {
entrypoint_export_name(contract_entrypoint_id(
interface_name,
version_str,
method,
))
}
pub fn entrypoint_export_name(id: ContractEntrypointID) -> String {
format!("{:016X}", u64::from_be_bytes(*id))
}
pub fn entrypoint_link_name(
interface_name: Option<&str>,
version_str: &str,
method: &NormalizedMethodSignature,
) -> String {
let (interface_name, major_version, method_sig) =
entrypoint_id_preimage(interface_name, version_str, method);
format!(
"__thruput_entrypoint::{interface_name}::{major_version}::{method_sig}"
)
}
pub fn exception_selector(
version_str: &str,
crate_name: &str,
type_name: &str,
) -> u64 {
let major_version = semver::major_version_from_string(version_str);
exception_selector_from_major(&major_version, crate_name, type_name)
}
pub fn exception_selector_from_major(
major_version: &str,
crate_name: &str,
type_name: &str,
) -> u64 {
let mut hasher = sha2::Sha256::new();
hasher.update(major_version.as_bytes());
hasher.update(b"::");
hasher.update(crate_name.as_bytes());
hasher.update(b"::");
hasher.update(type_name.as_bytes());
let result = hasher.finalize();
u64::from_be_bytes(result[..8].try_into().unwrap())
}
pub fn event_signature(
version_str: &str,
crate_name: &str,
type_name: &str,
) -> [u8; 32] {
let major_version = semver::major_version_from_string(version_str);
let mut hasher = sha2::Sha256::new();
hasher.update(major_version.as_bytes());
hasher.update(b"::");
hasher.update(crate_name.as_bytes());
hasher.update(b"::");
hasher.update(type_name.as_bytes());
hasher.finalize().into()
}
#[cfg(test)]
mod tests;