From 59792b2ee6a71d341c9f153735540426a85328d7 Mon Sep 17 00:00:00 2001 From: mchain0 Date: Thu, 5 Mar 2026 12:28:52 +0100 Subject: [PATCH 1/2] cre-2203 --- .../tests/smoke/cre/maxlimits/Makefile | 23 + .../tests/smoke/cre/maxlimits/ballast.go | 23 + .../smoke/cre/maxlimits/config/config.go | 100 ++++ system-tests/tests/smoke/cre/maxlimits/go.mod | 27 + system-tests/tests/smoke/cre/maxlimits/go.sum | 44 ++ .../tests/smoke/cre/maxlimits/main.go | 512 ++++++++++++++++++ 6 files changed, 729 insertions(+) create mode 100644 system-tests/tests/smoke/cre/maxlimits/Makefile create mode 100644 system-tests/tests/smoke/cre/maxlimits/ballast.go create mode 100644 system-tests/tests/smoke/cre/maxlimits/config/config.go create mode 100644 system-tests/tests/smoke/cre/maxlimits/go.mod create mode 100644 system-tests/tests/smoke/cre/maxlimits/go.sum create mode 100644 system-tests/tests/smoke/cre/maxlimits/main.go diff --git a/system-tests/tests/smoke/cre/maxlimits/Makefile b/system-tests/tests/smoke/cre/maxlimits/Makefile new file mode 100644 index 00000000000..737ce288388 --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/Makefile @@ -0,0 +1,23 @@ +.PHONY: build build-max clean + +export GOOS := wasip1 +export GOARCH := wasm +export CGO_ENABLED := 0 + +LDFLAGS := -buildid= -w -s +# 14 MB of random data pushes compressed binary to ~18 MB (under 20 MB limit) +BALLAST_KB := 14336 + +# Standard build — exercises all call limits, config, and logging limits. +build: + go build -o workflow.wasm -trimpath -ldflags="$(LDFLAGS)" . + +# Max-binary build — also pushes WASMCompressedBinarySizeLimit (~18 MB compressed). +build-max: ballast.dat + go build -tags maxbinary -o workflow.wasm -trimpath -ldflags="$(LDFLAGS)" . + +ballast.dat: + dd if=/dev/urandom of=$@ bs=1024 count=$(BALLAST_KB) + +clean: + rm -f workflow.wasm ballast.dat diff --git a/system-tests/tests/smoke/cre/maxlimits/ballast.go b/system-tests/tests/smoke/cre/maxlimits/ballast.go new file mode 100644 index 00000000000..3558b6f56d0 --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/ballast.go @@ -0,0 +1,23 @@ +//go:build wasip1 && maxbinary + +package main + +import ( + _ "embed" + "fmt" +) + +// ballast.dat must be generated before building with -tags maxbinary: +// +// dd if=/dev/urandom of=ballast.dat bs=1024 count=14336 +// +// This produces a ~14 MB file that inflates the compressed WASM binary +// to approach the WASMCompressedBinarySizeLimit (20 MB). +// +//go:embed ballast.dat +var ballastData []byte + +func init() { + // Reference ballastData so the linker retains it. + _ = fmt.Sprintf("ballast: %d bytes", len(ballastData)) +} diff --git a/system-tests/tests/smoke/cre/maxlimits/config/config.go b/system-tests/tests/smoke/cre/maxlimits/config/config.go new file mode 100644 index 00000000000..a5bda8df7cc --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/config/config.go @@ -0,0 +1,100 @@ +package config + +// LogTriggerConfig defines an EVM log trigger subscription. +type LogTriggerConfig struct { + ChainSelector uint64 `yaml:"chainSelector"` + Addresses []string `yaml:"addresses"` + TopicSlots []TopicSlotConfig `yaml:"topicSlots"` + Confidence int32 `yaml:"confidence"` +} + +// TopicSlotConfig represents topic values for a single slot. +type TopicSlotConfig struct { + Values []string `yaml:"values"` +} + +// AuthorizedKeyConfig holds an authorized key for the HTTP trigger. +type AuthorizedKeyConfig struct { + Type string `yaml:"type"` + PublicKey string `yaml:"publicKey"` +} + +// ChainReadCallConfig describes a single EVM read call. +type ChainReadCallConfig struct { + Method string `yaml:"method"` // BalanceAt, CallContract, FilterLogs, HeaderByNumber, GetTxReceipt, GetTxByHash, EstimateGas + ChainSelector uint64 `yaml:"chainSelector"` + AccountAddress []byte `yaml:"accountAddress,omitempty"` + ContractAddress []byte `yaml:"contractAddress,omitempty"` + CallData []byte `yaml:"callData,omitempty"` + TxHash []byte `yaml:"txHash,omitempty"` + FromBlock int64 `yaml:"fromBlock,omitempty"` + ToBlock int64 `yaml:"toBlock,omitempty"` + BlockNumber int64 `yaml:"blockNumber,omitempty"` +} + +// ChainWriteTargetConfig describes a single EVM write target. +type ChainWriteTargetConfig struct { + ChainSelector uint64 `yaml:"chainSelector"` + Receiver []byte `yaml:"receiver"` + GasLimit uint64 `yaml:"gasLimit"` +} + +// HTTPEndpointConfig describes an HTTP endpoint to call. +type HTTPEndpointConfig struct { + URL string `yaml:"url"` + Method string `yaml:"method"` + Body string `yaml:"body,omitempty"` +} + +// ConfHTTPEndpointConfig describes a confidential HTTP endpoint to call. +type ConfHTTPEndpointConfig struct { + URL string `yaml:"url"` + Method string `yaml:"method"` + Body string `yaml:"body,omitempty"` + SecretKey string `yaml:"secretKey"` +} + +// SecretConfig describes a secret to fetch. +type SecretConfig struct { + ID string `yaml:"id"` + Namespace string `yaml:"namespace"` +} + +// Config is the top-level workflow configuration. +// All arrays are sized to match CRE per-execution call limits. +type Config struct { + // Trigger configuration + CronSchedule string `yaml:"cronSchedule"` + HTTPAuthorizedKeys []AuthorizedKeyConfig `yaml:"httpAuthorizedKeys"` + LogTriggers []LogTriggerConfig `yaml:"logTriggers"` + + // Chain read configuration (15 calls max) + ChainReads []ChainReadCallConfig `yaml:"chainReads"` + + // Chain write configuration (10 targets max) + ChainWrites []ChainWriteTargetConfig `yaml:"chainWrites"` + + // HTTP action configuration (5 calls max) + HTTPEndpoints []HTTPEndpointConfig `yaml:"httpEndpoints"` + + // Confidential HTTP configuration (5 calls max) + ConfHTTPEndpoints []ConfHTTPEndpointConfig `yaml:"confHttpEndpoints"` + + // Secrets configuration (5 calls max) + Secrets []SecretConfig `yaml:"secrets"` + + // Consensus observation payload size target in bytes (up to 100 KB) + ConsensusPayloadSize int `yaml:"consensusPayloadSize"` + + // Number of consensus rounds (up to 20) + ConsensusRounds int `yaml:"consensusRounds"` + + // Number of log lines to emit (up to 999) + LogEventCount int `yaml:"logEventCount"` + + // Report payload for chain writes (up to ~5 KB) + ReportPayload []byte `yaml:"reportPayload"` + + // Metadata is a large key-value map used to approach the 1 MB config limit. + Metadata map[string]string `yaml:"metadata"` +} diff --git a/system-tests/tests/smoke/cre/maxlimits/go.mod b/system-tests/tests/smoke/cre/maxlimits/go.mod new file mode 100644 index 00000000000..8aea794fdd0 --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/go.mod @@ -0,0 +1,27 @@ +module github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits + +go 1.25.3 + +require ( + github.com/ethereum/go-ethereum v1.17.0 + github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260211172625-dff40e83b3c9 + github.com/smartcontractkit/cre-sdk-go v1.1.0 + github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.9.0 + github.com/smartcontractkit/cre-sdk-go/capabilities/networking/confidentialhttp v0.0.0-20260211203328-1f3721436119 + github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.10.0 + github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.9.0 + google.golang.org/protobuf v1.36.11 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect + golang.org/x/sys v0.39.0 // indirect +) diff --git a/system-tests/tests/smoke/cre/maxlimits/go.sum b/system-tests/tests/smoke/cre/maxlimits/go.sum new file mode 100644 index 00000000000..a27630fdabd --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/go.sum @@ -0,0 +1,44 @@ +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= +github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/go-ethereum v1.17.0 h1:2D+1Fe23CwZ5tQoAS5DfwKFNI1HGcTwi65/kRlAVxes= +github.com/ethereum/go-ethereum v1.17.0/go.mod h1:2W3msvdosS/MCWytpqTcqgFiRYbTH59FxDJzqah120o= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA= +github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260211172625-dff40e83b3c9 h1:tp3AN+zX8dboiugE005O3rY/HBWKmSdN9LhNbZGhNWY= +github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= +github.com/smartcontractkit/cre-sdk-go v1.1.0 h1:7tcN/uDty2ex39xw8sDnvLaEh6swQJBYRsrBVDpGSrw= +github.com/smartcontractkit/cre-sdk-go v1.1.0/go.mod h1:sgiRyHUiPcxp1e/EMnaJ+ddMFL4MbE3UMZ2MORAAS9U= +github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.9.0 h1:0ddtacyL1aAFxIolQnbysYlJKP9FOLJc1YRFS/Z9OJA= +github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm v0.9.0/go.mod h1:VVJ4mvA7wOU1Ic5b/vTaBMHEUysyxd0gdPPXkAu8CmY= +github.com/smartcontractkit/cre-sdk-go/capabilities/networking/confidentialhttp v0.0.0-20260211203328-1f3721436119 h1:P69M59tBeLevOldspLxedrYNyAu+vtaD6wnpWwhstxM= +github.com/smartcontractkit/cre-sdk-go/capabilities/networking/confidentialhttp v0.0.0-20260211203328-1f3721436119/go.mod h1:KOn3NK4AbtvuMs2oKlNRxL2fACSuuGI114xPqO5igtQ= +github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.10.0 h1:nP6PVWrrTIICvjwQuFitsQecQWbqpPaYzaTEjx92eTQ= +github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http v0.10.0/go.mod h1:M83m3FsM1uqVu06OO58mKUSZJjjH8OGJsmvFpFlRDxI= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.9.0 h1:BWqX7Cnd6VnhHEpjfrQGEajPtAwqH4MH0D7o3iEPvvU= +github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron v0.9.0/go.mod h1:PWyrIw16It4TSyq6mDXqmSR0jF2evZRKuBxu7pK1yDw= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/system-tests/tests/smoke/cre/maxlimits/main.go b/system-tests/tests/smoke/cre/maxlimits/main.go new file mode 100644 index 00000000000..e8512b3bf8e --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/main.go @@ -0,0 +1,512 @@ +//go:build wasip1 + +package main + +import ( + "encoding/hex" + "fmt" + "log/slog" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "google.golang.org/protobuf/types/known/durationpb" + "gopkg.in/yaml.v3" + + protos "github.com/smartcontractkit/chainlink-protos/cre/go/sdk" + pb "github.com/smartcontractkit/chainlink-protos/cre/go/values/pb" + + "github.com/smartcontractkit/cre-sdk-go/capabilities/blockchain/evm" + "github.com/smartcontractkit/cre-sdk-go/capabilities/networking/confidentialhttp" + "github.com/smartcontractkit/cre-sdk-go/capabilities/networking/http" + "github.com/smartcontractkit/cre-sdk-go/capabilities/scheduler/cron" + "github.com/smartcontractkit/cre-sdk-go/cre" + "github.com/smartcontractkit/cre-sdk-go/cre/wasm" + + "github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits/config" +) + +func main() { + wasm.NewRunner(func(b []byte) (config.Config, error) { + var cfg config.Config + if err := yaml.Unmarshal(b, &cfg); err != nil { + return config.Config{}, fmt.Errorf("error unmarshalling config: %w", err) + } + return cfg, nil + }).Run(RunWorkflow) +} + +// --------------------------------------------------------------------------- +// Workflow registration: 10 trigger subscriptions (max TriggerSubscriptionLimit) +// 1 cron + 1 HTTP + 8 EVM log triggers +// --------------------------------------------------------------------------- + +func RunWorkflow(cfg config.Config, logger *slog.Logger, _ cre.SecretsProvider) (cre.Workflow[config.Config], error) { + wf := cre.Workflow[config.Config]{ + cre.Handler( + cron.Trigger(&cron.Config{Schedule: cfg.CronSchedule}), + onCronTrigger, + ), + cre.Handler( + http.Trigger(buildHTTPTriggerConfig(cfg)), + onHTTPTrigger, + ), + } + + for _, lt := range cfg.LogTriggers { + wf = append(wf, cre.Handler( + evm.LogTrigger(lt.ChainSelector, buildLogFilter(lt)), + onLogTrigger, + )) + } + + logger.Info("Max-limits workflow registered", + "triggers", len(wf), + "chainReads", len(cfg.ChainReads), + "chainWrites", len(cfg.ChainWrites), + "httpEndpoints", len(cfg.HTTPEndpoints), + "confHTTPEndpoints", len(cfg.ConfHTTPEndpoints), + "secrets", len(cfg.Secrets), + "consensusRounds", cfg.ConsensusRounds, + "logEventCount", cfg.LogEventCount, + ) + + return wf, nil +} + +// --------------------------------------------------------------------------- +// Cron handler: the main stress path exercising all per-execution call limits +// --------------------------------------------------------------------------- + +func onCronTrigger(cfg config.Config, runtime cre.Runtime, _ *cron.Payload) (_ string, err error) { + logger := runtime.Logger() + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic in onCronTrigger: %v", r) + } + }() + + logger.Info("[maxlimits] cron handler started") + + // Phase 1: Secrets (up to 5 calls — Secrets.CallLimit) + secretValues, sErr := runSecretsPhase(cfg, runtime) + if sErr != nil { + logger.Warn("[maxlimits] secrets phase error", "error", sErr) + } else { + logger.Info("[maxlimits] secrets phase done", "count", len(secretValues)) + } + + // Phase 2: HTTP actions via consensus (up to 5 HTTP calls + 5 consensus rounds) + httpErr := runHTTPPhase(cfg, runtime) + if httpErr != nil { + logger.Warn("[maxlimits] HTTP phase error", "error", httpErr) + } else { + logger.Info("[maxlimits] HTTP phase done") + } + + // Phase 3: Confidential HTTP (up to 5 calls — ConfidentialHTTP.CallLimit) + confErr := runConfHTTPPhase(cfg, runtime) + if confErr != nil { + logger.Warn("[maxlimits] confHTTP phase error", "error", confErr) + } else { + logger.Info("[maxlimits] confHTTP phase done") + } + + // Phase 4: Chain reads (up to 15 calls — ChainRead.CallLimit) + readErr := runChainReadPhase(cfg, runtime) + if readErr != nil { + logger.Warn("[maxlimits] chain-read phase error", "error", readErr) + } else { + logger.Info("[maxlimits] chain-read phase done") + } + + // Phase 5: Additional consensus rounds (up to 15 remaining — Consensus.CallLimit) + consErr := runConsensusPhase(cfg, runtime) + if consErr != nil { + logger.Warn("[maxlimits] consensus phase error", "error", consErr) + } else { + logger.Info("[maxlimits] consensus phase done") + } + + // Phase 6: Chain writes (up to 10 targets — ChainWrite.TargetsLimit) + writeErr := runChainWritePhase(cfg, runtime) + if writeErr != nil { + logger.Warn("[maxlimits] chain-write phase error", "error", writeErr) + } else { + logger.Info("[maxlimits] chain-write phase done") + } + + // Phase 7: Logging (up to 999 events — LogEventLimit minus overhead) + runLoggingPhase(cfg, runtime) + + logger.Info("[maxlimits] cron handler completed all phases") + + return buildMaxResponse(), nil +} + +// --------------------------------------------------------------------------- +// Phase 1: Secrets — up to Secrets.CallLimit (5) +// --------------------------------------------------------------------------- + +func runSecretsPhase(cfg config.Config, runtime cre.Runtime) ([]string, error) { + var results []string + for _, s := range cfg.Secrets { + resp, err := runtime.GetSecret(&protos.SecretRequest{ + Id: s.ID, + Namespace: s.Namespace, + }).Await() + if err != nil { + return results, fmt.Errorf("secret %s/%s: %w", s.Namespace, s.ID, err) + } + results = append(results, resp.Value) + } + return results, nil +} + +// --------------------------------------------------------------------------- +// Phase 2: HTTP actions — up to HTTPAction.CallLimit (5) +// Each http.SendRequest wraps RunInNodeMode (consumes 1 consensus round each) +// --------------------------------------------------------------------------- + +func runHTTPPhase(cfg config.Config, runtime cre.Runtime) error { + for i, ep := range cfg.HTTPEndpoints { + client := &http.Client{} + _, err := http.SendRequest(cfg, runtime, client, + func(_ config.Config, _ *slog.Logger, req *http.SendRequester) (string, error) { + resp, reqErr := req.SendRequest(&http.Request{ + Url: ep.URL, + Method: ep.Method, + Body: []byte(ep.Body), + Headers: map[string]string{ + "Content-Type": "application/json", + }, + Timeout: &durationpb.Duration{Seconds: 10}, + CacheSettings: &http.CacheSettings{ + Store: true, + MaxAge: &durationpb.Duration{Seconds: 300}, + }, + }).Await() + if reqErr != nil { + return "", reqErr + } + return string(resp.Body), nil + }, + cre.ConsensusIdenticalAggregation[string](), + ).Await() + if err != nil { + return fmt.Errorf("HTTP endpoint %d (%s): %w", i, ep.URL, err) + } + } + return nil +} + +// --------------------------------------------------------------------------- +// Phase 3: Confidential HTTP — up to ConfidentialHTTP.CallLimit (5) +// Runs at DON level, no separate consensus round needed +// --------------------------------------------------------------------------- + +func runConfHTTPPhase(cfg config.Config, runtime cre.Runtime) error { + client := confidentialhttp.Client{} + for i, ep := range cfg.ConfHTTPEndpoints { + _, err := client.SendRequest(runtime, &confidentialhttp.ConfidentialHTTPRequest{ + Request: &confidentialhttp.HTTPRequest{ + Url: ep.URL, + Method: ep.Method, + Body: &confidentialhttp.HTTPRequest_BodyString{BodyString: ep.Body}, + MultiHeaders: map[string]*confidentialhttp.HeaderValues{ + "Content-Type": {Values: []string{"application/json"}}, + }, + }, + VaultDonSecrets: []*confidentialhttp.SecretIdentifier{ + {Key: ep.SecretKey}, + }, + }).Await() + if err != nil { + return fmt.Errorf("confHTTP endpoint %d (%s): %w", i, ep.URL, err) + } + } + return nil +} + +// --------------------------------------------------------------------------- +// Phase 4: Chain reads — up to ChainRead.CallLimit (15) +// Mix of BalanceAt, CallContract, FilterLogs, HeaderByNumber, +// GetTransactionReceipt, GetTransactionByHash, EstimateGas +// --------------------------------------------------------------------------- + +func runChainReadPhase(cfg config.Config, runtime cre.Runtime) error { + for i, rc := range cfg.ChainReads { + client := evm.Client{ChainSelector: rc.ChainSelector} + var err error + switch rc.Method { + case "BalanceAt": + _, err = client.BalanceAt(runtime, &evm.BalanceAtRequest{ + Account: rc.AccountAddress, + BlockNumber: nil, + }).Await() + case "CallContract": + _, err = client.CallContract(runtime, &evm.CallContractRequest{ + Call: &evm.CallMsg{ + To: rc.ContractAddress, + Data: rc.CallData, + }, + }).Await() + case "FilterLogs": + _, err = client.FilterLogs(runtime, &evm.FilterLogsRequest{ + FilterQuery: &evm.FilterQuery{ + FromBlock: pb.NewBigIntFromInt(big.NewInt(rc.FromBlock)), + ToBlock: pb.NewBigIntFromInt(big.NewInt(rc.ToBlock)), + Addresses: [][]byte{rc.ContractAddress}, + }, + }).Await() + case "HeaderByNumber": + _, err = client.HeaderByNumber(runtime, &evm.HeaderByNumberRequest{ + BlockNumber: pb.NewBigIntFromInt(big.NewInt(rc.BlockNumber)), + }).Await() + case "GetTxReceipt": + _, err = client.GetTransactionReceipt(runtime, &evm.GetTransactionReceiptRequest{ + Hash: rc.TxHash, + }).Await() + case "GetTxByHash": + _, err = client.GetTransactionByHash(runtime, &evm.GetTransactionByHashRequest{ + Hash: rc.TxHash, + }).Await() + case "EstimateGas": + _, err = client.EstimateGas(runtime, &evm.EstimateGasRequest{ + Msg: &evm.CallMsg{ + To: rc.ContractAddress, + Data: rc.CallData, + }, + }).Await() + default: + err = fmt.Errorf("unknown chain-read method: %s", rc.Method) + } + if err != nil { + return fmt.Errorf("chain-read %d (%s): %w", i, rc.Method, err) + } + } + return nil +} + +// --------------------------------------------------------------------------- +// Phase 5: Additional consensus rounds — fills remaining Consensus.CallLimit (20) +// Phase 2 consumed up to 5, so we run up to 15 more here. +// Alternates between IdenticalAggregation and MedianAggregation. +// --------------------------------------------------------------------------- + +func runConsensusPhase(cfg config.Config, runtime cre.Runtime) error { + rounds := cfg.ConsensusRounds + if rounds <= 0 { + rounds = 15 + } + payloadSize := cfg.ConsensusPayloadSize + if payloadSize <= 0 { + payloadSize = 99 * 1024 // just under 100 KB + } + + for i := 0; i < rounds; i++ { + if i%2 == 0 { + _, err := cre.RunInNodeMode(cfg, runtime, + func(_ config.Config, _ cre.NodeRuntime) ([]byte, error) { + data := make([]byte, payloadSize) + for j := range data { + data[j] = byte((i + j) % 256) + } + return data, nil + }, + cre.ConsensusIdenticalAggregation[[]byte](), + ).Await() + if err != nil { + return fmt.Errorf("consensus round %d (identical): %w", i, err) + } + } else { + _, err := cre.RunInNodeMode(cfg, runtime, + func(_ config.Config, _ cre.NodeRuntime) (int, error) { + return 42 + i, nil + }, + cre.ConsensusMedianAggregation[int](), + ).Await() + if err != nil { + return fmt.Errorf("consensus round %d (median): %w", i, err) + } + } + } + return nil +} + +// --------------------------------------------------------------------------- +// Phase 6: Chain writes — up to ChainWrite.TargetsLimit (10) +// Each write: GenerateReport + WriteReport to a distinct receiver +// Reports approach ReportSizeLimit (5 KB), gas up to TransactionGasLimit (5M) +// --------------------------------------------------------------------------- + +func runChainWritePhase(cfg config.Config, runtime cre.Runtime) error { + payload := cfg.ReportPayload + if len(payload) == 0 { + payload = buildDefaultReportPayload() + } + + for i, wt := range cfg.ChainWrites { + report, err := runtime.GenerateReport(&cre.ReportRequest{ + EncodedPayload: payload, + EncoderName: "evm", + SigningAlgo: "ecdsa", + HashingAlgo: "keccak256", + }).Await() + if err != nil { + return fmt.Errorf("generate report %d: %w", i, err) + } + + client := evm.Client{ChainSelector: wt.ChainSelector} + gasLimit := wt.GasLimit + if gasLimit == 0 { + gasLimit = 4_999_000 // just under 5M + } + _, err = client.WriteReport(runtime, &evm.WriteCreReportRequest{ + Receiver: wt.Receiver, + Report: report, + GasConfig: &evm.GasConfig{GasLimit: gasLimit}, + }).Await() + if err != nil { + return fmt.Errorf("write report %d: %w", i, err) + } + } + return nil +} + +// --------------------------------------------------------------------------- +// Phase 7: Logging — up to LogEventLimit (1000), each line under LogLineLimit (1 KB) +// We emit cfg.LogEventCount lines (default 999). +// --------------------------------------------------------------------------- + +func runLoggingPhase(cfg config.Config, runtime cre.Runtime) { + count := cfg.LogEventCount + if count <= 0 { + count = 999 + } + for i := 0; i < count; i++ { + runtime.Logger().Info(fmt.Sprintf("[maxlimits] telemetry line %d/%d: phase=logging ts=%d", + i+1, count, i)) + } +} + +// --------------------------------------------------------------------------- +// HTTP trigger handler (light path) +// --------------------------------------------------------------------------- + +func onHTTPTrigger(cfg config.Config, runtime cre.Runtime, payload *http.Payload) (string, error) { + runtime.Logger().Info("[maxlimits] HTTP trigger fired", + "inputLen", len(payload.Input), + "hasKey", payload.Key != nil, + ) + return "maxlimits-http-ack", nil +} + +// --------------------------------------------------------------------------- +// EVM log trigger handler (light path, shared by all 8 log triggers) +// --------------------------------------------------------------------------- + +func onLogTrigger(cfg config.Config, runtime cre.Runtime, log *evm.Log) (string, error) { + runtime.Logger().Info("[maxlimits] log trigger fired", + "txHash", hex.EncodeToString(log.TxHash), + "logIndex", log.Index, + "dataLen", len(log.Data), + ) + return "maxlimits-log-ack", nil +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +func buildHTTPTriggerConfig(cfg config.Config) *http.Config { + keys := make([]*http.AuthorizedKey, len(cfg.HTTPAuthorizedKeys)) + for i, k := range cfg.HTTPAuthorizedKeys { + var kt http.KeyType + switch k.Type { + case "ECDSA_EVM": + kt = http.KeyType_KEY_TYPE_ECDSA_EVM + default: + kt = http.KeyType_KEY_TYPE_ECDSA_EVM + } + keys[i] = &http.AuthorizedKey{ + Type: kt, + PublicKey: k.PublicKey, + } + } + return &http.Config{AuthorizedKeys: keys} +} + +func buildLogFilter(lt config.LogTriggerConfig) *evm.FilterLogTriggerRequest { + addresses := make([][]byte, len(lt.Addresses)) + for i, a := range lt.Addresses { + b, _ := hex.DecodeString(strings.TrimPrefix(a, "0x")) + addresses[i] = b + } + + topics := make([]*evm.TopicValues, len(lt.TopicSlots)) + for i, slot := range lt.TopicSlots { + vals := make([][]byte, len(slot.Values)) + for j, v := range slot.Values { + b, _ := hex.DecodeString(strings.TrimPrefix(v, "0x")) + vals[j] = b + } + topics[i] = &evm.TopicValues{Values: vals} + } + + return &evm.FilterLogTriggerRequest{ + Addresses: addresses, + Topics: topics, + Confidence: evm.ConfidenceLevel(lt.Confidence), + } +} + +// buildDefaultReportPayload creates an ABI-encoded payload near 5 KB. +func buildDefaultReportPayload() []byte { + typ, err := abi.NewType("tuple[]", "", []abi.ArgumentMarshaling{ + {Name: "FeedID", Type: "bytes32"}, + {Name: "Timestamp", Type: "uint32"}, + {Name: "Price", Type: "uint224"}, + }) + if err != nil { + return []byte("fallback-payload") + } + + type report struct { + FeedID [32]byte + Timestamp uint32 + Price *big.Int + } + + // ~150 bytes per entry × 30 entries ≈ 4.5 KB + reports := make([]report, 30) + for i := range reports { + var feedID [32]byte + copy(feedID[:], common.LeftPadBytes(big.NewInt(int64(i+1)).Bytes(), 32)) + reports[i] = report{ + FeedID: feedID, + Timestamp: uint32(1700000000 + i), + Price: big.NewInt(int64((i + 1) * 100)), + } + } + + args := abi.Arguments{{Name: "Reports", Type: typ}} + packed, err := args.Pack(reports) + if err != nil { + return []byte("fallback-payload") + } + return packed +} + +// buildMaxResponse returns a string near 100 KB (ExecutionResponseLimit). +func buildMaxResponse() string { + const targetLen = 99 * 1024 // 99 KB, safely under 100 KB + var sb strings.Builder + sb.Grow(targetLen) + line := "[maxlimits] execution-complete " + for sb.Len() < targetLen { + sb.WriteString(line) + } + return sb.String()[:targetLen] +} From ed959b455431876d42ba9925104eae65a75267b3 Mon Sep 17 00:00:00 2001 From: mchain0 Date: Fri, 6 Mar 2026 12:39:53 +0100 Subject: [PATCH 2/2] cre-2203: getting ballast to work --- system-tests/lib/cre/workflow/compile.go | 2 +- system-tests/tests/go.mod | 3 + .../tests/smoke/cre/cre_suite_test.go | 5 ++ .../tests/smoke/cre/maxlimits/.gitignore | 1 + .../tests/smoke/cre/maxlimits/Makefile | 15 ++--- .../tests/smoke/cre/maxlimits/ballast.go | 11 ++-- system-tests/tests/smoke/cre/maxlimits/go.mod | 2 +- .../tests/smoke/cre/maxlimits/main.go | 7 ++- .../tests/smoke/cre/v2_maxlimits_test.go | 60 +++++++++++++++++++ .../smoke/cre/v2suite/config/bucketing.go | 4 ++ system-tests/tests/test-helpers/t_helpers.go | 9 ++- 11 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 system-tests/tests/smoke/cre/maxlimits/.gitignore create mode 100644 system-tests/tests/smoke/cre/v2_maxlimits_test.go diff --git a/system-tests/lib/cre/workflow/compile.go b/system-tests/lib/cre/workflow/compile.go index cf45e2c460a..1a8ed51c189 100644 --- a/system-tests/lib/cre/workflow/compile.go +++ b/system-tests/lib/cre/workflow/compile.go @@ -102,7 +102,7 @@ func compileGoWorkflow(ctx context.Context, workflowFilePath, workflowName strin return "", errors.Wrapf(err, "failed to run go mod tidy: %s", string(output)) } - compileCmd := exec.CommandContext(ctx, "go", "build", "-o", workflowWasmPath, filepath.Base(workflowFilePath)) // #nosec G204 -- we control the value of the cmd so the lint/sec error is a false positive + compileCmd := exec.CommandContext(ctx, "go", "build", "-o", workflowWasmPath, ".") // #nosec G204 -- we control the value of the cmd so the lint/sec error is a false positive compileCmd.Dir = filepath.Dir(workflowFilePath) compileCmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GOOS=wasip1", "GOARCH=wasm") if output, err := compileCmd.CombinedOutput(); err != nil { diff --git a/system-tests/tests/go.mod b/system-tests/tests/go.mod index d1b539d4ca0..6e1245fc2e8 100644 --- a/system-tests/tests/go.mod +++ b/system-tests/tests/go.mod @@ -39,6 +39,8 @@ replace github.com/smartcontractkit/chainlink/system-tests/tests/regression/cre/ replace github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/solana/solwrite => ./smoke/cre/solana/solwrite +replace github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits => ./smoke/cre/maxlimits + require ( github.com/Masterminds/semver/v3 v3.4.0 github.com/avast/retry-go/v4 v4.6.1 @@ -82,6 +84,7 @@ require ( github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/evm/logtrigger v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/evmread v0.0.0-20250917232237-c4ecf802c6f8 github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/solana/solwrite v0.0.0-00010101000000-000000000000 + github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits v0.0.0-00010101000000-000000000000 github.com/smartcontractkit/libocr v0.0.0-20260130195252-6e18e2a30acc github.com/stretchr/testify v1.11.1 golang.org/x/sync v0.19.0 diff --git a/system-tests/tests/smoke/cre/cre_suite_test.go b/system-tests/tests/smoke/cre/cre_suite_test.go index 2f4a7075c36..4e9c9fbeea7 100644 --- a/system-tests/tests/smoke/cre/cre_suite_test.go +++ b/system-tests/tests/smoke/cre/cre_suite_test.go @@ -161,6 +161,11 @@ func runV2SuiteScenario(t *testing.T, topology string, scenario v2suite_config.S testEnv := t_helpers.SetupTestEnvironmentWithConfig(t, t_helpers.GetDefaultTestConfig(t)) ExecuteConsensusTest(t, testEnv) }) + case v2suite_config.SuiteScenarioMaxLimits: + t.Run("[v2] Max Limits - "+topology, func(t *testing.T) { + testEnv := t_helpers.SetupTestEnvironmentWithConfig(t, t_helpers.GetDefaultTestConfig(t)) + ExecuteMaxLimitsTest(t, testEnv) + }) default: require.Failf(t, "unsupported V2 suite scenario", "scenario %q is not supported by the runner", scenario.String()) } diff --git a/system-tests/tests/smoke/cre/maxlimits/.gitignore b/system-tests/tests/smoke/cre/maxlimits/.gitignore new file mode 100644 index 00000000000..634a04a0a19 --- /dev/null +++ b/system-tests/tests/smoke/cre/maxlimits/.gitignore @@ -0,0 +1 @@ +ballast.dat \ No newline at end of file diff --git a/system-tests/tests/smoke/cre/maxlimits/Makefile b/system-tests/tests/smoke/cre/maxlimits/Makefile index 737ce288388..e7e392b6b6b 100644 --- a/system-tests/tests/smoke/cre/maxlimits/Makefile +++ b/system-tests/tests/smoke/cre/maxlimits/Makefile @@ -6,18 +6,19 @@ export CGO_ENABLED := 0 LDFLAGS := -buildid= -w -s # 14 MB of random data pushes compressed binary to ~18 MB (under 20 MB limit) -BALLAST_KB := 14336 +BALLAST_MAX_KB := 14336 -# Standard build — exercises all call limits, config, and logging limits. -build: +# Standard build — uses a minimal 1 KB ballast placeholder. +build: ballast.dat go build -o workflow.wasm -trimpath -ldflags="$(LDFLAGS)" . -# Max-binary build — also pushes WASMCompressedBinarySizeLimit (~18 MB compressed). -build-max: ballast.dat - go build -tags maxbinary -o workflow.wasm -trimpath -ldflags="$(LDFLAGS)" . +# Max-binary build — generates a large ballast to push WASMCompressedBinarySizeLimit. +build-max: + dd if=/dev/urandom of=ballast.dat bs=1024 count=$(BALLAST_MAX_KB) + go build -o workflow.wasm -trimpath -ldflags="$(LDFLAGS)" . ballast.dat: - dd if=/dev/urandom of=$@ bs=1024 count=$(BALLAST_KB) + dd if=/dev/urandom of=$@ bs=1024 count=1 clean: rm -f workflow.wasm ballast.dat diff --git a/system-tests/tests/smoke/cre/maxlimits/ballast.go b/system-tests/tests/smoke/cre/maxlimits/ballast.go index 3558b6f56d0..f870e649c43 100644 --- a/system-tests/tests/smoke/cre/maxlimits/ballast.go +++ b/system-tests/tests/smoke/cre/maxlimits/ballast.go @@ -1,4 +1,4 @@ -//go:build wasip1 && maxbinary +//go:build wasip1 package main @@ -7,12 +7,9 @@ import ( "fmt" ) -// ballast.dat must be generated before building with -tags maxbinary: -// -// dd if=/dev/urandom of=ballast.dat bs=1024 count=14336 -// -// This produces a ~14 MB file that inflates the compressed WASM binary -// to approach the WASMCompressedBinarySizeLimit (20 MB). +// ballast.dat must exist before building. Use `make build` for a minimal +// placeholder or `make build-max` for a ~14 MB file that pushes the +// compressed WASM binary toward the 20 MB limit. // //go:embed ballast.dat var ballastData []byte diff --git a/system-tests/tests/smoke/cre/maxlimits/go.mod b/system-tests/tests/smoke/cre/maxlimits/go.mod index 8aea794fdd0..9cf8fc3f3b0 100644 --- a/system-tests/tests/smoke/cre/maxlimits/go.mod +++ b/system-tests/tests/smoke/cre/maxlimits/go.mod @@ -1,6 +1,6 @@ module github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits -go 1.25.3 +go 1.25.7 require ( github.com/ethereum/go-ethereum v1.17.0 diff --git a/system-tests/tests/smoke/cre/maxlimits/main.go b/system-tests/tests/smoke/cre/maxlimits/main.go index e8512b3bf8e..e9b91c914d8 100644 --- a/system-tests/tests/smoke/cre/maxlimits/main.go +++ b/system-tests/tests/smoke/cre/maxlimits/main.go @@ -48,10 +48,13 @@ func RunWorkflow(cfg config.Config, logger *slog.Logger, _ cre.SecretsProvider) cron.Trigger(&cron.Config{Schedule: cfg.CronSchedule}), onCronTrigger, ), - cre.Handler( + } + + if len(cfg.HTTPAuthorizedKeys) > 0 { + wf = append(wf, cre.Handler( http.Trigger(buildHTTPTriggerConfig(cfg)), onHTTPTrigger, - ), + )) } for _, lt := range cfg.LogTriggers { diff --git a/system-tests/tests/smoke/cre/v2_maxlimits_test.go b/system-tests/tests/smoke/cre/v2_maxlimits_test.go new file mode 100644 index 00000000000..5fb3d2974f1 --- /dev/null +++ b/system-tests/tests/smoke/cre/v2_maxlimits_test.go @@ -0,0 +1,60 @@ +package cre + +import ( + "crypto/rand" + "os" + "path/filepath" + "testing" + "time" + + commonevents "github.com/smartcontractkit/chainlink-protos/workflows/go/common" + workflowevents "github.com/smartcontractkit/chainlink-protos/workflows/go/events" + + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/stretchr/testify/require" + + maxlimitsconfig "github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits/config" + t_helpers "github.com/smartcontractkit/chainlink/system-tests/tests/test-helpers" + ttypes "github.com/smartcontractkit/chainlink/system-tests/tests/test-helpers/configuration" +) + +func ExecuteMaxLimitsTest(t *testing.T, testEnv *ttypes.TestEnvironment) { + testLogger := framework.L + + ballastPath := filepath.Join("maxlimits", "ballast.dat") + generateBallast(t, ballastPath, 14*1024*1024) + t.Cleanup(func() { os.Remove(ballastPath) }) + + userLogsCh := make(chan *workflowevents.UserLogs, 1000) + baseMessageCh := make(chan *commonevents.BaseMessage, 1000) + + server := t_helpers.StartChipTestSink(t, t_helpers.GetPublishFn(testLogger, userLogsCh, baseMessageCh)) + t.Cleanup(func() { + server.Shutdown(t.Context()) + close(userLogsCh) + close(baseMessageCh) + }) + + workflowConfig := maxlimitsconfig.Config{ + CronSchedule: "*/30 * * * * *", + ConsensusRounds: 1, + ConsensusPayloadSize: 100, + LogEventCount: 5, + } + + _ = t_helpers.CompileAndDeployWorkflow(t, testEnv, testLogger, "maxlimitsworkflow", + &workflowConfig, "./maxlimits/main.go") + + t_helpers.WatchWorkflowLogs(t, testLogger, userLogsCh, baseMessageCh, + t_helpers.WorkflowEngineInitErrorLog, + "[maxlimits] cron handler completed all phases", + 4*time.Minute) +} + +func generateBallast(t *testing.T, path string, sizeBytes int) { + t.Helper() + data := make([]byte, sizeBytes) + _, err := rand.Read(data) + require.NoError(t, err, "failed to generate ballast data") + require.NoError(t, os.WriteFile(path, data, 0644), "failed to write ballast.dat") +} diff --git a/system-tests/tests/smoke/cre/v2suite/config/bucketing.go b/system-tests/tests/smoke/cre/v2suite/config/bucketing.go index fe7df85fbd4..5cdb46a1284 100644 --- a/system-tests/tests/smoke/cre/v2suite/config/bucketing.go +++ b/system-tests/tests/smoke/cre/v2suite/config/bucketing.go @@ -12,6 +12,7 @@ const ( SuiteScenarioHTTPActionCRUD SuiteScenarioDONTime SuiteScenarioConsensus + SuiteScenarioMaxLimits SuiteScenarioLen ) @@ -31,6 +32,8 @@ func (s SuiteScenario) String() string { return "DONTime" case SuiteScenarioConsensus: return "Consensus" + case SuiteScenarioMaxLimits: + return "MaxLimits" default: return fmt.Sprintf("unknown SuiteScenario: %d", s) } @@ -68,6 +71,7 @@ var suiteBucketRegistry = []suiteBucketDefinition{ Bucket: SuiteBucketB, Scenarios: []SuiteScenario{ SuiteScenarioVaultDON, + SuiteScenarioMaxLimits, }, }, { diff --git a/system-tests/tests/test-helpers/t_helpers.go b/system-tests/tests/test-helpers/t_helpers.go index e73824c14ae..f35fc7b309e 100644 --- a/system-tests/tests/test-helpers/t_helpers.go +++ b/system-tests/tests/test-helpers/t_helpers.go @@ -67,6 +67,7 @@ import ( http_config "github.com/smartcontractkit/chainlink/system-tests/tests/regression/cre/http/config" httpaction_negative_config "github.com/smartcontractkit/chainlink/system-tests/tests/regression/cre/httpaction-negative/config" httpaction_smoke_config "github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/httpaction/config" + maxlimits_config "github.com/smartcontractkit/chainlink/system-tests/tests/smoke/cre/maxlimits/config" ) const WorkflowEngineInitErrorLog = "Workflow Engine initialization failed" @@ -290,7 +291,8 @@ type WorkflowConfig interface { http_config.Config | httpaction_smoke_config.Config | httpaction_negative_config.Config | - solwrite_config.Config + solwrite_config.Config | + maxlimits_config.Config } // None represents an empty workflow configuration @@ -440,6 +442,11 @@ func workflowConfigFactory[T WorkflowConfig](t *testing.T, testLogger zerolog.Lo workflowConfigFilePath = workflowCfgFilePath require.NoError(t, configErr, "failed to create solwrite workflow config file") testLogger.Info().Msg("Solana write workflow config file created.") + case *maxlimits_config.Config: + workflowCfgFilePath, configErr := CreateWorkflowYamlConfigFile(workflowName, cfg) + workflowConfigFilePath = workflowCfgFilePath + require.NoError(t, configErr, "failed to create maxlimits workflow config file") + testLogger.Info().Msg("Max-limits workflow config file created.") default: require.NoError(t, fmt.Errorf("unsupported workflow config type: %T", cfg)) }