github.com/ethersphere/bee/v2@v2.2.0/pkg/replicas/replicas.go (about)

     1  // Copyright 2023 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package replicas implements a scheme to replicate chunks
     6  // in such a way that
     7  // - the replicas are optimally dispersed to aid cross-neighbourhood redundancy
     8  // - the replicas addresses can be deduced by retrievers only knowing the address
     9  // of the original content addressed chunk
    10  // - no new chunk validation rules are introduced
    11  package replicas
    12  
    13  import (
    14  	"time"
    15  
    16  	"github.com/ethersphere/bee/v2/pkg/crypto"
    17  	"github.com/ethersphere/bee/v2/pkg/file/redundancy"
    18  	"github.com/ethersphere/bee/v2/pkg/swarm"
    19  )
    20  
    21  var (
    22  	// RetryInterval is the duration between successive additional requests
    23  	RetryInterval = 300 * time.Millisecond
    24  	privKey, _    = crypto.DecodeSecp256k1PrivateKey(append([]byte{1}, make([]byte, 31)...))
    25  	signer        = crypto.NewDefaultSigner(privKey)
    26  )
    27  
    28  // replicator running the find for replicas
    29  type replicator struct {
    30  	addr   []byte       // chunk address
    31  	queue  [16]*replica // to sort addresses according to di
    32  	exist  [30]bool     //  maps the 16 distinct nibbles on all levels
    33  	sizes  [5]int       // number of distinct neighnourhoods redcorded for each depth
    34  	c      chan *replica
    35  	rLevel redundancy.Level
    36  }
    37  
    38  // newReplicator replicator constructor
    39  func newReplicator(addr swarm.Address, rLevel redundancy.Level) *replicator {
    40  	rr := &replicator{
    41  		addr:   addr.Bytes(),
    42  		sizes:  redundancy.GetReplicaCounts(),
    43  		c:      make(chan *replica, 16),
    44  		rLevel: rLevel,
    45  	}
    46  	go rr.replicas()
    47  	return rr
    48  }
    49  
    50  // replica of the mined SOC chunk (address) that serve as replicas
    51  type replica struct {
    52  	addr, id []byte // byte slice of SOC address and SOC ID
    53  }
    54  
    55  // replicate returns a replica params structure seeded with a byte of entropy as argument
    56  func (rr *replicator) replicate(i uint8) (sp *replica) {
    57  	// change the last byte of the address to create SOC ID
    58  	id := make([]byte, 32)
    59  	copy(id, rr.addr)
    60  	id[0] = i
    61  	// calculate SOC address for potential replica
    62  	h := swarm.NewHasher()
    63  	_, _ = h.Write(id)
    64  	_, _ = h.Write(swarm.ReplicasOwner)
    65  	return &replica{h.Sum(nil), id}
    66  }
    67  
    68  // replicas enumerates replica parameters (SOC ID) pushing it in a channel given as argument
    69  // the order of replicas is so that addresses are always maximally dispersed
    70  // in successive sets of addresses.
    71  // I.e., the binary tree representing the new addresses prefix bits up to depth is balanced
    72  func (rr *replicator) replicas() {
    73  	defer close(rr.c)
    74  	n := 0
    75  	for i := uint8(0); n < rr.rLevel.GetReplicaCount() && i < 255; i++ {
    76  		// create soc replica (ID and address using constant owner)
    77  		// the soc is added to neighbourhoods of depths in the closed interval [from...to]
    78  		r := rr.replicate(i)
    79  		d, m := rr.add(r, rr.rLevel)
    80  		if d == 0 {
    81  			continue
    82  		}
    83  		for m, r = range rr.queue[n:] {
    84  			if r == nil {
    85  				break
    86  			}
    87  			rr.c <- r
    88  		}
    89  		n += m
    90  	}
    91  }
    92  
    93  // add inserts the soc replica into a replicator so that addresses are balanced
    94  func (rr *replicator) add(r *replica, rLevel redundancy.Level) (depth int, rank int) {
    95  	if rLevel == redundancy.NONE {
    96  		return 0, 0
    97  	}
    98  	nh := nh(rLevel, r.addr)
    99  	if rr.exist[nh] {
   100  		return 0, 0
   101  	}
   102  	rr.exist[nh] = true
   103  	l, o := rr.add(r, rLevel.Decrement())
   104  	d := uint8(rLevel) - 1
   105  	if l == 0 {
   106  		o = rr.sizes[d]
   107  		rr.sizes[d]++
   108  		rr.queue[o] = r
   109  		l = rLevel.GetReplicaCount()
   110  	}
   111  	return l, o
   112  }
   113  
   114  // UTILS
   115  
   116  // index bases needed to keep track how many addresses were mined for a level.
   117  var replicaIndexBases = [5]int{0, 2, 6, 14}
   118  
   119  // nh returns the lookup key based on the redundancy level
   120  // to be used as index to the replicators exist array
   121  func nh(rLevel redundancy.Level, addr []byte) int {
   122  	d := uint8(rLevel)
   123  	return replicaIndexBases[d-1] + int(addr[0]>>(8-d))
   124  }