use cbor_event::{
self,
de::Deserializer,
se::{Serialize, Serializer},
};
use hex::FromHex;
use num_bigint::Sign;
use serde_json;
use std::convert::TryFrom;
use std::ops::Div;
use std::{
collections::HashMap,
io::{BufRead, Seek, Write},
};
use std::fmt::Display;
use super::*;
use crate::error::{DeserializeError, DeserializeFailure};
use crate::fakes::fake_data_hash;
use schemars::JsonSchema;
pub fn to_bytes<T: cbor_event::se::Serialize>(data_item: &T) -> Vec<u8> {
let mut buf = Serializer::new_vec();
data_item.serialize(&mut buf).unwrap();
buf.finalize()
}
pub fn from_bytes<T: Deserialize>(data: &Vec<u8>) -> Result<T, DeserializeError> {
let mut raw = Deserializer::from(std::io::Cursor::new(data));
T::deserialize(&mut raw)
}
#[wasm_bindgen]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema,)]
pub struct TransactionUnspentOutput {
pub(crate) input: TransactionInput,
pub(crate) output: TransactionOutput,
}
impl_to_from!(TransactionUnspentOutput);
#[wasm_bindgen]
impl TransactionUnspentOutput {
pub fn new(input: &TransactionInput, output: &TransactionOutput) -> TransactionUnspentOutput {
Self {
input: input.clone(),
output: output.clone(),
}
}
pub fn input(&self) -> TransactionInput {
self.input.clone()
}
pub fn output(&self) -> TransactionOutput {
self.output.clone()
}
}
impl cbor_event::se::Serialize for TransactionUnspentOutput {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_array(cbor_event::Len::Len(2))?;
self.input.serialize(serializer)?;
self.output.serialize(serializer)
}
}
impl Deserialize for TransactionUnspentOutput {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<_, DeserializeError> {
match raw.cbor_type()? {
cbor_event::Type::Array => {
let len = raw.array()?;
let input = (|| -> Result<_, DeserializeError> {
Ok(TransactionInput::deserialize(raw)?)
})()
.map_err(|e| e.annotate("input"))?;
let output = (|| -> Result<_, DeserializeError> {
Ok(TransactionOutput::deserialize(raw)?)
})()
.map_err(|e| e.annotate("output"))?;
let ret = Ok(Self { input, output });
match len {
cbor_event::Len::Len(n) => match n {
2 =>
{
()
}
n => {
return Err(
DeserializeFailure::DefiniteLenMismatch(n, Some(2)).into()
);
}
},
cbor_event::Len::Indefinite => match raw.special()? {
CBORSpecial::Break =>
{
()
}
_ => return Err(DeserializeFailure::EndingBreakMissing.into()),
},
}
ret
}
_ => Err(DeserializeFailure::NoVariantMatched.into()),
}
})()
.map_err(|e| e.annotate("TransactionUnspentOutput"))
}
}
#[wasm_bindgen]
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema,)]
pub struct TransactionUnspentOutputs(pub(crate) Vec<TransactionUnspentOutput>);
to_from_json!(TransactionUnspentOutputs);
#[wasm_bindgen]
impl TransactionUnspentOutputs {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn get(&self, index: usize) -> TransactionUnspentOutput {
self.0[index].clone()
}
pub fn add(&mut self, elem: &TransactionUnspentOutput) {
self.0.push(elem.clone());
}
}
impl<'a> IntoIterator for &'a TransactionUnspentOutputs {
type Item = &'a TransactionUnspentOutput;
type IntoIter = std::slice::Iter<'a, TransactionUnspentOutput>;
fn into_iter(self) -> std::slice::Iter<'a, TransactionUnspentOutput> {
self.0.iter()
}
}
#[wasm_bindgen]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BigNum(u64);
impl_to_from!(BigNum);
impl std::fmt::Display for BigNum {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[wasm_bindgen]
impl BigNum {
pub fn from_str(string: &str) -> Result<BigNum, JsError> {
string
.parse::<u64>()
.map_err(|e| JsError::from_str(&format! {"{:?}", e}))
.map(BigNum)
}
pub fn to_str(&self) -> String {
format!("{}", self.0)
}
pub fn zero() -> Self {
Self(0)
}
pub fn one() -> Self {
Self(1)
}
pub fn is_zero(&self) -> bool {
self.0 == 0
}
pub fn div_floor(&self, other: &BigNum) -> BigNum {
let res = self.0.div(&other.0);
Self(res)
}
pub fn checked_mul(&self, other: &BigNum) -> Result<BigNum, JsError> {
match self.0.checked_mul(other.0) {
Some(value) => Ok(BigNum(value)),
None => Err(JsError::from_str("overflow")),
}
}
pub fn checked_add(&self, other: &BigNum) -> Result<BigNum, JsError> {
match self.0.checked_add(other.0) {
Some(value) => Ok(BigNum(value)),
None => Err(JsError::from_str("overflow")),
}
}
pub fn checked_sub(&self, other: &BigNum) -> Result<BigNum, JsError> {
match self.0.checked_sub(other.0) {
Some(value) => Ok(BigNum(value)),
None => Err(JsError::from_str("underflow")),
}
}
pub fn clamped_sub(&self, other: &BigNum) -> BigNum {
match self.0.checked_sub(other.0) {
Some(value) => BigNum(value),
None => BigNum(0),
}
}
pub fn compare(&self, rhs_value: &BigNum) -> i8 {
match self.cmp(&rhs_value) {
std::cmp::Ordering::Equal => 0,
std::cmp::Ordering::Less => -1,
std::cmp::Ordering::Greater => 1,
}
}
pub fn less_than(&self, rhs_value: &BigNum) -> bool {
self.compare(rhs_value) < 0
}
pub fn max_value() -> BigNum {
BigNum(u64::max_value())
}
pub fn max(a: &BigNum, b: &BigNum) -> BigNum {
if a.less_than(b) { b.clone() } else { a.clone() }
}
}
impl TryFrom<BigNum> for u32 {
type Error = JsError;
fn try_from(value: BigNum) -> Result<Self, Self::Error> {
if value.0 > u32::MAX.into() {
Err(JsError::from_str(&format!(
"Value {} is bigger than max u32 {}",
value.0,
u32::MAX
)))
} else {
Ok(value.0 as u32)
}
}
}
impl From<BigNum> for u64 {
fn from(value: BigNum) -> Self {
value.0
}
}
impl From<BigNum> for usize {
fn from(value: BigNum) -> Self {
value.0 as usize
}
}
impl From<u64> for BigNum {
fn from(value: u64) -> Self {
return BigNum(value);
}
}
impl From<usize> for BigNum {
fn from(value: usize) -> Self {
return BigNum(value as u64);
}
}
impl From<u32> for BigNum {
fn from(value: u32) -> Self {
return BigNum(value.into());
}
}
impl cbor_event::se::Serialize for BigNum {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
serializer.write_unsigned_integer(self.0)
}
}
impl Deserialize for BigNum {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
match raw.unsigned_integer() {
Ok(value) => Ok(Self(value)),
Err(e) => Err(DeserializeError::new("BigNum", DeserializeFailure::CBOR(e))),
}
}
}
impl serde::Serialize for BigNum {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_str())
}
}
impl<'de> serde::de::Deserialize<'de> for BigNum {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = <String as serde::de::Deserialize>::deserialize(deserializer)?;
Self::from_str(&s).map_err(|_e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&s),
&"string rep of a number",
)
})
}
}
impl JsonSchema for BigNum {
fn schema_name() -> String {
String::from("BigNum")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
pub fn to_bignum(val: u64) -> BigNum {
BigNum(val)
}
pub fn from_bignum(val: &BigNum) -> u64 {
val.0
}
pub fn to_bigint(val: u64) -> BigInt {
BigInt::from_str(&val.to_string()).unwrap()
}
pub type Coin = BigNum;
#[wasm_bindgen]
#[derive(
Clone,
Debug,
Eq,
Ord,
PartialEq,
serde::Serialize,
serde::Deserialize,
JsonSchema,
)]
pub struct Value {
pub(crate) coin: Coin,
pub(crate) multiasset: Option<MultiAsset>,
}
impl_to_from!(Value);
#[wasm_bindgen]
impl Value {
pub fn new(coin: &Coin) -> Value {
Self {
coin: coin.clone(),
multiasset: None,
}
}
pub fn new_from_assets(multiasset: &MultiAsset) -> Value {
Value::new_with_assets(&Coin::zero(), multiasset)
}
pub fn new_with_assets(coin: &Coin, multiasset: &MultiAsset) -> Value {
match multiasset.0.is_empty() {
true => Value::new(coin),
false => Self {
coin: coin.clone(),
multiasset: Some(multiasset.clone()),
},
}
}
pub fn zero() -> Value {
Value::new(&Coin::zero())
}
pub fn is_zero(&self) -> bool {
self.coin.is_zero()
&& self
.multiasset
.as_ref()
.map(|m| m.len() == 0)
.unwrap_or(true)
}
pub fn coin(&self) -> Coin {
self.coin
}
pub fn set_coin(&mut self, coin: &Coin) {
self.coin = coin.clone();
}
pub fn multiasset(&self) -> Option<MultiAsset> {
self.multiasset.clone()
}
pub fn set_multiasset(&mut self, multiasset: &MultiAsset) {
self.multiasset = Some(multiasset.clone());
}
pub fn checked_add(&self, rhs: &Value) -> Result<Value, JsError> {
use std::collections::btree_map::Entry;
let coin = self.coin.checked_add(&rhs.coin)?;
let multiasset = match (&self.multiasset, &rhs.multiasset) {
(Some(lhs_multiasset), Some(rhs_multiasset)) => {
let mut multiasset = MultiAsset::new();
for ma in &[lhs_multiasset, rhs_multiasset] {
for (policy, assets) in &ma.0 {
for (asset_name, amount) in &assets.0 {
match multiasset.0.entry(policy.clone()) {
Entry::Occupied(mut assets) => {
match assets.get_mut().0.entry(asset_name.clone()) {
Entry::Occupied(mut assets) => {
let current = assets.get_mut();
*current = current.checked_add(&amount)?;
}
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(amount.clone());
}
}
}
Entry::Vacant(entry) => {
let mut assets = Assets::new();
assets.0.insert(asset_name.clone(), amount.clone());
entry.insert(assets);
}
}
}
}
}
Some(multiasset)
}
(None, None) => None,
(Some(ma), None) => Some(ma.clone()),
(None, Some(ma)) => Some(ma.clone()),
};
Ok(Value { coin, multiasset })
}
pub fn checked_sub(&self, rhs_value: &Value) -> Result<Value, JsError> {
let coin = self.coin.checked_sub(&rhs_value.coin)?;
let multiasset = match (&self.multiasset, &rhs_value.multiasset) {
(Some(lhs_ma), Some(rhs_ma)) => match lhs_ma.sub(rhs_ma).len() {
0 => None,
_ => Some(lhs_ma.sub(rhs_ma)),
},
(Some(lhs_ma), None) => Some(lhs_ma.clone()),
(None, Some(_rhs_ma)) => None,
(None, None) => None,
};
Ok(Value { coin, multiasset })
}
pub fn clamped_sub(&self, rhs_value: &Value) -> Value {
let coin = self.coin.clamped_sub(&rhs_value.coin);
let multiasset = match (&self.multiasset, &rhs_value.multiasset) {
(Some(lhs_ma), Some(rhs_ma)) => match lhs_ma.sub(rhs_ma).len() {
0 => None,
_ => Some(lhs_ma.sub(rhs_ma)),
},
(Some(lhs_ma), None) => Some(lhs_ma.clone()),
(None, Some(_rhs_ma)) => None,
(None, None) => None,
};
Value { coin, multiasset }
}
pub fn compare(&self, rhs_value: &Value) -> Option<i8> {
match self.partial_cmp(&rhs_value) {
None => None,
Some(std::cmp::Ordering::Equal) => Some(0),
Some(std::cmp::Ordering::Less) => Some(-1),
Some(std::cmp::Ordering::Greater) => Some(1),
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering::*;
fn compare_assets(
lhs: &Option<MultiAsset>,
rhs: &Option<MultiAsset>,
) -> Option<std::cmp::Ordering> {
match (lhs, rhs) {
(None, None) => Some(Equal),
(None, Some(rhs_assets)) => MultiAsset::new().partial_cmp(&rhs_assets),
(Some(lhs_assets), None) => lhs_assets.partial_cmp(&MultiAsset::new()),
(Some(lhs_assets), Some(rhs_assets)) => lhs_assets.partial_cmp(&rhs_assets),
}
}
compare_assets(&self.multiasset(), &other.multiasset()).and_then(|assets_match| {
let coin_cmp = self.coin.cmp(&other.coin);
match (coin_cmp, assets_match) {
(coin_order, Equal) => Some(coin_order),
(Equal, Less) => Some(Less),
(Less, Less) => Some(Less),
(Equal, Greater) => Some(Greater),
(Greater, Greater) => Some(Greater),
(_, _) => None,
}
})
}
}
impl cbor_event::se::Serialize for Value {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
match &self.multiasset {
Some(multiasset) => {
serializer.write_array(cbor_event::Len::Len(2))?;
self.coin.serialize(serializer)?;
multiasset.serialize(serializer)
}
None => self.coin.serialize(serializer),
}
}
}
impl Deserialize for Value {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<_, DeserializeError> {
match raw.cbor_type()? {
cbor_event::Type::UnsignedInteger => Ok(Value::new(&Coin::deserialize(raw)?)),
cbor_event::Type::Array => {
let len = raw.array()?;
let coin =
(|| -> Result<_, DeserializeError> { Ok(Coin::deserialize(raw)?) })()
.map_err(|e| e.annotate("coin"))?;
let multiasset =
(|| -> Result<_, DeserializeError> { Ok(MultiAsset::deserialize(raw)?) })()
.map_err(|e| e.annotate("multiasset"))?;
let ret = Ok(Self {
coin,
multiasset: Some(multiasset),
});
match len {
cbor_event::Len::Len(n) => match n {
2 =>
{
()
}
n => {
return Err(
DeserializeFailure::DefiniteLenMismatch(n, Some(2)).into()
);
}
},
cbor_event::Len::Indefinite => match raw.special()? {
CBORSpecial::Break =>
{
()
}
_ => return Err(DeserializeFailure::EndingBreakMissing.into()),
},
}
ret
}
_ => Err(DeserializeFailure::NoVariantMatched.into()),
}
})()
.map_err(|e| e.annotate("Value"))
}
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Int(pub(crate) i128);
impl_to_from!(Int);
#[wasm_bindgen]
impl Int {
pub fn new(x: &BigNum) -> Self {
Self(x.0 as i128)
}
pub fn new_negative(x: &BigNum) -> Self {
Self(-(x.0 as i128))
}
pub fn new_i32(x: i32) -> Self {
Self(x as i128)
}
pub fn is_positive(&self) -> bool {
return self.0 >= 0;
}
pub fn as_positive(&self) -> Option<BigNum> {
if self.is_positive() {
Some(to_bignum(self.0 as u64))
} else {
None
}
}
pub fn as_negative(&self) -> Option<BigNum> {
if !self.is_positive() {
Some(to_bignum((-self.0) as u64))
} else {
None
}
}
#[deprecated(
since = "10.0.0",
note = "Unsafe ignoring of possible boundary error and it's not clear from the function name. Use `as_i32_or_nothing`, `as_i32_or_fail`, or `to_str`"
)]
pub fn as_i32(&self) -> Option<i32> {
self.as_i32_or_nothing()
}
pub fn as_i32_or_nothing(&self) -> Option<i32> {
use std::convert::TryFrom;
i32::try_from(self.0).ok()
}
pub fn as_i32_or_fail(&self) -> Result<i32, JsError> {
use std::convert::TryFrom;
i32::try_from(self.0).map_err(|e| JsError::from_str(&format!("{}", e)))
}
pub fn to_str(&self) -> String {
format!("{}", self.0)
}
pub fn from_str(string: &str) -> Result<Int, JsError> {
let x = string
.parse::<i128>()
.map_err(|e| JsError::from_str(&format! {"{:?}", e}))?;
if x.abs() > u64::MAX as i128 {
return Err(JsError::from_str(&format!(
"{} out of bounds. Value (without sign) must fit within 4 bytes limit of {}",
x,
u64::MAX
)));
}
Ok(Self(x))
}
}
impl cbor_event::se::Serialize for Int {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
if self.0 < 0 {
serializer.write_negative_integer(self.0 as i64)
} else {
serializer.write_unsigned_integer(self.0 as u64)
}
}
}
impl Deserialize for Int {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<_, DeserializeError> {
match raw.cbor_type()? {
cbor_event::Type::UnsignedInteger => Ok(Self(raw.unsigned_integer()? as i128)),
cbor_event::Type::NegativeInteger => Ok(Self(read_nint(raw)?)),
_ => Err(DeserializeFailure::NoVariantMatched.into()),
}
})()
.map_err(|e| e.annotate("Int"))
}
}
impl serde::Serialize for Int {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_str())
}
}
impl<'de> serde::de::Deserialize<'de> for Int {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = <String as serde::de::Deserialize>::deserialize(deserializer)?;
Self::from_str(&s).map_err(|_e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&s),
&"string rep of a number",
)
})
}
}
impl JsonSchema for Int {
fn schema_name() -> String {
String::from("Int")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
fn read_nint<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<i128, DeserializeError> {
let found = raw.cbor_type()?;
if found != cbor_event::Type::NegativeInteger {
return Err(cbor_event::Error::Expected(cbor_event::Type::NegativeInteger, found).into());
}
let (len, len_sz) = raw.cbor_len()?;
match len {
cbor_event::Len::Indefinite => Err(cbor_event::Error::IndefiniteLenNotSupported(
cbor_event::Type::NegativeInteger,
)
.into()),
cbor_event::Len::Len(v) => {
raw.advance(1 + len_sz)?;
Ok(-(v as i128) - 1)
}
}
}
const BOUNDED_BYTES_CHUNK_SIZE: usize = 64;
pub(crate) fn write_bounded_bytes<'se, W: Write>(
serializer: &'se mut Serializer<W>,
bytes: &[u8],
) -> cbor_event::Result<&'se mut Serializer<W>> {
if bytes.len() <= BOUNDED_BYTES_CHUNK_SIZE {
serializer.write_bytes(bytes)
} else {
serializer.write_raw_bytes(&[0x5f])?;
for chunk in bytes.chunks(BOUNDED_BYTES_CHUNK_SIZE) {
serializer.write_bytes(chunk)?;
}
serializer.write_special(CBORSpecial::Break)
}
}
pub(crate) fn read_bounded_bytes<R: BufRead + Seek>(
raw: &mut Deserializer<R>,
) -> Result<Vec<u8>, DeserializeError> {
use std::io::Read;
let t = raw.cbor_type()?;
if t != CBORType::Bytes {
return Err(cbor_event::Error::Expected(CBORType::Bytes, t).into());
}
let (len, len_sz) = raw.cbor_len()?;
match len {
cbor_event::Len::Len(_) => {
let bytes = raw.bytes()?;
if bytes.len() > BOUNDED_BYTES_CHUNK_SIZE {
return Err(DeserializeFailure::OutOfRange {
min: 0,
max: BOUNDED_BYTES_CHUNK_SIZE,
found: bytes.len(),
}
.into());
}
Ok(bytes)
}
cbor_event::Len::Indefinite => {
let mut bytes = Vec::new();
raw.advance(1 + len_sz)?;
while raw.cbor_type()? != CBORType::Special {
let chunk_t = raw.cbor_type()?;
if chunk_t != CBORType::Bytes {
return Err(cbor_event::Error::Expected(CBORType::Bytes, chunk_t).into());
}
let (chunk_len, chunk_len_sz) = raw.cbor_len()?;
match chunk_len {
cbor_event::Len::Indefinite => {
return Err(cbor_event::Error::CustomError(String::from(
"Illegal CBOR: Indefinite string found inside indefinite string",
))
.into());
}
cbor_event::Len::Len(len) => {
if chunk_len_sz > BOUNDED_BYTES_CHUNK_SIZE {
return Err(DeserializeFailure::OutOfRange {
min: 0,
max: BOUNDED_BYTES_CHUNK_SIZE,
found: chunk_len_sz,
}
.into());
}
raw.advance(1 + chunk_len_sz)?;
raw.as_mut_ref()
.by_ref()
.take(len)
.read_to_end(&mut bytes)
.map_err(|e| cbor_event::Error::IoError(e))?;
}
}
}
if raw.special()? != CBORSpecial::Break {
return Err(DeserializeFailure::EndingBreakMissing.into());
}
Ok(bytes)
}
}
}
#[wasm_bindgen]
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)]
pub struct BigInt(num_bigint::BigInt);
impl_to_from!(BigInt);
impl serde::Serialize for BigInt {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_str())
}
}
impl<'de> serde::de::Deserialize<'de> for BigInt {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = <String as serde::de::Deserialize>::deserialize(deserializer)?;
BigInt::from_str(&s).map_err(|_e| {
serde::de::Error::invalid_value(
serde::de::Unexpected::Str(&s),
&"string rep of a big int",
)
})
}
}
impl JsonSchema for BigInt {
fn schema_name() -> String {
String::from("BigInt")
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
String::json_schema(gen)
}
fn is_referenceable() -> bool {
String::is_referenceable()
}
}
#[wasm_bindgen]
impl BigInt {
pub fn is_zero(&self) -> bool {
self.0.sign() == Sign::NoSign
}
pub fn as_u64(&self) -> Option<BigNum> {
let (sign, u64_digits) = self.0.to_u64_digits();
if sign == num_bigint::Sign::Minus {
return None;
}
match u64_digits.len() {
0 => Some(to_bignum(0)),
1 => Some(to_bignum(*u64_digits.first().unwrap())),
_ => None,
}
}
pub fn as_int(&self) -> Option<Int> {
let (sign, u64_digits) = self.0.to_u64_digits();
let u64_digit = match u64_digits.len() {
0 => Some(to_bignum(0)),
1 => Some(to_bignum(*u64_digits.first().unwrap())),
_ => None,
}?;
match sign {
num_bigint::Sign::NoSign | num_bigint::Sign::Plus => Some(Int::new(&u64_digit)),
num_bigint::Sign::Minus => Some(Int::new_negative(&u64_digit)),
}
}
pub fn from_str(text: &str) -> Result<BigInt, JsError> {
use std::str::FromStr;
num_bigint::BigInt::from_str(text)
.map_err(|e| JsError::from_str(&format! {"{:?}", e}))
.map(Self)
}
pub fn to_str(&self) -> String {
self.0.to_string()
}
pub fn add(&self, other: &BigInt) -> BigInt {
Self(&self.0 + &other.0)
}
pub fn mul(&self, other: &BigInt) -> BigInt {
Self(&self.0 * &other.0)
}
pub fn one() -> BigInt {
use std::str::FromStr;
Self(num_bigint::BigInt::from_str("1").unwrap())
}
pub fn increment(&self) -> BigInt {
self.add(&Self::one())
}
pub fn div_ceil(&self, other: &BigInt) -> BigInt {
use num_integer::Integer;
let (res, rem) = self.0.div_rem(&other.0);
let result = Self(res);
if Self(rem).is_zero() {
result
} else {
result.increment()
}
}
}
impl cbor_event::se::Serialize for BigInt {
fn serialize<'se, W: Write>(
&self,
serializer: &'se mut Serializer<W>,
) -> cbor_event::Result<&'se mut Serializer<W>> {
let (sign, u64_digits) = self.0.to_u64_digits();
match u64_digits.len() {
0 => serializer.write_unsigned_integer(0),
1 => match sign {
num_bigint::Sign::Plus | num_bigint::Sign::NoSign => {
serializer.write_unsigned_integer(*u64_digits.first().unwrap())
}
num_bigint::Sign::Minus => serializer
.write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64),
},
_ => {
if sign == num_bigint::Sign::Minus && u64_digits == vec![0, 1] {
serializer.write_negative_integer(-18446744073709551616i128 as i64)
} else {
let (sign, bytes) = self.0.to_bytes_be();
match sign {
num_bigint::Sign::Plus | num_bigint::Sign::NoSign => {
serializer.write_tag(2u64)?;
write_bounded_bytes(serializer, &bytes)
}
num_bigint::Sign::Minus => {
serializer.write_tag(3u64)?;
use std::ops::Neg;
let adjusted = self
.0
.clone()
.neg()
.checked_sub(&num_bigint::BigInt::from(1u32))
.unwrap()
.to_biguint()
.unwrap();
write_bounded_bytes(serializer, &adjusted.to_bytes_be())
}
}
}
}
}
}
}
impl Deserialize for BigInt {
fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
(|| -> Result<_, DeserializeError> {
match raw.cbor_type()? {
CBORType::Tag => {
let tag = raw.tag()?;
let bytes = read_bounded_bytes(raw)?;
match tag {
2 => Ok(Self(num_bigint::BigInt::from_bytes_be(
num_bigint::Sign::Plus,
&bytes,
))),
3 => {
let initial =
num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes);
use std::ops::Neg;
let adjusted = initial
.checked_add(&num_bigint::BigInt::from(1u32))
.unwrap()
.neg();
Ok(Self(adjusted))
}
_ => {
return Err(DeserializeFailure::TagMismatch {
found: tag,
expected: 2,
}
.into());
}
}
}
CBORType::UnsignedInteger => {
Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?)))
}
CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(read_nint(raw)?))),
_ => return Err(DeserializeFailure::NoVariantMatched.into()),
}
})()
.map_err(|e| e.annotate("BigInt"))
}
}
impl<T> std::convert::From<T> for BigInt
where
T: std::convert::Into<num_bigint::BigInt>,
{
fn from(x: T) -> Self {
Self(x.into())
}
}
impl From<BigNum> for BigInt
where
{
fn from(x: BigNum) -> Self {
Self(x.0.into())
}
}
pub struct CBORReadLen {
deser_len: cbor_event::Len,
read: u64,
}
impl CBORReadLen {
pub fn new(len: cbor_event::Len) -> Self {
Self {
deser_len: len,
read: 0,
}
}
pub fn read_elems(&mut self, count: usize) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
self.read += count as u64;
if self.read > n {
Err(DeserializeFailure::DefiniteLenMismatch(n, None))
} else {
Ok(())
}
}
cbor_event::Len::Indefinite => Ok(()),
}
}
pub fn finish(&self) -> Result<(), DeserializeFailure> {
match self.deser_len {
cbor_event::Len::Len(n) => {
if self.read == n {
Ok(())
} else {
Err(DeserializeFailure::DefiniteLenMismatch(n, Some(self.read)))
}
}
cbor_event::Len::Indefinite => Ok(()),
}
}
}
#[wasm_bindgen]
pub fn make_daedalus_bootstrap_witness(
tx_body_hash: &TransactionHash,
addr: &ByronAddress,
key: &LegacyDaedalusPrivateKey,
) -> BootstrapWitness {
let chain_code = key.chaincode();
let pubkey = Bip32PublicKey::from_bytes(&key.0.to_public().as_ref()).unwrap();
let vkey = Vkey::new(&pubkey.to_raw_key());
let signature =
Ed25519Signature::from_bytes(key.0.sign(&tx_body_hash.to_bytes()).as_ref().to_vec())
.unwrap();
BootstrapWitness::new(&vkey, &signature, chain_code, addr.attributes())
}
#[wasm_bindgen]
pub fn make_icarus_bootstrap_witness(
tx_body_hash: &TransactionHash,
addr: &ByronAddress,
key: &Bip32PrivateKey,
) -> BootstrapWitness {
let chain_code = key.chaincode();
let raw_key = key.to_raw_key();
let vkey = Vkey::new(&raw_key.to_public());
let signature = raw_key.sign(&tx_body_hash.to_bytes());
BootstrapWitness::new(&vkey, &signature, chain_code, addr.attributes())
}
#[wasm_bindgen]
pub fn make_vkey_witness(tx_body_hash: &TransactionHash, sk: &PrivateKey) -> Vkeywitness {
let sig = sk.sign(tx_body_hash.0.as_ref());
Vkeywitness::new(&Vkey::new(&sk.to_public()), &sig)
}
#[wasm_bindgen]
pub fn hash_auxiliary_data(auxiliary_data: &AuxiliaryData) -> AuxiliaryDataHash {
AuxiliaryDataHash::from(blake2b256(&auxiliary_data.to_bytes()))
}
#[wasm_bindgen]
pub fn hash_transaction(tx_body: &TransactionBody) -> TransactionHash {
TransactionHash::from(crypto::blake2b256(tx_body.to_bytes().as_ref()))
}
#[wasm_bindgen]
pub fn hash_plutus_data(plutus_data: &PlutusData) -> DataHash {
DataHash::from(blake2b256(&plutus_data.to_bytes()))
}
#[wasm_bindgen]
pub fn hash_script_data(
redeemers: &Redeemers,
cost_models: &Costmdls,
datums: Option<PlutusList>,
) -> ScriptDataHash {
let mut buf = Vec::new();
if redeemers.len() == 0 && datums.is_some() {
buf.push(0x80);
if let Some(d) = &datums {
buf.extend(d.to_bytes());
}
buf.push(0xA0);
} else {
buf.extend(redeemers.to_bytes());
if let Some(d) = &datums {
buf.extend(d.to_bytes());
}
buf.extend(cost_models.language_views_encoding());
}
ScriptDataHash::from(blake2b256(&buf))
}
pub fn internal_get_implicit_input(
withdrawals: &Option<Withdrawals>,
certs: &Option<Certificates>,
pool_deposit: &BigNum, key_deposit: &BigNum, ) -> Result<Value, JsError> {
let withdrawal_sum = match &withdrawals {
None => to_bignum(0),
Some(x) => {
x.0.values()
.try_fold(to_bignum(0), |acc, ref withdrawal_amt| {
acc.checked_add(&withdrawal_amt)
})?
}
};
let certificate_refund = match &certs {
None => to_bignum(0),
Some(certs) => certs
.0
.iter()
.try_fold(to_bignum(0), |acc, ref cert| match &cert.0 {
CertificateEnum::StakeDeregistration(cert) => {
if let Some(coin) = cert.coin {
acc.checked_add(&coin)
} else {
acc.checked_add(&key_deposit)
}
}
CertificateEnum::PoolRetirement(_) => acc.checked_add(&pool_deposit),
CertificateEnum::DrepDeregistration(cert) => acc.checked_add(&cert.coin),
_ => Ok(acc),
})?,
};
Ok(Value::new(
&withdrawal_sum.checked_add(&certificate_refund)?,
))
}
pub fn internal_get_deposit(
certs: &Option<Certificates>,
pool_deposit: &BigNum, key_deposit: &BigNum, ) -> Result<Coin, JsError> {
let certificate_refund = match &certs {
None => to_bignum(0),
Some(certs) => certs
.0
.iter()
.try_fold(to_bignum(0), |acc, ref cert| match &cert.0 {
CertificateEnum::PoolRegistration(_) => acc.checked_add(&pool_deposit),
CertificateEnum::StakeRegistration(cert) => {
if let Some(coin) = cert.coin {
acc.checked_add(&coin)
} else {
acc.checked_add(&key_deposit)
}
}
CertificateEnum::DrepRegistration(cert) => acc.checked_add(&cert.coin),
CertificateEnum::StakeRegistrationAndDelegation(cert) => acc.checked_add(&cert.coin),
CertificateEnum::VoteRegistrationAndDelegation(cert) => acc.checked_add(&cert.coin),
CertificateEnum::StakeVoteRegistrationAndDelegation(cert) => acc.checked_add(&cert.coin),
_ => Ok(acc),
})?,
};
Ok(certificate_refund)
}
#[wasm_bindgen]
pub fn get_implicit_input(
txbody: &TransactionBody,
pool_deposit: &BigNum, key_deposit: &BigNum, ) -> Result<Value, JsError> {
internal_get_implicit_input(
&txbody.withdrawals,
&txbody.certs,
&pool_deposit,
&key_deposit,
)
}
#[wasm_bindgen]
pub fn get_deposit(
txbody: &TransactionBody,
pool_deposit: &BigNum, key_deposit: &BigNum, ) -> Result<Coin, JsError> {
internal_get_deposit(&txbody.certs, &pool_deposit, &key_deposit)
}
#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct MinOutputAdaCalculator {
output: TransactionOutput,
data_cost: DataCost,
}
impl MinOutputAdaCalculator {
pub fn new(output: &TransactionOutput, data_cost: &DataCost) -> Self {
Self {
output: output.clone(),
data_cost: data_cost.clone(),
}
}
pub fn new_empty(data_cost: &DataCost) -> Result<MinOutputAdaCalculator, JsError> {
Ok(Self {
output: MinOutputAdaCalculator::create_fake_output()?,
data_cost: data_cost.clone(),
})
}
pub fn set_address(&mut self, address: &Address) {
self.output.address = address.clone();
}
pub fn set_plutus_data(&mut self, data: &PlutusData) {
self.output.plutus_data = Some(DataOption::Data(data.clone()));
}
pub fn set_data_hash(&mut self, data_hash: &DataHash) {
self.output.plutus_data = Some(DataOption::DataHash(data_hash.clone()));
}
pub fn set_amount(&mut self, amount: &Value) {
self.output.amount = amount.clone();
}
pub fn set_script_ref(&mut self, script_ref: &ScriptRef) {
self.output.script_ref = Some(script_ref.clone());
}
pub fn calculate_ada(&self) -> Result<BigNum, JsError> {
let mut output: TransactionOutput = self.output.clone();
for _ in 0..3 {
let required_coin = Self::calc_required_coin(&output, &self.data_cost)?;
if output.amount.coin.less_than(&required_coin) {
output.amount.coin = required_coin.clone();
} else {
return Ok(required_coin);
}
}
output.amount.coin = to_bignum(u64::MAX);
Ok(Self::calc_required_coin(&output, &self.data_cost)?)
}
fn create_fake_output() -> Result<TransactionOutput, JsError> {
let fake_base_address: Address = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w")?;
let fake_value: Value = Value::new(&to_bignum(1000000));
Ok(TransactionOutput::new(&fake_base_address, &fake_value))
}
pub fn calc_size_cost(data_cost: &DataCost, size: usize) -> Result<Coin, JsError> {
to_bignum(size as u64).checked_add(&to_bignum(160))?
.checked_mul(&data_cost.coins_per_byte())
}
pub fn calc_required_coin(output: &TransactionOutput, data_cost: &DataCost) -> Result<Coin, JsError> {
Self::calc_size_cost(data_cost,output.to_bytes().len())
}
}
#[wasm_bindgen]
pub fn min_ada_for_output(
output: &TransactionOutput,
data_cost: &DataCost,
) -> Result<BigNum, JsError> {
MinOutputAdaCalculator::new(output, data_cost).calculate_ada()
}
#[wasm_bindgen]
#[deprecated(since = "11.0.0", note = "Use `min_ada_for_output` instead")]
pub fn min_ada_required(
assets: &Value,
has_data_hash: bool, coins_per_utxo_word: &BigNum, ) -> Result<BigNum, JsError> {
let data_cost = DataCost::new_coins_per_word(coins_per_utxo_word);
let mut calc = MinOutputAdaCalculator::new_empty(&data_cost)?;
calc.set_amount(assets);
if has_data_hash {
calc.set_data_hash(&fake_data_hash(0));
}
calc.calculate_ada()
}
#[wasm_bindgen]
pub enum ScriptSchema {
Wallet,
Node,
}
#[wasm_bindgen]
pub fn encode_json_str_to_native_script(
json: &str,
self_xpub: &str,
schema: ScriptSchema,
) -> Result<NativeScript, JsError> {
let value: serde_json::Value =
serde_json::from_str(&json).map_err(|e| JsError::from_str(&e.to_string()))?;
let native_script = match schema {
ScriptSchema::Wallet => encode_wallet_value_to_native_script(value, self_xpub)?,
ScriptSchema::Node => todo!(),
};
Ok(native_script)
}
fn encode_wallet_value_to_native_script(
value: serde_json::Value,
self_xpub: &str,
) -> Result<NativeScript, JsError> {
match value {
serde_json::Value::Object(map)
if map.contains_key("cosigners") && map.contains_key("template") =>
{
let mut cosigners = HashMap::new();
if let serde_json::Value::Object(cosigner_map) = map.get("cosigners").unwrap() {
for (key, value) in cosigner_map.iter() {
if let serde_json::Value::String(xpub) = value {
if xpub == "self" {
cosigners.insert(key.to_owned(), self_xpub.to_owned());
} else {
cosigners.insert(key.to_owned(), xpub.to_owned());
}
} else {
return Err(JsError::from_str("cosigner value must be a string"));
}
}
} else {
return Err(JsError::from_str("cosigners must be a map"));
}
let template = map.get("template").unwrap();
let template_native_script = encode_template_to_native_script(template, &cosigners)?;
Ok(template_native_script)
}
_ => Err(JsError::from_str(
"top level must be an object. cosigners and template keys are required",
)),
}
}
fn encode_template_to_native_script(
template: &serde_json::Value,
cosigners: &HashMap<String, String>,
) -> Result<NativeScript, JsError> {
match template {
serde_json::Value::String(cosigner) => {
if let Some(xpub) = cosigners.get(cosigner) {
let bytes = Vec::from_hex(xpub).map_err(|e| JsError::from_str(&e.to_string()))?;
let public_key = Bip32PublicKey::from_bytes(&bytes)?;
Ok(NativeScript::new_script_pubkey(&ScriptPubkey::new(
&public_key.to_raw_key().hash(),
)))
} else {
Err(JsError::from_str(&format!(
"cosigner {} not found",
cosigner
)))
}
}
serde_json::Value::Object(map) if map.contains_key("all") => {
let mut all = NativeScripts::new();
if let serde_json::Value::Array(array) = map.get("all").unwrap() {
for val in array {
all.add(&encode_template_to_native_script(val, cosigners)?);
}
} else {
return Err(JsError::from_str("all must be an array"));
}
Ok(NativeScript::new_script_all(&ScriptAll::new(&all)))
}
serde_json::Value::Object(map) if map.contains_key("any") => {
let mut any = NativeScripts::new();
if let serde_json::Value::Array(array) = map.get("any").unwrap() {
for val in array {
any.add(&encode_template_to_native_script(val, cosigners)?);
}
} else {
return Err(JsError::from_str("any must be an array"));
}
Ok(NativeScript::new_script_any(&ScriptAny::new(&any)))
}
serde_json::Value::Object(map) if map.contains_key("some") => {
if let serde_json::Value::Object(some) = map.get("some").unwrap() {
if some.contains_key("at_least") && some.contains_key("from") {
let n = if let serde_json::Value::Number(at_least) =
some.get("at_least").unwrap()
{
if let Some(n) = at_least.as_u64() {
n as u32
} else {
return Err(JsError::from_str("at_least must be an integer"));
}
} else {
return Err(JsError::from_str("at_least must be an integer"));
};
let mut from_scripts = NativeScripts::new();
if let serde_json::Value::Array(array) = some.get("from").unwrap() {
for val in array {
from_scripts.add(&encode_template_to_native_script(val, cosigners)?);
}
} else {
return Err(JsError::from_str("from must be an array"));
}
Ok(NativeScript::new_script_n_of_k(&ScriptNOfK::new(
n,
&from_scripts,
)))
} else {
Err(JsError::from_str("some must contain at_least and from"))
}
} else {
Err(JsError::from_str("some must be an object"))
}
}
serde_json::Value::Object(map) if map.contains_key("active_from") => {
if let serde_json::Value::Number(active_from) = map.get("active_from").unwrap() {
if let Some(n) = active_from.as_u64() {
let slot: SlotBigNum = n.into();
let time_lock_start = TimelockStart::new_timelockstart(&slot);
Ok(NativeScript::new_timelock_start(&time_lock_start))
} else {
Err(JsError::from_str(
"active_from slot must be an integer greater than or equal to 0",
))
}
} else {
Err(JsError::from_str("active_from slot must be a number"))
}
}
serde_json::Value::Object(map) if map.contains_key("active_until") => {
if let serde_json::Value::Number(active_until) = map.get("active_until").unwrap() {
if let Some(n) = active_until.as_u64() {
let slot: SlotBigNum = n.into();
let time_lock_expiry = TimelockExpiry::new_timelockexpiry(&slot);
Ok(NativeScript::new_timelock_expiry(&time_lock_expiry))
} else {
Err(JsError::from_str(
"active_until slot must be an integer greater than or equal to 0",
))
}
} else {
Err(JsError::from_str("active_until slot must be a number"))
}
}
_ => Err(JsError::from_str("invalid template format")),
}
}
pub(crate) fn opt64<T>(o: &Option<T>) -> u64 {
o.is_some() as u64
}
pub struct ValueShortage {
pub(crate) ada_shortage: Option<(Coin, Coin, Coin)>,
pub(crate) asset_shortage: Vec<(PolicyID, AssetName, Coin, Coin)>,
}
impl Display for ValueShortage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "shortage: {{")?;
if let Some((input_data, out_data, fee)) = self.ada_shortage {
writeln!(f, "ada in inputs: {}, ada in outputs: {}, fee {}", input_data, out_data, fee)?;
writeln!(f, "NOTE! \"ada in inputs\" must be >= (\"ada in outputs\" + fee) before adding change")?;
writeln!(f, "and \"ada in inputs\" must be == (\"ada in outputs\" + fee) after adding change")?;
}
for (policy_id, asset_name, asset_shortage, asset_available) in
&self.asset_shortage
{
write!(f, "policy id: \"{}\", asset name: \"{}\" ", policy_id, asset_name)?;
writeln!(f, "coins in inputs: {}, coins in outputs: {}", asset_shortage, asset_available)?;
}
write!(f, " }}")
}
}
pub(crate) fn get_input_shortage(all_inputs_value: &Value, all_outputs_value: &Value, fee: &Coin)
-> Result<Option<ValueShortage>, JsError> {
let mut shortage = ValueShortage{
ada_shortage: None,
asset_shortage: Vec::new()};
if all_inputs_value.coin < all_outputs_value.coin.checked_add(fee)? {
shortage.ada_shortage = Some((
all_inputs_value.coin.clone(),
all_outputs_value.coin.clone(),
fee.clone()));
}
if let Some(policies) = &all_outputs_value.multiasset {
for (policy_id, assets) in &policies.0 {
for (asset_name, coins) in &assets.0 {
let inputs_coins = match &all_inputs_value.multiasset {
Some(multiasset) => multiasset.get_asset(policy_id, asset_name),
None => Coin::zero()
};
if inputs_coins < *coins {
shortage.asset_shortage.push((policy_id.clone(), asset_name.clone(), inputs_coins, coins.clone()));
}
}
}
}
if shortage.ada_shortage.is_some() || shortage.asset_shortage.len() > 0 {
Ok(Some(shortage))
} else {
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tx_builder_constants::TxBuilderConstants;
const COINS_PER_UTXO_WORD: u64 = 34_482;
fn one_policy_one_0_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(0),
multiasset: Some(token_bundle),
}
}
fn one_policy_one_1_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![1]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(1407406),
multiasset: Some(token_bundle),
}
}
fn one_policy_three_1_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![1]), &BigNum(1));
asset_list.insert(&AssetName(vec![2]), &BigNum(1));
asset_list.insert(&AssetName(vec![3]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(1555554),
multiasset: Some(token_bundle),
}
}
fn two_policies_one_0_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
token_bundle.insert(&PolicyID::from([1; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(1592591),
multiasset: Some(token_bundle),
}
}
fn two_policies_one_1_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![1]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
token_bundle.insert(&PolicyID::from([1; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(1592591),
multiasset: Some(token_bundle),
}
}
fn three_policies_96_1_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
fn add_policy(token_bundle: &mut MultiAsset, index: u8) -> () {
let mut asset_list = Assets::new();
for i in 0..32 {
asset_list.insert(&AssetName(vec![index * 32 + i]), &BigNum(1));
}
token_bundle.insert(
&PolicyID::from([index; ScriptHash::BYTE_COUNT]),
&asset_list,
);
}
add_policy(&mut token_bundle, 1);
add_policy(&mut token_bundle, 2);
add_policy(&mut token_bundle, 3);
Value {
coin: BigNum(7592585),
multiasset: Some(token_bundle),
}
}
fn one_policy_three_32_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(&AssetName(vec![1; 32]), &BigNum(1));
asset_list.insert(&AssetName(vec![2; 32]), &BigNum(1));
asset_list.insert(&AssetName(vec![3; 32]), &BigNum(1));
token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list);
Value {
coin: BigNum(1555554),
multiasset: Some(token_bundle),
}
}
#[test]
fn min_ada_value_no_multiasset() {
assert_eq!(
from_bignum(
&min_ada_required(
&Value::new(&Coin::zero()),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
969750,
);
}
#[test]
fn min_ada_value_one_policy_one_0_char_asset() {
assert_eq!(
from_bignum(
&min_ada_required(
&one_policy_one_0_char_asset(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_120_600,
);
}
#[test]
fn min_ada_value_one_policy_one_1_char_asset() {
assert_eq!(
from_bignum(
&min_ada_required(
&one_policy_one_1_char_asset(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_124_910,
);
}
#[test]
fn min_ada_value_one_policy_three_1_char_assets() {
assert_eq!(
from_bignum(
&min_ada_required(
&one_policy_three_1_char_assets(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_150_770,
);
}
#[test]
fn min_ada_value_two_policies_one_0_char_asset() {
assert_eq!(
from_bignum(
&min_ada_required(
&two_policies_one_0_char_asset(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_262_830,
);
}
#[test]
fn min_ada_value_two_policies_one_1_char_asset() {
assert_eq!(
from_bignum(
&min_ada_required(
&two_policies_one_1_char_asset(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_271_450,
);
}
#[test]
fn min_ada_value_three_policies_96_1_char_assets() {
assert_eq!(
from_bignum(
&min_ada_required(
&three_policies_96_1_char_assets(),
false,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
2_633_410,
);
}
#[test]
fn min_ada_value_one_policy_one_0_char_asset_datum_hash() {
assert_eq!(
from_bignum(
&min_ada_required(
&one_policy_one_0_char_asset(),
true,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_267_140,
);
}
#[test]
fn min_ada_value_one_policy_three_32_char_assets_datum_hash() {
assert_eq!(
from_bignum(
&min_ada_required(
&one_policy_three_32_char_assets(),
true,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_711_070,
);
}
#[test]
fn min_ada_value_two_policies_one_0_char_asset_datum_hash() {
assert_eq!(
from_bignum(
&min_ada_required(
&two_policies_one_0_char_asset(),
true,
&to_bignum(COINS_PER_UTXO_WORD),
)
.unwrap()
),
1_409_370,
);
}
#[test]
fn subtract_values() {
let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]);
let policy2 = PolicyID::from([1; ScriptHash::BYTE_COUNT]);
let asset1 = AssetName(vec![1]);
let asset2 = AssetName(vec![2]);
let asset3 = AssetName(vec![3]);
let asset4 = AssetName(vec![4]);
let mut token_bundle1 = MultiAsset::new();
{
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
asset_list1.insert(&asset2, &BigNum(1));
asset_list1.insert(&asset3, &BigNum(1));
asset_list1.insert(&asset4, &BigNum(2));
token_bundle1.insert(&policy1, &asset_list1);
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy2, &asset_list2);
}
let assets1 = Value {
coin: BigNum(1555554),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
{
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(2));
asset_list2.insert(&asset2, &BigNum(1));
asset_list2.insert(&asset4, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy2, &asset_list2);
}
let assets2 = Value {
coin: BigNum(2555554),
multiasset: Some(token_bundle2),
};
let result = assets1.clamped_sub(&assets2);
assert_eq!(result.coin().to_str(), "0");
assert_eq!(
result.multiasset().unwrap().len(),
1 );
let policy1_content = result.multiasset().unwrap().get(&policy1).unwrap();
assert_eq!(policy1_content.len(), 2);
assert_eq!(policy1_content.get(&asset3).unwrap().to_str(), "1");
assert_eq!(policy1_content.get(&asset4).unwrap().to_str(), "1");
}
#[test]
fn compare_values() {
let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]);
let asset1 = AssetName(vec![1]);
let asset2 = AssetName(vec![2]);
{
let a = Value::new(&to_bignum(1));
let b = Value::new(&to_bignum(1));
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal);
}
{
let a = Value::new(&to_bignum(2));
let b = Value::new(&to_bignum(1));
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater);
}
{
let a = Value::new(&to_bignum(1));
let b = Value::new(&to_bignum(2));
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let b = Value::new(&to_bignum(1));
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value::new(&to_bignum(1));
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(2),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(2),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(2));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(2));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(2),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(2));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(2),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b), None);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(2));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(2));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(2),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(2),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset1, &BigNum(2));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b), None);
}
{
let mut token_bundle1 = MultiAsset::new();
let mut asset_list1 = Assets::new();
asset_list1.insert(&asset1, &BigNum(1));
token_bundle1.insert(&policy1, &asset_list1);
let a = Value {
coin: BigNum(1),
multiasset: Some(token_bundle1),
};
let mut token_bundle2 = MultiAsset::new();
let mut asset_list2 = Assets::new();
asset_list2.insert(&asset2, &BigNum(1));
token_bundle2.insert(&policy1, &asset_list2);
let b = Value {
coin: BigNum(1),
multiasset: Some(token_bundle2),
};
assert_eq!(a.partial_cmp(&b), None);
}
}
#[test]
fn bigint_serialization() {
let zero = BigInt::from_str("0").unwrap();
let zero_rt = BigInt::from_bytes(zero.to_bytes()).unwrap();
assert_eq!(zero.to_str(), zero_rt.to_str());
assert_eq!(zero.to_bytes(), vec![0x00]);
let pos_small = BigInt::from_str("100").unwrap();
let pos_small_rt = BigInt::from_bytes(pos_small.to_bytes()).unwrap();
assert_eq!(pos_small.to_str(), pos_small_rt.to_str());
let pos_big = BigInt::from_str("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap();
let pos_big_rt = BigInt::from_bytes(pos_big.to_bytes()).unwrap();
assert_eq!(pos_big.to_str(), pos_big_rt.to_str());
let neg_small = BigInt::from_str("-100").unwrap();
let neg_small_rt = BigInt::from_bytes(neg_small.to_bytes()).unwrap();
assert_eq!(neg_small.to_str(), neg_small_rt.to_str());
let neg_big = BigInt::from_str("-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap();
let neg_big_rt = BigInt::from_bytes(neg_big.to_bytes()).unwrap();
assert_eq!(neg_big.to_str(), neg_big_rt.to_str());
assert_eq!(
hex::decode("c349010000000000000000").unwrap(),
BigInt::from_str("-18446744073709551617")
.unwrap()
.to_bytes()
);
assert_eq!(
hex::decode("c249010000000000000000").unwrap(),
BigInt::from_str("18446744073709551616").unwrap().to_bytes()
);
assert_eq!(
hex::decode("1b000000e8d4a51000").unwrap(),
BigInt::from_str("1000000000000").unwrap().to_bytes()
);
assert_eq!(
hex::decode("3bffffffffffffffff").unwrap(),
BigInt::from_str("-18446744073709551616")
.unwrap()
.to_bytes()
);
assert_eq!(
hex::decode("3903e7").unwrap(),
BigInt::from_str("-1000").unwrap().to_bytes()
);
let x = BigInt::from_str("-18446744073709551617").unwrap();
let x_rt = BigInt::from_bytes(x.to_bytes()).unwrap();
assert_eq!(x.to_str(), x_rt.to_str());
}
#[test]
fn bounded_bytes_read_chunked() {
use std::io::Cursor;
let chunks = vec![
vec![
0x52, 0x73, 0x6F, 0x6D, 0x65, 0x20, 0x72, 0x61, 0x6E, 0x64, 0x6F, 0x6D, 0x20, 0x73,
0x74, 0x72, 0x69, 0x6E, 0x67,
],
vec![0x44, 0x01, 0x02, 0x03, 0x04],
];
let mut expected = Vec::new();
for chunk in chunks.iter() {
expected.extend_from_slice(&chunk[1..]);
}
let mut vec = vec![0x5f];
for mut chunk in chunks {
vec.append(&mut chunk);
}
vec.push(0xff);
let mut raw = Deserializer::from(Cursor::new(vec.clone()));
let found = read_bounded_bytes(&mut raw).unwrap();
assert_eq!(found, expected);
}
#[test]
fn bounded_bytes_write_chunked() {
let mut chunk_64 = vec![0x58, BOUNDED_BYTES_CHUNK_SIZE as u8];
chunk_64.extend(std::iter::repeat(37).take(BOUNDED_BYTES_CHUNK_SIZE));
let chunks = vec![chunk_64, vec![0x44, 0x01, 0x02, 0x03, 0x04]];
let mut input = Vec::new();
input.extend_from_slice(&chunks[0][2..]);
input.extend_from_slice(&chunks[1][1..]);
let mut serializer = cbor_event::se::Serializer::new_vec();
write_bounded_bytes(&mut serializer, &input).unwrap();
let written = serializer.finalize();
let mut expected = vec![0x5f];
for mut chunk in chunks {
expected.append(&mut chunk);
}
expected.push(0xff);
assert_eq!(expected, written);
}
#[test]
fn correct_script_data_hash() {
let mut datums = PlutusList::new();
datums.add(&PlutusData::new_integer(&BigInt::from_str("1000").unwrap()));
let mut redeemers = Redeemers::new();
redeemers.add(&Redeemer::new(
&RedeemerTag::new_spend(),
&BigNum::from_str("1").unwrap(),
&PlutusData::new_integer(&BigInt::from_str("2000").unwrap()),
&ExUnits::new(
&BigNum::from_str("0").unwrap(),
&BigNum::from_str("0").unwrap(),
),
));
let plutus_cost_model = CostModel::from_bytes(vec![
159, 26, 0, 3, 2, 89, 0, 1, 1, 26, 0, 6, 11, 199, 25, 2, 109, 0, 1, 26, 0, 2, 73, 240,
25, 3, 232, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 37, 206, 168, 25, 113, 247, 4, 25,
116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25,
116, 77, 24, 100, 25, 116, 77, 24, 100, 24, 100, 24, 100, 25, 116, 77, 24, 100, 26, 0,
2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73,
240, 25, 3, 232, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 25, 3, 232, 0, 8,
26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 2, 73, 240, 25, 3, 232,
0, 8, 26, 0, 2, 73, 240, 26, 0, 1, 183, 152, 24, 247, 1, 26, 0, 2, 73, 240, 25, 39, 16,
1, 26, 0, 2, 21, 94, 25, 5, 46, 1, 25, 3, 232, 26, 0, 2, 73, 240, 25, 3, 232, 1, 26, 0,
2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 1, 1, 26, 0,
2, 73, 240, 1, 26, 0, 2, 73, 240, 4, 26, 0, 1, 148, 175, 24, 248, 1, 26, 0, 1, 148,
175, 24, 248, 1, 26, 0, 2, 55, 124, 25, 5, 86, 1, 26, 0, 2, 189, 234, 25, 1, 241, 1,
26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0,
2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 66,
32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 25, 240, 76, 25, 43, 210, 0, 1, 26, 0, 2, 73,
240, 24, 32, 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 2, 66, 32,
26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 37, 206, 168, 25, 113, 247, 4, 0, 26, 0, 1,
65, 187, 4, 26, 0, 2, 73, 240, 25, 19, 136, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 3,
2, 89, 0, 1, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73,
240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240,
24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 51, 13, 167, 1, 1, 255,
])
.unwrap();
let mut cost_models = Costmdls::new();
cost_models.insert(&Language::new_plutus_v1(), &plutus_cost_model);
let script_data_hash = hash_script_data(&redeemers, &cost_models, Some(datums));
assert_eq!(
hex::encode(script_data_hash.to_bytes()),
"4415e6667e6d6bbd992af5092d48e3c2ba9825200d0234d2470068f7f0f178b3"
);
}
#[test]
fn native_scripts_from_wallet_json() {
let cosigner0_hex = "1423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db11423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db1";
let cosigner1_hex = "a48d97f57ce49433f347d44ee07e54a100229b4f8e125d25f7bca9ad66d9707a25cd1331f46f7d6e279451637ca20802a25c441ba9436abf644fe5410d1080e3";
let self_key_hex = "6ce83a12e9d4c783f54c0bb511303b37160a6e4f3f96b8e878a7c1f7751e18c4ccde3fb916d330d07f7bd51fb6bd99aa831d925008d3f7795033f48abd6df7f6";
let native_script = encode_json_str_to_native_script(
&format!(
r#"
{{
"cosigners": {{
"cosigner#0": "{}",
"cosigner#1": "{}",
"cosigner#2": "self"
}},
"template": {{
"some": {{
"at_least": 2,
"from": [
{{
"all": [
"cosigner#0",
{{ "active_from": 120 }}
]
}},
{{
"any": [
"cosigner#1",
{{ "active_until": 1000 }}
]
}},
"cosigner#2"
]
}}
}}
}}"#,
cosigner0_hex, cosigner1_hex
),
self_key_hex,
ScriptSchema::Wallet,
);
let n_of_k = native_script.unwrap().as_script_n_of_k().unwrap();
let from = n_of_k.native_scripts();
assert_eq!(n_of_k.n(), 2);
assert_eq!(from.len(), 3);
let all = from.get(0).as_script_all().unwrap().native_scripts();
assert_eq!(all.len(), 2);
let all_0 = all.get(0).as_script_pubkey().unwrap();
assert_eq!(
all_0.addr_keyhash(),
Bip32PublicKey::from_bytes(&hex::decode(cosigner0_hex).unwrap())
.unwrap()
.to_raw_key()
.hash()
);
let all_1 = all.get(1).as_timelock_start().unwrap();
assert_eq!(all_1.slot().unwrap(), 120);
let any = from.get(1).as_script_any().unwrap().native_scripts();
assert_eq!(all.len(), 2);
let any_0 = any.get(0).as_script_pubkey().unwrap();
assert_eq!(
any_0.addr_keyhash(),
Bip32PublicKey::from_bytes(&hex::decode(cosigner1_hex).unwrap())
.unwrap()
.to_raw_key()
.hash()
);
let any_1 = any.get(1).as_timelock_expiry().unwrap();
assert_eq!(any_1.slot().unwrap(), 1000);
let self_key = from.get(2).as_script_pubkey().unwrap();
assert_eq!(
self_key.addr_keyhash(),
Bip32PublicKey::from_bytes(&hex::decode(self_key_hex).unwrap())
.unwrap()
.to_raw_key()
.hash()
);
}
#[test]
fn int_to_str() {
assert_eq!(
Int::new(&BigNum(u64::max_value())).to_str(),
u64::max_value().to_string()
);
assert_eq!(
Int::new(&BigNum(u64::min_value())).to_str(),
u64::min_value().to_string()
);
assert_eq!(
Int::new_negative(&BigNum(u64::max_value())).to_str(),
(-(u64::max_value() as i128)).to_string()
);
assert_eq!(
Int::new_negative(&BigNum(u64::min_value())).to_str(),
(-(u64::min_value() as i128)).to_string()
);
assert_eq!(Int::new_i32(142).to_str(), "142");
assert_eq!(Int::new_i32(-142).to_str(), "-142");
}
#[test]
fn int_as_i32_or_nothing() {
let over_pos_i32 = (i32::max_value() as i64) + 1;
assert!(Int::new(&BigNum(over_pos_i32 as u64))
.as_i32_or_nothing()
.is_none());
let valid_pos_i32 = i32::max_value() as i64;
assert_eq!(
Int::new(&BigNum(valid_pos_i32 as u64))
.as_i32_or_nothing()
.unwrap(),
i32::max_value()
);
let over_neg_i32 = (i32::min_value() as i64) - 1;
assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64))
.as_i32_or_nothing()
.is_none());
let valid_neg_i32 = i32::min_value() as i64;
assert_eq!(
Int::new_negative(&BigNum((-valid_neg_i32) as u64))
.as_i32_or_nothing()
.unwrap(),
i32::min_value()
);
assert!(Int::new(&BigNum(u64::max_value()))
.as_i32_or_nothing()
.is_none());
assert_eq!(
Int::new(&BigNum(i32::max_value() as u64))
.as_i32_or_nothing()
.unwrap(),
i32::max_value()
);
assert_eq!(
Int::new_negative(&BigNum(i32::max_value() as u64))
.as_i32_or_nothing()
.unwrap(),
-i32::max_value()
);
assert_eq!(Int::new_i32(42).as_i32_or_nothing().unwrap(), 42);
assert_eq!(Int::new_i32(-42).as_i32_or_nothing().unwrap(), -42);
}
#[test]
fn int_as_i32_or_fail() {
let over_pos_i32 = (i32::max_value() as i64) + 1;
assert!(Int::new(&BigNum(over_pos_i32 as u64))
.as_i32_or_fail()
.is_err());
let valid_pos_i32 = i32::max_value() as i64;
assert_eq!(
Int::new(&BigNum(valid_pos_i32 as u64))
.as_i32_or_fail()
.unwrap(),
i32::max_value()
);
let over_neg_i32 = (i32::min_value() as i64) - 1;
assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64))
.as_i32_or_fail()
.is_err());
let valid_neg_i32 = i32::min_value() as i64;
assert_eq!(
Int::new_negative(&BigNum((-valid_neg_i32) as u64))
.as_i32_or_fail()
.unwrap(),
i32::min_value()
);
assert!(Int::new(&BigNum(u64::max_value()))
.as_i32_or_fail()
.is_err());
assert_eq!(
Int::new(&BigNum(i32::max_value() as u64))
.as_i32_or_fail()
.unwrap(),
i32::max_value()
);
assert_eq!(
Int::new_negative(&BigNum(i32::max_value() as u64))
.as_i32_or_fail()
.unwrap(),
-i32::max_value()
);
assert_eq!(Int::new_i32(42).as_i32_or_fail().unwrap(), 42);
assert_eq!(Int::new_i32(-42).as_i32_or_fail().unwrap(), -42);
}
#[test]
fn int_full_range() {
let bytes_x = vec![0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let x = Int::from_bytes(bytes_x.clone()).unwrap();
assert_eq!(x.to_str(), "-9223372036854775809");
assert_eq!(bytes_x, x.to_bytes());
let bytes_y = vec![0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
let y = Int::from_bytes(bytes_y.clone()).unwrap();
assert_eq!(y.to_str(), "-18446744073709551616");
assert_eq!(bytes_y, y.to_bytes());
}
#[test]
fn test_bigint_add() {
assert_eq!(to_bigint(10).add(&to_bigint(20)), to_bigint(30), );
assert_eq!(to_bigint(500).add(&to_bigint(800)), to_bigint(1300), );
}
#[test]
fn test_bigint_mul() {
assert_eq!(to_bigint(10).mul(&to_bigint(20)), to_bigint(200), );
assert_eq!(to_bigint(500).mul(&to_bigint(800)), to_bigint(400000), );
assert_eq!(to_bigint(12).mul(&to_bigint(22)), to_bigint(264), );
}
#[test]
fn test_bigint_div_ceil() {
assert_eq!(to_bigint(20).div_ceil(&to_bigint(10)), to_bigint(2), );
assert_eq!(to_bigint(20).div_ceil(&to_bigint(2)), to_bigint(10), );
assert_eq!(to_bigint(21).div_ceil(&to_bigint(2)), to_bigint(11), );
assert_eq!(to_bigint(6).div_ceil(&to_bigint(3)), to_bigint(2), );
assert_eq!(to_bigint(5).div_ceil(&to_bigint(3)), to_bigint(2), );
assert_eq!(to_bigint(7).div_ceil(&to_bigint(3)), to_bigint(3), );
}
#[test]
fn test_bignum_div() {
assert_eq!(to_bignum(10).div_floor(&to_bignum(1)), to_bignum(10), );
assert_eq!(to_bignum(10).div_floor(&to_bignum(3)), to_bignum(3), );
assert_eq!(to_bignum(10).div_floor(&to_bignum(4)), to_bignum(2), );
assert_eq!(to_bignum(10).div_floor(&to_bignum(5)), to_bignum(2), );
assert_eq!(to_bignum(10).div_floor(&to_bignum(6)), to_bignum(1), );
assert_eq!(to_bignum(10).div_floor(&to_bignum(12)), to_bignum(0), );
}
#[test]
fn test_vasil_v1_costmodel_hashing() {
let v1 = Language::new_plutus_v1();
let v1_cost_model = TxBuilderConstants::plutus_vasil_cost_models()
.get(&v1)
.unwrap();
let mut costmodels = Costmdls::new();
costmodels.insert(&v1, &v1_cost_model);
let hash = hash_script_data(
&Redeemers(vec![Redeemer::new(
&RedeemerTag::new_spend(),
&BigNum::zero(),
&PlutusData::new_integer(&BigInt::from_str("42").unwrap()),
&ExUnits::new(&to_bignum(1700), &to_bignum(368100)),
)]),
&costmodels,
Some(PlutusList::from(vec![PlutusData::new_integer(
&BigInt::from_str("42").unwrap(),
)])),
);
assert_eq!(
hex::encode(hash.to_bytes()),
"887e1b6416d750d871c0f5b7136b54f7b8e8b0e293379d090f38f8f821d08a29"
);
}
#[test]
fn bigint_as_int() {
let zero = BigInt::from_str("0").unwrap();
let zero_int = zero.as_int().unwrap();
assert_eq!(zero_int.0, 0i128);
let pos = BigInt::from_str("1024").unwrap();
let pos_int = pos.as_int().unwrap();
assert_eq!(pos_int.0, 1024i128);
let neg = BigInt::from_str("-1024").unwrap();
let neg_int = neg.as_int().unwrap();
assert_eq!(neg_int.0, -1024i128);
}
}