use alloc::string::String;
use alloc::vec::Vec;
use crate::pallas_codec::{
minicbor::{self, bytes::ByteVec, Decode, Encode},
utils::{OrderPreservingProperties, TagWrap},
};
use crate::pallas_crypto::hash::Hash;
use crate::pallas_addresses::Error;
pub type Blake2b224 = Hash<28>;
pub type AddressId = Blake2b224;
pub type StakeholderId = Blake2b224;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum AddrDistr {
SingleKeyDistribution(StakeholderId),
BootstrapEraDistribution,
}
impl<'b, C> minicbor::Decode<'b, C> for AddrDistr {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
d.array()?;
let variant = d.u32()?;
match variant {
0 => Ok(AddrDistr::SingleKeyDistribution(d.decode_with(ctx)?)),
1 => Ok(AddrDistr::BootstrapEraDistribution),
_ => Err(minicbor::decode::Error::message(
"invalid variant for addrdstr",
)),
}
}
}
impl minicbor::Encode<()> for AddrDistr {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut (),
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
AddrDistr::SingleKeyDistribution(x) => {
e.array(2)?;
e.u32(0)?;
e.encode(x)?;
Ok(())
}
AddrDistr::BootstrapEraDistribution => {
e.array(1)?;
e.u32(1)?;
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum AddrType {
PubKey,
Script,
Redeem,
Other(u32),
}
impl<'b, C> minicbor::Decode<'b, C> for AddrType {
fn decode(
d: &mut minicbor::Decoder<'b>,
_ctx: &mut C,
) -> Result<Self, minicbor::decode::Error> {
let variant = d.u32()?;
match variant {
0 => Ok(AddrType::PubKey),
1 => Ok(AddrType::Script),
2 => Ok(AddrType::Redeem),
x => Ok(AddrType::Other(x)),
}
}
}
impl<C> minicbor::Encode<C> for AddrType {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
AddrType::PubKey => e.u32(0)?,
AddrType::Script => e.u32(1)?,
AddrType::Redeem => e.u32(2)?,
AddrType::Other(x) => e.u32(*x)?,
};
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum AddrAttrProperty {
AddrDistr(AddrDistr),
DerivationPath(ByteVec),
NetworkTag(ByteVec),
}
impl<'b, C> minicbor::Decode<'b, C> for AddrAttrProperty {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
let key = d.u8()?;
match key {
0 => Ok(AddrAttrProperty::AddrDistr(d.decode_with(ctx)?)),
1 => Ok(AddrAttrProperty::DerivationPath(d.decode_with(ctx)?)),
2 => Ok(AddrAttrProperty::NetworkTag(d.decode_with(ctx)?)),
_ => Err(minicbor::decode::Error::message(
"unknown tag for address attribute",
)),
}
}
}
impl<C> minicbor::Encode<C> for AddrAttrProperty {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match self {
AddrAttrProperty::AddrDistr(x) => {
e.u8(0)?;
e.encode(x)?;
Ok(())
}
AddrAttrProperty::DerivationPath(x) => {
e.u8(1)?;
e.encode(x)?;
Ok(())
}
AddrAttrProperty::NetworkTag(b) => {
e.u8(2)?;
e.encode(b)?;
Ok(())
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd)]
pub enum SpendingData {
PubKey(ByteVec),
Script(ByteVec),
Redeem(ByteVec),
}
impl<'b, C> minicbor::Decode<'b, C> for SpendingData {
fn decode(d: &mut minicbor::Decoder<'b>, ctx: &mut C) -> Result<Self, minicbor::decode::Error> {
d.array()?;
let key = d.u8()?;
match key {
0 => Ok(Self::PubKey(d.decode_with(ctx)?)),
1 => Ok(Self::Script(d.decode_with(ctx)?)),
2 => Ok(Self::Redeem(d.decode_with(ctx)?)),
_ => Err(minicbor::decode::Error::message(
"unknown tag for spending data",
)),
}
}
}
impl<C> minicbor::Encode<C> for SpendingData {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
_ctx: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(2)?;
match self {
Self::PubKey(x) => {
e.u8(0)?;
e.encode(x)?;
Ok(())
}
Self::Script(x) => {
e.u8(1)?;
e.encode(x)?;
Ok(())
}
Self::Redeem(b) => {
e.u8(2)?;
e.encode(b)?;
Ok(())
}
}
}
}
pub type AddrAttrs = OrderPreservingProperties<AddrAttrProperty>;
#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, PartialOrd)]
pub struct AddressPayload {
#[n(0)]
pub root: AddressId,
#[n(1)]
pub attributes: AddrAttrs,
#[n(2)]
pub addrtype: AddrType,
}
use cryptoxide::hashing::sha3_256;
impl AddressPayload {
pub fn hash_address_id(
addrtype: &AddrType,
spending_data: &SpendingData,
attributes: &AddrAttrs,
) -> Hash<28> {
let parts = (addrtype, spending_data, attributes);
let buf = minicbor::to_vec(parts).unwrap();
crate::pallas_crypto::hash::Hasher::<224>::hash(&sha3_256(&buf))
}
pub fn new(addrtype: AddrType, spending_data: SpendingData, attributes: AddrAttrs) -> Self {
AddressPayload {
root: Self::hash_address_id(&addrtype, &spending_data, &attributes),
attributes,
addrtype,
}
}
pub fn new_redeem(
pubkey: crate::pallas_crypto::key::ed25519::PublicKey,
network_tag: Option<Vec<u8>>,
) -> Self {
let spending_data = SpendingData::Redeem(ByteVec::from(Vec::from(pubkey.as_ref())));
let attributes = match network_tag {
Some(x) => vec![
AddrAttrProperty::NetworkTag(x.into()),
]
.into(),
None => vec![
]
.into(),
};
Self::new(AddrType::Redeem, spending_data, attributes)
}
}
impl From<AddressPayload> for ByronAddress {
fn from(value: AddressPayload) -> Self {
ByronAddress::from_decoded(value)
}
}
#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ByronAddress {
#[n(0)]
pub payload: TagWrap<ByteVec, 24>,
#[n(1)]
pub crc: u32,
}
const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
impl ByronAddress {
pub fn new(payload: &[u8], crc: u32) -> Self {
Self {
payload: TagWrap(ByteVec::from(Vec::from(payload))),
crc,
}
}
pub fn from_decoded(payload: AddressPayload) -> Self {
let payload = minicbor::to_vec(payload).unwrap();
let c = CRC.checksum(&payload);
ByronAddress::new(&payload, c)
}
pub fn from_bytes(value: &[u8]) -> Result<Self, Error> {
crate::pallas_codec::minicbor::decode(value).map_err(Error::InvalidByronCbor)
}
pub fn from_base58(value: &str) -> Result<Self, Error> {
let bytes = base58::FromBase58::from_base58(value).map_err(Error::BadBase58)?;
Self::from_bytes(&bytes)
}
pub fn typeid(&self) -> u8 {
0b1000
}
pub fn to_vec(&self) -> Vec<u8> {
crate::pallas_codec::minicbor::to_vec(self).unwrap()
}
pub fn to_base58(&self) -> String {
let bytes = self.to_vec();
base58::ToBase58::to_base58(bytes.as_slice())
}
pub fn to_hex(&self) -> String {
let bytes = self.to_vec();
hex::encode(bytes)
}
pub fn decode(&self) -> Result<AddressPayload, Error> {
minicbor::decode(&self.payload.0).map_err(Error::InvalidByronCbor)
}
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_VECTORS: [&str; 3] = [
"37btjrVyb4KDXBNC4haBVPCrro8AQPHwvCMp3RFhhSVWwfFmZ6wwzSK6JK1hY6wHNmtrpTf1kdbva8TCneM2YsiXT7mrzT21EacHnPpz5YyUdj64na",
"DdzFFzCqrht7PQiAhzrn6rNNoADJieTWBt8KeK9BZdUsGyX9ooYD9NpMCTGjQoUKcHN47g8JMXhvKogsGpQHtiQ65fZwiypjrC6d3a4Q",
"Ae2tdPwUPEZLs4HtbuNey7tK4hTKrwNwYtGqp7bDfCy2WdR3P6735W5Yfpe",
];
#[test]
fn roundtrip_base58() {
for vector in TEST_VECTORS {
let addr = ByronAddress::from_base58(vector).unwrap();
let ours = addr.to_base58();
assert_eq!(vector, ours);
}
}
#[test]
fn roundtrip_cbor() {
for vector in TEST_VECTORS {
let addr = ByronAddress::from_base58(vector).unwrap();
let addr = addr.decode().unwrap();
let addr = ByronAddress::from_decoded(addr);
let ours = addr.to_base58();
assert_eq!(vector, ours);
}
}
#[test]
fn payload_crc_matches() {
for vector in TEST_VECTORS {
let addr = ByronAddress::from_base58(vector).unwrap();
let crc2 = CRC.checksum(addr.payload.as_ref());
assert_eq!(crc2, addr.crc);
}
}
}