laconicd-deprecated/x/wasm/keeper/snapshotter.go
2023-02-28 15:05:49 +05:30

157 lines
4.2 KiB
Go

package keeper
import (
"encoding/hex"
"io"
snapshot "github.com/cosmos/cosmos-sdk/snapshots/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
protoio "github.com/gogo/protobuf/io"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/cerc-io/laconicd/x/wasm/ioutils"
"github.com/cerc-io/laconicd/x/wasm/types"
)
var _ snapshot.ExtensionSnapshotter = &WasmSnapshotter{}
// SnapshotFormat format 1 is just gzipped wasm byte code for each item payload. No protobuf envelope, no metadata.
const SnapshotFormat = 1
type WasmSnapshotter struct {
wasm *Keeper
cms sdk.MultiStore
}
func NewWasmSnapshotter(cms sdk.MultiStore, wasm *Keeper) *WasmSnapshotter {
return &WasmSnapshotter{
wasm: wasm,
cms: cms,
}
}
func (ws *WasmSnapshotter) SnapshotName() string {
return types.ModuleName
}
func (ws *WasmSnapshotter) SnapshotFormat() uint32 {
return SnapshotFormat
}
func (ws *WasmSnapshotter) SupportedFormats() []uint32 {
// If we support older formats, add them here and handle them in Restore
return []uint32{SnapshotFormat}
}
func (ws *WasmSnapshotter) Snapshot(height uint64, protoWriter protoio.Writer) error {
cacheMS, err := ws.cms.CacheMultiStoreWithVersion(int64(height))
if err != nil {
return err
}
ctx := sdk.NewContext(cacheMS, tmproto.Header{}, false, log.NewNopLogger())
seenBefore := make(map[string]bool)
var rerr error
ws.wasm.IterateCodeInfos(ctx, func(id uint64, info types.CodeInfo) bool {
// Many code ids may point to the same code hash... only sync it once
hexHash := hex.EncodeToString(info.CodeHash)
// if seenBefore, just skip this one and move to the next
if seenBefore[hexHash] {
return false
}
seenBefore[hexHash] = true
// load code and abort on error
wasmBytes, err := ws.wasm.GetByteCode(ctx, id)
if err != nil {
rerr = err
return true
}
compressedWasm, err := ioutils.GzipIt(wasmBytes)
if err != nil {
rerr = err
return true
}
err = snapshot.WriteExtensionItem(protoWriter, compressedWasm)
if err != nil {
rerr = err
return true
}
return false
})
return rerr
}
func (ws *WasmSnapshotter) Restore(
height uint64, format uint32, protoReader protoio.Reader,
) (snapshot.SnapshotItem, error) {
if format == SnapshotFormat {
return ws.processAllItems(height, protoReader, restoreV1, finalizeV1)
}
return snapshot.SnapshotItem{}, snapshot.ErrUnknownFormat
}
func restoreV1(ctx sdk.Context, k *Keeper, compressedCode []byte) error {
if !ioutils.IsGzip(compressedCode) {
return types.ErrInvalid.Wrap("not a gzip")
}
wasmCode, err := ioutils.Uncompress(compressedCode, uint64(types.MaxWasmSize))
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
// FIXME: check which codeIDs the checksum matches??
_, err = k.wasmVM.Create(wasmCode)
if err != nil {
return sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
}
return nil
}
func finalizeV1(ctx sdk.Context, k *Keeper) error {
// FIXME: ensure all codes have been uploaded?
return k.InitializePinnedCodes(ctx)
}
func (ws *WasmSnapshotter) processAllItems(
height uint64,
protoReader protoio.Reader,
cb func(sdk.Context, *Keeper, []byte) error,
finalize func(sdk.Context, *Keeper) error,
) (snapshot.SnapshotItem, error) {
ctx := sdk.NewContext(ws.cms, tmproto.Header{Height: int64(height)}, false, log.NewNopLogger())
// keep the last item here... if we break, it will either be empty (if we hit io.EOF)
// or contain the last item (if we hit payload == nil)
var item snapshot.SnapshotItem
for {
item = snapshot.SnapshotItem{}
err := protoReader.ReadMsg(&item)
if err == io.EOF {
break
} else if err != nil {
return snapshot.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message")
}
// if it is not another ExtensionPayload message, then it is not for us.
// we should return it an let the manager handle this one
payload := item.GetExtensionPayload()
if payload == nil {
break
}
if err := cb(ctx, ws.wasm, payload.Payload); err != nil {
return snapshot.SnapshotItem{}, sdkerrors.Wrap(err, "processing snapshot item")
}
}
return item, finalize(ctx, ws.wasm)
}