Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(addresses): Improve API ergonomics #148

Merged
merged 1 commit into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pallas-addresses/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ authors = [
hex = "0.4.3"
pallas-crypto = { version = "0.11.0", path = "../pallas-crypto" }
pallas-codec = { version = "0.11.0", path = "../pallas-codec" }
pallas-primitives = { version = "0.11.0", path = "../pallas-primitives" }
base58 = "0.2.0"
bech32 = "0.8.1"
thiserror = "1.0.31"
184 changes: 130 additions & 54 deletions pallas-addresses/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ pub enum Error {
}

pub type PaymentKeyHash = Hash<28>;

pub type StakeKeyHash = Hash<28>;

pub type ScriptHash = Hash<28>;

pub type Slot = u64;
Expand Down Expand Up @@ -104,31 +102,31 @@ impl Pointer {
/// The payment part of a Shelley address
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ShelleyPaymentPart {
PaymentKey(PaymentKeyHash),
Key(PaymentKeyHash),
Script(ScriptHash),
}

impl ShelleyPaymentPart {
fn payment_key(bytes: &[u8]) -> Result<Self, Error> {
slice_to_hash(bytes).map(ShelleyPaymentPart::PaymentKey)
pub fn key_hash(hash: Hash<28>) -> Self {
Self::Key(hash)
}

fn script(bytes: &[u8]) -> Result<Self, Error> {
slice_to_hash(bytes).map(ShelleyPaymentPart::Script)
pub fn script_hash(hash: Hash<28>) -> Self {
Self::Script(hash)
}

/// Get a reference to the inner hash of this address part
pub fn as_hash(&self) -> &Hash<28> {
match self {
Self::PaymentKey(x) => x,
Self::Key(x) => x,
Self::Script(x) => x,
}
}

/// Encodes this address as a sequence of bytes
pub fn to_vec(&self) -> Vec<u8> {
match self {
Self::PaymentKey(x) => x.to_vec(),
Self::Key(x) => x.to_vec(),
Self::Script(x) => x.to_vec(),
}
}
Expand All @@ -142,30 +140,30 @@ impl ShelleyPaymentPart {
/// The delegation part of a Shelley address
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ShelleyDelegationPart {
StakeKey(StakeKeyHash),
Key(StakeKeyHash),
Script(ScriptHash),
Pointer(Pointer),
Null,
}

impl ShelleyDelegationPart {
fn stake_key(bytes: &[u8]) -> Result<Self, Error> {
slice_to_hash(bytes).map(Self::StakeKey)
pub fn key_hash(hash: Hash<28>) -> Self {
Self::Key(hash)
}

fn script(bytes: &[u8]) -> Result<Self, Error> {
slice_to_hash(bytes).map(Self::Script)
pub fn script_hash(hash: Hash<28>) -> Self {
Self::Script(hash)
}

fn pointer(bytes: &[u8]) -> Result<Self, Error> {
pub fn from_pointer(bytes: &[u8]) -> Result<Self, Error> {
let pointer = Pointer::parse(bytes)?;
Ok(Self::Pointer(pointer))
}

/// Get a reference to the inner hash of this address part
pub fn as_hash(&self) -> Option<&Hash<28>> {
match self {
Self::StakeKey(x) => Some(x),
Self::Key(x) => Some(x),
Self::Script(x) => Some(x),
Self::Pointer(_) => todo!(),
Self::Null => todo!(),
Expand All @@ -174,7 +172,7 @@ impl ShelleyDelegationPart {

pub fn to_vec(&self) -> Vec<u8> {
match self {
Self::StakeKey(x) => x.to_vec(),
Self::Key(x) => x.to_vec(),
Self::Script(x) => x.to_vec(),
Self::Pointer(x) => x.to_vec(),
Self::Null => vec![],
Expand Down Expand Up @@ -208,6 +206,16 @@ pub enum Network {
Other(u8),
}

impl From<u8> for Network {
fn from(id: u8) -> Self {
match id {
0 => Network::Testnet,
1 => Network::Mainnet,
x => Network::Other(x),
}
}
}

/// A decoded Shelley address
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct ShelleyAddress(Network, ShelleyPaymentPart, ShelleyDelegationPart);
Expand Down Expand Up @@ -260,8 +268,9 @@ macro_rules! parse_shelley_fn {
($name:tt, $payment:tt, pointer) => {
fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
let net = parse_network(header);
let p1 = ShelleyPaymentPart::$payment(&payload[0..=27])?;
let p2 = ShelleyDelegationPart::pointer(&payload[28..])?;
let h1 = slice_to_hash(&payload[0..=27])?;
let p1 = ShelleyPaymentPart::$payment(h1);
let p2 = ShelleyDelegationPart::from_pointer(&payload[28..])?;
let addr = ShelleyAddress(net, p1, p2);

Ok(addr.into())
Expand All @@ -270,8 +279,10 @@ macro_rules! parse_shelley_fn {
($name:tt, $payment:tt, $delegation:tt) => {
fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
let net = parse_network(header);
let p1 = ShelleyPaymentPart::$payment(&payload[0..=27])?;
let p2 = ShelleyDelegationPart::$delegation(&payload[28..=55])?;
let h1 = slice_to_hash(&payload[0..=27])?;
let p1 = ShelleyPaymentPart::$payment(h1);
let h2 = slice_to_hash(&payload[28..=55])?;
let p2 = ShelleyDelegationPart::$delegation(h2);
let addr = ShelleyAddress(net, p1, p2);

Ok(addr.into())
Expand All @@ -280,7 +291,8 @@ macro_rules! parse_shelley_fn {
($name:tt, $payment:tt) => {
fn $name(header: u8, payload: &[u8]) -> Result<Address, Error> {
let net = parse_network(header);
let p1 = ShelleyPaymentPart::$payment(&payload[0..=27])?;
let h1 = slice_to_hash(&payload[0..=27])?;
let p1 = ShelleyPaymentPart::$payment(h1);
let addr = ShelleyAddress(net, p1, ShelleyDelegationPart::Null);

Ok(addr.into())
Expand All @@ -301,14 +313,14 @@ macro_rules! parse_stake_fn {
}

// types 0-7 are Shelley addresses
parse_shelley_fn!(parse_type_0, payment_key, stake_key);
parse_shelley_fn!(parse_type_1, script, stake_key);
parse_shelley_fn!(parse_type_2, payment_key, script);
parse_shelley_fn!(parse_type_3, script, script);
parse_shelley_fn!(parse_type_4, payment_key, pointer);
parse_shelley_fn!(parse_type_5, script, pointer);
parse_shelley_fn!(parse_type_6, payment_key);
parse_shelley_fn!(parse_type_7, script);
parse_shelley_fn!(parse_type_0, key_hash, key_hash);
parse_shelley_fn!(parse_type_1, script_hash, key_hash);
parse_shelley_fn!(parse_type_2, key_hash, script_hash);
parse_shelley_fn!(parse_type_3, script_hash, script_hash);
parse_shelley_fn!(parse_type_4, key_hash, pointer);
parse_shelley_fn!(parse_type_5, script_hash, pointer);
parse_shelley_fn!(parse_type_6, key_hash);
parse_shelley_fn!(parse_type_7, script_hash);

// type 8 (1000) are Byron addresses
fn parse_type_8(header: u8, payload: &[u8]) -> Result<Address, Error> {
Expand Down Expand Up @@ -345,22 +357,6 @@ fn bech32_to_address(bech32: &str) -> Result<Address, Error> {
bytes_to_address(&bytes)
}

fn address_to_bech32(addr: &Address) -> Result<String, Error> {
match addr {
Address::Byron(_) => Err(Error::InvalidForByron),
Address::Shelley(ref x) => {
let hrp = x.hrp()?;
let bytes = x.to_vec();
encode_bech32(&bytes, hrp)
}
Address::Stake(ref x) => {
let hrp = x.hrp()?;
let bytes = x.to_vec();
encode_bech32(&bytes, hrp)
}
}
}

impl Network {
pub fn is_mainnet(&self) -> bool {
matches!(self, Network::Mainnet)
Expand All @@ -380,9 +376,32 @@ impl ByronAddress {
pub fn typeid(&self) -> u8 {
0b1000
}

fn to_vec(&self) -> Vec<u8> {
self.0.clone()
}

pub fn to_hex(&self) -> String {
let bytes = self.to_vec();
hex::encode(bytes)
}
}

impl AsRef<[u8]> for ByronAddress {
fn as_ref(&self) -> &[u8] {
&self.0
}
}

impl ShelleyAddress {
pub fn new(
network: Network,
payment: ShelleyPaymentPart,
delegation: ShelleyDelegationPart,
) -> Self {
Self(network, payment, delegation)
}

/// Gets the network assoaciated with this address
pub fn network(&self) -> Network {
self.0
Expand All @@ -391,13 +410,13 @@ impl ShelleyAddress {
/// Gets a numeric id describing the type of the address
pub fn typeid(&self) -> u8 {
match (&self.1, &self.2) {
(ShelleyPaymentPart::PaymentKey(_), ShelleyDelegationPart::StakeKey(_)) => 0b0000,
(ShelleyPaymentPart::Script(_), ShelleyDelegationPart::StakeKey(_)) => 0b0001,
(ShelleyPaymentPart::PaymentKey(_), ShelleyDelegationPart::Script(_)) => 0b0010,
(ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Key(_)) => 0b0000,
(ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Key(_)) => 0b0001,
(ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Script(_)) => 0b0010,
(ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Script(_)) => 0b0011,
(ShelleyPaymentPart::PaymentKey(_), ShelleyDelegationPart::Pointer(_)) => 0b0100,
(ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Pointer(_)) => 0b0100,
(ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Pointer(_)) => 0b0101,
(ShelleyPaymentPart::PaymentKey(_), ShelleyDelegationPart::Null) => 0b0110,
(ShelleyPaymentPart::Key(_), ShelleyDelegationPart::Null) => 0b0110,
(ShelleyPaymentPart::Script(_), ShelleyDelegationPart::Null) => 0b0111,
}
}
Expand Down Expand Up @@ -435,6 +454,17 @@ impl ShelleyAddress {
[&[header], payment.as_slice(), delegation.as_slice()].concat()
}

pub fn to_hex(&self) -> String {
let bytes = self.to_vec();
hex::encode(bytes)
}

pub fn to_bech32(&self) -> Result<String, Error> {
let hrp = self.hrp()?;
let bytes = self.to_vec();
encode_bech32(&bytes, hrp)
}

/// Indicates if either the payment or delegation part is a script
pub fn has_script(&self) -> bool {
self.payment().is_script() || self.delegation().is_script()
Expand Down Expand Up @@ -493,6 +523,17 @@ impl StakeAddress {
[&[header], self.1.as_ref()].concat()
}

pub fn to_hex(&self) -> String {
let bytes = self.to_vec();
hex::encode(bytes)
}

pub fn to_bech32(&self) -> Result<String, Error> {
let hrp = self.hrp()?;
let bytes = self.to_vec();
encode_bech32(&bytes, hrp)
}

pub fn is_script(&self) -> bool {
self.payload().is_script()
}
Expand All @@ -501,7 +542,11 @@ impl StakeAddress {
impl Address {
/// Tries to encode an Address into a bech32 string
pub fn to_bech32(&self) -> Result<String, Error> {
address_to_bech32(self)
match self {
Address::Byron(_) => Err(Error::InvalidForByron),
Address::Shelley(x) => x.to_bech32(),
Address::Stake(x) => x.to_bech32(),
}
}

/// Tries to parse a bech32 address into an Address
Expand Down Expand Up @@ -552,6 +597,22 @@ impl Address {
_ => false,
}
}

pub fn to_vec(&self) -> Vec<u8> {
match self {
Address::Byron(x) => x.to_vec(),
Address::Shelley(x) => x.to_vec(),
Address::Stake(x) => x.to_vec(),
}
}

pub fn to_hex(&self) -> String {
match self {
Address::Byron(x) => x.to_hex(),
Address::Shelley(x) => x.to_hex(),
Address::Stake(x) => x.to_hex(),
}
}
}

impl From<ByronAddress> for Address {
Expand Down Expand Up @@ -639,7 +700,7 @@ mod tests {
match addr {
Address::Shelley(x) => {
match x.payment() {
ShelleyPaymentPart::PaymentKey(hash) => {
ShelleyPaymentPart::Key(hash) => {
let expected = &hash_vector_key(PAYMENT_PUBLIC_KEY);
assert_eq!(hash, expected);
}
Expand All @@ -651,7 +712,7 @@ mod tests {
};

match x.delegation() {
ShelleyDelegationPart::StakeKey(hash) => {
ShelleyDelegationPart::Key(hash) => {
let expected = &hash_vector_key(STAKE_PUBLIC_KEY);
assert_eq!(hash, expected);
}
Expand Down Expand Up @@ -683,4 +744,19 @@ mod tests {
};
}
}

#[test]
fn construct_from_parts() {
let payment_hash = hash_vector_key(PAYMENT_PUBLIC_KEY);
let delegation_hash = hash_vector_key(STAKE_PUBLIC_KEY);

let addr: Address = ShelleyAddress::new(
Network::Mainnet,
ShelleyPaymentPart::key_hash(payment_hash),
ShelleyDelegationPart::key_hash(delegation_hash),
)
.into();

assert_eq!(addr.to_bech32().unwrap(), MAINNET_TEST_VECTORS[0].0);
}
}