@@ -3,10 +3,10 @@ package kvstore
33
44import (
55 "bytes"
6+ "encoding/hex"
67 "encoding/json"
78 "errors"
89 "fmt"
9- "math"
1010 "os"
1111 "path/filepath"
1212
@@ -15,6 +15,7 @@ import (
1515 abci "github.com/tendermint/tendermint/abci/types"
1616 "github.com/tendermint/tendermint/crypto"
1717 tmbytes "github.com/tendermint/tendermint/libs/bytes"
18+ "github.com/tendermint/tendermint/libs/ds"
1819)
1920
2021const (
@@ -27,11 +28,17 @@ const (
2728// SnapshotStore stores state sync snapshots. Snapshots are stored simply as
2829// JSON files, and chunks are generated on-the-fly by splitting the JSON data
2930// into fixed-size chunks.
30- type SnapshotStore struct {
31- sync.RWMutex
32- dir string
33- metadata []abci.Snapshot
34- }
31+ type (
32+ SnapshotStore struct {
33+ sync.RWMutex
34+ dir string
35+ metadata []abci.Snapshot
36+ }
37+ chunkItem struct {
38+ Data []byte `json:"data"`
39+ NextChunkIDs [][]byte `json:"nextChunkIDs"`
40+ }
41+ )
3542
3643// NewSnapshotStore creates a new snapshot store.
3744func NewSnapshotStore (dir string ) (* SnapshotStore , error ) {
@@ -49,7 +56,7 @@ func NewSnapshotStore(dir string) (*SnapshotStore, error) {
4956// called internally on construction.
5057func (s * SnapshotStore ) loadMetadata () error {
5158 file := filepath .Join (s .dir , "metadata.json" )
52- metadata := []abci.Snapshot {}
59+ var metadata []abci.Snapshot
5360
5461 bz , err := os .ReadFile (file )
5562 switch {
@@ -96,10 +103,9 @@ func (s *SnapshotStore) Create(state State) (abci.Snapshot, error) {
96103 }
97104 height := state .GetHeight ()
98105 snapshot := abci.Snapshot {
99- Height : uint64 (height ),
100- Format : 1 ,
101- Hash : crypto .Checksum (bz ),
102- Chunks : byteChunks (bz ),
106+ Height : uint64 (height ),
107+ Version : 1 ,
108+ Hash : crypto .Checksum (bz ),
103109 }
104110 err = os .WriteFile (filepath .Join (s .dir , fmt .Sprintf ("%v.json" , height )), bz , 0644 )
105111 if err != nil {
@@ -152,16 +158,18 @@ func (s *SnapshotStore) List() ([]*abci.Snapshot, error) {
152158}
153159
154160// LoadChunk loads a snapshot chunk.
155- func (s * SnapshotStore ) LoadChunk (height uint64 , format uint32 , chunk uint32 ) ([]byte , error ) {
161+ func (s * SnapshotStore ) LoadChunk (height uint64 , version uint32 , chunkID [] byte ) ([]byte , error ) {
156162 s .RLock ()
157163 defer s .RUnlock ()
158164 for _ , snapshot := range s .metadata {
159- if snapshot .Height == height && snapshot .Format == format {
160- bz , err := os .ReadFile (filepath .Join (s .dir , fmt .Sprintf ("%v .json" , height )))
165+ if snapshot .Height == height && snapshot .Version == version {
166+ bz , err := os .ReadFile (filepath .Join (s .dir , fmt .Sprintf ("%d .json" , height )))
161167 if err != nil {
162168 return nil , err
163169 }
164- return byteChunk (bz , chunk ), nil
170+ chunks := makeChunks (bz , snapshotChunkSize )
171+ item := makeChunkItem (chunks , chunkID )
172+ return json .Marshal (item )
165173 }
166174 }
167175 return nil , nil
@@ -170,54 +178,79 @@ func (s *SnapshotStore) LoadChunk(height uint64, format uint32, chunk uint32) ([
170178type offerSnapshot struct {
171179 snapshot * abci.Snapshot
172180 appHash tmbytes.HexBytes
173- chunks [][]byte
174- chunkCnt int
181+ chunks * ds.OrderedMap [string , []byte ]
175182}
176183
177184func newOfferSnapshot (snapshot * abci.Snapshot , appHash tmbytes.HexBytes ) * offerSnapshot {
178185 return & offerSnapshot {
179186 snapshot : snapshot ,
180187 appHash : appHash ,
181- chunks : make ([][]byte , snapshot .Chunks ),
182- chunkCnt : 0 ,
188+ chunks : ds .NewOrderedMap [string , []byte ](),
183189 }
184190}
185191
186- func (s * offerSnapshot ) addChunk (index int , chunk []byte ) {
187- if s .chunks [index ] != nil {
188- return
192+ func (s * offerSnapshot ) addChunk (chunkID tmbytes.HexBytes , data []byte ) [][]byte {
193+ chunkIDStr := chunkID .String ()
194+ if s .chunks .Has (chunkIDStr ) {
195+ return nil
189196 }
190- s .chunks [index ] = chunk
191- s .chunkCnt ++
197+ var item chunkItem
198+ err := json .Unmarshal (data , & item )
199+ if err != nil {
200+ panic ("failed to decode a chunk data: " + err .Error ())
201+ }
202+ s .chunks .Put (chunkIDStr , item .Data )
203+ return item .NextChunkIDs
192204}
193205
194206func (s * offerSnapshot ) isFull () bool {
195- return s . chunkCnt == int ( s .snapshot .Chunks )
207+ return bytes . Equal ( crypto . Checksum ( s . bytes ()), s .snapshot .Hash )
196208}
197209
198210func (s * offerSnapshot ) bytes () []byte {
211+ chunks := s .chunks .Values ()
199212 buf := bytes .NewBuffer (nil )
200- for _ , chunk := range s . chunks {
213+ for _ , chunk := range chunks {
201214 buf .Write (chunk )
202215 }
203216 return buf .Bytes ()
204217}
205218
206- // byteChunk returns the chunk at a given index from the full byte slice.
207- func byteChunk (bz []byte , index uint32 ) []byte {
208- start := int (index * snapshotChunkSize )
209- end := int ((index + 1 ) * snapshotChunkSize )
210- switch {
211- case start >= len (bz ):
212- return nil
213- case end >= len (bz ):
214- return bz [start :]
215- default :
216- return bz [start :end ]
219+ // makeChunkItem returns the chunk at a given index from the full byte slice.
220+ func makeChunkItem (chunks * ds.OrderedMap [string , []byte ], chunkID []byte ) chunkItem {
221+ chunkIDStr := hex .EncodeToString (chunkID )
222+ val , ok := chunks .Get (chunkIDStr )
223+ if ! ok {
224+ panic ("chunk not found" )
217225 }
226+ chunkIDs := chunks .Keys ()
227+ ci := chunkItem {Data : val }
228+ i := 0
229+ for ; i < len (chunkIDs ) && chunkIDs [i ] != chunkIDStr ; i ++ {
230+ }
231+ if i + 1 < len (chunkIDs ) {
232+ data , err := hex .DecodeString (chunkIDs [i + 1 ])
233+ if err != nil {
234+ panic (err )
235+ }
236+ ci .NextChunkIDs = [][]byte {data }
237+ }
238+ return ci
218239}
219240
220- // byteChunks calculates the number of chunks in the byte slice.
221- func byteChunks (bz []byte ) uint32 {
222- return uint32 (math .Ceil (float64 (len (bz )) / snapshotChunkSize ))
241+ func makeChunks (bz []byte , chunkSize int ) * ds.OrderedMap [string , []byte ] {
242+ chunks := ds .NewOrderedMap [string , []byte ]()
243+ totalHash := hex .EncodeToString (crypto .Checksum (bz ))
244+ key := totalHash
245+ for i := 0 ; i < len (bz ); i += chunkSize {
246+ j := i + chunkSize
247+ if j > len (bz ) {
248+ j = len (bz )
249+ }
250+ if i > 1 {
251+ key = hex .EncodeToString (crypto .Checksum (bz [i :j ]))
252+ }
253+ chunks .Put (key , append ([]byte (nil ), bz [i :j ]... ))
254+ }
255+ return chunks
223256}
0 commit comments