mukan-ibc/modules/light-clients/08-wasm/keeper/snapshotter.go
Mukan Erkin Törük 88dd97a9f8
Some checks failed
CodeQL / Analyze (push) Waiting to run
golangci-lint / lint (push) Waiting to run
Tests / Code Coverage / build (amd64) (push) Waiting to run
Tests / Code Coverage / build (arm64) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[additional-args:-tags="test_e2e" name:e2e path:./e2e]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:08-wasm path:./modules/light-clients/08-wasm]) (push) Waiting to run
Tests / Code Coverage / unit-tests (map[name:ibc-go path:.]) (push) Waiting to run
Docker Build & Push Simapp (main) / docker-build (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:22 +03:00

146 lines
4.6 KiB
Go

package keeper
import (
"encoding/hex"
"io"
errorsmod "cosmossdk.io/errors"
snapshot "cosmossdk.io/store/snapshots/types"
storetypes "cosmossdk.io/store/types"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
cmtproto "git.cw.tr/mukan-network/mukan-consensus/proto/tendermint/types"
"github.com/cosmos/ibc-go/modules/light-clients/08-wasm/v10/types"
)
var _ snapshot.ExtensionSnapshotter = &WasmSnapshotter{}
// SnapshotFormat defines the default snapshot extension encoding format.
// SnapshotFormat 1 is gzipped wasm byte code for each item payload. No protobuf envelope, no metadata.
const SnapshotFormat = 1
// WasmSnapshotter implements the snapshot.ExtensionSnapshotter interface and is used to
// import and export state maintained within the wasmvm cache.
// NOTE: The following ExtensionSnapshotter has been adapted from CosmWasm's x/wasm:
// https://github.com/CosmWasm/wasmd/blob/v0.43.0/x/wasm/keeper/snapshotter.go
type WasmSnapshotter struct {
cms storetypes.MultiStore
keeper *Keeper
}
// NewWasmSnapshotter creates and returns a new snapshot.ExtensionSnapshotter implementation for the 08-wasm module.
func NewWasmSnapshotter(cms storetypes.MultiStore, keeper *Keeper) snapshot.ExtensionSnapshotter {
return &WasmSnapshotter{
cms: cms,
keeper: keeper,
}
}
// SnapshotName implements the snapshot.ExtensionSnapshotter interface.
// A unique name should be provided such that the implementation can be identified by the manager.
func (*WasmSnapshotter) SnapshotName() string {
return types.ModuleName
}
// SnapshotFormat implements the snapshot.ExtensionSnapshotter interface.
// This is the default format used for encoding payloads when taking a snapshot.
func (*WasmSnapshotter) SnapshotFormat() uint32 {
return SnapshotFormat
}
// SupportedFormats implements the snapshot.ExtensionSnapshotter interface.
// This defines a list of supported formats the snapshotter extension can restore from.
func (*WasmSnapshotter) SupportedFormats() []uint32 {
return []uint32{SnapshotFormat}
}
// SnapshotExtension implements the snapshot.ExntensionSnapshotter interface.
// SnapshotExtension is used to write data payloads into the underlying protobuf stream from the 08-wasm module.
func (ws *WasmSnapshotter) SnapshotExtension(height uint64, payloadWriter snapshot.ExtensionPayloadWriter) error {
cacheMS, err := ws.cms.CacheMultiStoreWithVersion(int64(height))
if err != nil {
return err
}
ctx := sdk.NewContext(cacheMS, cmtproto.Header{}, false, nil)
checksums, err := ws.keeper.GetAllChecksums(ctx)
if err != nil {
return err
}
for _, checksum := range checksums {
wasmCode, err := ws.keeper.GetVM().GetCode(checksum)
if err != nil {
return err
}
compressedWasm, err := types.GzipIt(wasmCode)
if err != nil {
return err
}
if err = payloadWriter(compressedWasm); err != nil {
return err
}
}
return nil
}
// RestoreExtension implements the snapshot.ExtensionSnapshotter interface.
// RestoreExtension is used to read data from an existing extension state snapshot into the 08-wasm module.
// The payload reader returns io.EOF when it has reached the end of the extension state snapshot.
func (ws *WasmSnapshotter) RestoreExtension(height uint64, format uint32, payloadReader snapshot.ExtensionPayloadReader) error {
if format == ws.SnapshotFormat() {
return ws.processAllItems(height, payloadReader, restoreV1)
}
return errorsmod.Wrapf(snapshot.ErrUnknownFormat, "expected %d, got %d", ws.SnapshotFormat(), format)
}
func restoreV1(ctx sdk.Context, k *Keeper, compressedCode []byte) error {
if !types.IsGzip(compressedCode) {
return errorsmod.Wrap(types.ErrInvalidData, "expected wasm code is not gzip format")
}
wasmCode, err := types.Uncompress(compressedCode, types.MaxWasmSize)
if err != nil {
return errorsmod.Wrap(err, "failed to uncompress wasm code")
}
checksum, err := k.GetVM().StoreCodeUnchecked(wasmCode)
if err != nil {
return errorsmod.Wrap(err, "failed to store wasm code")
}
if err := k.GetVM().Pin(checksum); err != nil {
return errorsmod.Wrapf(err, "failed to pin checksum: %s to in-memory cache", hex.EncodeToString(checksum))
}
return nil
}
func (ws *WasmSnapshotter) processAllItems(
height uint64,
payloadReader snapshot.ExtensionPayloadReader,
cb func(sdk.Context, *Keeper, []byte) error,
) error {
ctx := sdk.NewContext(ws.cms, cmtproto.Header{Height: int64(height)}, false, nil)
for {
payload, err := payloadReader()
if err == io.EOF {
break
} else if err != nil {
return err
}
if err := cb(ctx, ws.keeper, payload); err != nil {
return errorsmod.Wrap(err, "failure processing snapshot item")
}
}
return nil
}