Skip to content

Commit a78f99a

Browse files
authored
Implement evictions (#196)
* Eviction support in Core * Update inherents to use evictions * Complete PoE piece (but not tests yet)
1 parent 9a18761 commit a78f99a

File tree

21 files changed

+379
-171
lines changed

21 files changed

+379
-171
lines changed

tuxedo-core/aggregator/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,13 @@ pub fn tuxedo_constraint_checker(_attrs: TokenStream, body: TokenStream) -> Toke
253253
fn check (
254254
&self,
255255
inputs: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
256+
evicted_inputs: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
256257
peeks: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
257258
outputs: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
258259
) -> Result<TransactionPriority, Self::Error> {
259260
match self {
260261
#(
261-
Self::#variants5(inner) => inner.check(inputs, peeks, outputs).map_err(|e| Self::Error::#variants5(e)),
262+
Self::#variants5(inner) => inner.check(inputs, evicted_inputs, peeks, outputs).map_err(|e| Self::Error::#variants5(e)),
262263
)*
263264
}
264265
}

tuxedo-core/src/constraint_checker.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ pub trait SimpleConstraintChecker: Debug + Encode + Decode + Clone {
4848
fn check(
4949
&self,
5050
input_data: &[DynamicallyTypedData],
51+
evicted_input_data: &[DynamicallyTypedData],
5152
peek_data: &[DynamicallyTypedData],
5253
output_data: &[DynamicallyTypedData],
5354
) -> Result<TransactionPriority, Self::Error>;
@@ -76,6 +77,7 @@ pub trait ConstraintChecker: Debug + Encode + Decode + Clone {
7677
fn check(
7778
&self,
7879
input_data: &[DynamicallyTypedData],
80+
evicted_input_data: &[DynamicallyTypedData],
7981
peek_data: &[DynamicallyTypedData],
8082
output_data: &[DynamicallyTypedData],
8183
) -> Result<TransactionPriority, Self::Error>;
@@ -116,10 +118,11 @@ impl<T: SimpleConstraintChecker> ConstraintChecker for T {
116118
fn check(
117119
&self,
118120
input_data: &[DynamicallyTypedData],
121+
evicted_input_data: &[DynamicallyTypedData],
119122
peek_data: &[DynamicallyTypedData],
120123
output_data: &[DynamicallyTypedData],
121124
) -> Result<TransactionPriority, Self::Error> {
122-
SimpleConstraintChecker::check(self, input_data, peek_data, output_data)
125+
SimpleConstraintChecker::check(self, input_data, evicted_input_data, peek_data, output_data)
123126
}
124127

125128
fn is_inherent(&self) -> bool {
@@ -177,6 +180,7 @@ pub mod testing {
177180
fn check(
178181
&self,
179182
_input_data: &[DynamicallyTypedData],
183+
_evicted_input_data: &[DynamicallyTypedData],
180184
_peek_data: &[DynamicallyTypedData],
181185
_output_data: &[DynamicallyTypedData],
182186
) -> Result<TransactionPriority, ()> {
@@ -217,7 +221,7 @@ pub mod testing {
217221
checks: true,
218222
inherent: false,
219223
}
220-
.check(&[], &[], &[]);
224+
.check(&[], &[], &[], &[]);
221225
assert_eq!(result, Ok(0));
222226
}
223227

@@ -227,7 +231,7 @@ pub mod testing {
227231
checks: false,
228232
inherent: false,
229233
}
230-
.check(&[], &[], &[]);
234+
.check(&[], &[], &[], &[]);
231235
assert_eq!(result, Err(()));
232236
}
233237
}

tuxedo-core/src/executive.rs

+26-18
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
dynamic_typing::DynamicallyTypedData,
1212
ensure,
1313
inherents::PARENT_INHERENT_IDENTIFIER,
14-
types::{DispatchResult, OutputRef, Transaction, UtxoError},
14+
types::{DispatchResult, OutputRef, RedemptionStrategy, Transaction, UtxoError},
1515
utxo_set::TransparentUtxoSet,
1616
verifier::Verifier,
1717
EXTRINSIC_KEY, HEADER_KEY, LOG_TARGET,
@@ -69,26 +69,34 @@ where
6969
// This will be passed to the verifiers
7070
let mut stripped = transaction.clone();
7171
for input in stripped.inputs.iter_mut() {
72-
input.redeemer = Vec::new();
72+
input.redeemer = Default::default();
7373
}
7474
let stripped_encoded = stripped.encode();
7575

7676
// Check that the verifiers of all inputs are satisfied
7777
// Keep a Vec of the input data for passing to the constraint checker
7878
// Keep track of any missing inputs for use in the tagged transaction pool
7979
let mut input_data = Vec::new();
80+
let mut evicted_input_data = Vec::new();
8081
let mut missing_inputs = Vec::new();
8182
for input in transaction.inputs.iter() {
8283
if let Some(input_utxo) = TransparentUtxoSet::<V>::peek_utxo(&input.output_ref) {
83-
let redeemer = V::Redeemer::decode(&mut &input.redeemer[..])
84-
.map_err(|_| UtxoError::VerifierError)?;
85-
ensure!(
86-
input_utxo
87-
.verifier
88-
.verify(&stripped_encoded, Self::block_height(), &redeemer),
89-
UtxoError::VerifierError
90-
);
91-
input_data.push(input_utxo.payload);
84+
match input.redeemer {
85+
RedemptionStrategy::Redemption(ref redeemer) => {
86+
let redeemer = V::Redeemer::decode(&mut &redeemer[..])
87+
.map_err(|_| UtxoError::VerifierError)?;
88+
ensure!(
89+
input_utxo.verifier.verify(
90+
&stripped_encoded,
91+
Self::block_height(),
92+
&redeemer
93+
),
94+
UtxoError::VerifierError
95+
);
96+
input_data.push(input_utxo.payload);
97+
}
98+
RedemptionStrategy::Eviction => evicted_input_data.push(input_utxo.payload),
99+
}
92100
} else {
93101
missing_inputs.push(input.output_ref.clone().encode());
94102
}
@@ -163,7 +171,7 @@ where
163171
// Call the constraint checker
164172
transaction
165173
.checker
166-
.check(&input_data, &peek_data, &output_data)
174+
.check(&input_data, &evicted_input_data, &peek_data, &output_data)
167175
.map_err(UtxoError::ConstraintCheckerError)?;
168176

169177
// Return the valid transaction
@@ -671,7 +679,7 @@ mod tests {
671679
.execute_with(|| {
672680
let input = Input {
673681
output_ref,
674-
redeemer: Vec::new(),
682+
redeemer: Default::default(),
675683
};
676684

677685
let tx = TestTransactionBuilder::default()
@@ -737,7 +745,7 @@ mod tests {
737745
let output_ref = mock_output_ref(0, 0);
738746
let input = Input {
739747
output_ref: output_ref.clone(),
740-
redeemer: Vec::new(),
748+
redeemer: Default::default(),
741749
};
742750

743751
let tx = TestTransactionBuilder::default()
@@ -783,7 +791,7 @@ mod tests {
783791
.execute_with(|| {
784792
let input = Input {
785793
output_ref,
786-
redeemer: Vec::new(),
794+
redeemer: Default::default(),
787795
};
788796

789797
let tx = TestTransactionBuilder::default()
@@ -831,7 +839,7 @@ mod tests {
831839
.execute_with(|| {
832840
let input = Input {
833841
output_ref,
834-
redeemer: Vec::new(),
842+
redeemer: Default::default(),
835843
};
836844

837845
let tx = TestTransactionBuilder::default()
@@ -901,7 +909,7 @@ mod tests {
901909
let output_ref = mock_output_ref(0, 0);
902910
let input = Input {
903911
output_ref: output_ref.clone(),
904-
redeemer: Vec::new(),
912+
redeemer: Default::default(),
905913
};
906914

907915
let tx = TestTransactionBuilder::default()
@@ -939,7 +947,7 @@ mod tests {
939947
.execute_with(|| {
940948
let input = Input {
941949
output_ref: output_ref.clone(),
942-
redeemer: Vec::new(),
950+
redeemer: Default::default(),
943951
};
944952

945953
let tx = TestTransactionBuilder::default()

tuxedo-core/src/inherents.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,17 @@ impl<C: SimpleConstraintChecker + InherentHooks + 'static> ConstraintChecker
165165
fn check(
166166
&self,
167167
input_data: &[crate::dynamic_typing::DynamicallyTypedData],
168+
evicted_input_data: &[crate::dynamic_typing::DynamicallyTypedData],
168169
peek_data: &[crate::dynamic_typing::DynamicallyTypedData],
169170
output_data: &[crate::dynamic_typing::DynamicallyTypedData],
170171
) -> Result<sp_runtime::transaction_validity::TransactionPriority, Self::Error> {
171-
SimpleConstraintChecker::check(&self.0, input_data, peek_data, output_data)
172+
SimpleConstraintChecker::check(
173+
&self.0,
174+
input_data,
175+
evicted_input_data,
176+
peek_data,
177+
output_data,
178+
)
172179
}
173180

174181
fn is_inherent(&self) -> bool {

tuxedo-core/src/types.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,28 @@ where
143143
pub struct Input {
144144
/// a reference to the output being consumed
145145
pub output_ref: OutputRef,
146-
// Eg the signature
147-
pub redeemer: Vec<u8>,
146+
/// A means of showing that this input data can be used.
147+
/// It is most often a proof such as a signature, but could also be a forceful eviction.
148+
pub redeemer: RedemptionStrategy,
149+
}
150+
151+
//TODO Consider making this enum generic over the redeemer type so there is not an additional decoding necessary.
152+
// This would percolate up though. For example input would also have to be generic over the redeemer type.
153+
// IDK if it is appropriate to be making so many types non-opaque??
154+
/// An input can be consumed in two way. It can be redeemed normally (probably with some signature, or proof) or it
155+
/// can be evicted. This enum is isomorphic to `Option<Vec<u8>>` but has more meaningful names.
156+
#[derive(Serialize, Deserialize, Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
157+
pub enum RedemptionStrategy {
158+
/// The input is being consumed in the normal way with a signature or other proof provided by the spender.
159+
Redemption(Vec<u8>),
160+
/// The input is being forcefully evicted without satisfying its Verifier.
161+
Eviction,
162+
}
163+
164+
impl Default for RedemptionStrategy {
165+
fn default() -> Self {
166+
Self::Redemption(Vec::new())
167+
}
148168
}
149169

150170
#[derive(Debug, PartialEq, Eq)]

tuxedo-template-runtime/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,11 @@ impl tuxedo_core::SimpleConstraintChecker for DummyParachainInfo {
242242
fn check(
243243
&self,
244244
_input_data: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
245+
_evicted_input_data: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
245246
_peeks: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
246247
_output_data: &[tuxedo_core::dynamic_typing::DynamicallyTypedData],
247248
) -> Result<TransactionPriority, ()> {
248-
Ok(0)
249+
panic!("Transactions should not be sent to the dummy parachain info piece.")
249250
}
250251
}
251252

wallet/src/amoeba.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ pub async fn amoeba_demo(client: &HttpClient) -> anyhow::Result<()> {
6666
let mitosis_tx = Transaction {
6767
inputs: vec![Input {
6868
output_ref: eve_ref,
69-
redeemer: Vec::new(),
69+
redeemer: Default::default(),
7070
}],
7171
peeks: Vec::new(),
7272
outputs: vec![

wallet/src/money.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use sled::Db;
1414
use sp_core::sr25519::Public;
1515
use sp_runtime::traits::{BlakeTwo256, Hash};
1616
use tuxedo_core::{
17-
types::{Input, Output, OutputRef},
17+
types::{Input, Output, OutputRef, RedemptionStrategy},
1818
verifier::Sr25519Signature,
1919
};
2020

@@ -120,7 +120,7 @@ pub async fn spend_coins(
120120
get_coin_from_storage(output_ref, client).await?;
121121
transaction.inputs.push(Input {
122122
output_ref: output_ref.clone(),
123-
redeemer: vec![], // We will sign the total transaction so this should be empty
123+
redeemer: Default::default(), // We will sign the total transaction so this should be empty
124124
});
125125
}
126126

@@ -143,7 +143,7 @@ pub async fn spend_coins(
143143
};
144144

145145
// insert the proof
146-
input.redeemer = redeemer;
146+
input.redeemer = RedemptionStrategy::Redemption(redeemer);
147147
}
148148

149149
// Send the transaction

wardrobe/amoeba/src/lib.rs

+23
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ pub enum ConstraintCheckerError {
4444
BadlyTypedInput,
4545
/// An output data has the wrong type.
4646
BadlyTypedOutput,
47+
/// The Amoeba piece does not allow any evictions at all.
48+
NoEvictionsAllowed,
4749

4850
/// Amoeba creation requires a new amoeba to be created, but none was provided.
4951
CreatedNothing,
@@ -87,9 +89,16 @@ impl SimpleConstraintChecker for AmoebaMitosis {
8789
fn check(
8890
&self,
8991
input_data: &[DynamicallyTypedData],
92+
evicted_input_data: &[DynamicallyTypedData],
9093
_peeks: &[DynamicallyTypedData],
9194
output_data: &[DynamicallyTypedData],
9295
) -> Result<TransactionPriority, ConstraintCheckerError> {
96+
// Can't evict anything
97+
ensure!(
98+
evicted_input_data.is_empty(),
99+
ConstraintCheckerError::NoEvictionsAllowed
100+
);
101+
93102
// Make sure there is exactly one mother.
94103
ensure!(
95104
input_data.len() == 1,
@@ -145,9 +154,16 @@ impl SimpleConstraintChecker for AmoebaDeath {
145154
fn check(
146155
&self,
147156
input_data: &[DynamicallyTypedData],
157+
evicted_input_data: &[DynamicallyTypedData],
148158
_peeks: &[DynamicallyTypedData],
149159
output_data: &[DynamicallyTypedData],
150160
) -> Result<TransactionPriority, Self::Error> {
161+
// Can't evict anything
162+
ensure!(
163+
evicted_input_data.is_empty(),
164+
ConstraintCheckerError::NoEvictionsAllowed
165+
);
166+
151167
// Make sure there is a single victim
152168
ensure!(!input_data.is_empty(), ConstraintCheckerError::NoVictim);
153169
ensure!(
@@ -184,9 +200,16 @@ impl SimpleConstraintChecker for AmoebaCreation {
184200
fn check(
185201
&self,
186202
input_data: &[DynamicallyTypedData],
203+
evicted_input_data: &[DynamicallyTypedData],
187204
_peeks: &[DynamicallyTypedData],
188205
output_data: &[DynamicallyTypedData],
189206
) -> Result<TransactionPriority, Self::Error> {
207+
// Can't evict anything
208+
ensure!(
209+
evicted_input_data.is_empty(),
210+
ConstraintCheckerError::NoEvictionsAllowed
211+
);
212+
190213
// Make sure there is a single created amoeba
191214
ensure!(
192215
!output_data.is_empty(),

0 commit comments

Comments
 (0)