142 lines
3.5 KiB
Go
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)
|
|
}
|