Skip to content
Merged
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
54 changes: 1 addition & 53 deletions crates/rbitcoin-esplora/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2001,8 +2001,7 @@ pub async fn post_tx_package(State(st): State<AppState>, body: Bytes) -> Respons

#[cfg(test)]
mod pure_helper_tests {
use super::{block_summary_json, fee_estimates_from, outspend_json, resolve_address_sh};
use bitcoin::Network;
use super::{block_summary_json, fee_estimates_from, outspend_json};
use rbitcoin_primitives::{Fk, Height};
use rbitcoin_query::testutil::FixtureChain;
use rbitcoin_query::{Query, TxApply};
Expand Down Expand Up @@ -2065,16 +2064,6 @@ mod pure_helper_tests {
assert_eq!(est["144"], json!(0.101));
}

#[test]
fn fee_routes_do_not_invent_an_estimate() {
// A target the estimator cannot answer is left out, as electrs does.
let est = fee_estimates_from(&[(1, -1.0), (6, 0.000_002)]).expect("one target");
assert!(est.get("1").is_none(), "{est}");
assert_eq!(est["6"], json!(0.2));
assert!(fee_estimates_from(&[(1, -1.0)]).is_none());
assert!(fee_estimates_from(&[]).is_none());
}

#[test]
fn block_summary_bits_u32_and_witness_size_weight() {
let (dir, q) = temp_query();
Expand Down Expand Up @@ -2141,45 +2130,4 @@ mod pure_helper_tests {
assert_eq!(v["vin"], 0, "outspend vin from spent slot: {v}");
let _ = std::fs::remove_dir_all(dir);
}

#[test]
fn resolve_address_sh_and_block_summary_surface() {
// Invalid / wrong-network addresses.
assert!(resolve_address_sh("not-an-address", Network::Bitcoin).is_err());
assert!(resolve_address_sh(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
Network::Regtest
)
.is_err());
// Well-known mainnet P2WPKH.
let sh = resolve_address_sh(
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
Network::Bitcoin,
)
.expect("mainnet p2wpkh");
assert_ne!(sh, [0u8; 32]);

let (dir, q) = temp_query();
let hash = seed_genesis(&q);
let summary = block_summary_json(&q, &hash).expect("genesis summary");
assert_eq!(summary["height"], 0);
assert_eq!(summary["tx_count"], 1);
assert!(summary["previousblockhash"].is_null());
assert!(summary["id"].as_str().unwrap().len() == 64);
assert!(summary["difficulty"].as_f64().unwrap() > 0.0);
assert!(summary["size"].as_u64().unwrap() > 80);
assert!(summary["weight"].as_u64().unwrap() > 0);
// Unknown hash → NotFound class error.
assert!(block_summary_json(&q, &[0x11; 32]).is_err());
let _ = q.sample_reset_reconstruct_archived();
let _ = block_summary_json(&q, &hash).expect("summary again");
assert_eq!(
q.sample_reset_reconstruct_archived(),
0,
"block JSON uses stamped size/weight"
);
assert!(q.reconstruct_archived_block(&hash).unwrap().is_some());
assert!(q.sample_reset_reconstruct_archived() >= 1);
let _ = std::fs::remove_dir_all(dir);
}
}
137 changes: 0 additions & 137 deletions crates/rbitcoin-esplora/src/script_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,140 +63,3 @@ fn is_bare_multisig(s: &Script) -> bool {
}
matches!(b[b.len() - 1], 0xae | 0xaf)
}

#[cfg(test)]
mod tests {
use super::*;
use bitcoin::hashes::Hash;
use bitcoin::key::PublicKey;
use bitcoin::opcodes::all::*;
use bitcoin::script::Builder;
use bitcoin::{PubkeyHash, ScriptHash, WPubkeyHash, XOnlyPublicKey};

/// secp256k1 G compressed / x-only (well-known generator).
const G_COMPRESSED: [u8; 33] = [
0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16,
0xf8, 0x17, 0x98,
];
const G_XONLY: [u8; 32] = [
0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b,
0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8,
0x17, 0x98,
];

#[test]
fn p2pkh_fields() {
let h160 = PubkeyHash::from_slice(&[0x11; 20]).unwrap();
let spk = Builder::new()
.push_opcode(OP_DUP)
.push_opcode(OP_HASH160)
.push_slice(h160)
.push_opcode(OP_EQUALVERIFY)
.push_opcode(OP_CHECKSIG)
.into_script();
let f = esplora_script_fields(spk.as_bytes(), Network::Bitcoin);
assert_eq!(f.script_type, "p2pkh");
assert!(f.asm.contains("OP_DUP"));
assert!(f.asm.contains("OP_HASH160"));
assert!(!f.hex.is_empty());
assert!(f.address.is_some());
}

#[test]
fn p2wpkh_fields() {
let w = WPubkeyHash::from_slice(&[0x22; 20]).unwrap();
let spk = Builder::new().push_int(0).push_slice(w).into_script();
let f = esplora_script_fields(spk.as_bytes(), Network::Bitcoin);
assert_eq!(f.script_type, "v0_p2wpkh");
assert!(f.address.as_ref().unwrap().starts_with("bc1q"));
}

#[test]
fn p2tr_fields() {
let xonly = XOnlyPublicKey::from_slice(&G_XONLY).unwrap();
let spk = Builder::new()
.push_int(1)
.push_x_only_key(&xonly)
.into_script();
let f = esplora_script_fields(spk.as_bytes(), Network::Bitcoin);
assert_eq!(f.script_type, "v1_p2tr");
assert!(f.address.as_ref().unwrap().starts_with("bc1p"));
}

#[test]
fn p2sh_p2wpkh_outer_is_p2sh() {
// Outer P2SH wrapping a v0 P2WPKH redeem script (type is still p2sh).
let w = WPubkeyHash::from_slice(&[0x33; 20]).unwrap();
let redeem = Builder::new().push_int(0).push_slice(w).into_script();
let sh = ScriptHash::hash(redeem.as_bytes());
let spk = Builder::new()
.push_opcode(OP_HASH160)
.push_slice(sh)
.push_opcode(OP_EQUAL)
.into_script();
let f = esplora_script_fields(spk.as_bytes(), Network::Bitcoin);
assert_eq!(f.script_type, "p2sh");
assert!(f.address.as_ref().unwrap().starts_with('3'));
}

#[test]
fn unknown_op_true() {
let f = esplora_script_fields(&[0x51], Network::Regtest);
assert_eq!(f.script_type, "unknown");
// rust-bitcoin names OP_1 as OP_PUSHNUM_1 in asm.
assert!(f.asm.contains("1") || f.asm.contains("OP_"));
assert!(f.address.is_none());
}

#[test]
fn p2pk_type() {
let pk = PublicKey::from_slice(&G_COMPRESSED).expect("G");
let spk = Builder::new()
.push_key(&pk)
.push_opcode(OP_CHECKSIG)
.into_script();
let f = esplora_script_fields(spk.as_bytes(), Network::Bitcoin);
assert_eq!(f.script_type, "p2pk");
assert!(f.asm.contains("OP_CHECKSIG"));
}

#[test]
fn p2wsh_op_return_and_bare_multisig_types() {
// v0 P2WSH: OP_0 + 32-byte push
let spk_wsh = {
let mut v = vec![0x00, 0x20];
v.extend_from_slice(&[0xab; 32]);
v
};
assert_eq!(
esplora_script_fields(&spk_wsh, Network::Bitcoin).script_type,
"v0_p2wsh"
);
// OP_RETURN
let opreturn = Builder::new()
.push_opcode(OP_RETURN)
.push_slice(b"hi")
.into_script();
assert_eq!(
esplora_script_fields(opreturn.as_bytes(), Network::Bitcoin).script_type,
"op_return"
);
// Bare multisig ends with CHECKMULTISIG
let multi = Builder::new()
.push_int(1)
.push_key(&PublicKey::from_slice(&G_COMPRESSED).unwrap())
.push_int(1)
.push_opcode(OP_CHECKMULTISIG)
.into_script();
assert_eq!(
esplora_script_fields(multi.as_bytes(), Network::Bitcoin).script_type,
"multisig"
);
// Short script is not bare multisig
assert_eq!(
esplora_script_fields(&[0x51, 0x51], Network::Regtest).script_type,
"unknown"
);
}
}
Loading
Loading