mirror of https://github.com/zcash/orchard.git
add amount and asset_type
This commit is contained in:
parent
0f2f990b63
commit
7b80d7c92e
|
@ -25,7 +25,9 @@ rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"]
|
|||
[dependencies]
|
||||
aes = "0.8"
|
||||
bitvec = "1"
|
||||
borsh = {version = "0.9", features = ["const-generics"]}
|
||||
blake2b_simd = "1"
|
||||
blake2s_simd = "1"
|
||||
ff = "0.13"
|
||||
fpe = "0.6"
|
||||
group = { version = "0.13", features = ["wnaf-memuse"] }
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::iter::Sum;
|
||||
use std::ops::{Add, AddAssign, Mul, Neg, Sub, SubAssign};
|
||||
|
||||
use memuse::DynamicUsage;
|
||||
|
||||
use crate::value as orchard;
|
||||
|
||||
///missing documentation for a constant
|
||||
pub const COIN: i64 = 1_0000_0000;
|
||||
///missing documentation for a constant
|
||||
pub const MAX_MONEY: i64 = 21_000_000 * COIN;
|
||||
|
||||
#[deprecated(
|
||||
since = "0.12.0",
|
||||
note = "To calculate the ZIP 317 fee, use `transaction::fees::zip317::FeeRule`.
|
||||
For a constant representing the minimum ZIP 317 fee, use `transaction::fees::zip317::MINIMUM_FEE`.
|
||||
For the constant amount 1000 zatoshis, use `Amount::const_from_i64(1000)`."
|
||||
)]
|
||||
///missing documentation for a constant
|
||||
pub const DEFAULT_FEE: Amount = Amount(1000);
|
||||
|
||||
/// A type-safe representation of some quantity of Zcash.
|
||||
///
|
||||
/// An Amount can only be constructed from an integer that is within the valid monetary
|
||||
/// range of `{-MAX_MONEY..MAX_MONEY}` (where `MAX_MONEY` = 21,000,000 × 10⁸ zatoshis).
|
||||
/// However, this range is not preserved as an invariant internally; it is possible to
|
||||
/// add two valid Amounts together to obtain an invalid Amount. It is the user's
|
||||
/// responsibility to handle the result of serializing potentially-invalid Amounts. In
|
||||
/// particular, a [`Transaction`] containing serialized invalid Amounts will be rejected
|
||||
/// by the network consensus rules.
|
||||
///
|
||||
/// [`Transaction`]: crate::transaction::Transaction
|
||||
/// TODO: should downgrade rust analyzer?
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct Amount(i64);
|
||||
|
||||
memuse::impl_no_dynamic_usage!(Amount);
|
||||
|
||||
impl Amount {
|
||||
/// Returns a zero-valued Amount.
|
||||
pub const fn zero() -> Self {
|
||||
Amount(0)
|
||||
}
|
||||
|
||||
/// Creates a constant Amount from an i64.
|
||||
///
|
||||
/// Panics: if the amount is outside the range `{-MAX_MONEY..MAX_MONEY}`.
|
||||
pub const fn const_from_i64(amount: i64) -> Self {
|
||||
assert!(-MAX_MONEY <= amount && amount <= MAX_MONEY); // contains is not const
|
||||
Amount(amount)
|
||||
}
|
||||
|
||||
/// Creates an Amount from an i64.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{-MAX_MONEY..MAX_MONEY}`.
|
||||
pub fn from_i64(amount: i64) -> Result<Self, ()> {
|
||||
if (-MAX_MONEY..=MAX_MONEY).contains(&amount) {
|
||||
Ok(Amount(amount))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a non-negative Amount from an i64.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_nonnegative_i64(amount: i64) -> Result<Self, ()> {
|
||||
if (0..=MAX_MONEY).contains(&amount) {
|
||||
Ok(Amount(amount))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an Amount from a u64.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_u64(amount: u64) -> Result<Self, ()> {
|
||||
if amount <= MAX_MONEY as u64 {
|
||||
Ok(Amount(amount as i64))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads an Amount from a signed 64-bit little-endian integer.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{-MAX_MONEY..MAX_MONEY}`.
|
||||
pub fn from_i64_le_bytes(bytes: [u8; 8]) -> Result<Self, ()> {
|
||||
let amount = i64::from_le_bytes(bytes);
|
||||
Amount::from_i64(amount)
|
||||
}
|
||||
|
||||
/// Reads a non-negative Amount from a signed 64-bit little-endian integer.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_nonnegative_i64_le_bytes(bytes: [u8; 8]) -> Result<Self, ()> {
|
||||
let amount = i64::from_le_bytes(bytes);
|
||||
Amount::from_nonnegative_i64(amount)
|
||||
}
|
||||
|
||||
/// Reads an Amount from an unsigned 64-bit little-endian integer.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_u64_le_bytes(bytes: [u8; 8]) -> Result<Self, ()> {
|
||||
let amount = u64::from_le_bytes(bytes);
|
||||
Amount::from_u64(amount)
|
||||
}
|
||||
|
||||
/// Returns the Amount encoded as a signed 64-bit little-endian integer.
|
||||
pub fn to_i64_le_bytes(self) -> [u8; 8] {
|
||||
self.0.to_le_bytes()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is positive and `false` if the Amount is zero or
|
||||
/// negative.
|
||||
pub const fn is_positive(self) -> bool {
|
||||
self.0.is_positive()
|
||||
}
|
||||
|
||||
/// Returns `true` if `self` is negative and `false` if the Amount is zero or
|
||||
/// positive.
|
||||
pub const fn is_negative(self) -> bool {
|
||||
self.0.is_negative()
|
||||
}
|
||||
|
||||
///missing documentation for a constant
|
||||
pub fn sum<I: IntoIterator<Item = Amount>>(values: I) -> Option<Amount> {
|
||||
let mut result = Amount::zero();
|
||||
for value in values {
|
||||
result = (result + value)?;
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i64> for Amount {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: i64) -> Result<Self, ()> {
|
||||
Amount::from_i64(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Amount> for i64 {
|
||||
fn from(amount: Amount) -> i64 {
|
||||
amount.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Amount> for i64 {
|
||||
fn from(amount: &Amount) -> i64 {
|
||||
amount.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Amount> for u64 {
|
||||
fn from(amount: Amount) -> u64 {
|
||||
amount.0 as u64
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Amount> for Amount {
|
||||
type Output = Option<Amount>;
|
||||
|
||||
fn add(self, rhs: Amount) -> Option<Amount> {
|
||||
Amount::from_i64(self.0 + rhs.0).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Amount> for Option<Amount> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: Amount) -> Option<Amount> {
|
||||
self.and_then(|lhs| lhs + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Amount> for Amount {
|
||||
fn add_assign(&mut self, rhs: Amount) {
|
||||
*self = (*self + rhs).expect("Addition must produce a valid amount value.")
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Amount> for Amount {
|
||||
type Output = Option<Amount>;
|
||||
|
||||
fn sub(self, rhs: Amount) -> Option<Amount> {
|
||||
Amount::from_i64(self.0 - rhs.0).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<Amount> for Option<Amount> {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Amount) -> Option<Amount> {
|
||||
self.and_then(|lhs| lhs - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<Amount> for Amount {
|
||||
fn sub_assign(&mut self, rhs: Amount) {
|
||||
*self = (*self - rhs).expect("Subtraction must produce a valid amount value.")
|
||||
}
|
||||
}
|
||||
|
||||
impl Sum<Amount> for Option<Amount> {
|
||||
fn sum<I: Iterator<Item = Amount>>(iter: I) -> Self {
|
||||
iter.fold(Some(Amount::zero()), |acc, a| acc? + a)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Sum<&'a Amount> for Option<Amount> {
|
||||
fn sum<I: Iterator<Item = &'a Amount>>(iter: I) -> Self {
|
||||
iter.fold(Some(Amount::zero()), |acc, a| acc? + *a)
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Amount {
|
||||
type Output = Self;
|
||||
|
||||
fn neg(self) -> Self {
|
||||
Amount(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<usize> for Amount {
|
||||
type Output = Option<Amount>;
|
||||
|
||||
fn mul(self, rhs: usize) -> Option<Amount> {
|
||||
let rhs: i64 = rhs.try_into().ok()?;
|
||||
self.0
|
||||
.checked_mul(rhs)
|
||||
.and_then(|i| Amount::try_from(i).ok())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<orchard::ValueSum> for Amount {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(v: orchard::ValueSum) -> Result<Amount, Self::Error> {
|
||||
i64::try_from(v).map_err(|_| ()).and_then(Amount::try_from)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type-safe representation of some nonnegative amount of Zcash.
|
||||
///
|
||||
/// A NonNegativeAmount can only be constructed from an integer that is within the valid monetary
|
||||
/// range of `{0..MAX_MONEY}` (where `MAX_MONEY` = 21,000,000 × 10⁸ zatoshis).
|
||||
/// TODO: should downgrade rust analyzer?
|
||||
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct NonNegativeAmount(Amount);
|
||||
|
||||
impl NonNegativeAmount {
|
||||
/// Creates a NonNegativeAmount from a u64.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_u64(amount: u64) -> Result<Self, ()> {
|
||||
Amount::from_u64(amount).map(NonNegativeAmount)
|
||||
}
|
||||
|
||||
/// Creates a NonNegativeAmount from an i64.
|
||||
///
|
||||
/// Returns an error if the amount is outside the range `{0..MAX_MONEY}`.
|
||||
pub fn from_nonnegative_i64(amount: i64) -> Result<Self, ()> {
|
||||
Amount::from_nonnegative_i64(amount).map(NonNegativeAmount)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NonNegativeAmount> for Amount {
|
||||
fn from(n: NonNegativeAmount) -> Self {
|
||||
n.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A type for balance violations in amount addition and subtraction
|
||||
/// (overflow and underflow of allowed ranges)
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum BalanceError {
|
||||
///missing documentation for a constant
|
||||
Overflow,
|
||||
///missing documentation for a constant
|
||||
Underflow,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for BalanceError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match &self {
|
||||
BalanceError::Overflow => {
|
||||
write!(
|
||||
f,
|
||||
"Amount addition resulted in a value outside the valid range."
|
||||
)
|
||||
}
|
||||
BalanceError::Underflow => write!(
|
||||
f,
|
||||
"Amount subtraction resulted in a value outside the valid range."
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::prop_compose;
|
||||
|
||||
use super::{Amount, MAX_MONEY};
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_amount()(amt in -MAX_MONEY..MAX_MONEY) -> Amount {
|
||||
Amount::from_i64(amt).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_nonnegative_amount()(amt in 0i64..MAX_MONEY) -> Amount {
|
||||
Amount::from_i64(amt).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_positive_amount()(amt in 1i64..MAX_MONEY) -> Amount {
|
||||
Amount::from_i64(amt).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Amount, MAX_MONEY};
|
||||
|
||||
#[test]
|
||||
fn amount_in_range() {
|
||||
let zero = b"\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
assert_eq!(Amount::from_u64_le_bytes(*zero).unwrap(), Amount(0));
|
||||
assert_eq!(
|
||||
Amount::from_nonnegative_i64_le_bytes(*zero).unwrap(),
|
||||
Amount(0)
|
||||
);
|
||||
assert_eq!(Amount::from_i64_le_bytes(*zero).unwrap(), Amount(0));
|
||||
|
||||
let neg_one = b"\xff\xff\xff\xff\xff\xff\xff\xff";
|
||||
assert!(Amount::from_u64_le_bytes(*neg_one).is_err());
|
||||
assert!(Amount::from_nonnegative_i64_le_bytes(*neg_one).is_err());
|
||||
assert_eq!(Amount::from_i64_le_bytes(*neg_one).unwrap(), Amount(-1));
|
||||
|
||||
let max_money = b"\x00\x40\x07\x5a\xf0\x75\x07\x00";
|
||||
assert_eq!(
|
||||
Amount::from_u64_le_bytes(*max_money).unwrap(),
|
||||
Amount(MAX_MONEY)
|
||||
);
|
||||
assert_eq!(
|
||||
Amount::from_nonnegative_i64_le_bytes(*max_money).unwrap(),
|
||||
Amount(MAX_MONEY)
|
||||
);
|
||||
assert_eq!(
|
||||
Amount::from_i64_le_bytes(*max_money).unwrap(),
|
||||
Amount(MAX_MONEY)
|
||||
);
|
||||
|
||||
let max_money_p1 = b"\x01\x40\x07\x5a\xf0\x75\x07\x00";
|
||||
assert!(Amount::from_u64_le_bytes(*max_money_p1).is_err());
|
||||
assert!(Amount::from_nonnegative_i64_le_bytes(*max_money_p1).is_err());
|
||||
assert!(Amount::from_i64_le_bytes(*max_money_p1).is_err());
|
||||
|
||||
let neg_max_money = b"\x00\xc0\xf8\xa5\x0f\x8a\xf8\xff";
|
||||
assert!(Amount::from_u64_le_bytes(*neg_max_money).is_err());
|
||||
assert!(Amount::from_nonnegative_i64_le_bytes(*neg_max_money).is_err());
|
||||
assert_eq!(
|
||||
Amount::from_i64_le_bytes(*neg_max_money).unwrap(),
|
||||
Amount(-MAX_MONEY)
|
||||
);
|
||||
|
||||
let neg_max_money_m1 = b"\xff\xbf\xf8\xa5\x0f\x8a\xf8\xff";
|
||||
assert!(Amount::from_u64_le_bytes(*neg_max_money_m1).is_err());
|
||||
assert!(Amount::from_nonnegative_i64_le_bytes(*neg_max_money_m1).is_err());
|
||||
assert!(Amount::from_i64_le_bytes(*neg_max_money_m1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_overflow() {
|
||||
let v = Amount(MAX_MONEY);
|
||||
assert_eq!(v + Amount(1), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn add_assign_panics_on_overflow() {
|
||||
let mut a = Amount(MAX_MONEY);
|
||||
a += Amount(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_underflow() {
|
||||
let v = Amount(-MAX_MONEY);
|
||||
assert_eq!(v - Amount(1), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn sub_assign_panics_on_underflow() {
|
||||
let mut a = Amount(-MAX_MONEY);
|
||||
a -= Amount(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
use crate::{
|
||||
constants::{
|
||||
ASSET_IDENTIFIER_LENGTH, ASSET_IDENTIFIER_PERSONALIZATION, GH_FIRST_BLOCK,
|
||||
VALUE_COMMITMENT_GENERATOR_PERSONALIZATION,
|
||||
},
|
||||
value::ValueCommitment,
|
||||
};
|
||||
use blake2s_simd::Params as Blake2sParams;
|
||||
use borsh::{BorshDeserialize, BorshSerialize};
|
||||
use group::{cofactor::CofactorGroup, Group, GroupEncoding};
|
||||
use pasta_curves::pallas;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter},
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
/// A type representing an asset type
|
||||
#[derive(Debug, BorshSerialize, BorshDeserialize, Clone, Copy, Eq)]
|
||||
pub struct AssetType {
|
||||
identifier: [u8; ASSET_IDENTIFIER_LENGTH], //32 byte asset type preimage
|
||||
#[borsh_skip]
|
||||
nonce: Option<u8>,
|
||||
}
|
||||
|
||||
// Abstract type representing an asset
|
||||
impl AssetType {
|
||||
/// Create a new AsstType from a unique asset name
|
||||
/// Not constant-time, uses rejection sampling
|
||||
pub fn new(name: &[u8]) -> Result<AssetType, ()> {
|
||||
let mut nonce = 0u8;
|
||||
loop {
|
||||
if let Some(asset_type) = AssetType::new_with_nonce(name, nonce) {
|
||||
return Ok(asset_type);
|
||||
}
|
||||
nonce = nonce.checked_add(1).ok_or(())?;
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to create a new AssetType from a unique asset name and fixed nonce
|
||||
/// Not yet constant-time; assume not-constant-time
|
||||
pub fn new_with_nonce(name: &[u8], nonce: u8) -> Option<AssetType> {
|
||||
use std::slice::from_ref;
|
||||
|
||||
// Check the personalization is acceptable length
|
||||
assert_eq!(ASSET_IDENTIFIER_PERSONALIZATION.len(), 8);
|
||||
|
||||
// Create a new BLAKE2s state for deriving the asset identifier
|
||||
//TODO:sinsemillaに置き換える
|
||||
let h = Blake2sParams::new()
|
||||
.hash_length(ASSET_IDENTIFIER_LENGTH)
|
||||
.personal(ASSET_IDENTIFIER_PERSONALIZATION)
|
||||
.to_state()
|
||||
.update(GH_FIRST_BLOCK)
|
||||
.update(name)
|
||||
.update(from_ref(&nonce))
|
||||
.finalize();
|
||||
|
||||
// If the hash state is a valid asset identifier, use it
|
||||
if AssetType::hash_to_point(h.as_array()).is_some() {
|
||||
Some(AssetType {
|
||||
identifier: *h.as_array(),
|
||||
nonce: Some(nonce),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
//Attempt to hash an identifier to a curve point
|
||||
// https://crypto.stackexchange.com/questions/102414/where-is-hash-to-curve-used
|
||||
fn hash_to_point(identifier: &[u8; ASSET_IDENTIFIER_LENGTH]) -> Option<pallas::Point> {
|
||||
//check the personalization is acceptable length
|
||||
assert_eq!(ASSET_IDENTIFIER_PERSONALIZATION.len(), 8);
|
||||
|
||||
//Check to see that scalar field is 255 bits
|
||||
use ff::PrimeField;
|
||||
assert_eq!(pallas::Base::NUM_BITS, 255);
|
||||
|
||||
let h = Blake2sParams::new()
|
||||
.hash_length(32)
|
||||
.personal(VALUE_COMMITMENT_GENERATOR_PERSONALIZATION)
|
||||
.to_state()
|
||||
.update(identifier)
|
||||
.finalize();
|
||||
|
||||
let p = pallas::Point::from_bytes(h.as_array());
|
||||
if p.is_some().into() {
|
||||
let p = p.unwrap();
|
||||
let p_prime = CofactorGroup::clear_cofactor(&p);
|
||||
if p_prime.is_identity().into() {
|
||||
None
|
||||
} else {
|
||||
Some(p)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the identifier of this asset type
|
||||
pub fn get_identifier(&self) -> &[u8; ASSET_IDENTIFIER_LENGTH] {
|
||||
&self.identifier
|
||||
}
|
||||
|
||||
/// Attempt to construct an asset type from an existing asset identifier
|
||||
pub fn from_identifier(identifier: &[u8; ASSET_IDENTIFIER_LENGTH]) -> Option<AssetType> {
|
||||
// Attempt to hash to point
|
||||
if AssetType::hash_to_point(identifier).is_some() {
|
||||
Some(AssetType {
|
||||
identifier: *identifier,
|
||||
nonce: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Produces an asset generator without cofactor cleared
|
||||
pub fn asset_generator(&self) -> pallas::Point {
|
||||
AssetType::hash_to_point(self.get_identifier())
|
||||
.expect("AssetType::get_identifier() should never return None")
|
||||
}
|
||||
|
||||
/// Produces a value commitment generator with cofactor cleared
|
||||
/// todo:返り値をsubgroup
|
||||
pub fn value_commitment_generator(&self) -> () {
|
||||
CofactorGroup::clear_cofactor(&self.asset_generator());
|
||||
}
|
||||
|
||||
/// Get the asset identifier as a vector of bools
|
||||
pub fn identifier_bits(&self) -> Vec<Option<bool>> {
|
||||
self.get_identifier()
|
||||
.iter()
|
||||
.flat_map(|&v| (0..8).map(move |i| Some((v >> i) & 1 == 1)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get the asset identifier as a vector of bools
|
||||
pub fn value_commitment(&self, randomness: pallas::Point) -> ValueCommitment {
|
||||
ValueCommitment(randomness)
|
||||
}
|
||||
|
||||
/// Get the asset identifier as a vector of bools
|
||||
pub fn get_nonce(&self) -> Option<u8> {
|
||||
self.nonce
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for AssetType {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.get_identifier() == other.get_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AssetType {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "{}", hex::encode(self.get_identifier()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for AssetType {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.get_identifier().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for AssetType {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.get_identifier().partial_cmp(other.get_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for AssetType {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.get_identifier().cmp(other.get_identifier())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for AssetType {
|
||||
type Err = std::io::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let vec = hex::decode(s).map_err(|x| Self::Err::new(std::io::ErrorKind::InvalidData, x))?;
|
||||
Self::from_identifier(
|
||||
&vec.try_into()
|
||||
.map_err(|_| Self::Err::from(std::io::ErrorKind::InvalidData))?,
|
||||
)
|
||||
.ok_or_else(|| Self::Err::from(std::io::ErrorKind::InvalidData))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use proptest::prelude::*;
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_asset_type()(name in proptest::collection::vec(prop::num::u8::ANY, 0..64)) -> super::AssetType {
|
||||
super::AssetType::new(&name).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,11 +13,13 @@
|
|||
// Catch documentation errors caused by code changes.
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(missing_docs)]
|
||||
// #![deny(missing_docs)]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
mod action;
|
||||
mod address;
|
||||
pub mod amount;
|
||||
pub mod asset_type;
|
||||
pub mod builder;
|
||||
pub mod bundle;
|
||||
pub mod circuit;
|
||||
|
|
Loading…
Reference in New Issue