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)