-
Notifications
You must be signed in to change notification settings - Fork 898
/
corrupt_data.go
130 lines (108 loc) · 3.18 KB
/
corrupt_data.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
package availability_test
import (
"context"
"crypto/rand"
"fmt"
mrand "math/rand"
"testing"
"github.com/ipfs/boxo/blockstore"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
ds "github.com/ipfs/go-datastore"
dssync "github.com/ipfs/go-datastore/sync"
)
var _ blockstore.Blockstore = (*FraudulentBlockstore)(nil)
// CorruptBlock is a block where the cid doesn't match the data. It fulfills the blocks.Block
// interface.
type CorruptBlock struct {
cid cid.Cid
data []byte
}
func (b *CorruptBlock) RawData() []byte {
return b.data
}
func (b *CorruptBlock) Cid() cid.Cid {
return b.cid
}
func (b *CorruptBlock) String() string {
return fmt.Sprintf("[Block %s]", b.Cid())
}
func (b *CorruptBlock) Loggable() map[string]interface{} {
return map[string]interface{}{
"block": b.Cid().String(),
}
}
func NewCorruptBlock(data []byte, fakeCID cid.Cid) *CorruptBlock {
return &CorruptBlock{
fakeCID,
data,
}
}
// FraudulentBlockstore is a mock blockstore.Blockstore that saves both corrupted and original data
// for every block it receives. If FraudulentBlockstore.Attacking is true, it will serve the
// corrupted data on requests.
type FraudulentBlockstore struct {
ds.Datastore
Attacking bool
}
func (fb FraudulentBlockstore) Has(context.Context, cid.Cid) (bool, error) {
return false, nil
}
func (fb FraudulentBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) {
key := cid.String()
if fb.Attacking {
key = "corrupt_get" + key
}
data, err := fb.Datastore.Get(ctx, ds.NewKey(key))
if err != nil {
return nil, err
}
return NewCorruptBlock(data, cid), nil
}
func (fb FraudulentBlockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) {
key := cid.String()
if fb.Attacking {
key = "corrupt_size" + key
}
return fb.Datastore.GetSize(ctx, ds.NewKey(key))
}
func (fb FraudulentBlockstore) Put(ctx context.Context, block blocks.Block) error {
err := fb.Datastore.Put(ctx, ds.NewKey(block.Cid().String()), block.RawData())
if err != nil {
return err
}
// create data that doesn't match the CID with arbitrary lengths between 1 and
// len(block.RawData())*2
corrupted := make([]byte, 1+mrand.Int()%(len(block.RawData())*2-1)) //nolint:gosec
_, _ = rand.Read(corrupted)
return fb.Datastore.Put(ctx, ds.NewKey("corrupt"+block.Cid().String()), corrupted)
}
func (fb FraudulentBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error {
for _, b := range blocks {
err := fb.Put(ctx, b)
if err != nil {
return err
}
}
return nil
}
func (fb FraudulentBlockstore) DeleteBlock(context.Context, cid.Cid) error {
panic("implement me")
}
func (fb FraudulentBlockstore) AllKeysChan(context.Context) (<-chan cid.Cid, error) {
panic("implement me")
}
func (fb FraudulentBlockstore) HashOnRead(bool) {
panic("implement me")
}
// MockNode creates a TestNode that uses a FraudulentBlockstore to simulate serving corrupted data.
func MockNode(t *testing.T, net *TestDagNet) (*TestNode, *FraudulentBlockstore) {
t.Helper()
dstore := dssync.MutexWrap(ds.NewMapDatastore())
mockBS := &FraudulentBlockstore{
Datastore: dstore,
Attacking: false,
}
provider := net.NewTestNodeWithBlockstore(dstore, mockBS)
return provider, mockBS
}