Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor token state management #25

Merged
merged 3 commits into from
Jul 25, 2022
Merged

refactor token state management #25

merged 3 commits into from
Jul 25, 2022

Conversation

alexytsu
Copy link
Collaborator

@alexytsu alexytsu commented Jul 21, 2022

Before addressing the cleanup of tests and additional test-cases from the feedback in #24, I wanted to refactor the API provided by Token for loading and saving itself to the blockstore.

There was some awkwardness noted around the Token::new pattern. This change addresses that with the following options:

  • New tokens are created with Token::new
  • Existing tokens are loaded from state with Token::load

Previously, every call to a method on Token would cause state to be loaded and written to the blockstore. Now, by holding a TokenState rather than a Cid pointing to it, we can cache state changes in memory. This pushes the onus of persistance onto the library consumer, requiring a call to Token::flush to persist state changes into the blockstore. This should be a more flexible API allowing greater control of when expensive read/writes occur and enables batching of transactions.

This incidentally highlights a previously uncaught bug in transfer and burn which made multiple sequential calls to change TokenState. If any step within the transaction fails, previous changes would need to be reverted. This is much easier done if changes are made in memory rather than being committed to the blockstore. To help this, a Token::transaction method is introduced to help us write atomic transactions.

@alexytsu alexytsu requested review from abright and anorth July 21, 2022 07:01
@alexytsu
Copy link
Collaborator Author

#16

By putting the responsibility of calling flush on the consumers, they can implement their own batching methods

fil_fungible_token/src/token/mod.rs Outdated Show resolved Hide resolved
fil_fungible_token/src/token/mod.rs Outdated Show resolved Hide resolved
///
/// If the closure returns an error, the transaction is dropped atomically and no change is
/// observed on token state.
pub fn transaction<F, Res>(&mut self, f: F) -> Result<Res>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting but not quite perfect.

  1. Ideally don't expose the token state directly to Token users
  2. Users can still use the API outside a transaction.

I think this method makes a pretty good internal transaction helper. I.e. call it from within Token methods so they are atomic, but don't expose it publicly. So I reckon make it non-pub for this PR, fixing (1).

I'm not sure 2 is a serious problem, but would be nice to address. For application-level transactions, we'd need the callback parameter to be Token or a new TokenTxn trait. If callers strictly used that, it would kinda give (2).

fil_fungible_token/src/token/mod.rs Outdated Show resolved Hide resolved
@@ -266,76 +279,99 @@ where
)));
}

let mut state = self.load_state();
let _old_state = self.state.clone();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused (for now)

@alexytsu alexytsu merged commit b532035 into main Jul 25, 2022
@alexytsu alexytsu deleted the token/state branch July 25, 2022 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants