Skip to content

Commit 953de29

Browse files
anorthabright
andauthored
Fix allowances map keys to be BytesKey (#125)
* Fix allowances map keys to be BytesKey * fix test * Bump frc46_token to version 1.1.0 Co-authored-by: Andrew Bright <[email protected]>
1 parent aa8302e commit 953de29

File tree

7 files changed

+69
-49
lines changed

7 files changed

+69
-49
lines changed

frc46_token/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "frc46_token"
33
description = "Filecoin FRC-0046 fungible token reference implementation"
4-
version = "1.0.0"
4+
version = "1.1.0"
55
license = "MIT OR Apache-2.0"
66
keywords = ["filecoin", "fvm", "token", "frc-0046"]
77
repository = "https://github.com/helix-onchain/filecoin/"

frc46_token/src/token/state.rs

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ pub enum StateInvariantError {
8080
type Result<T> = std::result::Result<T, StateError>;
8181

8282
type Map<'bs, BS, K, V> = Hamt<&'bs BS, V, K>;
83+
type BalanceMap<'bs, BS> = Map<'bs, BS, BytesKey, TokenAmount>;
84+
type AllowanceMap<'bs, BS> = Map<'bs, BS, BytesKey, Cid>;
85+
type OwnerAllowanceMap<'bs, BS> = Map<'bs, BS, BytesKey, TokenAmount>;
8386

8487
/// Token state IPLD structure
8588
#[derive(Serialize_tuple, Deserialize_tuple, PartialEq, Eq, Clone, Debug)]
@@ -112,9 +115,9 @@ impl TokenState {
112115
/// 1 <= hamt_bit_width <= 8.
113116
pub fn new_with_bit_width<BS: Blockstore>(store: &BS, hamt_bit_width: u32) -> Result<Self> {
114117
// Blockstore is still needed to create valid Cids for the Hamts
115-
let empty_balance_map = Hamt::<_, ()>::new_with_bit_width(store, hamt_bit_width).flush()?;
118+
let empty_balance_map = BalanceMap::new_with_bit_width(store, hamt_bit_width).flush()?;
116119
let empty_allowances_map =
117-
Hamt::<_, ()>::new_with_bit_width(store, hamt_bit_width).flush()?;
120+
AllowanceMap::new_with_bit_width(store, hamt_bit_width).flush()?;
118121

119122
Ok(Self {
120123
supply: Default::default(),
@@ -236,11 +239,8 @@ impl TokenState {
236239
}
237240

238241
/// Retrieve the balance map as a HAMT
239-
pub fn get_balance_map<'bs, BS: Blockstore>(
240-
&self,
241-
bs: &'bs BS,
242-
) -> Result<Map<'bs, BS, BytesKey, TokenAmount>> {
243-
Ok(Hamt::load_with_bit_width(&self.balances, bs, self.hamt_bit_width)?)
242+
pub fn get_balance_map<'bs, BS: Blockstore>(&self, bs: &'bs BS) -> Result<BalanceMap<'bs, BS>> {
243+
Ok(BalanceMap::load_with_bit_width(&self.balances, bs, self.hamt_bit_width)?)
244244
}
245245

246246
/// Retrieve the number of token holders
@@ -278,8 +278,8 @@ impl TokenState {
278278
) -> Result<TokenAmount> {
279279
let owner_allowances = self.get_owner_allowance_map(bs, owner)?;
280280
match owner_allowances {
281-
Some(hamt) => {
282-
let maybe_allowance = hamt.get(&operator)?;
281+
Some(map) => {
282+
let maybe_allowance = map.get(&actor_id_key(operator))?;
283283
if let Some(allowance) = maybe_allowance {
284284
return Ok(allowance.clone());
285285
}
@@ -305,39 +305,41 @@ impl TokenState {
305305
let mut global_allowances_map = self.get_allowances_map(bs)?;
306306

307307
// get or create the owner's allowance map
308-
let mut allowance_map = match global_allowances_map.get(&owner)? {
309-
Some(hamt) => Hamt::load_with_bit_width(hamt, bs, self.hamt_bit_width)?,
308+
let owner_key = actor_id_key(owner);
309+
let mut allowance_map = match global_allowances_map.get(&owner_key)? {
310+
Some(cid) => OwnerAllowanceMap::load_with_bit_width(cid, bs, self.hamt_bit_width)?,
310311
None => {
311312
// the owner doesn't have any allowances, and the delta is negative, this is a no-op
312313
if delta.is_negative() {
313314
return Ok(TokenAmount::zero());
314315
}
315316

316317
// else create a new map for the owner
317-
Hamt::<&BS, TokenAmount, ActorID>::new_with_bit_width(bs, self.hamt_bit_width)
318+
OwnerAllowanceMap::new_with_bit_width(bs, self.hamt_bit_width)
318319
}
319320
};
320321

321322
// calculate new allowance (max with zero)
322-
let new_allowance = match allowance_map.get(&operator)? {
323+
let operator_key = actor_id_key(operator);
324+
let new_allowance = match allowance_map.get(&operator_key)? {
323325
Some(existing_allowance) => existing_allowance + delta,
324326
None => (*delta).clone(),
325327
}
326328
.max(TokenAmount::zero());
327329

328330
// if the new allowance is zero, we can remove the entry from the state tree
329331
if new_allowance.is_zero() {
330-
allowance_map.delete(&operator)?;
332+
allowance_map.delete(&operator_key)?;
331333
} else {
332-
allowance_map.set(operator, new_allowance.clone())?;
334+
allowance_map.set(operator_key, new_allowance.clone())?;
333335
}
334336

335337
// if the owner-allowance map is empty, remove it from the global allowances map
336338
if allowance_map.is_empty() {
337-
global_allowances_map.delete(&owner)?;
339+
global_allowances_map.delete(&owner_key)?;
338340
} else {
339341
// else update the global-allowance map
340-
global_allowances_map.set(owner, allowance_map.flush()?)?;
342+
global_allowances_map.set(owner_key, allowance_map.flush()?)?;
341343
}
342344

343345
// update the state with the updated global map
@@ -358,20 +360,22 @@ impl TokenState {
358360
let allowance_map = self.get_owner_allowance_map(bs, owner)?;
359361
if let Some(mut map) = allowance_map {
360362
// revoke the allowance
361-
let old_allowance = match map.delete(&operator)? {
363+
let operator_key = actor_id_key(operator);
364+
let old_allowance = match map.delete(&operator_key)? {
362365
Some((_, amount)) => amount,
363366
None => TokenAmount::zero(),
364367
};
365368

366369
// if the allowance map has become empty it can be dropped entirely
370+
let owner_key = actor_id_key(owner);
367371
if map.is_empty() {
368372
let mut root_allowance_map = self.get_allowances_map(bs)?;
369-
root_allowance_map.delete(&owner)?;
373+
root_allowance_map.delete(&owner_key)?;
370374
self.allowances = root_allowance_map.flush()?;
371375
} else {
372376
let new_cid = map.flush()?;
373377
let mut root_allowance_map = self.get_allowances_map(bs)?;
374-
root_allowance_map.set(owner, new_cid)?;
378+
root_allowance_map.set(owner_key, new_cid)?;
375379
self.allowances = root_allowance_map.flush()?;
376380
}
377381

@@ -397,13 +401,15 @@ impl TokenState {
397401
let mut root_allowances_map = self.get_allowances_map(bs)?;
398402

399403
// get or create the owner's allowance map
400-
let mut allowance_map = match root_allowances_map.get(&owner)? {
401-
Some(hamt) => Hamt::load_with_bit_width(hamt, bs, self.hamt_bit_width)?,
402-
None => Hamt::<&BS, TokenAmount, ActorID>::new_with_bit_width(bs, self.hamt_bit_width),
404+
let owner_key = actor_id_key(owner);
405+
let mut allowance_map = match root_allowances_map.get(&owner_key)? {
406+
Some(cid) => OwnerAllowanceMap::load_with_bit_width(cid, bs, self.hamt_bit_width)?,
407+
None => OwnerAllowanceMap::new_with_bit_width(bs, self.hamt_bit_width),
403408
};
404409

405410
// determine the existing allowance
406-
let old_allowance = match allowance_map.get(&operator)? {
411+
let operator_key = actor_id_key(operator);
412+
let old_allowance = match allowance_map.get(&operator_key)? {
407413
Some(a) => a.clone(),
408414
None => TokenAmount::zero(),
409415
};
@@ -415,9 +421,9 @@ impl TokenState {
415421
}
416422

417423
// set the new allowance
418-
allowance_map.set(operator, amount.clone())?;
424+
allowance_map.set(operator_key, amount.clone())?;
419425
// update the root map
420-
root_allowances_map.set(owner, allowance_map.flush()?)?;
426+
root_allowances_map.set(owner_key, allowance_map.flush()?)?;
421427
// update the state with the updated global map
422428
self.allowances = root_allowances_map.flush()?;
423429

@@ -474,10 +480,12 @@ impl TokenState {
474480
&self,
475481
bs: &'bs BS,
476482
owner: ActorID,
477-
) -> Result<Option<Map<'bs, BS, ActorID, TokenAmount>>> {
483+
) -> Result<Option<OwnerAllowanceMap<'bs, BS>>> {
478484
let allowances_map = self.get_allowances_map(bs)?;
479-
let owner_allowances = match allowances_map.get(&owner)? {
480-
Some(cid) => Some(Hamt::load_with_bit_width(cid, bs, self.hamt_bit_width)?),
485+
let owner_allowances = match allowances_map.get(&actor_id_key(owner))? {
486+
Some(cid) => {
487+
Some(OwnerAllowanceMap::load_with_bit_width(cid, bs, self.hamt_bit_width)?)
488+
}
481489
None => None,
482490
};
483491
Ok(owner_allowances)
@@ -489,8 +497,8 @@ impl TokenState {
489497
pub fn get_allowances_map<'bs, BS: Blockstore>(
490498
&self,
491499
bs: &'bs BS,
492-
) -> Result<Map<'bs, BS, ActorID, Cid>> {
493-
Ok(Hamt::load_with_bit_width(&self.allowances, bs, self.hamt_bit_width)?)
500+
) -> Result<AllowanceMap<'bs, BS>> {
501+
Ok(AllowanceMap::load_with_bit_width(&self.allowances, bs, self.hamt_bit_width)?)
494502
}
495503

496504
/// Checks that the current state obeys all system invariants
@@ -551,38 +559,49 @@ impl TokenState {
551559
// check allowances are all non-negative
552560
let allowances_map = self.get_allowances_map(bs)?;
553561
let res = allowances_map.for_each(|owner, _| {
554-
let allowance_map = self.get_owner_allowance_map(bs, *owner)?;
562+
let owner = match decode_actor_id(owner) {
563+
None => {
564+
bail!("invalid owner key in allowances map")
565+
}
566+
Some(a) => a,
567+
};
568+
let allowance_map = self.get_owner_allowance_map(bs, owner)?;
555569
// check that the allowance map isn't empty
556570
if allowance_map.is_none() {
557-
maybe_err = Some(StateInvariantError::ExplicitEmptyAllowance(*owner));
571+
maybe_err = Some(StateInvariantError::ExplicitEmptyAllowance(owner));
558572
bail!("invariant failed")
559573
}
560574

561575
let allowance_map = allowance_map.unwrap();
562576
allowance_map.for_each(|operator, allowance| {
577+
let operator = match decode_actor_id(operator) {
578+
None => {
579+
bail!("invalid operator key in allowances map")
580+
}
581+
Some(a) => a,
582+
};
583+
563584
// check there's no stored self-stored allowance
564-
if *owner == *operator {
585+
if owner == operator {
565586
maybe_err = Some(StateInvariantError::ExplicitSelfAllowance {
566-
account: *owner,
587+
account: owner,
567588
allowance: allowance.clone(),
568589
});
569590
bail!("invariant failed")
570591
}
571592
// check the allowance isn't negative
572593
if allowance.is_negative() {
573594
maybe_err = Some(StateInvariantError::NegativeAllowance {
574-
owner: *owner,
575-
operator: *operator,
595+
owner,
596+
operator,
576597
allowance: allowance.clone(),
577598
});
578599
bail!("invariant failed")
579600
}
580601
// check there's no explicit zero allowance
581602
if allowance.is_zero() {
582-
maybe_err = Some(StateInvariantError::ExplicitZeroAllowance {
583-
owner: *owner,
584-
operator: *operator,
585-
});
603+
maybe_err =
604+
Some(StateInvariantError::ExplicitZeroAllowance { owner, operator });
586605
bail!("invariant failed")
587606
}
588607
Ok(())
@@ -615,7 +634,7 @@ mod test {
615634
use fvm_shared::{bigint::Zero, ActorID};
616635

617636
use super::TokenState;
618-
use crate::token::state::StateError;
637+
use crate::token::state::{actor_id_key, StateError};
619638

620639
#[test]
621640
fn it_instantiates() {
@@ -750,7 +769,8 @@ mod test {
750769
assert_eq!(returned_allowance, allowance);
751770
// the map entry is cleaned-up
752771
let root_map = state.get_allowances_map(bs).unwrap();
753-
assert!(!root_map.contains_key(&owner).unwrap());
772+
let owner_key = actor_id_key(owner);
773+
assert!(!root_map.contains_key(&owner_key).unwrap());
754774

755775
// can't set negative allowance
756776
let allowance = TokenAmount::from_atto(-50);

testing/fil_token_integration/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cid = { version = "0.8.5", default-features = false }
1010
fvm = { version = "2.0.0-alpha.2", default-features = false }
1111
frcxx_nft = { path = "../../frcxx_nft" }
1212
frc42_dispatch = { path = "../../frc42_dispatch" }
13-
frc46_token = { version = "1.0.0", path = "../../frc46_token" }
13+
frc46_token = { version = "1.1.0", path = "../../frc46_token" }
1414
fvm_integration_tests = "2.0.0-alpha.1"
1515
fvm_ipld_blockstore = "0.1.1"
1616
fvm_ipld_encoding = "0.2.2"

testing/fil_token_integration/actors/basic_receiving_actor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
frc46_token = { version = "1.0.0", path = "../../../../frc46_token" }
7+
frc46_token = { version = "1.1.0", path = "../../../../frc46_token" }
88
frc42_dispatch = { path = "../../../../frc42_dispatch" }
99
fvm_ipld_blockstore = "0.1.1"
1010
fvm_ipld_encoding = "0.2.2"

testing/fil_token_integration/actors/basic_token_actor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ fvm_ipld_blockstore = { version = "0.1.1" }
1111
fvm_ipld_encoding = { version = "0.2.2" }
1212
fvm_sdk = { version = "2.0.0-alpha.2" }
1313
fvm_shared = { version = "2.0.0-alpha.2" }
14-
frc46_token = { version = "1.0.0", path = "../../../../frc46_token" }
14+
frc46_token = { version = "1.1.0", path = "../../../../frc46_token" }
1515
num-traits = { version = "0.2.15" }
1616
serde = { version = "1.0.136", features = ["derive"] }
1717
serde_tuple = { version = "0.5.0" }

testing/fil_token_integration/actors/basic_transfer_actor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
cid = { version = "0.8.5", default-features = false }
8-
frc46_token = { version = "1.0.0", path = "../../../../frc46_token" }
8+
frc46_token = { version = "1.1.0", path = "../../../../frc46_token" }
99
frc42_dispatch = { path = "../../../../frc42_dispatch" }
1010
fvm_ipld_blockstore = { version = "0.1.1" }
1111
fvm_ipld_encoding = { version = "0.2.2" }

testing/fil_token_integration/actors/test_actor/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
cid = { version = "0.8.5", default-features = false }
8-
frc46_token = { version = "1.0.0", path = "../../../../frc46_token" }
8+
frc46_token = { version = "1.1.0", path = "../../../../frc46_token" }
99
frc42_dispatch = { path = "../../../../frc42_dispatch" }
1010
fvm_ipld_blockstore = { version = "0.1.1" }
1111
fvm_ipld_encoding = { version = "0.2.2" }

0 commit comments

Comments
 (0)