lnd.xprv/amp/derivation_test.go
2021-04-27 09:45:14 +02:00

142 lines
3.5 KiB
Go

package amp_test
import (
"testing"
"github.com/lightningnetwork/lnd/amp"
"github.com/stretchr/testify/require"
)
type sharerTest struct {
name string
numShares int
merge bool
}
var sharerTests = []sharerTest{
{
name: "root only",
numShares: 1,
},
{
name: "two shares",
numShares: 2,
},
{
name: "many shares",
numShares: 10,
},
{
name: "merge 4 shares",
numShares: 4,
merge: true,
},
{
name: "merge many shares",
numShares: 20,
merge: true,
},
}
// TestSharer executes the end-to-end derivation between sender and receiver,
// asserting that shares are properly computed and, when reconstructed by the
// receiver, produce identical child hashes and preimages as the sender.
func TestSharer(t *testing.T) {
for _, test := range sharerTests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
testSharer(t, test)
})
}
}
func testSharer(t *testing.T, test sharerTest) {
// Construct a new sharer with a random seed.
var (
sharer amp.Sharer
err error
)
sharer, err = amp.NewSeedSharer()
require.NoError(t, err)
// Assert that we can instantiate an equivalent root sharer using the
// root share.
root := sharer.Root()
sharerFromRoot := amp.SeedSharerFromRoot(&root)
require.Equal(t, sharer, sharerFromRoot)
// Generate numShares-1 randomized shares.
children := make([]*amp.Child, 0, test.numShares)
for i := 0; i < test.numShares-1; i++ {
var left amp.Sharer
left, sharer, err = sharer.Split()
require.NoError(t, err)
child := left.Child(0)
assertChildShare(t, child, 0)
children = append(children, child)
}
// Compute the final share and finalize the sharing.
child := sharer.Child(0)
sharer = sharer.Zero()
assertChildShare(t, child, 0)
children = append(children, child)
// If we are testing merging, merge half of the created children back
// into the sharer.
if test.merge {
for i := len(children) / 2; i < len(children); i++ {
sharer = sharer.Merge(children[i])
}
children = children[:len(children)/2]
// We must create a new last child from what we just merged
// back.
child := sharer.Child(0)
assertChildShare(t, child, 0)
children = append(children, child)
}
assertReconstruction(t, children...)
}
// assertChildShare checks that the child has the expected child index, and that
// the child's preimage is valid for the its hash.
func assertChildShare(t *testing.T, child *amp.Child, expIndex int) {
t.Helper()
require.Equal(t, uint32(expIndex), child.Index)
require.True(t, child.Preimage.Matches(child.Hash))
}
// assertReconstruction takes a list of children and simulates the receiver
// recombining the shares, and then deriving the child preimage and hash for
// each HTLC. This asserts that the receiver can always rederive the full set of
// children knowing only the shares and child indexes for each.
func assertReconstruction(t *testing.T, children ...*amp.Child) {
t.Helper()
// Reconstruct a child descriptor for each of the provided children.
// In practice, the receiver will only know the share and the child
// index it learns for each HTLC.
descs := make([]amp.ChildDesc, 0, len(children))
for _, child := range children {
descs = append(descs, amp.ChildDesc{
Share: child.Share,
Index: child.Index,
})
}
// Now, recombine the shares and rederive a child for each of the
// descriptors above. The resulting set of children should exactly match
// the set provided.
children2 := amp.ReconstructChildren(descs...)
require.Equal(t, children, children2)
}