1
- use anyhow:: anyhow;
2
- use anyhow:: bail;
3
- use anyhow:: Result ;
4
1
use cid:: multihash:: Code ;
5
2
use cid:: Cid ;
6
-
7
3
use fvm_ipld_blockstore:: Block ;
8
4
use fvm_ipld_blockstore:: Blockstore as IpldStore ;
9
5
use fvm_ipld_encoding:: tuple:: * ;
10
6
use fvm_ipld_encoding:: Cbor ;
11
7
use fvm_ipld_encoding:: CborStore ;
12
8
use fvm_ipld_encoding:: DAG_CBOR ;
9
+ use fvm_ipld_hamt:: Error as HamtError ;
13
10
use fvm_ipld_hamt:: Hamt ;
14
11
use fvm_shared:: bigint:: bigint_ser;
15
12
use fvm_shared:: bigint:: bigint_ser:: BigIntDe ;
16
13
use fvm_shared:: bigint:: Zero ;
17
14
use fvm_shared:: econ:: TokenAmount ;
18
15
use fvm_shared:: ActorID ;
19
16
use num_traits:: Signed ;
17
+ use thiserror:: Error ;
20
18
21
19
const HAMT_BIT_WIDTH : u32 = 5 ;
22
20
21
+ #[ derive( Error , Debug ) ]
22
+ pub enum StateError {
23
+ #[ error( "ipld hamt error: {0}" ) ]
24
+ IpldHamt ( #[ from] HamtError ) ,
25
+ #[ error( "missing state at cid: {0}" ) ]
26
+ MissingState ( Cid ) ,
27
+ #[ error( "underlying serialization error: {0}" ) ]
28
+ Serialization ( String ) ,
29
+ #[ error(
30
+ "negative balance caused by subtracting {delta:?} from {owner:?}'s balance of {balance:?}"
31
+ ) ]
32
+ NegativeBalance {
33
+ owner : ActorID ,
34
+ balance : TokenAmount ,
35
+ delta : TokenAmount ,
36
+ } ,
37
+ #[ error(
38
+ "{spender:?} attempted to utilise {delta:?} of allowance {allowance:?} set by {owner:?}"
39
+ ) ]
40
+ InsufficentAllowance {
41
+ owner : ActorID ,
42
+ spender : ActorID ,
43
+ allowance : TokenAmount ,
44
+ delta : TokenAmount ,
45
+ } ,
46
+ }
47
+
48
+ type Result < T > = std:: result:: Result < T , StateError > ;
49
+
23
50
/// Token state IPLD structure
24
51
#[ derive( Serialize_tuple , Deserialize_tuple , PartialEq , Clone , Debug ) ]
25
52
pub struct TokenState {
@@ -37,9 +64,8 @@ pub struct TokenState {
37
64
///
38
65
/// This is a simple wrapper of state and in general does not account for token protocol level
39
66
/// checks such as ensuring necessary approvals are enforced during transfers. This is left for the
40
- /// caller to handle. However, some invariants such as enforcing non-negative balances, allowances
41
- /// and total supply are enforced. Furthermore, this layer returns errors if any of the underlying
42
- /// arithmetic overflows.
67
+ /// caller to handle. However, some invariants such as non-negative balances, allowances and total
68
+ /// supply are enforced.
43
69
///
44
70
/// Some methods on TokenState require the caller to pass in a blockstore implementing the Clone
45
71
/// trait. It is assumed that when cloning the blockstore implementation does a "shallow-clone"
@@ -63,24 +89,24 @@ impl TokenState {
63
89
// Load the actor state from the state tree.
64
90
match bs. get_cbor :: < Self > ( cid) {
65
91
Ok ( Some ( state) ) => Ok ( state) ,
66
- Ok ( None ) => Err ( anyhow ! ( "no state at this cid {:?}" , cid) ) ,
67
- Err ( err) => Err ( anyhow ! ( "failed to get state: {}" , err) ) ,
92
+ Ok ( None ) => Err ( StateError :: MissingState ( * cid) ) ,
93
+ Err ( err) => Err ( StateError :: Serialization ( err. to_string ( ) ) ) ,
68
94
}
69
95
}
70
96
71
97
/// Saves the current state to the blockstore, returning the cid
72
98
pub fn save < BS : IpldStore > ( & self , bs : & BS ) -> Result < Cid > {
73
99
let serialized = match fvm_ipld_encoding:: to_vec ( self ) {
74
100
Ok ( s) => s,
75
- Err ( err) => return Err ( anyhow ! ( "failed to serialize state: {:?}" , err) ) ,
101
+ Err ( err) => return Err ( StateError :: Serialization ( err. to_string ( ) ) ) ,
76
102
} ;
77
103
let block = Block {
78
104
codec : DAG_CBOR ,
79
105
data : serialized,
80
106
} ;
81
107
let cid = match bs. put ( Code :: Blake2b256 , & block) {
82
108
Ok ( cid) => cid,
83
- Err ( err) => return Err ( anyhow ! ( "failed to store initial state: {:}" , err) ) ,
109
+ Err ( err) => return Err ( StateError :: Serialization ( err. to_string ( ) ) ) ,
84
110
} ;
85
111
Ok ( cid)
86
112
}
@@ -126,11 +152,11 @@ impl TokenState {
126
152
127
153
// if the new_balance is negative, return an error
128
154
if new_balance. is_negative ( ) {
129
- bail ! (
130
- "resulting balance was negative adding {:?} to {:?}" ,
131
- delta,
132
- balance . unwrap_or ( & BigIntDe ( TokenAmount :: zero ( ) ) ) . 0
133
- ) ;
155
+ return Err ( StateError :: NegativeBalance {
156
+ balance : new_balance ,
157
+ delta : delta . clone ( ) ,
158
+ owner ,
159
+ } ) ;
134
160
}
135
161
136
162
balance_map. set ( owner, BigIntDe ( new_balance. clone ( ) ) ) ?;
@@ -144,30 +170,19 @@ impl TokenState {
144
170
& self ,
145
171
bs : & BS ,
146
172
) -> Result < Hamt < BS , BigIntDe , ActorID > > {
147
- match Hamt :: < BS , BigIntDe , ActorID > :: load_with_bit_width (
173
+ Ok ( Hamt :: < BS , BigIntDe , ActorID > :: load_with_bit_width (
148
174
& self . balances ,
149
175
( * bs) . clone ( ) ,
150
176
HAMT_BIT_WIDTH ,
151
- ) {
152
- Ok ( map) => Ok ( map) ,
153
- Err ( err) => return Err ( anyhow ! ( "failed to load balances hamt: {:?}" , err) ) ,
154
- }
177
+ ) ?)
155
178
}
156
179
157
180
/// Increase the total supply by the specified value
158
181
///
159
- /// The requested amount must be non-negative.
160
- /// Returns an error if the total supply overflows, else returns the new total supply
161
- pub fn increase_supply ( & mut self , value : & TokenAmount ) -> Result < TokenAmount > {
162
- let new_supply = self . supply . checked_add ( value) . ok_or_else ( || {
163
- anyhow ! (
164
- "Overflow when adding {} to the total_supply of {}" ,
165
- value,
166
- self . supply
167
- )
168
- } ) ?;
169
- self . supply = new_supply. clone ( ) ;
170
- Ok ( new_supply)
182
+ /// The requested amount must be non-negative. Returns the new total supply
183
+ pub fn increase_supply ( & mut self , value : & TokenAmount ) -> Result < & TokenAmount > {
184
+ self . supply += value;
185
+ Ok ( & self . supply )
171
186
}
172
187
173
188
/// Get the allowance that an owner has approved for a spender
@@ -296,25 +311,17 @@ impl TokenState {
296
311
return Ok ( current_allowance) ;
297
312
}
298
313
299
- let new_allowance = current_allowance. checked_sub ( value) . ok_or_else ( || {
300
- anyhow ! (
301
- "Overflow when subtracting {} from {}'s allowance of {}" ,
302
- value,
303
- owner,
304
- current_allowance
305
- )
306
- } ) ?;
307
-
308
- if new_allowance. is_negative ( ) {
309
- return Err ( anyhow ! (
310
- "Attempted to use {} of {}'s tokens from {}'s allowance of {}" ,
311
- value,
314
+ if current_allowance. lt ( value) {
315
+ return Err ( StateError :: InsufficentAllowance {
312
316
owner,
313
317
spender,
314
- current_allowance
315
- ) ) ;
318
+ allowance : current_allowance,
319
+ delta : value. clone ( ) ,
320
+ } ) ;
316
321
}
317
322
323
+ let new_allowance = current_allowance - value;
324
+
318
325
// TODO: helper function to set a new allowance and flush hamts
319
326
let owner_allowances = self . get_owner_allowance_map ( bs, owner) ?;
320
327
// to reach here, allowance must have been previously non zero; so safe to assume the map exists
@@ -353,12 +360,11 @@ impl TokenState {
353
360
///
354
361
/// Gets a HAMT with CIDs linking to other HAMTs
355
362
fn get_allowances_map < BS : IpldStore + Clone > ( & self , bs : & BS ) -> Result < Hamt < BS , Cid , ActorID > > {
356
- Hamt :: < BS , Cid , ActorID > :: load_with_bit_width (
363
+ Ok ( Hamt :: < BS , Cid , ActorID > :: load_with_bit_width (
357
364
& self . allowances ,
358
365
( * bs) . clone ( ) ,
359
366
HAMT_BIT_WIDTH ,
360
- )
361
- . map_err ( |e| anyhow ! ( "failed to load base allowances map {}" , e) )
367
+ ) ?)
362
368
}
363
369
}
364
370
0 commit comments