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

Tiered Storage #1832

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft

Tiered Storage #1832

wants to merge 14 commits into from

Conversation

RodrigoVillar
Copy link
Contributor

@RodrigoVillar RodrigoVillar commented Dec 12, 2024

This PR introduces tiered storage into the HyperSDK. By adding tiered storage, users are now refunded if any KV-pairs their TX touches was actually in memory in contrast to the current assumption that data always lives on disk.

TODO:

  • Add UT for Scope
  • Fix MorpheusVM e2e tests

@RodrigoVillar RodrigoVillar added the DO NOT MERGE This PR is not meant to be merged in its current state label Dec 12, 2024
@@ -300,7 +302,15 @@ func (t *Transaction) PreExecute(
if err != nil {
return err
}
return bh.CanDeduct(ctx, t.Auth.Sponsor(), im, fee)

var st state.Immutable
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this detail need to be handled by the transaction execution?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nope; changed it so now callers are responsible for translating their values

Comment on lines 376 to 387
// We refund here
refund, err := scope.Refund(r)
if err != nil {
return nil, ErrRefundDimensions
}
refundFee, err := feeManager.Fee(refund)
if err != nil {
return nil, ErrFailedToComputeRefund
}
if err := bh.AddBalance(ctx, t.Auth.Sponsor(), ts, refundFee); err != nil {
return nil, ErrRefundFailed
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't we have all the information needed to handle the refund immediately after fetching, so we can differentiate between tiered storage accessed? Do we need to wait until after execution for some reason?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Keeping the refund logic inside of transaction.Execute() would be consistent with how we're currently manipulating with the sponsor's balance. It's true that once we've fetched all values from the fetcher (in both the block builder and the validator) that we can immediately compute/apply the refund. However, this would require us to make a call to BalanceHandler outside of transaction.Execute(), which could be considered an antipattern as the sponsor's balance is manipulated only in transaction.Execute().

As where the refund logic is placed inside transaction.Execute(), we could apply the refund after deducting. A property we gain from the current implementation is that refunds are applied only to successful transactions, but I wouldn't mind removing it.

Comment on lines +1215 to +1238
func (*VM) removeSuffixes(values [][]byte, errors []error) ([][]byte, []error) {
returnValues := make([][]byte, 0, len(values))
returnErrors := make([]error, 0, len(values))

// Invariant: len(values) == len(errors)
for i, v := range values {
if errors[i] != nil {
returnErrors = append(returnErrors, errors[i])
returnValues = append(returnValues, nil)
continue
}
// Time to unsuffix
if len(v) < consts.Uint64Len {
returnErrors = append(returnErrors, ErrValueTooShortForPrefix)
returnValues = append(returnValues, nil)
continue
}
returnValues = append(returnValues, v[:len(v)-consts.Uint64Len])
returnErrors = append(returnErrors, nil)
}

return returnValues, returnErrors
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to be coupled to the VM? In effect, we are grabbing the state from the VM's merkledb, could we apply the same transformation we normally do to a merkledb view before giving actions read access to it?

This function gives raw access to the statedb, should we leave to the caller (that knows where the information is used) to strip the suffixes / transform the data as needed?

@@ -397,6 +405,26 @@ func (c *Builder) BuildBlock(ctx context.Context, parentView state.View, parent
c.metrics.emptyBlockBuilt.Inc()
}

// Before continuing, we append the executing block's suffix to all existing
Copy link
Contributor Author

@RodrigoVillar RodrigoVillar Dec 18, 2024

Choose a reason for hiding this comment

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

After hook

@@ -290,7 +291,12 @@ func (c *Builder) BuildBlock(ctx context.Context, parentView state.View, parent
}

// Execute block
tsv := ts.NewView(stateKeys, storage)
unsuffixedStorage, hotkeys, err := state.ExtractSuffixes(storage, height, c.config.Epsilon)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before hook

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
DO NOT MERGE This PR is not meant to be merged in its current state
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants