Skip to content

Commit

Permalink
changes required for readcapability tests (#15231)
Browse files Browse the repository at this point in the history
* test changes

* test changes

* syncer stats
  • Loading branch information
ettec authored Nov 14, 2024
1 parent f9f9079 commit 1956151
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 147 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ func (r *CapabilitiesRegistry) getAddress() common.Address {
type capability struct {
donCapabilityConfig *pb.CapabilityConfig
registryConfig kcr.CapabilitiesRegistryCapability
// internalOnly is true if the capability is published in the registry but not made available outside the DON in which it runs
internalOnly bool
}

// SetupDON sets up a new DON with the given capabilities and returns the DON ID
Expand Down
147 changes: 117 additions & 30 deletions core/capabilities/integration_tests/framework/don.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package framework

import (
"context"
"encoding/hex"
"fmt"
"strconv"
"testing"
Expand All @@ -13,6 +14,8 @@ import (

"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types"

"github.com/smartcontractkit/chainlink-common/pkg/services"

"github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer"

commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities"
Expand Down Expand Up @@ -40,20 +43,43 @@ import (

type DonContext struct {
EthBlockchain *EthBlockchain
p2pNetwork *MockRageP2PNetwork
p2pNetwork *FakeRageP2PNetwork
capabilityRegistry *CapabilitiesRegistry
}

func CreateDonContext(ctx context.Context, t *testing.T) DonContext {
ethBlockchain := NewEthBlockchain(t, 1000, 1*time.Second)
rageP2PNetwork := NewMockRageP2PNetwork(t, 1000)
rageP2PNetwork := NewFakeRageP2PNetwork(ctx, t, 1000)
capabilitiesRegistry := NewCapabilitiesRegistry(ctx, t, ethBlockchain)

servicetest.Run(t, rageP2PNetwork)
servicetest.Run(t, ethBlockchain)
return DonContext{EthBlockchain: ethBlockchain, p2pNetwork: rageP2PNetwork, capabilityRegistry: capabilitiesRegistry}
}

func (c DonContext) WaitForCapabilitiesToBeExposed(t *testing.T, dons ...*DON) {
allExpectedCapabilities := make(map[CapabilityRegistration]bool)
for _, don := range dons {
caps, err := don.GetExternalCapabilities()
require.NoError(t, err)
for k, v := range caps {
allExpectedCapabilities[k] = v
}
}

require.Eventually(t, func() bool {
registrations := c.p2pNetwork.GetCapabilityRegistrations()

for k := range allExpectedCapabilities {
if _, ok := registrations[k]; !ok {
return false
}
}

return true
}, 1*time.Minute, 1*time.Second, "timeout waiting for capabilities to be exposed")
}

type capabilityNode struct {
*cltest.TestApplication
registry *capabilities.Registry
Expand All @@ -64,12 +90,13 @@ type capabilityNode struct {
}

type DON struct {
services.StateMachine
t *testing.T
config DonConfiguration
lggr logger.Logger
nodes []*capabilityNode
standardCapabilityJobs []*job.Job
externalCapabilities []capability
publishedCapabilities []capability
capabilitiesRegistry *CapabilitiesRegistry

nodeConfigModifiers []func(c *chainlink.Config, node *capabilityNode)
Expand All @@ -84,10 +111,13 @@ func NewDON(ctx context.Context, t *testing.T, lggr logger.Logger, donConfig Don
dependentDONs []commoncap.DON, donContext DonContext, supportsOCR bool) *DON {
don := &DON{t: t, lggr: lggr.Named(donConfig.name), config: donConfig, capabilitiesRegistry: donContext.capabilityRegistry}

protocolRoundInterval := 1 * time.Second

var newOracleFactoryFn standardcapabilities.NewOracleFactoryFn
var libOcr *MockLibOCR
var libOcr *FakeLibOCR
if supportsOCR {
libOcr = NewMockLibOCR(t, lggr, donConfig.F, 1*time.Second)
// This is required to support the non standard OCR3 capability - will be removed when required OCR3 behaviour is implemented as standard capabilities
libOcr = NewFakeLibOCR(t, lggr, donConfig.F, protocolRoundInterval)
servicetest.Run(t, libOcr)
}

Expand All @@ -110,7 +140,8 @@ func NewDON(ctx context.Context, t *testing.T, lggr logger.Logger, donConfig Don
don.nodes = append(don.nodes, cn)

if supportsOCR {
factory := newMockLibOcrOracleFactory(libOcr, donConfig.KeyBundles[i], len(donConfig.Members), int(donConfig.F))
factory := newFakeOracleFactoryFactory(t, lggr, donConfig.KeyBundles[i], len(donConfig.Members), donConfig.F,
protocolRoundInterval)
newOracleFactoryFn = factory.NewOracleFactory
}

Expand All @@ -134,12 +165,10 @@ func NewDON(ctx context.Context, t *testing.T, lggr logger.Logger, donConfig Don

// Initialise must be called after all capabilities have been added to the DONs and before Start is called
func (d *DON) Initialise() {
if len(d.externalCapabilities) > 0 {
id := d.capabilitiesRegistry.setupDON(d.config, d.externalCapabilities)
id := d.capabilitiesRegistry.setupDON(d.config, d.publishedCapabilities)

//nolint:gosec // disable G115
d.config.DON.ID = uint32(id)
}
//nolint:gosec // disable G115
d.config.DON.ID = uint32(id)
}

func (d *DON) GetID() uint32 {
Expand All @@ -150,6 +179,29 @@ func (d *DON) GetID() uint32 {
return d.config.ID
}

func (d *DON) GetExternalCapabilities() (map[CapabilityRegistration]bool, error) {
result := map[CapabilityRegistration]bool{}
for _, publishedCapability := range d.publishedCapabilities {
if publishedCapability.internalOnly {
continue
}

for _, node := range d.nodes {
peerIDBytes, err := peerIDToBytes(node.peerID.PeerID)
if err != nil {
return nil, fmt.Errorf("failed to convert peer ID to bytes: %w", err)
}
result[CapabilityRegistration{
nodePeerID: hex.EncodeToString(peerIDBytes[:]),
capabilityID: publishedCapability.registryConfig.LabelledName + "@" + publishedCapability.registryConfig.Version,
capabilityDonID: d.GetID(),
}] = true
}
}

return result, nil
}

func (d *DON) GetConfigVersion() uint32 {
return d.config.ConfigVersion
}
Expand All @@ -162,20 +214,22 @@ func (d *DON) GetPeerIDs() []peer {
return d.config.peerIDs
}

func (d *DON) Start(ctx context.Context, t *testing.T) {
func (d *DON) Start(ctx context.Context) error {
for _, triggerFactory := range d.triggerFactories {
for _, node := range d.nodes {
trigger := triggerFactory.CreateNewTrigger(t)
err := node.registry.Add(ctx, trigger)
require.NoError(t, err)
trigger := triggerFactory.CreateNewTrigger(d.t)
if err := node.registry.Add(ctx, trigger); err != nil {
return fmt.Errorf("failed to add trigger: %w", err)
}
}
}

for _, targetFactory := range d.targetFactories {
for _, node := range d.nodes {
target := targetFactory.CreateNewTarget(t)
err := node.registry.Add(ctx, target)
require.NoError(t, err)
target := targetFactory.CreateNewTarget(d.t)
if err := node.registry.Add(ctx, target); err != nil {
return fmt.Errorf("failed to add target: %w", err)
}
}
}

Expand All @@ -184,26 +238,39 @@ func (d *DON) Start(ctx context.Context, t *testing.T) {
}

if d.addOCR3NonStandardCapability {
libocr := NewMockLibOCR(t, d.lggr, d.config.F, 1*time.Second)
servicetest.Run(t, libocr)
libocr := NewFakeLibOCR(d.t, d.lggr, d.config.F, 1*time.Second)
servicetest.Run(d.t, libocr)

for _, node := range d.nodes {
addOCR3Capability(ctx, t, d.lggr, node.registry, libocr, d.config.F, node.KeyBundle)
addOCR3Capability(ctx, d.t, d.lggr, node.registry, libocr, d.config.F, node.KeyBundle)
}
}

for _, capabilityJob := range d.standardCapabilityJobs {
err := d.AddJob(ctx, capabilityJob)
require.NoError(t, err)
if err := d.AddJob(ctx, capabilityJob); err != nil {
return fmt.Errorf("failed to add standard capability job: %w", err)
}
}

return nil
}

func (d *DON) Close() error {
for _, node := range d.nodes {
if err := node.Stop(); err != nil {
return fmt.Errorf("failed to stop node: %w", err)
}
}

return nil
}

const StandardCapabilityTemplateJobSpec = `
type = "standardcapabilities"
schemaVersion = 1
name = "%s"
command="%s"
config="%s"
config=%s
`

func (d *DON) AddStandardCapability(name string, command string, config string) {
Expand All @@ -214,11 +281,30 @@ func (d *DON) AddStandardCapability(name string, command string, config string)
d.standardCapabilityJobs = append(d.standardCapabilityJobs, &capabilitiesSpecJob)
}

func (d *DON) AddPublishedStandardCapability(name string, command string, config string,
defaultCapabilityRequestConfig *pb.CapabilityConfig,
registryConfig kcr.CapabilitiesRegistryCapability) {
spec := fmt.Sprintf(StandardCapabilityTemplateJobSpec, name, command, config)
capabilitiesSpecJob, err := standardcapabilities.ValidatedStandardCapabilitiesSpec(spec)
require.NoError(d.t, err)

d.standardCapabilityJobs = append(d.standardCapabilityJobs, &capabilitiesSpecJob)

d.publishedCapabilities = append(d.publishedCapabilities, capability{
donCapabilityConfig: defaultCapabilityRequestConfig,
registryConfig: registryConfig,
})
}

// TODO - add configuration for remote support - do this for each capability as an option
func (d *DON) AddTargetCapability(targetFactory TargetFactory) {
d.targetFactories = append(d.targetFactories, targetFactory)
}

func (d *DON) AddTriggerCapability(triggerFactory TriggerFactory) {
d.triggerFactories = append(d.triggerFactories, triggerFactory)
}

func (d *DON) AddExternalTriggerCapability(triggerFactory TriggerFactory) {
d.triggerFactories = append(d.triggerFactories, triggerFactory)

Expand All @@ -243,7 +329,7 @@ func (d *DON) AddExternalTriggerCapability(triggerFactory TriggerFactory) {
},
}

d.externalCapabilities = append(d.externalCapabilities, triggerCapability)
d.publishedCapabilities = append(d.publishedCapabilities, triggerCapability)
}

func (d *DON) AddJob(ctx context.Context, j *job.Job) error {
Expand Down Expand Up @@ -323,9 +409,10 @@ func (d *DON) AddOCR3NonStandardCapability() {
CapabilityType: uint8(registrysyncer.ContractCapabilityTypeConsensus),
}

d.externalCapabilities = append(d.externalCapabilities, capability{
d.publishedCapabilities = append(d.publishedCapabilities, capability{
donCapabilityConfig: newCapabilityConfig(),
registryConfig: ocr,
internalOnly: true,
})
}

Expand Down Expand Up @@ -357,7 +444,7 @@ func (d *DON) AddEthereumWriteTargetNonStandardCapability(forwarderAddr common.A
},
}

d.externalCapabilities = append(d.externalCapabilities, capability{
d.publishedCapabilities = append(d.publishedCapabilities, capability{
donCapabilityConfig: targetCapabilityConfig,
registryConfig: writeChain,
})
Expand All @@ -366,7 +453,7 @@ func (d *DON) AddEthereumWriteTargetNonStandardCapability(forwarderAddr common.A
}

func addOCR3Capability(ctx context.Context, t *testing.T, lggr logger.Logger, capabilityRegistry *capabilities.Registry,
libocr *MockLibOCR, donF uint8, ocr2KeyBundle ocr2key.KeyBundle) {
libocr *FakeLibOCR, donF uint8, ocr2KeyBundle ocr2key.KeyBundle) {
requestTimeout := 10 * time.Minute
cfg := ocr3.Config{
Logger: lggr,
Expand Down Expand Up @@ -394,6 +481,6 @@ func addOCR3Capability(ctx context.Context, t *testing.T, lggr logger.Logger, ca
libocr.AddNode(plugin, transmitter, ocr2KeyBundle)
}

func Context(tb testing.TB) context.Context {
return testutils.Context(tb)
func Context(tb testing.TB) (ctx context.Context, cancel func()) {
return context.WithCancel(testutils.Context(tb))
}
Loading

0 comments on commit 1956151

Please sign in to comment.