Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,42 @@ jobs:
(!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[ci skip]') &&
!contains(github.event.head_commit.message, '[no ci]'))
defaults:
run:
working-directory: blvm-spec-lock
steps:
- uses: actions/checkout@v6
with:
path: blvm-spec-lock

# production_lock tests read ../blvm-consensus, ../blvm-primitives and ../blvm-secp256k1 sources.
# Check them out at pinned commits so the locks always run against real code;
# bump these SHAs when the locks are re-checked against newer consensus.
- uses: actions/checkout@v6
with:
repository: BTCDecoded/blvm-consensus
ref: 994480ddb6d03a828b4c00654521986d7551fcaf
path: blvm-consensus

- uses: actions/checkout@v6
with:
repository: BTCDecoded/blvm-primitives
ref: 9720541b8897c383cbdd0f3f6334d4bcc0af9485
path: blvm-primitives

- uses: actions/checkout@v6
with:
repository: BTCDecoded/blvm-secp256k1
ref: 926f41588fce67f0d87cb5ae0b4ea62fc3f92d62
path: blvm-secp256k1

- name: Install Rust
uses: BTCDecoded/rust-ci/install-rust-toolchain@main

- name: Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: blvm-spec-lock

# Single job + no incremental: avoids flaky "could not parse/generate dep info" (.d missing)
# on self-hosted runners (NFS / high parallelism / cache races).
Expand Down
40 changes: 15 additions & 25 deletions src/translator/production_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ pub fn facts_of(func: &syn::ItemFn) -> ProductionFacts {
.cmps
.iter()
.all(|cmp| cmp.left.lit != Some(0x31) && cmp.right.lit != Some(0x31));
v.facts.merkle_eq_present = scanned.marks.iter().any(|mark| *mark == "merkle-eq");
v.facts.merkle_eq_present = scanned.marks.contains(&"merkle-eq");
v.facts.merkle_eq_before_pad = v.facts.merkle_eq_present
&& match (
scanned.marks.iter().position(|mark| *mark == "merkle-eq"),
Expand Down Expand Up @@ -1485,10 +1485,7 @@ pub fn op_success_query(src: &str) -> SatResult {

/// `vsize` is `ceil(weight / 4)`.
pub fn vsize_query(src: &str, weight: u64) -> SatResult {
let got = sample_eval::eval_u64(
src,
&[("weight", Val::Int(i128::try_from(weight).expect("weight")))],
);
let got = sample_eval::eval_u64(src, &[("weight", Val::Int(i128::from(weight)))]);
let spec = weight.div_ceil(4);
check(|ctx, solver| {
let left = BV::from_u64(ctx, got, 64);
Expand Down Expand Up @@ -2881,10 +2878,8 @@ fn has_nonempty_input_witness(src: &str, witnesses: Option<&[Vec<Vec<u8>>]>) ->
witnesses.iter().all(|w| input_not_witness_empty(src, w))
} else if src.contains(".any(") {
witnesses.iter().any(|w| input_not_witness_empty(src, w))
} else if src.contains("\n true\n") {
true
} else {
false
src.contains("\n true\n")
}
}

Expand All @@ -2908,9 +2903,7 @@ fn reserved_nonce(src: &str, witnesses: &[Vec<Vec<u8>>]) -> Option<[u8; 32]> {
if witnesses.is_empty() && src.contains("coinbase_witnesses.is_empty()") {
return Some([0u8; 32]);
}
let Some(stack) = witnesses.first() else {
return None;
};
let stack = witnesses.first()?;
if stack.is_empty() && src.contains("stack.is_empty()") {
return Some([0u8; 32]);
}
Expand Down Expand Up @@ -4270,7 +4263,7 @@ fn bip54_stripped(src: &str, tx: &Val, size: u64) -> bool {

/// Valid unless the transaction is not a coinbase and its witness-stripped size is 64.
fn spec_bip54_stripped(coinbase: bool, size: u64) -> bool {
!(!coinbase && size == 64)
coinbase || size != 64
}

pub fn bip54_stripped_query(src: &str, tx: &Val, coinbase: bool, size: u64) -> SatResult {
Expand Down Expand Up @@ -4309,11 +4302,8 @@ pub fn transaction_size_query(src: &str, wire: &[u8]) -> SatResult {
fn header_record(version: i128, prev: u8, merkle: u8, time: i128, bits: i128, nonce: i128) -> Val {
Val::Rec(vec![
("version".into(), Val::Int(version)),
(
"prev_block_hash".into(),
sample_eval::bytes(&vec![prev; 32]),
),
("merkle_root".into(), sample_eval::bytes(&vec![merkle; 32])),
("prev_block_hash".into(), sample_eval::bytes(&[prev; 32])),
("merkle_root".into(), sample_eval::bytes(&[merkle; 32])),
("timestamp".into(), Val::Int(time)),
("bits".into(), Val::Int(bits)),
("nonce".into(), Val::Int(nonce)),
Expand Down Expand Up @@ -6606,22 +6596,22 @@ mod tests {
let key_src = extract_fn(&file, "extract_taproot_output_key");
let valid = {
let mut script = vec![0x51, 0x20];
script.extend(std::iter::repeat(0x11).take(32));
script.extend(std::iter::repeat_n(0x11, 32));
script
};
let short = {
let mut script = vec![0x51, 0x20];
script.extend(std::iter::repeat(0x11).take(31));
script.extend(std::iter::repeat_n(0x11, 31));
script
};
let bad_prefix = {
let mut script = vec![0x00, 0x20];
script.extend(std::iter::repeat(0x11).take(32));
script.extend(std::iter::repeat_n(0x11, 32));
script
};
let bad_push = {
let mut script = vec![0x51, 0x00];
script.extend(std::iter::repeat(0x11).take(32));
script.extend(std::iter::repeat_n(0x11, 32));
script
};
for script in [&valid, &short, &bad_prefix, &bad_push] {
Expand Down Expand Up @@ -6689,12 +6679,12 @@ mod tests {
);
let p2wpkh = {
let mut program = vec![0x00, 0x14];
program.extend(std::iter::repeat(0x11).take(20));
program.extend(std::iter::repeat_n(0x11, 20));
program
};
let p2wsh = {
let mut program = vec![0x00, 0x20];
program.extend(std::iter::repeat(0x22).take(32));
program.extend(std::iter::repeat_n(0x22, 32));
program
};
let witness = [0x51u8];
Expand Down Expand Up @@ -7038,7 +7028,7 @@ mod tests {
let push = extract_fn(&file, "sigop_count_push_opcode");
let valid = {
let mut script = vec![0xa9, 0x14];
script.extend(std::iter::repeat(0x11).take(20));
script.extend(std::iter::repeat_n(0x11, 20));
script.push(0x87);
script
};
Expand Down Expand Up @@ -7947,7 +7937,7 @@ mod tests {
redeem_push_query(&src, &trailer, &redeem22),
SatResult::Unsat
);
let wrong = direct(&vec![0u8; 22]);
let wrong = direct(&[0u8; 22]);
assert_eq!(redeem_push_query(&src, &wrong, &redeem22), SatResult::Unsat);
let mut bad_opcode = redeem22.clone();
bad_opcode.insert(0, 0x4c);
Expand Down
8 changes: 3 additions & 5 deletions src/translator/sample_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1008,7 +1008,7 @@ fn call_func(
.next()
.cloned()
.unwrap_or_else(|| panic!("sample eval missing argument {name}"));
ctx.bind(&name, value);
ctx.bind(name, value);
}
let flow = eval_block(&func.block, ctx);
let refs = func
Expand All @@ -1024,7 +1024,7 @@ fn call_func(
}
let name = pat_ident(&typed.pat);
Some(
ctx.get(&name)
ctx.get(name)
.unwrap_or_else(|| panic!("missing mut {name}")),
)
})
Expand Down Expand Up @@ -1491,9 +1491,7 @@ fn u256_shift(bytes: &[u8], shift: i128, left: bool) -> Vec<u8> {
}
}
} else if bit_shift == 0 {
for index in word_shift..4 {
result[index - word_shift] = limbs[index];
}
result[..(4 - word_shift)].copy_from_slice(&limbs[word_shift..4]);
} else {
for index in word_shift..4 {
let mut word = limbs[index] >> bit_shift;
Expand Down
Loading