ceremonyclient/protobufs/compute_test.go
Cassandra Heart dbd95bd9e9
v2.1.0 (#439)
* v2.1.0 [omit consensus and adjacent] - this commit will be amended with the full release after the file copy is complete

* 2.1.0 main node rollup
2025-09-30 02:48:15 -05:00

1163 lines
32 KiB
Go

package protobufs
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestComputeConfiguration_Serialization(t *testing.T) {
tests := []struct {
name string
config *ComputeConfiguration
}{
{
name: "complete configuration",
config: &ComputeConfiguration{
ReadPublicKey: make([]byte, 57), // Ed448 key
WritePublicKey: make([]byte, 57), // Ed448 key
OwnerPublicKey: make([]byte, 585), // BLS48-581 key
},
},
{
name: "different keys",
config: &ComputeConfiguration{
ReadPublicKey: append([]byte{0x01}, make([]byte, 56)...),
WritePublicKey: append([]byte{0x02}, make([]byte, 56)...),
OwnerPublicKey: append([]byte{0x03}, make([]byte, 584)...),
},
},
{
name: "minimal configuration",
config: &ComputeConfiguration{
ReadPublicKey: []byte{},
WritePublicKey: []byte{},
OwnerPublicKey: []byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.config.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
config2 := &ComputeConfiguration{}
err = config2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.config.ReadPublicKey, config2.ReadPublicKey)
assert.Equal(t, tt.config.WritePublicKey, config2.WritePublicKey)
assert.Equal(t, tt.config.OwnerPublicKey, config2.OwnerPublicKey)
})
}
}
func TestCodeDeployment_Serialization(t *testing.T) {
tests := []struct {
name string
deployment *CodeDeployment
}{
{
name: "complete deployment",
deployment: &CodeDeployment{
Circuit: []byte("test QCL circuit code"),
InputTypes: []string{"garbler_type", "evaluator_type"},
OutputTypes: []string{"output1", "output2", "output3"},
Domain: make([]byte, 32),
},
},
{
name: "minimal deployment",
deployment: &CodeDeployment{
Circuit: []byte("minimal circuit"),
InputTypes: []string{"type1", "type2"},
OutputTypes: []string{},
Domain: make([]byte, 32),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.deployment.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
deployment2 := &CodeDeployment{}
err = deployment2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.deployment.Circuit, deployment2.Circuit)
assert.Equal(t, tt.deployment.InputTypes, deployment2.InputTypes)
assert.Equal(t, tt.deployment.OutputTypes, deployment2.OutputTypes)
assert.Equal(t, tt.deployment.Domain, deployment2.Domain)
})
}
}
// Note: Application serialization is tested in application_test.go
func TestExecuteOperation_Serialization(t *testing.T) {
tests := []struct {
name string
op *ExecuteOperation
}{
{
name: "operation with dependencies",
op: &ExecuteOperation{
Application: &Application{
Address: make([]byte, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("op-123"),
Dependencies: [][]byte{[]byte("dep-1"), []byte("dep-2"), []byte("dep-3")},
},
},
{
name: "operation without dependencies",
op: &ExecuteOperation{
Application: &Application{
Address: []byte("app-address"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("standalone-op"),
Dependencies: [][]byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.op.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
op2 := &ExecuteOperation{}
err = op2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.op.Application.Address, op2.Application.Address)
assert.Equal(t, tt.op.Application.ExecutionContext, op2.Application.ExecutionContext)
assert.Equal(t, tt.op.Identifier, op2.Identifier)
assert.Equal(t, tt.op.Dependencies, op2.Dependencies)
})
}
}
func TestCodeExecute_Serialization(t *testing.T) {
tests := []struct {
name string
execute *CodeExecute
}{
{
name: "complete execute",
execute: &CodeExecute{
ProofOfPayment: [][]byte{
[]byte("payment proof 1"),
[]byte("payment proof 2"),
},
Domain: make([]byte, 32),
Rendezvous: make([]byte, 32),
ExecuteOperations: []*ExecuteOperation{
{
Application: &Application{
Address: make([]byte, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("op1"),
Dependencies: [][]byte{},
},
{
Application: &Application{
Address: make([]byte, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("op2"),
Dependencies: [][]byte{[]byte("op1")},
},
},
},
},
{
name: "single operation",
execute: &CodeExecute{
ProofOfPayment: [][]byte{
make([]byte, 100),
make([]byte, 100),
},
Domain: make([]byte, 32),
Rendezvous: append([]byte{0xFF}, make([]byte, 31)...),
ExecuteOperations: []*ExecuteOperation{
{
Application: &Application{
Address: []byte("single-app"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC,
},
Identifier: []byte("single-op"),
Dependencies: [][]byte{},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.execute.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
execute2 := &CodeExecute{}
err = execute2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.execute.ProofOfPayment, execute2.ProofOfPayment)
assert.Equal(t, tt.execute.Domain, execute2.Domain)
assert.Equal(t, tt.execute.Rendezvous, execute2.Rendezvous)
assert.Equal(t, len(tt.execute.ExecuteOperations), len(execute2.ExecuteOperations))
for i, op := range tt.execute.ExecuteOperations {
assert.Equal(t, op.Application.Address, execute2.ExecuteOperations[i].Application.Address)
assert.Equal(t, op.Application.ExecutionContext, execute2.ExecuteOperations[i].Application.ExecutionContext)
assert.Equal(t, op.Identifier, execute2.ExecuteOperations[i].Identifier)
assert.Equal(t, op.Dependencies, execute2.ExecuteOperations[i].Dependencies)
}
})
}
}
func TestStateTransition_Serialization(t *testing.T) {
tests := []struct {
name string
transition *StateTransition
}{
{
name: "complete transition",
transition: &StateTransition{
Domain: make([]byte, 32),
Address: []byte("state-address"),
OldValue: []byte("old state value"),
NewValue: []byte("new state value"),
Proof: make([]byte, 128),
},
},
{
name: "empty values",
transition: &StateTransition{
Domain: make([]byte, 32),
Address: []byte("addr"),
OldValue: []byte{},
NewValue: []byte{},
Proof: []byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.transition.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
transition2 := &StateTransition{}
err = transition2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.transition.Domain, transition2.Domain)
assert.Equal(t, tt.transition.Address, transition2.Address)
assert.Equal(t, tt.transition.OldValue, transition2.OldValue)
assert.Equal(t, tt.transition.NewValue, transition2.NewValue)
assert.Equal(t, tt.transition.Proof, transition2.Proof)
})
}
}
func TestExecutionResult_Serialization(t *testing.T) {
tests := []struct {
name string
result *ExecutionResult
}{
{
name: "successful result",
result: &ExecutionResult{
OperationId: []byte("op-123"),
Success: true,
Output: []byte("operation output data"),
Error: []byte{},
},
},
{
name: "failed result",
result: &ExecutionResult{
OperationId: []byte("op-failed"),
Success: false,
Output: []byte{},
Error: []byte("operation failed: invalid input"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.result.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
result2 := &ExecutionResult{}
err = result2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.result.OperationId, result2.OperationId)
assert.Equal(t, tt.result.Success, result2.Success)
assert.Equal(t, tt.result.Output, result2.Output)
assert.Equal(t, tt.result.Error, result2.Error)
})
}
}
func TestCodeFinalize_Serialization(t *testing.T) {
tests := []struct {
name string
finalize *CodeFinalize
}{
{
name: "complete finalize",
finalize: &CodeFinalize{
Rendezvous: make([]byte, 32),
Results: []*ExecutionResult{
{
OperationId: []byte("op1"),
Success: true,
Output: []byte("result1"),
Error: []byte{},
},
{
OperationId: []byte("op2"),
Success: false,
Output: []byte{},
Error: []byte("error2"),
},
},
StateChanges: []*StateTransition{
{
Domain: make([]byte, 32),
Address: []byte("addr1"),
OldValue: []byte("old1"),
NewValue: []byte("new1"),
Proof: make([]byte, 64),
},
},
ProofOfExecution: make([]byte, 256),
MessageOutput: []byte("final output message"),
},
},
{
name: "minimal finalize",
finalize: &CodeFinalize{
Rendezvous: make([]byte, 32),
Results: []*ExecutionResult{},
StateChanges: []*StateTransition{},
ProofOfExecution: []byte{},
MessageOutput: []byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.finalize.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
finalize2 := &CodeFinalize{}
err = finalize2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.finalize.Rendezvous, finalize2.Rendezvous)
assert.Equal(t, len(tt.finalize.Results), len(finalize2.Results))
assert.Equal(t, len(tt.finalize.StateChanges), len(finalize2.StateChanges))
assert.Equal(t, tt.finalize.ProofOfExecution, finalize2.ProofOfExecution)
assert.Equal(t, tt.finalize.MessageOutput, finalize2.MessageOutput)
// Compare results
for i, result := range tt.finalize.Results {
assert.Equal(t, result.OperationId, finalize2.Results[i].OperationId)
assert.Equal(t, result.Success, finalize2.Results[i].Success)
assert.Equal(t, result.Output, finalize2.Results[i].Output)
assert.Equal(t, result.Error, finalize2.Results[i].Error)
}
// Compare state changes
for i, change := range tt.finalize.StateChanges {
assert.Equal(t, change.Domain, finalize2.StateChanges[i].Domain)
assert.Equal(t, change.Address, finalize2.StateChanges[i].Address)
assert.Equal(t, change.OldValue, finalize2.StateChanges[i].OldValue)
assert.Equal(t, change.NewValue, finalize2.StateChanges[i].NewValue)
assert.Equal(t, change.Proof, finalize2.StateChanges[i].Proof)
}
})
}
}
func TestComputeDeploy_Serialization(t *testing.T) {
tests := []struct {
name string
args *ComputeDeploy
}{
{
name: "complete arguments",
args: &ComputeDeploy{
Config: &ComputeConfiguration{
ReadPublicKey: make([]byte, 57),
WritePublicKey: make([]byte, 57),
OwnerPublicKey: make([]byte, 585),
},
RdfSchema: []byte("RDF schema definition content"),
},
},
{
name: "large schema",
args: &ComputeDeploy{
Config: &ComputeConfiguration{
ReadPublicKey: append([]byte{0xAA}, make([]byte, 56)...),
WritePublicKey: append([]byte{0xBB}, make([]byte, 56)...),
OwnerPublicKey: append([]byte{0xCC}, make([]byte, 584)...),
},
RdfSchema: make([]byte, 1024), // Large schema
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test serialization
data, err := tt.args.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
// Test deserialization
args2 := &ComputeDeploy{}
err = args2.FromCanonicalBytes(data)
require.NoError(t, err)
// Compare
assert.Equal(t, tt.args.Config.ReadPublicKey, args2.Config.ReadPublicKey)
assert.Equal(t, tt.args.Config.WritePublicKey, args2.Config.WritePublicKey)
assert.Equal(t, tt.args.Config.OwnerPublicKey, args2.Config.OwnerPublicKey)
assert.Equal(t, tt.args.RdfSchema, args2.RdfSchema)
})
}
}
func TestComputeTypes_Validation(t *testing.T) {
t.Run("ComputeConfiguration validation", func(t *testing.T) {
// Valid configuration
config := &ComputeConfiguration{
ReadPublicKey: make([]byte, 57),
WritePublicKey: make([]byte, 57),
}
assert.NoError(t, config.Validate())
// Invalid read key length
config.ReadPublicKey = make([]byte, 56)
assert.Error(t, config.Validate())
// Invalid write key length
config.ReadPublicKey = make([]byte, 57)
config.WritePublicKey = make([]byte, 58)
assert.Error(t, config.Validate())
// Nil configuration
var nilConfig *ComputeConfiguration
assert.Error(t, nilConfig.Validate())
})
t.Run("CodeDeployment validation", func(t *testing.T) {
// Valid deployment
deployment := &CodeDeployment{
Circuit: []byte("circuit"),
InputTypes: []string{"type1", "type2"},
OutputTypes: []string{"output"},
Domain: make([]byte, 32),
}
assert.NoError(t, deployment.Validate())
// Empty circuit
deployment.Circuit = []byte{}
assert.Error(t, deployment.Validate())
// Wrong number of input types
deployment.Circuit = []byte("circuit")
deployment.InputTypes = []string{"type1"}
assert.Error(t, deployment.Validate())
// Invalid domain length
deployment.InputTypes = []string{"type1", "type2"}
deployment.Domain = make([]byte, 31)
assert.Error(t, deployment.Validate())
})
t.Run("CodeExecute validation", func(t *testing.T) {
// Valid execute
execute := &CodeExecute{
ProofOfPayment: [][]byte{
[]byte("proof1"),
[]byte("proof2"),
},
Domain: make([]byte, 32),
Rendezvous: make([]byte, 32),
ExecuteOperations: []*ExecuteOperation{
{
Application: &Application{
Address: []byte("app"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("op1"),
},
},
}
assert.NoError(t, execute.Validate())
// Wrong number of payment proofs
execute.ProofOfPayment = [][]byte{[]byte("proof1")}
assert.Error(t, execute.Validate())
// Invalid domain length
execute.ProofOfPayment = [][]byte{[]byte("proof1"), []byte("proof2")}
execute.Domain = make([]byte, 31)
assert.Error(t, execute.Validate())
// No operations
execute.Domain = make([]byte, 32)
execute.ExecuteOperations = []*ExecuteOperation{}
assert.Error(t, execute.Validate())
})
t.Run("CodeFinalize validation", func(t *testing.T) {
// Valid finalize
finalize := &CodeFinalize{
Rendezvous: make([]byte, 32),
Results: []*ExecutionResult{},
}
assert.NoError(t, finalize.Validate())
// Invalid rendezvous length
finalize.Rendezvous = make([]byte, 31)
assert.Error(t, finalize.Validate())
// Nil finalize
var nilFinalize *CodeFinalize
assert.Error(t, nilFinalize.Validate())
})
t.Run("ComputeDeployArguments validation", func(t *testing.T) {
// Valid arguments
args := &ComputeDeploy{
Config: &ComputeConfiguration{
ReadPublicKey: make([]byte, 57),
WritePublicKey: make([]byte, 57),
OwnerPublicKey: make([]byte, 585),
},
RdfSchema: []byte("schema"),
}
assert.NoError(t, args.Validate())
// Invalid key lengths
args.Config.ReadPublicKey = make([]byte, 56)
assert.Error(t, args.Validate())
// Empty schema
args.Config.ReadPublicKey = make([]byte, 57)
args.RdfSchema = []byte{}
assert.NoError(t, args.Validate())
})
}
func TestExecutionContext_Values(t *testing.T) {
// Test that enum values match expected constants
assert.Equal(t, ExecutionContext(0), ExecutionContext_EXECUTION_CONTEXT_INTRINSIC)
assert.Equal(t, ExecutionContext(1), ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH)
assert.Equal(t, ExecutionContext(2), ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC)
}
func TestComputeSerialization_RoundTrip(t *testing.T) {
// Test that serialize -> deserialize -> serialize produces the same bytes
config := &ComputeConfiguration{
ReadPublicKey: randomBytes(t, 57),
WritePublicKey: randomBytes(t, 57),
}
// First serialization
data1, err := config.ToCanonicalBytes()
require.NoError(t, err)
// Deserialize
config2 := &ComputeConfiguration{}
err = config2.FromCanonicalBytes(data1)
require.NoError(t, err)
// Second serialization
data2, err := config2.ToCanonicalBytes()
require.NoError(t, err)
// Should be identical
assert.Equal(t, data1, data2)
}
func TestCodeExecute_ComplexDAG(t *testing.T) {
// Test a complex execution DAG
execute := &CodeExecute{
ProofOfPayment: [][]byte{
randomBytes(t, 100),
randomBytes(t, 100),
},
Domain: randomBytes(t, 32),
Rendezvous: randomBytes(t, 32),
ExecuteOperations: []*ExecuteOperation{
{
Application: &Application{
Address: randomBytes(t, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("root"),
Dependencies: [][]byte{},
},
{
Application: &Application{
Address: randomBytes(t, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("child1"),
Dependencies: [][]byte{[]byte("root")},
},
{
Application: &Application{
Address: randomBytes(t, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("child2"),
Dependencies: [][]byte{[]byte("root")},
},
{
Application: &Application{
Address: randomBytes(t, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC,
},
Identifier: []byte("grandchild"),
Dependencies: [][]byte{[]byte("child1"), []byte("child2")},
},
},
}
// Serialize
data, err := execute.ToCanonicalBytes()
require.NoError(t, err)
// Deserialize
execute2 := &CodeExecute{}
err = execute2.FromCanonicalBytes(data)
require.NoError(t, err)
// Verify complex structure is preserved
assert.Equal(t, len(execute.ExecuteOperations), len(execute2.ExecuteOperations))
assert.Equal(t, execute.ExecuteOperations[3].Dependencies, execute2.ExecuteOperations[3].Dependencies)
}
func TestComputeUpdate_Serialization(t *testing.T) {
tests := []struct {
name string
update *ComputeUpdate
}{
{
name: "complete compute update",
update: &ComputeUpdate{
Config: &ComputeConfiguration{
ReadPublicKey: make([]byte, 57),
WritePublicKey: make([]byte, 57),
OwnerPublicKey: make([]byte, 585),
},
PublicKeySignatureBls48581: &BLS48581AggregateSignature{
PublicKey: &BLS48581G2PublicKey{
KeyValue: make([]byte, 585),
},
Signature: make([]byte, 74),
Bitmask: []byte{0xFF, 0xFF},
},
},
},
{
name: "update with different keys",
update: &ComputeUpdate{
Config: &ComputeConfiguration{
ReadPublicKey: append([]byte{0x01}, make([]byte, 56)...),
WritePublicKey: append([]byte{0x02}, make([]byte, 56)...),
OwnerPublicKey: append([]byte{0x03}, make([]byte, 584)...),
},
PublicKeySignatureBls48581: &BLS48581AggregateSignature{
PublicKey: &BLS48581G2PublicKey{
KeyValue: append([]byte{0xAA}, make([]byte, 584)...),
},
Signature: append([]byte{0xBB}, make([]byte, 73)...),
Bitmask: []byte{0x0F},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.update.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
update2 := &ComputeUpdate{}
err = update2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.update.Config.ReadPublicKey, update2.Config.ReadPublicKey)
assert.Equal(t, tt.update.Config.WritePublicKey, update2.Config.WritePublicKey)
assert.Equal(t, tt.update.Config.OwnerPublicKey, update2.Config.OwnerPublicKey)
assert.Equal(t, tt.update.PublicKeySignatureBls48581.PublicKey.KeyValue, update2.PublicKeySignatureBls48581.PublicKey.KeyValue)
assert.Equal(t, tt.update.PublicKeySignatureBls48581.Signature, update2.PublicKeySignatureBls48581.Signature)
assert.Equal(t, tt.update.PublicKeySignatureBls48581.Bitmask, update2.PublicKeySignatureBls48581.Bitmask)
})
}
}
func TestIntrinsicExecutionInput_Serialization(t *testing.T) {
tests := []struct {
name string
input *IntrinsicExecutionInput
}{
{
name: "complete intrinsic input",
input: &IntrinsicExecutionInput{
Address: make([]byte, 32),
Input: []byte("intrinsic input data"),
},
},
{
name: "empty input",
input: &IntrinsicExecutionInput{
Address: append([]byte{0xFF}, make([]byte, 31)...),
Input: []byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.input.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
input2 := &IntrinsicExecutionInput{}
err = input2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.input.Address, input2.Address)
assert.Equal(t, tt.input.Input, input2.Input)
})
}
}
func TestIntrinsicExecutionOutput_Serialization(t *testing.T) {
tests := []struct {
name string
output *IntrinsicExecutionOutput
}{
{
name: "complete intrinsic output",
output: &IntrinsicExecutionOutput{
Address: make([]byte, 32),
Output: []byte("intrinsic output data"),
Proof: make([]byte, 128),
},
},
{
name: "output with different data",
output: &IntrinsicExecutionOutput{
Address: append([]byte{0xAA}, make([]byte, 31)...),
Output: []byte("different output"),
Proof: append([]byte{0xFF}, make([]byte, 127)...),
},
},
{
name: "minimal output",
output: &IntrinsicExecutionOutput{
Address: []byte{},
Output: []byte{},
Proof: []byte{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.output.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
output2 := &IntrinsicExecutionOutput{}
err = output2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.output.Address, output2.Address)
assert.Equal(t, tt.output.Output, output2.Output)
assert.Equal(t, tt.output.Proof, output2.Proof)
})
}
}
func TestExecutionDependency_Serialization(t *testing.T) {
tests := []struct {
name string
dep *ExecutionDependency
}{
{
name: "complete dependency",
dep: &ExecutionDependency{
Identifier: []byte("operation-12345"),
ReadSet: [][]byte{[]byte("read-addr-1"), []byte("read-addr-2")},
WriteSet: [][]byte{[]byte("write-addr-1")},
Stage: 2,
},
},
{
name: "dependency with no read/write sets",
dep: &ExecutionDependency{
Identifier: []byte("standalone-op"),
ReadSet: [][]byte{},
WriteSet: [][]byte{},
Stage: 0,
},
},
{
name: "dependency with large sets",
dep: &ExecutionDependency{
Identifier: []byte("complex-op"),
ReadSet: [][]byte{
[]byte("read1"), []byte("read2"), []byte("read3"),
},
WriteSet: [][]byte{
[]byte("write1"), []byte("write2"),
},
Stage: uint32(1<<31 - 1),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.dep.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
dep2 := &ExecutionDependency{}
err = dep2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.dep.Identifier, dep2.Identifier)
assert.Equal(t, tt.dep.ReadSet, dep2.ReadSet)
assert.Equal(t, tt.dep.WriteSet, dep2.WriteSet)
assert.Equal(t, tt.dep.Stage, dep2.Stage)
})
}
}
func TestExecutionNode_Serialization(t *testing.T) {
tests := []struct {
name string
node *ExecutionNode
}{
{
name: "complete execution node",
node: &ExecutionNode{
Operation: &ExecuteOperation{
Application: &Application{
Address: make([]byte, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("op-99999"),
Dependencies: [][]byte{[]byte("dep-1"), []byte("dep-2")},
},
ReadSet: [][]byte{[]byte("read-addr-1"), []byte("read-addr-2")},
WriteSet: [][]byte{[]byte("write-addr-1")},
Stage: 2,
Visited: true,
InProgress: false,
},
},
{
name: "node in progress",
node: &ExecutionNode{
Operation: &ExecuteOperation{
Application: &Application{
Address: append([]byte{0xAA}, make([]byte, 31)...),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("in-progress-op"),
Dependencies: [][]byte{},
},
ReadSet: [][]byte{[]byte("read-only")},
WriteSet: [][]byte{},
Stage: 0,
Visited: false,
InProgress: true,
},
},
{
name: "minimal node",
node: &ExecutionNode{
Operation: &ExecuteOperation{
Application: &Application{
Address: []byte{},
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC,
},
Identifier: []byte{},
Dependencies: [][]byte{},
},
ReadSet: [][]byte{},
WriteSet: [][]byte{},
Stage: 0,
Visited: false,
InProgress: false,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.node.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
node2 := &ExecutionNode{}
err = node2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.node.Operation.Application.Address, node2.Operation.Application.Address)
assert.Equal(t, tt.node.Operation.Application.ExecutionContext, node2.Operation.Application.ExecutionContext)
assert.Equal(t, tt.node.Operation.Identifier, node2.Operation.Identifier)
assert.Equal(t, tt.node.Operation.Dependencies, node2.Operation.Dependencies)
assert.Equal(t, tt.node.ReadSet, node2.ReadSet)
assert.Equal(t, tt.node.WriteSet, node2.WriteSet)
assert.Equal(t, tt.node.Stage, node2.Stage)
assert.Equal(t, tt.node.Visited, node2.Visited)
assert.Equal(t, tt.node.InProgress, node2.InProgress)
})
}
}
func TestExecutionStage_Serialization(t *testing.T) {
tests := []struct {
name string
stage *ExecutionStage
}{
{
name: "complete execution stage",
stage: &ExecutionStage{
OperationIds: []string{"op-1", "op-2", "op-3", "op-4", "op-5"},
},
},
{
name: "single operation stage",
stage: &ExecutionStage{
OperationIds: []string{"standalone-op"},
},
},
{
name: "empty stage",
stage: &ExecutionStage{
OperationIds: []string{},
},
},
{
name: "stage with complex operation names",
stage: &ExecutionStage{
OperationIds: []string{"compute-123", "transform-abc", "aggregate-xyz"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.stage.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
stage2 := &ExecutionStage{}
err = stage2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, tt.stage.OperationIds, stage2.OperationIds)
})
}
}
func TestExecutionDAG_Serialization(t *testing.T) {
tests := []struct {
name string
dag *ExecutionDAG
}{
{
name: "complete DAG",
dag: &ExecutionDAG{
Operations: map[string]*ExecutionNode{
"op-1": {
Operation: &ExecuteOperation{
Application: &Application{
Address: make([]byte, 32),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("op-1"),
Dependencies: [][]byte{},
},
ReadSet: [][]byte{[]byte("read1")},
WriteSet: [][]byte{[]byte("write1")},
Stage: 0,
Visited: false,
InProgress: false,
},
"op-2": {
Operation: &ExecuteOperation{
Application: &Application{
Address: append([]byte{0xAA}, make([]byte, 31)...),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_HYPERGRAPH,
},
Identifier: []byte("op-2"),
Dependencies: [][]byte{[]byte("op-1")},
},
ReadSet: [][]byte{[]byte("write1")},
WriteSet: [][]byte{[]byte("write2")},
Stage: 1,
Visited: true,
InProgress: false,
},
},
Stages: []*ExecutionStage{
{OperationIds: []string{"op-1"}},
{OperationIds: []string{"op-2"}},
},
},
},
{
name: "DAG with parallel execution",
dag: &ExecutionDAG{
Operations: map[string]*ExecutionNode{
"parallel-1": {
Operation: &ExecuteOperation{
Application: &Application{
Address: []byte("app1"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC,
},
Identifier: []byte("parallel-1"),
Dependencies: [][]byte{},
},
ReadSet: [][]byte{},
WriteSet: [][]byte{[]byte("output1")},
Stage: 0,
Visited: false,
InProgress: false,
},
"parallel-2": {
Operation: &ExecuteOperation{
Application: &Application{
Address: []byte("app2"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_EXTRINSIC,
},
Identifier: []byte("parallel-2"),
Dependencies: [][]byte{},
},
ReadSet: [][]byte{},
WriteSet: [][]byte{[]byte("output2")},
Stage: 0,
Visited: false,
InProgress: false,
},
"merge": {
Operation: &ExecuteOperation{
Application: &Application{
Address: []byte("merge-app"),
ExecutionContext: ExecutionContext_EXECUTION_CONTEXT_INTRINSIC,
},
Identifier: []byte("merge"),
Dependencies: [][]byte{[]byte("parallel-1"), []byte("parallel-2")},
},
ReadSet: [][]byte{[]byte("output1"), []byte("output2")},
WriteSet: [][]byte{[]byte("final-output")},
Stage: 1,
Visited: false,
InProgress: false,
},
},
Stages: []*ExecutionStage{
{OperationIds: []string{"parallel-1", "parallel-2"}},
{OperationIds: []string{"merge"}},
},
},
},
{
name: "empty DAG",
dag: &ExecutionDAG{
Operations: map[string]*ExecutionNode{},
Stages: []*ExecutionStage{},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
data, err := tt.dag.ToCanonicalBytes()
require.NoError(t, err)
require.NotNil(t, data)
dag2 := &ExecutionDAG{}
err = dag2.FromCanonicalBytes(data)
require.NoError(t, err)
assert.Equal(t, len(tt.dag.Operations), len(dag2.Operations))
assert.Equal(t, len(tt.dag.Stages), len(dag2.Stages))
for key, node := range tt.dag.Operations {
assert.Contains(t, dag2.Operations, key)
node2 := dag2.Operations[key]
assert.Equal(t, node.Operation.Identifier, node2.Operation.Identifier)
assert.Equal(t, node.ReadSet, node2.ReadSet)
assert.Equal(t, node.WriteSet, node2.WriteSet)
assert.Equal(t, node.Stage, node2.Stage)
assert.Equal(t, node.Visited, node2.Visited)
assert.Equal(t, node.InProgress, node2.InProgress)
}
for i := range tt.dag.Stages {
assert.Equal(t, tt.dag.Stages[i].OperationIds, dag2.Stages[i].OperationIds)
}
})
}
}