github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/ipld/sample.go (about)

     1  package ipld
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"math/big"
     6  
     7  	"github.com/ipfs/go-cid"
     8  	"github.com/lazyledger/nmt/namespace"
     9  
    10  	"github.com/lazyledger/lazyledger-core/ipfs/plugin"
    11  	"github.com/lazyledger/lazyledger-core/types"
    12  )
    13  
    14  // Sample is a point in 2D space over square.
    15  type Sample struct {
    16  	Row, Col uint32
    17  }
    18  
    19  // SampleSquare randomly picks *num* unique points from arbitrary *width* square
    20  // and returns them as samples.
    21  func SampleSquare(squareWidth uint32, num int) []Sample {
    22  	ss := newSquareSampler(squareWidth, num)
    23  	ss.sample(num)
    24  	return ss.samples()
    25  }
    26  
    27  // Leaf returns leaf info needed for retrieval using data provided with DAHeader.
    28  func (s Sample) Leaf(dah *types.DataAvailabilityHeader) (cid.Cid, uint32, error) {
    29  	var (
    30  		leaf uint32
    31  		root namespace.IntervalDigest
    32  	)
    33  
    34  	// spread leaves retrieval from both Row and Column roots
    35  	if randUint32(2) == 0 {
    36  		root = dah.ColumnRoots[s.Col]
    37  		leaf = s.Row
    38  	} else {
    39  		root = dah.RowsRoots[s.Row]
    40  		leaf = s.Col
    41  	}
    42  
    43  	rootCid, err := plugin.CidFromNamespacedSha256(root.Bytes())
    44  	if err != nil {
    45  		return cid.Undef, 0, err
    46  	}
    47  
    48  	return rootCid, leaf, nil
    49  }
    50  
    51  // Equals check whenever to samples are equal.
    52  func (s Sample) Equals(to Sample) bool {
    53  	return s.Row == to.Row && s.Col == to.Col
    54  }
    55  
    56  type squareSampler struct {
    57  	squareWidth uint32
    58  	smpls       map[Sample]struct{}
    59  }
    60  
    61  func newSquareSampler(squareWidth uint32, expectedSamples int) *squareSampler {
    62  	return &squareSampler{
    63  		squareWidth: squareWidth,
    64  		smpls:       make(map[Sample]struct{}, expectedSamples),
    65  	}
    66  }
    67  
    68  func (ss *squareSampler) sample(num int) {
    69  	if uint32(num) > ss.squareWidth*ss.squareWidth {
    70  		panic("number of samples must be less than square width")
    71  	}
    72  
    73  	done := 0
    74  	for done < num {
    75  		s := Sample{
    76  			Row: randUint32(ss.squareWidth),
    77  			Col: randUint32(ss.squareWidth),
    78  		}
    79  
    80  		if _, ok := ss.smpls[s]; ok {
    81  			continue
    82  		}
    83  
    84  		done++
    85  		ss.smpls[s] = struct{}{}
    86  	}
    87  }
    88  
    89  func (ss *squareSampler) samples() []Sample {
    90  	samples := make([]Sample, 0, len(ss.smpls))
    91  	for s := range ss.smpls {
    92  		samples = append(samples, s)
    93  	}
    94  	return samples
    95  }
    96  
    97  func randUint32(max uint32) uint32 {
    98  	n, err := crand.Int(crand.Reader, big.NewInt(int64(max)))
    99  	if err != nil {
   100  		panic(err) // won't panic as rand.Reader is endless
   101  	}
   102  
   103  	return uint32(n.Int64())
   104  }