@@ -26,9 +26,28 @@ impl<const ID: u8> Cash for Coin<ID> {
26
26
const ID : u8 = ID ;
27
27
}
28
28
29
- // use log::info;
29
+ #[ derive(
30
+ Serialize ,
31
+ Deserialize ,
32
+ PartialEq ,
33
+ Eq ,
34
+ PartialOrd ,
35
+ Ord ,
36
+ Clone ,
37
+ Encode ,
38
+ Decode ,
39
+ Hash ,
40
+ Debug ,
41
+ TypeInfo ,
42
+ ) ]
43
+ /// A typical spend transaction where some coins are consumed and others are created.
44
+ /// Input value must exceed output value. The difference is burned and reflected in the
45
+ /// transaction's priority.
46
+ ///
47
+ /// This is the primary way to move money around the economy. Allows managing your own
48
+ /// coins as well as paying others.
49
+ pub struct Spend < const ID : u8 > ;
30
50
31
- /// The main constraint checker for the money piece. Allows spending and minting tokens.
32
51
#[ derive(
33
52
Serialize ,
34
53
Deserialize ,
@@ -43,16 +62,9 @@ impl<const ID: u8> Cash for Coin<ID> {
43
62
Debug ,
44
63
TypeInfo ,
45
64
) ]
46
- pub enum MoneyConstraintChecker < const ID : u8 > {
47
- /// A typical spend transaction where some coins are consumed and others are created.
48
- /// Input value must exceed output value. The difference is burned and reflected in the
49
- /// transaction's priority.
50
- Spend ,
51
- /// A mint transaction that creates no coins out of the void. In a real-world chain,
52
- /// this should be protected somehow, or not included at all. For now it is publicly
53
- /// available. I'm adding it to explore multiple validation paths in a single piece.
54
- Mint ,
55
- }
65
+ /// A mint transaction that creates no coins out of the void. In a real-world chain,
66
+ /// this should be protected somehow, or not included at all.
67
+ pub struct Mint < const ID : u8 > ;
56
68
57
69
/// A single coin in the fungible money system.
58
70
/// A new-type wrapper around a `u128` value.
@@ -82,13 +94,15 @@ impl<const ID: u8> Coin<ID> {
82
94
where
83
95
V : Verifier ,
84
96
OV : Verifier + From < V > ,
85
- OC : tuxedo_core:: ConstraintChecker < OV > + From < MoneyConstraintChecker < ID > > ,
97
+ // FIXME https://github.com/Off-Narrative-Labs/Tuxedo/issues/177
98
+ // Do these bounds respect nested piece aggregation?
99
+ OC : tuxedo_core:: ConstraintChecker < OV > + From < Mint < ID > > ,
86
100
{
87
101
Transaction {
88
102
inputs : vec ! [ ] ,
89
103
peeks : vec ! [ ] ,
90
104
outputs : vec ! [ ( Self :: new( amt) , v) . into( ) ] ,
91
- checker : MoneyConstraintChecker :: Mint . into ( ) ,
105
+ checker : Mint . into ( ) ,
92
106
}
93
107
}
94
108
}
@@ -135,7 +149,7 @@ pub enum ConstraintCheckerError {
135
149
ZeroValueCoin ,
136
150
}
137
151
138
- impl < const ID : u8 > SimpleConstraintChecker for MoneyConstraintChecker < ID > {
152
+ impl < const ID : u8 > SimpleConstraintChecker for Spend < ID > {
139
153
type Error = ConstraintCheckerError ;
140
154
141
155
fn check (
@@ -144,78 +158,84 @@ impl<const ID: u8> SimpleConstraintChecker for MoneyConstraintChecker<ID> {
144
158
_peeks : & [ DynamicallyTypedData ] ,
145
159
output_data : & [ DynamicallyTypedData ] ,
146
160
) -> Result < TransactionPriority , Self :: Error > {
147
- match & self {
148
- Self :: Spend => {
149
- // Check that we are consuming at least one input
150
- ensure ! (
151
- !input_data. is_empty( ) ,
152
- ConstraintCheckerError :: SpendingNothing
153
- ) ;
154
-
155
- let mut total_input_value: u128 = 0 ;
156
- let mut total_output_value: u128 = 0 ;
157
-
158
- // Check that sum of input values < output values
159
- for input in input_data {
160
- let utxo_value = input
161
- . extract :: < Coin < ID > > ( )
162
- . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
163
- . 0 ;
164
- total_input_value = total_input_value
165
- . checked_add ( utxo_value)
166
- . ok_or ( ConstraintCheckerError :: ValueOverflow ) ?;
167
- }
168
-
169
- for utxo in output_data {
170
- let utxo_value = utxo
171
- . extract :: < Coin < ID > > ( )
172
- . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
173
- . 0 ;
174
- ensure ! ( utxo_value > 0 , ConstraintCheckerError :: ZeroValueCoin ) ;
175
- total_output_value = total_output_value
176
- . checked_add ( utxo_value)
177
- . ok_or ( ConstraintCheckerError :: ValueOverflow ) ?;
178
- }
179
-
180
- ensure ! (
181
- total_output_value <= total_input_value,
182
- ConstraintCheckerError :: OutputsExceedInputs
183
- ) ;
184
-
185
- // Priority is based on how many token are burned
186
- // Type stuff is kinda ugly. Maybe division would be better?
187
- let burned = total_input_value - total_output_value;
188
- Ok ( if burned < u64:: max_value ( ) as u128 {
189
- burned as u64
190
- } else {
191
- u64:: max_value ( )
192
- } )
193
- }
194
- Self :: Mint => {
195
- // Make sure there are no inputs being consumed
196
- ensure ! (
197
- input_data. is_empty( ) ,
198
- ConstraintCheckerError :: MintingWithInputs
199
- ) ;
200
-
201
- // Make sure there is at least one output being minted
202
- ensure ! (
203
- !output_data. is_empty( ) ,
204
- ConstraintCheckerError :: MintingNothing
205
- ) ;
206
-
207
- // Make sure the outputs are the right type
208
- for utxo in output_data {
209
- let utxo_value = utxo
210
- . extract :: < Coin < ID > > ( )
211
- . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
212
- . 0 ;
213
- ensure ! ( utxo_value > 0 , ConstraintCheckerError :: ZeroValueCoin ) ;
214
- }
215
-
216
- // No priority for minting
217
- Ok ( 0 )
218
- }
161
+ // Check that we are consuming at least one input
162
+ ensure ! (
163
+ !input_data. is_empty( ) ,
164
+ ConstraintCheckerError :: SpendingNothing
165
+ ) ;
166
+
167
+ let mut total_input_value: u128 = 0 ;
168
+ let mut total_output_value: u128 = 0 ;
169
+
170
+ // Check that sum of input values < output values
171
+ for input in input_data {
172
+ let utxo_value = input
173
+ . extract :: < Coin < ID > > ( )
174
+ . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
175
+ . 0 ;
176
+ total_input_value = total_input_value
177
+ . checked_add ( utxo_value)
178
+ . ok_or ( ConstraintCheckerError :: ValueOverflow ) ?;
219
179
}
180
+
181
+ for utxo in output_data {
182
+ let utxo_value = utxo
183
+ . extract :: < Coin < ID > > ( )
184
+ . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
185
+ . 0 ;
186
+ ensure ! ( utxo_value > 0 , ConstraintCheckerError :: ZeroValueCoin ) ;
187
+ total_output_value = total_output_value
188
+ . checked_add ( utxo_value)
189
+ . ok_or ( ConstraintCheckerError :: ValueOverflow ) ?;
190
+ }
191
+
192
+ ensure ! (
193
+ total_output_value <= total_input_value,
194
+ ConstraintCheckerError :: OutputsExceedInputs
195
+ ) ;
196
+
197
+ // Priority is based on how many token are burned
198
+ // Type stuff is kinda ugly. Maybe division would be better?
199
+ let burned = total_input_value - total_output_value;
200
+ Ok ( if burned < u64:: max_value ( ) as u128 {
201
+ burned as u64
202
+ } else {
203
+ u64:: max_value ( )
204
+ } )
205
+ }
206
+ }
207
+
208
+ impl < const ID : u8 > SimpleConstraintChecker for Mint < ID > {
209
+ type Error = ConstraintCheckerError ;
210
+
211
+ fn check (
212
+ & self ,
213
+ input_data : & [ DynamicallyTypedData ] ,
214
+ _peeks : & [ DynamicallyTypedData ] ,
215
+ output_data : & [ DynamicallyTypedData ] ,
216
+ ) -> Result < TransactionPriority , Self :: Error > {
217
+ // Make sure there are no inputs being consumed
218
+ ensure ! (
219
+ input_data. is_empty( ) ,
220
+ ConstraintCheckerError :: MintingWithInputs
221
+ ) ;
222
+
223
+ // Make sure there is at least one output being minted
224
+ ensure ! (
225
+ !output_data. is_empty( ) ,
226
+ ConstraintCheckerError :: MintingNothing
227
+ ) ;
228
+
229
+ // Make sure the outputs are the right type
230
+ for utxo in output_data {
231
+ let utxo_value = utxo
232
+ . extract :: < Coin < ID > > ( )
233
+ . map_err ( |_| ConstraintCheckerError :: BadlyTyped ) ?
234
+ . 0 ;
235
+ ensure ! ( utxo_value > 0 , ConstraintCheckerError :: ZeroValueCoin ) ;
236
+ }
237
+
238
+ // No priority for minting
239
+ Ok ( 0 )
220
240
}
221
241
}
0 commit comments