Skip to content

Commit

Permalink
agd export option for diagnosis (#10344)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
usmanmani1122 authored Nov 21, 2024
1 parent 83191e7 commit 6112a8b
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 46 deletions.
33 changes: 25 additions & 8 deletions golang/cosmos/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,22 @@ import (

const appName = "agoric"

// FlagSwingStoreExportDir defines the config flag used to specify where a
// genesis swing-store export is expected. For start from genesis, the default
// value is config/swing-store in the home directory. For genesis export, the
// value is always a "swing-store" directory sibling to the exported
// genesis.json file.
// TODO: document this flag in config, likely alongside the genesis path
const FlagSwingStoreExportDir = "swing-store-export-dir"
const (
// FlagSwingStoreExportDir defines the config flag used to specify where a
// genesis swing-store export is expected. For start from genesis, the default
// value is config/swing-store in the home directory. For genesis export, the
// value is always a "swing-store" directory sibling to the exported
// genesis.json file.
// TODO: document this flag in config, likely alongside the genesis path
FlagSwingStoreExportDir = "swing-store-export-dir"
// FlagSwingStoreExportMode defines the export mode for the swing store
// Alongside the default mode `operational`, there are two other modes
//
// 1- `skip` mode will skip the swing store export altogether
//
// 2- `debug` mode will export all the available store
FlagSwingStoreExportMode = "swing-store-export-mode"
)

var (
// DefaultNodeHome default home directories for the application daemon
Expand Down Expand Up @@ -673,6 +682,7 @@ func NewAgoricApp(
app.EvidenceKeeper = *evidenceKeeper

swingStoreExportDir := cast.ToString(appOpts.Get(FlagSwingStoreExportDir))
swingStoreExportMode := cast.ToString(appOpts.Get(FlagSwingStoreExportMode))

// NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here.
Expand Down Expand Up @@ -702,7 +712,14 @@ func NewAgoricApp(
icaModule,
packetforward.NewAppModule(app.PacketForwardKeeper),
vstorage.NewAppModule(app.VstorageKeeper),
swingset.NewAppModule(app.SwingSetKeeper, &app.SwingStoreExportsHandler, setBootstrapNeeded, app.ensureControllerInited, swingStoreExportDir),
swingset.NewAppModule(
app.SwingSetKeeper,
&app.SwingStoreExportsHandler,
setBootstrapNeeded,
app.ensureControllerInited,
swingStoreExportDir,
swingStoreExportMode,
),
vibcModule,
vbankModule,
vtransferModule,
Expand Down
63 changes: 48 additions & 15 deletions golang/cosmos/daemon/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,12 @@ const (
ExportedSwingStoreDirectoryName = "swing-store"
)

var allowedSwingSetExportModes = map[string]bool{
swingset.SwingStoreExportModeDebug: true,
swingset.SwingStoreExportModeOperational: true,
swingset.SwingStoreExportModeSkip: true,
}

// extendCosmosExportCommand monkey-patches the "export" command added by
// cosmos-sdk to add a required "export-dir" command-line flag, and create the
// genesis export in the specified directory if the VM is running.
Expand All @@ -396,31 +402,52 @@ func extendCosmosExportCommand(cmd *cobra.Command) {
panic(err)
}

var keys []string
for key := range allowedSwingSetExportModes {
keys = append(keys, key)
}

cmd.Flags().String(
gaia.FlagSwingStoreExportMode,
swingset.SwingStoreExportModeOperational,
fmt.Sprintf(
"The mode for swingstore export (%s)",
strings.Join(keys, " | "),
),
)

originalRunE := cmd.RunE

extendedRunE := func(cmd *cobra.Command, args []string) error {
serverCtx := server.GetServerContextFromCmd(cmd)

exportDir, _ := cmd.Flags().GetString(FlagExportDir)
swingStoreExportMode, _ := cmd.Flags().GetString(gaia.FlagSwingStoreExportMode)

err := os.MkdirAll(exportDir, os.ModePerm)
if err != nil {
return err
}

genesisPath := filepath.Join(exportDir, ExportedGenesisFileName)
swingStoreExportPath := filepath.Join(exportDir, ExportedSwingStoreDirectoryName)

err = os.MkdirAll(swingStoreExportPath, os.ModePerm)
if err != nil {
return err
// Since none mode doesn't perform any swing store export
// There is no point in creating the export directory
if swingStoreExportMode != swingset.SwingStoreExportModeSkip {
swingStoreExportPath := filepath.Join(exportDir, ExportedSwingStoreDirectoryName)

err = os.MkdirAll(swingStoreExportPath, os.ModePerm)
if err != nil {
return err
}
// We unconditionally set FlagSwingStoreExportDir as for export, it makes
// little sense for users to control this location separately, and we don't
// want to override any swing-store artifacts that may be associated to the
// current genesis.
serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath)
}
// We unconditionally set FlagSwingStoreExportDir as for export, it makes
// little sense for users to control this location separately, and we don't
// want to override any swing-store artifacts that may be associated to the
// current genesis.
serverCtx.Viper.Set(gaia.FlagSwingStoreExportDir, swingStoreExportPath)

if hasVMController(serverCtx) {
if hasVMController(serverCtx) || swingStoreExportMode == swingset.SwingStoreExportModeSkip {
// Capture the export in the genesisPath.
// This will fail if a genesis.json already exists in the export-dir
genesisFile, err := os.OpenFile(
Expand Down Expand Up @@ -453,7 +480,16 @@ func (ac appCreator) appExport(
jailAllowedAddrs []string,
appOpts servertypes.AppOptions,
) (servertypes.ExportedApp, error) {
if OnExportHook != nil {
swingStoreExportMode, ok := appOpts.Get(gaia.FlagSwingStoreExportMode).(string)
if !(ok && allowedSwingSetExportModes[swingStoreExportMode]) {
return servertypes.ExportedApp{}, fmt.Errorf(
"export mode '%s' is not supported",
swingStoreExportMode,
)
}

// We don't have to launch VM in case the swing store export is not required
if swingStoreExportMode != swingset.SwingStoreExportModeSkip && OnExportHook != nil {
if err := OnExportHook(ac.agdServer, logger, appOpts); err != nil {
return servertypes.ExportedApp{}, err
}
Expand All @@ -464,10 +500,7 @@ func (ac appCreator) appExport(
return servertypes.ExportedApp{}, errors.New("application home is not set")
}

var loadLatest bool
if height == -1 {
loadLatest = true
}
loadLatest := height == -1

gaiaApp := gaia.NewAgoricApp(
ac.sender, ac.agdServer,
Expand Down
120 changes: 99 additions & 21 deletions golang/cosmos/x/swingset/genesis.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package swingset

import (
// "os"
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"hash"
"io"
"strings"

agoric "github.com/Agoric/agoric-sdk/golang/cosmos/types"
Expand All @@ -16,6 +16,20 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

const (
// SwingStoreExportModeSkip indicates swing store data should be
// excluded from the export.
SwingStoreExportModeSkip = "skip"

// SwingStoreExportModeOperational (default) indicates export should
// have the minimal set of artifacts needed to operate a node.
SwingStoreExportModeOperational = "operational"

// SwingStoreExportModeDebug indicates export should have the maximal
// set of artifacts available in the JS swing-store.
SwingStoreExportModeDebug = "debug"
)

func ValidateGenesis(data *types.GenesisState) error {
if data == nil {
return fmt.Errorf("swingset genesis data cannot be nil")
Expand Down Expand Up @@ -130,36 +144,59 @@ func InitGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingStore
return false
}

func ExportGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingStoreExportsHandler, swingStoreExportDir string) *types.GenesisState {
func ExportGenesis(
ctx sdk.Context,
k Keeper,
swingStoreExportsHandler *SwingStoreExportsHandler,
swingStoreExportDir string,
swingStoreExportMode string,
) *types.GenesisState {
gs := &types.GenesisState{
Params: k.GetParams(ctx),
State: k.GetState(ctx),
SwingStoreExportData: nil,
}

// This will only be used in non skip mode
artifactMode := swingStoreExportMode
exportDataMode := keeper.SwingStoreExportDataModeSkip
hasher := sha256.New()
snapshotHeight := uint64(ctx.BlockHeight())

eventHandler := swingStoreGenesisEventHandler{exportDir: swingStoreExportDir, snapshotHeight: snapshotHeight, swingStore: k.GetSwingStore(ctx), hasher: sha256.New()}

err := swingStoreExportsHandler.InitiateExport(
// The export will fail if the export of a historical height was requested
snapshotHeight,
eventHandler,
keeper.SwingStoreExportOptions{
ArtifactMode: keeper.SwingStoreArtifactModeOperational,
ExportDataMode: keeper.SwingStoreExportDataModeSkip,
},
)
if err != nil {
panic(err)
if swingStoreExportMode == SwingStoreExportModeDebug {
exportDataMode = keeper.SwingStoreExportDataModeAll
snapshotHeight = 0
}

err = keeper.WaitUntilSwingStoreExportDone()
if err != nil {
panic(err)
if swingStoreExportMode != SwingStoreExportModeSkip {
eventHandler := swingStoreGenesisEventHandler{
exportDir: swingStoreExportDir,
snapshotHeight: snapshotHeight,
swingStore: k.GetSwingStore(ctx),
hasher: hasher,
exportMode: swingStoreExportMode,
}

err := swingStoreExportsHandler.InitiateExport(
// The export will fail if the export of a historical height was requested outside of debug mode
snapshotHeight,
eventHandler,
keeper.SwingStoreExportOptions{
ArtifactMode: artifactMode,
ExportDataMode: exportDataMode,
},
)
if err != nil {
panic(err)
}

err = keeper.WaitUntilSwingStoreExportDone()
if err != nil {
panic(err)
}
}

gs.SwingStoreExportDataHash = fmt.Sprintf("sha256:%x", eventHandler.hasher.Sum(nil))
gs.SwingStoreExportDataHash = fmt.Sprintf("sha256:%x", hasher.Sum(nil))

return gs
}
Expand All @@ -169,17 +206,20 @@ type swingStoreGenesisEventHandler struct {
snapshotHeight uint64
swingStore sdk.KVStore
hasher hash.Hash
exportMode string
}

func (eventHandler swingStoreGenesisEventHandler) OnExportStarted(height uint64, retrieveSwingStoreExport func() error) error {
return retrieveSwingStoreExport()
}

func (eventHandler swingStoreGenesisEventHandler) OnExportRetrieved(provider keeper.SwingStoreExportProvider) error {
if eventHandler.snapshotHeight != provider.BlockHeight {
if eventHandler.exportMode != SwingStoreExportModeDebug && eventHandler.snapshotHeight != provider.BlockHeight {
return fmt.Errorf("snapshot block height (%d) doesn't match requested height (%d)", provider.BlockHeight, eventHandler.snapshotHeight)
}

artifactsEnded := false

artifactsProvider := keeper.SwingStoreExportProvider{
GetExportDataReader: func() (agoric.KVEntryReader, error) {
exportDataIterator := eventHandler.swingStore.Iterator(nil, nil)
Expand All @@ -194,7 +234,45 @@ func (eventHandler swingStoreGenesisEventHandler) OnExportRetrieved(provider kee
return nil
}), nil
},
ReadNextArtifact: provider.ReadNextArtifact,
ReadNextArtifact: func() (types.SwingStoreArtifact, error) {
var (
artifact types.SwingStoreArtifact
err error
encodedExportData bytes.Buffer
)

if !artifactsEnded {
artifact, err = provider.ReadNextArtifact()
} else {
return types.SwingStoreArtifact{}, io.EOF
}

if err == io.EOF {
artifactsEnded = true
if eventHandler.exportMode == SwingStoreExportModeDebug {
exportDataReader, _ := provider.GetExportDataReader()

defer exportDataReader.Close()

err = agoric.EncodeKVEntryReaderToJsonl(
exportDataReader,
&encodedExportData,
)
if err == nil {
artifact = types.SwingStoreArtifact{
Data: encodedExportData.Bytes(),
Name: keeper.UntrustedExportDataArtifactName,
}
}
}
}

return artifact, err
},
}

if eventHandler.exportMode == SwingStoreExportModeDebug {
artifactsProvider.BlockHeight = provider.BlockHeight
}

return keeper.WriteSwingStoreExportToDirectory(artifactsProvider, eventHandler.exportDir)
Expand Down
19 changes: 17 additions & 2 deletions golang/cosmos/x/swingset/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,26 @@ type AppModule struct {
setBootstrapNeeded func()
ensureControllerInited func(sdk.Context)
swingStoreExportDir string
swingStoreExportMode string
}

// NewAppModule creates a new AppModule Object
func NewAppModule(k Keeper, swingStoreExportsHandler *SwingStoreExportsHandler, setBootstrapNeeded func(), ensureControllerInited func(sdk.Context), swingStoreExportDir string) AppModule {
func NewAppModule(
k Keeper,
swingStoreExportsHandler *SwingStoreExportsHandler,
setBootstrapNeeded func(),
ensureControllerInited func(sdk.Context),
swingStoreExportDir string,
swingStoreExportMode string,
) AppModule {
am := AppModule{
AppModuleBasic: AppModuleBasic{},
keeper: k,
swingStoreExportsHandler: swingStoreExportsHandler,
setBootstrapNeeded: setBootstrapNeeded,
ensureControllerInited: ensureControllerInited,
swingStoreExportDir: swingStoreExportDir,
swingStoreExportMode: swingStoreExportMode,
}
return am
}
Expand Down Expand Up @@ -173,6 +182,12 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.

func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
am.checkSwingStoreExportSetup()
gs := ExportGenesis(ctx, am.keeper, am.swingStoreExportsHandler, am.swingStoreExportDir)
gs := ExportGenesis(
ctx,
am.keeper,
am.swingStoreExportsHandler,
am.swingStoreExportDir,
am.swingStoreExportMode,
)
return cdc.MustMarshalJSON(gs)
}

0 comments on commit 6112a8b

Please sign in to comment.