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
39 changes: 39 additions & 0 deletions libstackerdb/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ use stacks_common::util::secp256k1::MessageSignature;

use crate::*;

#[test]
fn test_stackerdb_chunk_data_consensus_codec_round_trip() {
let original = StackerDBChunkData {
slot_id: 0x0a0b0c0d,
slot_version: 0x11223344,
sig: MessageSignature([0xaa; 65]),
data: vec![0xde, 0xad, 0xbe, 0xef, 0x00, 0xff],
};
let bytes = original.serialize_to_vec();
// Empty / no-op serialize would leave bytes empty; the underflow check below traps that.
assert!(!bytes.is_empty(), "serialize must write bytes");
let decoded = StackerDBChunkData::consensus_deserialize(&mut &bytes[..]).unwrap();
assert_eq!(decoded.slot_id, original.slot_id);
assert_eq!(decoded.slot_version, original.slot_version);
assert_eq!(decoded.sig.0, original.sig.0);
assert_eq!(decoded.data, original.data);
}

#[test]
fn test_stackerdb_slot_metadata_sign_verify() {
let pk = StacksPrivateKey::random();
Expand Down Expand Up @@ -67,6 +85,27 @@ fn test_stackerdb_slot_metadata_sign_verify() {
assert!(!bad_slot_metadata.verify(&addr).unwrap());
}

#[test]
fn test_stackerdb_chunk_data_sign_verify() {
let pk = StacksPrivateKey::random();
let addr = StacksAddress::from_public_keys(
C32_ADDRESS_VERSION_MAINNET_SINGLESIG,
&AddressHashMode::SerializeP2PKH,
1,
&vec![StacksPublicKey::from_private(&pk)],
)
.unwrap();
let bad_addr = StacksAddress::new(0x01, Hash160([0x01; 20])).unwrap();

let mut chunk_data = StackerDBChunkData::new(7, 3, vec![0xab; 64]);
chunk_data.sign(&pk).unwrap();

// The chunk verifies against the address whose private key signed it.
assert!(chunk_data.verify(&addr).unwrap());
// The chunk does not verify against an unrelated address.
assert!(!chunk_data.verify(&bad_addr).unwrap());
}

#[test]
fn test_stackerdb_paths() {
let pk = StacksPrivateKey::from_hex(
Expand Down
51 changes: 51 additions & 0 deletions stacks-codec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,3 +266,54 @@ pub const PEER_ADDRESS_ENCODED_SIZE: u32 = 16;

pub const HASH160_ENCODED_SIZE: u32 = 20;
pub const MESSAGE_SIGNATURE_ENCODED_SIZE: u32 = 65;

#[cfg(test)]
mod tests {
use crate::*;

fn encode_vec_u32_len_prefix(len: u32, items: &[u32]) -> Vec<u8> {
let mut buf = Vec::with_capacity(4 + items.len() * 4);
buf.extend_from_slice(&len.to_be_bytes());
for x in items {
buf.extend_from_slice(&x.to_be_bytes());
}
buf
}

#[test]
fn read_next_at_most_enforces_length_bound() {
// len < max: succeeds with the decoded items.
let bytes = encode_vec_u32_len_prefix(3, &[10, 20, 30]);
let v: Vec<u32> = read_next_at_most(&mut &bytes[..], 5).unwrap();
assert_eq!(v, vec![10, 20, 30]);

// len == max: succeeds (the bound is inclusive: `len > max` is what rejects).
let bytes = encode_vec_u32_len_prefix(5, &[1, 2, 3, 4, 5]);
let v: Vec<u32> = read_next_at_most(&mut &bytes[..], 5).unwrap();
assert_eq!(v, vec![1, 2, 3, 4, 5]);

// len > max: errors with DeserializeError.
let bytes = encode_vec_u32_len_prefix(6, &[1, 2, 3, 4, 5, 6]);
let r: Result<Vec<u32>, Error> = read_next_at_most(&mut &bytes[..], 5);
assert!(
matches!(r, Err(Error::DeserializeError(_))),
"expected DeserializeError, got {r:?}"
);
}

#[test]
fn read_next_exact_enforces_exact_length() {
// len == num_items: succeeds.
let bytes = encode_vec_u32_len_prefix(5, &[1, 2, 3, 4, 5]);
let v: Vec<u32> = read_next_exact(&mut &bytes[..], 5).unwrap();
assert_eq!(v, vec![1, 2, 3, 4, 5]);

// len != num_items: errors.
let bytes = encode_vec_u32_len_prefix(3, &[10, 20, 30]);
let r: Result<Vec<u32>, Error> = read_next_exact(&mut &bytes[..], 5);
assert!(
matches!(r, Err(Error::DeserializeError(_))),
"expected DeserializeError, got {r:?}"
);
}
}
Loading