ceremonyclient/node/tests/hypergraph_test.go
Cassandra Heart 615e3bdcbc
v2.1.0.14
2025-12-03 23:53:46 -06:00

404 lines
14 KiB
Go

package tests
import (
"bytes"
"crypto/rand"
"crypto/sha512"
"math/big"
"testing"
"github.com/cloudflare/circl/sign/ed448"
"github.com/stretchr/testify/mock"
"go.uber.org/zap"
"source.quilibrium.com/quilibrium/monorepo/config"
hg "source.quilibrium.com/quilibrium/monorepo/hypergraph"
"source.quilibrium.com/quilibrium/monorepo/node/store"
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
"source.quilibrium.com/quilibrium/monorepo/types/hypergraph"
"source.quilibrium.com/quilibrium/monorepo/types/mocks"
"source.quilibrium.com/quilibrium/monorepo/types/tries"
)
func TestHypergraph(t *testing.T) {
// Test conversion operations
t.Run("Conversion retains ordering", func(t *testing.T) {
prover := &mocks.MockInclusionProver{}
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
verEnc := &mocks.MockVerifiableEncryptor{}
ves := make([]hypergraph.Encrypted, 1000)
for i := range ves {
ve := &mocks.MockVerEnc{}
ve.On("ToBytes").Return([]byte{byte(i)})
verEnc.On("FromBytes", []byte{byte(i)}).Return(ve)
ve.On("GetStatement").Return(bytes.Repeat([]byte{0x02, byte(i)}, 37))
ves[i] = ve
}
tree := hypergraph.EncryptedToVertexTree(prover, ves)
for i, leaf := range tries.GetAllPreloadedLeaves(tree.Root) {
if !bytes.Equal(leaf.HashTarget, bytes.Repeat([]byte{0x02, byte(i)}, 37)) {
t.Errorf("mismatch hashtarget on index %d", i)
}
if !bytes.Equal(leaf.Value, []byte{byte(i)}) {
t.Errorf("mismatch value on index %d", i)
}
}
encryptedSet := hypergraph.VertexTreeToEncrypted(verEnc, tree)
for i, e := range encryptedSet {
if !bytes.Equal(e.ToBytes(), []byte{byte(i)}) {
t.Errorf("mismatch value on index %d", i)
}
}
})
// Test vertex operations
t.Run("Vertex Operations", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v1 := hg.NewVertex([32]byte{1}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
v2 := hg.NewVertex([32]byte{1}, [32]byte{2}, dataTree.Commit(prover, false), dataTree.GetSize())
// Add vertices
err := hgcrdt.AddVertex(nil, v1)
if err != nil {
t.Errorf("Failed to add vertex v1: %v", err)
}
err = hgcrdt.AddVertex(nil, v2)
if err != nil {
t.Errorf("Failed to add vertex v2: %v", err)
}
// Lookup vertices
if !hgcrdt.LookupVertex(v1) {
t.Error("Failed to lookup vertex v1")
}
if !hgcrdt.LookupVertex(v2) {
t.Error("Failed to lookup vertex v2")
}
// Remove vertex
err = hgcrdt.RemoveVertex(nil, v1)
if err != nil {
t.Errorf("Failed to remove vertex v1: %v", err)
}
if hgcrdt.LookupVertex(v1) {
t.Error("Vertex v1 still exists after removal")
}
if !hgcrdt.LookupVertex(v2) {
t.Error("Vertex v2 was incorrectly removed")
}
})
// Test hyperedge operations
t.Run("Hyperedge Operations", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v3 := hg.NewVertex([32]byte{2}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
v4 := hg.NewVertex([32]byte{2}, [32]byte{2}, dataTree.Commit(prover, false), dataTree.GetSize())
hgcrdt.AddVertex(nil, v3)
hgcrdt.AddVertex(nil, v4)
h1 := hg.NewHyperedge([32]byte{3}, [32]byte{1})
h1.AddExtrinsic(v3)
h1.AddExtrinsic(v4)
// Add hyperedge
err := hgcrdt.AddHyperedge(nil, h1)
if err != nil {
t.Errorf("Failed to add hyperedge h1: %v", err)
}
// Lookup hyperedge
if !hgcrdt.LookupHyperedge(h1) {
t.Error("Failed to lookup hyperedge h1")
}
// Remove hyperedge
err = hgcrdt.RemoveHyperedge(nil, h1)
if err != nil {
t.Errorf("Failed to remove hyperedge h1: %v", err)
}
if hgcrdt.LookupHyperedge(h1) {
t.Error("Hyperedge h1 still exists after removal")
}
})
// Test "within" relationship
t.Run("Within Relationship", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v5 := hg.NewVertex([32]byte{4}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
v6 := hg.NewVertex([32]byte{4}, [32]byte{2}, dataTree.Commit(prover, false), dataTree.GetSize())
hgcrdt.AddVertex(nil, v5)
hgcrdt.AddVertex(nil, v6)
h2 := hg.NewHyperedge([32]byte{5}, [32]byte{1})
h2.AddExtrinsic(v5)
h2.AddExtrinsic(v6)
hgcrdt.AddHyperedge(nil, h2)
if !hgcrdt.Within(v5, h2) {
t.Error("v5 should be within h2")
}
if !hgcrdt.Within(v6, h2) {
t.Error("v6 should be within h2")
}
v7 := hg.NewVertex([32]byte{4}, [32]byte{3}, dataTree.Commit(prover, false), dataTree.GetSize())
hgcrdt.AddVertex(nil, v7)
if hgcrdt.Within(v7, h2) {
t.Error("v7 should not be within h2")
}
})
// Test nested hyperedges
t.Run("Nested Hyperedges", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v8 := hg.NewVertex([32]byte{6}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
v9 := hg.NewVertex([32]byte{6}, [32]byte{2}, dataTree.Commit(prover, false), dataTree.GetSize())
hgcrdt.AddVertex(nil, v8)
hgcrdt.AddVertex(nil, v9)
h3 := hg.NewHyperedge([32]byte{7}, [32]byte{1})
h3.AddExtrinsic(v8)
h4 := hg.NewHyperedge([32]byte{7}, [32]byte{2})
h4.AddExtrinsic(h3)
h4.AddExtrinsic(v9)
hgcrdt.AddHyperedge(nil, h3)
hgcrdt.AddHyperedge(nil, h4)
if !hgcrdt.Within(v8, h4) {
t.Error("v8 should be within h4 (nested)")
}
if !hgcrdt.Within(v9, h4) {
t.Error("v9 should be within h4 (direct)")
}
})
// Test error cases
t.Run("Error Cases", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v10 := hg.NewVertex([32]byte{8}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
h5 := hg.NewHyperedge([32]byte{8}, [32]byte{2})
h5.AddExtrinsic(v10)
// Add vertex and hyperedge
hgcrdt.AddVertex(nil, v10)
hgcrdt.AddHyperedge(nil, h5)
})
// Test sharding
t.Run("Sharding", func(t *testing.T) {
logger, _ := zap.NewDevelopment()
s := store.NewPebbleDB(logger, &config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, 0)
enc := &mocks.MockVerifiableEncryptor{}
prover := &mocks.MockInclusionProver{}
vep := &mocks.MockVerEncProof{}
ve := &mocks.MockVerEnc{}
pub, _, _ := ed448.GenerateKey(rand.Reader)
mockCommit := make([]byte, 74)
mockCommit[0] = 0x02
rand.Read(mockCommit[1:])
prover.On("CommitRaw", mock.Anything, mock.Anything).Return(mockCommit, nil)
ve.On("ToBytes").Return([]byte{})
ve.On("GetStatement").Return(make([]byte, 74))
vep.On("Compress").Return(ve)
enc.On("Encrypt", make([]byte, 20), []byte(pub)).Return([]crypto.VerEncProof{
vep,
})
hgcrdt := hg.NewHypergraph(
logger,
store.NewPebbleHypergraphStore(&config.DBConfig{InMemoryDONOTUSE: true, Path: ".configtest/store"}, s, logger, enc, prover),
prover,
[]int{},
&Nopthenticator{},
200,
)
data := enc.Encrypt(make([]byte, 20), pub)
verenc := data[0].Compress()
dataTree := &tries.VectorCommitmentTree{}
for _, d := range []hypergraph.Encrypted{verenc} {
dataBytes := d.ToBytes()
id := sha512.Sum512(dataBytes)
dataTree.Insert(id[:], dataBytes, d.GetStatement(), big.NewInt(int64(len(data)*55)))
}
dataTree.Commit(prover, false)
v11 := hg.NewVertex([32]byte{9}, [32]byte{1}, dataTree.Commit(prover, false), dataTree.GetSize())
v12 := hg.NewVertex([32]byte{9}, [32]byte{2}, dataTree.Commit(prover, false), dataTree.GetSize())
hgcrdt.AddVertex(nil, v11)
hgcrdt.AddVertex(nil, v12)
shard11 := hypergraph.GetShardAddress(v11)
shard12 := hypergraph.GetShardAddress(v12)
if !bytes.Equal(shard11.L1[:], shard12.L1[:]) ||
!bytes.Equal(shard11.L2[:], shard12.L2[:]) ||
bytes.Equal(shard11.L3[:], shard12.L3[:]) {
t.Error("v11 and v12 should be in the same L1 shard and the same L2 shard but not the same L3 shard")
}
})
}