-
Notifications
You must be signed in to change notification settings - Fork 4
/
snapshotter.go
204 lines (168 loc) · 4.96 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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package keeper
import (
"errors"
"io"
sdkerrors "cosmossdk.io/errors"
"cosmossdk.io/log"
"cosmossdk.io/math"
snapshot "cosmossdk.io/store/snapshots/types"
storetypes "cosmossdk.io/store/types"
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ODIN-PROTOCOL/odin-core/pkg/filecache"
"github.com/ODIN-PROTOCOL/odin-core/pkg/gzip"
"github.com/ODIN-PROTOCOL/odin-core/x/oracle/types"
)
var _ snapshot.ExtensionSnapshotter = &OracleSnapshotter{}
// SnapshotFormat format 1 is just gzipped byte code for each oracle scripts and data sources.
const SnapshotFormat = 1
type OracleSnapshotter struct {
keeper *Keeper
cms storetypes.MultiStore
}
func NewOracleSnapshotter(cms storetypes.MultiStore, keeper *Keeper) *OracleSnapshotter {
return &OracleSnapshotter{
keeper: keeper,
cms: cms,
}
}
func (os *OracleSnapshotter) SnapshotName() string {
return types.ModuleName
}
func (os *OracleSnapshotter) SnapshotFormat() uint32 {
return SnapshotFormat
}
func (os *OracleSnapshotter) SupportedFormats() []uint32 {
return []uint32{SnapshotFormat}
}
func (os *OracleSnapshotter) SnapshotExtension(height uint64, payloadWriter snapshot.ExtensionPayloadWriter) error {
cacheMS, err := os.cms.CacheMultiStoreWithVersion(int64(height))
if err != nil {
return err
}
ctx := sdk.NewContext(cacheMS, tmproto.Header{}, false, log.NewNopLogger())
seenBefore := make(map[string]bool)
// write all oracle scripts to snapshot
oracleScripts, err := os.keeper.GetAllOracleScripts(ctx)
if err != nil {
return err
}
for _, oracleScript := range oracleScripts {
if err := writeFileToSnapshot(payloadWriter, oracleScript.Filename, os.keeper, seenBefore); err != nil {
return err
}
}
// write all data sources to snapshot
dataSources, err := os.keeper.GetAllDataSources(ctx)
if err != nil {
return err
}
for _, dataSource := range dataSources {
if err := writeFileToSnapshot(payloadWriter, dataSource.Filename, os.keeper, seenBefore); err != nil {
return err
}
}
return nil
}
func (os *OracleSnapshotter) RestoreExtension(
height uint64, format uint32, payloadReader snapshot.ExtensionPayloadReader,
) error {
if format == SnapshotFormat {
return os.processAllItems(height, payloadReader, restoreV1, finalizeV1)
}
return snapshot.ErrUnknownFormat
}
func (os *OracleSnapshotter) processAllItems(
height uint64,
payloadReader snapshot.ExtensionPayloadReader,
restore func(sdk.Context, *Keeper, []byte, map[string]bool) error,
finalize func(sdk.Context, *Keeper, map[string]bool) error,
) error {
ctx := sdk.NewContext(os.cms, tmproto.Header{Height: int64(height)}, false, log.NewNopLogger())
// get all filename that we need to find and construct a map to store found status
foundCode := make(map[string]bool)
oracleScripts, err := os.keeper.GetAllOracleScripts(ctx)
if err != nil {
return err
}
for _, oracleScript := range oracleScripts {
foundCode[oracleScript.Filename] = false
}
dataSources, err := os.keeper.GetAllDataSources(ctx)
if err != nil {
return err
}
for _, dataSource := range dataSources {
foundCode[dataSource.Filename] = false
}
for {
payload, err := payloadReader()
if err == io.EOF {
break
} else if err != nil {
return sdkerrors.Wrap(err, "invalid protobuf message")
}
if err := restore(ctx, os.keeper, payload, foundCode); err != nil {
return sdkerrors.Wrap(err, "processing snapshot item")
}
}
return finalize(ctx, os.keeper, foundCode)
}
func writeFileToSnapshot(
payloadWriter snapshot.ExtensionPayloadWriter,
filename string,
k *Keeper,
seenBefore map[string]bool,
) error {
// no need to write if we write it before
if seenBefore[filename] {
return nil
}
seenBefore[filename] = true
// get byte code from filename
bytes, err := k.fileCache.GetFile(filename)
if err != nil {
return err
}
// zip it
compressBytes, err := gzip.Compress(bytes)
if err != nil {
return err
}
// write it to snapshot
if err = payloadWriter(compressBytes); err != nil {
return err
}
return nil
}
func restoreV1(_ sdk.Context, k *Keeper, compressedCode []byte, foundCode map[string]bool) error {
// uncompress code
code, err := gzip.Uncompress(
compressedCode,
int64(math.Max(types.MaxExecutableSize, types.MaxWasmCodeSize, types.MaxCompiledWasmCodeSize)),
)
if err != nil {
return sdkerrors.Wrapf(types.ErrUncompressionFailed, err.Error())
}
// check if we really need this file or not first
filename := filecache.GetFilename(code)
found, required := foundCode[filename]
if !required {
return errors.New("found unexpected code in the snapshot")
}
if !found {
// add the file to disk
foundCode[filename] = true
k.fileCache.AddFile(code)
}
return nil
}
func finalizeV1(_ sdk.Context, _ *Keeper, foundCode map[string]bool) error {
// check if there is any required code that we can't find in restore process
for _, found := range foundCode {
if !found {
return errors.New("some code is missing from the snapshot")
}
}
return nil
}