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

fix(rust/cardano-blockchain-types): cardano-blockchain-types base change #123

Merged
merged 55 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
dba341c
feat(rust): add cardano-blockchain-types crate
stevenj Dec 17, 2024
f3871a8
fix(rust): Remove unused dependencies
stevenj Dec 17, 2024
23a5138
fix(cardano-blockchain-types): time_to_slot calculation
bkioshn Dec 17, 2024
0662020
fix(cardano-blockchain-types): remove justfile
bkioshn Dec 17, 2024
c9185b5
Merge branch 'main' into feat/cardano-blockchain-types
bkioshn Dec 17, 2024
aa6bd36
fix(cardano-blockchain-types): point new should take type Slot and Bl…
bkioshn Dec 17, 2024
e027d8b
fix(cardano-blockchain-types): Fork type
bkioshn Dec 17, 2024
7efac20
fix(cardano-blockchain-types): point and fuzzy point test
bkioshn Dec 17, 2024
8ddebf5
Merge branch 'main' into feat/cardano-blockchain-types
stevenj Dec 17, 2024
1d600bd
fix(cardano-blockchain-types): add Fork increment function
bkioshn Dec 18, 2024
bfc2e15
fix(cardano-blockchain-types): add comment on tag 259
bkioshn Dec 18, 2024
d48e32b
fix(cardano-blockchain-types): add Fork decrement function
bkioshn Dec 18, 2024
349642c
test(rust): try earthly no-cache
bkioshn Dec 18, 2024
8583611
test(rust): try earthly no-cache and fix doc artifact
bkioshn Dec 18, 2024
42f71d7
test(rust): remove no-cache
bkioshn Dec 18, 2024
c267593
fix(cardano-blockchain-types): expose Fork and Network
bkioshn Dec 18, 2024
5eb13da
Merge branch 'main' into feat/cardano-blockchain-types
bkioshn Dec 18, 2024
c80fa33
fix(cardano-blockchain-types): add partailOrd to Fork
bkioshn Dec 18, 2024
0b0152d
Update rust/cardano-blockchain-types/src/point.rs
stevenj Dec 19, 2024
347045a
fix(cardano-blockchain-types): cleanup
bkioshn Dec 19, 2024
8d04f53
fix(cardano-blockchain-types): testdoc
bkioshn Dec 19, 2024
e48399a
Update rust/cardano-blockchain-types/src/point.rs
stevenj Dec 19, 2024
d17adf7
Merge branch 'main' into feat/cardano-blockchain-types
bkioshn Dec 19, 2024
48ca937
fix(cardano-blockchain-types): cleanup
bkioshn Dec 20, 2024
60802ce
fix(cardano-blockchain-types): format
bkioshn Dec 20, 2024
ea19d3e
fix(cardano-blockchain-types): add validate PR title
bkioshn Dec 20, 2024
4e2bdc6
fix(cardano-blockchain-types): comments
bkioshn Dec 20, 2024
519c415
fix(cardano-blockchain-types): fix hash_or_default
bkioshn Dec 20, 2024
e9b8ee3
fix(cardano-blockchain-types): redundant code
bkioshn Dec 20, 2024
0d964fd
test: no cache
bkioshn Dec 20, 2024
b553132
test: revert change
bkioshn Dec 20, 2024
881f07c
test ci
bkioshn Dec 23, 2024
be14a11
test ci
bkioshn Dec 23, 2024
c68a4a5
test ci
bkioshn Dec 23, 2024
b082ccc
test ci
bkioshn Dec 23, 2024
e5a1c53
test ci
bkioshn Dec 23, 2024
02cea97
test ci
bkioshn Dec 23, 2024
87b7aa7
test ci
bkioshn Dec 23, 2024
5150b0c
test ci
bkioshn Dec 23, 2024
7b51b80
test ci
bkioshn Dec 23, 2024
1ccf814
test ci
bkioshn Dec 23, 2024
6178014
revert change
bkioshn Dec 23, 2024
4291806
test ci
bkioshn Dec 23, 2024
fe48d5a
revert change
bkioshn Dec 23, 2024
2f176b2
fix(rust/cardano-blockchain-types): add more functionality to `Slot` …
bkioshn Dec 30, 2024
3c9befe
feat(rust/cardano-blockchain-types): Add CIP36 (#125)
bkioshn Dec 31, 2024
3c756fc
fix(cardano-blockchain-types): expose from_saturating (#131)
bkioshn Jan 2, 2025
a6643bf
Merge branch 'main' into fix/cardano-bc-types-base-change
bkioshn Jan 3, 2025
2c936f1
fix(cardano-blockchain-types): slot bigint conversion
bkioshn Jan 3, 2025
c7b88c7
fix(cardano-blockchain-types): txn index conversion
bkioshn Jan 3, 2025
ac9658e
fix(cardano-blockchain-types): conversion
bkioshn Jan 3, 2025
32609bc
Merge branch 'main' into fix/cardano-bc-types-base-change
stevenj Jan 3, 2025
543b893
fix(rust/cardano-blockchain-types): fix CIP36 (#133)
bkioshn Jan 6, 2025
1b2b089
fix(rust/cardano-blockchain-types): implement new error report for CI…
bkioshn Jan 9, 2025
3220b6e
Merge branch 'main' into fix/cardano-bc-types-base-change
bkioshn Jan 9, 2025
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
4 changes: 3 additions & 1 deletion rust/cardano-blockchain-types/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ dashmap = "6.1.0"
blake2b_simd = "1.0.2"
minicbor = { version = "0.25.1", features = ["alloc"] }
num-traits = "0.2.19"
ed25519-dalek = "2.1.1"
ed25519-dalek = "2.1.1"
serde = "1.0.210"
num-bigint = "0.4.6"
368 changes: 368 additions & 0 deletions rust/cardano-blockchain-types/src/cip36/key_registration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
//! CIP-36 Key Registration 61284.
//!
//! Catalyst registration data
//!
//! <https://cips.cardano.org/cip/CIP-36>
//! <https://github.com/cardano-foundation/CIPs/blob/master/CIP-0036/schema.cddl>

use std::collections::HashSet;

use anyhow::Context;
use ed25519_dalek::VerifyingKey;
use minicbor::{decode, Decode, Decoder};
use pallas::ledger::addresses::{Address, ShelleyAddress};
use strum::FromRepr;

use super::voting_pk::VotingPubKey;
use crate::utils::decode_helper::{decode_array_len, decode_bytes, decode_helper, decode_map_len};

/// CIP-36 key registration - 61284
///
///
/// ```cddl
/// key_registration = {
/// 1 : [+delegation] / legacy_key_registration,
/// 2 : $stake_credential,
/// 3 : $payment_address,
/// 4 : $nonce,
/// ? 5 : $voting_purpose .default 0
// }
/// ```
#[allow(clippy::module_name_repetitions)]
#[derive(Clone, Default, Debug)]
pub struct Cip36KeyRegistration {
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
/// Is this CIP36 or CIP15 format.
pub is_cip36: Option<bool>,
/// Voting public keys (called Delegations in the CIP-36 Spec).
/// Field 1 in the CIP-36 61284 Spec.
pub voting_pks: Vec<VotingPubKey>,
/// Stake public key to associate with the voting keys.
/// Field 2 in the CIP-36 61284 Spec.
pub stake_pk: VerifyingKey,
/// Payment Address to associate with the voting keys.
/// Field 3 in the CIP-36 61284 Spec.
pub payment_addr: Option<ShelleyAddress>,
/// Nonce (nonce that has been slot corrected).
/// Field 4 in the CIP-36 61284 Spec.
pub nonce: u64,
/// Registration Purpose (Always 0 for Catalyst).
/// Field 5 in the CIP-36 61284 Spec.
pub purpose: u64,
/// Raw nonce (nonce that has not had slot correction applied).
pub raw_nonce: u64,
/// Is payment address payable? (not a script)
pub is_payable: bool,
}

/// Enum of CIP36 registration (61284) with its associated unsigned integer key.
#[derive(FromRepr, Debug, PartialEq)]
#[repr(u16)]
pub enum Cip36KeyRegistrationKeys {
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
/// Voting key.
VotingKey = 1,
/// Stake public key.
StakePk = 2,
/// Payment address.
PaymentAddr = 3,
/// Nonce.
Nonce = 4,
/// Purpose.
Purpose = 5,
}

impl Decode<'_, ()> for Cip36KeyRegistration {
fn decode(d: &mut Decoder, ctx: &mut ()) -> Result<Self, decode::Error> {
let map_len = decode_map_len(d, "CIP36 Key Registration")?;

let mut cip36_key_registration = Cip36KeyRegistration::default();

// Record of founded keys. Check for duplicate keys in the map
let mut found_keys: HashSet<u16> = HashSet::new();

for _ in 0..map_len {
let key: u16 = decode_helper(d, "key in CIP36 Key Registration", ctx)?;

if let Some(key) = Cip36KeyRegistrationKeys::from_repr(key) {
match key {
Cip36KeyRegistrationKeys::VotingKey => {
if !found_keys.insert(key as u16) {
return Err(decode::Error::message(
"Duplicate key in CIP36 Key Registration voting key",
));
}
let (is_cip36, voting_keys) = decode_voting_key(d)?;
cip36_key_registration.is_cip36 = Some(is_cip36);
cip36_key_registration.voting_pks = voting_keys;
},
Cip36KeyRegistrationKeys::StakePk => {
if !found_keys.insert(key as u16) {
return Err(decode::Error::message(
"Duplicate key in CIP36 Key Registration stake public key",
));
}
let stake_pk = decode_stake_pk(d)?;
cip36_key_registration.stake_pk = stake_pk;
},
Cip36KeyRegistrationKeys::PaymentAddr => {
if !found_keys.insert(key as u16) {
return Err(decode::Error::message(
"Duplicate key in CIP36 Key Registration payment address",
));
}
let shelley_addr = decode_payment_addr(d)?;
cip36_key_registration.payment_addr = Some(shelley_addr.clone());
cip36_key_registration.is_payable = !shelley_addr.payment().is_script();
},
Cip36KeyRegistrationKeys::Nonce => {
if !found_keys.insert(key as u16) {
return Err(decode::Error::message(
"Duplicate key in CIP36 Key Registration nonce",
));
}
let raw_nonce = decode_nonce(d)?;
cip36_key_registration.raw_nonce = raw_nonce;
},
Cip36KeyRegistrationKeys::Purpose => {
if !found_keys.insert(key as u16) {
return Err(decode::Error::message(
"Duplicate key in CIP36 Key Registration purpose",
));
}
let purpose = decode_purpose(d)?;
cip36_key_registration.purpose = purpose;
},
}
}
}

// Check if all the required keys are present.
if found_keys.contains(&(Cip36KeyRegistrationKeys::VotingKey as u16))
&& found_keys.contains(&(Cip36KeyRegistrationKeys::StakePk as u16))
&& found_keys.contains(&(Cip36KeyRegistrationKeys::PaymentAddr as u16))
&& found_keys.contains(&(Cip36KeyRegistrationKeys::Nonce as u16))
{
Ok(cip36_key_registration)
} else {
Err(decode::Error::message(
"Missing required key in CIP36 Key Registration",
))
}
}
}

/// Helper function for decoding the voting key.
///
/// # Returns
///
/// A tuple containing a boolean value, true if it is CIP36 format, false if it is CIP15
/// format and a vector of voting public keys.
fn decode_voting_key(d: &mut Decoder) -> Result<(bool, Vec<VotingPubKey>), decode::Error> {
let mut voting_keys = Vec::new();
let mut is_cip36 = false;

match d.datatype()? {
// CIP15 type registration (single voting key).
// ```cddl
// legacy_key_registration = $cip36_vote_pub_key
// $cip36_vote_pub_key /= bytes .size 32
// ```
minicbor::data::Type::Bytes => {
let pub_key = decode_bytes(d, "CIP36 Key Registration voting key, single voting key")?;
let vk = voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| {
decode::Error::message(format!(
"CIP36 Key Registration voting key, singe voting key, {e}"
))
})?;
// Since there is 1 voting key, all the weight goes to this key = 1.
voting_keys.push(VotingPubKey {
voting_pk: vk,
weight: 1,
});
},
// CIP36 type registration (multiple voting keys).
// ```cddl
// [+delegation]
// delegation = [$cip36_vote_pub_key, $weight]
// $cip36_vote_pub_key /= bytes .size 32
// ```
minicbor::data::Type::Array => {
is_cip36 = true;
let len =
decode_array_len(d, "CIP36 Key Registration voting key, multiple voting keys")?;
for _ in 0..len {
let len = decode_array_len(d, "CIP36 Key Registration voting key, delegations")?;
// This fixed array should be a length of 2 (voting key, weight).
if len != 2 {
return Err(decode::Error::message(format!(
"Invalid length for CIP36 Key Registration voting key delegations, expected 2, got {len}"
)));
}
// The first entry.
let pub_key = decode_bytes(
d,
"CIP36 Key Registration voting key, delegation array first entry (voting public key)",
)?;
// The second entry.
let weight: u32 = decode_helper(
d,
"CIP36 Key Registration voting key, delegation array second entry (weight)",
&mut (),
)?;

let vk = voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| {
decode::Error::message(format!(
"CIP36 Key Registration voting key, multiple voting keys, {e}"
))
})?;

voting_keys.push(VotingPubKey {
voting_pk: vk,
weight,
});
}
},
_ => {
return Err(decode::Error::message(
"Invalid datatype for CIP36 Key Registration voting key",
))
},
}
Ok((is_cip36, voting_keys))
}

/// Helper function for converting `&[u8]` to `VerifyingKey`.
fn voting_pk_vec_to_verifying_key(pub_key: &[u8]) -> anyhow::Result<VerifyingKey> {
let bytes = pub_key.try_into().context("Invalid verifying key length")?;
VerifyingKey::from_bytes(bytes).context("Failed to convert to VerifyingKey")
}

/// Helper function for decoding the stake public key.
///
/// ```cddl
/// 2 : $stake_credential,
/// $stake_credential /= $staking_pub_key
/// $staking_pub_key /= bytes .size 32
/// ```
///
/// # Returns
///
/// The stake public key as a `VerifyingKey`.
fn decode_stake_pk(d: &mut Decoder) -> Result<VerifyingKey, decode::Error> {
let pub_key = decode_bytes(d, "CIP36 Key Registration stake public key")?;
voting_pk_vec_to_verifying_key(&pub_key).map_err(|e| {
decode::Error::message(format!("CIP36 Key Registration stake public key, {e}"))
})
}

/// Helper function for decoding the payment address.
///
/// ```cddl
/// 3 : $payment_address,
/// $payment_address /= bytes
/// ```
///
/// # Returns
///
/// The payment address as a `ShelleyAddress`.
fn decode_payment_addr(d: &mut Decoder) -> Result<ShelleyAddress, decode::Error> {
let raw_addr = decode_bytes(d, "CIP36 Key Registration payment address")?;
let address = Address::from_bytes(&raw_addr).map_err(|e| {
decode::Error::message(format!("CIP36 Key Registration payment address, {e}"))
})?;
if let Address::Shelley(addr) = address {
Ok(addr.clone())
} else {
Err(decode::Error::message(format!(
"Invalid CIP36 Key Registration payment address, expected Shelley address, got {address}"
)))
}
}

/// Helper function for decoding raw nonce.
///
/// ```cddl
/// 4 : $nonce,
/// $nonce /= uint
/// ```
///
/// # Returns
///
/// Raw nonce.
fn decode_nonce(d: &mut Decoder) -> Result<u64, decode::Error> {
decode_helper(d, "CIP36 Key Registration nonce", &mut ())
}

/// Helper function for decoding the purpose.
///
/// ```cddl
/// 5 : $voting_purpose .default 0
/// $voting_purpose /= uint
/// ```
///
/// # Returns
///
/// The purpose.
fn decode_purpose(d: &mut Decoder) -> Result<u64, decode::Error> {
decode_helper(d, "CIP36 Key Registration purpose", &mut ())
}

#[cfg(test)]
mod tests {

use super::*;

#[test]
fn test_decode_payment_address() {
let hex_data = hex::decode(
// 0x004777561e7d9ec112ec307572faec1aff61ff0cfed68df4cd5c847f1872b617657881e30ad17c46e4010c9cb3ebb2440653a34d32219c83e9
"5839004777561E7D9EC112EC307572FAEC1AFF61FF0CFED68DF4CD5C847F1872B617657881E30AD17C46E4010C9CB3EBB2440653A34D32219C83E9"
).expect("cannot decode hex");
let mut decoder = Decoder::new(&hex_data);
let address = decode_payment_addr(&mut decoder);
assert_eq!(address.unwrap().to_vec().len(), 57);
}

#[test]
fn test_decode_stake_pk() {
let hex_data = hex::decode(
// 0xe3cd2404c84de65f96918f18d5b445bcb933a7cda18eeded7945dd191e432369
"5820E3CD2404C84DE65F96918F18D5B445BCB933A7CDA18EEDED7945DD191E432369",
)
.expect("cannot decode hex");
let mut decoder = Decoder::new(&hex_data);
let stake_pk = decode_stake_pk(&mut decoder);
assert!(stake_pk.is_ok());
}

#[test]
// cip-36 version
fn test_decode_voting_key_cip36() {
let hex_data = hex::decode(
// [["0x0036ef3e1f0d3f5989e2d155ea54bdb2a72c4c456ccb959af4c94868f473f5a0", 1]]
"818258200036EF3E1F0D3F5989E2D155EA54BDB2A72C4C456CCB959AF4C94868F473F5A001",
)
.expect("cannot decode hex");
let mut decoder = Decoder::new(&hex_data);

let (is_cip36, voting_pk) = decode_voting_key(&mut decoder).expect("Failed to decode");

assert!(is_cip36);
assert_eq!(voting_pk.len(), 1);
}

#[test]
// cip-15 version
fn test_decode_voting_key_2() {
let hex_data = hex::decode(
// 0x0036ef3e1f0d3f5989e2d155ea54bdb2a72c4c456ccb959af4c94868f473f5a0
"58200036EF3E1F0D3F5989E2D155EA54BDB2A72C4C456CCB959AF4C94868F473F5A0",
)
.expect("cannot decode hex");
let mut decoder = Decoder::new(&hex_data);

let (is_cip36, voting_pk) = decode_voting_key(&mut decoder).expect("Failed to decode");

assert!(!is_cip36);
assert_eq!(voting_pk.len(), 1);
}
}
Loading
Loading