Skip to content

Commit

Permalink
Merge pull request #430 from Chia-Network/message-conditions
Browse files Browse the repository at this point in the history
Message conditions
  • Loading branch information
arvidn committed Mar 20, 2024
2 parents 73a0272 + eba0720 commit 36f6787
Show file tree
Hide file tree
Showing 14 changed files with 1,466 additions and 14 deletions.
7 changes: 7 additions & 0 deletions crates/chia-consensus/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,10 @@ path = "fuzz_targets/fast-forward.rs"
test = false
doc = false
bench = false

[[bin]]
name = "parse-spend-id"
path = "fuzz_targets/parse-spend-id.rs"
test = false
doc = false
bench = false
9 changes: 7 additions & 2 deletions crates/chia-consensus/fuzz/fuzz_targets/parse-cond-args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@ use chia_consensus::gen::conditions::parse_args;
use clvmr::allocator::Allocator;
use fuzzing_utils::{make_list, BitCursor};

use chia_consensus::gen::flags::{COND_ARGS_NIL, ENABLE_SOFTFORK_CONDITION, STRICT_ARGS_COUNT};
use chia_consensus::gen::flags::{
COND_ARGS_NIL, ENABLE_MESSAGE_CONDITIONS, ENABLE_SOFTFORK_CONDITION, STRICT_ARGS_COUNT,
};

use chia_consensus::gen::opcodes::{
AGG_SIG_AMOUNT, AGG_SIG_ME, AGG_SIG_PARENT, AGG_SIG_PARENT_AMOUNT, AGG_SIG_PARENT_PUZZLE,
AGG_SIG_PUZZLE, AGG_SIG_PUZZLE_AMOUNT, AGG_SIG_UNSAFE, ASSERT_COIN_ANNOUNCEMENT,
ASSERT_EPHEMERAL, ASSERT_HEIGHT_ABSOLUTE, ASSERT_HEIGHT_RELATIVE, ASSERT_MY_AMOUNT,
ASSERT_MY_COIN_ID, ASSERT_MY_PARENT_ID, ASSERT_MY_PUZZLEHASH, ASSERT_PUZZLE_ANNOUNCEMENT,
ASSERT_SECONDS_ABSOLUTE, ASSERT_SECONDS_RELATIVE, CREATE_COIN, CREATE_COIN_ANNOUNCEMENT,
CREATE_PUZZLE_ANNOUNCEMENT, REMARK, RESERVE_FEE,
CREATE_PUZZLE_ANNOUNCEMENT, RECEIVE_MESSAGE, REMARK, RESERVE_FEE, SEND_MESSAGE,
};

fuzz_target!(|data: &[u8]| {
Expand All @@ -25,6 +27,7 @@ fuzz_target!(|data: &[u8]| {
COND_ARGS_NIL,
STRICT_ARGS_COUNT,
ENABLE_SOFTFORK_CONDITION,
ENABLE_MESSAGE_CONDITIONS,
] {
for op in &[
AGG_SIG_ME,
Expand All @@ -44,6 +47,8 @@ fuzz_target!(|data: &[u8]| {
CREATE_COIN_ANNOUNCEMENT,
CREATE_PUZZLE_ANNOUNCEMENT,
RESERVE_FEE,
SEND_MESSAGE,
RECEIVE_MESSAGE,
ASSERT_EPHEMERAL,
AGG_SIG_PARENT,
AGG_SIG_PUZZLE,
Expand Down
4 changes: 3 additions & 1 deletion crates/chia-consensus/fuzz/fuzz_targets/parse-conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use std::collections::HashSet;
use std::sync::Arc;

use chia_consensus::gen::flags::{
COND_ARGS_NIL, ENABLE_SOFTFORK_CONDITION, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT,
COND_ARGS_NIL, ENABLE_MESSAGE_CONDITIONS, ENABLE_SOFTFORK_CONDITION, NO_UNKNOWN_CONDS,
STRICT_ARGS_COUNT,
};

fuzz_target!(|data: &[u8]| {
Expand Down Expand Up @@ -47,6 +48,7 @@ fuzz_target!(|data: &[u8]| {
STRICT_ARGS_COUNT,
NO_UNKNOWN_CONDS,
ENABLE_SOFTFORK_CONDITION,
ENABLE_MESSAGE_CONDITIONS,
] {
let mut coin_spend = Spend {
parent_id,
Expand Down
50 changes: 50 additions & 0 deletions crates/chia-consensus/fuzz/fuzz_targets/parse-spend-id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#![no_main]
use libfuzzer_sys::fuzz_target;

use chia_consensus::gen::messages::SpendId;
use clvmr::allocator::Allocator;
use fuzzing_utils::{make_list, BitCursor};

fuzz_target!(|data: &[u8]| {
let mut a = Allocator::new();
let mut cursor = BitCursor::new(data);
let mode = cursor.read_bits(3).unwrap_or(0);
let mut input = make_list(&mut a, &mut cursor);

let Ok(s) = SpendId::parse(&a, &mut input, mode) else {
return;
};

match s {
SpendId::OwnedCoinId(_bytes) => unreachable!(),
SpendId::CoinId(coinid) => {
assert_eq!(mode, 0b111);
assert_eq!(a.atom_len(coinid), 32);
}
SpendId::Parent(parent) => {
assert_eq!(mode, 0b100);
assert_eq!(a.atom_len(parent), 32);
}
SpendId::Puzzle(puzzle) => {
assert_eq!(mode, 0b010);
assert_eq!(a.atom_len(puzzle), 32);
}
SpendId::Amount(_amount) => {
assert_eq!(mode, 0b001);
}
SpendId::PuzzleAmount(puzzle, _amount) => {
assert_eq!(a.atom_len(puzzle), 32);
assert_eq!(mode, 0b011);
}
SpendId::ParentAmount(parent, _amount) => {
assert_eq!(a.atom_len(parent), 32);
assert_eq!(mode, 0b101);
}
SpendId::ParentPuzzle(parent, puzzle) => {
assert_eq!(a.atom_len(parent), 32);
assert_eq!(a.atom_len(puzzle), 32);
assert_eq!(mode, 0b110);
}
SpendId::None => assert_eq!(mode, 0),
}
});
4 changes: 3 additions & 1 deletion crates/chia-consensus/fuzz/fuzz_targets/parse-spends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use clvmr::{Allocator, NodePtr};
use fuzzing_utils::{make_list, BitCursor};

use chia_consensus::gen::flags::{
COND_ARGS_NIL, ENABLE_SOFTFORK_CONDITION, NO_UNKNOWN_CONDS, STRICT_ARGS_COUNT,
COND_ARGS_NIL, ENABLE_MESSAGE_CONDITIONS, ENABLE_SOFTFORK_CONDITION, NO_UNKNOWN_CONDS,
STRICT_ARGS_COUNT,
};

fuzz_target!(|data: &[u8]| {
Expand All @@ -20,6 +21,7 @@ fuzz_target!(|data: &[u8]| {
STRICT_ARGS_COUNT,
NO_UNKNOWN_CONDS,
ENABLE_SOFTFORK_CONDITION,
ENABLE_MESSAGE_CONDITIONS,
] {
let _ret = parse_spends::<MempoolVisitor>(&a, input, 33000000000, *flags);
}
Expand Down
71 changes: 71 additions & 0 deletions crates/chia-consensus/src/gen/condition_sanitizers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::sanitize_int::{sanitize_uint, SanitizedUint};
use super::validation_error::{atom, ErrorCode, ValidationErr};
use crate::gen::flags::NO_UNKNOWN_CONDS;
use clvmr::allocator::{Allocator, NodePtr};

pub fn sanitize_hash(
Expand Down Expand Up @@ -40,6 +41,76 @@ pub fn sanitize_announce_msg(
}
}

pub fn sanitize_message_mode(
a: &Allocator,
node: NodePtr,
flags: u32,
) -> Result<u32, ValidationErr> {
let Some(mode) = a.small_number(node) else {
return Err(ValidationErr(node, ErrorCode::InvalidMessageMode));
};
// only 6 bits are allowed to be set
if (mode & !0b111111) != 0 {
return Err(ValidationErr(node, ErrorCode::InvalidMessageMode));
}
// both the sender and the receiver must commit to *something*
// the mode flags are: parent, puzzle, amount. First for the sender, then
// for the receiver
if (flags & NO_UNKNOWN_CONDS) != 0 {
// in mempool mode, we only accept message modes where at least the
// parent or the puzzle hash is committed to, for both the sender and
// receiver
if (mode & 0b110) == 0 || (mode & 0b110000) == 0 {
return Err(ValidationErr(node, ErrorCode::InvalidMessageMode));
}
}
Ok(mode)
}

#[cfg(test)]
use rstest::rstest;

#[cfg(test)]
#[rstest]
#[case(0, false, true)]
#[case(-1, false, false)]
#[case(1, false, true)]
#[case(10000000000, false, false)]
#[case(0xffffffffffff, false, false)]
#[case(-0xffffffffffff, false, false)]
#[case(0b1001001, false, false)]
// committing to only amount is not allowed in mempool mode
#[case(0b001001, false, true)]
#[case(0b010010, true, true)]
#[case(0b100100, true, true)]
#[case(0b101101, true, true)]
// committing to only amount is not allowed in mempool mode
#[case(0b100001, false, true)]
#[case(0b111111, true, true)]
#[case(0b111100, true, true)]
#[case(0b100111, true, true)]
// not committing to anything is not allowed in mempool mode
#[case(0b000111, false, true)]
#[case(0b111000, false, true)]
fn test_sanitize_mode(#[case] value: i64, #[case] pass_mempool: bool, #[case] pass: bool) {
let mut a = Allocator::new();
let node = a.new_number(value.into()).unwrap();

let ret = sanitize_message_mode(&a, node, 0);
if pass {
assert_eq!(ret.unwrap() as i64, value);
} else {
assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidMessageMode);
}

let ret = sanitize_message_mode(&a, node, NO_UNKNOWN_CONDS);
if pass_mempool {
assert_eq!(ret.unwrap() as i64, value);
} else {
assert_eq!(ret.unwrap_err().1, ErrorCode::InvalidMessageMode);
}
}

#[cfg(test)]
fn zero_vec(len: usize) -> Vec<u8> {
let mut ret = Vec::<u8>::new();
Expand Down
Loading

0 comments on commit 36f6787

Please sign in to comment.