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

[drft] drp xtndd #700

Closed
wants to merge 1 commit into from
Closed
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
123 changes: 116 additions & 7 deletions rust/src/protocol_types/governance/drep.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::prelude::v1::Ok;
use crate::*;
use bech32::ToBase32;

Expand Down Expand Up @@ -29,6 +30,93 @@ pub enum DRepKind {
AlwaysNoConfidence,
}

#[wasm_bindgen]
#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub enum DRepExtendedId {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion to fully implement CIP-129 GovernanceIdentifier to not have N implementations of different things that just are subset of GovernanceIdentifier

Key(Ed25519KeyHash),
Script(ScriptHash),
}

const DREP_CIP129_PREFIX_KEY: u8 = 34;
const DREP_CIP129_PREFIX_SCRIPT: u8 = 35;

#[wasm_bindgen]
impl DRepExtendedId {

pub fn from_bytes(data: &Vec<u8>) -> Result<DRepExtendedId, JsError> {
if data.len() != 29 {
return Err(JsError::from_str("Malformed DRep Extended ID (incorrect len)"));
}
let prefix = data.get(0)
.ok_or(JsError::from_str("Malformed DRep (unexpected failure to get bytes prefix)"))?;
match prefix {
&DREP_CIP129_PREFIX_KEY => return Ok(
DRepExtendedId::Key(
Ed25519KeyHash::from_bytes(data[1..].to_vec())
.map_err(|_| JsError::from_str("Malformed DRep KeyHash"))?,
)
),
&DREP_CIP129_PREFIX_SCRIPT => return Ok(
DRepExtendedId::Script(
ScriptHash::from_bytes(data[1..].to_vec())
.map_err(|_| JsError::from_str("Malformed DRep ScriptHash"))?,
)
),
_ => return Err(JsError::from_str("Malformed DRep Extended ID (incorrect prefix byte)"))
}
}

pub fn from_hex(hex_str: &str) -> Result<DRepExtendedId, JsError> {
DRepExtendedId::from_bytes(
&hex::decode(hex_str)
.map_err(|e| JsError::from_str(&e.to_string()))?
)
}

pub fn from_bech32(bech32_str: &str) -> Result<DRepExtendedId, JsError> {
let (hrp, u5data) =
bech32::decode(bech32_str).map_err(|e| JsError::from_str(&e.to_string()))?;
if hrp != "drep" {
return Err(JsError::from_str("Malformed DRep Extended ID (incorrect prefix)"));
}
let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data)
.map_err(|e: bech32::Error| JsError::from_str(&format!("Malformed DRep base32: {}", &e.to_string())))?;
DRepExtendedId::from_bytes(&data)
}

pub fn to_hex(&self) -> String {
hex::encode(self.to_bytes())
}

pub fn to_bytes(&self) -> Vec<u8> {
let (prefix, data) = match self {
DRepExtendedId::Key(keyhash) => (DREP_CIP129_PREFIX_KEY, keyhash.to_bytes()),
DRepExtendedId::Script(scripthash) => (DREP_CIP129_PREFIX_SCRIPT, scripthash.to_bytes()),
};
let mut res = vec![prefix];
res.extend(data);
res
}

pub fn to_bech32(&self) -> Result<String, JsError> {
bech32::encode("drep", self.to_bytes().to_base32()).map_err(|e| JsError::from_str(&format! {"{:?}", e}))
}

pub fn kind(&self) -> DRepKind {
match self {
DRepExtendedId::Key(_) => DRepKind::KeyHash,
DRepExtendedId::Script(_) => DRepKind::ScriptHash,
}
}

pub fn to_drep(&self) -> DRep {
match self {
DRepExtendedId::Key(key) => DRep(DRepEnum::KeyHash(key.clone())),
DRepExtendedId::Script(script) => DRep(DRepEnum::ScriptHash(script.clone())),
}
}
}

#[derive(
Clone,
Debug,
Expand Down Expand Up @@ -95,9 +183,17 @@ impl DRep {
}
}

pub fn to_bech32(&self) -> Result<String, JsError> {
pub fn to_extended_id(&self) -> Option<DRepExtendedId> {
match &self.0 {
DRepEnum::KeyHash(x) => Some(DRepExtendedId::Key(x.clone())),
DRepEnum::ScriptHash(x) => Some(DRepExtendedId::Script(x.clone())),
_ => None
}
}

fn internal_to_bech32(&self, vkh_prefix: String) -> Result<String, JsError> {
let (hrp, data) = match &self.0 {
DRepEnum::KeyHash(keyhash) => Ok(("drep", keyhash.to_bytes())),
DRepEnum::KeyHash(keyhash) => Ok((vkh_prefix.as_str(), keyhash.to_bytes())),
DRepEnum::ScriptHash(scripthash) => Ok(("drep_script", scripthash.to_bytes())),
DRepEnum::AlwaysAbstain => {
Err(JsError::from_str("Cannot convert AlwaysAbstain to bech32"))
Expand All @@ -109,23 +205,36 @@ impl DRep {
bech32::encode(&hrp, data.to_base32()).map_err(|e| JsError::from_str(&format! {"{:?}", e}))
}

pub fn to_bech32(&self) -> Result<String, JsError> {
self.internal_to_bech32("drep".to_string())
}

pub fn to_bech32_cip129(&self) -> Result<String, JsError> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably we need to encode only in cip129 format and do not provide API to encode in a old format when cip129 will be merged

self.internal_to_bech32("drep_vkh".to_string())
}

pub fn from_bech32(bech32_str: &str) -> Result<DRep, JsError> {
let (hrp, u5data) =
bech32::decode(bech32_str).map_err(|e| JsError::from_str(&e.to_string()))?;
let data: Vec<u8> = bech32::FromBase32::from_base32(&u5data)
.map_err(|_| JsError::from_str("Malformed DRep"))?;
.map_err(|e: bech32::Error| JsError::from_str(&format!("Malformed DRep base32: {}", &e.to_string())))?;
let kind = match hrp.as_str() {
"drep" => DRepKind::KeyHash,
"drep" => match data.len() {
28 => DRepKind::KeyHash, // pre cip129 compatibility
29 => return DRepExtendedId::from_bech32(bech32_str).and_then(|id| Ok(id.to_drep())),
_ => return Err(JsError::from_str("Malformed DRep (drep1 byte len)"))
},
"drep_vkh" => DRepKind::KeyHash,
"drep_script" => DRepKind::ScriptHash,
_ => return Err(JsError::from_str("Malformed DRep")),
_ => return Err(JsError::from_str("Malformed DRep (bech prefix)")),
};
let drep = match kind {
DRepKind::KeyHash => DRepEnum::KeyHash(
Ed25519KeyHash::from_bytes(data)
.map_err(|_| JsError::from_str("Malformed DRep"))?,
.map_err(|_| JsError::from_str("Malformed DRep KeyHash"))?,
),
DRepKind::ScriptHash => DRepEnum::ScriptHash(
ScriptHash::from_bytes(data).map_err(|_| JsError::from_str("Malformed DRep"))?,
ScriptHash::from_bytes(data).map_err(|_| JsError::from_str("Malformed DRep ScriptHash"))?,
),
DRepKind::AlwaysAbstain => DRepEnum::AlwaysAbstain,
DRepKind::AlwaysNoConfidence => DRepEnum::AlwaysNoConfidence,
Expand Down
53 changes: 53 additions & 0 deletions rust/src/tests/protocol_types/governance/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,56 @@ fn voting_procedures_setters_getters_test() {
assert!(governance_action_ids_2.0.contains(&governance_action_id_2));
assert!(governance_action_ids_2.0.contains(&governance_action_id_3));
}

#[test]
fn drep_bech32_129_parsing_key_test() {
let drep1 = DRep::from_bech32("drep1uz9v5d3vzyp3xwh8y2ceswqxqx4ljecfwd2kwnjysjapzh3avs7").unwrap();
let drep2 = DRep::from_bech32("drep1ytsg4j3k9sgsxye6uu3trxpcqcq6h7t8p9e42e6wgjzt5yggls86y").unwrap();
assert_eq!(drep1.kind(), DRepKind::KeyHash);
assert_eq!(drep2.kind(), DRepKind::KeyHash);
assert_eq!(drep1.to_key_hash().unwrap(), drep2.to_key_hash().unwrap());
}

#[test]
fn drep_bech32_129_parsing_script_test() {
let drep1 = DRep::from_bech32("drep_script1dja6lg0xdt4tfrd7r2svc3ywh5xqrl6w85axjp0gtdu6xw6h2wn").unwrap();
let drep2 = DRep::from_bech32("drep1ydkthtapue4w4dydhcd2pnzy367scq0lfc7n56g9apdhngcaf8d6w").unwrap();
assert_eq!(drep1.kind(), DRepKind::ScriptHash);
assert_eq!(drep2.kind(), DRepKind::ScriptHash);
assert_eq!(drep1.to_script_hash().unwrap(), drep2.to_script_hash().unwrap());

}

#[test]
fn drep_bech32_129_to_bech() {
let drep1 = DRep::from_bech32("drep1uz9v5d3vzyp3xwh8y2ceswqxqx4ljecfwd2kwnjysjapzh3avs7").unwrap();
let drep2 = DRep::from_bech32("drep_script1dja6lg0xdt4tfrd7r2svc3ywh5xqrl6w85axjp0gtdu6xw6h2wn").unwrap();
assert_eq!(drep1.to_bech32().unwrap(), "drep1uz9v5d3vzyp3xwh8y2ceswqxqx4ljecfwd2kwnjysjapzh3avs7");
assert_eq!(drep1.to_bech32_cip129().unwrap(), "drep_vkh1uz9v5d3vzyp3xwh8y2ceswqxqx4ljecfwd2kwnjysjapz3kx3ey");
assert_eq!(drep2.to_bech32().unwrap(), "drep_script1dja6lg0xdt4tfrd7r2svc3ywh5xqrl6w85axjp0gtdu6xw6h2wn");
assert_eq!(drep2.to_bech32_cip129().unwrap(), "drep_script1dja6lg0xdt4tfrd7r2svc3ywh5xqrl6w85axjp0gtdu6xw6h2wn");
}

#[test]
fn drep_bech32_129_extended_id() {
let drep1 = DRep::from_bech32("drep_vkh1uz9v5d3vzyp3xwh8y2ceswqxqx4ljecfwd2kwnjysjapz3kx3ey").unwrap();
let drep2 = DRep::from_bech32("drep_script1dja6lg0xdt4tfrd7r2svc3ywh5xqrl6w85axjp0gtdu6xw6h2wn").unwrap();
assert_eq!(drep1.to_extended_id().unwrap().to_bech32().unwrap(), "drep1ytsg4j3k9sgsxye6uu3trxpcqcq6h7t8p9e42e6wgjzt5yggls86y");
assert_eq!(drep1.to_extended_id().unwrap().to_hex(), "22e08aca362c1103133ae722b198380601abf967097355674e4484ba11");
assert_eq!(drep2.to_extended_id().unwrap().to_bech32().unwrap(), "drep1ydkthtapue4w4dydhcd2pnzy367scq0lfc7n56g9apdhngcaf8d6w");
assert_eq!(drep2.to_extended_id().unwrap().to_hex(), "236cbbafa1e66aeab48dbe1aa0cc448ebd0c01ff4e3d3a6905e85b79a3");
assert_eq!(DRep::new_always_abstain().to_extended_id(), None);
assert_eq!(DRep::new_always_no_confidence().to_extended_id(), None);
}

#[test]
fn drep_bech32_129_extended_id_hex_and_bytes() {
let bech1 = "drep1ytsg4j3k9sgsxye6uu3trxpcqcq6h7t8p9e42e6wgjzt5yggls86y";
let bech2 = "drep1ydkthtapue4w4dydhcd2pnzy367scq0lfc7n56g9apdhngcaf8d6w";
let drep1 = DRepExtendedId::from_bech32(bech1).unwrap();
let drep2 = DRepExtendedId::from_bech32(bech2).unwrap();
assert_eq!(DRepExtendedId::from_bytes(&drep1.to_bytes()).unwrap().to_bech32().unwrap(), bech1);
assert_eq!(DRepExtendedId::from_bytes(&drep2.to_bytes()).unwrap().to_bech32().unwrap(), bech2);
assert_eq!(DRepExtendedId::from_hex(&drep1.to_hex()).unwrap().to_bech32().unwrap(), bech1);
assert_eq!(DRepExtendedId::from_hex(&drep2.to_hex()).unwrap().to_bech32().unwrap(), bech2);
}
Loading