-
Notifications
You must be signed in to change notification settings - Fork 17
/
constraint_checker.rs
129 lines (112 loc) · 4.85 KB
/
constraint_checker.rs
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
//! 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 typically calculate the correct final state, but rather determine whether the
//! proposed final state (as specified by the output set) meets the necessary constraints.
use sp_std::{fmt::Debug, vec::Vec};
use crate::{dynamic_typing::DynamicallyTypedData, types::Output, Verifier};
use parity_scale_codec::{Decode, Encode};
use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_runtime::transaction_validity::TransactionPriority;
/// A simplified constraint checker that a transaction can choose to call. 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 actual check validation logic
fn check(
&self,
input_data: &[DynamicallyTypedData],
peek_data: &[DynamicallyTypedData],
output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, Self::Error>;
}
/// A single constraint checker that a transaction can choose to call. Checks whether the input
/// and output data from a transaction meets the codified constraints.
///
/// This full ConstraintChecker should only be used if there is more that a piece wants to do such
/// as check the verifier information in some unique way.
///
/// 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 ConstraintChecker<V: Verifier>: Debug + Encode + Decode + Clone {
/// the error type that this constraint checker may return
type Error: Debug;
/// The actual check validation logic
fn check(
&self,
inputs: &[Output<V>],
peeks: &[Output<V>],
outputs: &[Output<V>],
) -> Result<TransactionPriority, Self::Error>;
}
// This blanket implementation makes it so that any type that chooses to
// implement the Simple trait also implements the more Powerful trait. This way
// the executive can always just call the more Powerful trait.
impl<T: SimpleConstraintChecker, V: Verifier> ConstraintChecker<V> for T {
// Use the same error type used in the simple implementation.
type Error = <T as SimpleConstraintChecker>::Error;
fn check(
&self,
inputs: &[Output<V>],
peeks: &[Output<V>],
outputs: &[Output<V>],
) -> Result<TransactionPriority, Self::Error> {
// Extract the input data
let input_data: Vec<DynamicallyTypedData> =
inputs.iter().map(|o| o.payload.clone()).collect();
// Extract the peek data
let peek_data: Vec<DynamicallyTypedData> =
peeks.iter().map(|o| o.payload.clone()).collect();
// Extract the output data
let output_data: Vec<DynamicallyTypedData> =
outputs.iter().map(|o| o.payload.clone()).collect();
// Call the simple constraint checker
SimpleConstraintChecker::check(self, &input_data, &peek_data, &output_data)
}
}
/// Utilities for writing constraint-checker-related unit tests
#[cfg(feature = "std")]
pub mod testing {
use super::*;
/// 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,
}
impl SimpleConstraintChecker for TestConstraintChecker {
type Error = ();
fn check(
&self,
_input_data: &[DynamicallyTypedData],
_peek_data: &[DynamicallyTypedData],
_output_data: &[DynamicallyTypedData],
) -> Result<TransactionPriority, ()> {
if self.checks {
Ok(0)
} else {
Err(())
}
}
}
#[test]
fn test_checker_passes() {
let result =
SimpleConstraintChecker::check(&TestConstraintChecker { checks: true }, &[], &[], &[]);
assert_eq!(result, Ok(0));
}
#[test]
fn test_checker_fails() {
let result =
SimpleConstraintChecker::check(&TestConstraintChecker { checks: false }, &[], &[], &[]);
assert_eq!(result, Err(()));
}
}