Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add local CapabilityRegistry #13225

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions contracts/src/v0.8/keystone/CapabilityRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,16 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
///
/// @dev Given the following capability ID: {name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}@{version}
// Then we denote the `labelledName` as the `{name}:{label1_key}_{label1_value}:{label2_key}_{label2_value}` portion of the ID.
///
/// Ex. id = "data-streams-reports:chain:[email protected]"
/// labelledName = "data-streams-reports:chain:ethereum"
///
/// bytes32(string); validation regex: ^[a-z0-9_\-:]{1,32}$
bytes32 labelledName;
/// @notice Semver, e.g., "1.2.3"
/// @dev must be valid Semver + max 32 characters.
bytes32 version;
/// @notice Indicates whether remote response requires
//
// Ex. id = "data-streams-reports:chain:[email protected]"
// labelledName = "data-streams-reports:chain:ethereum"
//
// validation regex: ^[a-z0-9_\-:]$
string labelledName;
// Semver, e.g., "1.2.3"
// must be valid Semver.
string version;
// responseType indicates whether remote response requires
// aggregation or is an OCR report. There are multiple possible
// ways to aggregate.
CapabilityResponseType responseType;
Expand Down Expand Up @@ -583,8 +583,7 @@ contract CapabilityRegistry is OwnerIsCreator, TypeAndVersionInterface {
/// @param labelledName The name of the capability
/// @param version The capability's version number
/// @return bytes32 A unique identifier for the capability
/// @dev The hash of the encoded labelledName and version
function getHashedCapabilityId(bytes32 labelledName, bytes32 version) public pure returns (bytes32) {
function getHashedCapabilityId(string calldata labelledName, string calldata version) public pure returns (bytes32) {
return keccak256(abi.encodePacked(labelledName, version));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
pragma solidity ^0.8.19;

import {BaseTest} from "./BaseTest.t.sol";
import {CapabilityConfigurationContract} from "./mocks/CapabilityConfigurationContract.sol";

import {CapabilityRegistry} from "../CapabilityRegistry.sol";

contract CapabilityRegistry_AddCapabilityTest is BaseTest {
Expand Down Expand Up @@ -47,10 +45,7 @@ contract CapabilityRegistry_AddCapabilityTest is BaseTest {
function test_AddCapability_NoConfigurationContract() public {
s_capabilityRegistry.addCapability(s_basicCapability);

bytes32 hashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId(
bytes32("data-streams-reports"),
bytes32("1.0.0")
);
bytes32 hashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId("data-streams-reports", "1.0.0");
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(hashedCapabilityId);

assertEq(storedCapability.labelledName, s_basicCapability.labelledName);
Expand All @@ -63,8 +58,8 @@ contract CapabilityRegistry_AddCapabilityTest is BaseTest {
s_capabilityRegistry.addCapability(s_capabilityWithConfigurationContract);

bytes32 hashedCapabilityId = s_capabilityRegistry.getHashedCapabilityId(
bytes32(s_capabilityWithConfigurationContract.labelledName),
bytes32(s_capabilityWithConfigurationContract.version)
s_capabilityWithConfigurationContract.labelledName,
s_capabilityWithConfigurationContract.version
);
CapabilityRegistry.Capability memory storedCapability = s_capabilityRegistry.getCapability(hashedCapabilityId);

Expand Down
60 changes: 60 additions & 0 deletions core/capabilities/integration_tests/internal/testutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package testutils

import (
"fmt"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/stretchr/testify/require"

kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/keystone_capability_registry"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
)

// var OWNER_ADDR = types.MustEIP55Address("0x0000000000000000000000000000000000000001").Address()

var DataStreamsReportCapability = kcr.CapabilityRegistryCapability{
LabelledName: "data-streams-report",
Version: "1.0.0",
ResponseType: uint8(0),
}

var WriteChainCapability = kcr.CapabilityRegistryCapability{
LabelledName: "write-chain",
Version: "1.0.1",
ResponseType: uint8(1),
}

func StartNewChain(t *testing.T) (*bind.TransactOpts, *backends.SimulatedBackend) {
owner := testutils.MustNewSimTransactor(t)

oneEth, _ := new(big.Int).SetString("100000000000000000000", 10)
gasLimit := ethconfig.Defaults.Miner.GasCeil * 2 // 60 M blocks

simulatedBackend := backends.NewSimulatedBackend(core.GenesisAlloc{owner.From: {
Balance: oneEth,
}}, gasLimit)
simulatedBackend.Commit()

return owner, simulatedBackend
}

func DeployCapabilityRegistry(t *testing.T, owner *bind.TransactOpts, simulatedBackend *backends.SimulatedBackend) (capabilityRegistry *kcr.CapabilityRegistry) {
capabilityRegistryAddress, _, capabilityRegistry, err := kcr.DeployCapabilityRegistry(owner, simulatedBackend)
require.NoError(t, err, "DeployCapabilityRegistry failed")

fmt.Println("Deployed CapabilityRegistry at", capabilityRegistryAddress.Hex())
simulatedBackend.Commit()

return capabilityRegistry
}

func AddCapability(t *testing.T, owner *bind.TransactOpts, simulatedBackend *backends.SimulatedBackend, capabilityRegistry *kcr.CapabilityRegistry, capability kcr.CapabilityRegistryCapability) {
_, err := capabilityRegistry.AddCapability(owner, capability)
require.NoError(t, err, "AddCapability failed for %s", capability.LabelledName)
simulatedBackend.Commit()
}
171 changes: 171 additions & 0 deletions core/capabilities/integration_tests/keystone_integration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package capabilities_test

import (
"fmt"
"math/big"
"testing"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"
"github.com/test-go/testify/mock"

ragetypes "github.com/smartcontractkit/libocr/ragep2p/types"

commonMocks "github.com/smartcontractkit/chainlink-common/pkg/types/mocks"

cap "github.com/smartcontractkit/chainlink/v2/core/capabilities"
utils "github.com/smartcontractkit/chainlink/v2/core/capabilities/integration_tests/internal"
remoteMocks "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types/mocks"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller"
evmmocks "github.com/smartcontractkit/chainlink/v2/core/chains/legacyevm/mocks"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest"
"github.com/smartcontractkit/chainlink/v2/core/logger"
"github.com/smartcontractkit/chainlink/v2/core/services/p2p/types/mocks"
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm"
)

func TestIntegration_GetCapabilities(t *testing.T) {
owner, simulatedBackend := utils.StartNewChain(t)

capabilityRegistry := utils.DeployCapabilityRegistry(t, owner, simulatedBackend)

utils.AddCapability(t, owner, simulatedBackend, capabilityRegistry, utils.DataStreamsReportCapability)
utils.AddCapability(t, owner, simulatedBackend, capabilityRegistry, utils.WriteChainCapability)

capabilities, err := capabilityRegistry.GetCapabilities(&bind.CallOpts{})
require.NoError(t, err, "GetCapabilities failed")

fmt.Println("Capabilities:", capabilities)

lggr := logger.TestLogger(t)
ctx := testutils.Context(t)

simulatedBackendClient := client.NewSimulatedBackendClient(t, simulatedBackend, testutils.SimulatedChainID)

// This requires having `CL_DATABASE_URL` set to something. DB does not appear to be used.
db := pgtest.NewSqlxDB(t)
lpOpts := logpoller.Opts{
PollPeriod: time.Millisecond,
FinalityDepth: 4,
BackfillBatchSize: 1,
RpcBatchSize: 1,
KeepFinalizedBlocksDepth: 10000,
}
lp := logpoller.NewLogPoller(
logpoller.NewORM(testutils.SimulatedChainID, db, lggr),
simulatedBackendClient,
lggr,
lpOpts,
)

require.NoError(t, lp.Start(ctx))

// ==========================================================================================
// START - Using ChainReaderService - This works, but we want to use a relayer instead.
// ==========================================================================================

// chainID, err := simulatedBackendClient.ChainID()
// require.NoError(t, err)

// chainConfig := types.ChainReaderConfig{
// Contracts: map[string]types.ChainContractReader{
// "capability_registry": {
// ContractABI: keystone_capability_registry.CapabilityRegistryABI,
// Configs: map[string]*types.ChainReaderDefinition{
// "get_capabilities": {
// ChainSpecificName: "getCapabilities",
// OutputModifications: codec.ModifiersConfig{
// &codec.RenameModifierConfig{Fields: map[string]string{"labelledName": "name"}},
// },
// },
// },
// },
// },
// }
// cr, err := evm.NewChainReaderService(ctx, lggr, lp, simulatedBackendClient, chainConfig)
// require.NoError(t, err)

// require.NoError(t, cr.Bind(ctx, []commontypes.BoundContract{
// {
// Name: "capability_registry",
// Address: capabilityRegistry.Address().String(),
// }}))

// require.NoError(t, cr.Start(ctx))

// type Cap struct {
// Name string
// Version string
// ResponseType int
// ConfigurationContract []byte
// }

// var returnedCapabilities []Cap

// err = cr.GetLatestValue(ctx, "capability_registry", "get_capabilities", nil, &returnedCapabilities)
// require.NoError(t, err)

// fmt.Println("Returned capabilities:", returnedCapabilities)

// ==========================================================================================
// END - Using ChainReaderService
// ==========================================================================================

// SYNCER DEPENDENCIES
var pid ragetypes.PeerID
err = pid.UnmarshalText([]byte("12D3KooWBCF1XT5Wi8FzfgNCqRL76Swv8TRU3TiD4QiJm8NMNX7N"))
require.NoError(t, err)
peer := mocks.NewPeer(t)
peer.On("UpdateConnections", mock.Anything).Return(nil)
peer.On("ID").Return(pid)
wrapper := mocks.NewPeerWrapper(t)
wrapper.On("GetPeer").Return(peer)
workflowEngineCapabilitiesRegistry := commonMocks.NewCapabilitiesRegistry(t)
workflowEngineCapabilitiesRegistry.On("Add", mock.Anything, mock.Anything).Return(nil)
dispatcher := remoteMocks.NewDispatcher(t)
dispatcher.On("SetReceiver", mock.Anything, mock.Anything, mock.Anything).Return(nil)

// ==========================================================================================
// Setting up a new relayer that reads from the simulated backend. This part doesn't work.
// ==========================================================================================

keyStore := cltest.NewKeyStore(t, db)
mockChain := &evmmocks.Chain{}
c := client.NewSimulatedBackendClient(t, simulatedBackend, big.NewInt(1337))
mockChain.On("Client").Return(c)
require.NoError(t, lp.Start(ctx))
mockChain.On("LogPoller").Return(lp)

relayer, err := evm.NewRelayer(
lggr,
mockChain,
evm.RelayerOpts{
DS: db,
CSAETHKeystore: keyStore,
CapabilitiesRegistry: workflowEngineCapabilitiesRegistry,
},
)
require.NoError(t, err)

syncer := cap.NewRegistrySyncer(
wrapper,
workflowEngineCapabilitiesRegistry,
dispatcher,
lggr,
relayer, // relayer
capabilityRegistry.Address().String(),
)

require.NoError(t, syncer.Start(ctx))

// Syncer.LocalState().getCapabilities()
// fmt.Println("Synced capabilities:", returnedCapabilities)

// // Do assertions here

// require.NoError(t, syncer.Close())
}
Loading
Loading