ceremonyclient/node/execution/engines/token_execution_engine_test.go
Cassandra Heart 53f7c2b5c9
v2.1.0.2 (#442)
* v2.1.0.2

* restore tweaks to simlibp2p

* fix: nil ref on size calc

* fix: panic should induce shutdown from event_distributor

* fix: friendlier initialization that requires less manual kickstarting for test/devnets

* fix: fewer available shards than provers should choose shard length

* fix: update stored worker registry, improve logging for debug mode

* fix: shut the fuck up, peer log

* qol: log value should be snake cased

* fix:non-archive snap sync issues

* fix: separate X448/Decaf448 signed keys, add onion key to registry

* fix: overflow arithmetic on frame number comparison

* fix: worker registration should be idempotent if inputs are same, otherwise permit updated records

* fix: remove global prover state from size calculation

* fix: divide by zero case

* fix: eager prover

* fix: broadcast listener default

* qol: diagnostic data for peer authenticator

* fix: master/worker connectivity issue in sparse networks

tight coupling of peer and workers can sometimes interfere if mesh is sparse, so give workers a pseudoidentity but publish messages with the proper peer key

* fix: reorder steps of join creation

* fix: join verify frame source + ensure domain is properly padded (unnecessary but good for consistency)

* fix: add delegate to protobuf <-> reified join conversion

* fix: preempt prover from planning with no workers

* fix: use the unallocated workers to generate a proof

* qol: underflow causes join fail in first ten frames on test/devnets

* qol: small logging tweaks for easier log correlation in debug mode

* qol: use fisher-yates shuffle to ensure prover allocations are evenly distributed when scores are equal

* qol: separate decisional logic on post-enrollment confirmation into consensus engine, proposer, and worker manager where relevant, refactor out scoring

* reuse shard descriptors for both join planning and confirm/reject decisions

* fix: add missing interface method and amend test blossomsub to use new peer id basis

* fix: only check allocations if they exist

* fix: pomw mint proof data needs to be hierarchically under global intrinsic domain

* staging temporary state under diagnostics

* fix: first phase of distributed lock refactoring

* fix: compute intrinsic locking

* fix: hypergraph intrinsic locking

* fix: token intrinsic locking

* fix: update execution engines to support new locking model

* fix: adjust tests with new execution shape

* fix: weave in lock/unlock semantics to liveness provider

* fix lock fallthrough, add missing allocation update

* qol: additional logging for diagnostics, also testnet/devnet handling for confirmations

* fix: establish grace period on halt scenario to permit recovery

* fix: support test/devnet defaults for coverage scenarios

* fix: nil ref on consensus halts for non-archive nodes

* fix: remove unnecessary prefix from prover ref

* add test coverage for fork choice behaviors and replay – once passing, blocker (2) is resolved

* fix: no fork replay on repeat for non-archive nodes, snap now behaves correctly

* rollup of pre-liveness check lock interactions

* ahead of tests, get the protobuf/metrics-related changes out so teams can prepare

* add test coverage for distributed lock behaviors – once passing, blocker (3) is resolved

* fix: blocker (3)

* Dev docs improvements (#445)

* Make install deps script more robust

* Improve testing instructions

* Worker node should stop upon OS SIGINT/SIGTERM signal (#447)

* move pebble close to Stop()

* move deferred Stop() to Start()

* add core id to worker stop log message

* create done os signal channel and stop worker upon message to it

---------

Co-authored-by: Cassandra Heart <7929478+CassOnMars@users.noreply.github.com>

---------

Co-authored-by: Daz <daz_the_corgi@proton.me>
Co-authored-by: Black Swan <3999712+blacks1ne@users.noreply.github.com>
2025-10-23 01:03:06 -05:00

740 lines
26 KiB
Go

package engines_test
import (
"bytes"
"crypto/rand"
"math/big"
"slices"
"testing"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"golang.org/x/crypto/sha3"
"source.quilibrium.com/quilibrium/monorepo/node/execution/engines"
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/token"
hgstate "source.quilibrium.com/quilibrium/monorepo/node/execution/state/hypergraph"
"source.quilibrium.com/quilibrium/monorepo/node/keys"
"source.quilibrium.com/quilibrium/monorepo/node/tests"
"source.quilibrium.com/quilibrium/monorepo/protobufs"
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
"source.quilibrium.com/quilibrium/monorepo/types/mocks"
"source.quilibrium.com/quilibrium/monorepo/types/schema"
"source.quilibrium.com/quilibrium/monorepo/types/tries"
)
func newTokenRDFHypergraphSchema(
appAddress []byte,
config *token.TokenIntrinsicConfiguration,
) (string, error) {
schemaDoc, err := token.PrepareRDFSchemaFromConfig(appAddress, config)
if err != nil {
return "", errors.Wrap(err, "new token rdf hypergraph schema")
}
valid, err := (&schema.TurtleRDFParser{}).Validate(schemaDoc)
if err != nil {
return "", errors.Wrap(err, "new token rdf hypergraph schema")
}
if !valid {
return "", errors.Wrap(
errors.New("invalid schema"),
"new token rdf hypergraph schema",
)
}
return schemaDoc, nil
}
// Creates a mintable token configuration with authority for testing
func createMintableTestConfig() *token.TokenIntrinsicConfiguration {
return &token.TokenIntrinsicConfiguration{
Name: "Test Token",
Symbol: "TEST",
Supply: big.NewInt(100000),
Units: big.NewInt(10),
Behavior: token.Acceptable | token.Divisible,
}
}
func TestTokenExecutionEngine_Start(t *testing.T) {
logger := zap.NewNop()
mockHG := new(mocks.MockHypergraph)
mockHG.On("GetCoveredPrefix").Return([]int{}, nil).Maybe()
mockClockStore := new(mocks.MockClockStore)
mockKeyManager := new(mocks.MockKeyManager)
mockInclusionProver := new(mocks.MockInclusionProver)
mockBulletproofProver := new(mocks.MockBulletproofProver)
mockVerEnc := new(mocks.MockVerifiableEncryptor)
mockDecaf := new(mocks.MockDecafConstructor)
engine, err := engines.NewTokenExecutionEngine(
logger,
mockHG,
mockClockStore,
mockKeyManager,
mockInclusionProver,
mockBulletproofProver,
mockVerEnc,
mockDecaf,
engines.ApplicationMode,
)
require.NoError(t, err)
// Test starting and stopping the engine
errChan := engine.Start()
// Engine should start without errors
select {
case err := <-errChan:
t.Fatalf("unexpected error from Start: %v", err)
default:
// Expected: no immediate error
}
// Stop the engine
<-engine.Stop(false)
}
func TestTokenExecutionEngine_ProcessMessage_DeployEdgeCases(t *testing.T) {
tests := []struct {
name string
mode engines.ExecutionMode
setupMocks func(*mocks.MockHypergraph, *mocks.MockKeyManager, *mocks.MockVerifiableEncryptor, *mocks.MockDecafConstructor, *mocks.MockBulletproofProver, *mocks.MockInclusionProver)
message *protobufs.Message
address []byte
wantErr bool
errContains string
}{
{
name: "nil_message",
mode: engines.ApplicationMode,
setupMocks: func(mockHG *mocks.MockHypergraph, mockKM *mocks.MockKeyManager, mockVE *mocks.MockVerifiableEncryptor, mockDC *mocks.MockDecafConstructor, mockBP *mocks.MockBulletproofProver, mockIP *mocks.MockInclusionProver) {
// No mocks needed
},
message: &protobufs.Message{},
address: token.TOKEN_BASE_DOMAIN[:], // Use 32-byte address
wantErr: true,
errContains: "invalid message",
},
{
name: "message_payload_too_short",
mode: engines.ApplicationMode,
setupMocks: func(mockHG *mocks.MockHypergraph, mockKM *mocks.MockKeyManager, mockVE *mocks.MockVerifiableEncryptor, mockDC *mocks.MockDecafConstructor, mockBP *mocks.MockBulletproofProver, mockIP *mocks.MockInclusionProver) {
// No mocks needed
},
message: &protobufs.Message{
Payload: []byte{0x01}, // Less than 4 bytes
},
address: token.TOKEN_BASE_DOMAIN[:], // Use 32-byte address
wantErr: true,
errContains: "invalid message",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
logger := zap.NewNop()
mockHG := new(mocks.MockHypergraph)
mockHG.On("GetCoveredPrefix").Return([]int{}, nil).Maybe()
mockClockStore := new(mocks.MockClockStore)
mockKeyManager := new(mocks.MockKeyManager)
mockInclusionProver := new(mocks.MockInclusionProver)
mockBulletproofProver := new(mocks.MockBulletproofProver)
mockVerEnc := new(mocks.MockVerifiableEncryptor)
mockDecaf := new(mocks.MockDecafConstructor)
tt.setupMocks(mockHG, mockKeyManager, mockVerEnc, mockDecaf, mockBulletproofProver, mockInclusionProver)
engine, err := engines.NewTokenExecutionEngine(
logger,
mockHG,
mockClockStore,
mockKeyManager,
mockInclusionProver,
mockBulletproofProver,
mockVerEnc,
mockDecaf,
tt.mode,
)
require.NoError(t, err)
// Process message
state := hgstate.NewHypergraphState(mockHG)
responses, err := engine.ProcessMessage(1, big.NewInt(1), tt.address, tt.message.Payload, state)
if tt.wantErr {
assert.Error(t, err)
if tt.errContains != "" {
assert.Contains(t, err.Error(), tt.errContains)
}
assert.Nil(t, responses)
} else {
assert.NoError(t, err)
assert.NotNil(t, responses)
err = responses.State.Commit()
assert.NoError(t, err)
}
})
}
}
func TestTokenExecutionEngine_BundledMessages(t *testing.T) {
t.Skip("something weird about payment setup")
logger := zap.NewNop()
mockHG := new(mocks.MockHypergraph)
mockHG.On("GetCoveredPrefix").Return([]int{}, nil).Maybe()
mockClockStore := new(mocks.MockClockStore)
mockKeyManager := new(mocks.MockKeyManager)
mockInclusionProver := new(mocks.MockInclusionProver)
mockBulletproofProver := new(mocks.MockBulletproofProver)
mockVerEnc := new(mocks.MockVerifiableEncryptor)
mockDecaf := new(mocks.MockDecafConstructor)
// Mock GetProver - required for hypergraph state initialization
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
// Mock inclusion prover for CommitRaw operations
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 64), nil).Maybe()
// Mock transaction operations
mockTxn := &mockTransaction{}
mockHG.On("NewTransaction", mock.Anything).Return(mockTxn, nil).Maybe()
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
// Mock for TrackChange
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
// Mock commit - should return [][]byte
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{tries.ShardKey{L1: [3]byte{}, L2: [32]byte{}}: [][]byte{make([]byte, 74), make([]byte, 74), make([]byte, 74), make([]byte, 74)}}).Maybe()
// Set up the mock to return not found for token transactions (LoadTokenIntrinsic)
mockHG.On("GetVertex", [64]byte{0xb, 0x8d, 0xa5, 0x37, 0xba, 0x80, 0x77, 0x1, 0x23, 0x9, 0xc6, 0x53, 0x32, 0xd2, 0xbb, 0x4b, 0x6c, 0xc6, 0x8e, 0x6e, 0x61, 0x60, 0xbd, 0xa, 0xa1, 0x98, 0xe8, 0x7f, 0xa8, 0x1c, 0x65, 0xd5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}).Return(nil, errors.New("not found")).Maybe()
// Mock for deployment
mockKeyManager.On("Prove", mock.Anything, mock.Anything, mock.Anything).Return([]byte("signature"), nil).Maybe()
mockBulletproofProver.On("GenerateRangeProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte("proof"), nil).Maybe()
mockBulletproofProver.On("ProveCreate", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte("proof"), nil).Maybe()
mockBulletproofProver.On("VerifyHidden", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true)
mockBulletproofProver.On("VerifyRangeProof", mock.Anything, mock.Anything, mock.Anything).Return(true)
mockBulletproofProver.On("SumCheck", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true)
mockVerEnc.On("GenerateKeyPair").Return(make([]byte, 57), make([]byte, 57), nil).Maybe()
engine, err := engines.NewTokenExecutionEngine(
logger,
mockHG,
mockClockStore,
mockKeyManager,
mockInclusionProver,
mockBulletproofProver,
mockVerEnc,
mockDecaf,
engines.ApplicationMode,
)
require.NoError(t, err)
// Create a bundle with just deploy and a payment
// We'll test vertex operations separately since they need the deployed address
tokenDeploy := createTokenDeployPayloadFull(t, mockHG)
require.NoError(t, err)
bundleBytes, err := tokenDeploy.ToCanonicalBytes()
require.NoError(t, err)
// Create bundle message
bundleMsg := &protobufs.Message{
Address: token.TOKEN_BASE_DOMAIN[:],
Payload: bundleBytes,
}
// Set hash
hash := sha3.Sum256(bundleMsg.Payload)
bundleMsg.Hash = hash[:]
// Process bundle
state := hgstate.NewHypergraphState(mockHG)
responses, err := engine.ProcessMessage(1, big.NewInt(1), token.TOKEN_BASE_DOMAIN[:], bundleMsg.Payload, state)
assert.NoError(t, err)
assert.NotNil(t, responses)
err = responses.State.Commit()
assert.NoError(t, err)
}
func TestTokenExecutionEngine_ModeSwitch(t *testing.T) {
// Test that GlobalMode properly restricts operations after deployment
logger := zap.NewNop()
mockHG := new(mocks.MockHypergraph)
mockHG.On("GetCoveredPrefix").Return([]int{}, nil).Maybe()
mockClockStore := new(mocks.MockClockStore)
mockKeyManager := new(mocks.MockKeyManager)
mockInclusionProver := new(mocks.MockInclusionProver)
mockBulletproofProver := new(mocks.MockBulletproofProver)
mockVerEnc := new(mocks.MockVerifiableEncryptor)
mockDecaf := new(mocks.MockDecafConstructor)
// Create test address
testAddr := make([]byte, 32)
rand.Read(testAddr)
vertexAddr := [64]byte(slices.Concat(testAddr, bytes.Repeat([]byte{0xff}, 32)))
// First, test that we can deploy in global mode
mockHG.On("GetVertex", mock.Anything).Return(nil, errors.New("not found")).Maybe()
// Mock GetProver - required for hypergraph state initialization
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
// Mock inclusion prover for CommitRaw operations
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 64), nil).Maybe()
// Mock transaction operations
mockTxn := &mockTransaction{}
mockHG.On("NewTransaction", mock.Anything).Return(mockTxn, nil).Maybe()
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("GetShardCommits", mock.Anything, mock.Anything).Return([][]byte{make([]byte, 64), make([]byte, 64), make([]byte, 64), make([]byte, 64)}, nil)
// Mock for TrackChange
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("GetMetadata", mock.Anything).Return(nil, errors.New("not found")).Maybe()
mockHG.On("SetMetadata", mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("SetVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{tries.ShardKey{L1: [3]byte{}, L2: [32]byte{}}: [][]byte{make([]byte, 74), make([]byte, 74), make([]byte, 74), make([]byte, 74)}}).Maybe()
mockKeyManager.On("Prove", mock.Anything, mock.Anything, mock.Anything).Return([]byte("signature"), nil).Maybe()
mockVerEnc.On("GenerateKeyPair").Return(make([]byte, 57), make([]byte, 57), nil).Maybe()
mockBulletproofProver.On("ProveCreate", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte("proof"), nil).Maybe()
mockBulletproofProver.On("VerifyHidden", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true).Maybe()
mockBulletproofProver.On("VerifyRangeProof", mock.Anything, mock.Anything, mock.Anything).Return(true)
mockBulletproofProver.On("SumCheck", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true)
globalEngine, err := engines.NewTokenExecutionEngine(
logger,
mockHG,
mockClockStore,
mockKeyManager,
mockInclusionProver,
mockBulletproofProver,
mockVerEnc,
mockDecaf,
engines.GlobalMode,
)
require.NoError(t, err)
deploy := createTokenDeployPayloadFull(t, mockHG)
deployReq, _ := deploy.ToCanonicalBytes()
// Deploy message should work
deployMsg := &protobufs.Message{
Address: token.TOKEN_BASE_DOMAIN[:],
Payload: deployReq,
}
state := hgstate.NewHypergraphState(mockHG)
responses, err := globalEngine.ProcessMessage(2, big.NewInt(1), token.TOKEN_BASE_DOMAIN[:], deployMsg.Payload, state)
assert.NoError(t, err)
assert.NotNil(t, responses)
err = responses.State.Commit()
assert.NoError(t, err)
// Mock that token exists for the second call
mockVertex := new(mocks.MockVertex)
mockHG.On("GetVertex", vertexAddr).Return(mockVertex, nil).Once()
metadataTree := &tries.VectorCommitmentTree{}
// Create test configuration
config := createMintableTestConfig()
// Create the metadata tree with valid configuration
rdfMultiprover := schema.NewRDFMultiprover(&schema.TurtleRDFParser{}, mockInclusionProver)
configTree, err := token.NewTokenConfigurationMetadata(config, rdfMultiprover)
require.NoError(t, err)
// Store consensus tree
consensus := &tries.VectorCommitmentTree{}
consensusData, _ := tries.SerializeNonLazyTree(consensus)
require.NoError(t, metadataTree.Insert([]byte{0 << 2}, consensusData, nil, big.NewInt(int64(len(consensusData)))))
// Store sumcheck tree
sumcheck := &tries.VectorCommitmentTree{}
sumcheckData, _ := tries.SerializeNonLazyTree(sumcheck)
require.NoError(t, metadataTree.Insert([]byte{1 << 2}, sumcheckData, nil, big.NewInt(int64(len(sumcheckData)))))
// Store RDF schema
rdfschema, _ := newTokenRDFHypergraphSchema(vertexAddr[:32], config)
require.NoError(t, metadataTree.Insert([]byte{2 << 2}, []byte(rdfschema), nil, big.NewInt(int64(len(rdfschema)))))
// Store config metadata at the right index
configBytes, err := tries.SerializeNonLazyTree(configTree)
require.NoError(t, err)
require.NoError(t, metadataTree.Insert([]byte{16 << 2}, configBytes, nil, big.NewInt(int64(len(configBytes)))))
mockHG.On("GetVertexData", mock.Anything).Return(metadataTree, nil)
payload2, _ := createTokenTransactionPayload(t).ToCanonicalBytes()
// Transaction should fail in global mode
txMsg := &protobufs.Message{
Address: testAddr,
Payload: payload2,
}
state = hgstate.NewHypergraphState(mockHG)
_, err = globalEngine.ProcessMessage(2, big.NewInt(1), testAddr, txMsg.Payload, state)
assert.Error(t, err)
}
// Helper functions
func createTokenDeployPayloadFull(t *testing.T, mockHG *mocks.MockHypergraph) *protobufs.MessageBundle {
deploy := &token.TokenDeploy{
Config: &token.TokenIntrinsicConfiguration{
Name: "Test Token",
Symbol: "TEST",
Supply: big.NewInt(100000),
Units: big.NewInt(10),
Behavior: token.Acceptable | token.Divisible,
},
RDFSchema: []byte("context"),
}
outFees := []*big.Int{big.NewInt(1)}
total := big.NewInt(10000000)
outFees = append(outFees, total)
payment, err := tests.CreateValidQUILPendingTransactionPayload(
mockHG,
1,
[][]byte{make([]byte, 56)},
[][]byte{make([]byte, 56)},
[][]byte{make([]byte, 56)},
[][]byte{make([]byte, 56)},
[]*big.Int{big.NewInt(10000001)},
[]*big.Int{big.NewInt(0)},
outFees,
0,
)
if err != nil {
panic(err)
}
return &protobufs.MessageBundle{
Requests: []*protobufs.MessageRequest{
payment,
{
Request: &protobufs.MessageRequest_TokenDeploy{
TokenDeploy: deploy.ToProtobuf(),
},
},
},
}
}
func createTokenTransactionPayload(t *testing.T) *protobufs.MessageRequest {
// Create mock dependencies
mockHG := new(mocks.MockHypergraph)
mockBP := new(mocks.MockBulletproofProver)
mockBP.On("GenerateRangeProofFromBig", mock.Anything, mock.Anything, mock.Anything).Return(crypto.RangeProofResult{
Proof: make([]byte, 56),
Commitment: make([]byte, 112),
Blinding: make([]byte, 112),
}, nil)
mockBP.On("SignHidden", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(make([]byte, 336))
mockIP := new(mocks.MockInclusionProver)
mockIP.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
mockMultiproof := new(mocks.MockMultiproof)
mockMultiproof.On("FromBytes", mock.Anything).Return(nil)
mockMultiproof.On("ToBytes").Return([]byte{}, nil)
mockIP.On("ProveMultiple", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockMultiproof).Maybe()
mockVE := new(mocks.MockVerifiableEncryptor)
mockDC := new(mocks.MockDecafConstructor)
mockKM := new(mocks.MockKeyManager)
mockKM.On("GetAgreementKey", "q-view-key").Return(&mocks.MockDecafAgreement{}, nil)
mockKM.On("GetAgreementKey", "q-spend-key").Return(&mocks.MockDecafAgreement{}, nil)
mockHG.On("GetVertex", mock.Anything).Return(nil, nil)
mockTraversalProofMultiproof := new(mocks.MockMultiproof)
// Create a properly sized multiproof byte array (74 bytes multicommitment + some proof data)
multiproofBytes := make([]byte, 148) // 74 for multicommitment + 74 for proof
rand.Read(multiproofBytes)
mockTraversalProofMultiproof.On("ToBytes").Return(multiproofBytes, nil)
mockTraversalProof := &tries.TraversalProof{
Multiproof: mockTraversalProofMultiproof,
SubProofs: []tries.TraversalSubProof{
{
Commits: [][]byte{make([]byte, 74)}, // At least one commit
Ys: [][]byte{make([]byte, 64)}, // Matching Ys
Paths: [][]uint64{{0}}, // At least one path
},
},
}
mockHG.On("CreateTraversalProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockTraversalProof, nil).Maybe()
// Mock token configuration
tokenconfig := &token.TokenIntrinsicConfiguration{
Behavior: token.Divisible | token.Acceptable,
Units: big.NewInt(8), // Divisible tokens need units >= 8
Name: "Test Token",
Symbol: "TEST",
}
// Create test domain
domain := make([]byte, 32)
rand.Read(domain)
tr := &tries.VectorCommitmentTree{}
tr.Insert([]byte{0}, []byte{0, 0, 0, 0, 0, 0, 0, 0}, nil, big.NewInt(0))
tr.Insert([]byte{1 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{2 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{3 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{4 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{5 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{6 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{7 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{8 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{9 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{10 << 2}, make([]byte, 56), nil, big.NewInt(0))
tr.Insert([]byte{11 << 2}, make([]byte, 64), nil, big.NewInt(0))
tr.Insert([]byte{12 << 2}, make([]byte, 56), nil, big.NewInt(0))
tybi, _ := poseidon.HashBytes(slices.Concat(make([]byte, 32), []byte("pending:PendingTransaction")))
ty := tybi.FillBytes(make([]byte, 32))
tr.Insert(slices.Repeat([]byte{0xff}, 32), ty, nil, big.NewInt(0))
mockHG.On("GetVertexData", mock.Anything).Return(tr, nil)
// Create mock inputs
input1, _ := token.NewTransactionInput(make([]byte, 64))
input2, _ := token.NewTransactionInput(make([]byte, 64))
// Create mock outputs (mock keys)
mockVK := make([]byte, 56)
mockSK := make([]byte, 56)
rand.Read(mockVK)
rand.Read(mockSK)
out1, _ := token.NewTransactionOutput(big.NewInt(7), mockVK, mockSK)
out2, _ := token.NewTransactionOutput(big.NewInt(2), mockVK, mockSK)
rdf, _ := newTokenRDFHypergraphSchema(domain, tokenconfig)
// Create transaction
tx := token.NewTransaction(
[32]byte(domain),
[]*token.TransactionInput{input1, input2},
[]*token.TransactionOutput{out1, out2},
[]*big.Int{big.NewInt(1), big.NewInt(2)},
tokenconfig,
mockHG,
mockBP,
mockIP,
mockVE,
mockDC,
keys.ToKeyRing(mockKM, false),
rdf,
schema.NewRDFMultiprover(&schema.TurtleRDFParser{}, mockIP),
)
// Mock the Prove call to set required fields
tx.RangeProof = make([]byte, 100) // Mock range proof
err := tx.Prove(0)
require.NoError(t, err)
tx.TraversalProof = &tries.TraversalProof{
Multiproof: mockMultiproof,
SubProofs: []tries.TraversalSubProof{
{
Commits: [][]byte{make([]byte, 64)},
Ys: [][]byte{make([]byte, 64)},
Paths: [][]uint64{{0}},
},
},
}
// Set signatures on inputs
for _, input := range tx.Inputs {
input.Signature = make([]byte, 114) // Ed448 signature size
rand.Read(input.Signature)
input.Commitment = make([]byte, 56)
rand.Read(input.Commitment)
input.Proofs = [][]byte{make([]byte, 32)}
}
return &protobufs.MessageRequest{
Request: &protobufs.MessageRequest_Transaction{
Transaction: tx.ToProtobuf(),
},
}
}
func createTokenPendingTransactionPayload(t *testing.T) *protobufs.MessageRequest {
// Create mock dependencies
mockHG := new(mocks.MockHypergraph)
mockBP := new(mocks.MockBulletproofProver)
mockIP := new(mocks.MockInclusionProver)
mockVE := new(mocks.MockVerifiableEncryptor)
mockDC := new(mocks.MockDecafConstructor)
mockKM := new(mocks.MockKeyManager)
// Mock token configuration
tokenconfig := &token.TokenIntrinsicConfiguration{
Behavior: token.Divisible | token.Acceptable,
Units: big.NewInt(8), // Divisible tokens need units >= 8
Name: "Test Token",
Symbol: "TEST",
}
// Create test domain
domain := make([]byte, 32)
rand.Read(domain)
// Create mock inputs
input1, _ := token.NewPendingTransactionInput(make([]byte, 64))
// Create mock outputs (mock keys)
mockToVK := make([]byte, 56)
mockToSK := make([]byte, 56)
mockRefundVK := make([]byte, 56)
mockRefundSK := make([]byte, 56)
rand.Read(mockToVK)
rand.Read(mockToSK)
rand.Read(mockRefundVK)
rand.Read(mockRefundSK)
out1, _ := token.NewPendingTransactionOutput(big.NewInt(9), mockToVK, mockToSK, mockRefundVK, mockRefundSK, 1000)
// Create pending transaction
tx := token.NewPendingTransaction(
[32]byte(domain),
[]*token.PendingTransactionInput{input1},
[]*token.PendingTransactionOutput{out1},
[]*big.Int{big.NewInt(1)},
tokenconfig,
mockHG,
mockBP,
mockIP,
mockVE,
mockDC,
keys.ToKeyRing(mockKM, false),
"", // rdfSchema
nil, // rdfMultiprover
)
// Mock the Prove call to set required fields
tx.RangeProof = make([]byte, 100) // Mock range proof
// Create a mock multiproof
mockMultiproof := new(mocks.MockMultiproof)
mockMultiproof.On("ToBytes").Return(make([]byte, 100), nil)
tx.TraversalProof = &tries.TraversalProof{
Multiproof: mockMultiproof,
SubProofs: []tries.TraversalSubProof{
{
Commits: [][]byte{make([]byte, 64)},
Ys: [][]byte{make([]byte, 64)},
Paths: [][]uint64{{0}},
},
},
}
// Set signatures on inputs
for _, input := range tx.Inputs {
input.Signature = make([]byte, 114) // Ed448 signature size
rand.Read(input.Signature)
input.Commitment = make([]byte, 56)
rand.Read(input.Commitment)
input.Proofs = [][]byte{make([]byte, 32)}
}
return &protobufs.MessageRequest{
Request: &protobufs.MessageRequest_PendingTransaction{
PendingTransaction: tx.ToProtobuf(),
},
}
}
func createTokenMintTransactionPayload(t *testing.T) *protobufs.MessageRequest {
// Create mock dependencies
mockHG := new(mocks.MockHypergraph)
mockBP := new(mocks.MockBulletproofProver)
mockIP := new(mocks.MockInclusionProver)
mockVE := new(mocks.MockVerifiableEncryptor)
mockDC := new(mocks.MockDecafConstructor)
mockKM := new(mocks.MockKeyManager)
// Mock token configuration with mint strategy
tokenconfig := &token.TokenIntrinsicConfiguration{
Behavior: token.Mintable | token.Divisible,
MintStrategy: &token.TokenMintStrategy{
MintBehavior: token.MintWithProof,
ProofBasis: token.ProofOfMeaningfulWork,
},
Units: big.NewInt(1),
Name: "Test Token",
Symbol: "TEST",
}
// Create test domain
domain := make([]byte, 32)
rand.Read(domain)
// Create mock mint input with output data
outputData := make([]byte, 200) // Mock output data
rand.Read(outputData)
input, _ := token.NewMintTransactionInput(big.NewInt(10000), outputData)
// Create mock outputs (mock keys)
mockVK := make([]byte, 56)
mockSK := make([]byte, 56)
rand.Read(mockVK)
rand.Read(mockSK)
out, _ := token.NewMintTransactionOutput(big.NewInt(10000), mockVK, mockSK)
// Create mint transaction
tx := token.NewMintTransaction(
[32]byte(domain),
[]*token.MintTransactionInput{input},
[]*token.MintTransactionOutput{out},
[]*big.Int{big.NewInt(1)},
tokenconfig,
mockHG,
mockBP,
mockIP,
mockVE,
mockDC,
keys.ToKeyRing(mockKM, false),
"", // rdfSchema
nil, // rdfMultiprover
&mocks.MockClockStore{},
)
// Mock the Prove call to set required fields
tx.RangeProof = make([]byte, 100) // Mock range proof
// Set signatures on inputs
for _, input := range tx.Inputs {
input.Signature = make([]byte, 114) // Ed448 signature size
rand.Read(input.Signature)
input.Commitment = make([]byte, 56)
rand.Read(input.Commitment)
input.Proofs = [][]byte{make([]byte, 32)}
}
return &protobufs.MessageRequest{
Request: &protobufs.MessageRequest_MintTransaction{
MintTransaction: tx.ToProtobuf(),
},
}
}
func createNonTokenPayload(t *testing.T) *protobufs.MessageRequest {
return &protobufs.MessageRequest{
Request: &protobufs.MessageRequest_Confirm{
Confirm: &protobufs.ProverConfirm{
Filter: []byte{},
FrameNumber: 1,
PublicKeySignatureBls48581: &protobufs.BLS48581AddressedSignature{
Address: make([]byte, 32),
Signature: make([]byte, 74),
},
},
},
}
}