use bsv_rs::script::op::*;
use bsv_rs::script::{LockingScript, Script, ScriptResource, Spend, SpendParams, UnlockingScript};
use std::time::Instant;
fn spend(lock: Script, unlock: Script, memory_limit: usize) -> Spend {
Spend::new(SpendParams {
source_txid: [0x11; 32],
source_output_index: 0,
source_satoshis: 1_000,
locking_script: LockingScript::from_script(lock),
transaction_version: 1,
other_inputs: vec![],
outputs: vec![],
input_index: 0,
unlocking_script: UnlockingScript::from_script(unlock),
input_sequence: 0xffff_ffff,
lock_time: 0,
memory_limit: Some(memory_limit),
})
}
#[test]
fn num2bin_refuses_an_oversized_element_before_allocating_it() {
let mut lock = Script::new();
lock.write_opcode(OP_1)
.write_bin(&1_000_000_000i64.to_le_bytes()[..4])
.write_opcode(OP_NUM2BIN);
let mut s = spend(lock, Script::new(), 64 * 1024);
let t0 = Instant::now();
let err = s.validate().expect_err("a 1 GB element exceeds the budget");
assert!(
t0.elapsed().as_millis() < 500,
"refused before allocating: {:?}",
t0.elapsed()
);
let limit = err
.resource_limit
.expect("a resource limit, not a script verdict");
assert_eq!(limit.resource, ScriptResource::ElementSize);
assert_eq!(limit.limit, 64 * 1024);
assert_eq!(limit.attempted, 1_000_000_000);
assert!(err.is_resource_limit());
assert!(
err.message
.starts_with("Script element allocation has exceeded 65536 bytes"),
"the reference's label + shape: {}",
err.message
);
}
#[test]
fn num2bin_within_the_budget_still_pads() {
let mut lock = Script::new();
lock.write_opcode(OP_1)
.write_opcode(OP_16)
.write_opcode(OP_NUM2BIN)
.write_opcode(OP_SIZE)
.write_opcode(OP_NIP)
.write_opcode(OP_16)
.write_opcode(OP_EQUAL);
let mut s = spend(lock, Script::new(), 64 * 1024);
assert!(s.validate().expect("a 16-byte pad is inside the budget"));
}
#[test]
fn the_stack_budget_trips_as_a_resource_limit_not_a_script_verdict() {
let mut lock = Script::new();
for _ in 0..12 {
lock.write_opcode(OP_DUP).write_opcode(OP_CAT);
}
lock.write_opcode(OP_DROP).write_opcode(OP_TRUE);
let mut unlock = Script::new();
unlock.write_bin(&vec![0x42u8; 8 * 1024]);
let mut s = spend(lock, unlock, 128 * 1024);
let err = s.validate().expect_err("the stack budget trips");
let limit = err.resource_limit.expect("a resource limit");
assert_eq!(limit.resource, ScriptResource::Stack);
assert_eq!(limit.limit, 128 * 1024);
assert!(limit.attempted > limit.limit);
assert!(err
.message
.starts_with("Stack memory usage has exceeded 131072 bytes"));
}
#[test]
fn a_script_verdict_is_never_a_resource_limit() {
let mut lock = Script::new();
lock.write_opcode(OP_1)
.write_opcode(OP_2)
.write_opcode(OP_EQUAL);
let mut s = spend(lock, Script::new(), 32_000_000);
let err = s.validate().expect_err("a false script is refused");
assert!(
!err.is_resource_limit(),
"a verdict on the script: {}",
err.message
);
assert!(err.resource_limit.is_none());
}