github.com/decred/dcrlnd@v0.7.6/amp/sharer.go (about)

     1  package amp
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  )
     7  
     8  // zeroShare is the all-zero 32-byte share.
     9  var zeroShare = Share{}
    10  
    11  // Sharer facilitates dynamic splitting of a root share value and derivation of
    12  // child preimage and hashes for individual HTLCs in an AMP payment. A sharer
    13  // represents a specific node in an abstract binary tree that can generate up to
    14  // 2^32-1 unique child preimage-hash pairs for the same share value. A node can
    15  // also be split into it's left and right child in the tree. The Sharer
    16  // guarantees that the share value of the left and right child XOR to the share
    17  // value of the parent. This allows larger HTLCs to split into smaller
    18  // subpayments, while ensuring that the reconstructed secret will exactly match
    19  // the root seed.
    20  type Sharer interface {
    21  	// Root returns the root share of the derivation tree. This is the value
    22  	// that will be reconstructed when combining the set of all child
    23  	// shares.
    24  	Root() Share
    25  
    26  	// Child derives a child preimage and child hash given a 32-bit index.
    27  	// Passing a different index will generate a unique preimage-hash pair
    28  	// with high probability, allowing the payment hash carried on HTLCs to
    29  	// be refreshed without needing to modify the share value. This would
    30  	// typically be used when an partial payment needs to be retried if it
    31  	// encounters routine network failures.
    32  	Child(index uint32) *Child
    33  
    34  	// Split returns a Sharer for the left and right child of the parent
    35  	// Sharer. XORing the share values of both sharers always yields the
    36  	// share value of the parent. The sender should use this to recursively
    37  	// divide payments that are too large into smaller subpayments, knowing
    38  	// that the shares of all nodes descending from the parent will XOR to
    39  	// the parent's share.
    40  	Split() (Sharer, Sharer, error)
    41  
    42  	// Merge takes the given Child and "merges" it into the Sharer by
    43  	// XOR-ing its share with the Sharer's current share.
    44  	Merge(*Child) Sharer
    45  
    46  	// Zero returns a a new "zero Sharer" that has its current share set to
    47  	// zero, while keeping the root share. Merging a Child from the
    48  	// original Sharer into this zero-Sharer gives back the original
    49  	// Sharer.
    50  	Zero() Sharer
    51  }
    52  
    53  // SeedSharer orchestrates the sharing of the root AMP seed along multiple
    54  // paths. It also supports derivation of the child payment hashes that get
    55  // attached to HTLCs, and the child preimages used by the receiver to settle
    56  // individual HTLCs in the set.
    57  type SeedSharer struct {
    58  	root Share
    59  	curr Share
    60  }
    61  
    62  // NewSeedSharer generates a new SeedSharer instance with a seed drawn at
    63  // random.
    64  func NewSeedSharer() (*SeedSharer, error) {
    65  	var root Share
    66  	if _, err := rand.Read(root[:]); err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	return SeedSharerFromRoot(&root), nil
    71  }
    72  
    73  // SeedSharerFromRoot instantiates a SeedSharer with an externally provided
    74  // seed.
    75  func SeedSharerFromRoot(root *Share) *SeedSharer {
    76  	return initSeedSharer(root, root)
    77  }
    78  
    79  func initSeedSharer(root, curr *Share) *SeedSharer {
    80  	return &SeedSharer{
    81  		root: *root,
    82  		curr: *curr,
    83  	}
    84  }
    85  
    86  // Seed returns the sharer's seed, the primary source of entropy for deriving
    87  // shares of the root.
    88  func (s *SeedSharer) Root() Share {
    89  	return s.root
    90  }
    91  
    92  // Split constructs two child Sharers whose shares sum to the parent Sharer.
    93  // This allows an HTLC whose payment amount could not be routed to be
    94  // recursively split into smaller subpayments. After splitting a sharer the
    95  // parent share should no longer be used, and the caller should use the Child
    96  // method on each to derive preimage/hash pairs for the HTLCs.
    97  func (s *SeedSharer) Split() (Sharer, Sharer, error) {
    98  	// We cannot split the zero-Sharer.
    99  	if s.curr == zeroShare {
   100  		return nil, nil, fmt.Errorf("cannot split zero-Sharer")
   101  	}
   102  
   103  	shareLeft, shareRight, err := split(&s.curr)
   104  	if err != nil {
   105  		return nil, nil, err
   106  	}
   107  
   108  	left := initSeedSharer(&s.root, &shareLeft)
   109  	right := initSeedSharer(&s.root, &shareRight)
   110  
   111  	return left, right, nil
   112  }
   113  
   114  // Merge takes the given Child and "merges" it into the Sharer by XOR-ing its
   115  // share with the Sharer's current share.
   116  func (s *SeedSharer) Merge(child *Child) Sharer {
   117  	var curr Share
   118  	curr.Xor(&s.curr, &child.Share)
   119  
   120  	sharer := initSeedSharer(&s.root, &curr)
   121  	return sharer
   122  }
   123  
   124  // Zero returns a a new "zero Sharer" that has its current share set to zero,
   125  // while keeping the root share. Merging a Child from the original Sharer into
   126  // this zero-Sharer gives back the original Sharer.
   127  func (s *SeedSharer) Zero() Sharer {
   128  	return initSeedSharer(&s.root, &zeroShare)
   129  }
   130  
   131  // Child derives a preimage/hash pair to be used for an AMP HTLC.
   132  // All children of s will use the same underlying share, but have unique
   133  // preimage and hash. This can be used to rerandomize the preimage/hash pair for
   134  // a given HTLC if a new route is needed.
   135  func (s *SeedSharer) Child(index uint32) *Child {
   136  	desc := ChildDesc{
   137  		Share: s.curr,
   138  		Index: index,
   139  	}
   140  
   141  	return DeriveChild(s.root, desc)
   142  }
   143  
   144  // ReconstructChildren derives the set of children hashes and preimages from the
   145  // provided descriptors. The shares from each child descriptor are first used to
   146  // compute the root, afterwards the child hashes and preimages are
   147  // deterministically computed. For child descriptor at index i in the input,
   148  // it's derived child will occupy index i of the returned children.
   149  func ReconstructChildren(descs ...ChildDesc) []*Child {
   150  	// Recompute the root by XORing the provided shares.
   151  	var root Share
   152  	for _, desc := range descs {
   153  		root.Xor(&root, &desc.Share)
   154  	}
   155  
   156  	// With the root computed, derive the child hashes and preimages from
   157  	// the child descriptors.
   158  	children := make([]*Child, len(descs))
   159  	for i, desc := range descs {
   160  		children[i] = DeriveChild(root, desc)
   161  	}
   162  
   163  	return children
   164  }
   165  
   166  // split splits a share into two random values, that when XOR'd reproduce the
   167  // original share. Given a share s, the two shares are derived as:
   168  //
   169  //	left <-$- random
   170  //	right = parent ^ left.
   171  //
   172  // When reconstructed, we have that:
   173  //
   174  //	left ^ right = left ^ parent ^ left
   175  //	             = parent.
   176  func split(parent *Share) (Share, Share, error) {
   177  	// Generate a random share for the left child.
   178  	var left Share
   179  	if _, err := rand.Read(left[:]); err != nil {
   180  		return Share{}, Share{}, err
   181  	}
   182  
   183  	// Compute right = parent ^ left.
   184  	var right Share
   185  	right.Xor(parent, &left)
   186  
   187  	return left, right, nil
   188  }
   189  
   190  var _ Sharer = (*SeedSharer)(nil)