use chia_protocol::{Bytes32, Coin, CoinSpend, Program};
use chia_sdk_types::conditions::{
AssertBeforeHeightAbsolute, AssertBeforeHeightRelative, AssertBeforeSecondsAbsolute,
AssertBeforeSecondsRelative, AssertEphemeral, AssertHeightAbsolute, AssertHeightRelative,
AssertSecondsAbsolute, AssertSecondsRelative, CreateCoin,
};
use chia_sdk_types::Condition;
use dig_block::{AssertionKind, ExecutionResult, PendingAssertion};
fn test_spend() -> (CoinSpend, Bytes32) {
let parent = Bytes32::new([0x11; 32]);
let puzzle_hash = Bytes32::new([0x22; 32]);
let coin = Coin::new(parent, puzzle_hash, 1);
let coin_id = coin.coin_id();
let spend = CoinSpend::new(coin, Program::from(vec![1]), Program::from(vec![0x80]));
(spend, coin_id)
}
#[test]
fn height_absolute_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> = Condition::AssertHeightAbsolute(AssertHeightAbsolute { height: 123 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::HeightAbsolute(123));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn height_relative_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> = Condition::AssertHeightRelative(AssertHeightRelative { height: 45 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::HeightRelative(45));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn seconds_absolute_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> = Condition::AssertSecondsAbsolute(AssertSecondsAbsolute {
seconds: 1_700_000_000,
});
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::SecondsAbsolute(1_700_000_000));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn seconds_relative_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> =
Condition::AssertSecondsRelative(AssertSecondsRelative { seconds: 60 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::SecondsRelative(60));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn before_height_absolute_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> =
Condition::AssertBeforeHeightAbsolute(AssertBeforeHeightAbsolute { height: 9000 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::BeforeHeightAbsolute(9000));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn before_height_relative_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> =
Condition::AssertBeforeHeightRelative(AssertBeforeHeightRelative { height: 10 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::BeforeHeightRelative(10));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn before_seconds_absolute_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> = Condition::AssertBeforeSecondsAbsolute(AssertBeforeSecondsAbsolute {
seconds: 2_000_000_000,
});
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::BeforeSecondsAbsolute(2_000_000_000));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn before_seconds_relative_variant() {
let (spend, coin_id) = test_spend();
let cond: Condition<()> =
Condition::AssertBeforeSecondsRelative(AssertBeforeSecondsRelative { seconds: 120 });
let p = PendingAssertion::from_condition(&cond, &spend).expect("must map");
assert_eq!(p.kind, AssertionKind::BeforeSecondsRelative(120));
assert_eq!(p.coin_id, coin_id);
}
#[test]
fn create_coin_returns_none() {
let (spend, _) = test_spend();
let cond: Condition<()> = Condition::CreateCoin(CreateCoin {
puzzle_hash: Bytes32::new([0xaa; 32]),
amount: 10,
memos: chia_sdk_types::conditions::Memos::Some(()),
});
assert!(PendingAssertion::from_condition(&cond, &spend).is_none());
}
#[test]
fn assert_ephemeral_returns_none() {
let (spend, _) = test_spend();
let cond: Condition<()> = Condition::AssertEphemeral(AssertEphemeral::default());
assert!(
PendingAssertion::from_condition(&cond, &spend).is_none(),
"ASSERT_EPHEMERAL is handled by EXE-004 separately, not via PendingAssertion"
);
}
#[test]
fn pending_assertion_bincode_roundtrip() {
let coin_id = Bytes32::new([0xaa; 32]);
let original = PendingAssertion {
kind: AssertionKind::HeightRelative(500),
coin_id,
};
let bytes = bincode::serialize(&original).expect("encode");
let decoded: PendingAssertion = bincode::deserialize(&bytes).expect("decode");
assert_eq!(original, decoded);
}
#[test]
fn assertion_kind_all_variants_roundtrip() {
let variants = [
AssertionKind::HeightAbsolute(1),
AssertionKind::HeightRelative(2),
AssertionKind::SecondsAbsolute(3),
AssertionKind::SecondsRelative(4),
AssertionKind::BeforeHeightAbsolute(5),
AssertionKind::BeforeHeightRelative(6),
AssertionKind::BeforeSecondsAbsolute(7),
AssertionKind::BeforeSecondsRelative(8),
];
for v in variants {
let bytes = bincode::serialize(&v).expect("encode");
let decoded: AssertionKind = bincode::deserialize(&bytes).expect("decode");
assert_eq!(v, decoded, "variant {:?} must round-trip", v);
}
}
#[test]
fn pending_assertion_fits_execution_result() {
let (spend, _) = test_spend();
let cond: Condition<()> = Condition::AssertHeightAbsolute(AssertHeightAbsolute { height: 1 });
let p = PendingAssertion::from_condition(&cond, &spend).unwrap();
let ex = ExecutionResult {
pending_assertions: vec![p.clone()],
..Default::default()
};
assert_eq!(ex.pending_assertions.len(), 1);
assert_eq!(ex.pending_assertions[0], p);
}