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

[WIP] Implementation of EIP-1153: Transient Storage using Disk Persistence and Lifecycle Management #1588

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

snissn
Copy link
Contributor

@snissn snissn commented Nov 20, 2024

Description

This WIP PR introduces transient storage support in the Filecoin EVM, conceptually aligning with Ethereum's EIP-1153. Transient storage functionality is implemented with a tombstone mechanism for lifecycle tracking keyed by transaction identifiers (origin and nonce). While the core implementation is in place, work remains on validating the transient data's lifecycle and completing associated tests.


Current Progress

State Modifications:

  1. Persistent Transient Data:

    • A Cid variable has been added to represent transient storage.
    pub transient_state: Cid
  2. Lifecycle Tracking:

    • Introduced TransientDataLifespan to track lifecycle with origin ActorID and nonce.
    pub struct TransientDataLifespan {
        pub origin: ActorID,
        pub nonce: u64,
    }

New Operations:

  1. TLOAD: Retrieves transient data while ensuring lifecycle validity.
  2. TSTORE: Updates transient data and lifecycle.
  3. Lifecycle validation (TODO): Compare transaction metadata with stored tombstone.

Remaining TODOs

  1. Liveliness Check for Transient Data:

    • Validate that transient data is only accessible within the valid transaction context by comparing the tombstone (stored origin and nonce) with the current transaction.

    Code Location:

    • get_transient_storage and set_transient_storage in actors/evm/src/interpreter/system.rs.
    // TODO check tombstone for liveliness of data
  2. Test Coverage:

    • Write tests to verify:
      • Transient data lifecycle (e.g., expiration between transactions).
      • Liveliness checks during nested and delegate calls.
      • Behavior when accessing invalid or expired transient data.
    • Existing transient storage operations (TLOAD, TSTORE) are partially tested.

    Code Location:

    • actors/evm/src/interpreter/instructions/storage.rs (Test module)
    // TODO test transient storage lifecycle
  3. Edge Case Handling:

    • Ensure transient data is cleared appropriately in all scenarios, including contract self-destruct or re-creation.

Testing

Implemented Tests:

  • test_tload: Validates TLOAD operation when data exists.
  • test_tload_oob: Ensures out-of-bounds TLOAD returns zero.
  • test_tstore: Confirms TSTORE updates transient data correctly.

Pending Tests:

  • Liveliness and lifecycle tests (TODO in the test module).
  • System-wide validation tests for lifecycle management.

Tradeoffs and Considerations

  1. Increased Storage Overhead:

    • Adds transient_state and transient_data_lifespan to State.
  2. Gas Inefficiency:

    • Current approach persists transient data, diverging from EIP-1153's ephemeral storage design.

Checklist

  • Core implementation of TLOAD and TSTORE.
  • Integration with State and System.
  • Basic tests for TLOAD and TSTORE.
  • Lifecycle validation for transient data (TODO).
  • Comprehensive test coverage (TODO).

Next Steps

  • Complete lifecycle validation in System.
  • Implement and validate lifecycle tests.

References


Review Feedback

Incorporates feedback from @Stebalien:

  • Minimize unnecessary writes when clearing transient data.
  • Keep State clean unless other updates occur.

Note: This PR is marked as WIP until lifecycle validation and associated tests are complete. Feedback on the current implementation and suggestions for completing TODOs are welcome!

…around comparing the liveliness of the transient data and validating that functionality in a test
@snissn snissn self-assigned this Nov 20, 2024
Comment on lines +119 to +121
pub transient_state: Cid,
/// The nonce and actor id that represents the lifespan of the transient storage data
pub transient_data_lifespan: Option<TransientDataLifespan>,
Copy link
Member

Choose a reason for hiding this comment

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

Can we combine these? That'll let us store a single "null" if there is no transient data.

@@ -314,6 +335,10 @@ impl<'r, RT: Runtime> System<'r, RT> {
self.slots
.set_root(&state.contract_state)
.context_code(ExitCode::USR_ILLEGAL_STATE, "state not in blockstore")?;
self.transient_slots
Copy link
Member

Choose a reason for hiding this comment

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

It would be nice if we could load this lazily on first-use, or even just if non-empty.

actors/evm/src/interpreter/system.rs Outdated Show resolved Hide resolved
actors/evm/src/interpreter/system.rs Outdated Show resolved Hide resolved
…bine transient_slots with transient_data_lifespan anyway.
…DO reinitialize does not properly clear state KAMT due to clone/reference issues that are being debugged
// Reinitialize the transient_slots with a fresh KAMT
//let transient_store = self.rt.store().clone();
//self.transient_slots = StateKamt::new_with_config(transient_store, KAMT_CONFIG.clone());
// TODO XXX reinitialize does not currently work due to blockstore reference issues
Copy link
Member

Choose a reason for hiding this comment

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

I'd be interested to know what these errors are and how you end up diagnosing the problem, for my own educational purposes because your commented code looks like it should work.

Copy link
Member

Choose a reason for hiding this comment

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

It's because self.rt.store() doesn't implement clone, while &... does, so rust is helpfully creating a reference for you.

Note: the new function explicitly requires that the store be clonable.

Options are:

  1. Lift that requirement up to the impl level.
  2. Use self.transient_slots.into_store() to "take" the existing store.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: ⌨️ In Progress
Development

Successfully merging this pull request may close these issues.

3 participants