use super::coin_id::compute_coin_id;
use super::condition_sanitizers::{
parse_amount, parse_height, parse_seconds, sanitize_announce_msg, sanitize_hash,
};
use super::opcodes::{
parse_opcode, ConditionOpcode, AGG_SIG_COST, AGG_SIG_ME, AGG_SIG_UNSAFE, ALWAYS_TRUE,
ASSERT_COIN_ANNOUNCEMENT, ASSERT_HEIGHT_ABSOLUTE, ASSERT_HEIGHT_RELATIVE, ASSERT_MY_AMOUNT,
ASSERT_MY_COIN_ID, ASSERT_MY_PARENT_ID, ASSERT_MY_PUZZLEHASH, ASSERT_PUZZLE_ANNOUNCEMENT,
ASSERT_SECONDS_ABSOLUTE, ASSERT_SECONDS_RELATIVE, CREATE_COIN, CREATE_COIN_ANNOUNCEMENT,
CREATE_COIN_COST, CREATE_PUZZLE_ANNOUNCEMENT, RESERVE_FEE,
};
use super::sanitize_int::sanitize_uint;
use super::validation_error::{first, next, rest, ErrorCode, ValidationErr};
use crate::bytes::Bytes32;
use crate::gen::flags::COND_ARGS_NIL;
use crate::gen::flags::NO_UNKNOWN_CONDS;
use crate::gen::flags::STRICT_ARGS_COUNT;
use crate::gen::validation_error::check_nil;
use clvmr::allocator::{Allocator, NodePtr, SExp};
use clvmr::cost::Cost;
use clvmr::op_utils::u64_from_bytes;
use clvmr::sha2::{Digest, Sha256};
use std::cmp::max;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[derive(PartialEq, Hash, Eq, Debug)]
pub enum Condition {
AggSigUnsafe(NodePtr, NodePtr),
AggSigMe(NodePtr, NodePtr),
CreateCoin(NodePtr, u64, NodePtr),
ReserveFee(u64),
CreateCoinAnnouncement(NodePtr),
CreatePuzzleAnnouncement(NodePtr),
AssertCoinAnnouncement(NodePtr),
AssertPuzzleAnnouncement(NodePtr),
AssertMyCoinId(NodePtr),
AssertMyParentId(NodePtr),
AssertMyPuzzlehash(NodePtr),
AssertMyAmount(u64),
AssertSecondsRelative(u64),
AssertSecondsAbsolute(u64),
AssertHeightRelative(u32),
AssertHeightAbsolute(u32),
Skip,
}
pub fn parse_args(
a: &Allocator,
mut c: NodePtr,
op: ConditionOpcode,
flags: u32,
) -> Result<Condition, ValidationErr> {
match op {
AGG_SIG_UNSAFE => {
let pubkey = sanitize_hash(a, first(a, c)?, 48, ErrorCode::InvalidPubkey)?;
c = rest(a, c)?;
let message = sanitize_announce_msg(a, first(a, c)?, ErrorCode::InvalidMessage)?;
if (flags & COND_ARGS_NIL) != 0 {
check_nil(a, rest(a, c)?)?;
Ok(Condition::AggSigUnsafe(pubkey, message))
} else {
match a.sexp(rest(a, c)?) {
SExp::Pair(_, _) => Err(ValidationErr(c, ErrorCode::InvalidCondition)),
_ => Ok(Condition::AggSigUnsafe(pubkey, message)),
}
}
}
AGG_SIG_ME => {
let pubkey = sanitize_hash(a, first(a, c)?, 48, ErrorCode::InvalidPubkey)?;
c = rest(a, c)?;
let message = sanitize_announce_msg(a, first(a, c)?, ErrorCode::InvalidMessage)?;
if (flags & COND_ARGS_NIL) != 0 {
check_nil(a, rest(a, c)?)?;
Ok(Condition::AggSigMe(pubkey, message))
} else {
match a.sexp(rest(a, c)?) {
SExp::Pair(_, _) => Err(ValidationErr(c, ErrorCode::InvalidCondition)),
_ => Ok(Condition::AggSigMe(pubkey, message)),
}
}
}
CREATE_COIN => {
let puzzle_hash = sanitize_hash(a, first(a, c)?, 32, ErrorCode::InvalidPuzzleHash)?;
c = rest(a, c)?;
let amount = parse_amount(a, first(a, c)?, ErrorCode::InvalidCoinAmount)?;
c = rest(a, c)?;
if let Ok(params) = first(a, c) {
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
if let Ok(param) = first(a, params) {
if let SExp::Atom(b) = a.sexp(param) {
if a.buf(&b).len() <= 32 {
return Ok(Condition::CreateCoin(puzzle_hash, amount, param));
}
}
}
} else if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, c)?;
}
Ok(Condition::CreateCoin(puzzle_hash, amount, a.null()))
}
RESERVE_FEE => {
let fee = parse_amount(a, first(a, c)?, ErrorCode::ReserveFeeConditionFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::ReserveFee(fee))
}
CREATE_COIN_ANNOUNCEMENT => {
let msg = sanitize_announce_msg(a, first(a, c)?, ErrorCode::InvalidCoinAnnouncement)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::CreateCoinAnnouncement(msg))
}
ASSERT_COIN_ANNOUNCEMENT => {
let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertCoinAnnouncementFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertCoinAnnouncement(id))
}
CREATE_PUZZLE_ANNOUNCEMENT => {
let msg = sanitize_announce_msg(a, first(a, c)?, ErrorCode::InvalidPuzzleAnnouncement)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::CreatePuzzleAnnouncement(msg))
}
ASSERT_PUZZLE_ANNOUNCEMENT => {
let id = sanitize_hash(
a,
first(a, c)?,
32,
ErrorCode::AssertPuzzleAnnouncementFailed,
)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertPuzzleAnnouncement(id))
}
ASSERT_MY_COIN_ID => {
let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertMyCoinIdFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertMyCoinId(id))
}
ASSERT_MY_PARENT_ID => {
let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertMyParentIdFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertMyParentId(id))
}
ASSERT_MY_PUZZLEHASH => {
let id = sanitize_hash(a, first(a, c)?, 32, ErrorCode::AssertMyPuzzlehashFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertMyPuzzlehash(id))
}
ASSERT_MY_AMOUNT => {
let amount = parse_amount(a, first(a, c)?, ErrorCode::AssertMyAmountFailed)?;
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertMyAmount(amount))
}
ASSERT_SECONDS_RELATIVE => {
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertSecondsRelative(parse_seconds(
a,
first(a, c)?,
ErrorCode::AssertSecondsRelative,
)?))
}
ASSERT_SECONDS_ABSOLUTE => {
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertSecondsAbsolute(parse_seconds(
a,
first(a, c)?,
ErrorCode::AssertSecondsAbsolute,
)?))
}
ASSERT_HEIGHT_RELATIVE => {
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
match sanitize_uint(a, first(a, c)?, 4, ErrorCode::AssertHeightRelative) {
Err(ValidationErr(_, ErrorCode::NegativeAmount)) => Ok(Condition::Skip),
Err(r) => Err(r),
Ok(r) => Ok(Condition::AssertHeightRelative(u64_from_bytes(r) as u32)),
}
}
ASSERT_HEIGHT_ABSOLUTE => {
if (flags & STRICT_ARGS_COUNT) != 0 {
check_nil(a, rest(a, c)?)?;
}
Ok(Condition::AssertHeightAbsolute(parse_height(
a,
first(a, c)?,
ErrorCode::AssertHeightAbsolute,
)?))
}
ALWAYS_TRUE => {
Ok(Condition::Skip)
}
_ => Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode)),
}
}
#[derive(Debug)]
pub struct NewCoin {
pub puzzle_hash: Bytes32,
pub amount: u64,
pub hint: NodePtr,
}
impl Hash for NewCoin {
fn hash<H: Hasher>(&self, h: &mut H) {
self.puzzle_hash.hash(h);
self.amount.hash(h);
}
}
impl Eq for NewCoin {}
impl PartialEq for NewCoin {
fn eq(&self, lhs: &NewCoin) -> bool {
self.amount == lhs.amount && self.puzzle_hash == lhs.puzzle_hash
}
}
#[derive(Debug)]
pub struct Spend {
pub coin_id: Arc<Bytes32>,
pub puzzle_hash: NodePtr,
pub height_relative: Option<u32>,
pub seconds_relative: u64,
pub create_coin: HashSet<NewCoin>,
pub agg_sig_me: Vec<(NodePtr, NodePtr)>,
}
#[derive(Debug)]
pub struct SpendBundleConditions {
pub spends: Vec<Spend>,
pub reserve_fee: u64,
pub height_absolute: u32,
pub seconds_absolute: u64,
pub agg_sig_unsafe: Vec<(NodePtr, NodePtr)>,
pub cost: u64,
}
struct ParseState {
announce_coin: HashSet<(Arc<Bytes32>, NodePtr)>,
announce_puzzle: HashSet<(NodePtr, NodePtr)>,
assert_coin: HashSet<NodePtr>,
assert_puzzle: HashSet<NodePtr>,
spent_coins: HashSet<Arc<Bytes32>>,
}
impl ParseState {
fn new() -> ParseState {
ParseState {
announce_coin: HashSet::new(),
announce_puzzle: HashSet::new(),
assert_coin: HashSet::new(),
assert_puzzle: HashSet::new(),
spent_coins: HashSet::new(),
}
}
}
fn parse_spend_conditions(
ret: &mut SpendBundleConditions,
a: &Allocator,
state: &mut ParseState,
mut spend: NodePtr,
flags: u32,
max_cost: &mut Cost,
) -> Result<(), ValidationErr> {
let parent_id = sanitize_hash(a, first(a, spend)?, 32, ErrorCode::InvalidParentId)?;
spend = rest(a, spend)?;
let puzzle_hash = sanitize_hash(a, first(a, spend)?, 32, ErrorCode::InvalidPuzzleHash)?;
spend = rest(a, spend)?;
let amount_buf = sanitize_uint(a, first(a, spend)?, 8, ErrorCode::InvalidCoinAmount)?.to_vec();
let my_amount = u64_from_bytes(&amount_buf);
let cond = rest(a, spend)?;
let coin_id = Arc::new(compute_coin_id(a, parent_id, puzzle_hash, &amount_buf));
if !state.spent_coins.insert(coin_id.clone()) {
return Err(ValidationErr(spend, ErrorCode::DoubleSpend));
}
let mut spend = Spend {
coin_id,
puzzle_hash,
height_relative: None,
seconds_relative: 0,
create_coin: HashSet::new(),
agg_sig_me: Vec::new(),
};
let mut iter = first(a, cond)?;
while let Some((mut c, next)) = next(a, iter)? {
iter = next;
let op = match parse_opcode(a, first(a, c)?) {
None => {
if (flags & NO_UNKNOWN_CONDS) != 0 {
return Err(ValidationErr(c, ErrorCode::InvalidConditionOpcode));
}
continue;
}
Some(v) => v,
};
match op {
CREATE_COIN => {
if *max_cost < CREATE_COIN_COST {
return Err(ValidationErr(c, ErrorCode::CostExceeded));
}
*max_cost -= CREATE_COIN_COST;
}
AGG_SIG_UNSAFE | AGG_SIG_ME => {
if *max_cost < AGG_SIG_COST {
return Err(ValidationErr(c, ErrorCode::CostExceeded));
}
*max_cost -= AGG_SIG_COST;
}
_ => (),
}
c = rest(a, c)?;
let cva = parse_args(a, c, op, flags)?;
match cva {
Condition::ReserveFee(limit) => {
ret.reserve_fee = ret
.reserve_fee
.checked_add(limit)
.ok_or(ValidationErr(c, ErrorCode::ReserveFeeConditionFailed))?;
}
Condition::CreateCoin(ph, amount, hint) => {
let new_coin = NewCoin {
puzzle_hash: a.atom(ph).into(),
amount,
hint,
};
if !spend.create_coin.insert(new_coin) {
return Err(ValidationErr(c, ErrorCode::DuplicateOutput));
}
}
Condition::AssertSecondsRelative(s) => {
spend.seconds_relative = max(spend.seconds_relative, s);
}
Condition::AssertSecondsAbsolute(s) => {
ret.seconds_absolute = max(ret.seconds_absolute, s);
}
Condition::AssertHeightRelative(h) => {
spend.height_relative = Some(max(spend.height_relative.unwrap_or(0), h));
}
Condition::AssertHeightAbsolute(h) => {
ret.height_absolute = max(ret.height_absolute, h);
}
Condition::AssertMyCoinId(id) => {
if a.atom(id) != (*spend.coin_id).as_ref() {
return Err(ValidationErr(c, ErrorCode::AssertMyCoinIdFailed));
}
}
Condition::AssertMyAmount(amount) => {
if amount != my_amount {
return Err(ValidationErr(c, ErrorCode::AssertMyAmountFailed));
}
}
Condition::AssertMyParentId(id) => {
if a.atom(id) != a.atom(parent_id) {
return Err(ValidationErr(c, ErrorCode::AssertMyParentIdFailed));
}
}
Condition::AssertMyPuzzlehash(hash) => {
if a.atom(hash) != a.atom(puzzle_hash) {
return Err(ValidationErr(c, ErrorCode::AssertMyPuzzlehashFailed));
}
}
Condition::CreateCoinAnnouncement(msg) => {
state.announce_coin.insert((spend.coin_id.clone(), msg));
}
Condition::CreatePuzzleAnnouncement(msg) => {
state.announce_puzzle.insert((spend.puzzle_hash, msg));
}
Condition::AssertCoinAnnouncement(msg) => {
state.assert_coin.insert(msg);
}
Condition::AssertPuzzleAnnouncement(msg) => {
state.assert_puzzle.insert(msg);
}
Condition::AggSigMe(pk, msg) => {
spend.agg_sig_me.push((pk, msg));
}
Condition::AggSigUnsafe(pk, msg) => {
ret.agg_sig_unsafe.push((pk, msg));
}
Condition::Skip => {}
}
}
ret.spends.push(spend);
Ok(())
}
pub fn parse_spends(
a: &Allocator,
spends: NodePtr,
max_cost: Cost,
flags: u32,
) -> Result<SpendBundleConditions, ValidationErr> {
let mut ret = SpendBundleConditions {
spends: Vec::new(),
reserve_fee: 0,
height_absolute: 0,
seconds_absolute: 0,
agg_sig_unsafe: Vec::new(),
cost: 0,
};
let mut state = ParseState::new();
let mut cost_left = max_cost;
let mut iter = first(a, spends)?;
while let Some((spend, next)) = next(a, iter)? {
iter = next;
parse_spend_conditions(&mut ret, a, &mut state, spend, flags, &mut cost_left)?;
}
if !state.assert_coin.is_empty() {
let mut announcements = HashSet::<Bytes32>::new();
for (coin_id, announce) in state.announce_coin {
let mut hasher = Sha256::new();
hasher.update(*coin_id);
hasher.update(a.atom(announce));
announcements.insert(hasher.finalize().as_slice().into());
}
for coin_assert in state.assert_coin {
if !announcements.contains(&a.atom(coin_assert).into()) {
return Err(ValidationErr(
coin_assert,
ErrorCode::AssertCoinAnnouncementFailed,
));
}
}
}
if !state.assert_puzzle.is_empty() {
let mut announcements = HashSet::<Bytes32>::new();
for (puzzle_hash, announce) in state.announce_puzzle {
let mut hasher = Sha256::new();
hasher.update(a.atom(puzzle_hash));
hasher.update(a.atom(announce));
announcements.insert(hasher.finalize().as_slice().into());
}
for puzzle_assert in state.assert_puzzle {
if !announcements.contains(&a.atom(puzzle_assert).into()) {
return Err(ValidationErr(
puzzle_assert,
ErrorCode::AssertPuzzleAnnouncementFailed,
));
}
}
}
ret.cost = max_cost - cost_left;
Ok(ret)
}
#[cfg(test)]
fn u64_to_bytes(n: u64) -> Vec<u8> {
let mut buf = Vec::<u8>::new();
buf.extend_from_slice(&n.to_be_bytes());
if (buf[0] & 0x80) != 0 {
buf.insert(0, 0);
} else {
while buf.len() > 1 && buf[0] == 0 && (buf[1] & 0x80) == 0 {
buf.remove(0);
}
}
buf
}
#[cfg(test)]
use clvmr::node::Node;
#[cfg(test)]
use clvmr::number::{ptr_from_number, Number};
#[cfg(test)]
use clvmr::serialize::node_to_bytes;
#[cfg(test)]
use hex::FromHex;
#[cfg(test)]
use num_traits::Num;
#[cfg(test)]
use std::collections::HashMap;
#[cfg(test)]
const H1: &[u8; 32] = &[
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
];
#[cfg(test)]
const H2: &[u8; 32] = &[
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
];
#[cfg(test)]
const LONG_VEC: &[u8; 33] = &[
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
3,
];
#[cfg(test)]
const PUBKEY: &[u8; 48] = &[
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
];
#[cfg(test)]
const MSG1: &[u8; 13] = &[3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3];
#[cfg(test)]
const MSG2: &[u8; 19] = &[4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4];
#[cfg(test)]
const LONGMSG: &[u8; 1025] = &[
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4,
];
#[cfg(test)]
fn hash_buf(b1: &[u8], b2: &[u8]) -> Vec<u8> {
let mut ctx = Sha256::new();
ctx.update(b1);
ctx.update(b2);
ctx.finalize().to_vec()
}
#[cfg(test)]
fn test_coin_id(parent_id: &[u8; 32], puzzle_hash: &[u8; 32], amount: u64) -> Bytes32 {
let mut hasher = Sha256::new();
hasher.update(parent_id);
hasher.update(puzzle_hash);
let buf = u64_to_bytes(amount);
hasher.update(&buf);
hasher.finalize().as_slice().into()
}
#[cfg(test)]
fn parse_list_impl(
a: &mut Allocator,
input: &str,
callback: &Option<fn(&mut Allocator) -> NodePtr>,
subs: &HashMap<&'static str, NodePtr>,
) -> (NodePtr, usize) {
if input.starts_with(" ") {
let (n, skip) = parse_list_impl(a, &input[1..], callback, subs);
return (n, skip + 1);
}
if input.starts_with(")") {
(a.null(), 1)
} else if input.starts_with("(") {
let (first, step1) = parse_list_impl(a, &input[1..], callback, subs);
let (rest, step2) = parse_list_impl(a, &input[(1 + step1)..], callback, subs);
(a.new_pair(first, rest).unwrap(), 1 + step1 + step2)
} else if input.starts_with("{") {
let var = input[1..].split_once("}").unwrap().0;
let ret = match var {
"" => callback.unwrap()(a),
_ => *subs.get(var).unwrap(),
};
(ret, var.len() + 2)
} else if input.starts_with("0x") {
let v = input.split_once(" ").unwrap().0;
let buf = Vec::from_hex(v.strip_prefix("0x").unwrap()).unwrap();
(a.new_atom(&buf).unwrap(), v.len() + 1)
} else if input.starts_with("-") || "0123456789".contains(input.get(0..1).unwrap()) {
let v = input.split_once(" ").unwrap().0;
let num = Number::from_str_radix(v, 10).unwrap();
(ptr_from_number(a, &num).unwrap(), v.len() + 1)
} else {
panic!("atom not supported \"{}\"", input);
}
}
#[cfg(test)]
fn parse_list(
a: &mut Allocator,
input: &str,
callback: &Option<fn(&mut Allocator) -> NodePtr>,
) -> NodePtr {
let mut subs = HashMap::<&'static str, NodePtr>::new();
subs.insert("h1", a.new_atom(H1).unwrap());
subs.insert("h2", a.new_atom(H2).unwrap());
subs.insert("long", a.new_atom(LONG_VEC).unwrap());
subs.insert("pubkey", a.new_atom(PUBKEY).unwrap());
subs.insert("msg1", a.new_atom(MSG1).unwrap());
subs.insert("msg2", a.new_atom(MSG2).unwrap());
subs.insert("longmsg", a.new_atom(LONGMSG).unwrap());
subs.insert("coin11", a.new_atom(&test_coin_id(H1, H1, 123)).unwrap());
subs.insert("coin12", a.new_atom(&test_coin_id(H1, H2, 123)).unwrap());
subs.insert("coin21", a.new_atom(&test_coin_id(H2, H1, 123)).unwrap());
subs.insert("coin22", a.new_atom(&test_coin_id(H2, H2, 123)).unwrap());
subs.insert(
"c11",
a.new_atom(&hash_buf(&test_coin_id(H1, H2, 123), MSG1))
.unwrap(),
);
subs.insert(
"c21",
a.new_atom(&hash_buf(&test_coin_id(H2, H2, 123), MSG1))
.unwrap(),
);
subs.insert(
"c12",
a.new_atom(&hash_buf(&test_coin_id(H1, H2, 123), MSG2))
.unwrap(),
);
subs.insert(
"c22",
a.new_atom(&hash_buf(&test_coin_id(H2, H2, 123), MSG2))
.unwrap(),
);
subs.insert("p11", a.new_atom(&hash_buf(H1, MSG1)).unwrap());
subs.insert("p21", a.new_atom(&hash_buf(H2, MSG1)).unwrap());
subs.insert("p12", a.new_atom(&hash_buf(H1, MSG2)).unwrap());
subs.insert("p22", a.new_atom(&hash_buf(H2, MSG2)).unwrap());
let (n, count) = parse_list_impl(a, input, callback, &subs);
assert_eq!(&input[count..], "");
n
}
#[cfg(test)]
fn cond_test_cb(
input: &str,
flags: u32,
callback: Option<fn(&mut Allocator) -> NodePtr>,
) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
let mut a = Allocator::new();
println!("input: {}", input);
let n = parse_list(&mut a, &input, &callback);
for c in node_to_bytes(&Node::new(&a, n)).unwrap() {
print!("{:02x}", c);
}
println!();
match parse_spends(&a, n, 11000000000, flags) {
Ok(list) => {
for n in &list.spends {
println!("{:?}", n);
}
Ok((a, list))
}
Err(e) => Err(e),
}
}
#[cfg(test)]
const MEMPOOL_MODE: u32 = COND_ARGS_NIL | STRICT_ARGS_COUNT | NO_UNKNOWN_CONDS;
#[cfg(test)]
fn cond_test(input: &str) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
cond_test_cb(input, MEMPOOL_MODE, None)
}
#[cfg(test)]
fn cond_test_flag(
input: &str,
flags: u32,
) -> Result<(Allocator, SpendBundleConditions), ValidationErr> {
cond_test_cb(input, flags, None)
}
#[test]
fn test_invalid_condition_list1() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (8 )))").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_condition_list2() {
assert_eq!(
cond_test("((({h1} ({h2} (123 ((8 ))))").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_condition_args_terminator() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((80 (50 8 ))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.seconds_relative, 50);
}
#[test]
fn test_invalid_condition_args_terminator_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_condition_list_terminator() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((80 (50 8 ))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.seconds_relative, 50);
}
#[test]
fn test_invalid_condition_list_terminator_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((80 (50 8 ))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_condition_short_list_terminator() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((80 8 ))))").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_spend_list1() {
assert_eq!(
cond_test("(8 )").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_spend_list2() {
assert_eq!(
cond_test("((8 ))").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_invalid_spend_list_terminator() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (()) 8 ))").unwrap_err().1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_single_seconds_relative() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((80 (101 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.seconds_relative, 101);
}
#[test]
fn test_single_seconds_relative_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((80 (101 (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.seconds_relative, 101);
}
#[test]
fn test_single_seconds_relative_extra_arg_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((80 (101 (1337 )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_seconds_relative_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((80 (0x010000000000000000 )))))")
.unwrap_err()
.1,
ErrorCode::AssertSecondsRelative
);
}
#[test]
fn test_multiple_seconds_relative() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((80 (100 ) ((80 (503 ) ((80 (90 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.seconds_relative, 503);
}
#[test]
fn test_single_seconds_absolute() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((81 (104 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.seconds_absolute, 104);
}
#[test]
fn test_single_seconds_absolute_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((81 (104 ( 1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.seconds_absolute, 104);
}
#[test]
fn test_single_seconds_absolute_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((81 (104 ( 1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_seconds_absolute_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((81 (0x010000000000000000 )))))")
.unwrap_err()
.1,
ErrorCode::AssertSecondsAbsolute
);
}
#[test]
fn test_multiple_seconds_absolute() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((81 (100 ) ((81 (503 ) ((81 (90 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.seconds_absolute, 503);
}
#[test]
fn test_single_height_relative() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((82 (101 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.height_relative, Some(101));
}
#[test]
fn test_single_height_relative_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((82 (101 (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.height_relative, Some(101));
}
#[test]
fn test_single_height_relative_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((82 (101 (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_single_height_relative_zero() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((82 (0 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.height_relative, Some(0));
}
#[test]
fn test_height_relative_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((82 (0x0100000000 )))))")
.unwrap_err()
.1,
ErrorCode::AssertHeightRelative
);
}
#[test]
fn test_multiple_height_relative() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((82 (100 ) ((82 (503 ) ((82 (90 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.height_relative, Some(503));
}
#[test]
fn test_single_height_absolute() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((83 (100 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.height_absolute, 100);
}
#[test]
fn test_single_height_absolute_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((83 (100 (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.height_absolute, 100);
}
#[test]
fn test_single_height_absolute_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((83 (100 (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_height_absolute_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((83 (0x0100000000 )))))")
.unwrap_err()
.1,
ErrorCode::AssertHeightAbsolute
);
}
#[test]
fn test_multiple_height_absolute() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((83 (100 ) ((83 (503 ) ((83 (90 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.height_absolute, 503);
}
#[test]
fn test_single_reserve_fee() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((52 (100 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.reserve_fee, 100);
}
#[test]
fn test_single_reserve_fee_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((52 (100 (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.reserve_fee, 100);
}
#[test]
fn test_single_reserve_fee_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((52 (100 (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_reserve_fee_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((52 (0x00fffffffffffffff0 ) ((52 (0x10 ) ))))")
.unwrap_err()
.1,
ErrorCode::ReserveFeeConditionFailed
);
}
#[test]
fn test_multiple_reserve_fee() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((52 (100 ) ((52 (25 ) ((52 (50 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.reserve_fee, 175);
}
#[test]
fn test_coin_announces_consume() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c11} )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_create_coin_announce_extra_arg() {
let (a, conds) = cond_test_flag(
"((({h1} ({h2} (123 (((60 ({msg1} (1337 ) ((61 ({c11} )))))",
0,
)
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_create_coin_announce_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((60 ({msg1} (1337 ) ((61 ({c11} )))))",
STRICT_ARGS_COUNT,
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_assert_coin_announce_extra_arg() {
let (a, conds) = cond_test_flag(
"((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c11} (1337 )))))",
0,
)
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_assert_coin_announce_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c11} (1337 )))))",
STRICT_ARGS_COUNT,
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_cross_coin_announces_consume() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((60 ({msg1} ))) (({h2} ({h2} (123 (((61 ({c11} )))))")
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 2);
assert_eq!(*conds.spends[0].coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(conds.spends[0].puzzle_hash), H2);
assert_eq!(*conds.spends[1].coin_id, test_coin_id(H2, H2, 123));
assert_eq!(a.atom(conds.spends[1].puzzle_hash), H2);
}
#[test]
fn test_coin_announce_missing_arg() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((60 ) ((61 ({p21} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_failing_coin_consume() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((61 ({c11} )))))")
.unwrap_err()
.1,
ErrorCode::AssertCoinAnnouncementFailed
);
}
#[test]
fn test_coin_announce_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((60 ({msg1} ) ((61 ({c12} )))))")
.unwrap_err()
.1,
ErrorCode::AssertCoinAnnouncementFailed
);
}
#[test]
fn test_puzzle_announces_consume() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p21} )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_create_puzzle_announces_extra_arg() {
let (a, conds) = cond_test_flag(
"((({h1} ({h2} (123 (((62 ({msg1} (1337 ) ((63 ({p21} )))))",
0,
)
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_create_puzzle_announces_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((62 ({msg1} (1337 ) ((63 ({p21} )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_assert_puzzle_announces_extra_arg() {
let (a, conds) = cond_test_flag(
"((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p21} (1337 )))))",
0,
)
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_assert_puzzle_announces_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p21} (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_cross_coin_puzzle_announces_consume() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((62 ({msg1} ))) (({h2} ({h2} (123 (((63 ({p21} )))))")
.unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 2);
assert_eq!(*conds.spends[0].coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(conds.spends[0].puzzle_hash), H2);
assert_eq!(*conds.spends[1].coin_id, test_coin_id(H2, H2, 123));
assert_eq!(a.atom(conds.spends[1].puzzle_hash), H2);
}
#[test]
fn test_puzzle_announce_missing_arg() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((62 ) ((63 ({p21} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_failing_puzzle_consume() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((63 ({p21} )))))")
.unwrap_err()
.1,
ErrorCode::AssertPuzzleAnnouncementFailed
);
}
#[test]
fn test_puzzle_announce_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((62 ({msg1} ) ((63 ({p11} )))))")
.unwrap_err()
.1,
ErrorCode::AssertPuzzleAnnouncementFailed
);
}
#[test]
fn test_single_assert_my_amount() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((73 (123 )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_amount_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((73 (123 (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_amount_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((73 (123 (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_single_assert_my_amount_exceed_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((73 (0x010000000000000000 )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyAmountFailed
);
}
#[test]
fn test_single_assert_my_amount_overlong() {
assert_eq!(
cond_test_flag("((({h1} ({h2} (123 (((73 (0x0000007b )))))", 0)
.unwrap_err()
.1,
ErrorCode::AssertMyAmountFailed
);
}
#[test]
fn test_single_assert_my_amount_overlong_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((73 (0x0000007b )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyAmountFailed
);
}
#[test]
fn test_multiple_assert_my_amount() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((73 (123 ) ((73 (123 ) ))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_multiple_failing_assert_my_amount() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((73 (123 ) ((73 (122 ) ))))")
.unwrap_err()
.1,
ErrorCode::AssertMyAmountFailed
);
}
#[test]
fn test_single_failing_assert_my_amount() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((73 (124 ) ))))")
.unwrap_err()
.1,
ErrorCode::AssertMyAmountFailed
);
}
#[test]
fn test_single_assert_my_coin_id() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((70 ({coin12} )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_coin_id_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((70 ({coin12} (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_coin_id_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((70 ({coin12} (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_single_assert_my_coin_id_overlong() {
assert_eq!(
cond_test_flag("((({h1} ({h2} (0x0000007b (((70 ({coin12} )))))", 0)
.unwrap_err()
.1,
ErrorCode::InvalidCoinAmount
);
}
#[test]
fn test_multiple_assert_my_coin_id() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((70 ({coin12} ) ((70 ({coin12} ) ))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_coin_id_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((70 ({coin11} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyCoinIdFailed
);
}
#[test]
fn test_multiple_assert_my_coin_id_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((60 (123 ))) (({h1} ({h1} (123 (((70 ({coin12} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyCoinIdFailed
);
}
#[test]
fn test_single_assert_my_parent_coin_id() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((71 ({h1} )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_parent_coin_id_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((71 ({h1} (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_parent_coin_id_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((71 ({h1} (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_multiple_assert_my_parent_coin_id() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((71 ({h1} ) ((71 ({h1} ) ))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_parent_coin_id_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((71 ({h2} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyParentIdFailed
);
}
#[test]
fn test_single_invalid_assert_my_parent_coin_id() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((71 ({long} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyParentIdFailed
);
}
#[test]
fn test_single_assert_my_puzzle_hash() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((72 ({h2} )))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_puzzle_hash_extra_arg() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((72 ({h2} (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_puzzle_hash_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((72 ({h2} (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_multiple_assert_my_puzzle_hash() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((72 ({h2} ) ((72 ({h2} ) ))))").unwrap();
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
}
#[test]
fn test_single_assert_my_puzzle_hash_mismatch() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((72 ({h1} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyPuzzlehashFailed
);
}
#[test]
fn test_single_invalid_assert_my_puzzle_hash() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((72 ({long} )))))")
.unwrap_err()
.1,
ErrorCode::AssertMyPuzzlehashFailed
);
}
#[test]
fn test_single_create_coin() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_create_coin_max_amount() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((51 ({h2} (0x00ffffffffffffffff )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 0xffffffffffffffff_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_create_coin_amount_exceeds_max() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((51 ({h2} (0x010000000000000000 )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCoinAmount
);
}
#[test]
fn test_create_coin_negative_amount() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((51 ({h2} (-1 )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCoinAmount
);
}
#[test]
fn test_create_coin_invalid_puzzlehash() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((51 ({long} (42 )))))")
.unwrap_err()
.1,
ErrorCode::InvalidPuzzleHash
);
}
#[test]
fn test_create_coin_with_hint() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1}) )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert!(c.puzzle_hash == H2.into());
assert!(c.amount == 42_u64);
assert!(a.atom(c.hint) == H1.to_vec());
}
}
#[test]
fn test_create_coin_extra_arg() {
let (a, conds) =
cond_test_flag("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1}) (1337 )))))", 0).unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert!(c.puzzle_hash == H2.into());
assert!(c.amount == 42_u64);
assert!(a.atom(c.hint) == H1.to_vec());
}
}
#[test]
fn test_create_coin_extra_arg_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((51 ({h2} (42 (({h1}) (1337 )))))",
STRICT_ARGS_COUNT
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_create_coin_with_multiple_hints() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1} ({h2}) )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert!(c.puzzle_hash == H2.into());
assert!(c.amount == 42_u64);
assert!(a.atom(c.hint) == H1.to_vec());
}
}
#[test]
fn test_create_coin_with_hint_as_atom() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ({h1} )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_create_coin_with_invalid_hint_as_terminator() {
let (a, conds) = cond_test_flag("((({h1} ({h2} (123 (((51 ({h2} (42 {h1}))))", 0).unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_create_coin_with_invalid_hint_as_terminator_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 {h1}))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_create_coin_with_short_hint() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({msg1}))))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert!(c.puzzle_hash == H2.into());
assert!(c.amount == 42_u64);
assert!(a.atom(c.hint) == MSG1.to_vec());
}
}
#[test]
fn test_create_coin_with_long_hint() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ({long})))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_create_coin_with_pair_hint() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 (({h1} {h2} )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(a.atom(c.hint), H1.to_vec());
}
}
#[test]
fn test_create_coin_with_cons_hint() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ((({h1} {h2}) )))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 1);
for c in &spend.create_coin {
assert_eq!(c.puzzle_hash, H2.into());
assert_eq!(c.amount, 42_u64);
assert_eq!(c.hint, a.null());
}
}
#[test]
fn test_multiple_create_coin() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ) ((51 ({h2} (43 ) ))))").unwrap();
assert_eq!(conds.cost, CREATE_COIN_COST * 2);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.create_coin.len(), 2);
assert!(spend.create_coin.contains(&NewCoin {
puzzle_hash: H2.into(),
amount: 42_u64,
hint: a.null()
}));
assert!(spend.create_coin.contains(&NewCoin {
puzzle_hash: H2.into(),
amount: 43_u64,
hint: a.null()
}));
}
#[test]
fn test_create_coin_exceed_cost() {
assert_eq!(
cond_test_cb(
"((({h1} ({h2} (123 ({} )))",
0,
Some(|a: &mut Allocator| -> NodePtr {
let mut rest: NodePtr = a.null();
for i in 0..6500 {
let coin = a.null();
let val = a.new_atom(&u64_to_bytes(i)).unwrap();
let coin = a.new_pair(val, coin).unwrap();
let val = a.new_atom(H2).unwrap();
let coin = a.new_pair(val, coin).unwrap();
let val = a.new_atom(&u64_to_bytes(CREATE_COIN as u64)).unwrap();
let coin = a.new_pair(val, coin).unwrap();
rest = a.new_pair(coin, rest).unwrap();
}
rest
})
)
.unwrap_err()
.1,
ErrorCode::CostExceeded
);
}
#[test]
fn test_duplicate_create_coin() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((51 ({h2} (42 ) ((51 ({h2} (42 ) ))))")
.unwrap_err()
.1,
ErrorCode::DuplicateOutput
);
}
#[test]
fn test_single_agg_sig_me() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} )))))").unwrap();
assert_eq!(conds.cost, AGG_SIG_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.agg_sig_me.len(), 1);
for c in &spend.agg_sig_me {
assert_eq!(a.atom(c.0), PUBKEY);
assert_eq!(a.atom(c.1), MSG1);
}
}
#[test]
fn test_duplicate_agg_sig_me() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} ) ((50 ({pubkey} ({msg1} ) ))))")
.unwrap();
assert_eq!(conds.cost, AGG_SIG_COST * 2);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.agg_sig_me.len(), 2);
for c in &spend.agg_sig_me {
assert_eq!(a.atom(c.0), PUBKEY);
assert_eq!(a.atom(c.1), MSG1);
}
}
#[test]
fn test_agg_sig_me_invalid_pubkey() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((50 ({h2} ({msg1} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidPubkey
);
}
#[test]
fn test_agg_sig_me_invalid_msg() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((50 ({pubkey} ({longmsg} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidMessage
);
}
#[test]
fn test_agg_sig_me_exceed_cost() {
assert_eq!(
cond_test_cb(
"((({h1} ({h2} (123 ({} )))",
0,
Some(|a: &mut Allocator| -> NodePtr {
let mut rest: NodePtr = a.null();
for _i in 0..9167 {
let aggsig = a.null();
let val = a.new_atom(MSG1).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
let val = a.new_atom(PUBKEY).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
let val = a.new_atom(&u64_to_bytes(AGG_SIG_ME as u64)).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
rest = a.new_pair(aggsig, rest).unwrap();
}
rest
})
)
.unwrap_err()
.1,
ErrorCode::CostExceeded
);
}
#[test]
fn test_single_agg_sig_unsafe() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} )))))").unwrap();
assert_eq!(conds.cost, AGG_SIG_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.agg_sig_unsafe.len(), 1);
for (pk, msg) in &conds.agg_sig_unsafe {
assert_eq!(a.atom(*pk), PUBKEY);
assert_eq!(a.atom(*msg), MSG1);
}
}
#[test]
fn test_agg_sig_unsafe_extra_arg() {
assert_eq!(
cond_test_flag("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} (456 )))))", 0)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_agg_sig_unsafe_extra_arg_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} (456 )))))")
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_agg_sig_me_extra_arg() {
assert_eq!(
cond_test_flag("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} (456 )))))", 0)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_agg_sig_me_extra_arg_mempool() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} (456 )))))",)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_agg_sig_unsafe_invalid_terminator() {
let (a, conds) =
cond_test_flag("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} 456 ))))", 0).unwrap();
assert_eq!(conds.cost, AGG_SIG_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.agg_sig_unsafe.len(), 1);
for (pk, msg) in &conds.agg_sig_unsafe {
assert_eq!(a.atom(*pk), PUBKEY);
assert_eq!(a.atom(*msg), MSG1);
}
}
#[test]
fn test_agg_sig_unsafe_invalid_terminator_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} 456 ))))",
COND_ARGS_NIL
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_agg_sig_me_invalid_terminator() {
let (a, conds) =
cond_test_flag("((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} 456 ))))", 0).unwrap();
assert_eq!(conds.cost, AGG_SIG_COST);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.agg_sig_me.len(), 1);
for (pk, msg) in &conds.agg_sig_unsafe {
assert_eq!(a.atom(*pk), PUBKEY);
assert_eq!(a.atom(*msg), MSG1);
}
}
#[test]
fn test_agg_sig_me_invalid_terminator_mempool() {
assert_eq!(
cond_test_flag(
"((({h1} ({h2} (123 (((50 ({pubkey} ({msg1} 456 ))))",
COND_ARGS_NIL
)
.unwrap_err()
.1,
ErrorCode::InvalidCondition
);
}
#[test]
fn test_duplicate_agg_sig_unsafe() {
let (a, conds) =
cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({msg1} ) ((49 ({pubkey} ({msg1} ) ))))")
.unwrap();
assert_eq!(conds.cost, AGG_SIG_COST * 2);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(conds.agg_sig_unsafe.len(), 2);
for (pk, msg) in &conds.agg_sig_unsafe {
assert_eq!(a.atom(*pk), PUBKEY);
assert_eq!(a.atom(*msg), MSG1);
}
}
#[test]
fn test_agg_sig_unsafe_invalid_pubkey() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((49 ({h2} ({msg1} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidPubkey
);
}
#[test]
fn test_agg_sig_unsafe_invalid_msg() {
assert_eq!(
cond_test("((({h1} ({h2} (123 (((49 ({pubkey} ({longmsg} )))))")
.unwrap_err()
.1,
ErrorCode::InvalidMessage
);
}
#[test]
fn test_agg_sig_unsafe_exceed_cost() {
assert_eq!(
cond_test_cb(
"((({h1} ({h2} (123 ({} )))",
0,
Some(|a: &mut Allocator| -> NodePtr {
let mut rest: NodePtr = a.null();
for _i in 0..9167 {
let aggsig = a.null();
let val = a.new_atom(MSG1).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
let val = a.new_atom(PUBKEY).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
let val = a.new_atom(&u64_to_bytes(AGG_SIG_UNSAFE as u64)).unwrap();
let aggsig = a.new_pair(val, aggsig).unwrap();
rest = a.new_pair(aggsig, rest).unwrap();
}
rest
})
)
.unwrap_err()
.1,
ErrorCode::CostExceeded
);
}
#[test]
fn test_spend_amount_exceeds_max() {
assert_eq!(
cond_test("((({h1} ({h2} (0x010000000000000000 ())))")
.unwrap_err()
.1,
ErrorCode::InvalidCoinAmount
);
}
#[test]
fn test_single_spend_negative_amount() {
assert_eq!(
cond_test("((({h1} ({h2} (-123 ())))").unwrap_err().1,
ErrorCode::NegativeAmount
);
}
#[test]
fn test_single_spend_invalid_puzle_hash() {
assert_eq!(
cond_test("((({h1} ({long} (123 ())))").unwrap_err().1,
ErrorCode::InvalidPuzzleHash
);
}
#[test]
fn test_single_spend_invalid_parent_id() {
assert_eq!(
cond_test("((({long} ({h2} (123 ())))").unwrap_err().1,
ErrorCode::InvalidParentId
);
}
#[test]
fn test_double_spend() {
assert_eq!(
cond_test("((({h1} ({h2} (123 ()) (({h1} ({h2} (123 ())))")
.unwrap_err()
.1,
ErrorCode::DoubleSpend
);
}
#[test]
fn test_always_true() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((1 )))))").unwrap();
assert_eq!(conds.agg_sig_unsafe.len(), 0);
assert_eq!(conds.reserve_fee, 0);
assert_eq!(conds.height_absolute, 0);
assert_eq!(conds.seconds_absolute, 0);
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.agg_sig_me.len(), 0);
}
#[test]
fn test_always_true_with_arg() {
let (a, conds) = cond_test("((({h1} ({h2} (123 (((1 ( 42 )))))").unwrap();
assert_eq!(conds.agg_sig_unsafe.len(), 0);
assert_eq!(conds.reserve_fee, 0);
assert_eq!(conds.height_absolute, 0);
assert_eq!(conds.seconds_absolute, 0);
assert_eq!(conds.cost, 0);
assert_eq!(conds.spends.len(), 1);
let spend = &conds.spends[0];
assert_eq!(*spend.coin_id, test_coin_id(H1, H2, 123));
assert_eq!(a.atom(spend.puzzle_hash), H2);
assert_eq!(spend.agg_sig_me.len(), 0);
}