Skip to content

Commit e23e869

Browse files
zeligfjl
authored andcommitted
swarm/network: fix chunk integrity checks (ethereum#3665)
* swarm/network: integrity on incoming known chunks * swarm/network: fix integrity check for incoming chunks * swarm/storage: imrpoved integrity checking on chunks * dbstore panics on corrupt chunk entry an prompts user to run cleandb * memstore adds logging for garbage collection * dbstore refactor item delete. correct partial deletes in Get * cmd/swarm: added cleandb subcommand
1 parent 65ed6a9 commit e23e869

File tree

6 files changed

+125
-21
lines changed

6 files changed

+125
-21
lines changed

cmd/swarm/cleandb.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2016 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"log"
21+
22+
"github.com/ethereum/go-ethereum/swarm/storage"
23+
"gopkg.in/urfave/cli.v1"
24+
)
25+
26+
func cleandb(ctx *cli.Context) {
27+
args := ctx.Args()
28+
if len(args) != 1 {
29+
log.Fatal("need path to chunks database as the first and only argument")
30+
}
31+
32+
chunkDbPath := args[0]
33+
hash := storage.MakeHashFunc("SHA3")
34+
dbStore, err := storage.NewDbStore(chunkDbPath, hash, 10000000, 0)
35+
if err != nil {
36+
log.Fatalf("cannot initialise dbstore: %v", err)
37+
}
38+
dbStore.Cleanup()
39+
}

cmd/swarm/main.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,15 @@ Removes a path from the manifest
191191
},
192192
},
193193
},
194+
{
195+
Action: cleandb,
196+
Name: "cleandb",
197+
Usage: "Cleans database of corrupted entries",
198+
ArgsUsage: " ",
199+
Description: `
200+
Cleans database of corrupted entries.
201+
`,
202+
},
194203
}
195204

196205
app.Flags = []cli.Flag{

swarm/network/depo.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func (self *Depo) HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer)
9999
// if key found locally, return. otherwise
100100
// remote is untrusted, so hash is verified and chunk passed on to NetStore
101101
func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) {
102+
var islocal bool
102103
req.from = p
103104
chunk, err := self.localStore.Get(req.Key)
104105
switch {
@@ -110,27 +111,32 @@ func (self *Depo) HandleStoreRequestMsg(req *storeRequestMsgData, p *peer) {
110111

111112
case chunk.SData == nil:
112113
// found chunk in memory store, needs the data, validate now
113-
hasher := self.hashfunc()
114-
hasher.Write(req.SData)
115-
if !bytes.Equal(hasher.Sum(nil), req.Key) {
116-
// data does not validate, ignore
117-
// TODO: peer should be penalised/dropped?
118-
glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req)
119-
return
120-
}
121114
glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v. request entry found", req)
122115

123116
default:
124117
// data is found, store request ignored
125118
// this should update access count?
126119
glog.V(logger.Detail).Infof("Depo.HandleStoreRequest: %v found locally. ignore.", req)
120+
islocal = true
121+
//return
122+
}
123+
124+
hasher := self.hashfunc()
125+
hasher.Write(req.SData)
126+
if !bytes.Equal(hasher.Sum(nil), req.Key) {
127+
// data does not validate, ignore
128+
// TODO: peer should be penalised/dropped?
129+
glog.V(logger.Warn).Infof("Depo.HandleStoreRequest: chunk invalid. store request ignored: %v", req)
127130
return
128131
}
129132

133+
if islocal {
134+
return
135+
}
130136
// update chunk with size and data
131137
chunk.SData = req.SData // protocol validates that SData is minimum 9 bytes long (int64 size + at least one byte of data)
132138
chunk.Size = int64(binary.LittleEndian.Uint64(req.SData[0:8]))
133-
glog.V(logger.Detail).Infof("delivery of %p from %v", chunk, p)
139+
glog.V(logger.Detail).Infof("delivery of %v from %v", chunk, p)
134140
chunk.Source = p
135141
self.netStore.Put(chunk)
136142
}

swarm/network/syncer.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ LOOP:
438438
for priority = High; priority >= 0; priority-- {
439439
// the first priority channel that is non-empty will be assigned to keys
440440
if len(self.keys[priority]) > 0 {
441-
glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority)
441+
glog.V(logger.Detail).Infof("syncer[%v]: reading request with priority %v", self.key.Log(), priority)
442442
keys = self.keys[priority]
443443
break PRIORITIES
444444
}
@@ -551,10 +551,10 @@ LOOP:
551551
}
552552
if sreq, err := self.newSyncRequest(req, priority); err == nil {
553553
// extract key from req
554-
glog.V(logger.Detail).Infof("syncer(priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced)
554+
glog.V(logger.Detail).Infof("syncer[%v]: (priority %v): request %v (synced = %v)", self.key.Log(), priority, req, state.Synced)
555555
unsynced = append(unsynced, sreq)
556556
} else {
557-
glog.V(logger.Warn).Infof("syncer(priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err)
557+
glog.V(logger.Warn).Infof("syncer[%v]: (priority %v): error creating request for %v: %v)", self.key.Log(), priority, req, state.Synced, err)
558558
}
559559

560560
}

swarm/storage/dbstore.go

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -252,12 +252,7 @@ func (s *DbStore) collectGarbage(ratio float32) {
252252
// actual gc
253253
for i := 0; i < gcnt; i++ {
254254
if s.gcArray[i].value <= cutval {
255-
batch := new(leveldb.Batch)
256-
batch.Delete(s.gcArray[i].idxKey)
257-
batch.Delete(getDataKey(s.gcArray[i].idx))
258-
s.entryCnt--
259-
batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt))
260-
s.db.Write(batch)
255+
s.delete(s.gcArray[i].idx, s.gcArray[i].idxKey)
261256
}
262257
}
263258

@@ -266,6 +261,52 @@ func (s *DbStore) collectGarbage(ratio float32) {
266261
s.db.Put(keyGCPos, s.gcPos)
267262
}
268263

264+
func (s *DbStore) Cleanup() {
265+
//Iterates over the database and checks that there are no faulty chunks
266+
it := s.db.NewIterator()
267+
startPosition := []byte{kpIndex}
268+
it.Seek(startPosition)
269+
var key []byte
270+
var errorsFound, total int
271+
for it.Valid() {
272+
key = it.Key()
273+
if (key == nil) || (key[0] != kpIndex) {
274+
break
275+
}
276+
total++
277+
var index dpaDBIndex
278+
decodeIndex(it.Value(), &index)
279+
280+
data, err := s.db.Get(getDataKey(index.Idx))
281+
if err != nil {
282+
glog.V(logger.Warn).Infof("Chunk %x found but could not be accessed: %v", key[:], err)
283+
s.delete(index.Idx, getIndexKey(key[1:]))
284+
errorsFound++
285+
} else {
286+
hasher := s.hashfunc()
287+
hasher.Write(data)
288+
hash := hasher.Sum(nil)
289+
if !bytes.Equal(hash, key[1:]) {
290+
glog.V(logger.Warn).Infof("Found invalid chunk. Hash mismatch. hash=%x, key=%x", hash, key[:])
291+
s.delete(index.Idx, getIndexKey(key[1:]))
292+
errorsFound++
293+
}
294+
}
295+
it.Next()
296+
}
297+
it.Release()
298+
glog.V(logger.Warn).Infof("Found %v errors out of %v entries", errorsFound, total)
299+
}
300+
301+
func (s *DbStore) delete(idx uint64, idxKey []byte) {
302+
batch := new(leveldb.Batch)
303+
batch.Delete(idxKey)
304+
batch.Delete(getDataKey(idx))
305+
s.entryCnt--
306+
batch.Put(keyEntryCnt, U64ToBytes(s.entryCnt))
307+
s.db.Write(batch)
308+
}
309+
269310
func (s *DbStore) Counter() uint64 {
270311
s.lock.Lock()
271312
defer s.lock.Unlock()
@@ -283,6 +324,7 @@ func (s *DbStore) Put(chunk *Chunk) {
283324
if chunk.dbStored != nil {
284325
close(chunk.dbStored)
285326
}
327+
glog.V(logger.Detail).Infof("Storing to DB: chunk already exists, only update access")
286328
return // already exists, only update access
287329
}
288330

@@ -348,16 +390,17 @@ func (s *DbStore) Get(key Key) (chunk *Chunk, err error) {
348390
var data []byte
349391
data, err = s.db.Get(getDataKey(index.Idx))
350392
if err != nil {
393+
glog.V(logger.Detail).Infof("DBStore: Chunk %v found but could not be accessed: %v", key.Log(), err)
394+
s.delete(index.Idx, getIndexKey(key))
351395
return
352396
}
353397

354398
hasher := s.hashfunc()
355399
hasher.Write(data)
356400
hash := hasher.Sum(nil)
357401
if !bytes.Equal(hash, key) {
358-
s.db.Delete(getDataKey(index.Idx))
359-
err = fmt.Errorf("invalid chunk. hash=%x, key=%v", hash, key[:])
360-
return
402+
s.delete(index.Idx, getIndexKey(key))
403+
panic("Invalid Chunk in Database. Please repair with command: 'swarm cleandb'")
361404
}
362405

363406
chunk = &Chunk{

swarm/storage/memstore.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ package storage
2020

2121
import (
2222
"sync"
23+
24+
"github.com/ethereum/go-ethereum/logger"
25+
"github.com/ethereum/go-ethereum/logger/glog"
2326
)
2427

2528
const (
@@ -284,7 +287,11 @@ func (s *MemStore) removeOldest() {
284287
}
285288

286289
if node.entry.dbStored != nil {
290+
glog.V(logger.Detail).Infof("Memstore Clean: Waiting for chunk %v to be saved", node.entry.Key.Log())
287291
<-node.entry.dbStored
292+
glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v saved to DBStore. Ready to clear from mem.", node.entry.Key.Log())
293+
} else {
294+
glog.V(logger.Detail).Infof("Memstore Clean: Chunk %v already in DB. Ready to delete.", node.entry.Key.Log())
288295
}
289296

290297
if node.entry.SData != nil {

0 commit comments

Comments
 (0)