-
Notifications
You must be signed in to change notification settings - Fork 409
/
snapshotter.go
153 lines (126 loc) · 4.08 KB
/
snapshotter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
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/CosmWasm/wasmd/x/wasm/ioutils"
"github.com/CosmWasm/wasmd/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 {
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)
}