mirror of
https://github.com/QuilibriumNetwork/ceremonyclient.git
synced 2026-02-22 02:47:26 +08:00
* wip: conversion of hotstuff from flow into Q-oriented model * bulk of tests * remaining non-integration tests * add integration test, adjust log interface, small tweaks * further adjustments, restore full pacemaker shape * add component lifecycle management+supervisor * further refinements * resolve timeout hanging * mostly finalized state for consensus * bulk of engine swap out * lifecycle-ify most types * wiring nearly complete, missing needed hooks for proposals * plugged in, vetting message validation paths * global consensus, plugged in and verified * app shard now wired in too * do not decode empty keys.yml (#456) * remove obsolete engine.maxFrames config parameter (#454) * default to Info log level unless debug is enabled (#453) * respect config's "logging" section params, remove obsolete single-file logging (#452) * Trivial code cleanup aiming to reduce Go compiler warnings (#451) * simplify range traversal * simplify channel read for single select case * delete rand.Seed() deprecated in Go 1.20 and no-op as of Go 1.24 * simplify range traversal * simplify channel read for single select case * remove redundant type from array * simplify range traversal * simplify channel read for single select case * RC slate * finalize 2.1.0.5 * Update comments in StrictMonotonicCounter Fix comment formatting and clarify description. --------- Co-authored-by: Black Swan <3999712+blacks1ne@users.noreply.github.com>
5551 lines
186 KiB
Go
5551 lines
186 KiB
Go
package engines_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"slices"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/cloudflare/circl/sign/ed448"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
"github.com/multiformats/go-multiaddr"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap"
|
|
"golang.org/x/crypto/sha3"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
|
"source.quilibrium.com/quilibrium/monorepo/config"
|
|
"source.quilibrium.com/quilibrium/monorepo/go-libp2p-blossomsub/pb"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/consensus/app"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/consensus/global"
|
|
consensustime "source.quilibrium.com/quilibrium/monorepo/node/consensus/time"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/execution/engines"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/execution/intrinsics/compute"
|
|
"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/p2p"
|
|
pstore "source.quilibrium.com/quilibrium/monorepo/node/store"
|
|
"source.quilibrium.com/quilibrium/monorepo/node/tests"
|
|
"source.quilibrium.com/quilibrium/monorepo/protobufs"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/channel"
|
|
consensustypes "source.quilibrium.com/quilibrium/monorepo/types/consensus"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/execution"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/hypergraph"
|
|
tkeys "source.quilibrium.com/quilibrium/monorepo/types/keys"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/mocks"
|
|
tp2p "source.quilibrium.com/quilibrium/monorepo/types/p2p"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/schema"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/store"
|
|
"source.quilibrium.com/quilibrium/monorepo/types/tries"
|
|
)
|
|
|
|
type mockEncryptedChannel struct{}
|
|
|
|
// DecryptTwoPartyMessage implements channel.EncryptedChannel.
|
|
func (m *mockEncryptedChannel) DecryptTwoPartyMessage(ratchetState string, envelope *channel.P2PChannelEnvelope) (newRatchetState string, message []byte, err error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// EncryptTwoPartyMessage implements channel.EncryptedChannel.
|
|
func (m *mockEncryptedChannel) EncryptTwoPartyMessage(ratchetState string, message []byte) (newRatchetState string, envelope *channel.P2PChannelEnvelope, err error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// EstablishTwoPartyChannel implements channel.EncryptedChannel.
|
|
func (m *mockEncryptedChannel) EstablishTwoPartyChannel(isSender bool, sendingIdentityPrivateKey []byte, sendingSignedPrePrivateKey []byte, receivingIdentityKey []byte, receivingSignedPreKey []byte) (string, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// makeExtrinsicAddress creates a 32-byte address for ExecutionContextExtrinsic testing
|
|
func makeExtrinsicAddress(name string) []byte {
|
|
addr := make([]byte, 32)
|
|
copy(addr, []byte(name))
|
|
return addr
|
|
}
|
|
|
|
func generateRDFPrelude(
|
|
appAddress []byte,
|
|
config *token.TokenIntrinsicConfiguration,
|
|
) string {
|
|
appAddressHex := hex.EncodeToString(appAddress)
|
|
|
|
prelude := "BASE <https://types.quilibrium.com/schema-repository/>\n" +
|
|
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" +
|
|
"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" +
|
|
"PREFIX qcl: <https://types.quilibrium.com/qcl/>\n" +
|
|
"PREFIX coin: <https://types.quilibrium.com/schema-repository/token/" + appAddressHex + "/coin/>\n"
|
|
|
|
if config.Behavior&token.Acceptable != 0 {
|
|
prelude += "PREFIX pending: <https://types.quilibrium.com/schema-repository/token/" + appAddressHex + "/pending/>\n"
|
|
}
|
|
|
|
prelude += "\n"
|
|
|
|
return prelude
|
|
}
|
|
|
|
func prepareRDFSchemaFromConfig(
|
|
appAddress []byte,
|
|
config *token.TokenIntrinsicConfiguration,
|
|
) (string, error) {
|
|
schema := generateRDFPrelude(appAddress, config)
|
|
|
|
schema += "coin:Coin a rdfs:Class.\n" +
|
|
"coin:FrameNumber a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 8;\n" +
|
|
" qcl:order 0;\n" +
|
|
" rdfs:range coin:Coin.\n" +
|
|
"coin:Commitment a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 1;\n" +
|
|
" rdfs:range coin:Coin.\n" +
|
|
"coin:OneTimeKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 2;\n" +
|
|
" rdfs:range coin:Coin.\n" +
|
|
"coin:VerificationKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 3;\n" +
|
|
" rdfs:range coin:Coin.\n" +
|
|
"coin:CoinBalance a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 4;\n" +
|
|
" rdfs:range coin:Coin.\n" +
|
|
"coin:Mask a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 5;\n" +
|
|
" rdfs:range coin:Coin.\n"
|
|
|
|
if config.Behavior&token.Divisible == 0 {
|
|
schema += "coin:AdditionalReference a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 64;\n" +
|
|
" qcl:order 6;\n" +
|
|
" rdfs:range coin:Coin.\n"
|
|
schema += "coin:AdditionalReferenceKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 7;\n" +
|
|
" rdfs:range coin:Coin.\n"
|
|
}
|
|
|
|
if config.Behavior&token.Acceptable != 0 {
|
|
schema += "\npending:PendingTransaction a rdfs:Class;\n" +
|
|
" rdfs:label \"a pending transaction\".\n" +
|
|
"pending:FrameNumber a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 8;\n" +
|
|
" qcl:order 0;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:Commitment a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 1;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:ToOneTimeKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 2;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundOneTimeKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 3;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:ToVerificationKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 4;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundVerificationKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 5;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:ToCoinBalance a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 6;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundCoinBalance a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 7;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:ToMask a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 8;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundMask a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 9;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n"
|
|
|
|
if config.Behavior&token.Divisible == 0 {
|
|
schema += "pending:ToAdditionalReference a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 64;\n" +
|
|
" qcl:order 10;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:ToAdditionalReferenceKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 11;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundAdditionalReference a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 64;\n" +
|
|
" qcl:order 12;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n" +
|
|
"pending:RefundAdditionalReferenceKey a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:ByteArray;\n" +
|
|
" qcl:size 56;\n" +
|
|
" qcl:order 13;\n" +
|
|
" rdfs:range pending:PendingTransaction.\n"
|
|
}
|
|
|
|
if config.Behavior&token.Expirable != 0 {
|
|
schema += "pending:Expiration a rdfs:Property;\n" +
|
|
" rdfs:domain qcl:Uint;\n" +
|
|
" qcl:size 8;\n"
|
|
|
|
if config.Behavior&token.Divisible == 0 {
|
|
schema += " qcl:order 14;\n"
|
|
} else {
|
|
schema += " qcl:order 10;\n"
|
|
}
|
|
|
|
schema += " rdfs:range pending:PendingTransaction.\n"
|
|
}
|
|
}
|
|
|
|
schema += "\n"
|
|
|
|
return schema, nil
|
|
}
|
|
|
|
type mockVertex struct {
|
|
mock.Mock
|
|
}
|
|
|
|
// Commit implements hypergraph.Vertex.
|
|
func (m *mockVertex) Commit(prover crypto.InclusionProver) []byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetAppAddress implements hypergraph.Vertex.
|
|
func (m *mockVertex) GetAppAddress() [32]byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetAtomType implements hypergraph.Vertex.
|
|
func (m *mockVertex) GetAtomType() hypergraph.AtomType {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetDataAddress implements hypergraph.Vertex.
|
|
func (m *mockVertex) GetDataAddress() [32]byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetID implements hypergraph.Vertex.
|
|
func (m *mockVertex) GetID() [64]byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetSize implements hypergraph.Vertex.
|
|
func (m *mockVertex) GetSize() *big.Int {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// ToBytes implements hypergraph.Vertex.
|
|
func (m *mockVertex) ToBytes() []byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
type mockFrameValidator struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockFrameValidator) Validate(frame *protobufs.AppShardFrame) (bool, error) {
|
|
args := m.Called(frame)
|
|
return args.Bool(0), args.Error(1)
|
|
}
|
|
|
|
type mockDynamicFeeManager struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) AddFrameFeeVote(filter []byte, frameNumber uint64, feeMultiplierVote uint64) error {
|
|
args := m.Called(filter, frameNumber, feeMultiplierVote)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) GetNextFeeMultiplier(filter []byte) (uint64, error) {
|
|
args := m.Called(filter)
|
|
return args.Get(0).(uint64), args.Error(1)
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) GetVoteHistory(filter []byte) ([]uint64, error) {
|
|
args := m.Called(filter)
|
|
return args.Get(0).([]uint64), args.Error(1)
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) GetAverageWindowSize(filter []byte) (int, error) {
|
|
args := m.Called(filter)
|
|
return args.Int(0), args.Error(1)
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) PruneOldData(maxAge uint64) error {
|
|
args := m.Called(maxAge)
|
|
return args.Error(0)
|
|
}
|
|
|
|
func (m *mockDynamicFeeManager) RewindToFrame(filter []byte, frameNumber uint64) (int, error) {
|
|
args := m.Called(filter, frameNumber)
|
|
return args.Int(0), args.Error(1)
|
|
}
|
|
|
|
type mockGlobalFrameValidator struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockGlobalFrameValidator) Validate(frame *protobufs.GlobalFrame) (bool, error) {
|
|
args := m.Called(frame)
|
|
return args.Bool(0), args.Error(1)
|
|
}
|
|
|
|
func createTestGlobalConsensusEngine(t *testing.T) (
|
|
*global.GlobalConsensusEngine,
|
|
*mockPubSub,
|
|
*mockGlobalFrameValidator,
|
|
*mocks.MockDifficultyAdjuster,
|
|
*mocks.MockEventDistributor,
|
|
*consensustime.GlobalTimeReel,
|
|
) {
|
|
logger := zap.NewNop()
|
|
config := &config.Config{
|
|
Engine: &config.EngineConfig{
|
|
Difficulty: 100,
|
|
},
|
|
P2P: &config.P2PConfig{
|
|
Network: 99,
|
|
},
|
|
}
|
|
|
|
// Create mocks
|
|
pubsub := newMockPubSub([]byte{0x01, 0x02, 0x03, 0x04})
|
|
hypergraph := &mocks.MockHypergraph{}
|
|
keyManager := &mocks.MockKeyManager{}
|
|
keyStore := &mocks.MockKeyStore{}
|
|
clockStore := &mocks.MockClockStore{}
|
|
frameProver := &mocks.MockFrameProver{}
|
|
inclusionProver := &mocks.MockInclusionProver{}
|
|
signerRegistry := &mocks.MockSignerRegistry{}
|
|
proverRegistry := &mocks.MockProverRegistry{}
|
|
dynamicFeeManager := &mockDynamicFeeManager{}
|
|
appFrameValidator := new(mockFrameValidator)
|
|
frameValidator := &mockGlobalFrameValidator{}
|
|
difficultyAdjuster := &mocks.MockDifficultyAdjuster{}
|
|
rewardIssuance := &mocks.MockRewardIssuance{}
|
|
eventDistributor := &mocks.MockEventDistributor{}
|
|
|
|
// Create time reel
|
|
globalTimeReel, err := consensustime.NewGlobalTimeReel(logger, proverRegistry, clockStore, 99, true)
|
|
require.NoError(t, err)
|
|
|
|
eventDistributor.On("Start", mock.Anything).Return(nil)
|
|
eventDistributor.On("Subscribe", mock.Anything).Return(make(<-chan consensustypes.ControlEvent, 10))
|
|
eventDistributor.On("Unsubscribe", mock.Anything).Return()
|
|
eventDistributor.On("Stop").Return(nil)
|
|
proverRegistry.On("GetOrderedProvers", mock.Anything, mock.Anything).Return([][]byte{}, nil)
|
|
|
|
// Create mock signer with Public method
|
|
mockSigner := &mocks.MockBLSSigner{}
|
|
mockSigner.On("Public").Return(make([]byte, 585))
|
|
mockSigner.On("SignWithDomain", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
|
|
keyManager.On("GetSigningKey", mock.Anything).Return(mockSigner, nil)
|
|
keyManager.On("GetRawKey", mock.Anything).Return(&tkeys.Key{
|
|
Type: crypto.KeyTypeBLS48581G1,
|
|
PublicKey: make([]byte, 585),
|
|
}, nil)
|
|
|
|
// Create engine
|
|
engine, err := global.NewGlobalConsensusEngine(
|
|
logger,
|
|
config,
|
|
1000, // frameTimeMillis
|
|
pubsub,
|
|
hypergraph,
|
|
keyManager,
|
|
keyStore,
|
|
frameProver,
|
|
inclusionProver,
|
|
signerRegistry,
|
|
proverRegistry,
|
|
dynamicFeeManager,
|
|
appFrameValidator,
|
|
frameValidator,
|
|
difficultyAdjuster,
|
|
rewardIssuance,
|
|
eventDistributor,
|
|
globalTimeReel,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
&mockEncryptedChannel{},
|
|
&mocks.MockBulletproofProver{},
|
|
&mocks.MockVerifiableEncryptor{},
|
|
&mocks.MockDecafConstructor{},
|
|
&mocks.MockCompiler{},
|
|
nil,
|
|
p2p.NewInMemoryPeerInfoManager(logger),
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
return engine, pubsub, frameValidator, difficultyAdjuster, eventDistributor, globalTimeReel
|
|
}
|
|
|
|
// createTestProverRegistry creates a mock ProverRegistry that returns expected provers
|
|
func createTestProverRegistry() *mocks.MockProverRegistry {
|
|
proverRegistry := new(mocks.MockProverRegistry)
|
|
proverRegistry.On("GetNextProver", mock.Anything, mock.Anything).Return([]byte{0x01, 0x02, 0x03, 0x04}, nil)
|
|
proverRegistry.On("GetOrderedProvers", mock.Anything, mock.Anything).Return([][]byte{{0x01, 0x02, 0x03, 0x04}}, nil)
|
|
return proverRegistry
|
|
}
|
|
|
|
func createTestAppTimeReel(
|
|
t *testing.T,
|
|
appAddress []byte,
|
|
clockStore store.ClockStore,
|
|
) *consensustime.AppTimeReel {
|
|
logger := zap.NewNop()
|
|
proverRegistry := createTestProverRegistry()
|
|
appTimeReel, err := consensustime.NewAppTimeReel(logger, appAddress, proverRegistry, clockStore, true)
|
|
require.NoError(t, err)
|
|
return appTimeReel
|
|
}
|
|
|
|
func createTestAppConsensusEngine(
|
|
t *testing.T,
|
|
) (
|
|
*app.AppConsensusEngine,
|
|
*mockPubSub,
|
|
*mockFrameValidator,
|
|
*mocks.MockDifficultyAdjuster,
|
|
*mocks.MockEventDistributor,
|
|
*consensustime.AppTimeReel,
|
|
) {
|
|
logger := zap.NewNop()
|
|
config := &config.Config{
|
|
Engine: &config.EngineConfig{
|
|
Difficulty: 100,
|
|
},
|
|
P2P: &config.P2PConfig{
|
|
Network: 99,
|
|
},
|
|
DB: &config.DBConfig{
|
|
InMemoryDONOTUSE: true,
|
|
Path: ".test/test_store",
|
|
},
|
|
}
|
|
appAddress := []byte{0x01, 0x02, 0x03}
|
|
|
|
mockPubSub := newMockPubSub([]byte{0x01, 0x02, 0x03, 0x04})
|
|
mockHypergraph := new(mocks.MockHypergraph)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyStore := new(mocks.MockKeyStore)
|
|
mockFrameProver := new(mocks.MockFrameProver)
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockSignerRegistry := new(mocks.MockSignerRegistry)
|
|
mockFrameValidator := new(mockFrameValidator)
|
|
mockDifficultyAdjuster := new(mocks.MockDifficultyAdjuster)
|
|
mockRewardIssuance := new(mocks.MockRewardIssuance)
|
|
mockEventDistributor := new(mocks.MockEventDistributor)
|
|
pebbleDB := pstore.NewPebbleDB(logger, config.DB, 0)
|
|
clockStore := pstore.NewPebbleClockStore(pebbleDB, logger)
|
|
inboxStore := pstore.NewPebbleInboxStore(pebbleDB, logger)
|
|
shardStore := pstore.NewPebbleShardsStore(pebbleDB, logger)
|
|
consensusStore := pstore.NewPebbleConsensusStore(pebbleDB, logger)
|
|
hypergraphStore := pstore.NewPebbleHypergraphStore(config.DB, pebbleDB, logger, &mocks.MockVerifiableEncryptor{}, mockInclusionProver)
|
|
appTimeReel := createTestAppTimeReel(t, appAddress, clockStore)
|
|
mockProverRegistry := createTestProverRegistry()
|
|
mockDynamicFeeManager := new(mockDynamicFeeManager)
|
|
|
|
// Create global time reel
|
|
globalTimeReel, err := consensustime.NewGlobalTimeReel(logger, mockProverRegistry, clockStore, 99, true)
|
|
require.NoError(t, err)
|
|
|
|
mockKey := &tkeys.Key{
|
|
Id: "q-prover-key",
|
|
Type: crypto.KeyTypeBLS48581G1,
|
|
PublicKey: make([]byte, 585),
|
|
PrivateKey: make([]byte, 74),
|
|
}
|
|
|
|
mockFrameProver.On("ProveFrameHeaderGenesis", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&protobufs.FrameHeader{
|
|
FrameNumber: 0,
|
|
Address: []byte{
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
},
|
|
Output: make([]byte, 516),
|
|
Timestamp: 0,
|
|
Difficulty: 160000,
|
|
ParentSelector: make([]byte, 32),
|
|
RequestsRoot: make([]byte, 64),
|
|
StateRoots: make([][]byte, 4),
|
|
Prover: make([]byte, 32),
|
|
FeeMultiplierVote: 100,
|
|
}, nil)
|
|
mockEventDistributor.On("Start", mock.Anything).Return(nil)
|
|
mockEventDistributor.On("Subscribe", mock.Anything).Return(make(<-chan consensustypes.ControlEvent, 10))
|
|
mockEventDistributor.On("Unsubscribe", mock.Anything).Return()
|
|
mockEventDistributor.On("Stop").Return(nil)
|
|
mockSigner := new(mocks.MockBLSSigner)
|
|
mockSigner.On("Public").Return(make([]byte, 585))
|
|
mockSigner.On("Private").Return(make([]byte, 74))
|
|
mockKeyManager.On("GetSigningKey", "q-prover-key").Return(mockSigner, nil)
|
|
mockKeyManager.On("GetRawKey", "q-prover-key").Return(mockKey, nil)
|
|
mockDynamicFeeManager.On("AddFrameFeeVote", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
engine, err := app.NewAppConsensusEngine(
|
|
logger,
|
|
config,
|
|
1,
|
|
appAddress,
|
|
mockPubSub,
|
|
mockHypergraph,
|
|
mockKeyManager,
|
|
mockKeyStore,
|
|
clockStore,
|
|
inboxStore,
|
|
shardStore,
|
|
hypergraphStore,
|
|
consensusStore,
|
|
mockFrameProver,
|
|
mockInclusionProver,
|
|
&mocks.MockBulletproofProver{}, // bulletproofProver
|
|
&mocks.MockVerifiableEncryptor{}, // verEnc
|
|
&mocks.MockDecafConstructor{}, // decafConstructor
|
|
&mocks.MockCompiler{}, // compiler
|
|
mockSignerRegistry,
|
|
mockProverRegistry,
|
|
mockDynamicFeeManager,
|
|
mockFrameValidator,
|
|
&mockGlobalFrameValidator{},
|
|
mockDifficultyAdjuster,
|
|
mockRewardIssuance,
|
|
mockEventDistributor,
|
|
p2p.NewInMemoryPeerInfoManager(logger),
|
|
appTimeReel,
|
|
globalTimeReel,
|
|
&mocks.MockBlsConstructor{},
|
|
&mockEncryptedChannel{},
|
|
nil,
|
|
)
|
|
assert.NoError(t, err)
|
|
return engine,
|
|
mockPubSub,
|
|
mockFrameValidator,
|
|
mockDifficultyAdjuster,
|
|
mockEventDistributor,
|
|
appTimeReel
|
|
}
|
|
|
|
// mockPubSub extends the basic mock with app-specific features
|
|
type mockPubSub struct {
|
|
mock.Mock
|
|
mu sync.RWMutex
|
|
subscribers map[string][]func(message *pb.Message) error
|
|
validators map[string]func(peerID peer.ID, message *pb.Message) tp2p.ValidationResult
|
|
peerID []byte
|
|
peerCount int
|
|
networkPeers map[string]*mockPubSub
|
|
messageLog []messageRecord // Track all messages for debugging
|
|
globalFrames []*protobufs.GlobalFrame // Store frames for sync
|
|
appFrames []*protobufs.AppShardFrame // Store frames for sync
|
|
}
|
|
|
|
// GetOwnMultiaddrs implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetOwnMultiaddrs() []multiaddr.Multiaddr {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// AddPeerScore implements tp2p.PubSub.
|
|
func (m *mockPubSub) AddPeerScore(peerId []byte, scoreDelta int64) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Bootstrap implements tp2p.PubSub.
|
|
func (m *mockPubSub) Bootstrap(ctx context.Context) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// DiscoverPeers implements tp2p.PubSub.
|
|
func (m *mockPubSub) DiscoverPeers(ctx context.Context) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetDirectChannel implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetDirectChannel(ctx context.Context, peerId []byte, purpose string) (*grpc.ClientConn, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetMultiaddrOfPeer implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetMultiaddrOfPeer(peerId []byte) string {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetMultiaddrOfPeerStream implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetMultiaddrOfPeerStream(ctx context.Context, peerId []byte) <-chan multiaddr.Multiaddr {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetNetwork implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetNetwork() uint {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetNetworkPeersCount implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetNetworkPeersCount() int {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetPeerScore implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetPeerScore(peerId []byte) int64 {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetPublicKey implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetPublicKey() []byte {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// GetRandomPeer implements tp2p.PubSub.
|
|
func (m *mockPubSub) GetRandomPeer(bitmask []byte) ([]byte, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// IsPeerConnected implements tp2p.PubSub.
|
|
func (m *mockPubSub) IsPeerConnected(peerId []byte) bool {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Publish implements tp2p.PubSub.
|
|
func (m *mockPubSub) Publish(address []byte, data []byte) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Reachability implements tp2p.PubSub.
|
|
func (m *mockPubSub) Reachability() *wrapperspb.BoolValue {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Reconnect implements tp2p.PubSub.
|
|
func (m *mockPubSub) Reconnect(peerId []byte) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// SetPeerScore implements tp2p.PubSub.
|
|
func (m *mockPubSub) SetPeerScore(peerId []byte, score int64) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// SignMessage implements tp2p.PubSub.
|
|
func (m *mockPubSub) SignMessage(msg []byte) ([]byte, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// StartDirectChannelListener implements tp2p.PubSub.
|
|
func (m *mockPubSub) StartDirectChannelListener(key []byte, purpose string, server *grpc.Server) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
type messageRecord struct {
|
|
timestamp time.Time
|
|
from []byte
|
|
to []byte
|
|
data []byte
|
|
}
|
|
|
|
func newMockPubSub(peerID []byte) *mockPubSub {
|
|
return &mockPubSub{
|
|
subscribers: make(map[string][]func(message *pb.Message) error),
|
|
validators: make(map[string]func(peerID peer.ID, message *pb.Message) tp2p.ValidationResult),
|
|
peerID: peerID,
|
|
peerCount: 10,
|
|
networkPeers: make(map[string]*mockPubSub),
|
|
messageLog: make([]messageRecord, 0),
|
|
globalFrames: make([]*protobufs.GlobalFrame, 0),
|
|
appFrames: make([]*protobufs.AppShardFrame, 0),
|
|
}
|
|
}
|
|
|
|
func (m *mockPubSub) Subscribe(bitmask []byte, handler func(message *pb.Message) error) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
key := string(bitmask)
|
|
m.subscribers[key] = append(m.subscribers[key], handler)
|
|
return nil
|
|
}
|
|
|
|
func (m *mockPubSub) PublishToBitmask(bitmask []byte, data []byte) error {
|
|
m.mu.Lock()
|
|
m.messageLog = append(m.messageLog, messageRecord{
|
|
timestamp: time.Now(),
|
|
from: m.peerID,
|
|
to: bitmask,
|
|
data: data,
|
|
})
|
|
|
|
// If this is an app frame, store it for sync
|
|
if len(bitmask) >= 4 && bitmask[0] != 0x01 { // Not a message bitmask
|
|
frame := &protobufs.AppShardFrame{}
|
|
if err := proto.Unmarshal(data, frame); err == nil {
|
|
m.appFrames = append(m.appFrames, frame)
|
|
}
|
|
}
|
|
m.mu.Unlock()
|
|
|
|
message := &pb.Message{
|
|
Data: data,
|
|
From: m.peerID,
|
|
}
|
|
|
|
// Deliver to local subscribers
|
|
m.mu.RLock()
|
|
handlers := m.subscribers[string(bitmask)]
|
|
m.mu.RUnlock()
|
|
|
|
for _, handler := range handlers {
|
|
go handler(message)
|
|
}
|
|
|
|
// Deliver to network peers
|
|
m.mu.RLock()
|
|
peers := make([]*mockPubSub, 0, len(m.networkPeers))
|
|
for _, peer := range m.networkPeers {
|
|
if peer != m {
|
|
peers = append(peers, peer)
|
|
}
|
|
}
|
|
m.mu.RUnlock()
|
|
|
|
for _, peer := range peers {
|
|
// Deliver synchronously to ensure proper ordering
|
|
peer.receiveFromNetwork(bitmask, message)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (m *mockPubSub) receiveFromNetwork(bitmask []byte, message *pb.Message) {
|
|
m.mu.RLock()
|
|
validator := m.validators[string(bitmask)]
|
|
m.mu.RUnlock()
|
|
|
|
if validator != nil {
|
|
result := validator(peer.ID(message.From), message)
|
|
if result != tp2p.ValidationResultAccept {
|
|
// Log validation rejection for debugging
|
|
frame := &protobufs.AppShardFrame{}
|
|
if err := proto.Unmarshal(message.Data, frame); err == nil && frame.Header != nil {
|
|
fmt.Printf("DEBUG: Node %x rejected frame %d from %x (validation result: %v)\n",
|
|
m.peerID[:4], frame.Header.FrameNumber, message.From[:4], result)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
m.mu.RLock()
|
|
handlers := m.subscribers[string(bitmask)]
|
|
m.mu.RUnlock()
|
|
|
|
for _, handler := range handlers {
|
|
go handler(message) // Make async to match PublishToBitmask behavior
|
|
}
|
|
}
|
|
|
|
func (m *mockPubSub) RegisterValidator(bitmask []byte, validator func(peerID peer.ID, message *pb.Message) tp2p.ValidationResult, sync bool) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.validators[string(bitmask)] = validator
|
|
return nil
|
|
}
|
|
|
|
func (m *mockPubSub) GetPeerstoreCount() int {
|
|
return m.peerCount
|
|
}
|
|
|
|
func (m *mockPubSub) GetPeerID() []byte {
|
|
return m.peerID
|
|
}
|
|
|
|
func (m *mockPubSub) Unsubscribe(bitmask []byte, raw bool) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
delete(m.subscribers, string(bitmask))
|
|
}
|
|
|
|
func (m *mockPubSub) UnregisterValidator(bitmask []byte) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
delete(m.validators, string(bitmask))
|
|
return nil
|
|
}
|
|
|
|
func (m *mockPubSub) GetNetworkInfo() *protobufs.NetworkInfoResponse {
|
|
return &protobufs.NetworkInfoResponse{}
|
|
}
|
|
|
|
type mockTransaction struct{}
|
|
|
|
// Abort implements store.Transaction.
|
|
func (m *mockTransaction) Abort() error {
|
|
return nil
|
|
}
|
|
|
|
// Commit implements store.Transaction.
|
|
func (m *mockTransaction) Commit() error {
|
|
return nil
|
|
}
|
|
|
|
// Delete implements store.Transaction.
|
|
func (m *mockTransaction) Delete(key []byte) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// DeleteRange implements store.Transaction.
|
|
func (m *mockTransaction) DeleteRange(lowerBound []byte, upperBound []byte) error {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Get implements store.Transaction.
|
|
func (m *mockTransaction) Get(key []byte) ([]byte, io.Closer, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// NewIter implements store.Transaction.
|
|
func (m *mockTransaction) NewIter(lowerBound []byte, upperBound []byte) (store.Iterator, error) {
|
|
panic("unimplemented")
|
|
}
|
|
|
|
// Set implements store.Transaction.
|
|
func (m *mockTransaction) Set(key []byte, value []byte) error {
|
|
return nil
|
|
}
|
|
|
|
// setupTokenIntrinsicMocks sets up token intrinsic mocks for payment verification
|
|
func setupTokenIntrinsicMocks(
|
|
t *testing.T,
|
|
mockHG *mocks.MockHypergraph,
|
|
) {
|
|
// For payment transactions, we need to use the QUIL token address
|
|
tokenDomain := token.QUIL_TOKEN_ADDRESS
|
|
|
|
// Set up token intrinsic metadata mock for this specific domain
|
|
tokenMetadataAddress := slices.Concat(tokenDomain, bytes.Repeat([]byte{0xff}, 32))
|
|
tokenMetadataTree := &tries.VectorCommitmentTree{}
|
|
// Create a mock vertex for token metadata
|
|
tokenMockVertex := &mockVertex{}
|
|
tokenMockVertex.On("GetAddress").Return(tokenMetadataAddress)
|
|
tokenMockVertex.On("GetData").Return(tokenMetadataTree, nil)
|
|
// Set up the mock expectations
|
|
mockHG.On("GetVertex", [64]byte(tokenMetadataAddress)).Return(tokenMockVertex, nil).Maybe()
|
|
mockHG.On("GetVertexData", [64]byte(tokenMetadataAddress)).Return(tokenMetadataTree, nil).Maybe()
|
|
}
|
|
|
|
// setupComputeMocks sets up the common mocks for compute execution engine tests
|
|
func setupComputeMocks(
|
|
t *testing.T,
|
|
mockHG *mocks.MockHypergraph,
|
|
mockInclusionProver *mocks.MockInclusionProver,
|
|
mockBulletproofProver *mocks.MockBulletproofProver,
|
|
engineMode engines.ExecutionMode,
|
|
) {
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil)
|
|
mockHG.On("GetProver").Return(mockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{})
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil)
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Add mock for multiproof creation
|
|
mockMultiproof := new(mocks.MockMultiproof)
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil)
|
|
mockMultiproof.On("Size").Return(0).Maybe()
|
|
mockMultiproof.On("ComputeSize").Return(0).Maybe()
|
|
mockMultiproof.On("Bind", mock.Anything).Return(nil).Maybe()
|
|
mockMultiproof.On("Prove", mock.Anything).Return(nil).Maybe()
|
|
mockMultiproof.On("Reset").Return(nil).Maybe()
|
|
mockMultiproof.On("Commit", mock.Anything).Return([]byte{}, nil).Maybe()
|
|
mockMultiproof.On("Verify", mock.Anything, mock.Anything).Return(true, nil).Maybe()
|
|
mockMultiproof.On("ProveMultiple", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{}, nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof)
|
|
|
|
// Don't set up the GlobalMode metadata check here - let each test handle it
|
|
// based on whether it's before or after deployment
|
|
|
|
// Set up a generic mock for any GetVertex calls (for code deployment addresses)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, nil).Maybe()
|
|
|
|
// Set up a generic mock for code deployment GetVertexData that matches the pattern
|
|
// Create the tree once to reuse
|
|
code := []byte("package main\n\nfunc main(a, b int) int { return a + b }")
|
|
codeTree := &tries.VectorCommitmentTree{}
|
|
codeTree.Insert([]byte{0 << 2}, code, nil, big.NewInt(int64(len(code))))
|
|
|
|
// This will match any 64-byte address that has "app" in it after byte 32
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if the address contains "app" starting from byte 32
|
|
for i := 32; i < 64-2; i++ {
|
|
if addr[i] == 'a' && addr[i+1] == 'p' && addr[i+2] == 'p' {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})).Return(codeTree, nil).Maybe()
|
|
}
|
|
|
|
// setupCodeDeploymentData sets up mock data for deployed code
|
|
func setupCodeDeploymentData(
|
|
t *testing.T,
|
|
mockHG *mocks.MockHypergraph,
|
|
domain []byte,
|
|
codeAddress []byte,
|
|
) {
|
|
// Create a 64-byte address from the code address
|
|
var addr64 [64]byte
|
|
copy(addr64[:], domain)
|
|
copy(addr64[32:], codeAddress)
|
|
|
|
// Set up code deployment data
|
|
code := []byte("package main\n\nfunc main(a, b int) int { return a + b }")
|
|
err := tests.SetHypergraphCodeData(mockHG, addr64[:], code)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// setupGlobalModePreDeploymentMocks sets up the mock for GlobalMode before deployment
|
|
func setupGlobalModePreDeploymentMocks(
|
|
mockHG *mocks.MockHypergraph,
|
|
domain []byte,
|
|
) {
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
// Before deployment, the metadata vertex doesn't exist
|
|
mockHG.On("GetVertex", [64]byte(metadataAddress)).Return(nil, store.ErrNotFound)
|
|
}
|
|
|
|
// setupComputeMetadata sets up the compute metadata after deployment
|
|
func setupComputeMetadata(
|
|
t *testing.T,
|
|
mockHG *mocks.MockHypergraph,
|
|
domain []byte,
|
|
readKey []byte,
|
|
writeKey []byte,
|
|
) {
|
|
// Use provided keys for metadata
|
|
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
`
|
|
|
|
// After deployment is done, set up the metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
err := tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
}
|
|
|
|
// Calling this test "just" a test for compute execution engine is a bit of a
|
|
// misnomer, because it involves a lot of moving pieces that need to occur in a
|
|
// specific order: The global execution engine needs to handle the deployment,
|
|
// allocate provers, verify the payment for the fee (for deployment and
|
|
// execution), and then the app execution engine has to consume the deployment,
|
|
// settle the metadata for it, process the rdf bundle (for hypergraph
|
|
// settlement), verify the payment for the execution fee, and handle the close
|
|
// out of the execution for fee dispatch. It is by far the most integration-y
|
|
// unit test we have. Like FAA guidelines, this test is written in the blood of
|
|
// past failures. Heed the failures like hard rules, and if you're questioning
|
|
// whether the test is wrong, you probably haven't bled enough.
|
|
func TestComputeExecutionEngine_ProcessMessage_DeployWithPaymentAndExecute(
|
|
t *testing.T,
|
|
) {
|
|
// Helper function to perform deployment steps with specific keys
|
|
performDeploymentWithKeys := func(t *testing.T, engine *engines.ComputeExecutionEngine, mockHG *mocks.MockHypergraph, mockCompiler *mocks.MockCompiler, readKey, writeKey []byte, engineMode engines.ExecutionMode) []byte {
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
|
|
// Use provided keys
|
|
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)
|
|
}
|
|
|
|
// Deploy RDF schema with keys
|
|
args := compute.ComputeDeploy{
|
|
Config: &compute.ComputeIntrinsicConfiguration{
|
|
ReadPublicKey: readKey,
|
|
WritePublicKey: writeKey,
|
|
},
|
|
RDFSchema: []byte(`BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
`),
|
|
}
|
|
deploy := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_ComputeDeploy{
|
|
ComputeDeploy: args.ToProtobuf(),
|
|
},
|
|
}
|
|
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
deploy,
|
|
},
|
|
}
|
|
bundleBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
deployHash := sha3.Sum256(bundleBytes)
|
|
msg := &protobufs.Message{
|
|
Address: compute.COMPUTE_INTRINSIC_DOMAIN[:],
|
|
Hash: deployHash[:],
|
|
Payload: bundleBytes,
|
|
}
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
result, err := engine.ProcessMessage(0, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
require.NoError(t, err)
|
|
|
|
ch := state.Changeset()
|
|
domain := ch[0].Domain
|
|
require.Len(t, result.Messages, 0)
|
|
|
|
mockCircuit := &mocks.MockCompiledCircuit{}
|
|
mockCircuit.On("Marshal", mock.Anything).Return(nil)
|
|
mockCompiler.On("Compile", mock.Anything, mock.Anything).Return(mockCircuit, nil)
|
|
mockCompiler.On("ValidateCircuit", mock.Anything).Return(nil)
|
|
|
|
// In GlobalMode, code deployment is not allowed after RDF schema deployment
|
|
if engineMode != engines.GlobalMode {
|
|
// Deploy code
|
|
sourceCode := []byte("package main\n\nfunc main(a, b int) int { return a + b }")
|
|
deployment, err := compute.NewCodeDeployment(
|
|
[32]byte(domain),
|
|
sourceCode,
|
|
[2]string{"qcl:Int", "qcl:Int"},
|
|
[2][]int{{4}, {4}},
|
|
[]string{"qcl:Int"},
|
|
mockCompiler,
|
|
)
|
|
require.NoError(t, err)
|
|
err = deployment.Prove(1)
|
|
require.NoError(t, err)
|
|
// Wrap in MessageRequest
|
|
deploy := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_CodeDeploy{
|
|
CodeDeploy: deployment.ToProtobuf(),
|
|
},
|
|
}
|
|
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)
|
|
}
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
deploy,
|
|
},
|
|
}
|
|
bundleBytes, err = bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
deployHash := sha3.Sum256(bundleBytes)
|
|
msg = &protobufs.Message{
|
|
Address: domain,
|
|
Hash: deployHash[:],
|
|
Payload: bundleBytes,
|
|
}
|
|
result, err = engine.ProcessMessage(0, big.NewInt(0), msg.Address, msg.Payload, result.State)
|
|
require.NoError(t, err)
|
|
err = result.State.Commit()
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
return domain
|
|
}
|
|
|
|
performBundledDeploymentAndExecution := func(
|
|
t *testing.T,
|
|
engine *engines.ComputeExecutionEngine,
|
|
mockHG *mocks.MockHypergraph,
|
|
domain []byte,
|
|
mockCompiler *mocks.MockCompiler,
|
|
readKey, writeKey []byte,
|
|
operations []*compute.ExecuteOperation,
|
|
) ([]*protobufs.Message, error) {
|
|
// Set up mock circuit for code deployment
|
|
mockCircuit := &mocks.MockCompiledCircuit{}
|
|
mockCircuit.On("Marshal", mock.Anything).Return(nil)
|
|
mockCompiler.On("Compile", mock.Anything, mock.Anything).Return(mockCircuit, nil)
|
|
mockCompiler.On("ValidateCircuit", mock.Anything).Return(nil)
|
|
|
|
// Create source code for deployment
|
|
sourceCode := []byte("package main\n\nfunc main(a, b int) int { return a + b }")
|
|
|
|
// Create bundled message with all operations
|
|
bundledMsg := createCompleteBundleWithPayment(
|
|
t,
|
|
mockHG,
|
|
domain,
|
|
readKey,
|
|
writeKey,
|
|
sourceCode,
|
|
mockCompiler,
|
|
operations,
|
|
)
|
|
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
|
|
// Process the bundled message
|
|
result, err := engine.ProcessMessage(0, big.NewInt(0), bundledMsg.Address, bundledMsg.Payload, state)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = state.Commit(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return result.Messages, nil
|
|
}
|
|
|
|
performDeployment := func(t *testing.T, engine *engines.ComputeExecutionEngine, mockHG *mocks.MockHypergraph, mockCompiler *mocks.MockCompiler, engineMode engines.ExecutionMode) []byte {
|
|
// Generate random keys
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
return domain
|
|
}
|
|
|
|
createTestCodeExecuteMessage := func(t *testing.T, mockHG *mocks.MockHypergraph, domain []byte, operations []*compute.ExecuteOperation) (*protobufs.Message, *compute.CodeExecute) {
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
// Create valid payment transaction using protobuf
|
|
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)
|
|
}
|
|
paymentTxBytes, err := payment.GetPendingTransaction().ToCanonicalBytes()
|
|
|
|
secretKey := make([]byte, 32)
|
|
rand.Read(secretKey)
|
|
|
|
ce := compute.NewCodeExecute(
|
|
[32]byte(domain),
|
|
paymentTxBytes,
|
|
secretKey,
|
|
rendezvous,
|
|
operations,
|
|
nil, nil, nil, nil, nil, nil, // Mock dependencies will be set by the engine
|
|
)
|
|
|
|
// Set up proof of payment
|
|
ce.ProofOfPayment[0] = paymentTxBytes
|
|
ce.ProofOfPayment[1] = make([]byte, 112) // Ed448 signature size
|
|
rand.Read(ce.ProofOfPayment[1])
|
|
|
|
// Wrap in MessageBundle and serialize
|
|
executeReq := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_CodeExecute{
|
|
CodeExecute: ce.ToProtobuf(),
|
|
},
|
|
}
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
executeReq,
|
|
},
|
|
}
|
|
ceBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create message
|
|
hash := sha3.Sum256(ceBytes)
|
|
require.NoError(t, err)
|
|
|
|
msg := &protobufs.Message{
|
|
Address: domain,
|
|
Hash: hash[:],
|
|
Payload: ceBytes,
|
|
}
|
|
|
|
return msg, ce
|
|
}
|
|
|
|
// Helper function to create a test CodeFinalize message
|
|
createTestCodeFinalizeMessage := func(t *testing.T, domain []byte, writePrivKey []byte, config *compute.ComputeIntrinsicConfiguration, rendezvous [32]byte, results []*compute.ExecutionResult, mockHG *mocks.MockHypergraph, keyManager *mocks.MockKeyManager) (*protobufs.Message, *compute.CodeFinalize) {
|
|
cf := compute.NewCodeFinalize(
|
|
rendezvous,
|
|
[32]byte(domain),
|
|
results,
|
|
[]*compute.StateTransition{},
|
|
[]byte("message_output"),
|
|
writePrivKey,
|
|
config,
|
|
nil, nil, nil, nil, nil, keyManager, // Mock dependencies
|
|
)
|
|
|
|
err := cf.Prove(1)
|
|
require.NoError(t, err)
|
|
|
|
// Create valid payment transaction using protobuf
|
|
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)
|
|
}
|
|
|
|
// Wrap in MessageRequest and serialize
|
|
finalizeReq := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_CodeFinalize{
|
|
CodeFinalize: cf.ToProtobuf(),
|
|
},
|
|
}
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
finalizeReq,
|
|
},
|
|
}
|
|
cfBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create message
|
|
hash := sha3.Sum256(cfBytes)
|
|
require.NoError(t, err)
|
|
|
|
msg := &protobufs.Message{
|
|
Address: domain,
|
|
Hash: hash[:],
|
|
Payload: cfBytes,
|
|
}
|
|
|
|
return msg, cf
|
|
}
|
|
|
|
// Test execution modes
|
|
type TestExecutionMode int
|
|
const (
|
|
TestGlobalMode TestExecutionMode = iota
|
|
TestApplicationMode
|
|
TestMixedMode
|
|
)
|
|
|
|
// Helper function to run test with all three execution modes
|
|
runTestWithAllModes := func(t *testing.T, testName string, testFunc func(t *testing.T, mode TestExecutionMode)) {
|
|
modes := []struct {
|
|
name string
|
|
mode TestExecutionMode
|
|
}{
|
|
{"global_must_sequence", TestGlobalMode},
|
|
{"app_fully_sequences", TestApplicationMode},
|
|
{"global_sequences_payment_and_partial_execute", TestMixedMode},
|
|
}
|
|
|
|
for _, mode := range modes {
|
|
t.Run(mode.name, func(t *testing.T) {
|
|
testFunc(t, mode.mode)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Helper function to assert code execution results based on mode
|
|
assertCodeExecutionResult := func(t *testing.T, mode TestExecutionMode, msgs *execution.ProcessMessageResult, err error, shouldError bool) {
|
|
// In GlobalMode, after deployment, only deploy messages are allowed
|
|
if mode == TestGlobalMode {
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
assert.Nil(t, msgs)
|
|
} else {
|
|
if shouldError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, msgs)
|
|
if msgs != nil && msgs.State != nil {
|
|
err = msgs.State.Commit()
|
|
assert.NoError(t, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper function to assert bundled execution results
|
|
assertBundledExecutionResult := func(t *testing.T, msgs []*protobufs.Message, err error) {
|
|
// Bundled operations should work in all modes, including GlobalMode
|
|
if err != nil {
|
|
// If there's an error, it should not be the global mode restriction
|
|
assert.NotContains(t, err.Error(), "non-deploy messages not allowed in global mode",
|
|
"Bundled operations should bypass global mode restriction")
|
|
}
|
|
}
|
|
|
|
// Test 1: Valid single operation no dependencies
|
|
t.Run("valid_single_operation_no_dependencies", func(t *testing.T) {
|
|
runTestWithAllModes(t, "valid_single_operation_no_dependencies", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create single operation with no dependencies
|
|
op := &compute.ExecuteOperation{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("test_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, []*compute.ExecuteOperation{op})
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
if engineMode == engines.GlobalMode {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
}
|
|
})
|
|
})
|
|
|
|
// Test 1b: Valid single operation using bundled approach (works in GlobalMode)
|
|
t.Run("valid_single_operation_bundled", func(t *testing.T) {
|
|
runTestWithAllModes(t, "valid_single_operation_bundled", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Create single operation
|
|
op := &compute.ExecuteOperation{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("test_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
}
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Use bundled approach
|
|
msgs, err := performBundledDeploymentAndExecution(
|
|
t, engine, mockHG, domain, mockCompiler,
|
|
readKey, writeKey,
|
|
[]*compute.ExecuteOperation{op},
|
|
)
|
|
|
|
// Bundled operations should work in all modes
|
|
assertBundledExecutionResult(t, msgs, err)
|
|
|
|
// In GlobalMode, this should succeed with bundling
|
|
if engineMode == engines.GlobalMode && err == nil {
|
|
assert.NotNil(t, msgs, "Bundled operations should produce responses in GlobalMode")
|
|
}
|
|
})
|
|
})
|
|
|
|
// Test 2: Valid multiple operations sequential dependencies
|
|
t.Run("valid_multiple_operations_sequential_dependencies", func(t *testing.T) {
|
|
runTestWithAllModes(t, "valid_multiple_operations_sequential_dependencies", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Create operations with sequential dependencies: op1 -> op2 -> op3
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("test_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op2")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("test_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("test_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 3: Valid multiple operations parallel execution
|
|
t.Run("valid_multiple_operations_parallel_execution", func(t *testing.T) {
|
|
runTestWithAllModes(t, "valid_multiple_operations_parallel_execution", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations with no dependencies (can run in parallel)
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app1"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app2"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app3"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 4: Valid diamond dependency pattern
|
|
t.Run("valid_diamond_dependency_pattern", func(t *testing.T) {
|
|
runTestWithAllModes(t, "valid_diamond_dependency_pattern", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create diamond pattern: op1 -> op2, op3 -> op4
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op4"),
|
|
Dependencies: [][]byte{[]byte("op2"), []byte("op3")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 5: Invalid circular dependency detection
|
|
t.Run("invalid_circular_dependency_detection", func(t *testing.T) {
|
|
runTestWithAllModes(t, "invalid_circular_dependency_detection", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Ensure compute metadata is available
|
|
setupComputeMetadata(t, mockHG, domain, []byte{}, []byte{})
|
|
|
|
// Set up mocks
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Create circular dependency: op1 -> op2 -> op3 -> op1
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{[]byte("op3")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op2")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 6: Invalid missing dependency reference
|
|
t.Run("invalid_missing_dependency_reference", func(t *testing.T) {
|
|
runTestWithAllModes(t, "invalid_missing_dependency_reference", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Create operations with missing dependency reference
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{[]byte("missing_op")}, // Reference to non-existent operation
|
|
},
|
|
}
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Ensure compute metadata is available
|
|
setupComputeMetadata(t, mockHG, domain, []byte{}, []byte{})
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail because compute intrinsic hasn't been deployed
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 7: Invalid payment proof rejected
|
|
t.Run("invalid_payment_proof_rejected", func(t *testing.T) {
|
|
runTestWithAllModes(t, "invalid_payment_proof_rejected", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks - payment validation fails
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(false)
|
|
|
|
// Additional mocks for payment transaction verification
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof, nil).Maybe()
|
|
mockMultiproof.On("Size").Return(0).Maybe()
|
|
mockMultiproof.On("ComputeSize").Return(0).Maybe()
|
|
mockMultiproof.On("Bind", mock.Anything).Return(nil).Maybe()
|
|
mockMultiproof.On("Prove", mock.Anything).Return(nil).Maybe()
|
|
mockMultiproof.On("Reset").Return(nil).Maybe()
|
|
mockMultiproof.On("Commit", mock.Anything).Return([]byte{}, nil).Maybe()
|
|
mockMultiproof.On("Verify", mock.Anything, mock.Anything).Return(true, nil).Maybe()
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockMultiproof.On("ProveMultiple", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{}, nil).Maybe()
|
|
|
|
// Setup GetProver mock for deployment
|
|
mockHG.On("GetProver").Return(mockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
|
|
// Setup transaction mocks for deployment
|
|
mockTxn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(mockTxn, nil)
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil)
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
|
|
// Generate keys and set up metadata before deployment
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Add catch-all for any other GetVertex calls during deployment
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, fmt.Errorf("not found")).Maybe()
|
|
|
|
// Perform deployment
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
|
|
// In global mode, the error is different because non-deploy messages are not allowed
|
|
if engineMode == engines.GlobalMode {
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
} else {
|
|
// Payment validation fails with "invalid signature" error
|
|
assert.Contains(t, err.Error(), "invalid signature")
|
|
}
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 8: Invalid payment insufficient amount
|
|
t.Run("invalid_payment_insufficient_amount", func(t *testing.T) {
|
|
runTestWithAllModes(t, "invalid_payment_insufficient_amount", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create mock agreements - the New() method creates with random keys
|
|
mockDecafConstructor := &mocks.MockDecafConstructor{}
|
|
vk, _ := mockDecafConstructor.New()
|
|
sk, _ := mockDecafConstructor.New()
|
|
|
|
out1, err := token.NewPendingTransactionOutput(big.NewInt(9), vk.Public(), sk.Public(), vk.Public(), sk.Public(), 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mockKeyManager.On("GetAgreementKey", "q-view-key").Return(&mocks.MockDecafAgreement{}, nil)
|
|
mockKeyManager.On("GetAgreementKey", "q-spend-key").Return(&mocks.MockDecafAgreement{}, nil)
|
|
|
|
address1 := [64]byte{}
|
|
copy(address1[:32], token.QUIL_TOKEN_ADDRESS)
|
|
rand.Read(address1[32:])
|
|
address2 := [64]byte{}
|
|
copy(address2[:32], token.QUIL_TOKEN_ADDRESS)
|
|
rand.Read(address2[32:])
|
|
|
|
tree1 := &tries.VectorCommitmentTree{}
|
|
otk1 := make([]byte, 56)
|
|
comm1 := make([]byte, 56)
|
|
mask1 := make([]byte, 56)
|
|
verifkey1 := make([]byte, 56)
|
|
rand.Read(otk1)
|
|
rand.Read(comm1)
|
|
rand.Read(mask1)
|
|
rand.Read(verifkey1)
|
|
|
|
maskedCoinBalanceBytes1 := make([]byte, 56)
|
|
rand.Read(maskedCoinBalanceBytes1)
|
|
|
|
tree1.Insert([]byte{0}, binary.BigEndian.AppendUint64(nil, token.FRAME_2_1_EXTENDED_ENROLL_CONFIRM_END+1), nil, big.NewInt(8))
|
|
tree1.Insert([]byte{1 << 2}, comm1, nil, big.NewInt(56))
|
|
tree1.Insert([]byte{2 << 2}, otk1, nil, big.NewInt(56))
|
|
tree1.Insert([]byte{3 << 2}, verifkey1, nil, big.NewInt(56))
|
|
tree1.Insert([]byte{4 << 2}, maskedCoinBalanceBytes1, nil, big.NewInt(56))
|
|
tree1.Insert([]byte{5 << 2}, mask1, nil, big.NewInt(56))
|
|
|
|
typeAddr, _ := hex.DecodeString("096de9a09f693f92cfa9cf3349bab2b3baee09f3e4f9c596514ecb3e8b0dff8f")
|
|
tree1.Insert(bytes.Repeat([]byte{0xff}, 32), typeAddr, nil, big.NewInt(32))
|
|
|
|
mockHG.On("GetVertex", [64]byte(slices.Concat(token.QUIL_TOKEN_ADDRESS, address1[32:]))).Return(&mockVertex{}, nil)
|
|
mockHG.On("GetVertexData", [64]byte(slices.Concat(token.QUIL_TOKEN_ADDRESS, address1[32:]))).Return(tree1, nil)
|
|
|
|
// simulate input as commitment to total
|
|
input1, _ := token.NewPendingTransactionInput(address1[:])
|
|
tokenconfig := &token.TokenIntrinsicConfiguration{
|
|
Behavior: token.Mintable | token.Burnable | token.Divisible | token.Acceptable | token.Expirable | token.Tenderable,
|
|
MintStrategy: &token.TokenMintStrategy{
|
|
MintBehavior: token.MintWithProof,
|
|
ProofBasis: token.ProofOfMeaningfulWork,
|
|
},
|
|
Units: big.NewInt(8000000000),
|
|
Name: "QUIL",
|
|
Symbol: "QUIL",
|
|
}
|
|
rdfSchema, _ := prepareRDFSchemaFromConfig(token.QUIL_TOKEN_ADDRESS, tokenconfig)
|
|
parser := &schema.TurtleRDFParser{}
|
|
rdfMultiprover := schema.NewRDFMultiprover(parser, mockInclusionProver)
|
|
mockPaymentTx := token.NewPendingTransaction(
|
|
[32]byte(token.QUIL_TOKEN_ADDRESS),
|
|
[]*token.PendingTransactionInput{input1},
|
|
[]*token.PendingTransactionOutput{out1},
|
|
[]*big.Int{},
|
|
tokenconfig,
|
|
mockHG,
|
|
mockBulletproofProver,
|
|
mockInclusionProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
keys.ToKeyRing(mockKeyManager, false),
|
|
rdfSchema,
|
|
rdfMultiprover,
|
|
)
|
|
|
|
// Additional mocks for transaction creation
|
|
// RangeProof needs to return a properly sized byte array
|
|
rangeProof := make([]byte, 112) // Typical range proof size
|
|
mockBulletproofProver.On("RangeProof", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(rangeProof, nil).Maybe()
|
|
|
|
// GenerateRangeProofFromBig needs to return a RangeProofResult struct
|
|
rangeProofResult := crypto.RangeProofResult{
|
|
Proof: rangeProof,
|
|
Commitment: make([]byte, 56),
|
|
Blinding: make([]byte, 56),
|
|
}
|
|
mockBulletproofProver.On("GenerateRangeProofFromBig", mock.Anything, mock.Anything, mock.Anything).Return(rangeProofResult, nil).Maybe()
|
|
mockBulletproofProver.On("SignHidden", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]byte{}).Maybe()
|
|
|
|
// Add ProveMultiple mock directly to the inclusion prover for this test
|
|
mockMultiproof := new(mocks.MockMultiproof)
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil)
|
|
mockMultiproof.On("ToBytes").Return([]byte{}, nil)
|
|
mockInclusionProver.On("ProveMultiple", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(mockMultiproof).Maybe()
|
|
|
|
// Add CreateTraversalProof mock for the hypergraph
|
|
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()
|
|
|
|
// Call Prove to populate the transaction fields
|
|
err = mockPaymentTx.Prove(0)
|
|
require.NoError(t, err)
|
|
|
|
paymentTxBytes, err := mockPaymentTx.ToProtobuf().ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create operations
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
// Create CodeExecute with insufficient payment
|
|
ce := compute.NewCodeExecute(
|
|
[32]byte(domain),
|
|
paymentTxBytes,
|
|
[]byte("mock_secret_key"),
|
|
rendezvous,
|
|
ops,
|
|
mockHG, mockBulletproofProver, mockInclusionProver,
|
|
mockVerEnc, mockDecaf, mockKeyManager,
|
|
)
|
|
|
|
// Set up proof of payment
|
|
ce.ProofOfPayment[0] = paymentTxBytes
|
|
ce.ProofOfPayment[1] = []byte("mock_signature")
|
|
|
|
// Wrap in MessageRequest and serialize
|
|
executeReq := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_CodeExecute{
|
|
CodeExecute: ce.ToProtobuf(),
|
|
},
|
|
}
|
|
outFees := []*big.Int{big.NewInt(10000000)}
|
|
total := big.NewInt(1)
|
|
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)
|
|
}
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
executeReq,
|
|
},
|
|
}
|
|
bundleBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create message
|
|
hash := sha3.Sum256(bundleBytes)
|
|
require.NoError(t, err)
|
|
|
|
msg := &protobufs.Message{
|
|
Address: domain,
|
|
Hash: hash[:],
|
|
Payload: bundleBytes,
|
|
}
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(1000), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
|
|
// Don't let the error check panic
|
|
if err != nil {
|
|
// In GlobalMode, it fails with global mode restriction
|
|
// In other modes, it should fail with insufficient payment
|
|
if engineMode == engines.GlobalMode {
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
} else {
|
|
assert.True(
|
|
t,
|
|
strings.Contains(err.Error(), "insufficient"),
|
|
"Expected error about insufficient payment, got: %v",
|
|
err,
|
|
)
|
|
}
|
|
}
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 9 removed, retaining number for future use
|
|
|
|
// Test 10: Invalid signature on rendezvous
|
|
t.Run("invalid_signature_on_rendezvous", func(t *testing.T) {
|
|
runTestWithAllModes(t, "invalid_signature_on_rendezvous", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks - SimpleVerify returns false for invalid signature
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(false)
|
|
|
|
// Set up compute mocks to handle the message processing
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
metadataTree := &tries.VectorCommitmentTree{}
|
|
// Set up the mock expectations for metadata
|
|
mockHG.On("GetVertex", [64]byte(metadataAddress)).Return(&mockVertex{}, nil)
|
|
mockHG.On("GetVertexData", [64]byte(metadataAddress)).Return(metadataTree, nil)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, ce := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Set invalid signature
|
|
ce.ProofOfPayment[1] = []byte("invalid_signature")
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 11: Intrinsic token operation execution
|
|
t.Run("intrinsic_token_operation_execution", func(t *testing.T) {
|
|
runTestWithAllModes(t, "intrinsic_token_operation_execution", func(t *testing.T, mode TestExecutionMode) {
|
|
t.Skip("When multiphasic locking is added, support this")
|
|
})
|
|
})
|
|
|
|
// Test 12: Intrinsic hardcoded app execution
|
|
t.Run("intrinsic_hardcoded_app_execution", func(t *testing.T) {
|
|
runTestWithAllModes(t, "intrinsic_hardcoded_app_execution", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: []byte{0x00, 0x01, 0x07, 0x01},
|
|
ExecutionContext: compute.ExecutionContextIntrinsic,
|
|
},
|
|
Identifier: []byte("intrinsic_op"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 13: Hypergraph context state mutation
|
|
t.Run("hypergraph_context_state_mutation", func(t *testing.T) {
|
|
runTestWithAllModes(t, "hypergraph_context_state_mutation", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: hgstate.VertexAddsDiscriminator,
|
|
ExecutionContext: compute.ExecutionContextHypergraph,
|
|
},
|
|
Identifier: []byte("add_vertex"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 14: Extrinsic deployed code execution
|
|
t.Run("extrinsic_deployed_code_execution", func(t *testing.T) {
|
|
runTestWithAllModes(t, "extrinsic_deployed_code_execution", func(t *testing.T, mode TestExecutionMode) {
|
|
// Create test dependencies
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Perform deployment
|
|
domain := performDeployment(t, engine, mockHG, mockCompiler, engineMode)
|
|
|
|
// Set up mock for deployed code retrieval
|
|
// The test is using "deployed_code_addr" as the code address
|
|
code := &tries.VectorCommitmentTree{}
|
|
code.Insert([]byte{0}, []byte("circuit"), nil, big.NewInt(7))
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if the address ends with "deployed_code_addr"
|
|
codeAddrPart := string(bytes.TrimRight(addr[32:], "\x00"))
|
|
return codeAddrPart == "deployed_code_addr"
|
|
})).Return(code, nil)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("deployed_code_addr"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("execute_circuit"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 15: Conflict detection same address write
|
|
t.Run("conflict_detection_same_address_write", func(t *testing.T) {
|
|
runTestWithAllModes(t, "conflict_detection_same_address_write", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations that write to the same address (should be placed in different stages)
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("same_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("same_app"), // Same address as op1
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 16: Conflict detection read write dependency
|
|
t.Run("conflict_detection_read_write_dependency", func(t *testing.T) {
|
|
runTestWithAllModes(t, "conflict_detection_read_write_dependency", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations with read-write dependency
|
|
// op1 reads from address A, op2 writes to address A
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("reader_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1_read"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("writer_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2_write"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 17: Conflict detection write write collision
|
|
t.Run("conflict_detection_write_write_collision", func(t *testing.T) {
|
|
runTestWithAllModes(t, "conflict_detection_write_write_collision", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Set up mocks for deployed code retrieval
|
|
// The test is using "state_writer1" and "state_writer2" as code addresses
|
|
code := &tries.VectorCommitmentTree{}
|
|
code.Insert([]byte{0}, []byte("circuit"), nil, big.NewInt(7))
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if the address ends with "state_writer1" or "state_writer2"
|
|
codeAddrPart := string(bytes.TrimRight(addr[32:], "\x00"))
|
|
return codeAddrPart == "state_writer1" || codeAddrPart == "state_writer2"
|
|
})).Return(code, nil)
|
|
|
|
// Create operations that both write to same state
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("state_writer1"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("writer1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("state_writer2"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("writer2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 18: Conflict free parallel stage grouping
|
|
t.Run("conflict_free_parallel_stage_grouping", func(t *testing.T) {
|
|
runTestWithAllModes(t, "conflict_free_parallel_stage_grouping", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations that can run in parallel (no conflicts)
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app1"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app2"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app3"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// All operations should be in the same stage since there are no conflicts
|
|
|
|
})
|
|
})
|
|
|
|
// Test 19: topological sort ordering
|
|
t.Run("topological_sort_ordering", func(t *testing.T) {
|
|
runTestWithAllModes(t, "topological_sort_ordering", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create complex DAG: op1 -> op2 -> op4, op1 -> op3 -> op4
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op4"),
|
|
Dependencies: [][]byte{[]byte("op2"), []byte("op3")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// Should produce stages: [op1], [op2, op3], [op4]
|
|
|
|
})
|
|
})
|
|
|
|
// Test 20: Execution stage assignment correctness
|
|
t.Run("execution_stage_assignment_correctness", func(t *testing.T) {
|
|
runTestWithAllModes(t, "execution_stage_assignment_correctness", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations with mixed dependencies and conflicts
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_a"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_b"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_a"), // Conflict with op1
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_c"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op4"),
|
|
Dependencies: [][]byte{[]byte("op1"), []byte("op2")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// Expected stages considering conflicts and dependencies:
|
|
// Stage 0: op1, op2 (no dependencies, no conflicts between them)
|
|
// Stage 1: op3 (conflicts with op1)
|
|
// Stage 2: op4 (depends on op1 and op2)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 21: Materialize stores dependency structure
|
|
t.Run("materialize_stores_dependency_structure", func(t *testing.T) {
|
|
runTestWithAllModes(t, "materialize_stores_dependency_structure", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations with dependencies
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
}
|
|
|
|
msg, ce := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// Verify DAG structure is preserved in the materialized CodeExecute
|
|
assert.Equal(t, 2, len(ce.ExecuteOperations))
|
|
assert.Equal(t, []byte("op1"), ce.ExecuteOperations[0].Identifier)
|
|
assert.Equal(t, []byte("op2"), ce.ExecuteOperations[1].Identifier)
|
|
assert.Equal(t, [][]byte{[]byte("op1")}, ce.ExecuteOperations[1].Dependencies)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 22: Materialize stores execution stages
|
|
t.Run("materialize_stores_execution_stages", func(t *testing.T) {
|
|
runTestWithAllModes(t, "materialize_stores_execution_stages", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations that will be in different stages
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app1"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app2"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app3"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op1"), []byte("op2")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// The execution stages should be computed and stored
|
|
|
|
})
|
|
})
|
|
|
|
// Test 23: Materialize stores rendezvous
|
|
t.Run("materialize_stores_rendezvous", func(t *testing.T) {
|
|
runTestWithAllModes(t, "materialize_stores_rendezvous", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, ce := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// Verify rendezvous is stored correctly
|
|
assert.NotNil(t, ce.Rendezvous)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 24: Finalize collects all operation results
|
|
t.Run("finalize_collects_all_operation_results", func(t *testing.T) {
|
|
runTestWithAllModes(t, "finalize_collects_all_operation_results", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
|
|
// Set up compute metadata with proper keys
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Create mock execution results
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: true,
|
|
Output: []byte("result1"),
|
|
},
|
|
{
|
|
OperationID: []byte("op2"),
|
|
Success: true,
|
|
Output: []byte("result2"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
msg, _ := createTestCodeFinalizeMessage(t, domain, []byte(writePriv), &compute.ComputeIntrinsicConfiguration{ReadPublicKey: readKey, WritePublicKey: writeKey}, rendezvous, results, mockHG, mockKeyManager)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 25: Finalize commits durable state changes
|
|
t.Run("finalize_commits_durable_state_changes", func(t *testing.T) {
|
|
runTestWithAllModes(t, "finalize_commits_durable_state_changes", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up only the mocks needed for finalize operation
|
|
// Set up metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Mock the deployed code retrieval for finalize
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if it's trying to get deployed code
|
|
return bytes.HasPrefix(addr[:32], domain) &&
|
|
bytes.Equal(addr[32:48], bytes.Repeat([]byte{0x00}, 16))
|
|
})).Return([]byte("deployed_code"), nil)
|
|
|
|
// Create state transitions
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: true,
|
|
Output: []byte("result1"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
msg, _ := createTestCodeFinalizeMessage(t, domain, []byte(writePriv), &compute.ComputeIntrinsicConfiguration{ReadPublicKey: readKey, WritePublicKey: writeKey}, rendezvous, results, mockHG, mockKeyManager)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// For finalize operations in global mode, we expect no state changes
|
|
// since it's just acknowledging the finalization
|
|
// In app mode, state changes should be persisted
|
|
if mode != TestGlobalMode {
|
|
// In app mode, verify that state changes were persisted
|
|
// Since we can't easily verify the exact calls, we trust the test passes
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
// Test 26: Finalize rollback on operation failure
|
|
t.Run("finalize_rollback_on_operation_failure", func(t *testing.T) {
|
|
runTestWithAllModes(t, "finalize_rollback_on_operation_failure", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Mock the deployed code retrieval for finalize
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if it's trying to get deployed code
|
|
return bytes.HasPrefix(addr[:32], domain) &&
|
|
bytes.Equal(addr[32:48], bytes.Repeat([]byte{0x00}, 16))
|
|
})).Return([]byte("deployed_code"), nil)
|
|
|
|
// Create results with failure
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: false,
|
|
Error: []byte("execution failed"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
msg, _ := createTestCodeFinalizeMessage(t, domain, []byte(writePriv), &compute.ComputeIntrinsicConfiguration{ReadPublicKey: readKey, WritePublicKey: writeKey}, rendezvous, results, mockHG, mockKeyManager)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
// State changes should not be committed for failed operations
|
|
|
|
})
|
|
})
|
|
|
|
// Test 27: Finalize dispatch message output only
|
|
t.Run("finalize_dispatch_message_output_only", func(t *testing.T) {
|
|
runTestWithAllModes(t, "finalize_dispatch_message_output_only", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Mock the deployed code retrieval for finalize
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if it's trying to get deployed code
|
|
return bytes.HasPrefix(addr[:32], domain) &&
|
|
bytes.Equal(addr[32:48], bytes.Repeat([]byte{0x00}, 16))
|
|
})).Return([]byte("deployed_code"), nil)
|
|
|
|
// Create results with message output
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: true,
|
|
Output: []byte("transient_output"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
// Create finalize with message output
|
|
cf := compute.NewCodeFinalize(
|
|
rendezvous,
|
|
[32]byte(domain),
|
|
results,
|
|
[]*compute.StateTransition{}, // No state transitions
|
|
[]byte("message_output_only"),
|
|
[]byte(writePriv),
|
|
&compute.ComputeIntrinsicConfiguration{
|
|
ReadPublicKey: readKey,
|
|
WritePublicKey: writeKey,
|
|
},
|
|
nil, nil, nil, nil, nil, nil,
|
|
)
|
|
|
|
// Wrap in MessageRequest and serialize
|
|
finalizeReq := &protobufs.MessageRequest{
|
|
Request: &protobufs.MessageRequest_CodeFinalize{
|
|
CodeFinalize: cf.ToProtobuf(),
|
|
},
|
|
}
|
|
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)
|
|
}
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: []*protobufs.MessageRequest{
|
|
payment,
|
|
finalizeReq,
|
|
},
|
|
}
|
|
bundleBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create message
|
|
hash := sha3.Sum256(bundleBytes)
|
|
require.NoError(t, err)
|
|
|
|
msg := &protobufs.Message{
|
|
Address: domain,
|
|
Hash: hash[:],
|
|
Payload: bundleBytes,
|
|
}
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 28: Finalize partial success handling
|
|
t.Run("finalize_partial_success_handling", func(t *testing.T) {
|
|
runTestWithAllModes(t, "finalize_partial_success_handling", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Mock the deployed code retrieval for finalize
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if it's trying to get deployed code
|
|
return bytes.HasPrefix(addr[:32], domain) &&
|
|
bytes.Equal(addr[32:48], bytes.Repeat([]byte{0x00}, 16))
|
|
})).Return([]byte("deployed_code"), nil)
|
|
|
|
// Create mixed results - some succeed, some fail
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: true,
|
|
Output: []byte("result1"),
|
|
},
|
|
{
|
|
OperationID: []byte("op2"),
|
|
Success: false,
|
|
Error: []byte("op2 failed"),
|
|
},
|
|
{
|
|
OperationID: []byte("op3"),
|
|
Success: true,
|
|
Output: []byte("result3"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
msg, _ := createTestCodeFinalizeMessage(t, domain, []byte(writePriv), &compute.ComputeIntrinsicConfiguration{ReadPublicKey: readKey, WritePublicKey: writeKey}, rendezvous, results, mockHG, mockKeyManager)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 29: Empty operations list rejected
|
|
t.Run("empty_operations_list_rejected", func(t *testing.T) {
|
|
runTestWithAllModes(t, "empty_operations_list_rejected", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Add NewMultiproof mock
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof).Maybe()
|
|
|
|
// Set up mocks
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Create message with empty operations list
|
|
ops := []*compute.ExecuteOperation{}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, msgs)
|
|
|
|
// In global mode, CodeExecute messages are not allowed after deployment
|
|
// In app mode, we should get the empty operations error
|
|
if mode == TestGlobalMode {
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
} else {
|
|
assert.Contains(t, err.Error(), "empty")
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
// Test 30: Max operations limit enforcement
|
|
t.Run("max_operations_limit_enforcement", func(t *testing.T) {
|
|
runTestWithAllModes(t, "max_operations_limit_enforcement", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// For app mode, set up compute metadata so the intrinsic can be loaded
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Add CommitRaw mock for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Add NewMultiproof mock
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof).Maybe()
|
|
|
|
// Mock GetVertexData for deployed code addresses (returns empty tree)
|
|
mockHG.On("GetVertexData", mock.Anything).Return(&tries.VectorCommitmentTree{}, nil).Maybe()
|
|
|
|
// Set up mocks
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Create more operations than allowed limit (assuming limit is 100)
|
|
ops := make([]*compute.ExecuteOperation, 101)
|
|
for i := 0; i < 101; i++ {
|
|
ops[i] = &compute.ExecuteOperation{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress(fmt.Sprintf("app%d", i)),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte(fmt.Sprintf("op%d", i)),
|
|
Dependencies: [][]byte{},
|
|
}
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
assert.Nil(t, msgs)
|
|
|
|
// In global mode, CodeExecute messages are not allowed after deployment
|
|
// In app mode, we should get the limit error
|
|
if mode == TestGlobalMode {
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
} else {
|
|
assert.Contains(t, err.Error(), "limit")
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
// Test 31: dynamic fee enforcement
|
|
t.Run("dynamic_fee_enforcement", func(t *testing.T) {
|
|
runTestWithAllModes(t, "dynamic_fee_enforcement", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockKeyManager.On("ValidateSignature", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
writePub, writePriv, err := ed448.GenerateKey(rand.Reader)
|
|
writeKey := []byte(writePub)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Additional mocks specific to this test
|
|
mockHG.On("Get", mock.Anything, mock.Anything, mock.Anything).Return([]byte("old_value"), nil)
|
|
mockHG.On("Set", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
|
|
// Mock GetVertex for any address (needed for finalize operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add transaction mocks for commit operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil)
|
|
mockHG.On("GetProver").Return(mockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
results := []*compute.ExecutionResult{
|
|
{
|
|
OperationID: []byte("op1"),
|
|
Success: true,
|
|
Output: []byte("result1"),
|
|
},
|
|
{
|
|
OperationID: []byte("op2"),
|
|
Success: true,
|
|
Output: []byte("result2"),
|
|
},
|
|
}
|
|
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
msg, cf := createTestCodeFinalizeMessage(t, domain, []byte(writePriv), &compute.ComputeIntrinsicConfiguration{ReadPublicKey: readKey, WritePublicKey: writeKey}, rendezvous, results, mockHG, mockKeyManager)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
// Dynamic fee too high
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(10000000), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, true)
|
|
assert.Len(t, cf.Results, 2)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 32: Multiple execution contexts
|
|
t.Run("multiple_execution_contexts", func(t *testing.T) {
|
|
runTestWithAllModes(t, "multiple_execution_contexts", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
// Perform deployment with the same keys
|
|
domain := performDeploymentWithKeys(t, engine, mockHG, mockCompiler, readKey, writeKey, engineMode)
|
|
|
|
// Set up compute metadata
|
|
setupComputeMetadata(t, mockHG, domain, readKey, writeKey)
|
|
|
|
// Create operations with different execution contexts
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: []byte{0x00, 0x01, 0x06, 0x01},
|
|
ExecutionContext: compute.ExecutionContextIntrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: hgstate.VertexAddsDiscriminator,
|
|
ExecutionContext: compute.ExecutionContextHypergraph,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{[]byte("op1")},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("extrinsic_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3"),
|
|
Dependencies: [][]byte{[]byte("op2")},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 33: Code not deployed execution fails
|
|
t.Run("code_not_deployed_execution_fails", func(t *testing.T) {
|
|
runTestWithAllModes(t, "code_not_deployed_execution_fails", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks - no deployment performed
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
mockHG.On("GetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil, fmt.Errorf("code not found"))
|
|
|
|
// Mock GetVertex for any address (needed for code execution operations)
|
|
mockHG.On("GetVertex", mock.Anything).Return(nil, store.ErrNotFound).Maybe()
|
|
|
|
// Add mocks for app mode
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil).Maybe()
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof, nil).Maybe()
|
|
|
|
// Add transaction mocks for verify operation
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil).Maybe()
|
|
mockHG.On("GetProver").Return(mockInclusionProver).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
|
|
// Create operation referencing non-deployed code
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("non_deployed_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message should fail
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assert.Error(t, err)
|
|
|
|
// Different error messages in different modes
|
|
if engineMode == engines.GlobalMode {
|
|
assert.Contains(t, err.Error(), "non-deploy messages not allowed in global mode")
|
|
} else {
|
|
assert.Contains(t, err.Error(), "not found")
|
|
}
|
|
assert.Nil(t, msgs)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 34: Input types validation from deployment
|
|
t.Run("input_types_validation_from_deployment", func(t *testing.T) {
|
|
runTestWithAllModes(t, "input_types_validation_from_deployment", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil)
|
|
mockHG.On("GetProver").Return(mockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof, nil).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil)
|
|
mockHG.On("GetVertex", mock.Anything).Return(&mockVertex{}, nil)
|
|
// Set up mock for deployed code retrieval
|
|
// The test is using "typed_app" as the code address
|
|
code := &tries.VectorCommitmentTree{}
|
|
code.Insert([]byte{0}, []byte("circuit"), nil, big.NewInt(7))
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if the address ends with "typed_app"
|
|
codeAddrPart := string(bytes.TrimRight(addr[32:], "\x00"))
|
|
return codeAddrPart == "typed_app"
|
|
})).Return(code, nil)
|
|
mockHG.On("GetVertexData", mock.Anything).Return(&tries.VectorCommitmentTree{}, nil)
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Perform deployment with specific input types
|
|
performDeployment(t, engine, mockHG, mockCompiler, engineMode)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("typed_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 35: Output types validation from deployment
|
|
t.Run("output_types_validation_from_deployment", func(t *testing.T) {
|
|
runTestWithAllModes(t, "output_types_validation_from_deployment", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up compute metadata with test keys (Ed448 keys are 57 bytes)
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
txn := &mockTransaction{}
|
|
mockHG.On("NewTransaction", false).Return(txn, nil)
|
|
mockHG.On("GetProver").Return(mockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockMultiproof := &mocks.MockMultiproof{}
|
|
mockMultiproof.On("FromBytes", mock.Anything).Return(nil).Maybe()
|
|
mockInclusionProver.On("NewMultiproof").Return(mockMultiproof, nil).Maybe()
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("AddVertex", mock.Anything, mock.Anything).Return(nil)
|
|
mockHG.On("GetVertex", mock.Anything).Return(&mockVertex{}, nil)
|
|
// Set up mock for deployed code retrieval
|
|
// The test is using "typed_output_app" as the code address
|
|
code := &tries.VectorCommitmentTree{}
|
|
code.Insert([]byte{0}, []byte("circuit"), nil, big.NewInt(7))
|
|
mockHG.On("GetVertexData", mock.MatchedBy(func(addr [64]byte) bool {
|
|
// Check if the address ends with "typed_output_app"
|
|
codeAddrPart := string(bytes.TrimRight(addr[32:], "\x00"))
|
|
return codeAddrPart == "typed_output_app"
|
|
})).Return(code, nil)
|
|
mockHG.On("GetVertexData", mock.Anything).Return(&tries.VectorCommitmentTree{}, nil)
|
|
mockHG.On("SetVertexData", mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
|
mockBulletproofProver.On("SimpleVerify", mock.Anything, mock.Anything, mock.Anything).Return(true)
|
|
|
|
// Perform deployment with specific output types
|
|
performDeployment(t, engine, mockHG, mockCompiler, engineMode)
|
|
|
|
ops := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("typed_output_app"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
msg, _ := createTestCodeExecuteMessage(t, mockHG, domain, ops)
|
|
|
|
// Process message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), msg.Address, msg.Payload, state)
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
|
|
})
|
|
})
|
|
|
|
// Test 23: Bundled messages support multiple operations atomically
|
|
t.Run("bundled_messages_atomic_operations", func(t *testing.T) {
|
|
runTestWithAllModes(t, "bundled_messages_atomic_operations", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
`
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Create operations for bundling
|
|
operations := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_a"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_b"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
// Create a bundled message that includes both deployment and execution
|
|
bundledMsg := createDeployAndExecuteBundle(t, mockHG, readKey, writeKey, operations)
|
|
|
|
// Process the bundled message - this should work even in Global mode
|
|
// because the bundle contains both deployment and execution atomically
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), bundledMsg.Address, bundledMsg.Payload, state)
|
|
|
|
// In global mode, this should fail because bundles bypass the
|
|
// single-operation restriction, except when not including a deploy
|
|
if engineMode == engines.GlobalMode {
|
|
assert.Error(t, err, "Non-deploy bundled messages should not work in GlobalMode")
|
|
assert.Nil(t, msgs, "Should not get responses from bundled operations")
|
|
} else {
|
|
assertCodeExecutionResult(t, mode, msgs, err, false)
|
|
}
|
|
|
|
})
|
|
})
|
|
|
|
// Test 24: Multiple bundled operations processed sequentially
|
|
t.Run("bundled_multiple_operations_sequential", func(t *testing.T) {
|
|
runTestWithAllModes(t, "bundled_multiple_operations_sequential", func(t *testing.T, mode TestExecutionMode) {
|
|
logger := zap.NewNop()
|
|
mockInclusionProver := new(mocks.MockInclusionProver)
|
|
mockInclusionProver.On("CommitRaw", mock.Anything, mock.Anything).Return(make([]byte, 74), nil)
|
|
mockHG := tests.CreateHypergraphWithInclusionProver(mockInclusionProver)
|
|
mockHG.On("Commit").Return(map[tries.ShardKey][][]byte{}).Maybe()
|
|
mockHG.On("TrackChange", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Maybe()
|
|
mockHG.On("GetCoveredPrefix").Return([]int{}, nil)
|
|
mockKeyManager := new(mocks.MockKeyManager)
|
|
mockBulletproofProver := new(mocks.MockBulletproofProver)
|
|
mockVerEnc := new(mocks.MockVerifiableEncryptor)
|
|
mockDecaf := new(mocks.MockDecafConstructor)
|
|
mockCompiler := new(mocks.MockCompiler)
|
|
|
|
// Map test mode to actual engine mode
|
|
var engineMode engines.ExecutionMode
|
|
switch mode {
|
|
case TestGlobalMode:
|
|
engineMode = engines.GlobalMode
|
|
case TestApplicationMode:
|
|
engineMode = engines.ApplicationMode
|
|
case TestMixedMode:
|
|
engineMode = engines.ApplicationMode
|
|
}
|
|
|
|
engine, err := engines.NewComputeExecutionEngine(
|
|
logger,
|
|
mockHG,
|
|
mockKeyManager,
|
|
mockInclusionProver,
|
|
mockBulletproofProver,
|
|
mockVerEnc,
|
|
mockDecaf,
|
|
mockCompiler,
|
|
engineMode,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Set up mocks
|
|
setupComputeMocks(t, mockHG, mockInclusionProver, mockBulletproofProver, engineMode)
|
|
setupTokenIntrinsicMocks(t, mockHG)
|
|
|
|
// Generate keys for metadata
|
|
readKey := make([]byte, 57)
|
|
writeKey := make([]byte, 57)
|
|
rand.Read(readKey)
|
|
rand.Read(writeKey)
|
|
|
|
domain := make([]byte, 32)
|
|
rand.Read(domain)
|
|
|
|
// Set up compute metadata
|
|
metadataAddress := slices.Concat(domain, bytes.Repeat([]byte{0xff}, 32))
|
|
rdfSchema := `BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
` // Valid RDF schema for test
|
|
err = tests.SetHypergraphComputeMetadataData(
|
|
mockHG,
|
|
metadataAddress,
|
|
rdfSchema,
|
|
nil, // creator not needed for test
|
|
readKey,
|
|
writeKey,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Create multiple operation sets
|
|
ops1 := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_1"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op1_a"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
ops2 := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_2"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op2_a"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
ops3 := []*compute.ExecuteOperation{
|
|
{
|
|
Application: compute.Application{
|
|
Address: makeExtrinsicAddress("app_3"),
|
|
ExecutionContext: compute.ExecutionContextExtrinsic,
|
|
},
|
|
Identifier: []byte("op3_a"),
|
|
Dependencies: [][]byte{},
|
|
},
|
|
}
|
|
|
|
// Create a bundle with multiple operation sets
|
|
bundledMsg := createMultiOperationBundle(t, mockHG, domain, ops1, ops2, ops3)
|
|
|
|
// Process the bundled message
|
|
state := hgstate.NewHypergraphState(mockHG)
|
|
msgs, err := engine.ProcessMessage(1, big.NewInt(0), bundledMsg.Address, bundledMsg.Payload, state)
|
|
|
|
// Verify that all operations were processed
|
|
if engineMode == engines.GlobalMode {
|
|
// In global mode, this will fail due to deployment requirements
|
|
// but it demonstrates the bundling concept
|
|
if err == nil {
|
|
t.Logf("Global mode result: error=%v, responses=%d", err, len(msgs.Messages))
|
|
} else {
|
|
t.Logf("Global mode result: error=%v", err)
|
|
}
|
|
} else {
|
|
// In application mode, verify sequential processing
|
|
if err == nil {
|
|
assert.NotNil(t, msgs)
|
|
}
|
|
}
|
|
|
|
})
|
|
})
|
|
}
|
|
|
|
// createBundledMessage creates a message containing multiple bundled operations
|
|
func createBundledMessage(t *testing.T, domain []byte, operations ...*protobufs.MessageRequest) *protobufs.Message {
|
|
bundle := &protobufs.MessageBundle{
|
|
Requests: operations,
|
|
}
|
|
// Serialize the bundle
|
|
bundleBytes, err := bundle.ToCanonicalBytes()
|
|
require.NoError(t, err)
|
|
|
|
// Create the message
|
|
hash := sha3.Sum256(bundleBytes)
|
|
require.NoError(t, err)
|
|
|
|
return &protobufs.Message{
|
|
Address: domain,
|
|
Hash: hash[:],
|
|
Payload: bundleBytes,
|
|
}
|
|
}
|
|
|
|
// createDeployAndExecuteBundle creates a bundle with deployment and execution operations
|
|
func createDeployAndExecuteBundle(t *testing.T, mockHg *mocks.MockHypergraph, readKey, writeKey []byte, operations []*compute.ExecuteOperation) *protobufs.Message {
|
|
// Create deployment payload
|
|
deployArgs := &protobufs.ComputeDeploy{
|
|
Config: &protobufs.ComputeConfiguration{
|
|
ReadPublicKey: readKey,
|
|
WritePublicKey: writeKey,
|
|
},
|
|
RdfSchema: []byte(`BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
|
|
qcl:Application a rdfs:Class.
|
|
qcl:address a rdfs:Property;
|
|
rdfs:domain qcl:ByteArray;
|
|
qcl:size 64;
|
|
qcl:order 0;
|
|
rdfs:range qcl:Application.
|
|
qcl:computationSchema a rdfs:Property;
|
|
rdfs:domain qcl:String;
|
|
qcl:size 1024;
|
|
qcl:order 1;
|
|
rdfs:range qcl:Application.
|
|
qcl:permittedContracts a rdfs:Property;
|
|
rdfs:domain qcl:ByteArray;
|
|
qcl:size 64;
|
|
qcl:order 2;
|
|
rdfs:range qcl:Application.`),
|
|
}
|
|
|
|
// Create code execute payload
|
|
var domain [32]byte
|
|
rand.Read(domain[:])
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
// Create valid payment transaction bytes
|
|
outFees := []*big.Int{big.NewInt(1), 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(10000002)},
|
|
[]*big.Int{big.NewInt(0)},
|
|
outFees,
|
|
0,
|
|
)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
paymentBytes, err := payment.GetPendingTransaction().ToCanonicalBytes()
|
|
|
|
ce := compute.NewCodeExecute(
|
|
domain,
|
|
paymentBytes,
|
|
[]byte("mock_secret"),
|
|
rendezvous,
|
|
operations,
|
|
nil, nil, nil, nil, nil, nil,
|
|
)
|
|
|
|
// Manually set up proof of payment (like createTestCodeExecuteMessage does)
|
|
ce.ProofOfPayment[0] = paymentBytes
|
|
ce.ProofOfPayment[1] = make([]byte, 112) // Schnorr signature size
|
|
rand.Read(ce.ProofOfPayment[1])
|
|
|
|
// Create bundled message
|
|
return createBundledMessage(t, domain[:], payment, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_ComputeDeploy{ComputeDeploy: deployArgs}}, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_CodeExecute{CodeExecute: ce.ToProtobuf()}})
|
|
}
|
|
|
|
// createCompleteBundleWithPayment creates a bundle with payment, intrinsic deploy, code deploy, and execution
|
|
func createCompleteBundleWithPayment(
|
|
t *testing.T,
|
|
mockHG *mocks.MockHypergraph,
|
|
domain []byte,
|
|
readKey, writeKey []byte,
|
|
sourceCode []byte,
|
|
mockCompiler *mocks.MockCompiler,
|
|
operations []*compute.ExecuteOperation,
|
|
) *protobufs.Message {
|
|
payloads := []*protobufs.MessageRequest{}
|
|
|
|
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)
|
|
}
|
|
paymentBytes, err := payment.GetPendingTransaction().ToCanonicalBytes()
|
|
payloads = append(payloads, payment)
|
|
|
|
// Create intrinsic deployment payload
|
|
deployArgs := &protobufs.ComputeDeploy{
|
|
Config: &protobufs.ComputeConfiguration{
|
|
ReadPublicKey: readKey,
|
|
WritePublicKey: writeKey,
|
|
},
|
|
RdfSchema: []byte(`BASE <https://types.quilibrium.com/schema-repository/>
|
|
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
|
|
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
|
|
PREFIX qcl: <https://types.quilibrium.com/qcl/>
|
|
PREFIX req: <https://types.quilibrium.com/schema-repository/example/a/>
|
|
|
|
req:Request a rdfs:Class.
|
|
req:A a rdfs:Property;
|
|
rdfs:domain qcl:Uint;
|
|
qcl:size 1;
|
|
qcl:order 0;
|
|
rdfs:range req:Request.
|
|
`),
|
|
}
|
|
payloads = append(payloads, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_ComputeDeploy{ComputeDeploy: deployArgs}})
|
|
|
|
// Create code deployment payload (if source code provided)
|
|
if sourceCode != nil {
|
|
deployment, err := compute.NewCodeDeployment(
|
|
[32]byte(domain),
|
|
sourceCode,
|
|
[2]string{"qcl:Int", "qcl:Int"},
|
|
[2][]int{{4}, {4}},
|
|
[]string{"qcl:Int"},
|
|
mockCompiler,
|
|
)
|
|
require.NoError(t, err)
|
|
err = deployment.Prove(1)
|
|
require.NoError(t, err)
|
|
|
|
payloads = append(payloads, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_CodeDeploy{CodeDeploy: deployment.ToProtobuf()}})
|
|
}
|
|
|
|
// Create code execution payload (if operations provided)
|
|
if operations != nil && len(operations) > 0 {
|
|
var domain [32]byte
|
|
rand.Read(domain[:])
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
// Manually set up proof of payment (like createTestCodeExecuteMessage does)
|
|
proofOfPayment := [2][]byte{}
|
|
proofOfPayment[0] = paymentBytes
|
|
proofOfPayment[1] = make([]byte, 112) // Schnorr signature size
|
|
rand.Read(proofOfPayment[1])
|
|
|
|
ce := &compute.CodeExecute{
|
|
Domain: domain,
|
|
Rendezvous: rendezvous,
|
|
ProofOfPayment: proofOfPayment,
|
|
ExecuteOperations: operations,
|
|
}
|
|
payloads = append(payloads, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_CodeExecute{CodeExecute: ce.ToProtobuf()}})
|
|
}
|
|
|
|
// Create bundled message
|
|
return createBundledMessage(t, compute.COMPUTE_INTRINSIC_DOMAIN[:], payloads...)
|
|
}
|
|
|
|
// createMultiOperationBundle creates a bundle with multiple code execution operations
|
|
func createMultiOperationBundle(t *testing.T, mockHG *mocks.MockHypergraph, domain []byte, operations ...[]*compute.ExecuteOperation) *protobufs.Message {
|
|
payloads := []*protobufs.MessageRequest{}
|
|
|
|
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)
|
|
}
|
|
paymentBytes, err := payment.GetPendingTransaction().ToCanonicalBytes()
|
|
|
|
for _, ops := range operations {
|
|
var rendezvous [32]byte
|
|
rand.Read(rendezvous[:])
|
|
|
|
ce := compute.NewCodeExecute(
|
|
[32]byte(domain),
|
|
paymentBytes,
|
|
[]byte("mock_secret"),
|
|
rendezvous,
|
|
ops,
|
|
nil, nil, nil, nil, nil, nil,
|
|
)
|
|
|
|
// Manually set up proof of payment (like createTestCodeExecuteMessage does)
|
|
ce.ProofOfPayment[0] = paymentBytes
|
|
ce.ProofOfPayment[1] = make([]byte, 112) // Schnorr signature size
|
|
rand.Read(ce.ProofOfPayment[1])
|
|
|
|
payloads = append(payloads, payment, &protobufs.MessageRequest{Request: &protobufs.MessageRequest_CodeExecute{CodeExecute: ce.ToProtobuf()}})
|
|
}
|
|
|
|
return createBundledMessage(t, domain[:], payloads...)
|
|
}
|
|
|
|
var _ hypergraph.Vertex = (*mockVertex)(nil)
|