diff --git a/GNUmakefile b/GNUmakefile index a5667cc5c86..b33dc4cb670 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -108,7 +108,6 @@ install-plugins-testing: ## Build & install testing only LOOPP binaries (plugins install-plugins-local: ## Build & install local plugins go install -ldflags="-s" \ ./plugins/cmd/chainlink-medianpoc \ - ./plugins/cmd/chainlink-ocr3-capability \ ./plugins/cmd/capabilities/log-event-trigger .PHONY: make install-plugins diff --git a/core/capabilities/aggregator_factory.go b/core/capabilities/aggregator_factory.go deleted file mode 100644 index 376ad5ac13a..00000000000 --- a/core/capabilities/aggregator_factory.go +++ /dev/null @@ -1,42 +0,0 @@ -package capabilities - -import ( - "fmt" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/aggregators" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/datafeeds" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" -) - -type Aggregator string - -const ( - // DataFeedsAggregator is the name of the data feeds aggregator. - DataFeedsAggregator Aggregator = "data_feeds" - // IdenticalAggregator is the name of the identical aggregator. - IdenticalAggregator Aggregator = "identical" - // ReduceAggregator is the name of the reduce aggregator. - ReduceAggregator Aggregator = "reduce" - // StreamsAggregator is the name of the streams aggregator. - LLOStreamsAggregator Aggregator = "llo_streams" -) - -// NewAggregator creates a new aggregator based on the provided name and config. -func NewAggregator(name string, config values.Map, lggr logger.Logger) (types.Aggregator, error) { - switch name { - case string(DataFeedsAggregator): - mc := streams.NewCodec(lggr) - return datafeeds.NewDataFeedsAggregator(config, mc) - case string(IdenticalAggregator): - return aggregators.NewIdenticalAggregator(config) - case string(ReduceAggregator): - return aggregators.NewReduceAggregator(config) - case string(LLOStreamsAggregator): - return datafeeds.NewLLOAggregator(config) - default: - return nil, fmt.Errorf("aggregator %s not supported", name) - } -} diff --git a/core/capabilities/encoder_factory.go b/core/capabilities/encoder_factory.go deleted file mode 100644 index 92e0b55abc5..00000000000 --- a/core/capabilities/encoder_factory.go +++ /dev/null @@ -1,26 +0,0 @@ -package capabilities - -import ( - "fmt" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/ocr3cap" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - - evm "github.com/smartcontractkit/chainlink-evm/pkg/relay" -) - -func NewEncoder(name string, config *values.Map, lggr logger.Logger) (types.Encoder, error) { - switch ocr3cap.Encoder(name) { - case ocr3cap.EncoderEVM: - return evm.NewEVMEncoder(config) - case ocr3cap.EncoderValueMap: - return ocr3.ValueMapEncoder{}, nil - // TODO: add a "no-op" encoder for users who only want to use dynamic ones? - // https://smartcontract-it.atlassian.net/browse/CAPPL-88 - default: - return nil, fmt.Errorf("encoder %s not supported", name) - } -} diff --git a/core/capabilities/encoder_factory_test.go b/core/capabilities/encoder_factory_test.go deleted file mode 100644 index b0d12850e1c..00000000000 --- a/core/capabilities/encoder_factory_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package capabilities - -import ( - "testing" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/ocr3cap" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - - "github.com/stretchr/testify/require" -) - -func Test_NewEncoder(t *testing.T) { - t.Parallel() - t.Run("All ocr3 encoder types return a factory", func(t *testing.T) { - evmEncoding, err := values.NewMap(map[string]any{"abi": "bytes[] Full_reports"}) - require.NoError(t, err) - - config := map[ocr3cap.Encoder]*values.Map{ - ocr3cap.EncoderEVM: evmEncoding, - } - - for _, tt := range ocr3cap.Encoders() { - if tt == ocr3cap.EncoderBorsh { - continue // Borsh encoder is deprecated - } - encoder, err2 := NewEncoder(string(tt), config[tt], logger.Nop()) - require.NoError(t, err2) - require.NotNil(t, encoder) - } - }) - - t.Run("Invalid encoder returns an error", func(t *testing.T) { - _, err2 := NewEncoder("NotReal", values.EmptyMap(), logger.Nop()) - require.Error(t, err2) - }) -} diff --git a/core/capabilities/fakes/consensus.go b/core/capabilities/fakes/consensus.go deleted file mode 100644 index 455556df8d1..00000000000 --- a/core/capabilities/fakes/consensus.go +++ /dev/null @@ -1,222 +0,0 @@ -package fakes - -import ( - "context" - "time" - - "github.com/jonboulle/clockwork" - "google.golang.org/protobuf/proto" - - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - commonCap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" - pbtypes "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/types" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/requests" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - - "github.com/smartcontractkit/chainlink/v2/core/capabilities" -) - -// This capability simulates consensus by running the OCR plugin on a single node, without libOCR. -type FakeConsensusConfig struct { - N int - F int - DeltaRound int - BatchSize int - OutcomePruningThreshold uint64 - RequestTimeout time.Duration -} - -func DefaultFakeConsensusConfig() FakeConsensusConfig { - return FakeConsensusConfig{ - N: 10, - F: 3, - DeltaRound: 5000, - BatchSize: 100, - OutcomePruningThreshold: 1000, - RequestTimeout: time.Second * 20, - } -} - -type fakeConsensus struct { - services.Service - eng *services.Engine - - config FakeConsensusConfig - plugin ocr3types.ReportingPlugin[[]byte] - transmitter *ocr3.ContractTransmitter - store *requests.Store[*ocr3.ReportRequest] - cap capIface - stats SimpleStats - - previousOutcome []byte -} - -type capIface interface { - commonCap.ExecutableCapability - services.Service -} - -var _ services.Service = (*fakeConsensus)(nil) -var _ commonCap.ExecutableCapability = (*fakeConsensus)(nil) - -const consensusCapID = "offchain_reporting@1.0.0" - -func NewFakeConsensus(lggr logger.Logger, config FakeConsensusConfig) (*fakeConsensus, error) { - rpConfig := ocr3types.ReportingPluginConfig{} - store := requests.NewStore[*ocr3.ReportRequest]() - - capability := ocr3.NewCapability(store, clockwork.NewRealClock(), config.RequestTimeout, capabilities.NewAggregator, capabilities.NewEncoder, lggr, 100) - - offchainConfig := &pbtypes.ReportingPluginConfig{ - OutcomePruningThreshold: config.OutcomePruningThreshold, - } - plugin, err := ocr3.NewReportingPlugin(store, capability, config.BatchSize, rpConfig, - offchainConfig, lggr) - if err != nil { - return nil, err - } - - transmitter := ocr3.NewContractTransmitter(lggr, nil, "") - transmitter.SetCapability(capability) - - fc := &fakeConsensus{ - config: config, - plugin: plugin, - transmitter: transmitter, - store: store, - cap: capability, - stats: *NewSimpleStats(), - } - fc.Service, fc.eng = services.Config{ - Name: "fakeConsensus", - Start: fc.start, - Close: fc.close, - }.NewServiceEngine(lggr) - return fc, nil -} - -func (fc *fakeConsensus) start(ctx context.Context) error { - ticker := services.TickerConfig{ - Initial: 500 * time.Millisecond, - JitterPct: 0.0, - }.NewTicker(time.Duration(fc.config.DeltaRound) * time.Millisecond) - fc.eng.GoTick(ticker, fc.simulateOCRRound) - return fc.cap.Start(ctx) -} - -func (fc *fakeConsensus) close() error { - err := fc.cap.Close() - fc.stats.PrintToStdout("Consensus Capability Stats") - return err -} - -func (fc *fakeConsensus) simulateOCRRound(ctx context.Context) { - runCtx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - ocrCtx := ocr3types.OutcomeContext{ - SeqNr: 1, - PreviousOutcome: fc.previousOutcome, - Epoch: 1, - Round: 1, - } - - query, err := fc.plugin.Query(runCtx, ocrCtx) - if err != nil { - fc.eng.Errorw("Error running Query", "error", err) - return - } - fc.eng.Debugw("Query execution complete", "size", len(query)) - - now := time.Now() - observation, err := fc.plugin.Observation(runCtx, ocrCtx, query) - elapsed := time.Since(now) - if err != nil { - fc.eng.Errorw("Error running Observation", "error", err) - return - } - fc.eng.Debugw("Observation execution complete", "size", len(observation), "durationMS", elapsed.Milliseconds()) - fc.stats.UpdateMaxStat("Max observation size (bytes)", int64(len(observation))) - fc.stats.UpdateMaxStat("Max observation duration (ms)", elapsed.Milliseconds()) - - aos := []types.AttributedObservation{} - oracleID := uint8(0) - for range 2*fc.config.F + 1 { - aos = append(aos, types.AttributedObservation{ - Observation: observation, - Observer: commontypes.OracleID(oracleID), - }) - oracleID++ - } - - now = time.Now() - outcome, err := fc.plugin.Outcome(runCtx, ocrCtx, query, aos) - elapsed = time.Since(now) - if err != nil { - fc.eng.Errorw("Error running Outcome", "error", err) - return - } - fc.eng.Debugw("Outcome execution complete", "size", len(outcome), "durationMS", elapsed.Milliseconds()) - fc.stats.UpdateMaxStat("Max outcome size (bytes)", int64(len(outcome))) - fc.stats.UpdateMaxStat("Max outcome duration (ms)", elapsed.Milliseconds()) - - fc.previousOutcome = outcome - - // calculate the size of the previous outcome minus current reports, - // which is the data that will be preserved as long as a workflow exists - // in the system - unmarshaled := &pbtypes.Outcome{} - _ = proto.Unmarshal(outcome, unmarshaled) - unmarshaled.Outcomes = nil - rawCleaned, _ := proto.Marshal(unmarshaled) - fc.stats.UpdateMaxStat("Max preserved outcome size (bytes)", int64(len(rawCleaned))) - - now = time.Now() - reports, err := fc.plugin.Reports(runCtx, 1, outcome) - elapsed = time.Since(now) - if err != nil { - fc.eng.Errorw("Error running Reports", "error", err) - return - } - - for _, report := range reports { - reportSize := len(report.ReportWithInfo.Report) - fc.eng.Infow("Sending report", "report", report, "size", reportSize) - fc.stats.UpdateMaxStat("Max report size (bytes)", int64(reportSize)) - fc.stats.UpdateMaxStat("Max report duration (ms)", elapsed.Milliseconds()) - emptyDigest := [32]byte{} - // TODO: add non-empty signatures - err = fc.transmitter.Transmit(runCtx, emptyDigest, 1, report.ReportWithInfo, []types.AttributedOnchainSignature{}) - if err != nil { - fc.eng.Errorw("Error transmitting report", "error", err) - } - } -} - -func (fc *fakeConsensus) Execute(ctx context.Context, request commonCap.CapabilityRequest) (commonCap.CapabilityResponse, error) { - return fc.cap.Execute(ctx, request) -} - -func (fc *fakeConsensus) RegisterToWorkflow(ctx context.Context, request commonCap.RegisterToWorkflowRequest) error { - fc.eng.Infow("Registering to Fake Consensus", "workflowID", request.Metadata.WorkflowID) - return fc.cap.RegisterToWorkflow(ctx, request) -} - -func (fc *fakeConsensus) UnregisterFromWorkflow(ctx context.Context, request commonCap.UnregisterFromWorkflowRequest) error { - fc.eng.Infow("Unregistering from Fake Consensus", "workflowID", request.Metadata.WorkflowID) - return fc.cap.UnregisterFromWorkflow(ctx, request) -} - -func (fc *fakeConsensus) Info(ctx context.Context) (commonCap.CapabilityInfo, error) { - return commonCap.CapabilityInfo{ - ID: consensusCapID, - CapabilityType: commonCap.CapabilityTypeConsensus, - Description: "Fake OCR Consensus", - DON: &commonCap.DON{}, - IsLocal: true, - }, nil -} diff --git a/core/capabilities/fakes/consensus_nodag.go b/core/capabilities/fakes/consensus_nodag.go index 4bd8f5a7577..682f51fa46c 100644 --- a/core/capabilities/fakes/consensus_nodag.go +++ b/core/capabilities/fakes/consensus_nodag.go @@ -33,10 +33,7 @@ type fakeConsensusNoDAG struct { seqNr uint64 } -var ( - _ services.Service = (*fakeConsensus)(nil) - _ consensusserver.ConsensusCapability = (*fakeConsensusNoDAG)(nil) -) +var _ consensusserver.ConsensusCapability = (*fakeConsensusNoDAG)(nil) func NewFakeConsensusNoDAG(signers []ocr2key.KeyBundle, lggr logger.Logger) *fakeConsensusNoDAG { configDigest := ocr2types.ConfigDigest{} diff --git a/core/capabilities/fakes/streams_trigger.go b/core/capabilities/fakes/streams_trigger.go deleted file mode 100644 index 96df90bd573..00000000000 --- a/core/capabilities/fakes/streams_trigger.go +++ /dev/null @@ -1,235 +0,0 @@ -package fakes - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "math/big" - "sync" - "time" - - "github.com/smartcontractkit/libocr/offchainreporting2/chains/evmutil" - - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys" - commonCap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" - commonStreams "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers/streams" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" -) - -type fakeStreamsTrigger struct { - services.Service - eng *services.Engine - lggr logger.Logger - - signers []ocr2key.KeyBundle - codec datastreams.ReportCodec - meta datastreams.Metadata - - registrations map[string]*regState - lastPrice map[string]int64 - lastEventTs int64 - mu sync.Mutex -} - -type regState struct { - cfg commonStreams.TriggerConfig - eventCh chan commonCap.TriggerResponse -} - -var _ services.Service = (*fakeStreamsTrigger)(nil) -var _ commonCap.TriggerCapability = (*fakeStreamsTrigger)(nil) - -const ( - triggerID = "streams-trigger@1.0.0" - resolutionMs = 5000 -) - -func (st *fakeStreamsTrigger) Info(ctx context.Context) (commonCap.CapabilityInfo, error) { - return commonCap.CapabilityInfo{ - ID: triggerID, - CapabilityType: commonCap.CapabilityTypeTrigger, - Description: "Fake Streams Trigger", - DON: &commonCap.DON{}, - IsLocal: true, - }, nil -} - -func (st *fakeStreamsTrigger) AckEvent(ctx context.Context, triggerID string, eventID string, method string) error { - return nil -} - -func (st *fakeStreamsTrigger) RegisterTrigger(ctx context.Context, request commonCap.TriggerRegistrationRequest) (<-chan commonCap.TriggerResponse, error) { - st.mu.Lock() - defer st.mu.Unlock() - - reg, found := st.registrations[request.Metadata.WorkflowID] - if found { - return reg.eventCh, nil - } - state := ®State{ - eventCh: make(chan commonCap.TriggerResponse, 1000), - } - if err := request.Config.UnwrapTo(&state.cfg); err != nil { - return nil, err - } - for _, feed := range state.cfg.FeedIds { - if _, found := st.lastPrice[string(feed)]; !found { - st.lastPrice[string(feed)] = 1 - } - } - - st.registrations[request.Metadata.WorkflowID] = state - st.eng.Infow("Registered to Fake Streams Trigger", "workflowID", request.Metadata.WorkflowID) - return state.eventCh, nil -} - -func (st *fakeStreamsTrigger) UnregisterTrigger(ctx context.Context, request commonCap.TriggerRegistrationRequest) error { - st.mu.Lock() - defer st.mu.Unlock() - reg, found := st.registrations[request.Metadata.WorkflowID] - if !found { - return errors.New("not registered") - } - close(reg.eventCh) - delete(st.registrations, request.Metadata.WorkflowID) - st.eng.Infow("Unregistered from Fake Streams Trigger", "workflowID", request.Metadata.WorkflowID) - return nil -} - -func NewFakeStreamsTrigger(lggr logger.Logger, nSigners int) *fakeStreamsTrigger { - signers := make([]ocr2key.KeyBundle, nSigners) - rawSigners := make([][]byte, nSigners) - for i := range nSigners { - signers[i], _ = ocr2key.New(corekeys.EVM) - rawSigners[i] = signers[i].PublicKey() - } - - st := &fakeStreamsTrigger{ - signers: signers, - meta: datastreams.Metadata{ - Signers: rawSigners, - MinRequiredSignatures: nSigners, - }, - codec: streams.NewCodec(lggr), - registrations: make(map[string]*regState), - lastPrice: make(map[string]int64), - lggr: lggr, - } - st.Service, st.eng = services.Config{ - Name: "fakeStreamsTrigger", - Start: st.start, - }.NewServiceEngine(lggr) - return st -} - -func (st *fakeStreamsTrigger) start(_ context.Context) error { - ticker := services.TickerConfig{ - Initial: 1000 * time.Millisecond, - JitterPct: 0.0, - }.NewTicker(100 * time.Millisecond) - st.eng.GoTick(ticker, st.emitEvent) - return nil -} - -func (st *fakeStreamsTrigger) emitEvent(ctx context.Context) { - st.mu.Lock() - defer st.mu.Unlock() - - nowMs := time.Now().UnixMilli() - newTs := (nowMs / resolutionMs) * resolutionMs - if newTs == st.lastEventTs { - return - } - st.lastEventTs = newTs - tsSec := uint32(newTs / 1000) //nolint:gosec // G115 - - // generate new reports for all feeds - reports := make(map[string]datastreams.FeedReport) - for feed, lastPrice := range st.lastPrice { - st.lastPrice[feed] = lastPrice + 1 - idBytes, err := hex.DecodeString(feed[2:]) - if err != nil { - st.eng.Errorw("Failed to decode feed ID", "error", err) - continue - } - idBytes32 := [32]byte{} - copy(idBytes32[:], idBytes) - fullReport := newReport(ctx, st.lggr, idBytes32, lastPrice, tsSec) - - reportCtx := ocrTypes.ReportContext{} - rawCtx := rawReportContext(reportCtx) - - feedReport := datastreams.FeedReport{ - FeedID: feed, - FullReport: fullReport, - ReportContext: rawCtx, - Signatures: [][]byte{}, - } - for _, signer := range st.signers { - signature, err := signer.Sign(reportCtx, fullReport) - if err != nil { - st.eng.Errorw("Failed to sign report", "error", err) - continue - } - feedReport.Signatures = append(feedReport.Signatures, signature) - } - reports[feed] = feedReport - } - - eventID := fmt.Sprintf("streams_%d", newTs) - - for wf, reg := range st.registrations { - if reg.cfg.MaxFrequencyMs > 0 && newTs%int64(reg.cfg.MaxFrequencyMs) != 0 { //nolint:gosec // G115 - continue - } - requestedReports := []datastreams.FeedReport{} - for _, feedID := range reg.cfg.FeedIds { - if report, found := reports[string(feedID)]; found { - requestedReports = append(requestedReports, report) - } - } - event, err := triggers.WrapReports(requestedReports, eventID, nowMs, st.meta, triggerID) - if err != nil { - st.eng.Errorw("Failed to wrap reports", "error", err) - return - } - st.eng.Infow("Sending event to workflow", "eventID", eventID, "workflowID", wf) - reg.eventCh <- event - } -} - -func newReport(ctx context.Context, lggr logger.Logger, feedID [32]byte, price int64, timestampSec uint32) []byte { - priceB := big.NewInt(price) - v3Codec := reportcodec.NewReportCodec(feedID, lggr) - raw, _ := v3Codec.BuildReport(ctx, v3.ReportFields{ - BenchmarkPrice: priceB, - Timestamp: timestampSec, - ValidFromTimestamp: timestampSec, - Bid: priceB, - Ask: priceB, - LinkFee: priceB, - NativeFee: priceB, - ExpiresAt: timestampSec + 3600, - }) - return raw -} - -func rawReportContext(reportCtx ocrTypes.ReportContext) []byte { - rc := evmutil.RawReportContext(reportCtx) - flat := []byte{} - for _, r := range rc { - flat = append(flat, r[:]...) - } - return flat -} diff --git a/core/capabilities/integration_tests/framework/capabilities_registry.go b/core/capabilities/integration_tests/framework/capabilities_registry.go deleted file mode 100644 index a02efaeed3c..00000000000 --- a/core/capabilities/integration_tests/framework/capabilities_registry.go +++ /dev/null @@ -1,132 +0,0 @@ -package framework - -import ( - "context" - "testing" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" - kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -type CapabilitiesRegistry struct { - t *testing.T - backend *EthBlockchain - contract *kcr.CapabilitiesRegistry - addr common.Address - nodeOperatorID uint32 - nextDonID int -} - -func NewCapabilitiesRegistry(ctx context.Context, t *testing.T, backend *EthBlockchain) *CapabilitiesRegistry { - addr, _, contract, err := kcr.DeployCapabilitiesRegistry(backend.transactionOpts, backend.Client()) - require.NoError(t, err) - backend.Commit() - - _, err = contract.AddNodeOperators(backend.transactionOpts, []kcr.CapabilitiesRegistryNodeOperator{ - { - Admin: backend.transactionOpts.From, - Name: "TEST_NODE_OPERATOR", - }, - }) - require.NoError(t, err) - blockHash := backend.Commit() - - logs, err := backend.Client().FilterLogs(ctx, ethereum.FilterQuery{ - BlockHash: &blockHash, - FromBlock: nil, - ToBlock: nil, - Addresses: nil, - Topics: nil, - }) - - require.NoError(t, err) - - recLog, err := contract.ParseNodeOperatorAdded(logs[0]) - require.NoError(t, err) - - nopID := recLog.NodeOperatorId - - return &CapabilitiesRegistry{t: t, addr: addr, contract: contract, backend: backend, nodeOperatorID: nopID} -} - -func (r *CapabilitiesRegistry) getAddress() common.Address { - return r.addr -} - -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 -func (r *CapabilitiesRegistry) setupDON(donInfo DonConfiguration, capabilities []capability) int { - hashedCapabilityIDs := make([][32]byte, 0, len(capabilities)) - - for _, c := range capabilities { - id, err := r.contract.GetHashedCapabilityId(&bind.CallOpts{}, c.registryConfig.LabelledName, c.registryConfig.Version) - require.NoError(r.t, err) - hashedCapabilityIDs = append(hashedCapabilityIDs, id) - } - - registryCapabilities := make([]kcr.CapabilitiesRegistryCapability, 0, len(capabilities)) - for _, c := range capabilities { - registryCapabilities = append(registryCapabilities, c.registryConfig) - } - - _, err := r.contract.AddCapabilities(r.backend.transactionOpts, registryCapabilities) - require.NoError(r.t, err) - - r.backend.Commit() - - peerIDs := make([][32]byte, 0, len(donInfo.p2pKeys)) - nodes := make([]kcr.CapabilitiesRegistryNodeParams, 0, len(donInfo.p2pKeys)) - for i, p2pkey := range donInfo.p2pKeys { - signer, innerErr := getSignerStringFromOCRKeyBundle(donInfo.KeyBundles[i]) - require.NoError(r.t, innerErr) - peer := peerIDAndOCRSigner{PeerID: p2ptypes.PeerID(p2pkey.PeerID()), Signer: signer} - peerIDs = append(peerIDs, p2pkey.PeerID()) - n, innerErr := peerToNode(r.nodeOperatorID, peer) - require.NoError(r.t, innerErr) - - n.HashedCapabilityIds = hashedCapabilityIDs - nodes = append(nodes, n) - } - - _, err = r.contract.AddNodes(r.backend.transactionOpts, nodes) - require.NoError(r.t, err) - r.backend.Commit() - - capabilityConfigurations := make([]kcr.CapabilitiesRegistryCapabilityConfiguration, 0, len(capabilities)) - for i, c := range capabilities { - configBinary, err2 := proto.Marshal(c.donCapabilityConfig) - require.NoError(r.t, err2) - - capabilityConfigurations = append(capabilityConfigurations, kcr.CapabilitiesRegistryCapabilityConfiguration{ - CapabilityId: hashedCapabilityIDs[i], - Config: configBinary, - }) - } - - _, err = r.contract.AddDON(r.backend.transactionOpts, peerIDs, capabilityConfigurations, true, donInfo.AcceptsWorkflows, donInfo.F) - require.NoError(r.t, err) - r.backend.Commit() - - r.nextDonID++ - return r.nextDonID -} - -func newCapabilityConfig() *pb.CapabilityConfig { - return &pb.CapabilityConfig{ - DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(), - } -} diff --git a/core/capabilities/integration_tests/framework/don.go b/core/capabilities/integration_tests/framework/don.go deleted file mode 100644 index 42f6fd48434..00000000000 --- a/core/capabilities/integration_tests/framework/don.go +++ /dev/null @@ -1,642 +0,0 @@ -package framework - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "maps" - "strconv" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/durationpb" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ethkey" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/workflowkey" - commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" - commonconfig "github.com/smartcontractkit/chainlink-common/pkg/config" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core" - kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" - "github.com/smartcontractkit/chainlink-evm/pkg/assets" - evmtestutils "github.com/smartcontractkit/chainlink-evm/pkg/testutils" - "github.com/smartcontractkit/chainlink-evm/pkg/types" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - "github.com/smartcontractkit/chainlink/v2/core/capabilities" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/compute" - remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" - "github.com/smartcontractkit/chainlink/v2/core/services/job" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" - "github.com/smartcontractkit/chainlink/v2/core/services/registrysyncer" - "github.com/smartcontractkit/chainlink/v2/core/services/standardcapabilities" - "github.com/smartcontractkit/chainlink/v2/core/services/workflows/syncer" - wftypes "github.com/smartcontractkit/chainlink/v2/core/services/workflows/types" - "github.com/smartcontractkit/chainlink/v2/core/utils/testutils/heavyweight" -) - -type DonContext struct { - EthBlockchain *EthBlockchain - p2pNetwork *FakeRageP2PNetwork - capabilityRegistry *CapabilitiesRegistry - workflowRegistry *WorkflowRegistry - syncerFetcherFunc wftypes.FetcherFunc - computeFetcherFactory compute.FetcherFactory -} - -func CreateDonContext(ctx context.Context, t *testing.T) DonContext { - ethBlockchain := NewEthBlockchain(t, 1000, 1*time.Second) - 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 CreateDonContextWithWorkflowRegistry(ctx context.Context, t *testing.T, syncerFetcherFunc wftypes.FetcherFunc, - computeFetcherFactory compute.FetcherFactory) DonContext { - donContext := CreateDonContext(ctx, t) - workflowRegistry := NewWorkflowRegistry(ctx, t, donContext.EthBlockchain) - donContext.workflowRegistry = workflowRegistry - donContext.syncerFetcherFunc = syncerFetcherFunc - donContext.computeFetcherFactory = computeFetcherFactory - return donContext -} - -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) - maps.Copy(allExpectedCapabilities, caps) - } - - 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") -} - -func (c DonContext) WaitForWorkflowRegistryMetadata(t *testing.T, workflowName string, owner string, workflowID [32]byte) { - require.Eventually(t, func() bool { - wf, err := c.workflowRegistry.contract.GetWorkflowMetadata(&bind.CallOpts{}, common.HexToAddress(owner), workflowName) - if err != nil { - return false - } - return wf.WorkflowID == workflowID - }, 1*time.Minute, 5*time.Second, "timeout waiting for workflow") -} - -type capabilityNode struct { - *cltest.TestApplication - registry *capabilities.Registry - key ethkey.KeyV2 - KeyBundle ocr2key.KeyBundle - peer peerIDAndOCRSigner - workflowKey *workflowkey.Key - start func() -} - -type DON struct { - services.StateMachine - t *testing.T - id *uint32 - config DonConfiguration - lggr logger.Logger - nodes []*capabilityNode - standardCapabilityJobs []*job.Job - publishedCapabilities []capability - - initialised bool - - capabilitiesRegistry *CapabilitiesRegistry - workflowRegistry *WorkflowRegistry - - nodeConfigModifiers []func(c *chainlink.Config, node *capabilityNode) - - fakeLibOcr *FakeLibOCR - addOCR3NonStandardCapability bool - - triggerFactories []TriggerFactory - targetFactories []TargetFactory -} - -func NewDON(ctx context.Context, t *testing.T, lggr logger.Logger, donConfig DonConfiguration, - dependentDONs []commoncap.DON, donContext DonContext, supportsOCR bool, protocolRoundInterval time.Duration) *DON { - don := &DON{t: t, lggr: logger.Named(lggr, donConfig.name), config: donConfig, capabilitiesRegistry: donContext.capabilityRegistry, - workflowRegistry: donContext.workflowRegistry} - - if supportsOCR { - // This is required to support the non standard OCR3 capability - will be removed when required OCR3 behaviour is implemented as standard capabilities - don.fakeLibOcr = NewFakeLibOCR(t, lggr, donConfig.F, protocolRoundInterval) - servicetest.Run(t, don.fakeLibOcr) - } - - for i, member := range donConfig.Members { - dispatcher := donContext.p2pNetwork.NewDispatcherForNode(member) - capabilityRegistry := capabilities.NewRegistry(lggr) - - nodeInfo := commoncap.Node{ - PeerID: &member, - WorkflowDON: donConfig.DON, - CapabilityDONs: dependentDONs, - } - - signer, err := getSignerStringFromOCRKeyBundle(donConfig.KeyBundles[i]) - require.NoError(t, err) - cn := &capabilityNode{ - registry: capabilityRegistry, - key: donConfig.keys[i], - KeyBundle: donConfig.KeyBundles[i], - peer: peerIDAndOCRSigner{PeerID: member, Signer: signer}, - } - don.nodes = append(don.nodes, cn) - - var newOracleFactoryFn standardcapabilities.NewOracleFactoryFn - if supportsOCR { - factory := newFakeOracleFactoryFactory(t, lggr, donConfig.KeyBundles[i], len(donConfig.Members), donConfig.F, - protocolRoundInterval) - newOracleFactoryFn = factory.NewOracleFactory - } - - cn.start = func() { - node := startNewNode(ctx, t, logger.Named(lggr, donConfig.name+"-"+strconv.Itoa(i)), nodeInfo, donContext.EthBlockchain, - donContext.capabilityRegistry.getAddress(), dispatcher, - peerWrapper{peer: p2pPeer{member}}, capabilityRegistry, newOracleFactoryFn, - donConfig.keys[i], func(c *chainlink.Config) { - for _, modifier := range don.nodeConfigModifiers { - modifier(c, cn) - } - }, donContext.syncerFetcherFunc, donContext.computeFetcherFactory) - require.NoError(t, node.KeyStore.P2P().Add(ctx, donConfig.p2pKeys[i])) - workflowKeys, err := node.KeyStore.Workflow().GetAll() - require.NoError(t, err) - - // Workflow nodes should only have at most 1 workflow key. - require.LessOrEqual(t, len(workflowKeys), 1) - if len(workflowKeys) == 1 { - cn.workflowKey = &workflowKeys[0] - } - require.NoError(t, node.Start(t.Context())) - cn.TestApplication = node - } - } - - return don -} - -// Initialise must be called after all capabilities have been added to the DONs and before Start is called -func (d *DON) Initialise() { - id := d.capabilitiesRegistry.setupDON(d.config, d.publishedCapabilities) - - //nolint:gosec // disable G115 - d.config.ID = uint32(id) - d.id = &d.config.ID - - if d.config.AcceptsWorkflows && d.workflowRegistry != nil { - d.workflowRegistry.UpdateAllowedDons([]uint32{d.config.ID}) - d.nodeConfigModifiers = append(d.nodeConfigModifiers, func(c *chainlink.Config, node *capabilityNode) { - workflowRegistryAddressStr := d.workflowRegistry.addr.String() - c.Capabilities.WorkflowRegistry.Address = &workflowRegistryAddressStr - c.Capabilities.WorkflowRegistry.ChainID = new(fmt.Sprintf("%d", testutils.SimulatedChainID)) - }) - } - d.initialised = true -} - -func (d *DON) GetID() uint32 { - if d.config.ID == 0 { - panic("DON ID not set, call Initialise() first") - } - - 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 { - result[CapabilityRegistration{ - nodePeerID: hex.EncodeToString(node.peer.PeerID[:]), - capabilityID: publishedCapability.registryConfig.LabelledName + "@" + publishedCapability.registryConfig.Version, - capabilityDonID: d.GetID(), - }] = true - } - } - - return result, nil -} - -func (d *DON) GetConfigVersion() uint32 { - return d.config.ConfigVersion -} - -func (d *DON) GetF() uint8 { - return d.config.F -} - -func (d *DON) GetPeerIDsAndOCRSigners() []peerIDAndOCRSigner { - peers := make([]peerIDAndOCRSigner, 0, len(d.nodes)) - for _, node := range d.nodes { - peers = append(peers, node.peer) - } - return peers -} - -func (d *DON) GetWorkflowPublicKeys() []*[32]byte { - keys := make([]*[32]byte, 0, len(d.nodes)) - for _, node := range d.nodes { - if node.workflowKey != nil { - pubKey := node.workflowKey.PublicKey() - keys = append(keys, &pubKey) - } - } - return keys -} - -func (d *DON) Start(ctx context.Context) error { - for _, triggerFactory := range d.triggerFactories { - for _, node := range d.nodes { - 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(d.t) - if err := node.registry.Add(ctx, target); err != nil { - return fmt.Errorf("failed to add target: %w", err) - } - } - } - - for _, node := range d.nodes { - node.start() - } - - if d.addOCR3NonStandardCapability { - if d.fakeLibOcr == nil { - return errors.New("don does not support OCR") - } - - for _, node := range d.nodes { - addOCR3Capability(ctx, d.t, d.lggr, node.registry, d.fakeLibOcr, d.config.F, node.KeyBundle) - } - } - - for _, capabilityJob := range d.standardCapabilityJobs { - 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 -` - -func (d *DON) AddStandardCapability(name string, command string, config string) { - spec := fmt.Sprintf(StandardCapabilityTemplateJobSpec, name, command, config) - capabilitiesSpecJob, err := standardcapabilities.ValidatedStandardCapabilitiesSpec(spec) - require.NoError(d.t, err) - - 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) - - // Arguably this should be a parameter to AddExternalTriggerCapability, but for now we're just using the default - // See TODO about local/remote exposure - defaultTriggerCapabilityConfig := newCapabilityConfig() - defaultTriggerCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTriggerConfig{ - RemoteTriggerConfig: &pb.RemoteTriggerConfig{ - RegistrationRefresh: durationpb.New(1000 * time.Millisecond), - RegistrationExpiry: durationpb.New(60000 * time.Millisecond), - // F + 1 - MinResponsesToAggregate: uint32(d.config.F) + 1, - }, - } - - triggerCapability := capability{ - donCapabilityConfig: defaultTriggerCapabilityConfig, - registryConfig: kcr.CapabilitiesRegistryCapability{ - LabelledName: triggerFactory.GetTriggerName(), - Version: triggerFactory.GetTriggerVersion(), - CapabilityType: uint8(registrysyncer.ContractCapabilityTypeTrigger), - }, - } - - d.publishedCapabilities = append(d.publishedCapabilities, triggerCapability) -} - -func (d *DON) AddExternalTargetCapability(targetFactory TargetFactory, defaultTargetCapabilityConfig *pb.CapabilityConfig) { - d.targetFactories = append(d.targetFactories, targetFactory) - - if defaultTargetCapabilityConfig == nil { - defaultTargetCapabilityConfig = newCapabilityConfig() - } - defaultTargetCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTargetConfig{ - RemoteTargetConfig: &pb.RemoteTargetConfig{ - RequestHashExcludedAttributes: []string{}, - }, - } - - targetCapability := capability{ - donCapabilityConfig: defaultTargetCapabilityConfig, - registryConfig: kcr.CapabilitiesRegistryCapability{ - LabelledName: targetFactory.GetTargetName(), - Version: targetFactory.GetTargetVersion(), - CapabilityType: uint8(registrysyncer.ContractCapabilityTypeTarget), - }, - } - - d.publishedCapabilities = append(d.publishedCapabilities, targetCapability) -} - -func (d *DON) AddJob(ctx context.Context, j *job.Job) error { - for _, node := range d.nodes { - err := node.AddJobV2(ctx, j) - if err != nil { - return fmt.Errorf("failed to add job: %w", err) - } - } - - return nil -} - -func (d *DON) AddWorkflow(workflow Workflow) error { - if !d.config.AcceptsWorkflows { - return errors.New("cannot add workflow to non-workflow DON") - } - - if !d.initialised { - return errors.New("cannot add workflow to non-initialised DON") - } - - d.workflowRegistry.RegisterWorkflow(workflow, *d.id) - - return nil -} - -func (d *DON) UpdateWorkflow(workflow UpdatedWorkflow) error { - if !d.config.AcceptsWorkflows { - return errors.New("cannot add workflow to non-workflow DON") - } - - if !d.initialised { - return errors.New("cannot add workflow to non-initialised DON") - } - - d.workflowRegistry.UpdateWorkflow(workflow, *d.id) - - return nil -} - -func (d *DON) ComputeHashKey(owner string, field string) [32]byte { - return d.workflowRegistry.ComputeHashKey(owner, field) -} - -type TriggerFactory interface { - CreateNewTrigger(t *testing.T) commoncap.TriggerCapability - GetTriggerID() string - GetTriggerName() string - GetTriggerVersion() string -} - -type TargetFactory interface { - CreateNewTarget(t *testing.T) commoncap.ExecutableCapability - GetTargetID() string - GetTargetName() string - GetTargetVersion() string -} - -func startNewNode(ctx context.Context, - t *testing.T, lggr logger.Logger, nodeInfo commoncap.Node, - ethBlockchain *EthBlockchain, capRegistryAddr common.Address, - dispatcher remotetypes.Dispatcher, - peerWrapper p2ptypes.PeerWrapper, - localCapabilities *capabilities.Registry, - newOracleFactoryFn standardcapabilities.NewOracleFactoryFn, - keyV2 ethkey.KeyV2, - setupCfg func(c *chainlink.Config), - fetcherFunc wftypes.FetcherFunc, - fetcherFactoryFunc compute.FetcherFactory, -) *cltest.TestApplication { - config, _ := heavyweight.FullTestDBV2(t, func(c *chainlink.Config, s *chainlink.Secrets) { - c.Capabilities.ExternalRegistry.ChainID = new(fmt.Sprintf("%d", testutils.SimulatedChainID)) - c.Capabilities.ExternalRegistry.Address = new(capRegistryAddr.String()) - c.Capabilities.Peering.V2.Enabled = new(true) - c.Capabilities.WorkflowRegistry.SyncStrategy = new(syncer.SyncStrategyReconciliation) - c.Feature.FeedsManager = new(false) - c.Feature.LogPoller = new(true) - c.CRE.UseLocalTimeProvider = new(true) - c.CRE.EnableDKGRecipient = new(true) - - c.P2P.V2.DeltaDial = commonconfig.MustNewDuration(100 * time.Millisecond) - c.P2P.V2.DeltaReconcile = commonconfig.MustNewDuration(100 * time.Millisecond) - c.EVM[0].LogPollInterval = commonconfig.MustNewDuration(500 * time.Millisecond) - - if setupCfg != nil { - setupCfg(c) - } - }) - - n, err := ethBlockchain.Client().NonceAt(ctx, ethBlockchain.transactionOpts.From, nil) - require.NoError(t, err) - - tx := evmtestutils.NewLegacyTransaction( - n, keyV2.Address, - assets.Ether(1).ToInt(), - 21000, - assets.GWei(1).ToInt(), - nil) - signedTx, err := ethBlockchain.transactionOpts.Signer(ethBlockchain.transactionOpts.From, tx) - require.NoError(t, err) - err = ethBlockchain.Client().SendTransaction(ctx, signedTx) - require.NoError(t, err) - ethBlockchain.Commit() - - return cltest.NewApplicationWithConfigV2AndKeyOnSimulatedBlockchain(t, config, ethBlockchain.Backend, - nodeInfo, dispatcher, peerWrapper, newOracleFactoryFn, localCapabilities, keyV2, lggr, fetcherFunc, - fetcherFactoryFunc, NewFakeBillingClient()) -} - -// Functions below this point are for adding non-standard capabilities to a DON, deliberately verbose. Eventually these -// should be replaced with standard capabilities. - -func (d *DON) AddOCR3NonStandardCapability() { - d.addOCR3NonStandardCapability = true - - ocr := kcr.CapabilitiesRegistryCapability{ - LabelledName: "offchain_reporting", - Version: "1.0.0", - CapabilityType: uint8(registrysyncer.ContractCapabilityTypeConsensus), - } - - d.publishedCapabilities = append(d.publishedCapabilities, capability{ - donCapabilityConfig: newCapabilityConfig(), - registryConfig: ocr, - internalOnly: true, - }) -} - -func (d *DON) AddPublishedEthereumWriteTargetNonStandardCapability(forwarderAddr common.Address) (string, error) { - published := true - - capabilityID, s, err := d.addEthereumWriteTarget(forwarderAddr, published) - if err != nil { - return s, err - } - - return capabilityID, nil -} - -func (d *DON) AddEthereumWriteTargetNonStandardCapability(forwarderAddr common.Address) (string, error) { - published := false - - capabilityID, s, err := d.addEthereumWriteTarget(forwarderAddr, published) - if err != nil { - return s, err - } - - return capabilityID, nil -} - -func (d *DON) addEthereumWriteTarget(forwarderAddr common.Address, published bool) (string, string, error) { - d.nodeConfigModifiers = append(d.nodeConfigModifiers, func(c *chainlink.Config, node *capabilityNode) { - eip55Address := types.EIP55AddressFromAddress(forwarderAddr) - c.EVM[0].Workflow.ForwarderAddress = &eip55Address - c.EVM[0].Workflow.FromAddress = &node.key.EIP55Address - }) - - labelledName := "write_geth-testnet" - version := "1.0.0" - - writeChain := kcr.CapabilitiesRegistryCapability{ - LabelledName: labelledName, - Version: version, - CapabilityType: uint8(registrysyncer.ContractCapabilityTypeTarget), - } - - capabilityID := fmt.Sprintf("%s@%s", labelledName, version) - - targetCapabilityConfig := newCapabilityConfig() - - configWithLimit, err := values.WrapMap(map[string]any{"gasLimit": 500000}) - if err != nil { - return "", "", fmt.Errorf("failed to wrap map: %w", err) - } - - targetCapabilityConfig.DefaultConfig = values.Proto(configWithLimit).GetMapValue() - - targetCapabilityConfig.RemoteConfig = &pb.CapabilityConfig_RemoteTargetConfig{ - RemoteTargetConfig: &pb.RemoteTargetConfig{ - RequestHashExcludedAttributes: []string{"signed_report.Signatures"}, - }, - } - - if published { - d.publishedCapabilities = append(d.publishedCapabilities, capability{ - donCapabilityConfig: targetCapabilityConfig, - registryConfig: writeChain, - }) - } - return capabilityID, "", nil -} - -func addOCR3Capability(ctx context.Context, t *testing.T, lggr logger.Logger, capabilityRegistry *capabilities.Registry, - libocr *FakeLibOCR, donF uint8, ocr2KeyBundle ocr2key.KeyBundle) { - requestTimeout := 10 * time.Minute - cfg := ocr3.Config{ - Logger: lggr, - EncoderFactory: capabilities.NewEncoder, - AggregatorFactory: capabilities.NewAggregator, - RequestTimeout: &requestTimeout, - } - - ocr3Capability := ocr3.NewOCR3(cfg) - servicetest.Run(t, ocr3Capability) - - pluginCfg := coretypes.ReportingPluginServiceConfig{} - pluginFactory, err := ocr3Capability.NewReportingPluginFactory(ctx, pluginCfg, nil, - nil, nil, nil, capabilityRegistry, nil, nil) - require.NoError(t, err) - - repConfig := ocr3types.ReportingPluginConfig{ - F: int(donF), - } - plugin, _, err := pluginFactory.NewReportingPlugin(ctx, repConfig) - require.NoError(t, err) - - transmitter := ocr3.NewContractTransmitter(lggr, capabilityRegistry, "") - - libocr.AddNode(plugin, transmitter, ocr2KeyBundle) -} diff --git a/core/capabilities/integration_tests/framework/don_configuration.go b/core/capabilities/integration_tests/framework/don_configuration.go deleted file mode 100644 index 7d1bfe15156..00000000000 --- a/core/capabilities/integration_tests/framework/don_configuration.go +++ /dev/null @@ -1,64 +0,0 @@ -package framework - -import ( - "errors" - "fmt" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ethkey" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey" - commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -type DonConfiguration struct { - commoncap.DON - name string - keys []ethkey.KeyV2 - KeyBundles []ocr2key.KeyBundle - p2pKeys []p2pkey.KeyV2 -} - -// NewDonConfigurationParams exists purely to make it obvious in the test code what DON configuration is being used -type NewDonConfigurationParams struct { - Name string - NumNodes int - F uint8 - AcceptsWorkflows bool -} - -func NewDonConfiguration(don NewDonConfigurationParams) (DonConfiguration, error) { - if don.NumNodes < int(3*don.F+1) { - return DonConfiguration{}, errors.New("invalid configuration, number of nodes must be at least 3*F+1") - } - - keyBundles, p2pKeys, err := getKeyBundlesAndP2PKeys(don.Name, don.NumNodes) - if err != nil { - return DonConfiguration{}, fmt.Errorf("failed to get key bundles and p2p keys: %w", err) - } - - donPeers := make([]p2ptypes.PeerID, len(p2pKeys)) - var donKeys []ethkey.KeyV2 - for i := range p2pKeys { - donPeers[i] = p2ptypes.PeerID(p2pKeys[i].PeerID()) - newKey, err := ethkey.NewV2() - if err != nil { - return DonConfiguration{}, fmt.Errorf("failed to create key: %w", err) - } - donKeys = append(donKeys, newKey) - } - - donConfiguration := DonConfiguration{ - DON: commoncap.DON{ - Members: donPeers, - F: don.F, - ConfigVersion: 1, - AcceptsWorkflows: don.AcceptsWorkflows, - }, - name: don.Name, - p2pKeys: p2pKeys, - keys: donKeys, - KeyBundles: keyBundles, - } - return donConfiguration, nil -} diff --git a/core/capabilities/integration_tests/framework/ethereum.go b/core/capabilities/integration_tests/framework/ethereum.go deleted file mode 100644 index 9bfde22b5a3..00000000000 --- a/core/capabilities/integration_tests/framework/ethereum.go +++ /dev/null @@ -1,76 +0,0 @@ -package framework - -import ( - "context" - "os" - "sync" - "testing" - "time" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/ethconfig" - gethlog "github.com/ethereum/go-ethereum/log" - - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-evm/pkg/assets" - "github.com/smartcontractkit/chainlink-evm/pkg/testutils" - evmtypes "github.com/smartcontractkit/chainlink-evm/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/internal/cltest" -) - -type EthBlockchain struct { - services.StateMachine - evmtypes.Backend - transactionOpts *bind.TransactOpts - - blockTimeProcessingTime time.Duration - - stopCh services.StopChan - wg sync.WaitGroup -} - -func NewEthBlockchain(t *testing.T, initialEth int, blockTimeProcessingTime time.Duration) *EthBlockchain { - transactOpts := testutils.MustNewSimTransactor(t) // config contract deployer and owner - genesisData := types.GenesisAlloc{transactOpts.From: {Balance: assets.Ether(initialEth).ToInt()}} - backend := cltest.NewSimulatedBackend(t, genesisData, ethconfig.Defaults.Miner.GasCeil) - gethlog.SetDefault(gethlog.NewLogger(gethlog.NewTerminalHandlerWithLevel(os.Stderr, gethlog.LevelWarn, true))) - backend.Commit() - - return &EthBlockchain{Backend: backend, stopCh: make(services.StopChan), - blockTimeProcessingTime: blockTimeProcessingTime, transactionOpts: transactOpts} -} - -func (b *EthBlockchain) Start(ctx context.Context) error { - return b.StartOnce("EthBlockchain", func() error { - b.wg.Go(func() { - ticker := time.NewTicker(b.blockTimeProcessingTime) - defer ticker.Stop() - - for { - select { - case <-b.stopCh: - return - case <-ctx.Done(): - return - case <-ticker.C: - b.Commit() - } - } - }) - - return nil - }) -} - -func (b *EthBlockchain) Close() error { - return b.StopOnce("EthBlockchain", func() error { - close(b.stopCh) - b.wg.Wait() - return nil - }) -} - -func (b *EthBlockchain) TransactionOpts() *bind.TransactOpts { - return b.transactionOpts -} diff --git a/core/capabilities/integration_tests/framework/fake_billing_client.go b/core/capabilities/integration_tests/framework/fake_billing_client.go deleted file mode 100644 index b34f521f098..00000000000 --- a/core/capabilities/integration_tests/framework/fake_billing_client.go +++ /dev/null @@ -1,33 +0,0 @@ -package framework - -import ( - "context" - - "google.golang.org/protobuf/types/known/emptypb" - - billing "github.com/smartcontractkit/chainlink-protos/billing/go" - "github.com/smartcontractkit/chainlink/v2/core/services/workflows/metering" -) - -type fakeBillingClient struct { -} - -func NewFakeBillingClient() metering.BillingClient { - return &fakeBillingClient{} -} - -func (f fakeBillingClient) GetOrganizationCreditsByWorkflow(ctx context.Context, req *billing.GetOrganizationCreditsByWorkflowRequest) (*billing.GetOrganizationCreditsByWorkflowResponse, error) { - return &billing.GetOrganizationCreditsByWorkflowResponse{OrganizationId: "", Credits: &billing.OrganizationCredits{CreditsReserved: "", Credits: ""}}, nil -} - -func (f fakeBillingClient) GetWorkflowExecutionRates(context.Context, *billing.GetWorkflowExecutionRatesRequest) (*billing.GetWorkflowExecutionRatesResponse, error) { - return &billing.GetWorkflowExecutionRatesResponse{RateCards: []*billing.RateCard{{ResourceType: billing.ResourceType_RESOURCE_TYPE_COMPUTE, MeasurementUnit: billing.MeasurementUnit_MEASUREMENT_UNIT_MILLISECONDS, UnitsPerCredit: "0.0001"}}}, nil -} - -func (f fakeBillingClient) SubmitWorkflowReceipt(ctx context.Context, request *billing.SubmitWorkflowReceiptRequest) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil -} - -func (f fakeBillingClient) ReserveCredits(ctx context.Context, request *billing.ReserveCreditsRequest) (*billing.ReserveCreditsResponse, error) { - return &billing.ReserveCreditsResponse{Success: true, RateCards: []*billing.RateCard{{ResourceType: billing.ResourceType_RESOURCE_TYPE_COMPUTE, MeasurementUnit: billing.MeasurementUnit_MEASUREMENT_UNIT_MILLISECONDS, UnitsPerCredit: "0.0001"}}, Credits: "10000"}, nil -} diff --git a/core/capabilities/integration_tests/framework/fake_dispatcher.go b/core/capabilities/integration_tests/framework/fake_dispatcher.go deleted file mode 100644 index b21db792b4e..00000000000 --- a/core/capabilities/integration_tests/framework/fake_dispatcher.go +++ /dev/null @@ -1,274 +0,0 @@ -package framework - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "maps" - "sync" - "testing" - "time" - - "github.com/smartcontractkit/libocr/ragep2p/types" - "google.golang.org/protobuf/proto" - - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" - remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -// FakeRageP2PNetwork backs the dispatchers created for each node in the test and effectively -// acts as the rageP2P network layer. -type FakeRageP2PNetwork struct { - services.StateMachine - t *testing.T - readyError error - - chanBufferSize int - stopCh services.StopChan - wg sync.WaitGroup - - peerIDToBrokerNode map[p2ptypes.PeerID]*brokerNode - - capabilityRegistrations map[CapabilityRegistration]bool - - mux sync.Mutex -} - -func NewFakeRageP2PNetwork(ctx context.Context, t *testing.T, chanBufferSize int) *FakeRageP2PNetwork { - network := &FakeRageP2PNetwork{ - t: t, - stopCh: make(services.StopChan), - chanBufferSize: chanBufferSize, - peerIDToBrokerNode: make(map[p2ptypes.PeerID]*brokerNode), - capabilityRegistrations: make(map[CapabilityRegistration]bool), - } - - go func() { - <-ctx.Done() - network.SetReadyError(errors.New("context done")) - }() - - return network -} - -func (a *FakeRageP2PNetwork) Start(ctx context.Context) error { - return a.StartOnce("FakeRageP2PNetwork", func() error { - return nil - }) -} - -func (a *FakeRageP2PNetwork) Close() error { - return a.StopOnce("FakeRageP2PNetwork", func() error { - close(a.stopCh) - a.wg.Wait() - return nil - }) -} - -func (a *FakeRageP2PNetwork) Ready() error { - a.mux.Lock() - defer a.mux.Unlock() - return a.readyError -} - -func (a *FakeRageP2PNetwork) SetReadyError(err error) { - a.mux.Lock() - defer a.mux.Unlock() - a.readyError = err -} - -// NewDispatcherForNode creates a new dispatcher for a node with the given peer ID. -func (a *FakeRageP2PNetwork) NewDispatcherForNode(nodePeerID p2ptypes.PeerID) remotetypes.Dispatcher { - return &brokerDispatcher{ - callerPeerID: nodePeerID, - broker: a, - receivers: map[key]remotetypes.Receiver{}, - } -} - -func (a *FakeRageP2PNetwork) HealthReport() map[string]error { - return nil -} - -func (a *FakeRageP2PNetwork) Name() string { - return "FakeRageP2PNetwork" -} - -type CapabilityRegistration struct { - nodePeerID string - capabilityID string - capabilityDonID uint32 -} - -func (a *FakeRageP2PNetwork) GetCapabilityRegistrations() map[CapabilityRegistration]bool { - a.mux.Lock() - defer a.mux.Unlock() - - copiedRegistrations := make(map[CapabilityRegistration]bool) - maps.Copy(copiedRegistrations, a.capabilityRegistrations) - return copiedRegistrations -} - -func (a *FakeRageP2PNetwork) registerReceiverNode(nodePeerID p2ptypes.PeerID, capabilityID string, capabilityDonID uint32, receiver remotetypes.Receiver) { - a.mux.Lock() - defer a.mux.Unlock() - - a.capabilityRegistrations[CapabilityRegistration{ - nodePeerID: hex.EncodeToString(nodePeerID[:]), - capabilityID: capabilityID, - capabilityDonID: capabilityDonID, - }] = true - - node, ok := a.peerIDToBrokerNode[nodePeerID] - if !ok { - node = a.newNode() - a.peerIDToBrokerNode[nodePeerID] = node - } - - node.registerReceiverCh <- ®isterReceiverRequest{ - receiverKey: receiverKey{ - capabilityID: capabilityID, - donID: capabilityDonID, - }, - receiver: receiver, - } -} - -func (a *FakeRageP2PNetwork) Send(msg *remotetypes.MessageBody) { - peerID := toPeerID(msg.Receiver) - - node, ok := a.getNodeForPeerID(peerID) - if !ok { - panic(fmt.Sprintf("node not found for peer ID %v", peerID)) - } - - node.receiveCh <- msg -} - -func (a *FakeRageP2PNetwork) getNodeForPeerID(peerID types.PeerID) (*brokerNode, bool) { - a.mux.Lock() - defer a.mux.Unlock() - node, ok := a.peerIDToBrokerNode[peerID] - return node, ok -} - -type brokerNode struct { - registerReceiverCh chan *registerReceiverRequest - receiveCh chan *remotetypes.MessageBody -} - -type receiverKey struct { - capabilityID string - donID uint32 -} - -type registerReceiverRequest struct { - receiverKey - receiver remotetypes.Receiver -} - -func (a *FakeRageP2PNetwork) newNode() *brokerNode { - n := &brokerNode{ - receiveCh: make(chan *remotetypes.MessageBody, a.chanBufferSize), - registerReceiverCh: make(chan *registerReceiverRequest, a.chanBufferSize), - } - - a.wg.Go(func() { - receivers := make(map[receiverKey]remotetypes.Receiver) - for { - select { - case <-a.stopCh: - return - case msg := <-n.receiveCh: - k := receiverKey{ - capabilityID: msg.CapabilityId, - donID: msg.CapabilityDonId, - } - - r, ok := receivers[k] - if !ok { - panic(fmt.Sprintf("receiver not found for key %+v", k)) - } - - r.Receive(a.t.Context(), msg) - case reg := <-n.registerReceiverCh: - receivers[reg.receiverKey] = reg.receiver - } - } - }) - return n -} - -func toPeerID(id []byte) p2ptypes.PeerID { - return [32]byte(id) -} - -type broker interface { - Send(msg *remotetypes.MessageBody) - Ready() error -} - -type brokerDispatcher struct { - callerPeerID p2ptypes.PeerID - broker broker - - receivers map[key]remotetypes.Receiver - mu sync.Mutex -} - -type key struct { - capID string - donID uint32 -} - -func (t *brokerDispatcher) Send(peerID p2ptypes.PeerID, msgBody *remotetypes.MessageBody) error { - clonedMsg := proto.Clone(msgBody).(*remotetypes.MessageBody) - clonedMsg.Version = 1 - clonedMsg.Sender = t.callerPeerID[:] - clonedMsg.Receiver = peerID[:] - clonedMsg.Timestamp = time.Now().UnixMilli() - t.broker.Send(clonedMsg) - return nil -} - -func (t *brokerDispatcher) SetReceiver(capabilityID string, donID uint32, receiver remotetypes.Receiver) error { - t.mu.Lock() - defer t.mu.Unlock() - k := key{capID: capabilityID, donID: donID} - _, ok := t.receivers[k] - if ok { - return fmt.Errorf("%w: receiver already exists for capability %s and don %d", remote.ErrReceiverExists, capabilityID, donID) - } - t.receivers[k] = receiver - - t.broker.(*FakeRageP2PNetwork).registerReceiverNode(t.callerPeerID, capabilityID, donID, receiver) - return nil -} -func (t *brokerDispatcher) RemoveReceiver(capabilityID string, donID uint32) {} - -func (t *brokerDispatcher) SetReceiverForMethod(capabilityID string, donID uint32, method string, receiver remotetypes.Receiver) error { - return errors.New("not implemented") -} -func (t *brokerDispatcher) RemoveReceiverForMethod(capabilityID string, donID uint32, method string) { -} - -func (t *brokerDispatcher) Start(context.Context) error { return nil } - -func (t *brokerDispatcher) Close() error { - return nil -} - -func (t *brokerDispatcher) Ready() error { - return t.broker.Ready() -} - -func (t *brokerDispatcher) HealthReport() map[string]error { - return nil -} - -func (t *brokerDispatcher) Name() string { - return "fakeDispatcher" -} diff --git a/core/capabilities/integration_tests/framework/fake_libocr.go b/core/capabilities/integration_tests/framework/fake_libocr.go deleted file mode 100644 index 583fa462bd1..00000000000 --- a/core/capabilities/integration_tests/framework/fake_libocr.go +++ /dev/null @@ -1,336 +0,0 @@ -package framework - -import ( - "bytes" - "context" - "fmt" - "math/rand" - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/smartcontractkit/libocr/commontypes" - "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/pkg/services" - coretypes "github.com/smartcontractkit/chainlink-common/pkg/types/core" - "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/generic" -) - -type oracleContext struct { - t *testing.T - lggr logger.Logger - key ocr2key.KeyBundle - N int - F uint8 - protocolRoundInterval time.Duration - mux sync.Mutex - pluginNameToFakeOcr map[string]*FakeLibOCR -} - -func (m *oracleContext) addPlugin(ctx context.Context, info ocr3types.ReportingPluginInfo, plugin ocr3types.ReportingPlugin[[]byte], - args coretypes.OracleArgs) error { - m.mux.Lock() - defer m.mux.Unlock() - - libOcr := m.pluginNameToFakeOcr[info.Name] - if libOcr == nil { - libOcr = NewFakeLibOCR(m.t, m.lggr, m.F, m.protocolRoundInterval) - m.pluginNameToFakeOcr[info.Name] = libOcr - } - - libOcr.AddNode(plugin, args.ContractTransmitter, m.key) - - if libOcr.GetNodeCount() == m.N { - err := libOcr.Start(ctx) - if err != nil { - return fmt.Errorf("failed to start fake lib ocr: %w", err) - } - } - return nil -} - -func (m *oracleContext) Close() error { - m.mux.Lock() - defer m.mux.Unlock() - - for _, libOcr := range m.pluginNameToFakeOcr { - if err := libOcr.Close(); err != nil { - return fmt.Errorf("failed to close fake lib ocr: %w", err) - } - } - return nil -} - -type oracleFactoryFactory struct { - oracleContext *oracleContext -} - -func newFakeOracleFactoryFactory(t *testing.T, lggr logger.Logger, key ocr2key.KeyBundle, n int, f uint8, protocolRoundInterval time.Duration) *oracleFactoryFactory { - return &oracleFactoryFactory{ - oracleContext: &oracleContext{ - t: t, - lggr: lggr, - key: key, - N: n, - F: f, - protocolRoundInterval: protocolRoundInterval, - pluginNameToFakeOcr: make(map[string]*FakeLibOCR), - }, - } -} - -func (o *oracleFactoryFactory) NewOracleFactory(params generic.OracleFactoryParams) (coretypes.OracleFactory, error) { - return &fakeOracleFactory{o.oracleContext}, nil -} - -type fakeOracleFactory struct { - oracleContext *oracleContext -} - -func (m *fakeOracleFactory) NewOracle(ctx context.Context, args coretypes.OracleArgs) (coretypes.Oracle, error) { - return &fakeOracle{oracleContext: m.oracleContext, args: args}, nil -} - -type fakeOracle struct { - oracleContext *oracleContext - args coretypes.OracleArgs -} - -func (m *fakeOracle) Start(ctx context.Context) error { - plugin, info, err := m.args.ReportingPluginFactoryService.NewReportingPlugin(ctx, ocr3types.ReportingPluginConfig{ - F: int(m.oracleContext.F), - N: m.oracleContext.N, - }) - - if err != nil { - return fmt.Errorf("failed to create reporting plugin: %w", err) - } - - if err = m.oracleContext.addPlugin(ctx, info, plugin, m.args); err != nil { - return fmt.Errorf("failed to add plugin: %w", err) - } - - return nil -} - -func (m *fakeOracle) Close(ctx context.Context) error { - return m.oracleContext.Close() -} - -type libocrNode struct { - id string - ocr3types.ReportingPlugin[[]byte] - ocr3types.ContractTransmitter[[]byte] - key ocr2key.KeyBundle -} - -// FakeLibOCR is a fake libocr implementation for testing purposes that simulates libocr protocol rounds without having -// to setup the libocr network -type FakeLibOCR struct { - services.StateMachine - t *testing.T - lggr logger.Logger - - nodes []*libocrNode - f uint8 - protocolRoundInterval time.Duration - - seqNr uint64 - outcomeCtx ocr3types.OutcomeContext - - mux sync.Mutex - stopCh services.StopChan - wg sync.WaitGroup -} - -func NewFakeLibOCR(t *testing.T, lggr logger.Logger, f uint8, protocolRoundInterval time.Duration) *FakeLibOCR { - return &FakeLibOCR{ - t: t, - lggr: lggr, - f: f, outcomeCtx: ocr3types.OutcomeContext{ - SeqNr: 1, - PreviousOutcome: nil, - Epoch: 0, - Round: 0, - }, - protocolRoundInterval: protocolRoundInterval, - stopCh: make(services.StopChan), - } -} - -func (m *FakeLibOCR) Start(ctx context.Context) error { - return m.StartOnce("FakeLibOCR", func() error { - m.wg.Go(func() { - ticker := time.NewTicker(m.protocolRoundInterval) - defer ticker.Stop() - - for { - select { - case <-m.stopCh: - return - case <-ticker.C: - serviceCtx, cancel := m.stopCh.NewCtx() - err := m.simulateProtocolRound(serviceCtx) - cancel() - if err != nil { - m.lggr.Errorf("simulating protocol round: %v", err) - } - } - } - }) - return nil - }) -} - -func (m *FakeLibOCR) Close() error { - return m.StopOnce("FakeLibOCR", func() error { - close(m.stopCh) - m.wg.Wait() - return nil - }) -} - -func (m *FakeLibOCR) AddNode(plugin ocr3types.ReportingPlugin[[]byte], transmitter ocr3types.ContractTransmitter[[]byte], key ocr2key.KeyBundle) string { - m.mux.Lock() - defer m.mux.Unlock() - node := &libocrNode{uuid.New().String(), plugin, transmitter, key} - m.nodes = append(m.nodes, node) - return node.id -} - -func (m *FakeLibOCR) GetNodeCount() int { - m.mux.Lock() - defer m.mux.Unlock() - return len(m.nodes) -} - -func (m *FakeLibOCR) RemoveNode(id string) { - m.mux.Lock() - defer m.mux.Unlock() - - var updatedNodes []*libocrNode - for _, node := range m.nodes { - if node.id != id { - updatedNodes = append(updatedNodes, node) - } - } - - m.nodes = updatedNodes -} - -func (m *FakeLibOCR) simulateProtocolRound(ctx context.Context) error { - m.mux.Lock() - defer m.mux.Unlock() - if len(m.nodes) == 0 { - return nil - } - - // randomly select a leader - leader := m.nodes[rand.Intn(len(m.nodes))] - - // get the query - query, err := leader.Query(ctx, m.outcomeCtx) - if err != nil { - return fmt.Errorf("failed to get query: %w", err) - } - - var observations []types.AttributedObservation - for oracleID, node := range m.nodes { - obs, err2 := node.Observation(ctx, m.outcomeCtx, query) - if err2 != nil { - return fmt.Errorf("failed to get observation: %w", err2) - } - - observations = append(observations, types.AttributedObservation{ - Observation: obs, - Observer: commontypes.OracleID(oracleID), - }) - } - - var outcomes []ocr3types.Outcome - for _, node := range m.nodes { - outcome, err2 := node.Outcome(ctx, m.outcomeCtx, query, observations) - if err2 != nil { - return fmt.Errorf("failed to get outcome: %w", err2) - } - - if len(outcome) == 0 { - return nil // wait until all nodes have an outcome for testing purposes - } - - outcomes = append(outcomes, outcome) - } - - // if all outcomes are equal proceed to reports - for _, outcome := range outcomes { - if !bytes.Equal(outcome, outcomes[0]) { - return nil - } - } - - reports, err := leader.Reports(ctx, 0, outcomes[0]) - if err != nil { - return fmt.Errorf("failed to get reports: %w", err) - } - for _, report := range reports { - // create signatures - var signatures []types.AttributedOnchainSignature - for i, node := range m.nodes { - sig, err := node.key.Sign(types.ReportContext{}, report.ReportWithInfo.Report) - if err != nil { - return fmt.Errorf("failed to sign report: %w", err) - } - - signatures = append(signatures, types.AttributedOnchainSignature{ - Signer: commontypes.OracleID(i), - Signature: sig, - }) - } - - for _, node := range m.nodes { - accept, err := node.ShouldAcceptAttestedReport(ctx, m.seqNr, report.ReportWithInfo) - if err != nil { - return fmt.Errorf("failed to check if report should be accepted: %w", err) - } - if !accept { - continue - } - - transmit, err := node.ShouldTransmitAcceptedReport(ctx, m.seqNr, report.ReportWithInfo) - if err != nil { - return fmt.Errorf("failed to check if report should be transmitted: %w", err) - } - - if !transmit { - continue - } - - // For each node select a random set of F+1 signatures to mimic libocr behaviour - s := rand.NewSource(time.Now().UnixNano()) - r := rand.New(s) - indices := r.Perm(len(signatures)) - selectedSignatures := make([]types.AttributedOnchainSignature, m.f+1) - for i := 0; i < int(m.f+1); i++ { - selectedSignatures[i] = signatures[indices[i]] - } - - err = node.Transmit(ctx, types.ConfigDigest{}, 0, report.ReportWithInfo, selectedSignatures) - if err != nil { - return fmt.Errorf("failed to transmit report: %w", err) - } - } - - m.seqNr++ - m.outcomeCtx = ocr3types.OutcomeContext{ - SeqNr: 0, - PreviousOutcome: outcomes[0], - } - } - - return nil -} diff --git a/core/capabilities/integration_tests/framework/fake_target.go b/core/capabilities/integration_tests/framework/fake_target.go deleted file mode 100644 index ed73a90f2df..00000000000 --- a/core/capabilities/integration_tests/framework/fake_target.go +++ /dev/null @@ -1,93 +0,0 @@ -package framework - -import ( - "context" - "testing" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/services" -) - -var ( - _ capabilities.ExecutableCapability = &fakeTarget{} -) - -type TargetSink struct { - services.StateMachine - targetID string - targetName string - version string - - targets []fakeTarget - Sink chan capabilities.CapabilityRequest -} - -func NewTargetSink(targetName string, version string) *TargetSink { - return &TargetSink{ - targetID: targetName + "@" + version, - targetName: targetName, - version: version, - Sink: make(chan capabilities.CapabilityRequest, 1000), - } -} - -func (ts *TargetSink) GetTargetVersion() string { - return ts.version -} - -func (ts *TargetSink) GetTargetName() string { - return ts.targetName -} - -func (ts *TargetSink) GetTargetID() string { - return ts.targetID -} - -func (ts *TargetSink) Start(ctx context.Context) error { - return ts.StartOnce("TargetSinkService", func() error { - return nil - }) -} - -func (ts *TargetSink) Close() error { - return ts.StopOnce("TargetSinkService", func() error { - return nil - }) -} - -func (ts *TargetSink) CreateNewTarget(t *testing.T) capabilities.ExecutableCapability { - target := fakeTarget{ - t: t, - targetID: ts.targetID, - ch: ts.Sink, - } - ts.targets = append(ts.targets, target) - return &target -} - -type fakeTarget struct { - t *testing.T - targetID string - ch chan capabilities.CapabilityRequest -} - -func (mt *fakeTarget) Execute(ctx context.Context, rawRequest capabilities.CapabilityRequest) (capabilities.CapabilityResponse, error) { - mt.ch <- rawRequest - return capabilities.CapabilityResponse{}, nil -} - -func (mt *fakeTarget) Info(ctx context.Context) (capabilities.CapabilityInfo, error) { - return capabilities.MustNewCapabilityInfo( - mt.targetID, - capabilities.CapabilityTypeTarget, - "fake target for target ID "+mt.targetID, - ), nil -} - -func (mt *fakeTarget) RegisterToWorkflow(ctx context.Context, request capabilities.RegisterToWorkflowRequest) error { - return nil -} - -func (mt *fakeTarget) UnregisterFromWorkflow(ctx context.Context, request capabilities.UnregisterFromWorkflowRequest) error { - return nil -} diff --git a/core/capabilities/integration_tests/framework/fake_trigger.go b/core/capabilities/integration_tests/framework/fake_trigger.go deleted file mode 100644 index 901856476db..00000000000 --- a/core/capabilities/integration_tests/framework/fake_trigger.go +++ /dev/null @@ -1,154 +0,0 @@ -package framework - -import ( - "context" - "sync" - "testing" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/services/servicetest" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" -) - -// TriggerSink is a TriggerFactory implementation that sends output to all triggers that are created by it. -type TriggerSink struct { - services.StateMachine - triggerID string - triggerName string - version string - - triggers []fakeTrigger - - stopCh services.StopChan - wg sync.WaitGroup -} - -func NewTriggerSink(t *testing.T, triggerName string, version string) *TriggerSink { - triggersFactory := &TriggerSink{ - triggerID: triggerName + "@" + version, - triggerName: triggerName, - version: version, - stopCh: make(services.StopChan), - } - servicetest.Run(t, triggersFactory) - return triggersFactory -} - -func (r *TriggerSink) GetTriggerVersion() string { - return r.version -} - -func (r *TriggerSink) GetTriggerName() string { - return r.triggerName -} - -func (r *TriggerSink) GetTriggerID() string { - return r.triggerID -} - -func (r *TriggerSink) Start(ctx context.Context) error { - return r.StartOnce("TriggerSink", func() error { - return nil - }) -} - -func (r *TriggerSink) Close() error { - return r.StopOnce("TriggerSink", func() error { - close(r.stopCh) - r.wg.Wait() - return nil - }) -} - -// SendOutput wraps the given output in a TriggerEvent and sends it to all triggers created by this factory -func (r *TriggerSink) SendOutput(outputs *values.Map, eventID string) { - triggerEvent := capabilities.TriggerEvent{ - TriggerType: r.triggerID, - ID: eventID, - Outputs: outputs, - } - - resp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - for _, trigger := range r.triggers { - trigger.sendResponse(resp) - } -} - -func (r *TriggerSink) CreateNewTrigger(t *testing.T) capabilities.TriggerCapability { - trigger := newFakeTrigger(t, r.triggerID, &r.wg, r.stopCh) - r.triggers = append(r.triggers, trigger) - return &trigger -} - -type fakeTrigger struct { - t *testing.T - triggerID string - cancel context.CancelFunc - toSend chan capabilities.TriggerResponse - - wg *sync.WaitGroup - stopCh services.StopChan -} - -func newFakeTrigger(t *testing.T, triggerID string, wg *sync.WaitGroup, stopCh services.StopChan) fakeTrigger { - return fakeTrigger{ - t: t, - triggerID: triggerID, - toSend: make(chan capabilities.TriggerResponse, 1000), - wg: wg, - stopCh: stopCh, - } -} - -func (s *fakeTrigger) sendResponse(resp capabilities.TriggerResponse) { - s.toSend <- resp -} - -func (s *fakeTrigger) Info(ctx context.Context) (capabilities.CapabilityInfo, error) { - return capabilities.MustNewCapabilityInfo( - s.triggerID, - capabilities.CapabilityTypeTrigger, - "fake trigger for trigger id "+s.triggerID, - ), nil -} - -func (s *fakeTrigger) AckEvent(ctx context.Context, triggerID string, eventID string, method string) error { - return nil -} - -func (s *fakeTrigger) RegisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) (<-chan capabilities.TriggerResponse, error) { - if s.cancel != nil { - s.t.Fatal("trigger already registered") - } - - responseCh := make(chan capabilities.TriggerResponse) - - ctxWithCancel, cancel := s.stopCh.NewCtx() - s.cancel = cancel - s.wg.Go(func() { - for { - select { - case <-ctxWithCancel.Done(): - return - case resp := <-s.toSend: - responseCh <- resp - } - } - }) - - return responseCh, nil -} - -func (s *fakeTrigger) UnregisterTrigger(ctx context.Context, request capabilities.TriggerRegistrationRequest) error { - if s.cancel == nil { - s.t.Fatal("trigger not registered") - } - - s.cancel() - s.cancel = nil - return nil -} diff --git a/core/capabilities/integration_tests/framework/peer.go b/core/capabilities/integration_tests/framework/peer.go deleted file mode 100644 index 84747e92889..00000000000 --- a/core/capabilities/integration_tests/framework/peer.go +++ /dev/null @@ -1,140 +0,0 @@ -package framework - -import ( - "context" - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/p2pkey" - kcr "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/capabilities_registry_1_1_0" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -type peerIDAndOCRSigner struct { - PeerID p2ptypes.PeerID - Signer string -} - -func peerToNode(nopID uint32, p peerIDAndOCRSigner) (kcr.CapabilitiesRegistryNodeParams, error) { - sig := strings.TrimPrefix(p.Signer, "0x") - signerB, err := hex.DecodeString(sig) - if err != nil { - return kcr.CapabilitiesRegistryNodeParams{}, fmt.Errorf("failed to convert signer: %w", err) - } - - var sigb [32]byte - copy(sigb[:], signerB) - - return kcr.CapabilitiesRegistryNodeParams{ - NodeOperatorId: nopID, - P2pId: p.PeerID, - Signer: sigb, - EncryptionPublicKey: testutils.Random32Byte(), - }, nil -} - -func getKeyBundlesAndP2PKeys(donName string, numNodes int) ([]ocr2key.KeyBundle, []p2pkey.KeyV2, error) { - var keyBundles []ocr2key.KeyBundle - var donPeerKeys []p2pkey.KeyV2 - for range numNodes { - key, err := p2pkey.NewV2() - if err != nil { - return nil, nil, fmt.Errorf("failed to create p2p key: %w", err) - } - - keyBundle, err := ocr2key.New(corekeys.EVM) - if err != nil { - return nil, nil, fmt.Errorf("failed to create key bundle: %w", err) - } - - keyBundles = append(keyBundles, keyBundle) - - donPeerKeys = append(donPeerKeys, key) - } - return keyBundles, donPeerKeys, nil -} - -type peerWrapper struct { - peer p2pPeer -} - -func (t peerWrapper) Start(ctx context.Context) error { - return nil -} - -func (t peerWrapper) Close() error { - return nil -} - -func (t peerWrapper) Ready() error { - return nil -} - -func (t peerWrapper) HealthReport() map[string]error { - return nil -} - -func (t peerWrapper) Name() string { - return "peerWrapper" -} - -func (t peerWrapper) GetPeer() p2ptypes.Peer { - return t.peer -} - -type p2pPeer struct { - id p2ptypes.PeerID -} - -func (t p2pPeer) Start(ctx context.Context) error { - return nil -} - -func (t p2pPeer) Close() error { - return nil -} - -func (t p2pPeer) Ready() error { - return nil -} - -func (t p2pPeer) HealthReport() map[string]error { - return nil -} - -func (t p2pPeer) Name() string { - return "p2pPeer" -} - -func (t p2pPeer) ID() p2ptypes.PeerID { - return t.id -} - -func (t p2pPeer) UpdateConnections(peers map[p2ptypes.PeerID]p2ptypes.StreamConfig) error { - return nil -} - -func (t p2pPeer) Send(peerID p2ptypes.PeerID, msg []byte) error { - return nil -} - -func (t p2pPeer) Receive() <-chan p2ptypes.Message { - return nil -} - -func (t p2pPeer) IsBootstrap() bool { - return false -} - -func getSignerStringFromOCRKeyBundle(keyBundle ocr2key.KeyBundle) (string, error) { - if keyBundle == nil { - return "", errors.New("keyBundle is nil") - } - - return fmt.Sprintf("0x%x", keyBundle.PublicKey()), nil -} diff --git a/core/capabilities/integration_tests/framework/workflow_registry.go b/core/capabilities/integration_tests/framework/workflow_registry.go deleted file mode 100644 index abe7d44c821..00000000000 --- a/core/capabilities/integration_tests/framework/workflow_registry.go +++ /dev/null @@ -1,146 +0,0 @@ -package framework - -import ( - "context" - "testing" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/require" - - workflow_registry_wrapper "github.com/smartcontractkit/chainlink-evm/gethwrappers/workflow/generated/workflow_registry_wrapper_v1" -) - -type WorkflowRegistry struct { - t *testing.T - backend *EthBlockchain - contract *workflow_registry_wrapper.WorkflowRegistry - addr common.Address -} - -func NewWorkflowRegistry(ctx context.Context, t *testing.T, backend *EthBlockchain) *WorkflowRegistry { - // Deploy a test workflow_registry - wfRegistryAddr, _, wfRegistryC, err := workflow_registry_wrapper.DeployWorkflowRegistry(backend.transactionOpts, backend.Client()) - backend.Commit() - require.NoError(t, err) - - // setup contract state to allow the secrets to be updated - updateAuthorizedAddress(t, backend, wfRegistryC, []common.Address{backend.transactionOpts.From}, true) - - return &WorkflowRegistry{t: t, addr: wfRegistryAddr, contract: wfRegistryC, backend: backend} -} - -func (r *WorkflowRegistry) UpdateAllowedDons(donIDs []uint32) { - updateAllowedDONs(r.t, r.backend, r.contract, donIDs, true) -} - -func (r *WorkflowRegistry) RegisterWorkflow(input Workflow, donID uint32) { - registerWorkflow(r.t, r.backend, r.contract, input, donID) -} - -func (r *WorkflowRegistry) UpdateWorkflow(input UpdatedWorkflow, donID uint32) { - updateWorkflow(r.t, r.backend, r.contract, input) -} - -func (r *WorkflowRegistry) ComputeHashKey(owner string, field string) [32]byte { - return computeHashKey(r.t, r.contract, owner, field) -} - -func updateAuthorizedAddress( - t *testing.T, - th *EthBlockchain, - wfRegC *workflow_registry_wrapper.WorkflowRegistry, - addresses []common.Address, - _ bool, -) { - t.Helper() - _, err := wfRegC.UpdateAuthorizedAddresses(th.transactionOpts, addresses, true) - require.NoError(t, err, "failed to update authorised addresses") - th.Commit() - th.Commit() - th.Commit() - gotAddresses, err := wfRegC.GetAllAuthorizedAddresses(&bind.CallOpts{ - From: th.transactionOpts.From, - }) - require.NoError(t, err) - require.ElementsMatch(t, addresses, gotAddresses) -} - -func updateAllowedDONs( - t *testing.T, - th *EthBlockchain, - wfRegC *workflow_registry_wrapper.WorkflowRegistry, - donIDs []uint32, - allowed bool, -) { - t.Helper() - _, err := wfRegC.UpdateAllowedDONs(th.transactionOpts, donIDs, allowed) - require.NoError(t, err, "failed to update DONs") - th.Commit() - th.Commit() - th.Commit() - gotDons, err := wfRegC.GetAllAllowedDONs(&bind.CallOpts{ - From: th.transactionOpts.From, - }) - require.NoError(t, err) - require.ElementsMatch(t, donIDs, gotDons) -} - -type Workflow struct { - Name string - ID [32]byte - Status uint8 - BinaryURL string - ConfigURL string - SecretsURL string -} - -func registerWorkflow( - t *testing.T, - th *EthBlockchain, - wfRegC *workflow_registry_wrapper.WorkflowRegistry, - input Workflow, - donID uint32, -) { - t.Helper() - _, err := wfRegC.RegisterWorkflow(th.transactionOpts, input.Name, input.ID, donID, - input.Status, input.BinaryURL, input.ConfigURL, input.SecretsURL) - require.NoError(t, err, "failed to register workflow") - th.Commit() - th.Commit() - th.Commit() -} - -type UpdatedWorkflow struct { - WorkflowKey [32]byte - ID [32]byte - BinaryURL string - ConfigURL string - SecretsURL string -} - -func updateWorkflow( - t *testing.T, - th *EthBlockchain, - wfRegC *workflow_registry_wrapper.WorkflowRegistry, - input UpdatedWorkflow, -) { - t.Helper() - _, err := wfRegC.UpdateWorkflow(th.transactionOpts, input.WorkflowKey, input.ID, input.BinaryURL, input.ConfigURL, input.SecretsURL) - require.NoError(t, err, "failed to update workflow") - th.Commit() - th.Commit() - th.Commit() -} - -func computeHashKey( - t *testing.T, - wfRegC *workflow_registry_wrapper.WorkflowRegistry, - owner string, - field string, -) [32]byte { - t.Helper() - hashKey, err := wfRegC.ComputeHashKey(&bind.CallOpts{}, common.HexToAddress(owner), field) - require.NoError(t, err, "failed to compute hash key") - return hashKey -} diff --git a/core/capabilities/launcher.go b/core/capabilities/launcher.go index eecac5b60de..8820f838b08 100644 --- a/core/capabilities/launcher.go +++ b/core/capabilities/launcher.go @@ -5,17 +5,14 @@ import ( "errors" "fmt" "slices" - "strings" "sync" "time" - "github.com/Masterminds/semver/v3" "github.com/smartcontractkit/libocr/ragep2p" ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" "github.com/smartcontractkit/chainlink-common/pkg/capabilities" "github.com/smartcontractkit/chainlink-common/pkg/capabilities/registry" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/settings/cresettings" @@ -26,7 +23,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/aggregation" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/executable" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" "github.com/smartcontractkit/chainlink/v2/core/capabilities/transmission" "github.com/smartcontractkit/chainlink/v2/core/config" p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" @@ -453,53 +449,7 @@ func (w *launcher) addRemoteCapability(ctx context.Context, cid string, capabili switch capability.CapabilityType { case capabilities.CapabilityTypeTrigger: newTriggerFn := func(info capabilities.CapabilityInfo) (capabilityService, error) { - var aggregator remotetypes.Aggregator - switch { - case strings.HasPrefix(info.ID, "streams-trigger"): - v := info.ID[strings.LastIndexAny(info.ID, "@")+1:] // +1 to skip the @; also gracefully handle the case where there is no @ (which should not happen) - version, err := semver.NewVersion(v) - if err != nil { - return nil, fmt.Errorf("could not extract version from %s (%s): %w", info.ID, v, err) - } - switch version.Major() { - case 1: // legacy streams trigger - codec := streams.NewCodec(w.lggr) - - signers, err := signersFor(remoteDON, localRegistry) - if err != nil { - return nil, fmt.Errorf("failed to get signers for streams-trigger: %w", err) - } - - // deprecated pre-LLO Mercury aggregator - aggregator = triggers.NewMercuryRemoteAggregator( - codec, - signers, - int(remoteDON.F+1), - info.ID, - w.lggr, - ) - case 2: // LLO - // TODO: add a flag in capability onchain config to indicate whether it's OCR based - // the "SignedReport" aggregator is generic - signers, err := signersFor(remoteDON, localRegistry) - if err != nil { - return nil, fmt.Errorf("failed to get signers for llo-trigger: %w", err) - } - - const maxAgeSec = 120 // TODO move to capability onchain config - aggregator = aggregation.NewSignedReportRemoteAggregator( - signers, - int(remoteDON.F+1), - info.ID, - maxAgeSec, - w.lggr, - ) - default: - return nil, fmt.Errorf("unsupported stream trigger %s", info.ID) - } - default: - aggregator = aggregation.NewDefaultModeAggregator(uint32(remoteDON.F) + 1) - } + aggregator := aggregation.NewDefaultModeAggregator(uint32(remoteDON.F) + 1) shimKey := shimKey(capability.ID, remoteDON.ID, "") // empty method name for V1 triggerCap, alreadyExists := w.cachedShims.triggerSubscribers[shimKey] diff --git a/core/capabilities/remote/aggregation/signed_report.go b/core/capabilities/remote/aggregation/signed_report.go deleted file mode 100644 index 70b65eb4140..00000000000 --- a/core/capabilities/remote/aggregation/signed_report.go +++ /dev/null @@ -1,135 +0,0 @@ -package aggregation - -import ( - "errors" - "fmt" - "time" - - "google.golang.org/protobuf/proto" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2/types" -) - -var ( - ErrMalformedSigner = errors.New("malformed signer") - ErrMalformedConfig = errors.New("malformed config digest") - ErrInsufficientSignatures = errors.New("insufficient signatures") - ErrMissingResponse = errors.New("missing trigger response") -) - -type signedReportRemoteAggregator struct { - allowedSigners map[common.Address]struct{} - minRequiredSignatures int - maxAgeSec int - capID string - lggr logger.Logger -} - -func NewSignedReportRemoteAggregator(allowedSigners [][]byte, minRequiredSignatures int, capID string, maxAgeSec int, lggr logger.Logger) *signedReportRemoteAggregator { - signersMap := make(map[common.Address]struct{}) - for _, signer := range allowedSigners { - signersMap[common.BytesToAddress(signer)] = struct{}{} - } - lggr = logger.Named(lggr, "SignedReportRemoteAggregator") - - lggr.Infow("created", "allowedSigners", signersMap, "minRequiredSignatures", minRequiredSignatures, "maxAgeSec", maxAgeSec, "capID", capID) - return &signedReportRemoteAggregator{ - allowedSigners: signersMap, - minRequiredSignatures: minRequiredSignatures, - maxAgeSec: maxAgeSec, - capID: capID, - lggr: logger.Named(lggr, "SignedReportRemoteAggregator"), - } -} - -// Accept first response with valid signatures and expected event ID -// every element in responses is must be a [capabilitypb.TriggerResponse] -// and for each of them, we expect the [capabilitypb.TriggerResponse.Event.Outputs] field -// to be the [values.Map] representation a [capabilities.OCRTriggerEvent] (see [capabilities.OCRTriggerEvent.ToMap]) -func (a *signedReportRemoteAggregator) Aggregate(triggerEventID string, responses [][]byte) (capabilities.TriggerResponse, error) { - for _, response := range responses { - triggerResp, err := capabilitiespb.UnmarshalTriggerResponse(response) - if err != nil { - a.lggr.Errorw("could not unmarshal one of capability responses (faulty sender?)", "err", err) - continue - } - ocrEvent := &capabilities.OCRTriggerEvent{} - err = ocrEvent.FromMap(triggerResp.Event.Outputs) - if err != nil { - a.lggr.Errorw("trigger response does not contain an OCR report", "id", triggerResp.Event.ID, "err", err) - } - rawReport := ocrEvent.Report - rep := &capabilitiespb.OCRTriggerReport{} - err = proto.Unmarshal(rawReport, rep) - if err != nil { - a.lggr.Errorw("failed to parse OCR report", "id", triggerResp.Event.ID) - continue - } - - if rep.EventID != triggerEventID { - a.lggr.Warnw("unexpected event ID", "expected", triggerEventID, "got", rep.EventID) - continue - } - - // use Abs to handle edge case of clock skew - timeDiff := time.Since(time.Unix(0, int64(rep.Timestamp))).Abs() //nolint:gosec // disable G115 this won't be running in 2262 - if timeDiff < 0 { - timeDiff = -timeDiff - } - if timeDiff.Nanoseconds() > int64(a.maxAgeSec)*1000000000 { - a.lggr.Warnw("aggregation report too old", "age", timeDiff, "maxAge", a.maxAgeSec, "reportTimestamp", rep.Timestamp) - continue - } - - if err2 := a.validateSignatures(ocrEvent); err2 != nil { - a.lggr.Errorw("invalid signatures", "err", err2) - continue - } - // Replace "Outputs" field with the one extracted from the OCR report and drop the binary report - outputsMap, err := values.FromMapValueProto(rep.Outputs) - if err != nil { - a.lggr.Errorw("failed to parse OCR report outputs", "err", err) - continue - } - triggerResp.Event.Outputs = outputsMap - return triggerResp, nil - } - return capabilities.TriggerResponse{}, fmt.Errorf("%w: %s", ErrMissingResponse, triggerEventID) -} - -func (a *signedReportRemoteAggregator) validateSignatures(event *capabilities.OCRTriggerEvent) error { - digest, err := ocr2types.BytesToConfigDigest(event.ConfigDigest) - if err != nil { - return errors.Join(ErrMalformedConfig, err) - } - fullHash := ocr2key.ReportToSigData3(digest, event.SeqNr, event.Report) - validated := map[common.Address]struct{}{} - for _, sig := range event.Sigs { - signerPubkey, err2 := crypto.SigToPub(fullHash, sig.Signature) - if err2 != nil { - return errors.Join(ErrMalformedSigner, err2) - } - signerAddr := crypto.PubkeyToAddress(*signerPubkey) - if _, ok := a.allowedSigners[signerAddr]; !ok { - a.lggr.Warnw("invalid signer", "signerAddr", signerAddr) - continue - } - validated[signerAddr] = struct{}{} - if len(validated) >= a.minRequiredSignatures { - break // early exit - } - } - if len(validated) < a.minRequiredSignatures { - return fmt.Errorf("%w: got %d, needed %d", ErrInsufficientSignatures, len(validated), a.minRequiredSignatures) - } - return nil -} diff --git a/core/capabilities/remote/aggregation/signed_report_test.go b/core/capabilities/remote/aggregation/signed_report_test.go deleted file mode 100644 index 83f6a5c711d..00000000000 --- a/core/capabilities/remote/aggregation/signed_report_test.go +++ /dev/null @@ -1,516 +0,0 @@ -package aggregation_test - -import ( - "errors" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - "go.uber.org/zap/zaptest/observer" - "google.golang.org/protobuf/proto" - - ocr2types "github.com/smartcontractkit/libocr/offchainreporting2/types" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - - // "github.com/smartcontractkit/chainlink/common/pkg/logger" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/aggregation" -) - -func TestSignedReportAggregator_Aggregate(t *testing.T) { - t.Parallel() - - // Setup test keys - kb1, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - addr1 := common.BytesToAddress(kb1.PublicKey()) - - kb2, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - addr2 := common.BytesToAddress(kb2.PublicKey()) - - kb3, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - addr3 := common.BytesToAddress(kb3.PublicKey()) - - // kb4 is an unauthorized signer - kb4, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - - // Setup test data - eventID := "test-event-123" - configDigest := ocr2types.ConfigDigest{} - for i := range len(configDigest) { - configDigest[i] = byte(i) - } - seqNr := uint64(123) - currentTime := uint64(time.Now().UnixNano()) - - // Create a valid outputs map - outputsMap, err := values.NewMap(map[string]values.Value{ - "result": values.NewString("success"), - "data": values.NewInt64(42), - }) - require.NoError(t, err) - outputsProto := values.ProtoMap(outputsMap) // outputsMap.ProtoMap() - - // Create a valid OCR report - validReport := &capabilitiespb.OCRTriggerReport{ - EventID: eventID, - Timestamp: currentTime, - Outputs: outputsProto, - } - - reportBytes, err := proto.Marshal(validReport) - require.NoError(t, err) - - sig1, err := kb1.Sign3(configDigest, seqNr, reportBytes) - require.NoError(t, err) - sig2, err := kb2.Sign3(configDigest, seqNr, reportBytes) - require.NoError(t, err) - unauthorizedSig, err := kb4.Sign3(configDigest, seqNr, reportBytes) // Unauthorized signature - require.NoError(t, err) - - // Setup logger - lggr, observedLogs := logger.TestObserved(t, zapcore.WarnLevel) - - // Setup aggregator with 2 required signatures - allowedSigners := [][]byte{ - addr1.Bytes(), - addr2.Bytes(), - addr3.Bytes(), - } - minRequiredSignatures := 2 - maxAgeSec := 10 - capID := "test-capability" - - aggregator := aggregation.NewSignedReportRemoteAggregator( - allowedSigners, - minRequiredSignatures, - capID, - maxAgeSec, - lggr, - ) - - // Test cases - // NOTE: we are checking the logs for errors, so we need to clear the logs before invocation of the aggregator - t.Run("happy path - valid response with enough signatures", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Prepare valid OCR event with enough valid signatures - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: reportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: sig2}, - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - // Create trigger event with OCR report - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - // Create TriggerResponse - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - // Marshal response - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Expected flattened response - expectedResp := capabilities.TriggerResponse{ - Event: capabilities.TriggerEvent{ - ID: eventID, - Outputs: outputsMap, - }, - } - - // Call Aggregate - clearLogger(t, observedLogs) - result, err := aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.NoError(t, err) - - // Verify response is flattened correctly - assert.Equal(t, expectedResp.Event.ID, result.Event.ID) - assert.Equal(t, expectedResp.Event.Outputs, result.Event.Outputs) - }) - - t.Run("error - unmarshallable response", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Invalid bytes that can't be unmarshalled - invalidBytes := []byte("not a valid response") - - // Call Aggregate with invalid bytes - clearLogger(t, observedLogs) - _, err := aggregator.Aggregate(eventID, [][]byte{invalidBytes}) - require.ErrorIs(t, err, aggregation.ErrMissingResponse) - gotLog := observedLogs.FilterMessage("could not unmarshal one of capability responses (faulty sender?)") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - response without OCR event", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Create event without OCR data - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - // No OCREvent - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with response without OCR event - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("trigger response does not contain an OCR report") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - response with malformed Output map", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Create event with malformed Output map - o, err := values.NewMap(map[string]any{ - "UnexpectedKey": "unexpected value", - }) - require.NoError(t, err) - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: o, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with response without OCR event - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("trigger response does not contain an OCR report") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - unparsable OCR report", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Create an OCR event with invalid report bytes - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: []byte("not a valid report"), - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: sig2}, - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with invalid report bytes - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("failed to parse OCR report") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - mismatched event ID", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Create a valid report but with wrong event ID - wrongIDReport := &capabilitiespb.OCRTriggerReport{ - EventID: "wrong-event-id", - Timestamp: currentTime, - Outputs: outputsProto, - } - - wrongReportBytes, err := proto.Marshal(wrongIDReport) - require.NoError(t, err) - - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: wrongReportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: sig2}, - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with mismatched event ID - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("unexpected event ID") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - report too old", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Create an old report (beyond maxAgeSec) - oldTime := currentTime - uint64((maxAgeSec+5)*1000000000) //nolint:gosec // disable G115 - - oldReport := &capabilitiespb.OCRTriggerReport{ - EventID: eventID, - Timestamp: oldTime, - Outputs: outputsProto, - } - - oldReportBytes, err := proto.Marshal(oldReport) - require.NoError(t, err) - - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: oldReportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: sig2}, - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with old report - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("aggregation report too old") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - not enough valid signatures", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Only one valid signature when two are required - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: reportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - // Missing second valid signature - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with not enough valid signatures - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("invalid signatures") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - signatures from unauthorized signers", func(t *testing.T) { //nolint:paralleltest // shared logger state - // One valid signature and one unauthorized - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: reportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: unauthorizedSig}, // Unauthorized signer - }, - } - - m, err := ocrEvent.ToMap() - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with unauthorized signer - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("invalid signer") - assert.Equal(t, 1, gotLog.Len()) - }) - - t.Run("error - malformed config digest", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Invalid config digest - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: []byte("invalid config digest"), // Wrong length - SeqNr: seqNr, - Report: reportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: sig1}, - {Signature: sig2}, - }, - } - - m, err := ocrEvent.ToMap() - require.NoError(t, err) - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with malformed config digest - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("invalid signatures") - assert.Equal(t, 1, gotLog.Len()) - gotLogLine := gotLog.All()[0] - - assert.True(t, logContainErr(t, gotLogLine, aggregation.ErrMalformedConfig), "expected error to be contained in log") - }) - - t.Run("error - malformed signature", func(t *testing.T) { //nolint:paralleltest // shared logger state - // Invalid signature format - ocrEvent := &capabilities.OCRTriggerEvent{ - ConfigDigest: configDigest[:], - SeqNr: seqNr, - Report: reportBytes, - Sigs: []capabilities.OCRAttributedOnchainSignature{ - {Signature: []byte("invalid signature")}, - {Signature: sig2}, - }, - } - m, err := ocrEvent.ToMap() - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - ID: eventID, - Outputs: m, - } - - triggerResp := capabilities.TriggerResponse{ - Event: triggerEvent, - } - - respBytes, err := capabilitiespb.MarshalTriggerResponse(triggerResp) - require.NoError(t, err) - - // Call Aggregate with malformed signature - clearLogger(t, observedLogs) - _, err = aggregator.Aggregate(eventID, [][]byte{respBytes}) - require.Error(t, err) - gotLog := observedLogs.FilterMessage("invalid signatures") - assert.Equal(t, 1, gotLog.Len()) - gotLogLine := gotLog.All()[0] - assert.True(t, logContainErr(t, gotLogLine, aggregation.ErrMalformedSigner), "expected error to be contained in log") - }) -} - -// TODO this seems useful in our common logging package -func logContainErr(t testing.TB, log observer.LoggedEntry, err error) bool { - gotErr := extractErr(t, log) - if gotErr == nil { - t.Logf("expected error, got nil") - return false - } - return errors.Is(gotErr, err) -} - -func extractErr(t testing.TB, log observer.LoggedEntry) error { - t.Helper() - const errKey = "err" - ctxMap := log.ContextMap() - if ctxMap == nil { - t.Logf("expected context map, got %v", log) - return nil - } - if _, ok := ctxMap[errKey]; !ok { - t.Logf("expected %s key in context map, got %v", errKey, ctxMap) - return nil - } - // don't use ctxMap because it erases the type - var f zapcore.Field - for _, c := range log.Context { - if c.Key == errKey { - f = c - break - } - } - gotErr, ok := f.Interface.(error) - if !ok { - t.Logf("expected error type, got %T", ctxMap[errKey]) - return nil - } - return gotErr -} - -func clearLogger(t testing.TB, lggr *observer.ObservedLogs) { - t.Helper() - lggr.TakeAll() -} diff --git a/core/capabilities/streams/codec.go b/core/capabilities/streams/codec.go deleted file mode 100644 index 7f9ab724168..00000000000 --- a/core/capabilities/streams/codec.go +++ /dev/null @@ -1,85 +0,0 @@ -package streams - -import ( - "encoding/hex" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" -) - -type codec struct { - lggr logger.Logger -} - -var _ datastreams.ReportCodec = &codec{} - -func (c *codec) Unwrap(wrapped values.Value) ([]datastreams.FeedReport, error) { - dest, err := datastreams.UnwrapStreamsTriggerEventToFeedReportList(wrapped) - if err != nil { - return nil, fmt.Errorf("failed to unwrap: %w", err) - } - for i, report := range dest { - // decoding fields - id, err2 := datastreams.NewFeedID(report.FeedID) - if err2 != nil { - return nil, fmt.Errorf("malformed feed ID: %w", err2) - } - v3Codec := reportcodec.NewReportCodec(id.Bytes(), nil) - decoded, err2 := v3Codec.Decode(report.FullReport) - if err2 != nil { - return nil, fmt.Errorf("failed to decode: %w", err2) - } - if decoded.FeedId != id.Bytes() { - return nil, fmt.Errorf("feed ID mismatch: FeedID: %s, FullReport.FeedId: %s", id, hex.EncodeToString(decoded.FeedId[:])) - } - dest[i].BenchmarkPrice = decoded.BenchmarkPrice.Bytes() - dest[i].ObservationTimestamp = int64(decoded.ObservationsTimestamp) - } - return dest, nil -} - -func (c *codec) Wrap(reports []datastreams.FeedReport) (values.Value, error) { - return values.Wrap(&datastreams.StreamsTriggerEvent{ - Payload: reports, - }) -} - -func (c *codec) Validate(report datastreams.FeedReport, allowedSigners [][]byte, minRequiredSignatures int) error { - signersMap := make(map[common.Address]struct{}) - for _, signer := range allowedSigners { - signersMap[common.BytesToAddress(signer)] = struct{}{} - } - // signatures (report and context are signed together) - sigData := append(crypto.Keccak256(report.FullReport), report.ReportContext...) - fullHash := crypto.Keccak256(sigData) - validated := map[common.Address]struct{}{} - for _, sig := range report.Signatures { - signerPubkey, err2 := crypto.SigToPub(fullHash, sig) - if err2 != nil { - return fmt.Errorf("malformed signer: %w", err2) - } - signerAddr := crypto.PubkeyToAddress(*signerPubkey) - if _, ok := signersMap[signerAddr]; !ok { - c.lggr.Debugw("invalid signer", "signerAddr", signerAddr) - continue - } - validated[signerAddr] = struct{}{} - if len(validated) >= minRequiredSignatures { - break // early exit - } - } - if len(validated) < minRequiredSignatures { - return fmt.Errorf("not enough valid signatures %d, needed %d", len(validated), minRequiredSignatures) - } - return nil -} - -func NewCodec(lggr logger.Logger) *codec { - return &codec{lggr: lggr} -} diff --git a/core/capabilities/streams/codec_test.go b/core/capabilities/streams/codec_test.go deleted file mode 100644 index 56c4d5bdade..00000000000 --- a/core/capabilities/streams/codec_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package streams_test - -import ( - "crypto/rand" - "encoding/hex" - "math/big" - "testing" - - "github.com/stretchr/testify/require" - - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" -) - -func TestCodec_WrapUnwrap(t *testing.T) { - codec := streams.NewCodec(logger.Test(t)) - - id1, id1Str := newFeedID(t) - id2, id2Str := newFeedID(t) - price1, price2 := big.NewInt(1), big.NewInt(2) - timestamp1, timestamp2 := int64(100), int64(200) - report1, report2 := newReport(t, id1, price1, timestamp1), newReport(t, id2, price2, timestamp2) - reportCtx := ocrTypes.ReportContext{} - rawCtx := rawReportContext(reportCtx) - - keyBundle1, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - keyBundle2, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - - signatureK1R1, err := keyBundle1.Sign(reportCtx, report1) - require.NoError(t, err) - signatureK1R2, err := keyBundle1.Sign(reportCtx, report2) - require.NoError(t, err) - signatureK2R1, err := keyBundle2.Sign(reportCtx, report1) - require.NoError(t, err) - signatureK2R2, err := keyBundle2.Sign(reportCtx, report2) - require.NoError(t, err) - - allowedSigners := [][]byte{keyBundle1.PublicKey(), keyBundle2.PublicKey()} // bad name - see comment on evmKeyring.PublicKey - - wrapped, err := codec.Wrap([]datastreams.FeedReport{ - { - FeedID: id1Str, - FullReport: report1, - ReportContext: rawCtx, - Signatures: [][]byte{signatureK1R1, signatureK2R1}, - }, - { - FeedID: id2Str, - FullReport: report2, - ReportContext: rawCtx, - Signatures: [][]byte{signatureK1R2, signatureK2R2}, - }, - }) - require.NoError(t, err) - - // wrong type - _, err = codec.Unwrap(values.NewBool(true)) - require.Error(t, err) - - // correct reports but wrong signatures - unwrapped, err := codec.Unwrap(wrapped) - require.NoError(t, err) - require.Len(t, unwrapped, 2) - require.Equal(t, price1.Bytes(), unwrapped[0].BenchmarkPrice) - require.Equal(t, price2.Bytes(), unwrapped[1].BenchmarkPrice) - require.Equal(t, timestamp1, unwrapped[0].ObservationTimestamp) - require.Equal(t, timestamp2, unwrapped[1].ObservationTimestamp) - for _, report := range unwrapped { - require.Error(t, codec.Validate(report, nil, 1)) - } - - // valid signatures - for _, report := range unwrapped { - require.NoError(t, codec.Validate(report, allowedSigners, 2)) - } - - // invalid FeedID - wrappedInvalid, err := codec.Wrap([]datastreams.FeedReport{ - { - FeedID: id2Str, // ID #2 doesn't match what's in report #1 - FullReport: report1, - ReportContext: rawCtx, - Signatures: [][]byte{signatureK1R1, signatureK2R1}, - }, - }) - require.NoError(t, err) - _, err = codec.Unwrap(wrappedInvalid) - require.Error(t, err) - require.Contains(t, err.Error(), "feed ID mismatch") -} - -func newFeedID(t *testing.T) ([32]byte, string) { - buf := [32]byte{} - _, err := rand.Read(buf[:]) - require.NoError(t, err) - return buf, "0x" + hex.EncodeToString(buf[:]) -} - -func newReport(t *testing.T, feedID [32]byte, price *big.Int, timestamp int64) []byte { - v3Codec := reportcodec.NewReportCodec(feedID, logger.Test(t)) - raw, err := v3Codec.BuildReport(t.Context(), v3.ReportFields{ - BenchmarkPrice: price, - Timestamp: uint32(timestamp), - ValidFromTimestamp: uint32(timestamp), - Bid: price, - Ask: price, - LinkFee: price, - NativeFee: price, - ExpiresAt: uint32(timestamp + 1000000), - }) - require.NoError(t, err) - return raw -} - -func rawReportContext(reportCtx ocrTypes.ReportContext) []byte { - rc := evmutil.RawReportContext(reportCtx) - flat := []byte{} - for _, r := range rc { - flat = append(flat, r[:]...) - } - return flat -} diff --git a/core/capabilities/streams/consensus_agg_test.go b/core/capabilities/streams/consensus_agg_test.go deleted file mode 100644 index f9d67bbde49..00000000000 --- a/core/capabilities/streams/consensus_agg_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package streams_test - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" - - "github.com/smartcontractkit/libocr/commontypes" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/datafeeds" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" -) - -// Integration/load test that combines Data Feeds Consensus Aggregator and Streams Codec. -// For more meaningful measurements, increase the values of parameters P and T. -func TestStreamsConsensusAggregator(t *testing.T) { - Nt := 10 // trigger DON nodes - Ft := 3 // trigger DON faulty nodes - Nw := 10 // workflow DON nodes - Fw := 3 // workflow DON faulty nodes - P := 40 // feeds - T := 2 // test iterations - - triggerNodes := newNodes(t, Nt) - feeds := newFeedsWithSignedReports(t, triggerNodes, Nt, P, 1) - allowedSigners := make([][]byte, Nt) - for i := range Nt { - allowedSigners[i] = triggerNodes[i].bundle.PublicKey() // bad name - see comment on evmKeyring.PublicKey - } - - config := newAggConfig(t, feeds) - lggr := logger.Test(t) - codec := streams.NewCodec(lggr) - agg, err := datafeeds.NewDataFeedsAggregator(*config, codec) - require.NoError(t, err) - - // init round - empty previous Outcome, empty observations - outcome, err := agg.Aggregate(lggr, nil, map[commontypes.OracleID][]values.Value{}, Fw) - require.NoError(t, err) - require.False(t, outcome.ShouldReport) - - // validate metadata - newState := &datafeeds.DataFeedsOutcomeMetadata{} - err = proto.Unmarshal(outcome.Metadata, newState) - require.NoError(t, err) - require.Len(t, newState.FeedInfo, P) - - // run test aggregations - startTs := time.Now().UnixMilli() - processingTime := int64(0) - for range T { - obs := newObservations(t, Nw, feeds, Ft+1, allowedSigners) - processingStart := time.Now().UnixMilli() - outcome, err = agg.Aggregate(lggr, outcome, obs, Fw) - processingTime += time.Now().UnixMilli() - processingStart - require.NoError(t, err) - } - totalTime := time.Now().UnixMilli() - startTs - lggr.Infow("elapsed", "totalMs", totalTime, "processingMs", processingTime) -} - -func newAggConfig(t *testing.T, feeds []feed) *values.Map { - feedMap := map[string]any{} - for _, feed := range feeds { - feedMap[feed.feedIDStr] = map[string]any{ - "deviation": "0.1", - "heartbeat": 1, - "remappedID": feed.feedIDStr, - } - } - unwrappedConfig := map[string]any{ - "feeds": feedMap, - "allowedPartialStaleness": "0.2", - } - config, err := values.NewMap(unwrappedConfig) - require.NoError(t, err) - return config -} - -func newObservations(t *testing.T, nNodes int, feeds []feed, minRequiredSignatures int, allowedSigners [][]byte) map[commontypes.OracleID][]values.Value { - observations := map[commontypes.OracleID][]values.Value{} - for i := range nNodes { - reportList := []datastreams.FeedReport{} - for j := range feeds { - reportIdx := 0 - signedStreamsReport := datastreams.FeedReport{ - FeedID: feeds[j].feedIDStr, - FullReport: feeds[j].reports[reportIdx].rawReport, - ReportContext: feeds[j].reports[reportIdx].reportCtx, - Signatures: feeds[j].reports[reportIdx].signatures, - } - reportList = append(reportList, signedStreamsReport) - } - - meta := datastreams.Metadata{ - Signers: allowedSigners, - MinRequiredSignatures: minRequiredSignatures, - } - p := datastreams.StreamsTriggerEvent{ - Payload: reportList, - Metadata: meta, - } - outputs, err := values.WrapMap(p) - require.NoError(t, err) - - observations[commontypes.OracleID(i)] = []values.Value{outputs} - } - return observations -} diff --git a/core/capabilities/streams/mock_trigger.go b/core/capabilities/streams/mock_trigger.go deleted file mode 100644 index 279a258bc52..00000000000 --- a/core/capabilities/streams/mock_trigger.go +++ /dev/null @@ -1,367 +0,0 @@ -package streams - -import ( - "context" - "crypto/ecdsa" - "fmt" - "math/big" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/smartcontractkit/libocr/offchainreporting2plus/chains/evmutil" - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers/streams" - "github.com/smartcontractkit/chainlink-common/pkg/services" - "github.com/smartcontractkit/chainlink-common/pkg/types/core" - v3 "github.com/smartcontractkit/chainlink-common/pkg/types/mercury/v3" - - "github.com/smartcontractkit/chainlink-data-streams/mercury/v3/reportcodec" - "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -const ( - mockTriggerCapabilityName = "mock-streams-trigger" - mockTriggerCapabilityVersion = "1.0.0" - mockTriggerRegisterResolution = 100 - defaultLoopIntervalMs int64 = 1000 - mockSignerFaultTolerance = 1 - mockSignerKeyLength = 32 - mockSignerKeyLastByteIndex = mockSignerKeyLength - 1 - reportExpiryOffsetSeconds = 1_000_000 - initialMockPriceA int64 = 300_000 - initialMockPriceB int64 = 40_000 - initialMockPriceC int64 = 5_000_000 -) - -func RegisterMockTrigger(lggr logger.Logger, capRegistry core.CapabilitiesRegistry) (*MockTriggerService, error) { - ctx := context.Background() - trigger, err := NewMockTriggerService(mockTriggerRegisterResolution, lggr) - if err != nil { - return nil, err - } - if err := trigger.Start(ctx); err != nil { - return nil, err - } - if err := capRegistry.Add(ctx, trigger); err != nil { - _ = trigger.Close() - return nil, err - } - - return trigger, nil -} - -const MockTriggerCapabilityID = mockTriggerCapabilityName + "@" + mockTriggerCapabilityVersion - -var capInfo = capabilities.MustNewCapabilityInfo( - MockTriggerCapabilityID, - capabilities.CapabilityTypeTrigger, - "Mock Streams Trigger", -) - -// Wraps the MercuryTriggerService to produce a trigger with mocked data -type MockTriggerService struct { - *triggers.MercuryTriggerService - meta datastreams.Metadata - signers []*ecdsa.PrivateKey - stopCh services.StopChan - closeOnce sync.Once - wg sync.WaitGroup - loopInterval time.Duration - subscribers map[string][]streams.FeedId - subscribersMu sync.Mutex - lggr logger.Logger -} - -func NewMockTriggerService(tickerResolutionMs int64, lggr logger.Logger) (*MockTriggerService, error) { - trigger, err := triggers.NewMercuryTriggerService(tickerResolutionMs, mockTriggerCapabilityName, mockTriggerCapabilityVersion, lggr) - if err != nil { - return nil, err - } - trigger.CapabilityInfo = capInfo - - if tickerResolutionMs <= 0 { - tickerResolutionMs = defaultLoopIntervalMs - } - - meta, signers, err := newMockMetadataAndSigners() - if err != nil { - return nil, err - } - - // MercuryTrigger is typically wrapped by other modules that ignore the trigger's meta and provide a different one. - // Since we're skipping those wrappers we need to provide our own meta here. - trigger.SetMetaOverride(meta) - - return &MockTriggerService{ - MercuryTriggerService: trigger, - meta: meta, - signers: signers, - stopCh: make(services.StopChan), - loopInterval: time.Duration(tickerResolutionMs) * time.Millisecond, - subscribers: make(map[string][]streams.FeedId), - lggr: lggr, - }, nil -} - -func newMockMetadataAndSigners() (datastreams.Metadata, []*ecdsa.PrivateKey, error) { - meta := datastreams.Metadata{MinRequiredSignatures: 2*mockSignerFaultTolerance + 1} - signers := make([]*ecdsa.PrivateKey, 0, meta.MinRequiredSignatures) - for i := 0; i < meta.MinRequiredSignatures; i++ { - privKey, err := newMockSigner(i + 1) - if err != nil { - return datastreams.Metadata{}, nil, err - } - signers = append(signers, privKey) - meta.Signers = append(meta.Signers, crypto.PubkeyToAddress(privKey.PublicKey).Bytes()) - } - return meta, signers, nil -} - -func newMockSigner(index int) (*ecdsa.PrivateKey, error) { - bytes := make([]byte, mockSignerKeyLength) - lastByte, err := toUint8(index) - if err != nil { - return nil, err - } - bytes[mockSignerKeyLastByteIndex] = lastByte - privKey, err := crypto.ToECDSA(bytes) - if err != nil { - return nil, err - } - return privKey, nil -} - -func (m *MockTriggerService) Start(ctx context.Context) error { - if err := m.MercuryTriggerService.Start(ctx); err != nil { - return err - } - m.wg.Add(1) - go m.loop() - return nil -} - -func (m *MockTriggerService) Close() error { - m.closeOnce.Do(func() { - close(m.stopCh) - }) - m.wg.Wait() - return m.MercuryTriggerService.Close() -} - -func (m *MockTriggerService) RegisterTrigger(ctx context.Context, req capabilities.TriggerRegistrationRequest) (<-chan capabilities.TriggerResponse, error) { - ch, err := m.MercuryTriggerService.RegisterTrigger(ctx, req) - if err != nil { - return nil, err - } - - config, err := m.ValidateConfig(req.Config) - if err != nil { - _ = m.MercuryTriggerService.UnregisterTrigger(ctx, req) - return nil, err - } - m.subscribersMu.Lock() - defer m.subscribersMu.Unlock() - m.subscribers[req.Metadata.WorkflowID] = config.FeedIds - return ch, nil -} - -func (m *MockTriggerService) UnregisterTrigger(ctx context.Context, req capabilities.TriggerRegistrationRequest) error { - err := m.MercuryTriggerService.UnregisterTrigger(ctx, req) - if err != nil { - return err - } - - m.subscribersMu.Lock() - defer m.subscribersMu.Unlock() - delete(m.subscribers, req.Metadata.WorkflowID) - return nil -} - -const baseTimestamp = 1000000000 - -// NOTE: duplicated from codec_test.go -func newReport(lggr logger.Logger, feedID [32]byte, price *big.Int, timestamp int64) ([]byte, error) { - uintTimestamp, err := toUint32(timestamp) - if err != nil { - return nil, err - } - expiresAt, err := toUint32(timestamp + reportExpiryOffsetSeconds) - if err != nil { - return nil, err - } - v3Codec := reportcodec.NewReportCodec(feedID, lggr) - raw, err := v3Codec.BuildReport(context.Background(), v3.ReportFields{ - BenchmarkPrice: price, - Timestamp: uintTimestamp, - ValidFromTimestamp: uintTimestamp, - Bid: price, - Ask: price, - LinkFee: price, - NativeFee: price, - ExpiresAt: expiresAt, - }) - if err != nil { - return nil, err - } - return raw, nil -} - -func toUint32(v int64) (uint32, error) { - if v < 0 || v > int64(^uint32(0)) { - return 0, fmt.Errorf("value %d out of uint32 range", v) - } - return uint32(v), nil -} - -func toUint8(v int) (uint8, error) { - if v < 0 || v > int(^uint8(0)) { - return 0, fmt.Errorf("value %d out of uint8 range", v) - } - return uint8(v), nil -} - -func rawReportContext(reportCtx ocrTypes.ReportContext) []byte { - rc := evmutil.RawReportContext(reportCtx) - flat := make([]byte, 0, len(rc)*32) - for _, r := range rc { - flat = append(flat, r[:]...) - } - return flat -} - -func (m *MockTriggerService) loop() { - defer m.wg.Done() - - ticker := time.NewTicker(m.loopInterval) - defer ticker.Stop() - - prices := []int64{initialMockPriceA, initialMockPriceB, initialMockPriceC} - iteration := 0 - - for { - select { - case <-m.stopCh: - return - case <-ticker.C: - } - - incrementPrices(prices) - iteration++ - - timestamp := time.Now().Unix() - reportCtx, err := newReportContext(iteration) - if err != nil { - m.lggr.Errorw("failed to build Mock report context", "err", err, "timestamp", timestamp) - continue - } - reports, err := m.buildReports(timestamp, prices[0], reportCtx) - if err != nil { - m.lggr.Errorw("failed to build Mock reports", "err", err, "timestamp", timestamp) - continue - } - if len(reports) == 0 { - continue - } - - m.lggr.Infow("New set of Mock reports", "timestamp", timestamp, "payload", reports) - if err := m.ProcessReport(reports); err != nil { - m.lggr.Errorw("failed to process Mock reports", "err", err, "timestamp", timestamp, "payload", reports) - } - } -} - -func incrementPrices(prices []int64) { - for i := range prices { - prices[i]++ - } -} - -func newReportContext(iteration int) (ocrTypes.ReportContext, error) { - epoch, err := toUint32(int64(baseTimestamp + iteration)) - if err != nil { - return ocrTypes.ReportContext{}, err - } - return ocrTypes.ReportContext{ - ReportTimestamp: ocrTypes.ReportTimestamp{Epoch: epoch}, - }, nil -} - -func (m *MockTriggerService) buildReports(timestamp, price int64, reportCtx ocrTypes.ReportContext) ([]datastreams.FeedReport, error) { - subscribers := m.snapshotSubscribers() - reports := make([]datastreams.FeedReport, 0, subscriberCount(subscribers)) - for _, feedIDs := range subscribers { - for _, feedID := range feedIDs { - report, err := m.newSignedReport(string(feedID), price, timestamp, reportCtx) - if err != nil { - return nil, err - } - reports = append(reports, report) - } - } - return reports, nil -} - -func (m *MockTriggerService) snapshotSubscribers() map[string][]streams.FeedId { - m.subscribersMu.Lock() - defer m.subscribersMu.Unlock() - - snapshot := make(map[string][]streams.FeedId, len(m.subscribers)) - for workflowID, feedIDs := range m.subscribers { - snapshot[workflowID] = cloneFeedIDs(feedIDs) - } - return snapshot -} - -func subscriberCount(subscribers map[string][]streams.FeedId) int { - total := 0 - for _, feedIDs := range subscribers { - total += len(feedIDs) - } - return total -} - -func cloneFeedIDs(feedIDs []streams.FeedId) []streams.FeedId { - cloned := make([]streams.FeedId, len(feedIDs)) - copy(cloned, feedIDs) - return cloned -} - -func (m *MockTriggerService) newSignedReport(feedID string, price, timestamp int64, reportCtx ocrTypes.ReportContext) (datastreams.FeedReport, error) { - fullReport, err := newReport(m.lggr, common.HexToHash(feedID), big.NewInt(price), timestamp) - if err != nil { - return datastreams.FeedReport{}, fmt.Errorf("build report for feed %s: %w", feedID, err) - } - - report := datastreams.FeedReport{ - FeedID: feedID, - FullReport: fullReport, - ReportContext: rawReportContext(reportCtx), - ObservationTimestamp: timestamp, - } - - if err := m.signReport(&report); err != nil { - return datastreams.FeedReport{}, fmt.Errorf("sign report for feed %s: %w", feedID, err) - } - - return report, nil -} - -func (m *MockTriggerService) signReport(report *datastreams.FeedReport) error { - sigData := append(crypto.Keccak256(report.FullReport), report.ReportContext...) - hash := crypto.Keccak256(sigData) - report.Signatures = make([][]byte, 0, m.meta.MinRequiredSignatures) - for n := 0; n < m.meta.MinRequiredSignatures; n++ { - sig, err := crypto.Sign(hash, m.signers[n]) - if err != nil { - return err - } - report.Signatures = append(report.Signatures, sig) - } - return nil -} diff --git a/core/capabilities/streams/mock_trigger_test.go b/core/capabilities/streams/mock_trigger_test.go deleted file mode 100644 index 362db73941a..00000000000 --- a/core/capabilities/streams/mock_trigger_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package streams - -import ( - "context" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/stretchr/testify/require" - - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - commoncap "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - triggercfg "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers/streams" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - - "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -const testFeedID triggercfg.FeedId = "0x0000000000000000000000000000000000000000000000000000000000000001" - -func TestMockTriggerServiceStartClose(t *testing.T) { - t.Parallel() - - svc, err := NewMockTriggerService(10, logger.TestLogger(t)) - require.NoError(t, err) - require.NoError(t, svc.Start(context.Background())) - - done := make(chan error, 1) - go func() { - done <- svc.Close() - }() - - select { - case err := <-done: - require.NoError(t, err) - case <-time.After(time.Second): - t.Fatal("timed out waiting for mock trigger service to close") - } -} - -func TestMockTriggerServiceRegisterEmitUnregister(t *testing.T) { - t.Parallel() - - ctx := context.Background() - svc, err := NewMockTriggerService(200, logger.TestLogger(t)) - require.NoError(t, err) - require.NoError(t, svc.MercuryTriggerService.Start(ctx)) - t.Cleanup(func() { - require.NoError(t, svc.MercuryTriggerService.Close()) - }) - - req := newMockTriggerRequest(t, "workflow-a", "trigger-a", 200) - ch, err := svc.RegisterTrigger(ctx, req) - require.NoError(t, err) - - svc.subscribersMu.Lock() - require.Equal(t, []triggercfg.FeedId{testFeedID}, svc.subscribers[req.Metadata.WorkflowID]) - svc.subscribersMu.Unlock() - - require.NoError(t, svc.ProcessReport([]datastreams.FeedReport{newSignedMockFeedReport(t, svc, testFeedID)})) - - var resp commoncap.TriggerResponse - select { - case resp = <-ch: - case <-time.After(2 * time.Second): - t.Fatal("timed out waiting for mock trigger event") - } - - triggerEvent := datastreams.StreamsTriggerEvent{} - require.NoError(t, resp.Event.Outputs.UnwrapTo(&triggerEvent)) - require.Equal(t, MockTriggerCapabilityID, resp.Event.TriggerType) - require.Len(t, triggerEvent.Payload, 1) - require.Len(t, triggerEvent.Payload[0].Signatures, svc.meta.MinRequiredSignatures) - require.Len(t, triggerEvent.Metadata.Signers, svc.meta.MinRequiredSignatures) - require.Equal(t, svc.meta.MinRequiredSignatures, triggerEvent.Metadata.MinRequiredSignatures) - - require.NoError(t, svc.UnregisterTrigger(ctx, req)) - - svc.subscribersMu.Lock() - require.Empty(t, svc.subscribers) - svc.subscribersMu.Unlock() - - select { - case _, ok := <-ch: - require.False(t, ok) - case <-time.After(time.Second): - t.Fatal("timed out waiting for subscriber channel to close") - } -} - -func TestMockTriggerServiceUnregisterKeepsSubscriberStateOnError(t *testing.T) { - t.Parallel() - - ctx := context.Background() - svc, err := NewMockTriggerService(200, logger.TestLogger(t)) - require.NoError(t, err) - require.NoError(t, svc.MercuryTriggerService.Start(ctx)) - t.Cleanup(func() { - require.NoError(t, svc.MercuryTriggerService.Close()) - }) - - req := newMockTriggerRequest(t, "workflow-a", "trigger-a", 200) - _, err = svc.RegisterTrigger(ctx, req) - require.NoError(t, err) - - badReq := req - badReq.TriggerID = "trigger-b" - err = svc.UnregisterTrigger(ctx, badReq) - require.Error(t, err) - - svc.subscribersMu.Lock() - require.Equal(t, []triggercfg.FeedId{testFeedID}, svc.subscribers[req.Metadata.WorkflowID]) - svc.subscribersMu.Unlock() -} - -func newMockTriggerRequest(t *testing.T, workflowID, triggerID string, maxFrequencyMs uint64) commoncap.TriggerRegistrationRequest { - t.Helper() - - cfg, err := values.WrapMap(triggercfg.TriggerConfig{ - FeedIds: []triggercfg.FeedId{testFeedID}, - MaxFrequencyMs: maxFrequencyMs, - }) - require.NoError(t, err) - - return commoncap.TriggerRegistrationRequest{ - TriggerID: triggerID, - Metadata: commoncap.RequestMetadata{ - WorkflowID: workflowID, - }, - Config: cfg, - } -} - -func newSignedMockFeedReport(t *testing.T, svc *MockTriggerService, feedID triggercfg.FeedId) datastreams.FeedReport { - t.Helper() - - timestamp := time.Now().Unix() - reportCtx := ocrTypes.ReportContext{ - ReportTimestamp: ocrTypes.ReportTimestamp{Epoch: uint32(baseTimestamp + 1)}, - } - - report := datastreams.FeedReport{ - FeedID: string(feedID), - ReportContext: rawReportContext(reportCtx), - ObservationTimestamp: timestamp, - } - fullReport, err := newReport(svc.lggr, common.HexToHash(string(feedID)), big.NewInt(123456), timestamp) - require.NoError(t, err) - report.FullReport = fullReport - - sigData := append(crypto.Keccak256(report.FullReport), report.ReportContext...) - hash := crypto.Keccak256(sigData) - for _, signer := range svc.signers { - sig, err := crypto.Sign(hash, signer) - require.NoError(t, err) - report.Signatures = append(report.Signatures, sig) - } - - return report -} diff --git a/core/capabilities/streams/trigger_test.go b/core/capabilities/streams/trigger_test.go deleted file mode 100644 index 0e529eeff17..00000000000 --- a/core/capabilities/streams/trigger_test.go +++ /dev/null @@ -1,223 +0,0 @@ -package streams_test - -import ( - "crypto/ed25519" - "crypto/rand" - "math/big" - "strconv" - "testing" - "time" - - "github.com/stretchr/testify/require" - - ocrTypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - ragetypes "github.com/smartcontractkit/libocr/ragep2p/types" - - "github.com/smartcontractkit/chainlink-common/keystore/corekeys" - "github.com/smartcontractkit/chainlink-common/keystore/corekeys/ocr2key" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/datastreams" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - "github.com/smartcontractkit/chainlink-protos/cre/go/values" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" - remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" - p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" -) - -const ( - triggerID = "streams-trigger@1.0.0" - workflowID = "workflowID1" - baseTriggerEventID = "triggerEvent_" - basePrice = 2000000000 - baseTimestamp = 1000000000 -) - -type feed struct { - feedID [32]byte - feedIDStr string - reports []report -} - -type report struct { - rawReport []byte - reportCtx []byte - signatures [][]byte -} - -type node struct { - peerID p2ptypes.PeerID - bundle ocr2key.KeyBundle -} - -// Integration/load test that combines Trigger Subscriber, Streams Trigger Aggregator and Streams Codec. -// It measures time needed to receive and process trigger events from multiple nodes and produce a local aggregated event. -// For more meaningful measurements, increase the values of parameters P and T. -func TestStreamsTrigger(t *testing.T) { - N := 31 // trigger DON nodes - F := 10 // faulty nodes - R := 5 // different reports per feed (i.e. prices and timestamps) - P := 2 // feeds - T := 2 // test iterations - - nodes := newNodes(t, N) - feeds := newFeedsWithSignedReports(t, nodes, N, P, R) - - allowedSigners := make([][]byte, N) - for i := range N { - allowedSigners[i] = nodes[i].bundle.PublicKey() // bad name - see comment on evmKeyring.PublicKey - } - lggr := logger.Test(t) - ctx := testutils.Context(t) - codec := streams.NewCodec(lggr) - agg := triggers.NewMercuryRemoteAggregator(codec, allowedSigners, F, "streams-trigger@1.2.3", lggr) - - capInfo := capabilities.CapabilityInfo{ - ID: triggerID, - } - capMembers := make([]p2ptypes.PeerID, N) - for i := range N { - capMembers[i] = nodes[i].peerID - } - capDonInfo := capabilities.DON{ - ID: 2, - Members: capMembers, - F: uint8(F), - } - config := &capabilities.RemoteTriggerConfig{ - MinResponsesToAggregate: uint32(F + 1), - } - subscriber := remote.NewTriggerSubscriber(triggerID, "method", nil, lggr) - require.NoError(t, subscriber.SetConfig(config, capInfo, 1, capDonInfo, agg)) - - // register trigger - req := capabilities.TriggerRegistrationRequest{ - Metadata: capabilities.RequestMetadata{ - WorkflowID: workflowID, - }, - } - triggerEventCallbackCh, err := subscriber.RegisterTrigger(ctx, req) - require.NoError(t, err) - - // send and process all trigger events - startTs := time.Now().UnixMilli() - processingTime := int64(0) - for c := range T { - triggerEventID := baseTriggerEventID + strconv.Itoa(c) - for i := range N { // every node ... - reportList := make([]datastreams.FeedReport, P) - for j := range P { // ... sends reports for every feed ... - reportIdx := (i + j) % R - signatures := make([][]byte, F+1) - for k := 0; k < F+1; k++ { // ... each signed by F+1 nodes - signatures[k] = feeds[j].reports[reportIdx].signatures[(i+k)%N] - } - signedStreamsReport := datastreams.FeedReport{ - FeedID: feeds[j].feedIDStr, - FullReport: feeds[j].reports[reportIdx].rawReport, - ReportContext: feeds[j].reports[reportIdx].reportCtx, - Signatures: signatures, - } - reportList[j] = signedStreamsReport - } - - msg := newTriggerEvent(t, reportList, triggerEventID, nodes[i].peerID) - - processingStart := time.Now().UnixMilli() - subscriber.Receive(ctx, msg) - processingTime += time.Now().UnixMilli() - processingStart - } - - response := <-triggerEventCallbackCh - validateLatestReports(t, response.Event.Outputs, P, basePrice+R-1, baseTimestamp+R-1) - } - totalTime := time.Now().UnixMilli() - startTs - lggr.Infow("elapsed", "totalMs", totalTime, "processingMs", processingTime) -} - -func newNodes(t *testing.T, N int) []node { - nodes := make([]node, N) - for i := range N { - bundle, err := ocr2key.New(corekeys.EVM) - require.NoError(t, err) - nodes[i].bundle = bundle - nodes[i].peerID = newPeerID(t) - } - return nodes -} - -func newPeerID(t *testing.T) ragetypes.PeerID { - _, privKey, err := ed25519.GenerateKey(rand.Reader) - require.NoError(t, err) - peerID, err := ragetypes.PeerIDFromPrivateKey(privKey) - require.NoError(t, err) - return peerID -} - -func newFeedsWithSignedReports(t *testing.T, nodes []node, N, P, R int) []feed { - feeds := make([]feed, P) - for i := range P { - id, idStr := newFeedID(t) - feeds[i].feedID = id - feeds[i].feedIDStr = idStr - feeds[i].reports = make([]report, R) - for j := range R { - report := newReport(t, id, big.NewInt(int64(basePrice+j)), int64(baseTimestamp+j)) - feeds[i].reports[j].rawReport = report - reportCtx := ocrTypes.ReportContext{ReportTimestamp: ocrTypes.ReportTimestamp{Epoch: uint32(baseTimestamp + j)}} - feeds[i].reports[j].reportCtx = rawReportContext(reportCtx) - feeds[i].reports[j].signatures = make([][]byte, N) - for k := range N { - signature, err := nodes[k].bundle.Sign(reportCtx, report) - require.NoError(t, err) - feeds[i].reports[j].signatures[k] = signature - } - } - } - return feeds -} - -func newTriggerEvent(t *testing.T, reportList []datastreams.FeedReport, triggerEventID string, sender ragetypes.PeerID) *remotetypes.MessageBody { - outputs, err := values.WrapMap(&datastreams.StreamsTriggerEvent{ - Timestamp: 10, - Payload: reportList, - }) - require.NoError(t, err) - - triggerEvent := capabilities.TriggerEvent{ - TriggerType: triggerID, - ID: triggerEventID, - Outputs: outputs, - } - - marshaled, err := pb.MarshalTriggerResponse(capabilities.TriggerResponse{Event: triggerEvent}) - - require.NoError(t, err) - msg := &remotetypes.MessageBody{ - Sender: sender[:], - Method: remotetypes.MethodTriggerEvent, - Metadata: &remotetypes.MessageBody_TriggerEventMetadata{ - TriggerEventMetadata: &remotetypes.TriggerEventMetadata{ - WorkflowIds: []string{workflowID}, - TriggerEventId: triggerEventID, - }, - }, - Payload: marshaled, - } - return msg -} - -func validateLatestReports(t *testing.T, wrapped values.Value, expectedFeedsLen int, expectedPrice int, expectedTimestamp int) { - triggerEvent := datastreams.StreamsTriggerEvent{} - require.NoError(t, wrapped.UnwrapTo(&triggerEvent)) - require.Len(t, triggerEvent.Payload, expectedFeedsLen) - priceBig := big.NewInt(int64(expectedPrice)) - for _, report := range triggerEvent.Payload { - require.Equal(t, priceBig.Bytes(), report.BenchmarkPrice) - require.Equal(t, int64(expectedTimestamp), report.ObservationTimestamp) - } -} diff --git a/core/scripts/keystone/src/01_provision_keystone.go b/core/scripts/keystone/src/01_provision_keystone.go index 53d5719dc8d..173d7c91dae 100644 --- a/core/scripts/keystone/src/01_provision_keystone.go +++ b/core/scripts/keystone/src/01_provision_keystone.go @@ -198,14 +198,6 @@ func provisionWorkflowDON( fmt.Println() deployForwarder(env, artefactsDir) - onchainMeta, _ = provisionOCR3( - env, - nodeSet, - chainID, - p2pPort, - ocrConfigFile, - artefactsDir, - ) distributeFunds(nodeSet.NodeKeys, env) // We don't technically need the capability registry as a dependency @@ -213,5 +205,5 @@ func provisionWorkflowDON( // We could remove it so that we can execute provisioning in parallel deployKeystoneWorkflowsTo(nodeSet, reg) - return onchainMeta + return LoadOnchainMeta(artefactsDir, env) } diff --git a/core/scripts/keystone/src/01_toolkit.go b/core/scripts/keystone/src/01_toolkit.go index 6fe896667ce..0de2b1004eb 100644 --- a/core/scripts/keystone/src/01_toolkit.go +++ b/core/scripts/keystone/src/01_toolkit.go @@ -9,7 +9,6 @@ import ( "fmt" "net/url" "os" - "strconv" "strings" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" @@ -29,8 +28,6 @@ func (t *Toolkit) Run(args []string) { if len(args) < 1 { fmt.Println("Available commands:") fmt.Println(" deploy-workflows") - fmt.Println(" deploy-ocr3-contracts") - fmt.Println(" deploy-ocr3-jobspecs") os.Exit(1) } @@ -42,10 +39,6 @@ func (t *Toolkit) Run(args []string) { t.AptosKeys(cmdArgs) case "deploy-workflows": t.DeployWorkflows(cmdArgs) - case "deploy-ocr3-contracts": - t.ProvisionOCR3Contracts(cmdArgs) - case "deploy-ocr3-jobspecs": - t.DeployOCR3JobSpecs(cmdArgs) default: fmt.Printf("Unknown command: %s\n", command) os.Exit(1) @@ -69,69 +62,6 @@ func (t *Toolkit) AptosKeys(args []string) { mustWriteJSON(*artefacts+"/pubnodekeys.json", keys) } -func (t *Toolkit) ProvisionOCR3Contracts(args []string) { - fs := flag.NewFlagSet("deploy-ocr3-contracts", flag.ExitOnError) - ethURL := fs.String("ethurl", "", "URL of the Ethereum node") - accountKey := fs.String("accountkey", "", "Private key of the deployer account") - chainID := fs.Int64("chainid", 1337, "Chain ID") - nodesListPath := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes") - artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location") - ocrConfigFile := fs.String("ocrfile", "ocr_config.json", "Path to OCR config file") - - if err := fs.Parse(args); err != nil || *ethURL == "" || *accountKey == "" { - fs.Usage() - os.Exit(1) - } - - // Set environment variables required by setupenv - os.Setenv("ETH_URL", *ethURL) - os.Setenv("ETH_CHAIN_ID", strconv.FormatInt(*chainID, 10)) - os.Setenv("ACCOUNT_KEY", *accountKey) - os.Setenv("INSECURE_SKIP_VERIFY", "true") - - env := helpers.SetupEnv(false) - - nodes := mustReadNodesList(*nodesListPath) - nodeKeys := mustFetchNodeKeys(*chainID, nodes, true) - - deployOCR3Contract(nodeKeys, env, *ocrConfigFile, *artefactsDir) -} - -func (t *Toolkit) DeployOCR3JobSpecs(args []string) { - fs := flag.NewFlagSet("deploy-ocr3-jobspecs", flag.ExitOnError) - - ethURL := fs.String("ethurl", "", "URL of the Ethereum node") - accountKey := fs.String("accountkey", "", "Private key of the deployer account") - chainID := fs.Int64("chainid", 1337, "Chain ID") - nodesListPath := fs.String("nodes", ".cache/NodesList.txt", "Path to file with list of nodes") - p2pPort := fs.Int64("p2pport", 6690, "P2P port") - artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location") - - if err := fs.Parse(args); err != nil || *ethURL == "" || *accountKey == "" { - fs.Usage() - os.Exit(1) - } - - os.Setenv("ETH_URL", *ethURL) - os.Setenv("ETH_CHAIN_ID", strconv.FormatInt(*chainID, 10)) - os.Setenv("ACCOUNT_KEY", *accountKey) - os.Setenv("INSECURE_SKIP_VERIFY", "true") - - env := helpers.SetupEnv(false) - - nodes := mustReadNodesList(*nodesListPath) - nodeKeys := mustFetchNodeKeys(*chainID, nodes, true) - o := LoadOnchainMeta(*artefactsDir, env) - - deployOCR3JobSpecs( - nodes, - *chainID, - nodeKeys, - *p2pPort, - o, - ) -} - func (t *Toolkit) DeployWorkflows(args []string) { fs := flag.NewFlagSet("deploy-workflows", flag.ExitOnError) workflowFile := fs.String("workflow", "", "Path to workflow file") diff --git a/core/scripts/keystone/src/02_provision_ocr3_capability.go b/core/scripts/keystone/src/02_provision_ocr3_capability.go deleted file mode 100644 index 7cd0291146c..00000000000 --- a/core/scripts/keystone/src/02_provision_ocr3_capability.go +++ /dev/null @@ -1,288 +0,0 @@ -package src - -import ( - "bytes" - "context" - "flag" - "fmt" - "strconv" - "text/template" - - focr "github.com/smartcontractkit/chainlink-deployments-framework/offchain/ocr" - - ksdeploy "github.com/smartcontractkit/chainlink/deployment/keystone/changeset" - - "github.com/smartcontractkit/libocr/offchainreporting2plus/types" - - "github.com/smartcontractkit/chainlink-evm/gethwrappers/keystone/generated/ocr3_capability" - - evm "github.com/smartcontractkit/chainlink-evm/pkg/relay" - helpers "github.com/smartcontractkit/chainlink/core/scripts/common" -) - -func provisionOCR3( - env helpers.Environment, - nodeSet NodeSet, - chainID int64, - p2pPort int64, - ocrConfigFile string, - artefactsDir string, -) (onchainMeta *onchainMeta, cacheHit bool) { - nodeKeys := nodeSet.NodeKeys - nodes := nodeSet.Nodes - - onchainMeta, cacheHit = deployOCR3Contract( - nodeKeys, - env, - ocrConfigFile, - artefactsDir, - ) - - deployOCR3JobSpecs( - nodes, - chainID, - nodeKeys, - p2pPort, - onchainMeta, - ) - - return -} - -func deployOCR3Contract( - nodeKeys []NodeKeys, - env helpers.Environment, - configFile string, - artefacts string, -) (o *onchainMeta, cacheHit bool) { - o = LoadOnchainMeta(artefacts, env) - ocrConf := generateOCR3Config( - nodeKeys, - configFile, - ) - - if o.OCR3 != nil { - // types.ConfigDigestPrefixKeystoneOCR3Capability - fmt.Println("OCR3 Contract already deployed, checking config...") - latestConfigDigestBytes, err := o.OCR3.LatestConfigDetails(nil) - PanicErr(err) - latestConfigDigest, err := types.BytesToConfigDigest(latestConfigDigestBytes.ConfigDigest[:]) - PanicErr(err) - - cc := ocrConfToContractConfig(ocrConf, latestConfigDigestBytes.ConfigCount) - digester := evm.OCR3CapabilityOffchainConfigDigester{ - ChainID: uint64(env.ChainID), //nolint:gosec // this won't overflow - ContractAddress: o.OCR3.Address(), - } - digest, err := digester.ConfigDigest(context.Background(), cc) - PanicErr(err) - - if digest.Hex() == latestConfigDigest.Hex() { - fmt.Printf("OCR3 Contract already deployed with the same config (digest: %s), skipping...\n", digest.Hex()) - return o, false - } - - fmt.Printf("OCR3 Contract contains a different config, updating...\nOld digest: %s\nNew digest: %s\n", latestConfigDigest.Hex(), digest.Hex()) - setOCRConfig(o, env, ocrConf, artefacts) - return o, true - } - - fmt.Println("Deploying keystone ocr3 contract...") - _, tx, ocrContract, err := ocr3_capability.DeployOCR3Capability(env.Owner, env.Ec) - PanicErr(err) - helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID) - o.OCR3 = ocrContract - setOCRConfig(o, env, ocrConf, artefacts) - - return o, true -} - -func generateOCR3Config(nodeKeys []NodeKeys, configFile string) ksdeploy.OCR3OnchainConfig { - topLevelCfg := mustReadOCR3Config(configFile) - cfg := topLevelCfg.OracleConfig - secrets := focr.XXXGenerateTestOCRSecrets() - c, err := ksdeploy.GenerateOCR3Config(cfg, nodeKeysToKsDeployNodeKeys(nodeKeys[1:]), secrets, nil) // skip the bootstrap node - helpers.PanicErr(err) - return c -} - -func setOCRConfig(o *onchainMeta, env helpers.Environment, ocrConf ksdeploy.OCR3OnchainConfig, artefacts string) { - fmt.Println("Setting OCR3 contract config...") - tx, err := o.OCR3.SetConfig(env.Owner, - ocrConf.Signers, - ocrConf.Transmitters, - ocrConf.F, - ocrConf.OnchainConfig, - ocrConf.OffchainConfigVersion, - ocrConf.OffchainConfig, - ) - PanicErr(err) - receipt := helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID) - o.SetConfigTxBlock = receipt.BlockNumber.Uint64() - WriteOnchainMeta(o, artefacts) -} - -func deployOCR3JobSpecs( - nodes []NodeWithCreds, - chainID int64, - nodeKeys []NodeKeys, - p2pPort int64, - onchainMeta *onchainMeta, -) { - ocrAddress := onchainMeta.OCR3.Address().Hex() - bootstrapURI := fmt.Sprintf("%s@%s:%d", nodeKeys[0].P2PPeerID, nodes[0].ServiceName, p2pPort) - - var specName string - for i, n := range nodes { - var spec string - - if i == 0 { - bootstrapSpecConfig := BootstrapJobSpecConfig{ - JobSpecName: "ocr3_bootstrap", - OCRConfigContractAddress: ocrAddress, - ChainID: chainID, - } - specName = bootstrapSpecConfig.JobSpecName - spec = createBootstrapJobSpec(bootstrapSpecConfig) - } else { - oc := OracleJobSpecConfig{ - JobSpecName: "ocr3_oracle", - OCRConfigContractAddress: ocrAddress, - OCRKeyBundleID: nodeKeys[i].OCR2BundleID, - BootstrapURI: bootstrapURI, - TransmitterID: nodeKeys[i].EthAddress, - ChainID: chainID, - AptosKeyBundleID: nodeKeys[i].AptosBundleID, - } - specName = oc.JobSpecName - spec = createOracleJobSpec(oc) - } - - api := newNodeAPI(n) - upsertJob(api, specName, spec) - - fmt.Printf("Replaying from block: %d\n", onchainMeta.SetConfigTxBlock) - fmt.Printf("EVM Chain ID: %d\n\n", chainID) - api.withFlags(api.methods.ReplayFromBlock, func(fs *flag.FlagSet) { - err := fs.Set("block-number", strconv.FormatUint(onchainMeta.SetConfigTxBlock, 10)) - helpers.PanicErr(err) - err = fs.Set("family", "evm") - helpers.PanicErr(err) - err = fs.Set("chain-id", strconv.FormatInt(chainID, 10)) - helpers.PanicErr(err) - }).mustExec() - } -} - -func mustReadOCR3Config(fileName string) (output ksdeploy.TopLevelConfigSource) { - return mustReadJSON[ksdeploy.TopLevelConfigSource](fileName) -} - -func nodeKeysToKsDeployNodeKeys(nks []NodeKeys) []ksdeploy.NodeKeys { - keys := []ksdeploy.NodeKeys{} - for _, nk := range nks { - keys = append(keys, ksdeploy.NodeKeys{ - EthAddress: nk.EthAddress, - AptosAccount: nk.AptosAccount, - AptosBundleID: nk.AptosBundleID, - AptosOnchainPublicKey: nk.AptosOnchainPublicKey, - P2PPeerID: nk.P2PPeerID, - OCR2BundleID: nk.OCR2BundleID, - OCR2OnchainPublicKey: nk.OCR2OnchainPublicKey, - OCR2OffchainPublicKey: nk.OCR2OffchainPublicKey, - OCR2ConfigPublicKey: nk.OCR2ConfigPublicKey, - CSAPublicKey: nk.CSAPublicKey, - }) - } - return keys -} - -// BootstrapJobSpecConfig holds configuration for the bootstrap job spec -type BootstrapJobSpecConfig struct { - JobSpecName string - OCRConfigContractAddress string - ChainID int64 -} - -// OracleJobSpecConfig holds configuration for the oracle job spec -type OracleJobSpecConfig struct { - JobSpecName string - OCRConfigContractAddress string - OCRKeyBundleID string - BootstrapURI string - TransmitterID string - ChainID int64 - AptosKeyBundleID string -} - -func createBootstrapJobSpec(config BootstrapJobSpecConfig) string { - const bootstrapTemplate = ` -type = "bootstrap" -schemaVersion = 1 -name = "{{ .JobSpecName }}" -contractID = "{{ .OCRConfigContractAddress }}" -relay = "evm" - -[relayConfig] -chainID = "{{ .ChainID }}" -providerType = "ocr3-capability" -` - - tmpl, err := template.New("bootstrap").Parse(bootstrapTemplate) - if err != nil { - panic(err) - } - - var rendered bytes.Buffer - err = tmpl.Execute(&rendered, config) - if err != nil { - panic(err) - } - - return rendered.String() -} - -func createOracleJobSpec(config OracleJobSpecConfig) string { - const oracleTemplate = ` -type = "offchainreporting2" -schemaVersion = 1 -name = "{{ .JobSpecName }}" -contractID = "{{ .OCRConfigContractAddress }}" -ocrKeyBundleID = "{{ .OCRKeyBundleID }}" -p2pv2Bootstrappers = [ - "{{ .BootstrapURI }}", -] -relay = "evm" -pluginType = "plugin" -transmitterID = "{{ .TransmitterID }}" - -[relayConfig] -chainID = "{{ .ChainID }}" - -[pluginConfig] -command = "chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" - -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "{{ .OCRKeyBundleID }}" -aptos = "{{ .AptosKeyBundleID }}" -` - - tmpl, err := template.New("oracle").Parse(oracleTemplate) - if err != nil { - panic(err) - } - - var rendered bytes.Buffer - err = tmpl.Execute(&rendered, config) - if err != nil { - panic(err) - } - - return rendered.String() -} diff --git a/core/scripts/keystone/src/02_provision_ocr3_capability_test.go b/core/scripts/keystone/src/02_provision_ocr3_capability_test.go deleted file mode 100644 index df4d9a41f5c..00000000000 --- a/core/scripts/keystone/src/02_provision_ocr3_capability_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package src - -import ( - "errors" - "fmt" - "testing" - - "github.com/gkampitakis/go-snaps/match" - "github.com/gkampitakis/go-snaps/snaps" -) - -func TestGenerateOCR3Config(t *testing.T) { - // Generate OCR3 config - nodeSet := downloadNodeSets(1337, "./testdata/node_sets.json", 4) - nodeKeys := nodeSet.Workflow.NodeKeys - config := generateOCR3Config(nodeKeys, "./testdata/SampleConfig.json") - - matchOffchainConfig := match.Custom("OffchainConfig", func(s any) (any, error) { - // coerce the value to a string - s, ok := s.(string) - if !ok { - return nil, errors.New("offchain config is not a string") - } - - // if the string is not empty - if s == "" { - return nil, errors.New("offchain config is empty") - } - - return "", nil - }) - - snaps.MatchJSON(t, config, matchOffchainConfig) -} - -func TestGenSpecs(t *testing.T) { - nodeSetsPath := "./testdata/node_sets.json" - chainID := int64(1337) - p2pPort := int64(6690) - contractAddress := "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A" - nodeSet := downloadNodeSets(chainID, nodeSetsPath, 4).Workflow - - // Create Bootstrap Job Spec - bootstrapConfig := BootstrapJobSpecConfig{ - JobSpecName: "ocr3_bootstrap", - OCRConfigContractAddress: contractAddress, - ChainID: chainID, - } - bootstrapSpec := createBootstrapJobSpec(bootstrapConfig) - - // Create Oracle Job Spec - oracleConfig := OracleJobSpecConfig{ - JobSpecName: "ocr3_oracle", - OCRConfigContractAddress: contractAddress, - OCRKeyBundleID: nodeSet.NodeKeys[1].OCR2BundleID, - BootstrapURI: fmt.Sprintf("%s@%s:%d", nodeSet.NodeKeys[0].P2PPeerID, nodeSet.Nodes[0].ServiceName, p2pPort), - TransmitterID: nodeSet.NodeKeys[1].P2PPeerID, - ChainID: chainID, - AptosKeyBundleID: nodeSet.NodeKeys[1].AptosBundleID, - } - oracleSpec := createOracleJobSpec(oracleConfig) - - // Combine Specs - generatedSpecs := fmt.Sprintf("%s\n\n%s", bootstrapSpec, oracleSpec) - - snaps.MatchSnapshot(t, generatedSpecs) -} diff --git a/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap b/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap deleted file mode 100755 index 9d38f78899b..00000000000 --- a/core/scripts/keystone/src/__snapshots__/02_provision_ocr3_capability_test.snap +++ /dev/null @@ -1,65 +0,0 @@ - -[TestGenerateOCR3Config - 1] -{ - "F": 1, - "OffchainConfig": "", - "OffchainConfigVersion": 30, - "OnchainConfig": "0x", - "Signers": [ - "011400321bc7af41a634375526006365a31bf32b4cfa7c0520004ca789105da974eec967758ad32b575741d6cb36c1bb3bcfd87b235502cc1753", - "0114005192c43a68efb7a698c0459ff8591a115da128ee052000169008927a60e6c03e99aac6fa268dabaf4d00e117419861d87836211267361b", - "011400ed613636925af2df6ed8332d95028eabcbe95a3f052000ce86b34de67249f92058f69e47961907ebbf8a71c12123f1d2a7cab4874f6365", - "01140053b5bbc0efa2e2d2770029bab5d5a647a260a72b052000f2cb4932d3ce8c10bf67c60d35372a5ff1578255e25c2a119c2dea70e919567a" - ], - "Transmitters": [ - "0x75cf1355cC4Eb358feaBb9e269a4DAEeB6721DBB", - "0xc6dcE30f492CBD223b9946603192f22D86e783ca", - "0x1289d00A6565Afcd6437B09548F6019EF49696d0", - "0x4b92B0aaC39932B7302676F48e78FA91852DC0EE" - ] -} ---- - -[TestGenSpecs - 1] - -type = "bootstrap" -schemaVersion = 1 -name = "ocr3_bootstrap" -contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A" -relay = "evm" - -[relayConfig] -chainID = "1337" -providerType = "ocr3-capability" - - - -type = "offchainreporting2" -schemaVersion = 1 -name = "ocr3_oracle" -contractID = "0xB29934624cAe3765E33115A9530a13f5aEC7fa8A" -ocrKeyBundleID = "20ccdc97afdf467465590115e3da4e5eb591bf5f43808e81a5d0807cd889b3c7" -p2pv2Bootstrappers = [ - "12D3KooWFSmZaLFF1nu3mzxPKj43F89WgVDqkpvwFUHBfMHSqpVq@app-0-ks-wf-bt-node1:6690", -] -relay = "evm" -pluginType = "plugin" -transmitterID = "12D3KooWHhXyDmHB6D1UQosLXmhczw3zxB3DLYBuq9Unb4iCD4Sc" - -[relayConfig] -chainID = "1337" - -[pluginConfig] -command = "chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" - -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "20ccdc97afdf467465590115e3da4e5eb591bf5f43808e81a5d0807cd889b3c7" -aptos = "ac364cec9fe7d9ea1035fc511e5b2f30900caa6e65ac0501168005d05129e088" - ---- diff --git a/core/services/cre/cre.go b/core/services/cre/cre.go index b4504a95b88..b0680a8c026 100644 --- a/core/services/cre/cre.go +++ b/core/services/cre/cre.go @@ -41,7 +41,6 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/capabilities/localcapmgr" "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote" remotetypes "github.com/smartcontractkit/chainlink/v2/core/capabilities/remote/types" - capStreams "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/keystore" @@ -603,14 +602,7 @@ func (w *dispatcherWrapper) newSubservices( } func newLocalTestMetadataRegistry(localCfg config.LocalCapabilities) *capabilities.TestMetadataRegistry { - registry := &capabilities.TestMetadataRegistry{} - if localCfg != nil && localCfg.GetCapabilityConfig(capStreams.MockTriggerCapabilityID) != nil { - // The mock streams trigger emits 2F+1 signatures, so the synthetic local - // workflow DON needs to advertise F=1 only for that opt-in compatibility path. - registry.WorkflowDONF = 1 - } - - return registry + return &capabilities.TestMetadataRegistry{} } // newDispatcherWrapper creates a new dispatcherWrapper service with peer wrappers if peering is enabled diff --git a/core/services/cre/cre_test.go b/core/services/cre/cre_test.go index 3e4bdd3a182..b765e8b9747 100644 --- a/core/services/cre/cre_test.go +++ b/core/services/cre/cre_test.go @@ -8,7 +8,6 @@ import ( commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" - capStreams "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" "github.com/smartcontractkit/chainlink/v2/core/config" "github.com/smartcontractkit/chainlink/v2/core/utils" ) @@ -65,40 +64,6 @@ func testWorkflowRegistry(addr string, urls ...string) config.CapabilitiesWorkfl return wfRegTestStub{addr: addr, additionalURLs: urls} } -type testLocalCapabilities struct { - cfgs map[string]config.CapabilityNodeConfig -} - -func (t testLocalCapabilities) RegistryBasedLaunchAllowlist() []string { - return nil -} - -func (t testLocalCapabilities) Capabilities() map[string]config.CapabilityNodeConfig { - return t.cfgs -} - -func (t testLocalCapabilities) IsAllowlisted(string) bool { - return false -} - -func (t testLocalCapabilities) GetCapabilityConfig(capabilityID string) config.CapabilityNodeConfig { - if t.cfgs == nil { - return nil - } - - return t.cfgs[capabilityID] -} - -type testCapabilityNodeConfig struct{} - -func (testCapabilityNodeConfig) BinaryPathOverride() string { - return "" -} - -func (testCapabilityNodeConfig) Config() map[string]string { - return nil -} - func TestWorkflowRegistrySemverMajor(t *testing.T) { t.Parallel() @@ -138,33 +103,6 @@ func TestWorkflowRegistryConfigured(t *testing.T) { func TestNewLocalTestMetadataRegistry(t *testing.T) { t.Parallel() - tests := []struct { - name string - localCfg config.LocalCapabilities - expectedF uint8 - }{ - { - name: "default workflow DON fault tolerance", - localCfg: nil, - expectedF: 0, - }, - { - name: "mock trigger opt-in uses workflow DON fault tolerance one", - localCfg: testLocalCapabilities{ - cfgs: map[string]config.CapabilityNodeConfig{ - capStreams.MockTriggerCapabilityID: testCapabilityNodeConfig{}, - }, - }, - expectedF: 1, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - registry := newLocalTestMetadataRegistry(tt.localCfg) - require.Equal(t, tt.expectedF, registry.WorkflowDONF) - }) - } + registry := newLocalTestMetadataRegistry(nil) + require.Equal(t, uint8(0), registry.WorkflowDONF) } diff --git a/core/services/standardcapabilities/delegate.go b/core/services/standardcapabilities/delegate.go index 63a37c1bcef..7f82b22f0b3 100644 --- a/core/services/standardcapabilities/delegate.go +++ b/core/services/standardcapabilities/delegate.go @@ -66,7 +66,6 @@ type Delegate struct { creSettings core.SettingsBroadcaster ocrConfigService capregconfig.OCRConfigService localCfg coreconfig.LocalCapabilities - initErr error isNewlyCreatedJob bool } @@ -100,11 +99,6 @@ func NewDelegate( localCfg coreconfig.LocalCapabilities, opts ...func(*gateway.RoundRobinSelector), ) *Delegate { - initErr := registerOptionalMockStreamsTrigger(logger, localCfg, registry) - if initErr != nil { - logger.Errorw("Failed to register optional mock streams trigger", "err", initErr) - } - return &Delegate{ logger: logger, ds: ds, @@ -125,7 +119,6 @@ func NewDelegate( creSettings: creSettings, ocrConfigService: ocrConfigService, localCfg: localCfg, - initErr: initErr, selectorOpts: opts, } } @@ -140,10 +133,6 @@ func (d *Delegate) BeforeJobCreated(job job.Job) { } func (d *Delegate) ServicesForSpec(ctx context.Context, spec job.Job) ([]job.ServiceCtx, error) { - if d.initErr != nil { - return nil, d.initErr - } - command := spec.StandardCapabilitiesSpec.Command configJSON := spec.StandardCapabilitiesSpec.Config @@ -186,10 +175,6 @@ func (d *Delegate) NewServices( oracleFactoryConfig job.OracleFactoryConfig, capabilityDonID uint32, ) ([]job.ServiceCtx, error) { - if d.initErr != nil { - return nil, d.initErr - } - log := d.logger.Named("StandardCapabilities").Named(strconv.Itoa(int(jobID))).Named(jobName) kvStore := job.NewKVStore(jobID, d.ds) diff --git a/core/services/standardcapabilities/local_capabilities.go b/core/services/standardcapabilities/local_capabilities.go deleted file mode 100644 index a8b9aad85c8..00000000000 --- a/core/services/standardcapabilities/local_capabilities.go +++ /dev/null @@ -1,22 +0,0 @@ -package standardcapabilities - -import ( - "github.com/smartcontractkit/chainlink-common/pkg/types/core" - - capStreams "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" - coreconfig "github.com/smartcontractkit/chainlink/v2/core/config" - "github.com/smartcontractkit/chainlink/v2/core/logger" -) - -func shouldRegisterMockStreamsTrigger(localCfg coreconfig.LocalCapabilities) bool { - return localCfg != nil && localCfg.GetCapabilityConfig(capStreams.MockTriggerCapabilityID) != nil -} - -func registerOptionalMockStreamsTrigger(lggr logger.Logger, localCfg coreconfig.LocalCapabilities, registry core.CapabilitiesRegistry) error { - if !shouldRegisterMockStreamsTrigger(localCfg) { - return nil - } - - _, err := capStreams.RegisterMockTrigger(lggr, registry) - return err -} diff --git a/core/services/standardcapabilities/local_capabilities_test.go b/core/services/standardcapabilities/local_capabilities_test.go deleted file mode 100644 index c3a6cc0f61c..00000000000 --- a/core/services/standardcapabilities/local_capabilities_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package standardcapabilities - -import ( - "testing" - - "github.com/stretchr/testify/require" - - capStreams "github.com/smartcontractkit/chainlink/v2/core/capabilities/streams" - "github.com/smartcontractkit/chainlink/v2/core/config" -) - -type mockTriggerLocalCapabilities struct { - cfgs map[string]config.CapabilityNodeConfig -} - -func (s mockTriggerLocalCapabilities) RegistryBasedLaunchAllowlist() []string { - return nil -} - -func (s mockTriggerLocalCapabilities) Capabilities() map[string]config.CapabilityNodeConfig { - return s.cfgs -} - -func (s mockTriggerLocalCapabilities) IsAllowlisted(string) bool { - return false -} - -func (s mockTriggerLocalCapabilities) GetCapabilityConfig(capabilityID string) config.CapabilityNodeConfig { - if s.cfgs == nil { - return nil - } - return s.cfgs[capabilityID] -} - -type mockTriggerCapabilityNodeConfig struct{} - -func (mockTriggerCapabilityNodeConfig) BinaryPathOverride() string { - return "" -} - -func (mockTriggerCapabilityNodeConfig) Config() map[string]string { - return nil -} - -func TestShouldRegisterMockStreamsTrigger(t *testing.T) { - t.Parallel() - - tests := []struct { - name string - localCfg config.LocalCapabilities - want bool - }{ - {name: "nil config", localCfg: nil, want: false}, - { - name: "different local capability", - localCfg: mockTriggerLocalCapabilities{ - cfgs: map[string]config.CapabilityNodeConfig{ - "cron@1.0.0": mockTriggerCapabilityNodeConfig{}, - }, - }, - want: false, - }, - { - name: "mock trigger opted in", - localCfg: mockTriggerLocalCapabilities{ - cfgs: map[string]config.CapabilityNodeConfig{ - capStreams.MockTriggerCapabilityID: mockTriggerCapabilityNodeConfig{}, - }, - }, - want: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - require.Equal(t, tt.want, shouldRegisterMockStreamsTrigger(tt.localCfg)) - }) - } -} diff --git a/core/services/workflows/cmd/cre/examples/compile_test.go b/core/services/workflows/cmd/cre/examples/compile_test.go index fe2331e5fc1..8972a6ba06d 100644 --- a/core/services/workflows/cmd/cre/examples/compile_test.go +++ b/core/services/workflows/cmd/cre/examples/compile_test.go @@ -14,7 +14,6 @@ const pathPrefix = "core/services/workflows/cmd/cre/examples" func Test_AllExampleWorkflowsCompileToWASM(t *testing.T) { t.Parallel() paths := []string{ - "legacy/data_feeds", "v2/http_read", "v2/simple_cron", "v2/simple_cron_with_config", diff --git a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_100_feeds.json b/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_100_feeds.json deleted file mode 100644 index 1bbce78f1cf..00000000000 --- a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_100_feeds.json +++ /dev/null @@ -1,304 +0,0 @@ -{ - "feeds": { - "0xe4048550c01d087ae9355e5de48be650eea298fa7977c90bc704c4250133e8d1": { - "remapped_id": "0x01c1e747fc328157e0b2c9ca7e02be13148bb716010a8755f0c18ce8916792e9" - }, - "0xbf1e5a403a2ca43cf51a930baa86958594a2fc8f36f3f0e15e8b7d17563572b0": { - "remapped_id": "0x011149259e466a0ebf9196f445b399c60c4ec706a5c5a25080c8665357f88733" - }, - "0xc761cc7c9b0c9ba5f4946cc808ee6b36aaf5107006e63e428cf37cac746e943f": { - "remapped_id": "0x018d60e87ae4e9f6f9d7f052f9ff8ac62fe6d621c5b671b1646c23d87721cd00" - }, - "0xe9034cecddb9c292f653590a6aebb9ae847ae0855e390f5e036526630c5d8c88": { - "remapped_id": "0x01560d98484f69bca5e745a4eb427c896cfdd3f7c08ce6ef36f0805511cd2fa7" - }, - "0x1ee461b68b16eedb8d7e29d47bfb7972216947337858ee2d39691684af761f05": { - "remapped_id": "0x018456724684ca6fb178590b2ff4af954065a1783eac907f6e87f27fc8c4437f" - }, - "0x152061619eaf3ba6b2845fb819ef7d852998a59b10bc44ee1fd6b78b187571c9": { - "remapped_id": "0x01bd425bd66b61278b33e093d8ff092265330693749c11ab47b224462b12da20" - }, - "0x1f39b374fbd3337fec2d1b79b34b512e9ee1e8fe2c22f94861e8c2895d34bc6f": { - "remapped_id": "0x01787cdd383eee9993a4890ec0db330a59038ae8490dc174df84228ca3ea654b" - }, - "0x294f5c90d09743eb713239ad90963303b711546b5c3866889a55fb0c3731effb": { - "remapped_id": "0x012365c11bf5fd29e5879f6f7671a91770d955b1e0efd36167eec769e2c27391" - }, - "0x6838045b4c1ec1f23fb670e581eb084eb036980683e8423470346b17ac495618": { - "remapped_id": "0x018797fd82c3dc89c375f70d6a2aeb119652f571c37eeee618ecab78fc8c6302" - }, - "0x44e2b1352600243b0bce859360e3bc775c0499bedd7998ce56eb90e888ee9f7f": { - "remapped_id": "0x01ee5c76aa14d97de727de7ffe27c30eada02aabb7596c4e37e60a4140bfc476" - }, - "0x302cfdc2060c9575d9e52b25fe233e9fc2c2aa97600139ee3496b15a7d1d925f": { - "remapped_id": "0x01ec9ae756d923aecfa0c9d0055906b9301856c9eac49dc62fde4d7dc8eb4e8c" - }, - "0x13218c2a7ebd5b14a318f337faaa4f115abd8fa9e245558778b79b16a41088ef": { - "remapped_id": "0x0128a32866e13e1d8cef508e7c235bbe47a09f76100f45fda21da33e7f44b785" - }, - "0xe39905ba380e8348671fb23ac5b164f9308e2b7efe57543c131c78931cdc3e42": { - "remapped_id": "0x01293b03a5b9216742aadd3f3e7bb149acbddc72cd0d066cea9f396b944bc5f5" - }, - "0x87c961215b18aa9153edebfe0a818cd08ffec3aa7fd5a78e5d09964cd8506cfd": { - "remapped_id": "0x01677cccb932f5ab3ad1525ca4989eaada79856f98a09807feb0030512da3bef" - }, - "0x765cb4c90949dd2bd71b363f8b9e271e1c19ce9a762203f1cfc0bd515bc17c0c": { - "remapped_id": "0x01f14f3dbffea64669117a17ac0714c88ba9271c7992fea00937844c597b4852" - }, - "0xdbfb3acc21eb41d50a21407b049e875a24f5057a2ba18efdcfccc02acb0e01d9": { - "remapped_id": "0x0181f7c56e69f7adf07403502f229f4d4cab1894f34fd87aadb671ec3cf7675a" - }, - "0x715038d31b65cfb61fc5ee43f0a5c3a4971a7ef6b62c8dc4d954e09a6f7d1aee": { - "remapped_id": "0x014397a19912e0e354a2dd40e563051c9709f74dcb38a1ca1440f4413da299f2" - }, - "0xc250cbadada8b688daec2c00761c4f677796d9b51274b6f22cc6a3e53ebffa84": { - "remapped_id": "0x0118cdaec0e85022700227dfe92657e7473b4cdce6142b9feae5f8425dfc9e2b" - }, - "0xecf54b2e07ef9e67b11f11eb59455661772927c73e2d20642e2cf65f9f3cb81c": { - "remapped_id": "0x01f3bd0919748114aa1e63f17640fc91bc2ab82989b968d54a2399bdf86d4db7" - }, - "0x753d313d378d3e799c077970ffa41e21f2a532e562fcfa4d9065fd30263c4b5d": { - "remapped_id": "0x01d965c4af0450d34c8fba4d599ee3aef100be47cad96e67545d0444f3913360" - }, - "0xe8f3e49d0492aba449717cca69b3f114b7e5922a58873c98d8c88e6990bb67ed": { - "remapped_id": "0x01a31672109dbf208077d18906e0a754575a89df1e33b23b3c4818b0a6e54607" - }, - "0x4871513efc5abc872f1a89c1006005aa14110edfaf37a8bee3304074d15132cd": { - "remapped_id": "0x01181959ad4920b4c1dcadef39c762f77423a14e4b93da3689e69a80b7302b3a" - }, - "0xde41ddd0c24af9404a819761d6057101a48b7d1b0d12b3c83118d675a6585a50": { - "remapped_id": "0x01fa54875eb54bb6044121bfccdd5030651ee1bd9c837a5882586c27a0526d86" - }, - "0xdbdba37a0512124f7aa23f10288c6299627f5c36c0c08745ecb5e19106276cb0": { - "remapped_id": "0x016b761641c69e3684d6bca792eff13b3b784a927205690cb61082aaa3d0416a" - }, - "0x716a518140dd9a1184ae5cb66153f66124801318a0fd7f8f00d8b6c5aebf946f": { - "remapped_id": "0x015040f8aa73f699df4b1b3ad40bf78a8ba667bca470c78682129a63d5d415cf" - }, - "0xf4fd045de98fe4139fad2092f1ce16daba2f70d942ef16a7f439007b75b9079f": { - "remapped_id": "0x01a82466bcc40bc637eb3b3ec199b8cb4f6d4006f65b72dde588cf5ff3c12134" - }, - "0x34f0df33349a1e2e118de8e12b20af605d4b55a36ba5189ba51c1d9fca5c02fa": { - "remapped_id": "0x01686b44b1fc0f938ddd53f07a79f99de621ef5353a89d95970fe7a33d8fad69" - }, - "0x7503ef2a240c9c3bd1f6c1162e9f94ff4e2bf3eb4f4d5fecb9299c33c0c4dca3": { - "remapped_id": "0x018ca92c6fdf513e621f46a772662df3bfd94ec6ab92fa0e66cc699cc0c99f89" - }, - "0xe0eff0fab7be21544a43a8b8ddc92c7b3a5ba3c749b9a46a64f7cd5027432151": { - "remapped_id": "0x0130b166c22c2e74a505df8b4a6819f1d22bbc62a9f8f9a0e3fa232f65eee836" - }, - "0xaacb7304b82b361a965265d37df9f19c7e1b54d2e4ef81b2a3c81eae7f0e243e": { - "remapped_id": "0x0149679ef315cb72721aa90c28253b596a44c7a1f49a5619e1f6170d186cae84" - }, - "0x6de298815a347e4b3386037f3e6f15047e8b0541368be66018c3cc3f10d6d4e6": { - "remapped_id": "0x01b586e9dfb8e63dba86eb9c3e562f09c38a9f628666941ed685a148441659fe" - }, - "0x4d1fa6f32723839a256efa192dddd855866e4bab8799e72f7ff68c71e4796159": { - "remapped_id": "0x0107cfe100798855160d4a91e89c9f755700f97d15da4344f7d51a3c710e0eb2" - }, - "0xaf3b177702cc291ac69a05bfd60802384a55115c86999829c9183be10b3814f3": { - "remapped_id": "0x01ca4956d98bee4dfa46c4e10afe81d13bb3059b505972d6b2b1b01135d08118" - }, - "0x8cb3bab8fe8c1ab8aa8ef99ec09681a2cf26f6918ad3a5b69077f2bd05a40875": { - "remapped_id": "0x0159c37175e656f06d13e35e3740fa4cbefbeda0e792f1e73fe72aee6d1db262" - }, - "0xb0ebbb740f0cde75e1937e3f2146f7125b9a557124f7637d30728e3d2f39eea5": { - "remapped_id": "0x01dd7b917fd4ef94bf17e9c8314779b5528131b04acd23b713fc041a0354ccee" - }, - "0x8c21b1ae1624c362ae99b6c4860123b90e437163e2a72659e453445148d869b8": { - "remapped_id": "0x01de662d0b0c80a3afb1e2677141aab61c0340d4b5cd57c81874dfbd8b1c7351" - }, - "0xa8dbc238a9680ab8dd48a0ec00357b661ef87dc7d7d06e419d6f1a25cb56e685": { - "remapped_id": "0x01d06184e8bceab5b503cf1469e1f552b63004f35eeb84afc231ee34c5ca9861" - }, - "0xbbb663486d196aa65726d8d848c554c1c58dd5b8cdcec2a2227cbcbc693b7efc": { - "remapped_id": "0x013e053d89a14396a9df2669fdbf9dff124c8cf84feb1c3f614867b64365600e" - }, - "0xa299bc9b4153215aa437c87372c1f10db1daa30377250e68b0e3a7797b55b5ef": { - "remapped_id": "0x01a90e1f8d8e25c57667e7c83ce1a44374d2308becc036414681d411b17e5ee3" - }, - "0xd36fdb1fecc8bae98881f823d4ff73515e57adeb3c50b51c620ffcf34d2f3a4e": { - "remapped_id": "0x01325d921664c13c9f3f3f17b446b4c90fbf1f1bff000ea67211780e3af4678d" - }, - "0xfe7dbcb36e6deeeecd1991115c8ed3f65bbeffe1f6c5a314e1d362ac83b51e2e": { - "remapped_id": "0x019150cce2056bb09299338fd6a6ef1d7f47691e89701f6682c3a26cfa291178" - }, - "0x29062e169a95896cbebafcdab69b44e17114d073153328eff9d26565e8db6a34": { - "remapped_id": "0x01ed8ffd75657ea83fb2b8bd46aeacda634fc3af4b251e65c18719941d5cc393" - }, - "0xb4d73be68c9dd686b2e4d7df84c16ba6f922f6c357c80297af6803f8521bf4c3": { - "remapped_id": "0x0180857ce561de38fdd9d28b4773d0182a401d1885ebc92b632ed691d1faa9c4" - }, - "0x2e9a43fdee9d4372f5d8dd8c3697852566e1eb66d7f6fc0ca346c76f32a5a091": { - "remapped_id": "0x01b79ec97f7705291518fdcd846ee787d3db0d680598cdcd68bb90ae36a25cbd" - }, - "0x1b9129a1fa476f831d13e23d5299f467ef01cd9eef9d28548f5c9aa654b53c1d": { - "remapped_id": "0x018567b293ce0a58624eba8adaf8ce617fd1dcc842d55fc152edc4c3d50f4810" - }, - "0x4e46acd4e779a55f392d08c2c275782139e21c60f81fb1e4d27a042732fbfaab": { - "remapped_id": "0x01b0da35e1d5ce6a1a307ab681cfa3976ce2ed04d8de715ca00898b462bd0a99" - }, - "0x68b31ada05c1c4b28c0479c800a60ec23d27b8705ca2c829dcc1c8db98d558ce": { - "remapped_id": "0x01c217590dfb49516310a23781a913cbc9a99816c51a294778826c7226f7fd64" - }, - "0x57933f786d29f187c9ce96eb06ccac5d6840c0b275e32a024d77be70f82890fb": { - "remapped_id": "0x01df55ec28583a022960b52e598f4f4775e2b91cec067c6772d2c1c3ab5ef959" - }, - "0x5037b4a79ea9e5b4633e0d051569c933be3d115b410023843fa68c3b492769b5": { - "remapped_id": "0x018994bbf0ce735d5aa1ce9e15ff056124a3422e2aa286fcf6db83012a2a677f" - }, - "0x098037196c6d58124475016137f2bdf286d90fc7a222294c358a112058d39dd6": { - "remapped_id": "0x01c815526986b8cab863dc68b46dbb3ca429124ccaf55779ed93106555b81283" - }, - "0x988cc8cbf87cb6aa84b48596a993b2b3cbc9b36c1eda45aff96bd1d800415f5b": { - "remapped_id": "0x010a772e8a88895edfb356b4f4c4300776963d2fe054915782e7e64975e10880" - }, - "0xcc0cea6fa2cc0f1262d8b14332cfc359849a99f3fb1736a6fbb7877826c440ae": { - "remapped_id": "0x01c08b61712d823d8555b800e562a47dd4bbff68fd1a52698c07d721a03771b9" - }, - "0xa91ad3b02d433735f2709f2fcd1ef1e235ca035872b41ca0dbcfba3180949307": { - "remapped_id": "0x01f6304e66a36fd8c52e1d787c85a527e1635ac843a11295a6d784a61acdd327" - }, - "0x8b9c5aeee757fe64f0c2969815eae7e6708064af3a5ab197faddae806facc513": { - "remapped_id": "0x01fcdc45866eb0fab5cde5861a96a4776c5bef971584a6c1bfbcdc97ab9dbd57" - }, - "0xe8ede2df615a58e1e53d048bfe5899b18d4d1788625fa9364d6687e460df0af2": { - "remapped_id": "0x0101be76de1f785da1889165f54fe6d8f6379f06fc4ab0cb9fd58532cd3d7d02" - }, - "0x7815ea0a56d447b439b9e1845f3ee64f66c6bd8b8e8799af4923b74ecfca536e": { - "remapped_id": "0x01f71aa5eadf21f6e1f9181372de1757f9779134c8decae116c3824c7415d468" - }, - "0xbc0017570e38443f7012f6310964e2dce739a895aa2ca6d11db141d29dd921c1": { - "remapped_id": "0x01b793f05d597e6a5343235a8e993624f44f95bfc2a95ea21a322b5ef0e541ba" - }, - "0xa467155c093a2bb26887e85c248dea9bc2a4c52359c15629f68ad3495d6acbc1": { - "remapped_id": "0x01e032724237896f7a13ad5a73a1f5f58cc4e2d118e95c2ed6b1bd1fa12adf7d" - }, - "0xae300cac97fcd7114f421a01988fa1afdc92d3fed80b5a70a50b7e5b40f3d7d5": { - "remapped_id": "0x018d375d012881bceb001ae9ebf70b085dab82dfa1905c67ba133ca1a6d3d420" - }, - "0x02e53ddb125189564f89d5584564c7545bd7e84e769eb7e63065d4485f38b2b8": { - "remapped_id": "0x01e96876cd0bcbfcbb40c1c774bb8adfbca984cb5488112ee185bc002d9addba" - }, - "0x7cae125ceb3e5f08ee7739b459ae40f03a5f0610cf93c6fa8128eaccc8f2b170": { - "remapped_id": "0x01497c58e51684d2beabc2f579395001ef97da8d2df2f217b1be4e217fb25505" - }, - "0xd57b76eb43db4daac560e9401b9e9fde3282cb6c107f2a9ec9e342e703b5c5e9": { - "remapped_id": "0x01e376081f092d92189e7c9ccb9ae7e85eba32b57aff393012ebce10753b7b94" - }, - "0x18545112958d07d09634caed1856c2adabf3e3332f0b722c5fe1ae3abbb96ba4": { - "remapped_id": "0x0181fcf1015ee27640b1600d4b8617315491d7d81b3c43c4efc08b5131db7ec6" - }, - "0x362c52f91060f9a5a5e2b5782c6fb663786f92e78106070a8fd3a5f8d9ee8599": { - "remapped_id": "0x016731001ff6f61ba6ec3dec70cdce8744b97c43370e61bb354629c8b1f6667d" - }, - "0x8d743eb6b81a9ea419fc6d96e55edcea917b61a32c41c2bdfa6039f00891d5a5": { - "remapped_id": "0x01861d92afd8353630c2a922a4480e99b00898ed5931322af3e4e5a9b263cc5c" - }, - "0x73ed7eb99517bc260b2e82d3a14615f8142f208857e53f5af33c3dc3e6ad2c99": { - "remapped_id": "0x01a89e326dc4de0f98cba0baeda023d268ea31ef55f78e827ba7c6fa00125ec0" - }, - "0xc84f988a43cf10bee6c8445e779337aea2aaf06feed9946d2680d3c3a007bfd4": { - "remapped_id": "0x01f0389e7d3e5b27ed0ea54d075bb2c9f1d9568c185df79bb223cb5f9adaef95" - }, - "0x7d558aa77d0771fe321cd13dbdbdbaba68cb538ae77a6c5520e2c634ec62b524": { - "remapped_id": "0x01f895c109528d6b565f6dd75ece5bd97b36d755c08d28d0a7e57606ae186c64" - }, - "0x813678b762095de2c4023f8ea60e7230baf1bcb32ad7a85a7e7bc2f15e6031e8": { - "remapped_id": "0x010c1ae68fd6beeabfc4900206b04424d6cea56d994dd0f730ba03e2b21e8965" - }, - "0x7a4862e9a73dfc445254fc9e17c07ae89926da876be9cb94124eca842560d7ea": { - "remapped_id": "0x0128319a7dc6f427a0290a98ccf2312ea6f4c8500125e29f8c21f3bac48a48c4" - }, - "0x80ba44f3bda4a52f213b5eabc3896613899e8a62eea00557af8be3b758136a91": { - "remapped_id": "0x014b3c0b40318c98433c3f97d16327dff397b7a79341526747531a386c543412" - }, - "0xaf26866ac2a322885f651258013190fc2bf4ed20dda482e96c55e084ef9f3933": { - "remapped_id": "0x01110e10ad1edc1fb789f663778c20a498a8de3da99579c7ca6702cf2cf4e44b" - }, - "0x5bd78eb1c2065cc80ae7224a052284513e5cf8312feca48a60983486237e60ca": { - "remapped_id": "0x01744a32332a81622c98764cf133b932dc4934da2a91268c7bfba648128e33bb" - }, - "0x53b64f0a73d6447925cacbb7909869ac567d4d8bf1135ed78aeb034282df849c": { - "remapped_id": "0x01609ae7c362eaff50eccd5726d39c0a25c494cc8b40edd1b1e72bf79a90c7a6" - }, - "0x0acecc90a25aa9aa3c3f41ca28fd0eddfee2953dd6a5b05e2eb52ab14abbd80d": { - "remapped_id": "0x01796cf249368a9944f39fb14f11a9e99c926da4adbe9d72f590f67738f6fda5" - }, - "0x4198cb99674ac60a9466a31627871f18e215ad78d58eb437c985c8f6af889d70": { - "remapped_id": "0x01bb2725c3c54ba24f10e0bcec3d134e8e362cf4aa88f66145d7b1f54a3f2fba" - }, - "0xb6750776f78786c8813cd5245073f31cdfa5ee03112f149c495cedd6e9652af8": { - "remapped_id": "0x01f152d5b97c23c63cb80335d90ff8e9d763aa98a73f842e199340bc21fab789" - }, - "0x29872a7e3238439013c102179bb3b8bab365360471ba4ca9b7020b4fa16d5f6a": { - "remapped_id": "0x0165e8e5a845a44dfda8ef7cee9793c729b927bea596150bc69315a99ccba635" - }, - "0xfe105a304323645464c6956b0a9905fe2ee60795d93dab9d25123aa3be3d820e": { - "remapped_id": "0x01453427c9206116ef58171f37c7bcf9965653817070663a999f0de59d7b6f3a" - }, - "0xdd4d40f513755775a23850d8c092687eba5ca957ee460e0b95fde8c185738286": { - "remapped_id": "0x019fd2bb615bd3e3ff2b619cc9b420b83cbee238a3def94c65da492c841c75e4" - }, - "0x2f9c6be2132d5462605be5de722ed1d67450137527aeaed827421f6f15e87206": { - "remapped_id": "0x01e50ea83f5d45013e81edb1807f09b3c7d9dc384fbc5d4c9b93bb48c570f391" - }, - "0x65cb64212ccadd01e4a3dc8c3928d4e2b1aae51de8e62392d4b648df384e89de": { - "remapped_id": "0x010c5351f30dcbd1925b9a1be4251ebafc5fa4f3863536eb40129e928b8d115c" - }, - "0xfc8853d650dc192f6888a3dbd84d6f94a494eef34127290a06440ecd854d29e5": { - "remapped_id": "0x018b58e658f7ec5910825335aaca064547e85a514f008b835dcf425abb203c94" - }, - "0x043d87c02de9ce6b5539908ec3cdc23694ab10edaf1798acd0e37d3623644c62": { - "remapped_id": "0x019728703271fecce4c8649213bff49ed0fa79b52072dd116a1c82b1018f0197" - }, - "0x1cb201daa0e7ea2954504ef5342f6512370e494551362da68af546329e6fa15e": { - "remapped_id": "0x0153eea0c480176a13eec657b496aad2714e1fb1ea6d777aa3996a1346e8aed6" - }, - "0xb859c6d8517f91e1d1a74379d56363120f1a2130402e5691d0b029c62f1788cc": { - "remapped_id": "0x010a4bec90f37233512ae23766307c50eb685610661950a30ebbefacf34ac770" - }, - "0xcb0d9df3f3af78d693d998a57a8239f7ff6a66f0962206707066f77e36cfb9a3": { - "remapped_id": "0x01792431495ef8e796059e799b4ba7b6482b30402615f004f004f60887027371" - }, - "0xc276873a03b0d9c7a3da2e1b4e229fbaa246ea64724f92751019c798df4c7858": { - "remapped_id": "0x0172c143f99a9a56851c36b314dadadda245393a5b4c634945bc9258c2ce25f3" - }, - "0xd980426e531a346cfa26450842a427144942cf0783b7c145cbdbde28d4882422": { - "remapped_id": "0x01c728776f3c57bd6871d018602ed07fab6b539f3af436a7c5cd3a4c388a87db" - }, - "0xb293987d909f442f334e6b5fd564e339c7db18546be033bc69c6bca6188974dc": { - "remapped_id": "0x01b5904d8a87646505212ed8a3613dac2cae6744754ad4d8e770a410ea0a4309" - }, - "0xd5612713b1916bb4b432228dd02d4002774b5fcb90a7a77d95e0fbb3829666c0": { - "remapped_id": "0x019171de17bcf8356ffe53e2500faa02710c345946ab753c49fb1cb7be02a76d" - }, - "0x6286f601051c2f2f301a6e2035bc67da259145aad8465797f6f117a7c6e4a6f2": { - "remapped_id": "0x016d42d6b911e23b7e412f7a2eacaec8cc323cbf3c1223e1755aee05bb13c8eb" - }, - "0x10f88aaf737bcb20754fa93fa8cee9551e757576f546b032e2fdb29d15b73349": { - "remapped_id": "0x013d8114b5619405cbfa82882f125e36a9db7079adc4252efe6d4ddb0085ef0f" - }, - "0xe9df0d6f7f7417f408cff0c2ec177fb794a0ad9685fcbe907d6a5ada37739b10": { - "remapped_id": "0x01831c786e702d7c6040ccde9589ec924f041acc793892a2d633e8f6b0a268b5" - }, - "0xa9688a3ab12f47093a40d7edc1c4c7222a8871c4941a4108acddf39ba030f4d7": { - "remapped_id": "0x0118ec5c41a857978680fc2e9192961a311cc4aa3e7b571600a2b291cb988c8a" - }, - "0x30c9ca5bbeacd85089bca10ab639c55b0c4a85f3ffc04b0af24163f7f8704975": { - "remapped_id": "0x016c4e35c6bf0d86ce3e367d8d7f8a4771106aa50827103f70b5e6c55972285e" - }, - "0xcb93c7bea8c7ec3558a19e4676b34a108c8da204d551875aa79b7c992508b379": { - "remapped_id": "0x016d0b6d5cc5059c7fa8df738c5167b2c7d8b6061f80c8542d1b383ca18e9ca3" - }, - "0xe4b0813fb08e2f5dd9509eb21d2ebe39089178ffc01bbbfa93b198b27d58779d": { - "remapped_id": "0x0153cd5846de98b39a2afeeca12d436c8e269996efffd757b004c4e89f7c6ab6" - }, - "0x8ce86be5d3d63cd1965a97a9d997fa06c9ee977994283838dc76f040d4a4eca1": { - "remapped_id": "0x01aee205cc175d7b9b63c700ba777847e0a4a25503f1c052444ae923a737a79e" - }, - "0x939242fd6f55674a4d21dabedfc11973ee68e82abffab39501ab2cc05b0eff67": { - "remapped_id": "0x015dd073ef8f30e77697b0c2449517b6b1d315c106a40272847a79699c174122" - } - } -} \ No newline at end of file diff --git a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_10_feeds.json b/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_10_feeds.json deleted file mode 100644 index 579d25cf90d..00000000000 --- a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_10_feeds.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "feeds": { - "0x0003d6f7b434e394fb9d3d8234a6b2d887a82ca247100b4409ff321413457370": { - "remapped_id": "0x01a0b4d920000332000000000000000000000000000000000000000000000000" - }, - "0x0003969b9f9a7449bb6103126212f320d518a6be1dbe2405955102bf6e3943f1": { - "remapped_id": "0x01d585327c000332000000000000000000000000000000000000000000000000" - }, - "0x0003e4d58e875c0ce63861117bae1bbd703c7267a465e401236fd8e861fd59e2": { - "remapped_id": "0x01dc3920b8000332000000000000000000000000000000000000000000000000" - }, - "0x0003d4d155f3e71cae5569cbfca1edf4c978f0b41c025e77e5d942da9e457986": { - "remapped_id": "0x01e9f4b024000332000000000000000000000000000000000000000000000000" - }, - "0x00036ac01171c872be55c5345c5f5e7a579d0347f64f7626d42fb873fe522fd0": { - "remapped_id": "0x01f816c278000332000000000000000000000000000000000000000000000000" - }, - "0x00035d26444090c2b0a0f4eea9477b3e94902f103778605d0047bd2a39f45f42": { - "remapped_id": "0x01b347c021000332000000000000000000000000000000000000000000000000" - }, - "0x00035b387b4b92aee625a5aab2be2685b0961d6518d2041a83ad401a748b1841": { - "remapped_id": "0x01c43e9180000332000000000000000000000000000000000000000000000000" - }, - "0x00035b09293ae83d187cae772b8732becfc92b51487921dd90464eaad5ec1e33": { - "remapped_id": "0x01f23ac480000332000000000000000000000000000000000000000000000000" - }, - "0x00036141f5846abb415da3458c91c564287eed3d07e304e9f05d44e0a015d185": { - "remapped_id": "0x01a25d3950000332000000000000000000000000000000000000000000000000" - }, - "0x0003312a7de76a53547a95ecd5530cab48d65a7b5fc64b1ffc3a731b58dd496d": { - "remapped_id": "0x01b3a4e780000332000000000000000000000000000000000000000000000000" - } - } -} \ No newline at end of file diff --git a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_2_feeds.json b/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_2_feeds.json deleted file mode 100644 index 17b58253891..00000000000 --- a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/config_2_feeds.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "feeds": { - "0x0003d6f7b434e394fb9d3d8234a6b2d887a82ca247100b4409ff321413457370": { - "remapped_id": "0x01a0b4d920000332000000000000000000000000000000000000000000000000" - }, - "0x0003969b9f9a7449bb6103126212f320d518a6be1dbe2405955102bf6e3943f1": { - "remapped_id": "0x01d585327c000332000000000000000000000000000000000000000000000000" - } - } -} \ No newline at end of file diff --git a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/data_feeds_workflow.go b/core/services/workflows/cmd/cre/examples/legacy/data_feeds/data_feeds_workflow.go deleted file mode 100644 index 0ac9aab3d81..00000000000 --- a/core/services/workflows/cmd/cre/examples/legacy/data_feeds/data_feeds_workflow.go +++ /dev/null @@ -1,85 +0,0 @@ -//go:build wasip1 - -package main - -import ( - "encoding/json" - - "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/ocr3cap" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/targets/chainwriter" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/triggers/streams" - "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" -) - -type FeedConfig struct { - RemappedID string `json:"remapped_id"` -} - -type ConfigFile struct { - Feeds map[string]FeedConfig `json:"feeds"` -} - -func BuildDataFeedsWorkflow(configData []byte) *sdk.WorkflowSpecFactory { - workflow := sdk.NewWorkflowSpecFactory() - - var config ConfigFile - if err := json.Unmarshal(configData, &config); err != nil { - panic(err) - } - - var feedIds []streams.FeedId - for feedId := range config.Feeds { - feedIds = append(feedIds, streams.FeedId(feedId)) - } - - triggerCfg := streams.TriggerConfig{ - FeedIds: feedIds, - MaxFrequencyMs: 5000, - } - trigger := triggerCfg.New(workflow) - - ocr3Config := ocr3cap.DataFeedsConsensusConfig{ - AggregationMethod: "data_feeds", - Encoder: "EVM", - EncoderConfig: map[string]any{ - "abi": "(bytes32 RemappedID, bytes RawReport)[] Reports", - }, - ReportId: "0001", - KeyId: "evm", - AggregationConfig: ocr3cap.DataFeedsConsensusConfigAggregationConfig{ - AllowedPartialStaleness: "0.5", - }, - } - - ocr3Config.AggregationConfig.Feeds = ocr3cap.DataFeedsConsensusConfigAggregationConfigFeeds{} - - // Configure feeds from JSON - for feedId, feedConfig := range config.Feeds { - ocr3Config.AggregationConfig.Feeds[feedId] = ocr3cap.FeedValue{ - Deviation: "0.001", - Heartbeat: 1800, - RemappedID: &feedConfig.RemappedID, - } - } - - consensus := ocr3Config.New(workflow, "aptos_feeds", ocr3cap.DataFeedsConsensusInput{ - Observations: sdk.ListOf(trigger), - }) - - chainWriterConfig := chainwriter.TargetConfig{ - Address: "0xf1099f135ddddad1c065203431be328a408b0ca452ada70374ce26bd2b32fdd3", - DeltaStage: "10s", - Schedule: "oneAtATime", - } - chainWriterConfig.New(workflow, "write_aptos-testnet@1.0.0", chainwriter.TargetInput{SignedReport: consensus}) - - return workflow -} - -func main() { - runner := wasm.NewRunner() - workflow := BuildDataFeedsWorkflow(runner.Config()) - runner.Run(workflow) -} diff --git a/core/services/workflows/cmd/cre/utils/standalone_engine.go b/core/services/workflows/cmd/cre/utils/standalone_engine.go index 66890a7fe6c..71bedc68a71 100644 --- a/core/services/workflows/cmd/cre/utils/standalone_engine.go +++ b/core/services/workflows/cmd/cre/utils/standalone_engine.go @@ -279,27 +279,12 @@ func NewCapabilities(ctx context.Context, lggr logger.Logger, registry *capabili func NewFakeCapabilities(ctx context.Context, lggr logger.Logger, registry *capabilities.Registry) ([]services.Service, error) { caps := make([]services.Service, 0) - streamsTrigger := fakes.NewFakeStreamsTrigger(lggr, 6) - if err := registry.Add(ctx, streamsTrigger); err != nil { - return nil, err - } - caps = append(caps, streamsTrigger) - httpAction := fakes.NewDirectHTTPAction(lggr) if err := registry.Add(ctx, httpserver.NewClientServer(httpAction)); err != nil { return nil, err } caps = append(caps, httpAction) - fakeConsensus, err := fakes.NewFakeConsensus(lggr, fakes.DefaultFakeConsensusConfig()) - if err != nil { - return nil, err - } - if err := registry.Add(ctx, fakeConsensus); err != nil { - return nil, err - } - caps = append(caps, fakeConsensus) - // generate deterministic signers - need to be configured on the Forwarder contract nSigners := 4 signers := make([]ocr2key.KeyBundle, nSigners) diff --git a/core/services/workflows/test/wasm/legacy/cmd/main.go b/core/services/workflows/test/wasm/legacy/cmd/main.go deleted file mode 100644 index ed077365bcf..00000000000 --- a/core/services/workflows/test/wasm/legacy/cmd/main.go +++ /dev/null @@ -1,33 +0,0 @@ -//go:build wasip1 - -package main - -import ( - "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/cli/cmd/testdata/fixtures/capabilities/basictrigger" - "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" -) - -func BuildWorkflow(config []byte) *sdk.WorkflowSpecFactory { - workflow := sdk.NewWorkflowSpecFactory() - - triggerCfg := basictrigger.TriggerConfig{Name: "trigger", Number: 100} - trigger := triggerCfg.New(workflow) - - sdk.Compute1( - workflow, - "compute", - sdk.Compute1Inputs[basictrigger.TriggerOutputs]{Arg0: trigger}, - func(sdk sdk.Runtime, outputs basictrigger.TriggerOutputs) (bool, error) { - return outputs.CoolOutput == "foo", nil - }) - - return workflow -} - -func main() { - runner := wasm.NewRunner() - workflow := BuildWorkflow(runner.Config()) - runner.Run(workflow) -} diff --git a/core/services/workflows/test/zerotimeout/cmd/main.go b/core/services/workflows/test/zerotimeout/cmd/main.go deleted file mode 100644 index 5f599b5b1e5..00000000000 --- a/core/services/workflows/test/zerotimeout/cmd/main.go +++ /dev/null @@ -1,43 +0,0 @@ -//go:build wasip1 - -package main - -import ( - "github.com/smartcontractkit/chainlink-common/pkg/workflows/wasm" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/targets/chainwriter" - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3/ocr3cap" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/cli/cmd/testdata/fixtures/capabilities/basictrigger" - "github.com/smartcontractkit/chainlink-common/pkg/workflows/sdk" -) - -func BuildWorkflow(config []byte) *sdk.WorkflowSpecFactory { - workflow := sdk.NewWorkflowSpecFactory() - - triggerCfg := basictrigger.TriggerConfig{Name: "trigger", Number: 100} - trigger := triggerCfg.New(workflow) - - // Config elided because it's not relevant for the test. - consensusInput := ocr3cap.ReduceConsensusInput[basictrigger.TriggerOutputs]{ - Observation: trigger, - } - consensus := ocr3cap.ReduceConsensusConfig[basictrigger.TriggerOutputs]{}.New(workflow, "consensus", consensusInput) - - input := chainwriter.TargetInput{ - SignedReport: consensus, - } - chainwriter.TargetConfig{ - Address: "0x0", - CreStepTimeout: 0, - DeltaStage: "30s", - Schedule: "oneAtATime", - }.New(workflow, "write_ethereum-testnet-sepolia@1.0.0", input) - - return workflow -} - -func main() { - runner := wasm.NewRunner() - workflow := BuildWorkflow(runner.Config()) - runner.Run(workflow) -} diff --git a/deployment/cre/jobs/pkg/ocr3_job.go b/deployment/cre/jobs/pkg/ocr3_job.go index ba2ba51f0f8..2b3703f66b8 100644 --- a/deployment/cre/jobs/pkg/ocr3_job.go +++ b/deployment/cre/jobs/pkg/ocr3_job.go @@ -67,7 +67,15 @@ type OCR3JobConfig struct { Auth0 *Auth0Config } +// ErrWorkerOCR3Deprecated is returned when the "worker-ocr3" template is +// requested. The chainlink-ocr3-capability plugin and its job spec have been +// removed; use another template (e.g. "worker-vault") instead. +var ErrWorkerOCR3Deprecated = errors.New("the \"worker-ocr3\" template is deprecated and no longer supported") + func (c OCR3JobConfig) Validate() error { + if c.TemplateName == "worker-ocr3" { + return ErrWorkerOCR3Deprecated + } if c.TemplateName == "" { return errors.New("TemplateName is empty") } diff --git a/deployment/cre/jobs/pkg/templates/ocr3_spec.tmpl b/deployment/cre/jobs/pkg/templates/ocr3_spec.tmpl deleted file mode 100644 index a1ac21aa3f1..00000000000 --- a/deployment/cre/jobs/pkg/templates/ocr3_spec.tmpl +++ /dev/null @@ -1,30 +0,0 @@ -type = "offchainreporting2" -schemaVersion = 1 -name = "{{ .JobName }}" -externalJobID = "{{ .ExternalJobID }}" -contractID = "{{ .ContractID }}" -ocrKeyBundleID = "{{ .OCR2EVMKeyBundleID }}" -p2pv2Bootstrappers = [ -{{ range .P2Pv2Bootstrappers }} "{{ . }}", -{{ end }}] -relay = "evm" -pluginType = "plugin" -transmitterID = "{{ .TransmitterID }}" - -[relayConfig] -chainID = "{{ .ChainID }}" - -[pluginConfig] -command = "chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" - -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "{{ .OCR2EVMKeyBundleID }}" -{{- if .OCR2AptosKeyBundleID }} -aptos = "{{ .OCR2AptosKeyBundleID }}" -{{ end }} diff --git a/deployment/cre/jobs/pkg/templates/worker-ocr3.tmpl b/deployment/cre/jobs/pkg/templates/worker-ocr3.tmpl deleted file mode 100644 index b11653f2605..00000000000 --- a/deployment/cre/jobs/pkg/templates/worker-ocr3.tmpl +++ /dev/null @@ -1,30 +0,0 @@ -type = "offchainreporting2" -schemaVersion = 1 -externalJobID = "{{ .ExternalJobID }}" -name = "{{ .JobName }}" -contractID = "{{ .ContractID }}" -ocrKeyBundleID = "{{ .OCR2EVMKeyBundleID }}" -p2pv2Bootstrappers = [ -{{ range .P2Pv2Bootstrappers }} "{{ . }}", -{{ end }}] -relay = "evm" -pluginType = "plugin" -transmitterID = "{{ .TransmitterID }}" -[relayConfig] -chainID = "{{ .ChainID }}" -[pluginConfig] -command = "/usr/local/bin/chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "{{ .OCR2EVMKeyBundleID }}" -{{- if .OCR2AptosKeyBundleID }} -aptos = "{{ .OCR2AptosKeyBundleID }}" -{{ end }} -{{- if .OCR2SolanaKeyBundleID }} -solana = "{{ .OCR2SolanaKeyBundleID }}" -{{ end }} diff --git a/deployment/cre/jobs/propose_job_spec_test.go b/deployment/cre/jobs/propose_job_spec_test.go index 3559c1a817b..fd1cd1ca12a 100644 --- a/deployment/cre/jobs/propose_job_spec_test.go +++ b/deployment/cre/jobs/propose_job_spec_test.go @@ -1082,71 +1082,6 @@ PerSenderBurst = 100 assert.Contains(t, err.Error(), "failed to get OCR3 contract address for chain selector 0 and qualifier ocr-contract-qualifier") }) - t.Run("successful ocr3 job distribution", func(t *testing.T) { - chainSelector := h.RegistrySelector - ds := datastore.NewMemoryDataStore() - - err := ds.Addresses().Add(datastore.AddressRef{ - ChainSelector: chainSelector, - Type: datastore.ContractType(ocr3.OCR3Capability), - Version: semver.MustParse("1.0.0"), - Address: "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B", - Qualifier: "ocr3-contract-qualifier", - }) - require.NoError(t, err) - - env.DataStore = ds.Seal() - - input := jobs.ProposeJobSpecInput{ - Environment: test.EnvironmentName, - Domain: "cre", - JobName: "ocr3-job", - DONName: test.DONName, - Template: job_types.OCR3, - DONFilters: []offchain.TargetDONFilter{ - {Key: offchain.FilterKeyDONName, Value: test.DONName}, - {Key: "environment", Value: test.EnvironmentName}, - {Key: "product", Value: offchain.ProductLabel}, - }, - Inputs: job_types.JobSpecInput{ - "templateName": "worker-ocr3", - "contractQualifier": "ocr3-contract-qualifier", - "chainSelectorEVM": strconv.FormatUint(chainSelector, 10), - "chainSelectorAptos": strconv.FormatUint(h.AptosSelector, 10), - "bootstrapperOCR3Urls": []string{ - "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001", - }, - }, - } - - out, err := jobs.ProposeJobSpec{}.Apply(*env, input) - require.NoError(t, err) - assert.Len(t, out.Reports, 1) - - reqs, err := h.TestJD.ListProposedJobRequests() - require.NoError(t, err) - - expectedChainID := chainsel.TEST_90000001.EvmChainID - - for _, req := range reqs { - if !strings.Contains(req.Spec, `type = "offchainreporting2"`) { - continue - } - // log each spec in readable yaml format - t.Logf("Job Spec:\n%s", req.Spec) - assert.Contains(t, req.Spec, `name = "ocr3-job`) - assert.Contains(t, req.Spec, `contractID = "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"`) - assert.Contains(t, req.Spec, `p2pv2Bootstrappers = [ - "12D3KooWHfYFQ8hGttAYbMCevQVESEQhzJAqFZokMVtom8bNxwGq@127.0.0.1:5001", -]`) - assert.Contains(t, req.Spec, fmt.Sprintf(`chainID = "%d"`, expectedChainID)) - assert.Contains(t, req.Spec, `command = "/usr/local/bin/chainlink-ocr3-capability"`) - assert.Contains(t, req.Spec, `pluginName = "ocr-capability"`) - assert.Contains(t, req.Spec, `providerType = "ocr3-capability"`) - assert.Contains(t, req.Spec, `strategyName = 'multi-chain'`) - } - }) - t.Run("failed ocr3 job distribution", func(t *testing.T) { chainSelector := h.RegistrySelector ds := datastore.NewMemoryDataStore() diff --git a/deployment/cre/ocr3/oracle_config.go b/deployment/cre/ocr3/oracle_config.go index c3262a54bbe..418f73850da 100644 --- a/deployment/cre/ocr3/oracle_config.go +++ b/deployment/cre/ocr3/oracle_config.go @@ -160,7 +160,6 @@ func (oc ConsensusCapOffchainConfig) MarshalYAML() (any, error) { func (oc *ConsensusCapOffchainConfig) ToProto() (proto.Message, error) { // let's keep reqTimeout as nil if it's 0, so we can use the default value within `chainlink-common`. - // See: https://github.com/smartcontractkit/chainlink-common/blob/main/pkg/capabilities/consensus/ocr3/factory.go#L73 var reqTimeout *durationpb.Duration if oc.RequestTimeout > 0 { reqTimeout = durationpb.New(oc.RequestTimeout) diff --git a/deployment/keystone/changeset/ocr3_job.go b/deployment/keystone/changeset/ocr3_job.go index abd00755a97..338f5972913 100644 --- a/deployment/keystone/changeset/ocr3_job.go +++ b/deployment/keystone/changeset/ocr3_job.go @@ -1,22 +1,18 @@ package changeset import ( - "bytes" - "crypto/sha256" - "embed" - "encoding/binary" "errors" - "fmt" - "text/template" - - "github.com/google/uuid" - chainsel "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/logger" nodev1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node" "github.com/smartcontractkit/chainlink/deployment" ) +// ErrOCR3JobDeprecated is returned by the OCR3 capability job builders below. +// The chainlink-ocr3-capability plugin and its job spec have been removed; +// use deployment/cre/jobs instead. +var ErrOCR3JobDeprecated = errors.New("the OCR3 capability job is deprecated and no longer supported") + type OCR3JobConfig struct { JobName string ChainID string @@ -30,47 +26,11 @@ type OCR3JobConfig struct { } func (c OCR3JobConfig) Validate() error { - if c.JobName == "" { - return errors.New("JobName is empty") - } - if c.ChainID == "" { - return errors.New("ChainID is empty") - } - if c.P2PID == "" { - return errors.New("P2PID is empty") - } - if c.OCR2EVMKeyBundleID == "" && c.OCR2AptosKeyBundleID == "" { - return errors.New("OCR2EVMKeyBundleID and OCR2AptosKeyBundleID are empty, one must be set") - } - if c.TransmitterID == "" { - return errors.New("TransmitterID is empty") - } - if c.ContractID == "" { - return errors.New("ContractID is empty") - } - if len(c.P2Pv2Bootstrappers) == 0 { - return errors.New("P2Pv2Bootstrappers is empty") - } - - return nil + return ErrOCR3JobDeprecated } -//go:embed *tmpl -var tmplFS embed.FS - func ResolveOCR3Job(cfg OCR3JobConfig) (string, error) { - t, err := template.New("s").ParseFS(tmplFS, "ocr3_spec.tmpl") - if err != nil { - return "", fmt.Errorf("failed to parse ocr3_spec.tmpl: %w", err) - } - - b := &bytes.Buffer{} - err = t.ExecuteTemplate(b, "ocr3_spec.tmpl", cfg) - if err != nil { - return "", fmt.Errorf("failed to execute template: %w", err) - } - - return b.String(), nil + return "", ErrOCR3JobDeprecated } type OCR3JobConfigSpec struct { @@ -88,98 +48,10 @@ func BuildOCR3JobConfigSpecs( btURLs []string, donName string, ) ([]OCR3JobConfigSpec, error) { - nodesLen := len(nodes) - if nodesLen == 0 { - return nil, errors.New("no nodes to build OCR3 job configs") - } - nodeIDs := make([]string, 0, nodesLen) - nodesByID := make(map[string]*nodev1.Node) - for _, node := range nodes { - nodesByID[node.Id] = node - nodeIDs = append(nodeIDs, node.Id) - } - nodeInfos, err := deployment.NodeInfo(nodeIDs, client) - if err != nil { - return nil, fmt.Errorf("failed to get node info: %w", err) - } - - chainID, err := chainsel.GetChainIDFromSelector(evmChainSel) - if err != nil { - return nil, fmt.Errorf("failed to get chain ID from selector: %w", err) - } - - extJobID, err := ExternalJobID(donName, evmChainSel) - if err != nil { - return nil, fmt.Errorf("failed to get external job ID: %w", err) - } - - jobConfigByNode := make(map[string]*OCR3JobConfig) - for _, node := range nodeInfos { - if node.IsBootstrap { - lggr.Infow("Skipping bootstrap node", "nodeID", node.NodeID, "chainSelector", evmChainSel) - continue - } - evmConfig, ok := node.OCRConfigForChainSelector(evmChainSel) - if !ok { - return nil, fmt.Errorf("no evm ocr2 config for node %s", node.NodeID) - } - aptosConfig, ok := node.OCRConfigForChainSelector(aptosChainSel) - if !ok { - return nil, fmt.Errorf("no aptos ocr2 config for node %s", node.NodeID) - } - - jobConfig := &OCR3JobConfig{ - JobName: "OCR3 Multichain Capability (" + node.Name + ")", - ChainID: chainID, - P2PID: node.PeerID.String(), - OCR2EVMKeyBundleID: evmConfig.KeyBundleID, - OCR2AptosKeyBundleID: aptosConfig.KeyBundleID, - ContractID: contractID, - TransmitterID: string(evmConfig.TransmitAccount), - P2Pv2Bootstrappers: btURLs, - ExternalJobID: extJobID, - } - - err1 := jobConfig.Validate() - if err1 != nil { - return nil, fmt.Errorf("failed to validate ocr3 job config: %w", err1) - } - jobConfigByNode[node.NodeID] = jobConfig - } - - specs := make([]OCR3JobConfigSpec, 0) - for nodeID, jobConfig := range jobConfigByNode { - spec, err1 := ResolveOCR3Job(*jobConfig) - if err1 != nil { - return nil, fmt.Errorf("failed to resolve ocr3 job: %w", err) - } - specs = append(specs, OCR3JobConfigSpec{ - Spec: spec, - NodeID: nodeID, - JobName: jobConfig.JobName, - }) - } - - return specs, nil + return nil, ErrOCR3JobDeprecated } // NOTE: consider adding contract address to the hash func ExternalJobID(donName string, evmChainSel uint64) (string, error) { - in := []byte(donName + "-ocr3-capability-job-spec") - b := make([]byte, 8) - binary.BigEndian.PutUint64(b, evmChainSel) - in = append(in, b...) - sha256Hash := sha256.New() - sha256Hash.Write(in) - in = sha256Hash.Sum(nil)[:16] - // tag as valid UUID v4 https://github.com/google/uuid/blob/0f11ee6918f41a04c201eceeadf612a377bc7fbc/version4.go#L53-L54 - in[6] = (in[6] & 0x0f) | 0x40 // Version 4 - in[8] = (in[8] & 0x3f) | 0x80 // Variant is 10 - - id, err := uuid.FromBytes(in) - if err != nil { - return "", err - } - - return id.String(), nil + return "", ErrOCR3JobDeprecated } diff --git a/deployment/keystone/changeset/ocr3_job_test.go b/deployment/keystone/changeset/ocr3_job_test.go deleted file mode 100644 index 7c15eafd2bb..00000000000 --- a/deployment/keystone/changeset/ocr3_job_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package changeset - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestResolveOCR3Job(t *testing.T) { - t.Parallel() - - type args struct { - cfg OCR3JobConfig - } - tests := []struct { - name string - args args - want string - wantErr bool - }{ - { - name: "basic", - args: args{ - cfg: OCR3JobConfig{ - JobName: "OCR3 MultiChain Capability", - ChainID: "11155111", - OCR2EVMKeyBundleID: "0x123", - TransmitterID: "0x456", - OCR2AptosKeyBundleID: "0x789", - ContractID: "0x5Cf6c5D188Ed72428aEC2E53afA5149F7B50f38D", - P2Pv2Bootstrappers: []string{ - "12D3KooWB78smeNRVYBVsZVQUXTZ9jemuW7Y7yvwSMov56NZW2z1@cl-keystone-one-bt-0:5001", - "12D3KooWBeFB8CGj1Pqph86ugvp7JCSuN88ffx1gtAS9857x9hoE@cl-keystone-one-bt-2:5001", - "12D3KooWDjdJ2kMS5CDZe41orMhpnFEZ2Ht13WY37ecWZRYjAVvi@cl-keystone-one-bt-3:5001", - "12D3KooWPBeMpPvDNmBdAjab7aLCEafZs5Q4EHcp4CAqZSLzScUL@cl-keystone-one-bt-1:5001", - }, - ExternalJobID: "f1ac5211-ab79-4c31-ba1c-0997b72db466", - }, - }, - want: `type = "offchainreporting2" -schemaVersion = 1 -name = "OCR3 MultiChain Capability" -externalJobID = "f1ac5211-ab79-4c31-ba1c-0997b72db466" -contractID = "0x5Cf6c5D188Ed72428aEC2E53afA5149F7B50f38D" -ocrKeyBundleID = "0x123" -p2pv2Bootstrappers = [ - "12D3KooWB78smeNRVYBVsZVQUXTZ9jemuW7Y7yvwSMov56NZW2z1@cl-keystone-one-bt-0:5001", - "12D3KooWBeFB8CGj1Pqph86ugvp7JCSuN88ffx1gtAS9857x9hoE@cl-keystone-one-bt-2:5001", - "12D3KooWDjdJ2kMS5CDZe41orMhpnFEZ2Ht13WY37ecWZRYjAVvi@cl-keystone-one-bt-3:5001", - "12D3KooWPBeMpPvDNmBdAjab7aLCEafZs5Q4EHcp4CAqZSLzScUL@cl-keystone-one-bt-1:5001", -] -relay = "evm" -pluginType = "plugin" -transmitterID = "0x456" - -[relayConfig] -chainID = "11155111" - -[pluginConfig] -command = "chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" - -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "0x123" -aptos = "0x789"`, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - got, err := ResolveOCR3Job(tt.args.cfg) - if (err != nil) != tt.wantErr { - t.Errorf("ResolveOCR3Job() error = %v, wantErr %v", err, tt.wantErr) - return - } - assert.Equal(t, tt.want, got) - }) - } -} diff --git a/deployment/keystone/changeset/ocr3_spec.tmpl b/deployment/keystone/changeset/ocr3_spec.tmpl deleted file mode 100644 index a167cc4f130..00000000000 --- a/deployment/keystone/changeset/ocr3_spec.tmpl +++ /dev/null @@ -1,28 +0,0 @@ -type = "offchainreporting2" -schemaVersion = 1 -name = "{{ .JobName }}" -externalJobID = "{{ .ExternalJobID }}" -contractID = "{{ .ContractID }}" -ocrKeyBundleID = "{{ .OCR2EVMKeyBundleID }}" -p2pv2Bootstrappers = [ -{{ range .P2Pv2Bootstrappers }} "{{ . }}", -{{ end }}] -relay = "evm" -pluginType = "plugin" -transmitterID = "{{ .TransmitterID }}" - -[relayConfig] -chainID = "{{ .ChainID }}" - -[pluginConfig] -command = "chainlink-ocr3-capability" -ocrVersion = 3 -pluginName = "ocr-capability" -providerType = "ocr3-capability" -telemetryType = "plugin" - -[onchainSigningStrategy] -strategyName = 'multi-chain' -[onchainSigningStrategy.config] -evm = "{{ .OCR2EVMKeyBundleID }}" -aptos = "{{ .OCR2AptosKeyBundleID }}" \ No newline at end of file diff --git a/plugins/cmd/chainlink-ocr3-capability/main.go b/plugins/cmd/chainlink-ocr3-capability/main.go deleted file mode 100644 index fad82c7b73b..00000000000 --- a/plugins/cmd/chainlink-ocr3-capability/main.go +++ /dev/null @@ -1,55 +0,0 @@ -package main - -import ( - "context" - - "github.com/hashicorp/go-plugin" - - "github.com/smartcontractkit/chainlink-common/pkg/capabilities/consensus/ocr3" - "github.com/smartcontractkit/chainlink-common/pkg/loop" - "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins" - ocr3rp "github.com/smartcontractkit/chainlink-common/pkg/loop/reportingplugins/ocr3" - "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/smartcontractkit/chainlink/v2/core/capabilities" -) - -const ( - loggerName = "PluginOCR3Capability" -) - -func main() { - s := loop.MustNewStartedServer(loggerName) - defer s.Stop() - - c := ocr3.Config{ - Logger: s.Logger, - EncoderFactory: capabilities.NewEncoder, - AggregatorFactory: capabilities.NewAggregator, - } - p := ocr3.NewOCR3(c) - if err := p.Start(context.Background()); err != nil { - s.Logger.Fatal("Failed to start OCR3 capability", err) - } - - defer s.Logger.ErrorIfFn(p.Close, "Failed to close") - - s.MustRegister(p) - - stop := make(chan struct{}) - defer close(stop) - - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: reportingplugins.ReportingPluginHandshakeConfig(), - Plugins: map[string]plugin.Plugin{ - ocr3rp.PluginServiceName: &ocr3rp.GRPCService[types.PluginProvider]{ - PluginServer: p, - BrokerConfig: loop.BrokerConfig{ - Logger: s.Logger, - StopCh: stop, - GRPCOpts: s.GRPCOpts, - }, - }, - }, - GRPCServer: s.GRPCOpts.NewServer, - }) -}