//go:build integrationtest // +build integrationtest package crypto_test import ( "bytes" "fmt" "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "source.quilibrium.com/quilibrium/monorepo/bls48581" "source.quilibrium.com/quilibrium/monorepo/types/schema" qcrypto "source.quilibrium.com/quilibrium/monorepo/types/tries" ) func TestName(t *testing.T) { rdfDoc := ` @prefix qcl: . @prefix rdfs: . @prefix name: . name:NameRecord a rdfs:Class ; rdfs:comment "Quilibrium name service record" . name:Name a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:String ; qcl:size 32 ; qcl:order 1 . name:AuthorityKey a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:ByteArray ; qcl:size 57 ; qcl:order 2 . name:Parent a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:ByteArray ; qcl:size 32 ; qcl:order 3 . name:CreatedAt a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:Uint ; qcl:size 64 ; qcl:order 4 . name:UpdatedAt a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:Uint ; qcl:size 64 ; qcl:order 5 . name:RecordType a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:Uint ; qcl:size 8 ; qcl:order 6 . name:Data a rdfs:Property ; rdfs:range name:NameRecord ; rdfs:domain qcl:String ; qcl:size 64 ; qcl:order 7 . ` // Create components parser := &schema.TurtleRDFParser{} log := zap.NewNop() inclusionProver := bls48581.NewKZGInclusionProver(log) multiprover := schema.NewRDFMultiprover(parser, inclusionProver) // Create a test tree tree := &qcrypto.VectorCommitmentTree{} // Insert test data at the correct indices // The tree needs to have data at indices that will be used in the polynomial // Insert enough data to ensure polynomial has values at indices 1, 2, 3 for i := 0; i < 63; i++ { data := bytes.Repeat([]byte{byte(i + 1)}, 57) err := tree.Insert([]byte{byte(i << 2)}, data, nil, big.NewInt(57)) require.NoError(t, err) } tree.Commit(inclusionProver, false) t.Run("Prove", func(t *testing.T) { fields := []string{"Name", "AuthorityKey"} proof, err := multiprover.Prove(rdfDoc, fields, tree) pb, _ := proof.ToBytes() fmt.Printf("proof %x\n", pb) require.NoError(t, err) assert.NotNil(t, proof) }) t.Run("ProveWithType", func(t *testing.T) { fields := []string{"Name", "AuthorityKey"} typeIndex := uint64(63) proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, &typeIndex) require.NoError(t, err) assert.NotNil(t, proof) pb, _ := proof.ToBytes() fmt.Printf("proof with type %x\n", pb) // Test without type index proof, err = multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) assert.NotNil(t, proof) pb, _ = proof.ToBytes() fmt.Printf("proof with type but type is nil %x\n", pb) }) t.Run("Get", func(t *testing.T) { // Test getting name field (order 1, so key is 1<<2 = 4, data at index 1) value, err := multiprover.Get(rdfDoc, "name:NameRecord", "Name", tree) require.NoError(t, err) assert.Equal(t, bytes.Repeat([]byte{2}, 57), value) // Index 1 has value 2 // Test getting one-time key field (order 2, so key is 2<<2 = 8, data at index 2) value, err = multiprover.Get(rdfDoc, "name:NameRecord", "AuthorityKey", tree) require.NoError(t, err) assert.Equal(t, bytes.Repeat([]byte{3}, 57), value) // Index 2 has value 3 }) t.Run("GetFieldOrder", func(t *testing.T) { order, maxOrder, err := multiprover.GetFieldOrder(rdfDoc, "name:NameRecord", "Name") require.NoError(t, err) assert.Equal(t, 1, order) assert.Equal(t, 7, maxOrder) order, maxOrder, err = multiprover.GetFieldOrder(rdfDoc, "name:NameRecord", "AuthorityKey") require.NoError(t, err) assert.Equal(t, 2, order) assert.Equal(t, 7, maxOrder) order, maxOrder, err = multiprover.GetFieldOrder(rdfDoc, "name:NameRecord", "Parent") require.NoError(t, err) assert.Equal(t, 3, order) assert.Equal(t, 7, maxOrder) }) t.Run("GetFieldKey", func(t *testing.T) { key, err := multiprover.GetFieldKey(rdfDoc, "name:NameRecord", "Name") require.NoError(t, err) assert.Equal(t, []byte{1 << 2}, key) key, err = multiprover.GetFieldKey(rdfDoc, "name:NameRecord", "AuthorityKey") require.NoError(t, err) assert.Equal(t, []byte{2 << 2}, key) }) t.Run("Verify", func(t *testing.T) { // Create proof without type index for simpler verification fields := []string{"Name", "AuthorityKey", "Parent"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) // Get actual data from tree for verification data := make([][]byte, len(fields)) for i, field := range fields { value, err := multiprover.Get(rdfDoc, "name:NameRecord", field, tree) require.NoError(t, err) data[i] = value } // Create commit commit := tree.Commit(inclusionProver, false) proofBytes, _ := proof.ToBytes() // Verify should pass with correct data // keys parameter is nil to use default index-based keys valid, err := multiprover.Verify(rdfDoc, fields, nil, commit, proofBytes, data) require.NoError(t, err) assert.True(t, valid) // Verify should fail with wrong data wrongData := make([][]byte, len(fields)) for i := range wrongData { wrongData[i] = []byte("wrong data") } valid, err = multiprover.Verify(rdfDoc, fields, nil, commit, proofBytes, wrongData) require.NoError(t, err) assert.False(t, valid) // Verify should error with non-existent field invalidFields := []string{"Name", "NonExistent"} _, err = multiprover.Verify(rdfDoc, invalidFields, nil, commit, proofBytes, data[:2]) assert.Error(t, err) assert.Contains(t, err.Error(), "not found") }) t.Run("VerifyWithType", func(t *testing.T) { // Add type marker to tree typeData := bytes.Repeat([]byte{0xff}, 32) typeIndex := uint64(63) err := tree.Insert(typeData, typeData, nil, big.NewInt(32)) require.NoError(t, err) // Get commit after all data is inserted commit := tree.Commit(inclusionProver, false) // Create proof with type fields := []string{"Name", "AuthorityKey"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, &typeIndex) require.NoError(t, err) // Get actual data from tree data := make([][]byte, len(fields)) for i, field := range fields { value, err := multiprover.Get(rdfDoc, "name:NameRecord", field, tree) require.NoError(t, err) data[i] = value } proofBytes, _ := proof.ToBytes() // Verify with type should pass valid, err := multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &typeIndex, typeData) require.NoError(t, err) assert.True(t, valid) // Verify without type when proof was created with type should fail valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, nil, nil) require.NoError(t, err) assert.False(t, valid) // Should fail due to missing type data // Create proof without type proofNoType, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) proofNoTypeBytes, _ := proofNoType.ToBytes() // Verify without type should pass valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofNoTypeBytes, data, nil, nil) require.NoError(t, err) assert.True(t, valid) // Verify with wrong type data should fail wrongTypeData := []byte("wrong type data") valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &typeIndex, wrongTypeData) require.NoError(t, err) assert.False(t, valid) // Verify with different type index but same data should still fail // because the hash uses the fixed key bytes.Repeat([]byte{0xff}, 32) differentTypeIndex := uint64(50) valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &differentTypeIndex, typeData) require.NoError(t, err) assert.False(t, valid) // Should fail because proof was created with index 63 }) t.Run("ErrorCases", func(t *testing.T) { // Test non-existent field _, err := multiprover.Get(rdfDoc, "name:NameRecord", "NonExistent", tree) assert.Error(t, err) assert.Contains(t, err.Error(), "not found") // Test invalid document _, err = multiprover.Get("invalid rdf", "name:NameRecord", "name", tree) assert.Error(t, err) // Test verify with mismatched data count fields := []string{"Name", "AuthorityKey"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) // Wrong number of data elements wrongData := [][]byte{[]byte("data1")} commit := tree.Commit(inclusionProver, false) _, err = multiprover.Verify(rdfDoc, fields, nil, commit, proof.GetProof(), wrongData) assert.Error(t, err) assert.Contains(t, err.Error(), "fields and data length mismatch") }) } func TestRDFMultiprover(t *testing.T) { // Create test RDF document rdfDoc := ` @prefix coin: . @prefix qcl: . @prefix rdfs: . coin:Coin a rdfs:Class. coin:Commitment a rdfs:Property; rdfs:domain qcl:ByteArray; qcl:size 56; qcl:order 1; rdfs:range coin:Coin. coin:OneTimeKey a rdfs:Property; rdfs:domain qcl:ByteArray; qcl:size 56; qcl:order 2; rdfs:range coin:Coin. coin:VerificationKey a rdfs:Property; rdfs:domain qcl:ByteArray; qcl:size 56; qcl:order 3; rdfs:range coin:Coin. ` // Create components parser := &schema.TurtleRDFParser{} log := zap.NewNop() inclusionProver := bls48581.NewKZGInclusionProver(log) multiprover := schema.NewRDFMultiprover(parser, inclusionProver) // Create a test tree tree := &qcrypto.VectorCommitmentTree{} // Insert test data at the correct indices // The tree needs to have data at indices that will be used in the polynomial // Insert enough data to ensure polynomial has values at indices 1, 2, 3 for i := 0; i < 63; i++ { data := bytes.Repeat([]byte{byte(i + 1)}, 56) err := tree.Insert([]byte{byte(i << 2)}, data, nil, big.NewInt(56)) require.NoError(t, err) } tree.Commit(inclusionProver, false) t.Run("Prove", func(t *testing.T) { fields := []string{"Commitment", "VerificationKey"} proof, err := multiprover.Prove(rdfDoc, fields, tree) require.NoError(t, err) assert.NotNil(t, proof) }) t.Run("ProveWithType", func(t *testing.T) { fields := []string{"Commitment", "VerificationKey"} typeIndex := uint64(63) proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, &typeIndex) require.NoError(t, err) assert.NotNil(t, proof) // Test without type index proof, err = multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) assert.NotNil(t, proof) }) t.Run("Get", func(t *testing.T) { // Test getting commitment field (order 1, so key is 1<<2 = 4, data at index 1) value, err := multiprover.Get(rdfDoc, "coin:Coin", "Commitment", tree) require.NoError(t, err) assert.Equal(t, bytes.Repeat([]byte{2}, 56), value) // Index 1 has value 2 // Test getting one-time key field (order 2, so key is 2<<2 = 8, data at index 2) value, err = multiprover.Get(rdfDoc, "coin:Coin", "OneTimeKey", tree) require.NoError(t, err) assert.Equal(t, bytes.Repeat([]byte{3}, 56), value) // Index 2 has value 3 // Test getting verification key field (order 3, so key is 3<<2 = 12, data at index 3) value, err = multiprover.Get(rdfDoc, "coin:Coin", "VerificationKey", tree) require.NoError(t, err) assert.Equal(t, bytes.Repeat([]byte{4}, 56), value) // Index 3 has value 4 }) t.Run("GetFieldOrder", func(t *testing.T) { order, maxOrder, err := multiprover.GetFieldOrder(rdfDoc, "coin:Coin", "Commitment") require.NoError(t, err) assert.Equal(t, 1, order) assert.Equal(t, 3, maxOrder) order, maxOrder, err = multiprover.GetFieldOrder(rdfDoc, "coin:Coin", "OneTimeKey") require.NoError(t, err) assert.Equal(t, 2, order) assert.Equal(t, 3, maxOrder) order, maxOrder, err = multiprover.GetFieldOrder(rdfDoc, "coin:Coin", "VerificationKey") require.NoError(t, err) assert.Equal(t, 3, order) assert.Equal(t, 3, maxOrder) }) t.Run("GetFieldKey", func(t *testing.T) { key, err := multiprover.GetFieldKey(rdfDoc, "coin:Coin", "Commitment") require.NoError(t, err) assert.Equal(t, []byte{1 << 2}, key) key, err = multiprover.GetFieldKey(rdfDoc, "coin:Coin", "OneTimeKey") require.NoError(t, err) assert.Equal(t, []byte{2 << 2}, key) }) t.Run("Verify", func(t *testing.T) { // Create proof without type index for simpler verification fields := []string{"Commitment", "OneTimeKey", "VerificationKey"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) // Get actual data from tree for verification data := make([][]byte, len(fields)) for i, field := range fields { value, err := multiprover.Get(rdfDoc, "coin:Coin", field, tree) require.NoError(t, err) data[i] = value } // Create commit commit := tree.Commit(inclusionProver, false) proofBytes, _ := proof.ToBytes() // Verify should pass with correct data // keys parameter is nil to use default index-based keys valid, err := multiprover.Verify(rdfDoc, fields, nil, commit, proofBytes, data) require.NoError(t, err) assert.True(t, valid) // Verify should fail with wrong data wrongData := make([][]byte, len(fields)) for i := range wrongData { wrongData[i] = []byte("wrong data") } valid, err = multiprover.Verify(rdfDoc, fields, nil, commit, proofBytes, wrongData) require.NoError(t, err) assert.False(t, valid) // Verify should error with non-existent field invalidFields := []string{"Commitment", "NonExistent"} _, err = multiprover.Verify(rdfDoc, invalidFields, nil, commit, proofBytes, data[:2]) assert.Error(t, err) assert.Contains(t, err.Error(), "not found") }) t.Run("VerifyWithType", func(t *testing.T) { // Add type marker to tree typeData := bytes.Repeat([]byte{0xff}, 32) typeIndex := uint64(63) err := tree.Insert(typeData, typeData, nil, big.NewInt(32)) require.NoError(t, err) // Get commit after all data is inserted commit := tree.Commit(inclusionProver, false) // Create proof with type fields := []string{"Commitment", "VerificationKey"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, &typeIndex) require.NoError(t, err) // Get actual data from tree data := make([][]byte, len(fields)) for i, field := range fields { value, err := multiprover.Get(rdfDoc, "coin:Coin", field, tree) require.NoError(t, err) data[i] = value } proofBytes, _ := proof.ToBytes() // Verify with type should pass valid, err := multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &typeIndex, typeData) require.NoError(t, err) assert.True(t, valid) // Verify without type when proof was created with type should fail valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, nil, nil) require.NoError(t, err) assert.False(t, valid) // Should fail due to missing type data // Create proof without type proofNoType, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) proofNoTypeBytes, _ := proofNoType.ToBytes() // Verify without type should pass valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofNoTypeBytes, data, nil, nil) require.NoError(t, err) assert.True(t, valid) // Verify with wrong type data should fail wrongTypeData := []byte("wrong type data") valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &typeIndex, wrongTypeData) require.NoError(t, err) assert.False(t, valid) // Verify with different type index but same data should still fail // because the hash uses the fixed key bytes.Repeat([]byte{0xff}, 32) differentTypeIndex := uint64(50) valid, err = multiprover.VerifyWithType(rdfDoc, fields, nil, commit, proofBytes, data, &differentTypeIndex, typeData) require.NoError(t, err) assert.False(t, valid) // Should fail because proof was created with index 63 }) t.Run("ErrorCases", func(t *testing.T) { // Test non-existent field _, err := multiprover.Get(rdfDoc, "coin:Coin", "NonExistent", tree) assert.Error(t, err) assert.Contains(t, err.Error(), "not found") // Test invalid document _, err = multiprover.Get("invalid rdf", "coin:Coin", "Commitment", tree) assert.Error(t, err) // Test verify with mismatched data count fields := []string{"Commitment", "OneTimeKey"} proof, err := multiprover.ProveWithType(rdfDoc, fields, tree, nil) require.NoError(t, err) // Wrong number of data elements wrongData := [][]byte{[]byte("data1")} commit := tree.Commit(inclusionProver, false) _, err = multiprover.Verify(rdfDoc, fields, nil, commit, proof.GetProof(), wrongData) assert.Error(t, err) assert.Contains(t, err.Error(), "fields and data length mismatch") }) }