mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-21 18:37:26 +08:00
349 lines
8.7 KiB
Go
349 lines
8.7 KiB
Go
package tries_test
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"math/bits"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
mt "github.com/txaty/go-merkletree"
|
|
"golang.org/x/crypto/sha3"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/tries"
|
|
)
|
|
|
|
func TestPackAndVerifyOutput(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
numLeaves int
|
|
modulo int
|
|
frameNum uint64
|
|
withPrev bool
|
|
}{
|
|
{
|
|
name: "Basic case without previous tree",
|
|
numLeaves: 4,
|
|
modulo: 4,
|
|
frameNum: 1,
|
|
withPrev: false,
|
|
},
|
|
{
|
|
name: "With previous tree",
|
|
numLeaves: 8,
|
|
modulo: 8,
|
|
frameNum: 2,
|
|
withPrev: true,
|
|
},
|
|
{
|
|
name: "Large tree with previous",
|
|
numLeaves: 16,
|
|
modulo: 16,
|
|
frameNum: 3,
|
|
withPrev: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
outputs := make([]mt.DataBlock, tc.numLeaves)
|
|
for i := range outputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
outputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
FrameNumber: tc.frameNum,
|
|
Output: make([]byte, 516),
|
|
}
|
|
rand.Read(frame.Output)
|
|
|
|
var previousTree *mt.MerkleTree
|
|
if tc.withPrev {
|
|
prevOutputs := make([]mt.DataBlock, tc.modulo)
|
|
for i := range prevOutputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
prevOutputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
var err error
|
|
previousTree, err = mt.New(
|
|
&mt.Config{
|
|
HashFunc: func(data []byte) ([]byte, error) {
|
|
hash := sha3.Sum256(data)
|
|
return hash[:], nil
|
|
},
|
|
Mode: mt.ModeProofGen,
|
|
DisableLeafHashing: true,
|
|
},
|
|
prevOutputs,
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tree, output, err := tries.PackOutputIntoPayloadAndProof(
|
|
outputs,
|
|
tc.modulo,
|
|
frame,
|
|
previousTree,
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tree)
|
|
require.NotEmpty(t, output)
|
|
|
|
var previousRoot []byte
|
|
if previousTree != nil {
|
|
previousRoot = previousTree.Root
|
|
}
|
|
|
|
treeRoot, modulo, frameNumber, verified, err := tries.UnpackAndVerifyOutput(
|
|
previousRoot,
|
|
output,
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
require.True(t, verified, "Output verification failed")
|
|
require.Equal(t, tree.Root, treeRoot, "Tree root mismatch")
|
|
require.Equal(t, uint32(tc.modulo), modulo, "Modulo mismatch")
|
|
require.Equal(t, tc.frameNum, frameNumber, "Frame number mismatch")
|
|
|
|
if tc.withPrev {
|
|
t.Run("corrupted_proof", func(t *testing.T) {
|
|
corruptedOutput := make([][]byte, len(output))
|
|
copy(corruptedOutput, output)
|
|
if len(corruptedOutput) > 3 {
|
|
corruptedSibling := make([]byte, len(corruptedOutput[3]))
|
|
copy(corruptedSibling, corruptedOutput[3])
|
|
corruptedSibling[0] ^= 0xFF
|
|
corruptedOutput[3] = corruptedSibling
|
|
}
|
|
|
|
_, _, _, verified, err := tries.UnpackAndVerifyOutput(
|
|
previousRoot,
|
|
corruptedOutput,
|
|
)
|
|
require.False(t, verified, "Verification should fail with corrupted sibling")
|
|
require.NoError(t, err, "Unexpected error with corrupted sibling")
|
|
|
|
corruptedOutput = make([][]byte, len(output))
|
|
copy(corruptedOutput, output)
|
|
if len(corruptedOutput) > 0 {
|
|
lastIdx := len(corruptedOutput) - 1
|
|
corruptedLeaf := make([]byte, len(corruptedOutput[lastIdx]))
|
|
copy(corruptedLeaf, corruptedOutput[lastIdx])
|
|
corruptedLeaf[0] ^= 0xFF
|
|
corruptedOutput[lastIdx] = corruptedLeaf
|
|
}
|
|
|
|
_, _, _, verified, err = tries.UnpackAndVerifyOutput(
|
|
previousRoot,
|
|
corruptedOutput,
|
|
)
|
|
require.False(t, verified, "Verification should fail with corrupted leaf")
|
|
require.NoError(t, err, "Unexpected error with corrupted leaf")
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPackAndVerifyMultiOutput(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
numLeaves int
|
|
modulo int
|
|
frameNum uint64
|
|
outputLen int
|
|
withPrev bool
|
|
}{
|
|
{
|
|
name: "Basic case without previous tree",
|
|
numLeaves: 4,
|
|
modulo: 4,
|
|
outputLen: 3,
|
|
frameNum: 1,
|
|
withPrev: false,
|
|
},
|
|
{
|
|
name: "Basic case with previous tree",
|
|
numLeaves: 4,
|
|
modulo: 4,
|
|
outputLen: 8,
|
|
frameNum: 1,
|
|
withPrev: true,
|
|
},
|
|
{
|
|
name: "With previous tree",
|
|
numLeaves: 8,
|
|
modulo: 8,
|
|
outputLen: 10,
|
|
frameNum: 2,
|
|
withPrev: true,
|
|
},
|
|
{
|
|
name: "Large tree with previous",
|
|
numLeaves: 16,
|
|
modulo: 16,
|
|
outputLen: 12,
|
|
frameNum: 3,
|
|
withPrev: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
outputs := make([]mt.DataBlock, tc.numLeaves)
|
|
for i := range outputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
outputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
FrameNumber: tc.frameNum,
|
|
Output: make([]byte, 516),
|
|
}
|
|
rand.Read(frame.Output)
|
|
|
|
var previousTree *mt.MerkleTree
|
|
if tc.withPrev {
|
|
prevOutputs := make([]mt.DataBlock, tc.modulo)
|
|
for i := range prevOutputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
prevOutputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
var err error
|
|
previousTree, err = mt.New(
|
|
&mt.Config{
|
|
HashFunc: func(data []byte) ([]byte, error) {
|
|
hash := sha3.Sum256(data)
|
|
return hash[:], nil
|
|
},
|
|
Mode: mt.ModeProofGen,
|
|
DisableLeafHashing: true,
|
|
},
|
|
prevOutputs,
|
|
)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
tree, output, err := tries.PackOutputIntoMultiPayloadAndProof(
|
|
outputs,
|
|
tc.modulo,
|
|
frame,
|
|
previousTree,
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tree)
|
|
require.NotEmpty(t, output)
|
|
|
|
var previousRoot []byte
|
|
if previousTree != nil {
|
|
previousRoot = previousTree.Root
|
|
}
|
|
|
|
treeRoot, modulo, frameNumber, verified, err := tries.UnpackAndVerifyMultiOutput(
|
|
previousRoot,
|
|
output,
|
|
)
|
|
|
|
require.NoError(t, err)
|
|
require.True(t, verified, "Output verification failed, %d", len(outputs))
|
|
require.Equal(t, tree.Root, treeRoot, "Tree root mismatch")
|
|
require.Equal(t, uint32(tc.modulo), modulo, "Modulo mismatch")
|
|
require.Equal(t, tc.frameNum, frameNumber, "Frame number mismatch")
|
|
require.Equal(t, len(output), tc.outputLen, "Output length mismatch")
|
|
|
|
if tc.withPrev {
|
|
t.Run("corrupted_proof", func(t *testing.T) {
|
|
corruptedOutput := make([][]byte, len(output))
|
|
copy(corruptedOutput, output)
|
|
if len(corruptedOutput) > 3 {
|
|
corruptedSibling := make([]byte, len(corruptedOutput[3]))
|
|
copy(corruptedSibling, corruptedOutput[3])
|
|
corruptedSibling[0] ^= 0xFF
|
|
corruptedOutput[3] = corruptedSibling
|
|
}
|
|
|
|
_, _, _, verified, err := tries.UnpackAndVerifyMultiOutput(
|
|
previousRoot,
|
|
corruptedOutput,
|
|
)
|
|
require.False(t, verified, "Verification should fail with corrupted sibling")
|
|
require.NoError(t, err, "Unexpected error with corrupted sibling")
|
|
|
|
corruptedOutput = make([][]byte, len(output))
|
|
copy(corruptedOutput, output)
|
|
if len(corruptedOutput) > 0 {
|
|
numSiblings := bits.Len64(uint64(modulo) - 1)
|
|
lastIdx := 4 + numSiblings
|
|
corruptedLeaf := make([]byte, len(corruptedOutput[lastIdx]))
|
|
copy(corruptedLeaf, corruptedOutput[lastIdx])
|
|
corruptedLeaf[0] ^= 0xFF
|
|
corruptedOutput[lastIdx] = corruptedLeaf
|
|
}
|
|
|
|
_, _, _, verified, err = tries.UnpackAndVerifyMultiOutput(
|
|
previousRoot,
|
|
corruptedOutput,
|
|
)
|
|
require.False(t, verified, "Verification should fail with corrupted leaf")
|
|
require.NoError(t, err, "Unexpected error with corrupted leaf")
|
|
})
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPackAndVerifyOutputFailover(t *testing.T) {
|
|
outputs := make([]mt.DataBlock, 3)
|
|
for i := range outputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
outputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
frame := &protobufs.ClockFrame{
|
|
FrameNumber: 1,
|
|
Output: make([]byte, 516),
|
|
}
|
|
rand.Read(frame.Output)
|
|
|
|
var previousTree *mt.MerkleTree
|
|
prevOutputs := make([]mt.DataBlock, 4)
|
|
for i := range prevOutputs {
|
|
data := make([]byte, 32)
|
|
binary.BigEndian.PutUint32(data, uint32(i))
|
|
prevOutputs[i] = tries.NewProofLeaf(data)
|
|
}
|
|
|
|
var err error
|
|
previousTree, err = mt.New(
|
|
&mt.Config{
|
|
HashFunc: func(data []byte) ([]byte, error) {
|
|
hash := sha3.Sum256(data)
|
|
return hash[:], nil
|
|
},
|
|
Mode: mt.ModeProofGen,
|
|
DisableLeafHashing: true,
|
|
},
|
|
prevOutputs,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
tree, output, err := tries.PackOutputIntoMultiPayloadAndProof(
|
|
outputs,
|
|
3,
|
|
frame,
|
|
previousTree,
|
|
)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, tree)
|
|
require.NotEmpty(t, output)
|
|
require.Len(t, output, 3)
|
|
}
|