1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
//! A constraint checker is a piece of logic that determines whether a transaction as a whole is valid
//! and should be committed. Most tuxedo pieces will provide one or more constraint checkers.
//! Constraint Checkers do not calculate the correct final state, but rather determine whether the
//! proposed final state (as specified by the output set) meets the necessary constraints.
//!
//! Constraint Checkers can be used to codify the laws of a monetary system, a chemistry or physics simulation,
//! NFT kitties, public elections and much more.
//!
//! The primary way for developers to write a constraint checker is with the `SimpleConstraintChecker`
//! trait. It provides a method called `check` which determines whether the relationship between the inputs
//! and outputs (and peeks) is valid. For example making sure no extra money was created, or making sure the chemical
//! reaction balances.
//!
//! ## Inherents
//!
//! If you need to tap in to [Substrate's inherent system](https://docs.substrate.io/learn/transaction-types/#inherent-transactions)
//! you may choose to also implement the `InherentHooks` trait for the same type that implements `SimpleConstraintChecker`.
//! When installing a constraint checker in your runtime, make sure to wrap it with the `InherentAdapter` type.
//! See the `inherents` module for more details.
//!
//! ## Constraint Checker Internals
//!
//! One of Tuxedo's killer features is its ability to aggregating pieces recursively.
//! To achieve this we have to consider that many intermediate layers in the aggregation tree
//! will have multiple inherent types. For this reason, we provide a much more flexible interface
//! that the aggregation macro can use called `ConstraintChecker`. Do not implement `ConstraintChecker`
//! directly.
use sp_core::H256;
use sp_inherents::{CheckInherentsResult, InherentData};
use sp_std::{fmt::Debug, vec::Vec};
use crate::{dynamic_typing::DynamicallyTypedData, types::Transaction, Verifier};
use parity_scale_codec::{Decode, Encode};
use sp_runtime::transaction_validity::TransactionPriority;
/// A particular constraint checker that a transaction can choose to be checked by.
/// Checks whether the input and output data from a transaction meets the codified constraints.
///
/// Additional transient information may be passed to the constraint checker by including it in the fields
/// of the constraint checker struct itself. Information passed in this way does not come from state, nor
/// is it stored in state.
pub trait SimpleConstraintChecker: Debug + Encode + Decode + Clone {
/// The error type that this constraint checker may return
type Error: Debug;
/// The on chain logic that makes the final check for whether a transaction is valid.
fn check(
&self,
input_data: &[DynamicallyTypedData],
evicted_input_data: &[DynamicallyTypedData],
peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error>;
}
/// The raw and fully powerful `ConstraintChecker` interface used by the
/// Tuxedo Executive.
///
/// You should never manually manually implement this trait.
/// If you are:
/// * Working on a simple non-inherent constraint checker -> Use the `SimpleConstraintChecker` trait instead
/// and rely on its blanket implementation.
/// * Working on an inherent constraint checker -> Implement `SimpleConstraintChecker` and `InherentHooks` and use the
/// `InherentAdapter` wrapper type.
/// * Considering an aggregate constraint checker that is part inherent, part not -> let the macro handle it for you.
///
/// If you are trying to implement some complex inherent logic that requires the interaction of
/// multiple inherents, or features a variable number of inherents in each block, you might be
/// able to express it by implementing this trait, but such designs are probably too complicated.
/// Think long and hard before implementing this trait directly.
pub trait ConstraintChecker: Debug + Encode + Decode + Clone {
/// The error type that this constraint checker may return
type Error: Debug;
/// The on chain logic that makes the final check for whether a transaction is valid.
fn check(
&self,
input_data: &[DynamicallyTypedData],
evicted_input_data: &[DynamicallyTypedData],
peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error>;
/// Tells whether this extrinsic is an inherent or not.
/// If you return true here, you must provide the correct inherent hooks above.
fn is_inherent(&self) -> bool;
/// Create the inherent extrinsics to insert into a block that is being authored locally.
/// The inherent data is supplied by the authoring node.
fn create_inherents<V: Verifier>(
authoring_inherent_data: &InherentData,
previous_inherents: Vec<(Transaction<V, Self>, H256)>,
) -> Vec<Transaction<V, Self>>;
/// Perform off-chain pre-execution checks on the inherents.
/// The inherent data is supplied by the importing node.
/// The inherent data available here is not necessarily the
/// same as what is available at authoring time.
fn check_inherents<V: Verifier>(
importing_inherent_data: &InherentData,
inherents: Vec<Transaction<V, Self>>,
results: &mut CheckInherentsResult,
);
/// Return the genesis transactions that are required for the inherents.
fn genesis_transactions<V: Verifier>() -> Vec<Transaction<V, Self>>;
}
// We automatically supply every single simple constraint checker with a dummy set
// of inherent hooks. This allows "normal" non-inherent constraint checkers to satisfy the
// executive's expected interfaces without the piece author worrying about inherents.
impl<T: SimpleConstraintChecker> ConstraintChecker for T {
// Use the same error type used in the simple implementation.
type Error = <T as SimpleConstraintChecker>::Error;
fn check(
&self,
input_data: &[DynamicallyTypedData],
evicted_input_data: &[DynamicallyTypedData],
peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error> {
SimpleConstraintChecker::check(self, input_data, evicted_input_data, peek_data, output_data)
}
fn is_inherent(&self) -> bool {
false
}
fn create_inherents<V>(
_authoring_inherent_data: &InherentData,
_previous_inherents: Vec<(Transaction<V, Self>, H256)>,
) -> Vec<Transaction<V, Self>> {
Vec::new()
}
fn check_inherents<V>(
_: &InherentData,
inherents: Vec<Transaction<V, Self>>,
_: &mut CheckInherentsResult,
) {
// Inherents should always be empty for this stub implementation. Not just in valid blocks, but as an invariant.
// The way we determined which inherents got here is by matching on the constraint checker.
assert!(
inherents.is_empty(),
"inherent extrinsic was passed to check inherents stub implementation."
)
}
fn genesis_transactions<V>() -> Vec<Transaction<V, Self>> {
Vec::new()
}
}
/// Utilities for writing constraint-checker-related unit tests
#[cfg(test)]
pub mod testing {
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
use super::{ConstraintChecker, DynamicallyTypedData, TransactionPriority};
/// A testing checker that passes (with zero priority) or not depending on
/// the boolean value enclosed.
#[derive(Serialize, Deserialize, Encode, Decode, Debug, Clone, PartialEq, Eq, TypeInfo)]
pub struct TestConstraintChecker {
/// Whether the checker should pass.
pub checks: bool,
/// Whether this constraint checker is an inherent.
pub inherent: bool,
}
impl ConstraintChecker for TestConstraintChecker {
type Error = ();
fn check(
&self,
_input_data: &[DynamicallyTypedData],
_evicted_input_data: &[DynamicallyTypedData],
_peek_data: &[DynamicallyTypedData],
_output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, ()> {
if self.checks {
Ok(0)
} else {
Err(())
}
}
fn is_inherent(&self) -> bool {
self.inherent
}
fn create_inherents<V: crate::Verifier>(
_: &sp_inherents::InherentData,
_: Vec<(crate::types::Transaction<V, Self>, sp_core::H256)>,
) -> Vec<crate::types::Transaction<V, Self>> {
unimplemented!()
}
fn check_inherents<V: crate::Verifier>(
_: &sp_inherents::InherentData,
_: Vec<crate::types::Transaction<V, Self>>,
_: &mut sp_inherents::CheckInherentsResult,
) {
unimplemented!()
}
fn genesis_transactions<V: crate::Verifier>() -> Vec<crate::types::Transaction<V, Self>> {
unimplemented!()
}
}
#[test]
fn test_checker_passes() {
let result = TestConstraintChecker {
checks: true,
inherent: false,
}
.check(&[], &[], &[], &[]);
assert_eq!(result, Ok(0));
}
#[test]
fn test_checker_fails() {
let result = TestConstraintChecker {
checks: false,
inherent: false,
}
.check(&[], &[], &[], &[]);
assert_eq!(result, Err(()));
}
}