use crate::legacy_address::{ExtendedAddr, ByronAddressType};
use crate::*;
use bech32::ToBase32;
use ed25519_bip32::XPub;
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum AddressKind {
Base,
Pointer,
Enterprise,
Reward,
Byron,
Malformed,
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NetworkInfo {
network_id: u8,
protocol_magic: u32,
}
#[wasm_bindgen]
impl NetworkInfo {
pub fn new(network_id: u8, protocol_magic: u32) -> Self {
Self {
network_id,
protocol_magic,
}
}
pub fn network_id(&self) -> u8 {
self.network_id
}
pub fn protocol_magic(&self) -> u32 {
self.protocol_magic
}
pub fn testnet_preview() -> NetworkInfo {
NetworkInfo {
network_id: 0b0000,
protocol_magic: 2,
}
}
pub fn testnet_preprod() -> NetworkInfo {
NetworkInfo {
network_id: 0b0000,
protocol_magic: 1,
}
}
pub fn mainnet() -> NetworkInfo {
NetworkInfo {
network_id: 0b0001,
protocol_magic: 764824073,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct MalformedAddress(pub(crate) Vec<u8>);
#[wasm_bindgen]
impl MalformedAddress {
pub fn original_bytes(&self) -> Vec<u8> {
self.0.clone()
}
pub fn to_address(&self) -> Address {
Address(AddrType::Malformed(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<MalformedAddress> {
match &addr.0 {
AddrType::Malformed(malformed) => Some(malformed.clone()),
_ => None,
}
}
}
impl serde::Serialize for MalformedAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let bech32 = self
.to_address()
.to_bech32(None)
.map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?;
serializer.serialize_str(&bech32)
}
}
impl<'de> serde::de::Deserialize<'de> for MalformedAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bech32 = <String as serde::de::Deserialize>::deserialize(deserializer)?;
match Address::from_bech32(&bech32).map(|addr| addr.0)
{
Ok(AddrType::Malformed(malformed_address)) => Ok(malformed_address),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&bech32),
&"bech32 malformed address string",
)),
}
}
}
impl JsonSchema for MalformedAddress {
fn schema_name() -> String {
String::from("MalformedAddress")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub(crate) enum AddrType {
Base(BaseAddress),
Ptr(PointerAddress),
Enterprise(EnterpriseAddress),
Reward(RewardAddress),
Byron(ByronAddress),
Malformed(MalformedAddress),
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct ByronAddress(pub(crate) ExtendedAddr);
#[wasm_bindgen]
impl ByronAddress {
pub fn to_base58(&self) -> String {
format!("{}", self.0)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut addr_bytes = Serializer::new_vec();
self.0.serialize(&mut addr_bytes).unwrap();
addr_bytes.finalize()
}
pub fn from_bytes(bytes: Vec<u8>) -> Result<ByronAddress, JsError> {
let mut raw = Deserializer::from(std::io::Cursor::new(bytes));
let extended_addr = ExtendedAddr::deserialize(&mut raw)?;
Ok(ByronAddress(extended_addr))
}
pub fn byron_protocol_magic(&self) -> u32 {
self.0.attributes.protocol_magic.unwrap_or_else(|| NetworkInfo::mainnet().protocol_magic())
}
pub fn byron_address_kind(&self) -> ByronAddressType {
self.0.addr_type.clone()
}
pub fn attributes(&self) -> Vec<u8> {
let mut attributes_bytes = Serializer::new_vec();
self.0.attributes.serialize(&mut attributes_bytes).unwrap();
attributes_bytes.finalize()
}
pub fn network_id(&self) -> Result<u8, JsError> {
let protocol_magic = self.byron_protocol_magic();
match protocol_magic {
magic if magic == NetworkInfo::mainnet().protocol_magic() => {
Ok(NetworkInfo::mainnet().network_id())
}
magic if magic == NetworkInfo::testnet_preprod().protocol_magic() => {
Ok(NetworkInfo::testnet_preprod().network_id())
}
magic if magic == NetworkInfo::testnet_preview().protocol_magic() => {
Ok(NetworkInfo::testnet_preview().network_id())
}
_ => Err(JsError::from_str(
&format! {"Unknown network {}", protocol_magic},
)),
}
}
pub fn from_base58(s: &str) -> Result<ByronAddress, JsError> {
use std::str::FromStr;
ExtendedAddr::from_str(s)
.map_err(|e| JsError::from_str(&format! {"{:?}", e}))
.map(ByronAddress)
}
pub fn icarus_from_key(key: &Bip32PublicKey, protocol_magic: u32) -> ByronAddress {
let mut out = [0u8; 64];
out.clone_from_slice(&key.as_bytes());
let filtered_protocol_magic = if protocol_magic == NetworkInfo::mainnet().protocol_magic() {
None
} else {
Some(protocol_magic)
};
ByronAddress(ExtendedAddr::new_simple(
&XPub::from_bytes(out),
filtered_protocol_magic,
))
}
pub fn is_valid(s: &str) -> bool {
use std::str::FromStr;
match ExtendedAddr::from_str(s) {
Ok(_v) => true,
Err(_err) => false,
}
}
pub fn to_address(&self) -> Address {
Address(AddrType::Byron(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<ByronAddress> {
match &addr.0 {
AddrType::Byron(byron) => Some(byron.clone()),
_ => None,
}
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Address(pub(crate) AddrType);
from_bytes!(Address, data, { Self::from_bytes_impl_safe(data.as_ref()) });
to_from_json!(Address);
impl serde::Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let bech32 = self
.to_bech32(None)
.map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?;
serializer.serialize_str(&bech32)
}
}
impl<'de> serde::de::Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bech32 = <String as serde::de::Deserialize>::deserialize(deserializer)?;
Address::from_bech32(&bech32).map_err(|_e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&bech32),
&"bech32 address string",
)
})
}
}
impl JsonSchema for Address {
fn schema_name() -> String {
String::from("Address")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
#[wasm_bindgen]
impl Address {
pub fn kind(&self) -> AddressKind {
match &self.0 {
AddrType::Base(_) => AddressKind::Base,
AddrType::Ptr(_) => AddressKind::Pointer,
AddrType::Enterprise(_) => AddressKind::Enterprise,
AddrType::Reward(_) => AddressKind::Reward,
AddrType::Byron(_) => AddressKind::Byron,
AddrType::Malformed(_) => AddressKind::Malformed,
}
}
pub fn payment_cred(&self) -> Option<Credential> {
match &self.0 {
AddrType::Base(a) => Some(a.payment_cred()),
AddrType::Enterprise(a) => Some(a.payment_cred()),
AddrType::Reward(a) => Some(a.payment_cred()),
AddrType::Ptr(a) => Some(a.payment_cred()),
AddrType::Byron(_) => None,
AddrType::Malformed(_) => None,
}
}
pub fn is_malformed(&self) -> bool {
matches!(&self.0, AddrType::Malformed(_))
}
pub fn to_hex(&self) -> String {
hex::encode(self.to_bytes())
}
pub fn from_hex(hex_str: &str) -> Result<Address, JsError> {
match hex::decode(hex_str) {
Ok(data) => Ok(Self::from_bytes_impl_safe(data.as_ref())?),
Err(e) => Err(JsError::from_str(&e.to_string())),
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut buf = Vec::new();
match &self.0 {
AddrType::Base(base) => {
let header: u8 = ((base.payment.kind() as u8) << 4)
| ((base.stake.kind() as u8) << 5)
| (base.network & 0xF);
buf.push(header);
buf.extend(base.payment.to_raw_bytes());
buf.extend(base.stake.to_raw_bytes());
}
AddrType::Ptr(ptr) => {
let header: u8 =
0b0100_0000 | ((ptr.payment.kind() as u8) << 4) | (ptr.network & 0xF);
buf.push(header);
buf.extend(ptr.payment.to_raw_bytes());
buf.extend(ptr.stake.to_bytes());
}
AddrType::Enterprise(enterprise) => {
let header: u8 = 0b0110_0000
| ((enterprise.payment.kind() as u8) << 4)
| (enterprise.network & 0xF);
buf.push(header);
buf.extend(enterprise.payment.to_raw_bytes());
}
AddrType::Reward(reward) => {
let header: u8 =
0b1110_0000 | ((reward.payment.kind() as u8) << 4) | (reward.network & 0xF);
buf.push(header);
buf.extend(reward.payment.to_raw_bytes());
}
AddrType::Byron(byron) => buf.extend(byron.to_bytes()),
AddrType::Malformed(malformed) => buf.extend(malformed.0.clone()),
}
buf
}
fn from_bytes_impl_safe(data: &[u8]) -> Result<Address, DeserializeError> {
Self::from_bytes_internal_impl(data, false)
}
fn from_bytes_impl_unsafe(data: &[u8]) -> Address {
match Self::from_bytes_internal_impl(data, true) {
Ok(addr) => addr,
Err(err) => {
println!("Address deserialization error: {:?}", err);
return Address(AddrType::Malformed(MalformedAddress(data.to_vec())));
}
}
}
fn from_bytes_internal_impl(data: &[u8], ignore_leftover_bytes: bool) -> Result<Address, DeserializeError> {
use std::convert::TryInto;
(|| -> Result<Self, DeserializeError> {
let header = data[0];
let network = header & 0x0F;
const HASH_LEN: usize = Ed25519KeyHash::BYTE_COUNT;
if ScriptHash::BYTE_COUNT != HASH_LEN {
return Err(DeserializeFailure::CustomError(
"ScriptHash and Ed25519KeyHash must have the same length".to_string(),
).into());
}
let read_addr_cred = |bit: u8, pos: usize| {
let hash_bytes: [u8; HASH_LEN] = data[pos..pos + HASH_LEN].try_into().unwrap();
let x = if header & (1 << bit) == 0 {
Credential::from_keyhash(&Ed25519KeyHash::from(hash_bytes))
} else {
Credential::from_scripthash(&ScriptHash::from(hash_bytes))
};
x
};
let addr: Result<AddrType, DeserializeError> = match (header & 0xF0) >> 4 {
0b0000 | 0b0001 | 0b0010 | 0b0011 => {
const BASE_ADDR_SIZE: usize = 1 + HASH_LEN * 2;
if data.len() < BASE_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), BASE_ADDR_SIZE).into())
} else if data.len() > BASE_ADDR_SIZE && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Base(BaseAddress::new(
network,
&read_addr_cred(4, 1),
&read_addr_cred(5, 1 + HASH_LEN),
)))
}
}
0b0100 | 0b0101 => {
const PTR_ADDR_MIN_SIZE: usize = 1 + HASH_LEN + 1 + 1 + 1;
if data.len() < PTR_ADDR_MIN_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), PTR_ADDR_MIN_SIZE).into())
} else {
let mut byte_index = 1;
let payment_cred = read_addr_cred(4, 1);
byte_index += HASH_LEN;
match Pointer::from_bytes(&data[byte_index..]) {
Ok((pointer, offset)) => {
byte_index += offset;
if byte_index < data.len() && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Ptr(PointerAddress::new(
network,
&payment_cred,
&pointer,
)))
}
}
Err(err) => Err(err)
}
}
}
0b0110 | 0b0111 => {
const ENTERPRISE_ADDR_SIZE: usize = 1 + HASH_LEN;
if data.len() < ENTERPRISE_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), ENTERPRISE_ADDR_SIZE).into())
} else {
if data.len() > ENTERPRISE_ADDR_SIZE && !ignore_leftover_bytes {
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Enterprise(EnterpriseAddress::new(
network,
&read_addr_cred(4, 1),
)))
}
}
}
0b1110 | 0b1111 => {
const REWARD_ADDR_SIZE: usize = 1 + HASH_LEN;
if data.len() < REWARD_ADDR_SIZE {
Err(cbor_event::Error::NotEnough(data.len(), REWARD_ADDR_SIZE).into())
} else {
if data.len() > REWARD_ADDR_SIZE && !ignore_leftover_bytes{
Err(cbor_event::Error::TrailingData.into())
} else {
Ok(AddrType::Reward(RewardAddress::new(
network,
&read_addr_cred(4, 1),
)))
}
}
}
0b1000 => {
match ByronAddress::from_bytes(data.to_vec()) {
Ok(addr) => Ok(AddrType::Byron(addr)),
Err(e) => Err(cbor_event::Error::CustomError(
e.as_string().unwrap_or_default(),
)
.into()),
}
}
_ => Err(DeserializeFailure::BadAddressType(header).into()),
};
Ok(Address(addr?))
})()
.map_err(|e| e.annotate("Address"))
}
pub fn to_bech32(&self, prefix: Option<String>) -> Result<String, JsError> {
let final_prefix = match prefix {
Some(prefix) => prefix,
None => {
let prefix_header = match &self.0 {
AddrType::Reward(_) => "stake",
_ => "addr",
};
let prefix_tail = if self.is_malformed() {
"_malformed"
} else {
match self.network_id()? {
id if id == NetworkInfo::testnet_preprod().network_id() => "_test",
id if id == NetworkInfo::testnet_preview().network_id() => "_test",
_ => "",
}
};
format!("{}{}", prefix_header, prefix_tail)
}
};
bech32::encode(&final_prefix, self.to_bytes().to_base32())
.map_err(|e| JsError::from_str(&format! {"{:?}", e}))
}
pub fn from_bech32(bech_str: &str) -> Result<Address, JsError> {
let (_hrp, u5data) =
bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?;
let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data)
.map_err(|_| JsError::from_str("Can't decode data from base32"))?;
Ok(Self::from_bytes_impl_safe(data.as_ref())?)
}
pub fn network_id(&self) -> Result<u8, JsError> {
match &self.0 {
AddrType::Base(a) => Ok(a.network),
AddrType::Enterprise(a) => Ok(a.network),
AddrType::Ptr(a) => Ok(a.network),
AddrType::Reward(a) => Ok(a.network),
AddrType::Byron(a) => a.network_id(),
AddrType::Malformed(_) => Err(JsError::from_str("Malformed address")),
}
}
}
impl cbor_event::se::Serialize for Address {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_bytes(self.to_bytes())
}
}
impl Deserialize for Address {
fn deserialize<R: BufRead>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
Ok(Self::from_bytes_impl_unsafe(raw.bytes()?.as_ref()))
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct BaseAddress {
pub(crate) network: u8,
pub(crate) payment: Credential,
pub(crate) stake: Credential,
}
#[wasm_bindgen]
impl BaseAddress {
pub fn new(network: u8, payment: &Credential, stake: &Credential) -> Self {
Self {
network,
payment: payment.clone(),
stake: stake.clone(),
}
}
pub fn payment_cred(&self) -> Credential {
self.payment.clone()
}
pub fn stake_cred(&self) -> Credential {
self.stake.clone()
}
pub fn to_address(&self) -> Address {
Address(AddrType::Base(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<BaseAddress> {
match &addr.0 {
AddrType::Base(base) => Some(base.clone()),
_ => None,
}
}
pub fn network_id(&self) -> u8 {
self.network
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct EnterpriseAddress {
pub(crate) network: u8,
pub(crate) payment: Credential,
}
#[wasm_bindgen]
impl EnterpriseAddress {
pub fn new(network: u8, payment: &Credential) -> Self {
Self {
network,
payment: payment.clone(),
}
}
pub fn payment_cred(&self) -> Credential {
self.payment.clone()
}
pub fn to_address(&self) -> Address {
Address(AddrType::Enterprise(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<EnterpriseAddress> {
match &addr.0 {
AddrType::Enterprise(enterprise) => Some(enterprise.clone()),
_ => None,
}
}
pub fn network_id(&self) -> u8 {
self.network
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RewardAddress {
pub(crate) network: u8,
pub(crate) payment: Credential,
}
#[wasm_bindgen]
impl RewardAddress {
pub fn new(network: u8, payment: &Credential) -> Self {
Self {
network,
payment: payment.clone(),
}
}
pub fn payment_cred(&self) -> Credential {
self.payment.clone()
}
pub fn to_address(&self) -> Address {
Address(AddrType::Reward(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<RewardAddress> {
match &addr.0 {
AddrType::Reward(reward) => Some(reward.clone()),
_ => None,
}
}
pub fn network_id(&self) -> u8 {
self.network
}
}
impl serde::Serialize for RewardAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let bech32 = self
.to_address()
.to_bech32(None)
.map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?;
serializer.serialize_str(&bech32)
}
}
impl<'de> serde::de::Deserialize<'de> for RewardAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let bech32 = <String as serde::de::Deserialize>::deserialize(deserializer)?;
match Address::from_bech32(&bech32)
.ok()
.map(|addr| RewardAddress::from_address(&addr))
{
Some(Some(ra)) => Ok(ra),
_ => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&bech32),
&"bech32 reward address string",
)),
}
}
}
impl JsonSchema for RewardAddress {
fn schema_name() -> String {
String::from("RewardAddress")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
impl cbor_event::se::Serialize for RewardAddress {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
self.to_address().serialize(serializer)
}
}
impl Deserialize for RewardAddress {
fn deserialize<R: BufRead>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<Self, DeserializeError> {
let bytes = raw.bytes()?;
match Address::from_bytes_impl_safe(bytes.as_ref())?.0 {
AddrType::Reward(ra) => Ok(ra),
_other_address => Err(DeserializeFailure::BadAddressType(bytes[0]).into()),
}
})()
.map_err(|e| e.annotate("RewardAddress"))
}
}
#[wasm_bindgen]
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct PointerAddress {
pub(crate) network: u8,
pub(crate) payment: Credential,
pub(crate) stake: Pointer,
}
#[wasm_bindgen]
impl PointerAddress {
pub fn new(network: u8, payment: &Credential, stake: &Pointer) -> Self {
Self {
network,
payment: payment.clone(),
stake: stake.clone(),
}
}
pub fn payment_cred(&self) -> Credential {
self.payment.clone()
}
pub fn stake_pointer(&self) -> Pointer {
self.stake.clone()
}
pub fn to_address(&self) -> Address {
Address(AddrType::Ptr(self.clone()))
}
pub fn from_address(addr: &Address) -> Option<PointerAddress> {
match &addr.0 {
AddrType::Ptr(ptr) => Some(ptr.clone()),
_ => None,
}
}
pub fn network_id(&self) -> u8 {
self.network
}
}