use super::*;
use frame_support::dispatch::{DispatchError, DispatchResult};
pub const EXIT_CLAIM: &'static [u8] = b"EXIT_CLAIM";
pub const EXIT_SPENT_CHALLENGE: &'static [u8] = b"EXIT_SPENT_CHALLENGE";
pub const EXIT_CHECKPOINT_CHALLENGE: &'static [u8] = b"EXIT_CHECKPOINT_CHALLENGE";
impl<T: Config> Module<T> {
pub fn bare_exit_claim(
plapps_id: &T::AccountId,
state_update: &StateUpdateOf<T>,
checkpoint: &Option<StateUpdateOf<T>>,
witness: &Option<InclusionProofOf<T>>,
) -> DispatchResult {
if let Some(checkpoint) = checkpoint {
ensure!(
Self::checkpoint_exitable(plapps_id, state_update, checkpoint)?,
"Checkpoint must be exitable for stateUpdate"
);
} else if let Some(inclusion_proof) = witness {
let root = Self::retrieve(plapps_id, &state_update.block_number);
ensure!(
Self::verify_inclusion_with_root(
&T::Hashing::hash_of(&state_update.state_object),
&state_update.deposit_contract_address,
&state_update.range,
inclusion_proof,
&root
)?,
"Inclusion verification failed"
);
}
let exit_predicate = Self::exit_predicate(plapps_id);
let property: PropertyOf<T> =
Self::create_property(exit_predicate.clone(), &state_update.encode(), EXIT_CLAIM);
pallet_ovm::Call::<T>::claim(property);
Ok(())
}
pub fn bare_exit_challenge(
plapps_id: &T::AccountId,
state_update: &StateUpdateOf<T>,
challenge_inputs: &Vec<Vec<u8>>,
witness: &InclusionProofOf<T>,
) -> DispatchResult {
ensure!(
challenge_inputs.len() == 2,
"challenge inputs length does not match. expected 2"
);
let challenge_property = if T::Hashing::hash_of(&challenge_inputs[0])
== T::Hashing::hash(EXIT_SPENT_CHALLENGE)
{
let spent_challenge_inputs = vec![challenge_inputs[1].clone()];
Self::validate_spent_challenge(
plapps_id,
state_update,
&spent_challenge_inputs,
witness,
)?;
let exit_predicate = Self::exit_predicate(plapps_id);
Ok(Self::create_property(
exit_predicate,
&challenge_inputs[0],
EXIT_SPENT_CHALLENGE,
)) as DispatchResultT<PropertyOf<T>>
} else if T::Hashing::hash_of(&challenge_inputs[0])
== T::Hashing::hash(EXIT_CHECKPOINT_CHALLENGE)
{
let invalid_history_challenge_inputs = vec![challenge_inputs[1].clone()];
let challenge_state_update: StateUpdateOf<T> =
Decode::decode(&mut &invalid_history_challenge_inputs[0][..])
.map_err(|_| Error::<T>::MustBeDecodable)?;
Self::validate_checkpoint_challenge(
plapps_id,
state_update.clone(),
challenge_state_update,
witness.clone(),
)?;
let exit_predicate = Self::exit_predicate(plapps_id);
Ok(Self::create_property(
exit_predicate,
&invalid_history_challenge_inputs[0],
EXIT_CHECKPOINT_CHALLENGE,
)) as DispatchResultT<PropertyOf<T>>
} else {
return Err(DispatchError::Other("illegal challenge type"));
}?;
let exit_predicate = Self::exit_predicate(plapps_id);
let claimed_property =
Self::create_property(exit_predicate.clone(), &state_update.encode(), EXIT_CLAIM);
ensure!(
pallet_ovm::Module::<T>::started(&pallet_ovm::Module::<T>::get_property_id(
&claimed_property
)),
"Claim does not exist"
);
pallet_ovm::Module::<T>::bare_challenge(
exit_predicate.clone(),
Self::create_property(exit_predicate, &state_update.encode(), EXIT_CLAIM),
challenge_property,
)
}
pub fn bare_exit_remove_challenge(
_plapps_id: &T::AccountId,
_inputs: Vec<Vec<u8>>,
_challenge_inputs: Vec<Vec<u8>>,
_witness: Vec<Vec<u8>>,
) -> DispatchResult {
Ok(())
}
pub fn bare_exit_settle(
plapps_id: &T::AccountId,
state_update: &StateUpdateOf<T>,
) -> DispatchResult {
let exit_predicate = Self::exit_predicate(plapps_id);
let property =
Self::create_property(exit_predicate.clone(), &state_update.encode(), EXIT_CLAIM);
pallet_ovm::Module::<T>::bare_settle_game(exit_predicate, property)?;
Ok(())
}
fn get_id(su: &StateUpdateOf<T>) -> T::Hash {
T::Hashing::hash_of(su)
}
pub fn get_claim_decision(
predicate_address: T::AccountId,
su: &StateUpdateOf<T>,
) -> DispatchResultT<Decision> {
let su_bytes = su.encode();
let exit_property =
Self::create_property(predicate_address, &su_bytes.encode(), EXIT_CLAIM);
let id = pallet_ovm::Module::<T>::get_property_id(&exit_property);
let game = pallet_ovm::Module::<T>::get_game(&id).ok_or(Error::<T>::NotFoundGame)?;
Ok(game.decision)
}
pub fn is_completable(plapps_id: &T::AccountId, su: &StateUpdateOf<T>) -> bool {
let su_bytes = su.encode();
let exit_predicate = Self::exit_predicate(plapps_id);
let exit_property = Self::create_property(exit_predicate, &su_bytes, EXIT_CLAIM);
let id = pallet_ovm::Module::<T>::get_property_id(&exit_property);
pallet_ovm::Module::<T>::is_decidable(&id)
}
fn checkpoint_exitable(
plapps_id: &T::AccountId,
state_update: &StateUpdateOf<T>,
checkpoint: &StateUpdateOf<T>,
) -> DispatchResultT<bool> {
ensure!(
Self::is_subrange(&state_update.range, &checkpoint.range),
"StateUpdate range must be subrange of checkpoint"
);
ensure!(
state_update.block_number == checkpoint.block_number,
"BlockNumber must be same"
);
let id = Self::get_id(checkpoint);
ensure!(
Self::checkpoints(plapps_id, &id),
"Checkpoint needs to be finalized or inclusionProof have to be provided"
);
Ok(true)
}
}
impl<T: Config> Module<T> {
fn validate_spent_challenge(
plapps_id: &T::AccountId,
state_update: &StateUpdateOf<T>,
challenge_inputs: &Vec<Vec<u8>>,
witness: &InclusionProofOf<T>,
) -> DispatchResult {
let transaction: TransactionOf<T> = Decode::decode(&mut &challenge_inputs[0][..])
.map_err(|_| Error::<T>::MustBeDecodable)?;
ensure!(
transaction.deposit_contract_address == state_update.deposit_contract_address,
"token must be same"
);
ensure!(
Self::has_intersection(&transaction.range, &state_update.range),
"range must contain subrange"
);
ensure!(
transaction.max_block_number >= state_update.block_number,
"blockNumber must be valid"
);
let new_inputs = vec![
state_update.state_object.inputs[0].clone(),
challenge_inputs[0].clone(),
];
let predicate_decide_inputs =
Self::make_compiled_predicate_decide_inputs(new_inputs, vec![witness.encode()]);
let result_bytes = pallet_ovm::Module::<T>::bare_call(
plapps_id.clone(),
state_update.state_object.predicate_address.clone(),
predicate_decide_inputs,
)
.map_err(|_| Error::<T>::PredicateExecError)?;
let result: bool =
Decode::decode(&mut &result_bytes[..]).map_err(|_| Error::<T>::MustBeDecodable)?;
ensure!(result, "State object decided to false");
Ok(())
}
fn make_compiled_predicate_decide_inputs(
inputs: Vec<Vec<u8>>,
witness: Vec<Vec<u8>>,
) -> Vec<u8> {
ovmi::predicates::PredicateCallInputs::CompiledPredicate::<T::AccountId>(
ovmi::predicates::CompiledPredicateCallInputs::DecideTrue { inputs, witness },
)
.encode()
}
}