Skip to content

Commit

Permalink
Swing-store export data outside of genesis file (tentative #9549)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhofman committed Jun 23, 2024
1 parent 63209c3 commit f036c5c
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 49 deletions.
25 changes: 25 additions & 0 deletions a3p-integration/proposals/a:upgrade-15/genesis-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

source /usr/src/upgrade-test-scripts/env_setup.sh

export_genesis() {
GENESIS_EXPORT_DIR="$1"
shift
GENESIS_HEIGHT_ARG=

if [ -n "$1" ]; then
GENESIS_HEIGHT_ARG="--height $1"
shift
fi

agd export --export-dir "$GENESIS_EXPORT_DIR" $GENESIS_HEIGHT_ARG "$@"
}

killAgd
FORK_TEST_DIR="$(mktemp -t -d fork-test-XXX)"
mkdir -p "$FORK_TEST_DIR/config"
cp /root/.agoric/config/priv_validator_key.json "$FORK_TEST_DIR/config"
agd --home "$FORK_TEST_DIR" tendermint unsafe-reset-all

export_genesis "$FORK_TEST_DIR/config"
startAgd --home "$FORK_TEST_DIR"
1 change: 1 addition & 0 deletions a3p-integration/proposals/a:upgrade-15/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
yarn ava

./state-sync-snapshots-test.sh
./genesis-test.sh
4 changes: 4 additions & 0 deletions golang/cosmos/proto/agoric/swingset/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ message GenesisState {
repeated SwingStoreExportDataEntry swing_store_export_data = 4 [
(gogoproto.jsontag) = "swingStoreExportData"
];

string swing_store_export_data_hash = 5 [
(gogoproto.jsontag) = "swingStoreExportDataHash"
];
}

// A SwingStore "export data" entry.
Expand Down
42 changes: 42 additions & 0 deletions golang/cosmos/types/kv_entry_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,45 @@ func EncodeKVEntryReaderToJsonl(reader KVEntryReader, bytesWriter io.Writer) (er
}
}
}

var _ KVEntryReader = &kvHookingReader{}

// kvHookingReader is a KVEntryReader backed by another KVEntryReader which
// provides callbacks for read and close
type kvHookingReader struct {
reader KVEntryReader
onRead func(entry KVEntry) error
onClose func() error
}

// NewKVHookingReader returns a KVEntryReader backed by another KVEntryReader
func NewKVHookingReader(reader KVEntryReader, onRead func(entry KVEntry) error, onClose func() error) KVEntryReader {
return &kvHookingReader{
reader,
onRead,
onClose,
}
}

// Read yields the next KVEntry from the source reader
// Implements KVEntryReader
func (hr kvHookingReader) Read() (next KVEntry, err error) {
next, err = hr.reader.Read()

if err == nil {
err = hr.onRead(next)
}

return next, err
}

// Close releases the underlying source reader
// Implements KVEntryReader
func (hr kvHookingReader) Close() error {
err := hr.reader.Close()
if err == nil {
err = hr.onClose()
}

return err
}
104 changes: 83 additions & 21 deletions golang/cosmos/x/swingset/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ package swingset

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

agoric "github.com/Agoric/agoric-sdk/golang/cosmos/types"
"github.com/Agoric/agoric-sdk/golang/cosmos/types/conv"
Expand Down Expand Up @@ -36,8 +42,10 @@ func InitGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingStore
k.SetState(ctx, data.GetState())

swingStoreExportData := data.GetSwingStoreExportData()
if len(swingStoreExportData) == 0 {
if len(swingStoreExportData) == 0 && data.SwingStoreExportDataHash == "" {
return true
} else if data.SwingStoreExportDataHash != "" && len(swingStoreExportData) > 0 {
panic("Swingset genesis state cannot have both export data and hash of export data")
}

artifactProvider, err := keeper.OpenSwingStoreExportDirectory(swingStoreExportDir)
Expand All @@ -47,15 +55,63 @@ func InitGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingStore

swingStore := k.GetSwingStore(ctx)

for _, entry := range swingStoreExportData {
swingStore.Set(conv.UnsafeStrToBytes(entry.Key), conv.UnsafeStrToBytes(entry.Value))
}

snapshotHeight := uint64(ctx.BlockHeight())

getExportDataReader := func() (agoric.KVEntryReader, error) {
exportDataIterator := swingStore.Iterator(nil, nil)
return agoric.NewKVIteratorReader(exportDataIterator), nil
var getExportDataReader func() (agoric.KVEntryReader, error)

if len(swingStoreExportData) > 0 {
for _, entry := range swingStoreExportData {
swingStore.Set(conv.UnsafeStrToBytes(entry.Key), conv.UnsafeStrToBytes(entry.Value))
}
getExportDataReader = func() (agoric.KVEntryReader, error) {
exportDataIterator := swingStore.Iterator(nil, nil)
return agoric.NewKVIteratorReader(exportDataIterator), nil
}
} else {
hashParts := strings.SplitN(data.SwingStoreExportDataHash, ":", 2)
if len(hashParts) != 2 {
panic(fmt.Errorf("invalid swing-store export data hash %s", data.SwingStoreExportDataHash))
}
if hashParts[0] != "sha256" {
panic(fmt.Errorf("invalid swing-store export data hash algorithm %s, expected sha256", hashParts[0]))
}
sha256Hash, err := hex.DecodeString(hashParts[1])
if err != nil {
panic(err)
}
getExportDataReader = func() (agoric.KVEntryReader, error) {
kvReader, err := artifactProvider.GetExportDataReader()
if err != nil {
return nil, err
}

if kvReader == nil {
return nil, fmt.Errorf("swing-store export has no export data")
}

hasher := sha256.New()
encoder := json.NewEncoder(hasher)
encoder.SetEscapeHTML(false)

return agoric.NewKVHookingReader(kvReader, func(entry agoric.KVEntry) error {
// The KVStore does not mutate the key and value byte slices
key := conv.UnsafeStrToBytes(entry.Key())

if !entry.HasValue() {
swingStore.Delete(key)
} else {
swingStore.Set(key, conv.UnsafeStrToBytes(entry.StringValue()))
}

return encoder.Encode(entry)
}, func() error {
sum := hasher.Sum(nil)
if !bytes.Equal(sum, sha256Hash) {
return fmt.Errorf("swing-store data sha256sum didn't match. expected %x, got %x", sha256Hash, sum)
}
return nil
}), nil
}
}

err = swingStoreExportsHandler.RestoreExport(
Expand All @@ -80,25 +136,17 @@ func ExportGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingSto
gs := &types.GenesisState{
Params: k.GetParams(ctx),
State: k.GetState(ctx),
SwingStoreExportData: []*types.SwingStoreExportDataEntry{},
}

exportDataIterator := k.GetSwingStore(ctx).Iterator(nil, nil)
defer exportDataIterator.Close()
for ; exportDataIterator.Valid(); exportDataIterator.Next() {
entry := types.SwingStoreExportDataEntry{
Key: string(exportDataIterator.Key()),
Value: string(exportDataIterator.Value()),
}
gs.SwingStoreExportData = append(gs.SwingStoreExportData, &entry)
SwingStoreExportData: nil,
}

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,
swingStoreGenesisEventHandler{exportDir: swingStoreExportDir, snapshotHeight: snapshotHeight},
eventHandler,
keeper.SwingStoreExportOptions{
ArtifactMode: keeper.SwingStoreArtifactModeOperational,
ExportDataMode: keeper.SwingStoreExportDataModeSkip,
Expand All @@ -113,12 +161,16 @@ func ExportGenesis(ctx sdk.Context, k Keeper, swingStoreExportsHandler *SwingSto
panic(err)
}

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

return gs
}

type swingStoreGenesisEventHandler struct {
exportDir string
snapshotHeight uint64
swingStore sdk.KVStore
hasher hash.Hash
}

func (eventHandler swingStoreGenesisEventHandler) OnExportStarted(height uint64, retrieveSwingStoreExport func() error) error {
Expand All @@ -132,7 +184,17 @@ func (eventHandler swingStoreGenesisEventHandler) OnExportRetrieved(provider kee

artifactsProvider := keeper.SwingStoreExportProvider{
GetExportDataReader: func() (agoric.KVEntryReader, error) {
return nil, nil
exportDataIterator := eventHandler.swingStore.Iterator(nil, nil)
kvReader := agoric.NewKVIteratorReader(exportDataIterator)
eventHandler.hasher.Reset()
encoder := json.NewEncoder(eventHandler.hasher)
encoder.SetEscapeHTML(false)

return agoric.NewKVHookingReader(kvReader, func(entry agoric.KVEntry) error {
return encoder.Encode(entry)
}, func() error {
return nil
}), nil
},
ReadNextArtifact: provider.ReadNextArtifact,
}
Expand Down
17 changes: 14 additions & 3 deletions golang/cosmos/x/swingset/keeper/swing_store_exports_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,18 @@ func (exportsHandler SwingStoreExportsHandler) RestoreExport(provider SwingStore
// a jsonl-like file, before saving the export manifest linking these together.
// The export manifest filename and overall export format is common with the JS
// swing-store import/export logic.
func WriteSwingStoreExportToDirectory(provider SwingStoreExportProvider, exportDir string) error {
func WriteSwingStoreExportToDirectory(provider SwingStoreExportProvider, exportDir string) (err error) {
handleDeferError := func(fn func() error) {
deferError := fn()
if err == nil {
err = deferError
} else if deferError != nil {
// Safe to wrap error and use detailed error info since this error
// will not go back into swingset layers
err = sdkerrors.Wrapf(err, "deferred error %+v", deferError)
}
}

manifest := exportManifest{
BlockHeight: provider.BlockHeight,
}
Expand All @@ -799,14 +810,14 @@ func WriteSwingStoreExportToDirectory(provider SwingStoreExportProvider, exportD
}

if exportDataReader != nil {
defer exportDataReader.Close()
defer handleDeferError(exportDataReader.Close)

manifest.Data = exportDataFilename
exportDataFile, err := os.OpenFile(filepath.Join(exportDir, exportDataFilename), os.O_CREATE|os.O_WRONLY, exportedFilesMode)
if err != nil {
return err
}
defer exportDataFile.Close()
defer handleDeferError(exportDataFile.Close)

err = agoric.EncodeKVEntryReaderToJsonl(exportDataReader, exportDataFile)
if err != nil {
Expand Down
Loading

0 comments on commit f036c5c

Please sign in to comment.