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

[x/programs] Refactor SDK storage #499

Merged
merged 21 commits into from
Oct 2, 2023
Merged

[x/programs] Refactor SDK storage #499

merged 21 commits into from
Oct 2, 2023

Conversation

hexfusion
Copy link
Contributor

@hexfusion hexfusion commented Sep 25, 2023

The current host module map was a POC example of storing data on the host. In order to integrate programs into a VM we need to refactor Program storage. This PR proposes a new host module called state_keys and changes to the SDK to provide clear separation between interactions with module Memory and the hosts State.

  • rename State to Program
  • propose a new enum based Program state schema which emulates HyperSDK.
  • Document how a Program can utilize Memory and State
  • New macro state_keys which implements StateKeys for the user and helps to standardize the format.
  • Handle errors inline with ETH standards. In general all examples will show halt on error usage.
  • Remove Wasi dependencies from example programs
  • Add tests

cargo expand for new state_key macro shows its implementation

/// The program state keys.
#[repr(u8)]
enum StateKey {
    /// The total supply of the token.
    TotalSupply,
    /// The name of the token.
    Name,
    /// The symbol of the token.
    Symbol,
    /// The balance of the token by address.
    Balance(Address),
}
#[automatically_derived]
impl ::core::marker::Copy for StateKey {}
#[automatically_derived]
impl ::core::clone::Clone for StateKey {
    #[inline]
    fn clone(&self) -> StateKey {
        let _: ::core::clone::AssertParamIsClone<Address>;
        *self
    }
}
impl StateKey {
    pub fn to_vec(self) -> Vec<u8> {
        match self {
            Self::TotalSupply => {
                <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([0u8]))
            }
            Self::Name => <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([1u8])),
            Self::Symbol => <[_]>::into_vec(#[rustc_box] ::alloc::boxed::Box::new([2u8])),
            Self::Balance(a) => std::iter::once(3u8).chain(a.into_iter()).collect(),
        }
    }
}

exdx
exdx previously approved these changes Sep 25, 2023
Copy link
Contributor

@exdx exdx left a comment

Choose a reason for hiding this comment

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

LGTM just some thoughts

@exdx
Copy link
Contributor

exdx commented Sep 25, 2023

In terms of actual design feedback, I'm not caught up enough to provide a clear opinion. But the proposed design seems reasonable, and importantly, simple to understand. So that's good.

@exdx
Copy link
Contributor

exdx commented Sep 26, 2023

In Stellar they use env as a wrapper around storage. Might be worth considering, the UX is nice. https://soroban.stellar.org/docs/getting-started/storing-data#contract-data-access

@hexfusion
Copy link
Contributor Author

In Stellar they use env as a wrapper around storage. Might be worth considering, the UX is nice. https://soroban.stellar.org/docs/getting-started/storing-data#contract-data-access

I think we started there and have changed a few times. I think Program makes sense but open to further conversation.

@hexfusion hexfusion force-pushed the storage_refactor branch 2 times, most recently from a306079 to ba39f34 Compare September 28, 2023 13:12
exdx
exdx previously approved these changes Sep 28, 2023
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
@hexfusion hexfusion marked this pull request as ready for review September 28, 2023 21:51
@hexfusion hexfusion requested a review from samliok as a code owner September 28, 2023 21:51
@hexfusion hexfusion added this to the WASM Programs + Parallel Execution milestone Sep 28, 2023
@richardpringle
Copy link
Contributor

image
@hexfusion... wait a minute, aren't you supposed to do that first 😝

@hexfusion
Copy link
Contributor Author

hexfusion commented Sep 29, 2023

@hexfusion... wait a minute, aren't you supposed to do that first 😝

Yeah I realized after the fact that I was making some assumptions in the memory module so added some tests.

Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
Signed-off-by: Sam Batschelet <[email protected]>
richardpringle
richardpringle previously approved these changes Sep 29, 2023
Copy link
Contributor

@richardpringle richardpringle left a comment

Choose a reason for hiding this comment

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

Looking good! Majority of my comments are more thoughts for the future.

Signed-off-by: Sam Batschelet <[email protected]>
Copy link
Contributor

@richardpringle richardpringle left a comment

Choose a reason for hiding this comment

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

🚀

offset, _ := memory.Alloc(bytesLen)

// copy bytes into memory allocation
_ = memory.Write(offset, bytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to ensure that the amount written is equal to bytesLen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the runtime implementation handles all this maybe the example is more confusing than helpful.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should just be clearer about expectations to safely use. I think the example is nice.

bytesLen := uint64(len(bytes))

// create a memory allocation
offset, _ := memory.Alloc(bytesLen)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there some error we should be checking here? (maybe out of scope for readme)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah just example usage error removed for brevity.

```

Each `Memory` is tightly bound to the lifecycle of the module. For this reason,
`Memory` is non durable and can not be relied upon outside the scope of a `#[public]` function.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: non-durable

type ProposalId = [u8; 8];

#[state_keys]
enum StateKeys {
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

# codegen-units = 1
# overflow-checks = true
# # https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html#unwinding-the-stack-or-aborting-in-response-to-a-panic
# panic = 'abort'
Copy link
Contributor

Choose a reason for hiding this comment

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

is this considered an optimization or required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

panic=abort is the default for the wasm32-unknown-unknown target which is now our default target. So this is just visibility to the default in case we wanted to changed it. I do have a question of how well the memory is cleaned up in this case by the OS.

Copy link
Contributor Author

@hexfusion hexfusion Oct 2, 2023

Choose a reason for hiding this comment

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

The rust book describes this as well as says "dont panic" :)

Ultimately, panics translate into aborts in wasm32-unknown-unknown anyways, so this gives you the same behavior but without the code bloat.

ref. https://rustwasm.github.io/docs/book/reference/code-size.html?highlight=panic#avoid-panicking

@patrick-ogrady patrick-ogrady merged commit 7931d01 into main Oct 2, 2023
@patrick-ogrady patrick-ogrady deleted the storage_refactor branch October 2, 2023 17:02
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.

4 participants