use essential_check as check;
use essential_storage::{StateStorage, Storage};
use essential_transaction_storage::TransactionStorage;
use essential_types::{predicate::Predicate, solution::Solution, ContentAddress, PredicateAddress};
use std::{collections::HashMap, sync::Arc};
use crate::TimeConfig;
pub(crate) mod read;
#[cfg(test)]
mod tests;
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, err(level=tracing::Level::DEBUG), ret(Display)))]
pub async fn submit_solution<S>(storage: &S, solution: Solution) -> anyhow::Result<ContentAddress>
where
S: Storage,
{
check::solution::check(&solution)?;
let contract: HashMap<PredicateAddress, Arc<Predicate>> =
read::read_contract_from_storage(&solution, storage).await?;
validate_contract(&solution, &contract)?;
let solution_hash = essential_hash::content_addr(&solution);
match storage.insert_solution_into_pool(solution).await {
Ok(()) => Ok(solution_hash),
Err(err) => anyhow::bail!("Failed to submit solution: {}", err),
}
}
pub(crate) fn apply_mutations<S>(storage: &mut TransactionStorage<S>, solution: &Solution)
where
S: StateStorage,
{
for data in &solution.data {
for mutation in data.state_mutations.iter() {
storage.apply_state(
&data.predicate_to_solve.contract,
mutation.key.clone(),
mutation.value.clone(),
);
}
}
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, err))]
pub fn create_post_state<S>(
pre_state: &TransactionStorage<S>,
solution: &Solution,
) -> anyhow::Result<TransactionStorage<S>>
where
S: Clone + StateStorage,
{
let mut post_state = pre_state.clone();
apply_mutations(&mut post_state, solution);
Ok(post_state)
}
#[cfg_attr(feature = "tracing", tracing::instrument(skip_all, err))]
pub fn validate_contract(
solution: &Solution,
contract: &HashMap<PredicateAddress, Arc<Predicate>>,
) -> anyhow::Result<()> {
contains_all_contract(solution, contract)?;
Ok(())
}
pub fn contains_all_contract(
solution: &Solution,
predicates: &HashMap<PredicateAddress, Arc<Predicate>>,
) -> anyhow::Result<()> {
anyhow::ensure!(
solution
.data
.iter()
.all(|data| predicates.contains_key(&data.predicate_to_solve)),
"All predicates must be in the contracts"
);
Ok(())
}
pub(crate) fn filter_solution(time_config: &TimeConfig, solution: &Solution) -> anyhow::Result<()> {
if time_config.enable_time && !time_config.allow_time_submissions {
let block_state_address = crate::protocol::block_state_contract_address();
if solution
.data
.iter()
.any(|data| data.predicate_to_solve.contract == block_state_address)
{
anyhow::bail!("Block state solutions are blocked");
}
}
Ok(())
}