use crate::error::{Error, Result};
use crate::primitives::encoding::{bounded_capacity, Reader, Writer};
use crate::primitives::hash::sha256d;
use std::collections::HashMap;
pub const SIGHASH_ALL: u32 = 0x00000001;
pub const SIGHASH_NONE: u32 = 0x00000002;
pub const SIGHASH_SINGLE: u32 = 0x00000003;
pub const SIGHASH_FORKID: u32 = 0x00000040;
pub const SIGHASH_ANYONECANPAY: u32 = 0x00000080;
pub const SIGHASH_CHRONICLE: u32 = 0x00000020;
const SIGHASH_BASE_MASK: u32 = 0x1F;
#[derive(Debug, Clone)]
pub struct TxInput {
pub txid: [u8; 32],
pub output_index: u32,
pub script: Vec<u8>,
pub sequence: u32,
}
#[derive(Debug, Clone)]
pub struct TxOutput {
pub satoshis: u64,
pub script: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct RawTransaction {
pub version: i32,
pub inputs: Vec<TxInput>,
pub outputs: Vec<TxOutput>,
pub locktime: u32,
}
pub fn parse_transaction(raw: &[u8]) -> Result<RawTransaction> {
let mut reader = Reader::new(raw);
let version = reader.read_i32_le()?;
let input_count = reader.read_var_int_num()?;
let mut inputs = Vec::with_capacity(bounded_capacity(input_count, reader.remaining(), 41));
for _ in 0..input_count {
let txid_bytes = reader.read_bytes(32)?;
let mut txid = [0u8; 32];
txid.copy_from_slice(txid_bytes);
let output_index = reader.read_u32_le()?;
let script = reader.read_var_bytes()?.to_vec();
let sequence = reader.read_u32_le()?;
inputs.push(TxInput {
txid,
output_index,
script,
sequence,
});
}
let output_count = reader.read_var_int_num()?;
let mut outputs = Vec::with_capacity(bounded_capacity(output_count, reader.remaining(), 9));
for _ in 0..output_count {
let satoshis = reader.read_u64_le()?;
let script = reader.read_var_bytes()?.to_vec();
outputs.push(TxOutput { satoshis, script });
}
let locktime = reader.read_u32_le()?;
Ok(RawTransaction {
version,
inputs,
outputs,
locktime,
})
}
#[derive(Debug)]
pub struct SighashParams<'a> {
pub version: i32,
pub inputs: &'a [TxInput],
pub outputs: &'a [TxOutput],
pub locktime: u32,
pub input_index: usize,
pub subscript: &'a [u8],
pub satoshis: u64,
pub scope: u32,
}
fn compute_hash_prevouts(inputs: &[TxInput], scope: u32) -> [u8; 32] {
if (scope & SIGHASH_ANYONECANPAY) != 0 {
return [0u8; 32];
}
let mut writer = Writer::new();
for input in inputs {
writer.write_bytes(&input.txid);
writer.write_u32_le(input.output_index);
}
sha256d(writer.as_bytes())
}
fn compute_hash_sequence(inputs: &[TxInput], scope: u32) -> [u8; 32] {
let base_type = scope & SIGHASH_BASE_MASK;
if (scope & SIGHASH_ANYONECANPAY) != 0
|| base_type == SIGHASH_SINGLE
|| base_type == SIGHASH_NONE
{
return [0u8; 32];
}
let mut writer = Writer::new();
for input in inputs {
writer.write_u32_le(input.sequence);
}
sha256d(writer.as_bytes())
}
fn compute_hash_outputs(outputs: &[TxOutput], input_index: usize, scope: u32) -> [u8; 32] {
let base_type = scope & SIGHASH_BASE_MASK;
if base_type != SIGHASH_SINGLE && base_type != SIGHASH_NONE {
let mut writer = Writer::new();
for output in outputs {
writer.write_u64_le(output.satoshis);
writer.write_var_int(output.script.len() as u64);
writer.write_bytes(&output.script);
}
sha256d(writer.as_bytes())
} else if base_type == SIGHASH_SINGLE && input_index < outputs.len() {
let output = &outputs[input_index];
let mut writer = Writer::new();
writer.write_u64_le(output.satoshis);
writer.write_var_int(output.script.len() as u64);
writer.write_bytes(&output.script);
sha256d(writer.as_bytes())
} else {
[0u8; 32]
}
}
pub fn build_sighash_preimage(params: &SighashParams) -> Vec<u8> {
let mut cache = SighashCache::from_parts(
params.version,
params.inputs,
params.outputs,
params.locktime,
);
match cache.preimage(
params.input_index,
params.subscript,
params.satoshis,
params.scope,
) {
Ok(preimage) => preimage,
Err(_) => panic!(
"input index {} out of range (transaction has {} inputs)",
params.input_index,
params.inputs.len()
),
}
}
#[derive(Debug, Clone)]
pub struct SighashCache<'t> {
version: i32,
inputs: &'t [TxInput],
outputs: &'t [TxOutput],
locktime: u32,
hash_prevouts: Option<[u8; 32]>,
hash_sequence: Option<[u8; 32]>,
hash_outputs_all: Option<[u8; 32]>,
hash_outputs_single: HashMap<usize, [u8; 32]>,
}
impl<'t> SighashCache<'t> {
pub fn new(tx: &'t RawTransaction) -> Self {
Self::from_parts(tx.version, &tx.inputs, &tx.outputs, tx.locktime)
}
pub fn from_parts(
version: i32,
inputs: &'t [TxInput],
outputs: &'t [TxOutput],
locktime: u32,
) -> Self {
Self {
version,
inputs,
outputs,
locktime,
hash_prevouts: None,
hash_sequence: None,
hash_outputs_all: None,
hash_outputs_single: HashMap::new(),
}
}
pub fn hash_prevouts(&mut self, scope: u32) -> [u8; 32] {
if (scope & SIGHASH_ANYONECANPAY) != 0 {
return [0u8; 32];
}
let inputs = self.inputs;
*self
.hash_prevouts
.get_or_insert_with(|| compute_hash_prevouts(inputs, scope))
}
pub fn hash_sequence(&mut self, scope: u32) -> [u8; 32] {
let base_type = scope & SIGHASH_BASE_MASK;
if (scope & SIGHASH_ANYONECANPAY) != 0
|| base_type == SIGHASH_SINGLE
|| base_type == SIGHASH_NONE
{
return [0u8; 32];
}
let inputs = self.inputs;
*self
.hash_sequence
.get_or_insert_with(|| compute_hash_sequence(inputs, scope))
}
pub fn hash_outputs(&mut self, input_index: usize, scope: u32) -> [u8; 32] {
let base_type = scope & SIGHASH_BASE_MASK;
let outputs = self.outputs;
if base_type != SIGHASH_SINGLE && base_type != SIGHASH_NONE {
*self
.hash_outputs_all
.get_or_insert_with(|| compute_hash_outputs(outputs, input_index, scope))
} else if base_type == SIGHASH_SINGLE && input_index < outputs.len() {
*self
.hash_outputs_single
.entry(input_index)
.or_insert_with(|| compute_hash_outputs(outputs, input_index, scope))
} else {
[0u8; 32]
}
}
pub fn preimage(
&mut self,
input_index: usize,
subscript: &[u8],
satoshis: u64,
scope: u32,
) -> Result<Vec<u8>> {
let inputs = self.inputs;
let input = inputs.get(input_index).ok_or_else(|| {
Error::CryptoError(format!(
"Input index {} out of range (transaction has {} inputs)",
input_index,
inputs.len()
))
})?;
let hash_prevouts = self.hash_prevouts(scope);
let hash_sequence = self.hash_sequence(scope);
let hash_outputs = self.hash_outputs(input_index, scope);
let mut writer = Writer::with_capacity(
4 + 32 + 32 + 32 + 4 + 9 + subscript.len() + 8 + 4 + 32 + 4 + 4, );
writer.write_i32_le(self.version);
writer.write_bytes(&hash_prevouts);
writer.write_bytes(&hash_sequence);
writer.write_bytes(&input.txid);
writer.write_u32_le(input.output_index);
writer.write_var_int(subscript.len() as u64);
writer.write_bytes(subscript);
writer.write_u64_le(satoshis);
writer.write_u32_le(input.sequence);
writer.write_bytes(&hash_outputs);
writer.write_u32_le(self.locktime);
writer.write_u32_le(scope);
Ok(writer.into_bytes())
}
pub fn sighash(
&mut self,
input_index: usize,
subscript: &[u8],
satoshis: u64,
scope: u32,
) -> Result<[u8; 32]> {
let preimage = self.preimage(input_index, subscript, satoshis, scope)?;
let mut hash = sha256d(&preimage);
hash.reverse();
Ok(hash)
}
pub fn sighash_for_signing(
&mut self,
input_index: usize,
subscript: &[u8],
satoshis: u64,
scope: u32,
) -> Result<[u8; 32]> {
let preimage = self.preimage(input_index, subscript, satoshis, scope)?;
Ok(sha256d(&preimage))
}
}
pub fn compute_sighash(params: &SighashParams) -> [u8; 32] {
let preimage = build_sighash_preimage(params);
let mut hash = sha256d(&preimage);
hash.reverse();
hash
}
pub fn compute_sighash_for_signing(params: &SighashParams) -> [u8; 32] {
let preimage = build_sighash_preimage(params);
sha256d(&preimage)
}
const SIGHASH_ONE: [u8; 32] = {
let mut one = [0u8; 32];
one[0] = 1;
one
};
pub fn compute_sighash_dispatched_for_signing(params: &SighashParams) -> [u8; 32] {
if (params.scope & SIGHASH_FORKID) != 0 && (params.scope & SIGHASH_CHRONICLE) == 0 {
compute_sighash_for_signing(params)
} else {
compute_sighash_original_for_signing(params)
}
}
pub fn compute_sighash_original_for_signing(params: &SighashParams) -> [u8; 32] {
let base = params.scope & SIGHASH_BASE_MASK;
if params.input_index >= params.inputs.len() {
return SIGHASH_ONE;
}
if base == SIGHASH_SINGLE && params.input_index >= params.outputs.len() {
return SIGHASH_ONE;
}
sha256d(&build_sighash_preimage_original(params))
}
pub fn build_sighash_preimage_original(params: &SighashParams) -> Vec<u8> {
let scope = params.scope;
let base = scope & SIGHASH_BASE_MASK;
let anyone_can_pay = (scope & SIGHASH_ANYONECANPAY) != 0;
let n_in = params.input_index;
assert!(
n_in < params.inputs.len(),
"input index {n_in} out of range"
);
let mut w = Writer::new();
w.write_i32_le(params.version);
let n_inputs = if anyone_can_pay {
1
} else {
params.inputs.len()
};
w.write_var_int(n_inputs as u64);
for i in 0..n_inputs {
let idx = if anyone_can_pay { n_in } else { i };
let input = ¶ms.inputs[idx];
w.write_bytes(&input.txid);
w.write_u32_le(input.output_index);
if idx == n_in {
write_script_code_original(&mut w, params.subscript);
} else {
w.write_var_int(0);
}
if idx != n_in && (base == SIGHASH_SINGLE || base == SIGHASH_NONE) {
w.write_u32_le(0);
} else {
w.write_u32_le(input.sequence);
}
}
let n_outputs = if base == SIGHASH_NONE {
0
} else if base == SIGHASH_SINGLE {
n_in + 1
} else {
params.outputs.len()
};
w.write_var_int(n_outputs as u64);
for o in 0..n_outputs {
if base == SIGHASH_SINGLE && o != n_in {
w.write_u64_le(u64::MAX);
w.write_var_int(0);
} else {
let output = ¶ms.outputs[o];
w.write_u64_le(output.satoshis);
w.write_var_int(output.script.len() as u64);
w.write_bytes(&output.script);
}
}
w.write_u32_le(params.locktime);
w.write_u32_le(scope);
w.into_bytes()
}
fn write_script_code_original(w: &mut Writer, script_code: &[u8]) {
const OP_CODESEPARATOR: u8 = 0xab;
let mut separators = 0usize;
let mut pc = 0usize;
while let (Some(op), next) = get_op(script_code, pc) {
if op == OP_CODESEPARATOR {
separators += 1;
}
pc = next;
}
w.write_var_int((script_code.len() - separators) as u64);
let mut begin = 0usize;
let mut pc = 0usize;
loop {
let (op, next) = get_op(script_code, pc);
match op {
Some(OP_CODESEPARATOR) => {
w.write_bytes(&script_code[begin..next - 1]);
begin = next;
pc = next;
}
Some(_) => pc = next,
None => {
pc = next;
break;
}
}
}
if begin != script_code.len() {
w.write_bytes(&script_code[begin..pc]);
}
}
fn get_op(bytes: &[u8], mut pc: usize) -> (Option<u8>, usize) {
const OP_PUSHDATA1: u8 = 0x4c;
const OP_PUSHDATA2: u8 = 0x4d;
const OP_PUSHDATA4: u8 = 0x4e;
if pc >= bytes.len() {
return (None, pc);
}
let opcode = bytes[pc];
pc += 1;
if opcode <= OP_PUSHDATA4 {
let size: usize = if opcode < OP_PUSHDATA1 {
opcode as usize
} else if opcode == OP_PUSHDATA1 {
if bytes.len() - pc < 1 {
return (None, pc);
}
let n = bytes[pc] as usize;
pc += 1;
n
} else if opcode == OP_PUSHDATA2 {
if bytes.len() - pc < 2 {
return (None, pc);
}
let n = u16::from_le_bytes([bytes[pc], bytes[pc + 1]]) as usize;
pc += 2;
n
} else {
if bytes.len() - pc < 4 {
return (None, pc);
}
let n = u32::from_le_bytes([bytes[pc], bytes[pc + 1], bytes[pc + 2], bytes[pc + 3]])
as usize;
pc += 4;
n
};
if bytes.len() - pc < size {
return (None, pc);
}
pc += size;
}
(Some(opcode), pc)
}
pub fn compute_sighash_from_raw(
raw_tx: &[u8],
input_index: usize,
subscript: &[u8],
satoshis: u64,
scope: u32,
) -> Result<[u8; 32]> {
let tx = parse_transaction(raw_tx)?;
if input_index >= tx.inputs.len() {
return Err(Error::CryptoError(format!(
"Input index {} out of range (transaction has {} inputs)",
input_index,
tx.inputs.len()
)));
}
Ok(compute_sighash(&SighashParams {
version: tx.version,
inputs: &tx.inputs,
outputs: &tx.outputs,
locktime: tx.locktime,
input_index,
subscript,
satoshis,
scope,
}))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_transaction() {
let raw = hex::decode(
"01000000\
01\
0000000000000000000000000000000000000000000000000000000000000000\
ffffffff\
00\
ffffffff\
01\
0000000000000000\
00\
00000000",
)
.unwrap();
let tx = parse_transaction(&raw).unwrap();
assert_eq!(tx.version, 1);
assert_eq!(tx.inputs.len(), 1);
assert_eq!(tx.outputs.len(), 1);
assert_eq!(tx.locktime, 0);
assert_eq!(tx.inputs[0].output_index, 0xffffffff); assert_eq!(tx.inputs[0].sequence, 0xffffffff);
}
#[test]
fn test_sighash_constants() {
assert_eq!(SIGHASH_ALL, 0x01);
assert_eq!(SIGHASH_NONE, 0x02);
assert_eq!(SIGHASH_SINGLE, 0x03);
assert_eq!(SIGHASH_FORKID, 0x40);
assert_eq!(SIGHASH_ANYONECANPAY, 0x80);
}
#[test]
fn test_base_type_extraction() {
assert_eq!(SIGHASH_ALL & SIGHASH_BASE_MASK, SIGHASH_ALL);
assert_eq!(
(SIGHASH_ALL | SIGHASH_FORKID) & SIGHASH_BASE_MASK,
SIGHASH_ALL
);
assert_eq!(
(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY | SIGHASH_FORKID) & SIGHASH_BASE_MASK,
SIGHASH_SINGLE
);
}
#[test]
fn test_hash_prevouts_anyonecanpay() {
let inputs = vec![TxInput {
txid: [1u8; 32],
output_index: 0,
script: vec![],
sequence: 0xffffffff,
}];
let hash = compute_hash_prevouts(&inputs, SIGHASH_ALL | SIGHASH_ANYONECANPAY);
assert_eq!(hash, [0u8; 32]);
let hash = compute_hash_prevouts(&inputs, SIGHASH_ALL);
assert_ne!(hash, [0u8; 32]);
}
#[test]
fn test_hash_sequence_conditions() {
let inputs = vec![TxInput {
txid: [1u8; 32],
output_index: 0,
script: vec![],
sequence: 0xffffffff,
}];
let hash = compute_hash_sequence(&inputs, SIGHASH_ALL | SIGHASH_ANYONECANPAY);
assert_eq!(hash, [0u8; 32]);
let hash = compute_hash_sequence(&inputs, SIGHASH_SINGLE);
assert_eq!(hash, [0u8; 32]);
let hash = compute_hash_sequence(&inputs, SIGHASH_NONE);
assert_eq!(hash, [0u8; 32]);
let hash = compute_hash_sequence(&inputs, SIGHASH_ALL);
assert_ne!(hash, [0u8; 32]);
}
#[test]
fn test_hash_outputs_all() {
let outputs = vec![
TxOutput {
satoshis: 1000,
script: vec![0x76, 0xa9],
},
TxOutput {
satoshis: 2000,
script: vec![0x76, 0xa9, 0x14],
},
];
let hash = compute_hash_outputs(&outputs, 0, SIGHASH_ALL);
assert_ne!(hash, [0u8; 32]);
}
#[test]
fn test_hash_outputs_single() {
let outputs = vec![
TxOutput {
satoshis: 1000,
script: vec![0x76, 0xa9],
},
TxOutput {
satoshis: 2000,
script: vec![0x76, 0xa9, 0x14],
},
];
let hash = compute_hash_outputs(&outputs, 0, SIGHASH_SINGLE);
assert_ne!(hash, [0u8; 32]);
let hash = compute_hash_outputs(&outputs, 5, SIGHASH_SINGLE);
assert_eq!(hash, [0u8; 32]);
}
#[test]
fn test_hash_outputs_none() {
let outputs = vec![TxOutput {
satoshis: 1000,
script: vec![0x76, 0xa9],
}];
let hash = compute_hash_outputs(&outputs, 0, SIGHASH_NONE);
assert_eq!(hash, [0u8; 32]);
}
fn cache_test_tx(n_in: usize, n_out: usize) -> RawTransaction {
let inputs = (0..n_in)
.map(|i| {
let mut txid = [0u8; 32];
txid[0] = i as u8;
txid[31] = 0xaa;
TxInput {
txid,
output_index: i as u32,
script: vec![],
sequence: 0xffff_ffff - i as u32,
}
})
.collect();
let outputs = (0..n_out)
.map(|i| {
let mut script = vec![0x76, 0xa9, 0x14];
script.extend((0..20).map(|j| (i + j) as u8));
script.extend([0x88, 0xac]);
TxOutput {
satoshis: 1000 + i as u64,
script,
}
})
.collect();
RawTransaction {
version: 1,
inputs,
outputs,
locktime: 17,
}
}
const CACHE_TEST_LOCK: [u8; 25] = [
0x76, 0xa9, 0x14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0x88, 0xac,
];
#[test]
fn sighash_cache_preimage_equals_free_functions_all_scope_classes() {
let tx = cache_test_tx(7, 3);
let scopes = [
SIGHASH_ALL | SIGHASH_FORKID,
SIGHASH_NONE | SIGHASH_FORKID,
SIGHASH_SINGLE | SIGHASH_FORKID,
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
0x04 | SIGHASH_FORKID,
0x1f | SIGHASH_FORKID,
];
for &scope in &scopes {
let mut cache = SighashCache::new(&tx);
for i in 0..tx.inputs.len() {
let params = SighashParams {
version: tx.version,
inputs: &tx.inputs,
outputs: &tx.outputs,
locktime: tx.locktime,
input_index: i,
subscript: &CACHE_TEST_LOCK,
satoshis: 500 + i as u64,
scope,
};
let cached = cache
.preimage(i, &CACHE_TEST_LOCK, 500 + i as u64, scope)
.unwrap();
assert_eq!(
cached,
build_sighash_preimage(¶ms),
"preimage: scope {scope:#x} input {i}"
);
assert_eq!(
cache
.sighash(i, &CACHE_TEST_LOCK, 500 + i as u64, scope)
.unwrap(),
compute_sighash(¶ms),
"sighash (display order): scope {scope:#x} input {i}"
);
assert_eq!(
cache
.sighash_for_signing(i, &CACHE_TEST_LOCK, 500 + i as u64, scope)
.unwrap(),
compute_sighash_for_signing(¶ms),
"sighash (signing order): scope {scope:#x} input {i}"
);
}
}
}
#[test]
fn sighash_cache_mixed_scopes_on_one_cache() {
let tx = cache_test_tx(6, 2);
let scopes = [
SIGHASH_ALL | SIGHASH_FORKID,
SIGHASH_SINGLE | SIGHASH_FORKID,
SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
SIGHASH_ALL | SIGHASH_FORKID,
SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
SIGHASH_NONE | SIGHASH_FORKID,
];
let mut cache = SighashCache::new(&tx);
for (i, &scope) in scopes.iter().enumerate() {
let cached = cache.preimage(i, &CACHE_TEST_LOCK, 42, scope).unwrap();
let fresh = build_sighash_preimage(&SighashParams {
version: tx.version,
inputs: &tx.inputs,
outputs: &tx.outputs,
locktime: tx.locktime,
input_index: i,
subscript: &CACHE_TEST_LOCK,
satoshis: 42,
scope,
});
assert_eq!(cached, fresh, "input {i} scope {scope:#x}");
}
}
#[test]
fn sighash_cache_single_out_of_range_zeros() {
let tx = cache_test_tx(5, 2);
let scope = SIGHASH_SINGLE | SIGHASH_FORKID;
let mut cache = SighashCache::new(&tx);
for i in 2..5 {
let cached = cache.preimage(i, &CACHE_TEST_LOCK, 42, scope).unwrap();
let fresh = build_sighash_preimage(&SighashParams {
version: tx.version,
inputs: &tx.inputs,
outputs: &tx.outputs,
locktime: tx.locktime,
input_index: i,
subscript: &CACHE_TEST_LOCK,
satoshis: 42,
scope,
});
assert_eq!(cached, fresh, "input {i}");
assert_eq!(&cached[cached.len() - 40..cached.len() - 8], &[0u8; 32]);
}
}
#[test]
fn sighash_cache_from_parts_equals_new() {
let tx = cache_test_tx(3, 3);
let scope = SIGHASH_ALL | SIGHASH_FORKID;
let mut a = SighashCache::new(&tx);
let mut b = SighashCache::from_parts(tx.version, &tx.inputs, &tx.outputs, tx.locktime);
for i in 0..3 {
assert_eq!(
a.preimage(i, &CACHE_TEST_LOCK, 7, scope).unwrap(),
b.preimage(i, &CACHE_TEST_LOCK, 7, scope).unwrap(),
);
}
}
#[test]
fn sighash_cache_input_index_out_of_range_errors() {
let tx = cache_test_tx(2, 1);
let mut cache = SighashCache::new(&tx);
let scope = SIGHASH_ALL | SIGHASH_FORKID;
assert!(cache.preimage(2, &CACHE_TEST_LOCK, 1, scope).is_err());
assert!(cache.sighash(9, &CACHE_TEST_LOCK, 1, scope).is_err());
assert!(cache
.sighash_for_signing(2, &CACHE_TEST_LOCK, 1, scope)
.is_err());
assert!(cache.preimage(1, &CACHE_TEST_LOCK, 1, scope).is_ok());
}
#[test]
fn sighash_cache_midstates_computed_once() {
let tx = cache_test_tx(4, 2);
let mut cache = SighashCache::new(&tx);
cache
.preimage(
0,
&CACHE_TEST_LOCK,
1,
SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY,
)
.unwrap();
assert!(cache.hash_prevouts.is_none());
assert!(cache.hash_sequence.is_none());
cache
.preimage(0, &CACHE_TEST_LOCK, 1, SIGHASH_ALL | SIGHASH_FORKID)
.unwrap();
let prevouts = cache.hash_prevouts;
let sequence = cache.hash_sequence;
let outputs_all = cache.hash_outputs_all;
assert!(prevouts.is_some() && sequence.is_some() && outputs_all.is_some());
cache
.preimage(3, &CACHE_TEST_LOCK, 1, SIGHASH_ALL | SIGHASH_FORKID)
.unwrap();
assert_eq!(cache.hash_prevouts, prevouts);
assert_eq!(cache.hash_sequence, sequence);
assert_eq!(cache.hash_outputs_all, outputs_all);
cache
.preimage(1, &CACHE_TEST_LOCK, 1, SIGHASH_SINGLE | SIGHASH_FORKID)
.unwrap();
assert!(cache.hash_outputs_single.contains_key(&1));
cache
.preimage(3, &CACHE_TEST_LOCK, 1, SIGHASH_SINGLE | SIGHASH_FORKID)
.unwrap();
assert!(!cache.hash_outputs_single.contains_key(&3));
}
}