-
Notifications
You must be signed in to change notification settings - Fork 212
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(x/swingset): include swing-store in genesis #8152
Conversation
539d8b0
to
fe58cd1
Compare
a0baaf3
to
9f17baa
Compare
33e99cd
to
0e2987e
Compare
The size of the uncompressed artifacts is too large to be included in the genesis.json. We'll look into an alternative, likely a new option to |
1e7b20f
to
38ac134
Compare
fe86f55
to
98c5236
Compare
Best reviewed commit-by-commit |
af1a396
to
39dc27a
Compare
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.
A few comments. I'll continue with review later.
golang/cosmos/cmd/agd/main.go
Outdated
@@ -13,7 +13,7 @@ import ( | |||
|
|||
func main() { | |||
// We need to delegate to our default app for running the actual chain. | |||
daemoncmd.OnStartHook = func(logger log.Logger) { | |||
daemoncmd.OnStartHook = func(logger log.Logger, daemonize bool) { |
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 dislike the use of a boolean to distinguish agd start
from agd export
, especially since then the names OnStartHook
and daemonize
are pretty misleading. Can you instead just add daemoncmd.OnExportHook
and leave the meaning of daemoncmd.OnStartHook
alone?
This main.go
could use this same closure for both:
daemoncmd.OnStartHook = func(logger log.Logger, daemonize bool) { | |
launchVM := func(logger log.Logger) { | |
... | |
} | |
daemoncmd.OnExportHook = launchVM | |
daemoncmd.OnStartHook = launchVM |
golang/cosmos/cmd/libdaemon/main.go
Outdated
daemoncmd.OnStartHook = func(logger log.Logger, daemonize bool) { | ||
if daemonize { | ||
// We tried running start, which should never exit, so exit with non-zero | ||
// code if we ever stop. | ||
exitCode = 99 | ||
} |
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.
As per the above in agd/main.go
, please revert this change.
golang/cosmos/daemon/cmd/root.go
Outdated
@@ -40,7 +41,7 @@ import ( | |||
type Sender func(needReply bool, str string) (string, error) | |||
|
|||
var AppName = "agd" | |||
var OnStartHook func(log.Logger) | |||
var OnStartHook func(logger log.Logger, daemonize bool) |
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.
var OnStartHook func(logger log.Logger, daemonize bool) | |
var OnStartHook func(logger log.Logger) | |
var OnExportHook func(logger log.Logger) |
golang/cosmos/daemon/cmd/root.go
Outdated
OnStartHook(logger) | ||
OnStartHook(logger, true) |
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.
Please revert.
golang/cosmos/daemon/cmd/root.go
Outdated
if OnStartHook != nil { | ||
OnStartHook(logger, false) | ||
} |
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.
if OnStartHook != nil { | |
OnStartHook(logger, false) | |
} | |
if OnExportHook != nil { | |
OnExportHook(logger) | |
} |
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 (conditional on addressing comments)! You probably should get a Golang wizard's review before landing, but I didn't see any obvious faux pas.
if err != nil { | ||
return err | ||
} | ||
serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath) |
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.
What I've discerned is that agd export --export-dir=foo
is a required flag, but --swing-store-export-dir=foo/something
can be used to override where the swingstore is exported. Is that true? In either case, could you add this in comments near FlagExportDir
and FlagSwingStoreExportDir
?
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.
FlagSwingStoreExportDir can only be used to set where to read the swing-store artifacts from for InitGenesis, not for export
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.
Do they need to be different? It seems like the purpose is aligned.
golang/cosmos/daemon/cmd/root.go
Outdated
// OnExportHook is only set when the VM is not started | ||
// We don't need to override the behavior if the process will exec to the VM | ||
if OnExportHook == nil { | ||
cmd.RunE = extendedRunE | ||
} |
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 don't understand why this special case is necessary. Isn't it more robust to have the same behaviour regardless of whether or not we're starting the VM?
// OnExportHook is only set when the VM is not started | |
// We don't need to override the behavior if the process will exec to the VM | |
if OnExportHook == nil { | |
cmd.RunE = extendedRunE | |
} | |
cmd.RunE = extendedRunE |
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.
Without it we'd try to create the genesis file twice, and fail the second time around. I initially worked around that by truncating existing files
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.
Ah, okay. That's more subtle than I saw at first. I suggest instead detecting whether sender
has been set, and only running this function in that particular case, as I have commented above in this file.
Then, you can omit this condition based on OnExportHook
, which is a separate concern from whether the current binary is executing a different one to obtain the controller (sender == nil
).
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 did a variation on this as I think we should fail early if --export-dir
is missing. PTAL
golang/cosmos/daemon/cmd/root.go
Outdated
// OnExportHook is only set when the VM is not started | ||
// We don't need to override the behavior if the process will exec to the VM | ||
if OnExportHook == nil { | ||
cmd.RunE = extendedRunE | ||
} |
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.
Ah, okay. That's more subtle than I saw at first. I suggest instead detecting whether sender
has been set, and only running this function in that particular case, as I have commented above in this file.
Then, you can omit this condition based on OnExportHook
, which is a separate concern from whether the current binary is executing a different one to obtain the controller (sender == nil
).
golang/cosmos/daemon/cmd/root.go
Outdated
for _, command := range rootCmd.Commands() { | ||
if command.Name() != "export" { | ||
continue | ||
} | ||
|
||
extendCosmosExportCommand(command) | ||
break | ||
} |
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.
for _, command := range rootCmd.Commands() { | |
if command.Name() != "export" { | |
continue | |
} | |
extendCosmosExportCommand(command) | |
break | |
} | |
// Only do the following modifications if we actually have a controller. | |
// Otherwise, there will be an exec of the VM program, and it will | |
// handle all of the export logic. | |
if sender != nil { | |
for _, command := range rootCmd.Commands() { | |
if command.Name() != "export" { | |
continue | |
} | |
extendCosmosExportCommand(command) | |
break | |
} | |
} |
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, the new code looks good.
82bf011
to
35414a0
Compare
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.
Golang code LGTM with some minor requests and suggestions.
golang/cosmos/daemon/cmd/root.go
Outdated
if command.Name() != "export" { | ||
continue | ||
} | ||
|
||
extendCosmosExportCommand(command, sender != nil) | ||
break |
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.
You can save a few lines by inverting the test and using the implicit continue
at the end of the loop body.
if command.Name() != "export" { | |
continue | |
} | |
extendCosmosExportCommand(command, sender != nil) | |
break | |
if command.Name() == "export" { | |
extendCosmosExportCommand(command, sender != nil) | |
break | |
} |
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.
Hmm, sender != nil
is a bit opaque. Maybe it should be assigned to a named variable with an explanatory comment?
@@ -28,6 +28,7 @@ import ( | |||
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli" | |||
"github.com/spf13/cast" | |||
"github.com/spf13/cobra" | |||
"github.com/spf13/viper" |
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 don't think viper
provides enough functionality to justify its use. However, I see it's already used in cosmos-sdk, so consistency with existing code trumps my concerns.
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.
Yeah this is just imported so I can cast to the Viper
instance that cosmos-sdk creates and that's underlying the appOpts
) | ||
|
||
func extendCosmosExportCommand(cmd *cobra.Command, hasVMController bool) { | ||
cmd.Flags().String(FlagExportDir, "", "The directory where to create the genesis export") |
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.
Add note in the flag help string explaining difference from the swing store export dir flag, and in that one's help string explaining the difference from this one.
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 didn't add a help string for FlagSwingStoreExportDir
as from what I understand it could technically be set by config as well, not just by the command line. I have to admit I'm a little fuzzy with how the whole config thing is plumbed in cosmos-sdk.
Actually I realize now that I didn't actually declare it as an official flag anywhere, I just abused the flag mechanism to plumb through an app option. Where do you recommend I declare the FlagSwingStoreExportDir
flag?
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 second the confusion, but FlagSwingStoreExportDir
seems to have strange wiring. At minimum I would look for doc comments.
ExportedSwingStoreDirectoryName = "swing-store" | ||
) | ||
|
||
func extendCosmosExportCommand(cmd *cobra.Command, hasVMController bool) { |
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.
Document, briefly, what this function should do.
golang/cosmos/x/swingset/genesis.go
Outdated
keeper.SwingStoreExportProvider{BlockHeight: snapshotHeight, GetExportDataReader: getExportDataReader, ReadNextArtifact: artifactProvider.ReadNextArtifact}, | ||
keeper.SwingStoreRestoreOptions{ArtifactMode: keeper.SwingStoreArtifactModeReplay, ExportDataMode: keeper.SwingStoreExportDataModeAll}, |
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.
keeper.SwingStoreExportProvider{BlockHeight: snapshotHeight, GetExportDataReader: getExportDataReader, ReadNextArtifact: artifactProvider.ReadNextArtifact}, | |
keeper.SwingStoreRestoreOptions{ArtifactMode: keeper.SwingStoreArtifactModeReplay, ExportDataMode: keeper.SwingStoreExportDataModeAll}, | |
keeper.SwingStoreExportProvider{ | |
BlockHeight: snapshotHeight, | |
GetExportDataReader: getExportDataReader, | |
ReadNextArtifact: artifactProvider.ReadNextArtifact | |
}, | |
keeper.SwingStoreRestoreOptions{ | |
ArtifactMode: keeper.SwingStoreArtifactModeReplay, | |
ExportDataMode: keeper.SwingStoreExportDataModeAll | |
}, |
@@ -29,5 +29,8 @@ func main() { | |||
} | |||
} | |||
|
|||
daemoncmd.OnStartHook = launchVM | |||
daemoncmd.OnExportHook = launchVM |
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.
There's some log output in launchVM
that could probably be improved for export.
golang/cosmos/daemon/cmd/root.go
Outdated
if command.Name() != "export" { | ||
continue | ||
} | ||
|
||
extendCosmosExportCommand(command, sender != nil) | ||
break |
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.
Hmm, sender != nil
is a bit opaque. Maybe it should be assigned to a named variable with an explanatory comment?
) | ||
|
||
func extendCosmosExportCommand(cmd *cobra.Command, hasVMController bool) { | ||
cmd.Flags().String(FlagExportDir, "", "The directory where to create the genesis export") |
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 second the confusion, but FlagSwingStoreExportDir
seems to have strange wiring. At minimum I would look for doc comments.
if err != nil { | ||
return err | ||
} | ||
serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath) |
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.
Do they need to be different? It seems like the purpose is aligned.
} | ||
serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath) | ||
|
||
genesisFile, err := os.OpenFile(genesisPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.ModePerm) |
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 will catch a preexisting genesis file, but is there also a potential issue if swingStoreExportPath was already created? I suspect not because it would be empty if this line succeeds, but it's worth confirming.
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.
If the export-manifest.json
file exists in the swingStoreExportPath, the JS export process will fail. Directories existing are not a problem.
127d93b
to
b034250
Compare
b034250
to
46d3466
Compare
…store feat(x/swingset): include swing-store in genesis
closes: #8420 ref: #8152 ## Description This PR adds a new optional flag `--swing-store-export-mode` to the `agd export` command which can be used to specify the nature of swing-store export ### Security Considerations None ### Scaling Considerations None ### Documentation Considerations An optional flag `--swing-store-export-mode` option is now supported for `agd export` to specify the kind of swing store export required: - `operational` (default) option will retain the existing behavior and export the swing-store artifacts using the latest height - `skip` option will not export any swing-store artifact - `debug` option will export all swing-store artifacts, starting from height `0` ### Testing Considerations Tested manually ### Upgrade Considerations None
closes: #6527
Description
This PR adds swing-store artifacts to the genesis export, and the ability to start from a genesis that includes a full swing-store export.
The artifacts are exported and imported from a directory alongside the
genesis.json
file, instead of being inlined like other state.While the full swing-store state is now included in the genesis state, it doesn't mean it's editable in any kind of capacity. Most swingset state is likely replicated in multiple places inside the swing-store, like transcripts and xsnap heap snapshots.
Security Considerations
As with any genesis start, the validators are in charge of verifying the validity of the genesis file against an expected hash, this PR does not change that.
They do not need to perform manual validation of the artifacts since those are checked for consistency by the swing-store import process, using metadata stored in the genesis file.
Scaling Considerations
The swing-store artifacts are pretty large binary blobs, and as such are not included in the protobuf (genesis state) that ends up JSON serialized. However their size is still significant, and I believe a genesis export of mainnet would result in a total size of about 10GB today. Validators may want to exchange the genesis and artifacts compressed as that should compress down to about 800MB as seen with state-sync snapshots.
Documentation Considerations
A new
--export-dir
option is required foragd export
to specify a path to a directory where the genesis export process will create thegenesis.json
file and aswing-store
directory containing the swing-store export artifacts. On start from genesis, thisswing-store
directory must be placed in theconfig
folder of the node's home (alongside where thegenesis.json
usual reside), at least until InitGenesis has completed.We do not expect this mechanism to be used for normal operations, just as a backstop in case of chain failure.
Testing Considerations
Added a genesis test to the docker upgrade test framework.
Upgrade Considerations
While this mechanism can be used for manual chain upgrades, it is fully transparent to any swingset operation.