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  }