github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/availability/test/corrupt_data.go (about) 1 package availability_test 2 3 import ( 4 "context" 5 "crypto/rand" 6 "fmt" 7 mrand "math/rand" 8 "testing" 9 10 "github.com/ipfs/boxo/blockstore" 11 blocks "github.com/ipfs/go-block-format" 12 "github.com/ipfs/go-cid" 13 ds "github.com/ipfs/go-datastore" 14 dssync "github.com/ipfs/go-datastore/sync" 15 ) 16 17 var _ blockstore.Blockstore = (*FraudulentBlockstore)(nil) 18 19 // CorruptBlock is a block where the cid doesn't match the data. It fulfills the blocks.Block 20 // interface. 21 type CorruptBlock struct { 22 cid cid.Cid 23 data []byte 24 } 25 26 func (b *CorruptBlock) RawData() []byte { 27 return b.data 28 } 29 30 func (b *CorruptBlock) Cid() cid.Cid { 31 return b.cid 32 } 33 34 func (b *CorruptBlock) String() string { 35 return fmt.Sprintf("[Block %s]", b.Cid()) 36 } 37 38 func (b *CorruptBlock) Loggable() map[string]interface{} { 39 return map[string]interface{}{ 40 "block": b.Cid().String(), 41 } 42 } 43 44 func NewCorruptBlock(data []byte, fakeCID cid.Cid) *CorruptBlock { 45 return &CorruptBlock{ 46 fakeCID, 47 data, 48 } 49 } 50 51 // FraudulentBlockstore is a mock blockstore.Blockstore that saves both corrupted and original data 52 // for every block it receives. If FraudulentBlockstore.Attacking is true, it will serve the 53 // corrupted data on requests. 54 type FraudulentBlockstore struct { 55 ds.Datastore 56 Attacking bool 57 } 58 59 func (fb FraudulentBlockstore) Has(context.Context, cid.Cid) (bool, error) { 60 return false, nil 61 } 62 63 func (fb FraudulentBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) { 64 key := cid.String() 65 if fb.Attacking { 66 key = "corrupt_get" + key 67 } 68 69 data, err := fb.Datastore.Get(ctx, ds.NewKey(key)) 70 if err != nil { 71 return nil, err 72 } 73 return NewCorruptBlock(data, cid), nil 74 } 75 76 func (fb FraudulentBlockstore) GetSize(ctx context.Context, cid cid.Cid) (int, error) { 77 key := cid.String() 78 if fb.Attacking { 79 key = "corrupt_size" + key 80 } 81 82 return fb.Datastore.GetSize(ctx, ds.NewKey(key)) 83 } 84 85 func (fb FraudulentBlockstore) Put(ctx context.Context, block blocks.Block) error { 86 err := fb.Datastore.Put(ctx, ds.NewKey(block.Cid().String()), block.RawData()) 87 if err != nil { 88 return err 89 } 90 91 // create data that doesn't match the CID with arbitrary lengths between 1 and 92 // len(block.RawData())*2 93 corrupted := make([]byte, 1+mrand.Int()%(len(block.RawData())*2-1)) //nolint:gosec 94 _, _ = rand.Read(corrupted) 95 return fb.Datastore.Put(ctx, ds.NewKey("corrupt"+block.Cid().String()), corrupted) 96 } 97 98 func (fb FraudulentBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error { 99 for _, b := range blocks { 100 err := fb.Put(ctx, b) 101 if err != nil { 102 return err 103 } 104 } 105 return nil 106 } 107 108 func (fb FraudulentBlockstore) DeleteBlock(context.Context, cid.Cid) error { 109 panic("implement me") 110 } 111 112 func (fb FraudulentBlockstore) AllKeysChan(context.Context) (<-chan cid.Cid, error) { 113 panic("implement me") 114 } 115 116 func (fb FraudulentBlockstore) HashOnRead(bool) { 117 panic("implement me") 118 } 119 120 // MockNode creates a TestNode that uses a FraudulentBlockstore to simulate serving corrupted data. 121 func MockNode(t *testing.T, net *TestDagNet) (*TestNode, *FraudulentBlockstore) { 122 t.Helper() 123 dstore := dssync.MutexWrap(ds.NewMapDatastore()) 124 mockBS := &FraudulentBlockstore{ 125 Datastore: dstore, 126 Attacking: false, 127 } 128 provider := net.NewTestNodeWithBlockstore(dstore, mockBS) 129 return provider, mockBS 130 }