feat(addresses): Improve API ergonomics (#148)
This commit is contained in:
parent
4d2950db0b
commit
30231d10b0
2 changed files with 131 additions and 54 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -104,23 +102,23 @@ 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,
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +126,7 @@ impl ShelleyPaymentPart {
|
|||
/// 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(),
|
||||
}
|
||||
}
|
||||
|
|
@ -142,22 +140,22 @@ 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))
|
||||
}
|
||||
|
|
@ -165,7 +163,7 @@ impl ShelleyDelegationPart {
|
|||
/// 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!(),
|
||||
|
|
@ -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![],
|
||||
|
|
@ -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);
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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> {
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue