use essential_check::{solution, vm::asm};
use essential_hash::content_addr;
use essential_types::{
contract::Contract,
predicate::{Edge, Node, Predicate, Program, Reads},
solution::{Mutation, Solution, SolutionSet},
ContentAddress, PredicateAddress, Word,
};
use std::{collections::HashMap, sync::Arc};
use util::{empty_solution_set, State};
pub mod util;
fn test_predicate_addr() -> PredicateAddress {
PredicateAddress {
contract: ContentAddress([0; 32]),
predicate: ContentAddress([0; 32]),
}
}
fn test_solution() -> Solution {
Solution {
predicate_to_solve: test_predicate_addr(),
predicate_data: vec![],
state_mutations: vec![],
}
}
fn test_mutation(salt: usize) -> Mutation {
Mutation {
key: vec![salt as Word; 4],
value: vec![42],
}
}
#[test]
fn solution_data_mut_not_be_empty() {
let set = empty_solution_set();
assert!(matches!(
solution::check_set(&set).unwrap_err(),
solution::InvalidSolutionSet::Solution(solution::InvalidSolution::Empty),
));
}
#[test]
fn too_many_solution_data() {
let set = SolutionSet {
solutions: (0..solution::MAX_SOLUTIONS + 1)
.map(|_| test_solution())
.collect(),
};
assert!(matches!(
solution::check_set(&set).unwrap_err(),
solution::InvalidSolutionSet::Solution(solution::InvalidSolution::TooMany(n))
if n == solution::MAX_SOLUTIONS + 1
));
}
#[test]
fn too_many_predicate_data() {
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: test_predicate_addr(),
predicate_data: vec![vec![0]; (solution::MAX_PREDICATE_DATA + 1) as usize],
state_mutations: vec![],
}],
};
assert!(matches!(
solution::check_set(&set).unwrap_err(),
solution::InvalidSolutionSet::Solution(solution::InvalidSolution::PredicateDataLenExceeded(0, n))
if n == solution::MAX_PREDICATE_DATA as usize + 1
));
}
#[test]
fn too_many_state_mutations() {
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: test_predicate_addr(),
predicate_data: vec![],
state_mutations: (0..(solution::MAX_STATE_MUTATIONS + 1))
.map(test_mutation)
.collect(),
}],
};
assert!(matches!(
solution::check_set(&set).unwrap_err(),
solution::InvalidSolutionSet::StateMutations(solution::InvalidSetStateMutations::TooMany(n))
if n == solution::MAX_STATE_MUTATIONS + 1
));
}
#[test]
fn multiple_mutations_for_slot() {
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: test_predicate_addr(),
predicate_data: vec![],
state_mutations: vec![
Mutation {
key: vec![0; 4],
value: vec![42],
};
2
],
}],
};
assert!(matches!(
solution::check_set(&set).unwrap_err(),
solution::InvalidSolutionSet::StateMutations(solution::InvalidSetStateMutations::MultipleMutationsForSlot(addr, key))
if addr == test_predicate_addr() && key == [0; 4]
));
}
#[tokio::test]
async fn predicate_graph_stack_passing() {
use essential_vm::asm::short::*;
let _ = tracing_subscriber::fmt::try_init();
let a = Program(asm::to_bytes([PUSH(1), PUSH(2), PUSH(3), HLT]).collect());
let b = Program(asm::to_bytes([PUSH(4), PUSH(5), PUSH(6), HLT]).collect());
let c = Program(
asm::to_bytes([
PUSH(1),
PUSH(2),
PUSH(3),
PUSH(4),
PUSH(5),
PUSH(6),
PUSH(6), EQRA,
HLT,
])
.collect(),
);
let a_ca = content_addr(&a);
let b_ca = content_addr(&b);
let c_ca = content_addr(&c);
let node = |program_address, edge_start| Node {
program_address,
edge_start,
reads: Reads::Pre, };
let nodes = vec![
node(a_ca.clone(), 0),
node(b_ca.clone(), 1),
node(c_ca.clone(), Edge::MAX),
];
let edges = vec![2, 2];
let predicate = Predicate { nodes, edges };
let contract = Contract::without_salt(vec![predicate]);
let pred_addr = PredicateAddress {
contract: content_addr(&contract),
predicate: content_addr(&contract.predicates[0]),
};
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: pred_addr.clone(),
predicate_data: Default::default(),
state_mutations: vec![],
}],
};
essential_check::predicate::check(&contract.predicates[0]).unwrap();
essential_check::solution::check_set(&set).unwrap();
let predicate = Arc::new(contract.predicates[0].clone());
let get_predicate = |addr: &PredicateAddress| {
assert_eq!(&pred_addr, addr);
predicate.clone()
};
let programs: HashMap<ContentAddress, Arc<Program>> = vec![
(a_ca, Arc::new(a)),
(b_ca, Arc::new(b)),
(c_ca, Arc::new(c)),
]
.into_iter()
.collect();
let get_program: Arc<HashMap<_, _>> = Arc::new(programs);
let gas = solution::check_set_predicates(
&State::EMPTY,
&State::EMPTY,
Arc::new(set),
get_predicate,
get_program,
Arc::new(solution::CheckPredicateConfig::default()),
)
.await
.unwrap();
assert!(gas > 0);
}
#[tokio::test]
async fn predicate_graph_memory_passing() {
use essential_vm::asm::short::*;
let _ = tracing_subscriber::fmt::try_init();
let a = Program(
asm::to_bytes([
PUSH(3),
ALOC,
PUSH(1),
STO,
PUSH(1),
PUSH(2),
STO,
PUSH(2),
PUSH(3),
STO,
HLT,
])
.collect(),
);
let b = Program(
asm::to_bytes([
PUSH(3),
ALOC,
PUSH(4),
STO,
PUSH(1),
PUSH(5),
STO,
PUSH(2),
PUSH(6),
STO,
HLT,
])
.collect(),
);
let c = Program(
asm::to_bytes([
PUSH(0),
LOD,
PUSH(1),
LOD,
PUSH(2),
LOD,
PUSH(3),
LOD,
PUSH(4),
LOD,
PUSH(5),
LOD,
PUSH(1),
PUSH(2),
PUSH(3),
PUSH(4),
PUSH(5),
PUSH(6),
PUSH(6), EQRA,
HLT,
])
.collect(),
);
let a_ca = content_addr(&a);
let b_ca = content_addr(&b);
let c_ca = content_addr(&c);
let node = |program_address, edge_start| Node {
program_address,
edge_start,
reads: Reads::Pre, };
let nodes = vec![
node(a_ca.clone(), 0),
node(b_ca.clone(), 1),
node(c_ca.clone(), Edge::MAX),
];
let edges = vec![2, 2];
let predicate = Predicate { nodes, edges };
let contract = Contract::without_salt(vec![predicate]);
let pred_addr = PredicateAddress {
contract: content_addr(&contract),
predicate: content_addr(&contract.predicates[0]),
};
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: pred_addr.clone(),
predicate_data: Default::default(),
state_mutations: vec![],
}],
};
essential_check::predicate::check(&contract.predicates[0]).unwrap();
essential_check::solution::check_set(&set).unwrap();
let predicate = Arc::new(contract.predicates[0].clone());
let get_predicate = |addr: &PredicateAddress| {
assert_eq!(&pred_addr, addr);
predicate.clone()
};
let programs: HashMap<ContentAddress, Arc<Program>> = vec![
(a_ca, Arc::new(a)),
(b_ca, Arc::new(b)),
(c_ca, Arc::new(c)),
]
.into_iter()
.collect();
let get_program: Arc<HashMap<_, _>> = Arc::new(programs);
let gas = solution::check_set_predicates(
&State::EMPTY,
&State::EMPTY,
Arc::new(set),
get_predicate,
get_program,
Arc::new(solution::CheckPredicateConfig::default()),
)
.await
.unwrap();
assert!(gas > 0);
}
#[tokio::test]
async fn predicate_graph_state_read() {
use essential_vm::asm::short::*;
let _ = tracing_subscriber::fmt::try_init();
let key = vec![9, 9, 9, 9];
let a = Program(
asm::to_bytes(key.iter().map(|&w| PUSH(w)).chain([
PUSH(4),
PUSH(1),
HLT,
]))
.collect(),
);
let b = Program(
asm::to_bytes([
PUSH(3),
ALOC,
KRNG,
PUSH(2),
LOD,
PUSH(0),
FREE,
HLT,
])
.collect(),
);
let c = Program(asm::to_bytes([MUL, PUSH(42), EQ]).collect());
let a_ca = content_addr(&a);
let b_ca = content_addr(&b);
let c_ca = content_addr(&c);
let node = |program_address, edge_start, reads| Node {
program_address,
edge_start,
reads,
};
let nodes = vec![
node(a_ca.clone(), 0, Reads::Pre),
node(b_ca.clone(), 2, Reads::Pre),
node(b_ca.clone(), 3, Reads::Post),
node(c_ca.clone(), Edge::MAX, Reads::Pre),
];
let edges = vec![1, 2, 3, 3];
let predicate = Predicate { nodes, edges };
let contract = Contract::without_salt(vec![predicate]);
let pred_addr = PredicateAddress {
contract: content_addr(&contract),
predicate: content_addr(&contract.predicates[0]),
};
let mut pre_state = State::EMPTY;
pre_state.deploy_namespace(pred_addr.contract.clone());
pre_state.set(pred_addr.contract.clone(), &key, vec![6]);
let set = SolutionSet {
solutions: vec![Solution {
predicate_to_solve: pred_addr.clone(),
predicate_data: Default::default(),
state_mutations: vec![
Mutation {
key,
value: vec![7],
},
],
}],
};
let mut post_state = pre_state.clone();
post_state.apply_mutations(&set);
essential_check::predicate::check(&contract.predicates[0]).unwrap();
essential_check::solution::check_set(&set).unwrap();
let predicate = Arc::new(contract.predicates[0].clone());
let get_predicate = |addr: &PredicateAddress| {
assert_eq!(&pred_addr, addr);
predicate.clone()
};
let programs: HashMap<ContentAddress, Arc<Program>> = vec![
(a_ca, Arc::new(a)),
(b_ca, Arc::new(b)),
(c_ca, Arc::new(c)),
]
.into_iter()
.collect();
let get_program: Arc<HashMap<_, _>> = Arc::new(programs);
let gas = solution::check_set_predicates(
&pre_state,
&post_state,
Arc::new(set),
get_predicate,
get_program,
Arc::new(solution::CheckPredicateConfig::default()),
)
.await
.unwrap();
assert!(gas > 0);
}