-
Notifications
You must be signed in to change notification settings - Fork 208
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
feat(cosmic-swingset): Add a config property for exporting storage to bootstrap #7489
Conversation
re testing... is it straightforward / cost-effective to mock |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the code looks plausible. And nailing down the name chainStorageEntries
is handy for coordination with #6645
But I hesitate to approve without a test. I'd like to think about that a bit more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm willing to take the risk and go without an automated test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be out of scope for this PR, but I think we should fix the underlying vstorage "entries" method. cc @michaelfig
Besides that, just some nits in comments.
* (e.g., `exportStorageSubtrees: ['a.b']` might result in vatParameters including | ||
* `chainStorageEntries: [ ['a.b', ''], ['a.b.c', '"foo"'], ['a.b.c2', '42'] ]`). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would also include descendant that are not direct children, e.g. ['a.b.c3.d', 'foo']
.
const callChainStorage = (method, path) => | ||
bridgeOutbound(BRIDGE_ID.STORAGE, { method, args: [path] }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I imagined we would extend makePrefixedBridgeStorage
instead of manually minting chain sends, but I guess there is no need for such abstractions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for adding these vstorage unit tests.
I missed it the first time around, but I think we need to add the custom
prefix used by chain-storage. I don't want bootstrap to possibly get full access to the root vstorage, which contains the kernel state, etc.
keeper.SetStorage(ctx, types.NewStorageEntry("empty", "")) | ||
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("special.nodata")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I'd prefer if the test was more realistic and no data entries only existed for cases where there are nested entries
keeper.SetStorage(ctx, types.NewStorageEntry("empty", "")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("special.nodata")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("placeholder.empty", "")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("placeholder")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed (in both places).
keeper.SetStorage(ctx, types.NewStorageEntry("key1", "value1")) | ||
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.grandchild1", "value1grandchild")) | ||
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key1.child1.grandchild2")) | ||
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.empty-non-terminal.leaf", "")) | ||
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2", "value2grandchild")) | ||
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2a", "value2grandchilda")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nit about representative test data
keeper.SetStorage(ctx, types.NewStorageEntry("key1", "value1")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.grandchild1", "value1grandchild")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key1.child1.grandchild2")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.empty-non-terminal.leaf", "")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2", "value2grandchild")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2a", "value2grandchilda")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key1", "value1")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.grandchild1", "value1grandchild")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key1.child1")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.empty-non-terminal.leaf", "")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key2")) | |
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key2.child2")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2", "value2grandchild")) | |
keeper.SetStorage(ctx, types.NewStorageEntry("key2.child2.grandchild2a", "value2grandchilda")) |
const fullPath = | ||
!path || path === '.' ? `${nextSegment}` : `${path}.${nextSegment}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe path === '.'
is invalid and I'm tempted to say we should disallow path
from ever being empty in the initial "get"
const fullPath = | |
!path || path === '.' ? `${nextSegment}` : `${path}.${nextSegment}`; | |
const fullPath =`${path}.${nextSegment}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interface doesn't currently allow enumerating top-level keys AFAICT, and that seems like a problem that could be addressed at some point—but in ignorance of this consumer. This anticipates and accommodates either a DNS-like .
or an even more simple empty-string fix.
However, I think you're right about needing to require STORAGE_PATH.CUSTOM
so it will be moot.
const value = callChainStorage('get', path); | ||
return [path, value]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you need to prepend STORAGE_PATH.CUSTOM
const value = callChainStorage('get', path); | |
return [path, value]; | |
const fullPath = `${STORAGE_PATH.CUSTOM}.${path}`; | |
const value = callChainStorage('get', fullPath); | |
return [fullPath, value]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've asked for confirmation, but I think we should keep that prefix explicit—albeit with enforcement.
Okay @mhofman, back to you. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
func TestEntries(t *testing.T) { | ||
kit := makeTestKit() | ||
keeper, handler, ctx, cctx := kit.keeper, kit.handler, kit.ctx, kit.cctx | ||
|
||
keeper.SetStorage(ctx, types.NewStorageEntry("key1", "value1")) | ||
keeper.SetStorage(ctx, types.NewStorageEntry("key1.child1.grandchild1", "value1grandchild")) | ||
keeper.SetStorage(ctx, types.NewStorageEntryWithNoData("key1.child1.grandchild2")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious why we need to test this case as it's not supposed to ever happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Robust handling if that ever changes (or better yet, a failure in this test).
d375995
to
4f1589c
Compare
Fixes #7156
Description
When SwingSet configuration includes
exportStorageSubtrees
, read all chain storage data at or under the specified paths and make the resulting array of entries available to the bootstrap vat at parameterchainStorageEntries
.Security Considerations
None known; launch-chain already had access to the bridge and control over configuration is already a highly privileged capability.
Scaling Considerations
Reading the data can potentially be very slow, but we have accepted that.
Documentation Considerations
I'm not sure where chain configuration and bootstrap vat parameters are officially documented, but would welcome suggestions.
Testing Considerations
There doesn't seem to be any existing test framework that could verify the new behavior. 😢