diff --git a/.github/workflows/chaos.yml b/.github/workflows/chaos.yml new file mode 100644 index 00000000..600ba169 --- /dev/null +++ b/.github/workflows/chaos.yml @@ -0,0 +1,98 @@ +name: chaos +on: + schedule: + - cron: '0 */3 * * *' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Cache cargo registry + uses: actions/cache@v2.1.7 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-v2-cargo-registry-${{ hashFiles('**/Cargo.lock') }} + - name: Cache cargo index + uses: actions/cache@v2.1.7 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-v2-cargo-index-${{ hashFiles('**/Cargo.lock') }} + - name: Build Artifacts + run: | + docker run --rm -v "$(pwd)":/code \ + --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ + --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ + cosmwasm/workspace-optimizer:0.12.4 + - uses: actions/upload-artifact@master + with: + name: artifacts + path: artifacts + chaos: + runs-on: ubuntu-latest + needs: build + env: + CGO_ENABLED: 1 + steps: + - name: Checkout the repo + uses: actions/checkout@v2 + - name: Setup go + uses: actions/setup-go@v1 + with: + go-version: 1.17 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.QA_AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.QA_AWS_SECRET_KEY }} + aws-region: ${{ secrets.QA_AWS_REGION }} + role-to-assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + role-duration-seconds: 3600 + - name: Set Kubernetes Context + uses: azure/k8s-set-context@v1 + with: + method: kubeconfig + kubeconfig: ${{ secrets.QA_KUBECONFIG }} + - name: Cache Vendor Packages + uses: actions/cache@v2 + id: cache-packages + with: + path: | + ~/.cache/go-build + ~/go/pkg/mod + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + - name: Download Go Vendor Packages + if: steps.cache-packages.outputs.cache-hit != 'true' + run: go mod download + - name: Install Ginkgo CLI + run: | + go get github.com/onsi/ginkgo/v2/ginkgo/generators@v2.0.0 + go get github.com/onsi/ginkgo/v2/ginkgo/internal@v2.0.0 + go get github.com/onsi/ginkgo/v2/ginkgo/labels@v2.0.0 + go install github.com/onsi/ginkgo/v2/ginkgo + - uses: actions/download-artifact@master + with: + name: artifacts + path: artifacts + - name: Run Tests + env: + SELECTED_NETWORKS: localterra + NETWORK_SETTINGS: /home/runner/work/chainlink-terra/chainlink-terra/tests/e2e/networks.yaml + run: | + export PATH=$PATH:$(go env GOPATH)/bin + ginkgo tests/e2e/chaos + - name: Publish Test Results + uses: mikepenz/action-junit-report@v2 + if: always() + with: + report_paths: "./tests-chaos-report.xml" + github_token: ${{ secrets.GITHUB_TOKEN }} + check_name: Chaos Test Results + - name: Publish Artifacts + if: failure() + uses: actions/upload-artifact@v2.2.4 + with: + name: test-logs + path: /home/runner/work/chainlink-terra/chainlink-terra/tests/e2e/logs diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 3af9b241..41e4980f 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -93,4 +93,4 @@ jobs: uses: actions/upload-artifact@v2.2.4 with: name: test-logs - path: ./logs + path: /home/runner/work/chainlink-terra/chainlink-terra/tests/e2e/logs diff --git a/.golangci.yml b/.golangci.yml index 7a2157a8..49a49da9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -79,6 +79,9 @@ issues: text: "^G404:" linters: - gosec + - path: tests + linters: + - revive include: # Enable GoDoc comment checks. - EXC0002 diff --git a/go.mod b/go.mod index 072cfe7f..0730c84d 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/rs/zerolog v1.26.1 github.com/satori/go.uuid v1.2.0 github.com/smartcontractkit/chainlink v1.0.1-0.20220114200720-95a78cb9fc2b - github.com/smartcontractkit/helmenv v1.0.27 + github.com/smartcontractkit/helmenv v1.0.32 github.com/smartcontractkit/integrations-framework v1.0.42 github.com/smartcontractkit/libocr v0.0.0-20220121130134-5d2b1d5f424b github.com/smartcontractkit/terra.go v1.0.3-0.20220108002221-62b39252ee16 diff --git a/go.sum b/go.sum index 69a6ae3d..a56f0281 100644 --- a/go.sum +++ b/go.sum @@ -2346,8 +2346,9 @@ github.com/smartcontractkit/chainlink-terra v0.0.0-20220114184203-9ed72d62eaac/g github.com/smartcontractkit/ed25519consensus v0.0.1 h1:Ta23Y6YJTACLCpKWSWAwgHCtkmWrGGfrUAV8Ns5r4z0= github.com/smartcontractkit/ed25519consensus v0.0.1/go.mod h1:8Wf0F4mu3B5DdEOFKMMLgSoZ8EyaA2Cmf4YMUsNHXzE= github.com/smartcontractkit/helmenv v1.0.20/go.mod h1:oFDq/QeOcHMJ8Cl+UEOA3tAmYMHo2ONxOsMYvbzg91E= -github.com/smartcontractkit/helmenv v1.0.27 h1:BVxTQZQlFElh6YVK8mY9LSljX7uyx4jyImmHh/fMqEo= github.com/smartcontractkit/helmenv v1.0.27/go.mod h1:ef0doolSZf8ckqaWMIK2M+EPXdIKYVzttd6EXaCgCK4= +github.com/smartcontractkit/helmenv v1.0.32 h1:CX8bcmZOsAPbhwdUbaD4AJxqgJV6R1CfgEb0YjT8CAc= +github.com/smartcontractkit/helmenv v1.0.32/go.mod h1:ef0doolSZf8ckqaWMIK2M+EPXdIKYVzttd6EXaCgCK4= github.com/smartcontractkit/integrations-framework v1.0.21/go.mod h1:mceWG89OYgDkqIVD64Ocp2pn1utqoHAC4ycVV33JOTo= github.com/smartcontractkit/integrations-framework v1.0.24/go.mod h1:yFgbE88F6+om0jo5mFskgKEQBnxqfYg6H1OL2dCIekk= github.com/smartcontractkit/integrations-framework v1.0.42 h1:3AGYdYz86Jl10eXagmhFGvIyDZdDPcZ/JwqkUlCzXTE= diff --git a/ops/Pulumi.localterra.yaml b/ops/Pulumi.localterra.yaml index 934d495a..c1d8f162 100644 --- a/ops/Pulumi.localterra.yaml +++ b/ops/Pulumi.localterra.yaml @@ -22,7 +22,13 @@ config: - FEATURE_OFFCHAIN_REPORTING2 - P2P_NETWORKING_STACK - P2P_LISTEN_PORT + - EVM_ENABLED + - EVM_RPC_ENABLED + - TERRA_ENABLED terra-env:CL-CHAINLINK_DEV: true + terra-env:CL-EVM_ENABLED: false + terra-env:CL-EVM_RPC_ENABLED: false + terra-env:CL-TERRA_ENABLED: true terra-env:CL-LOG_LEVEL: "debug" terra-env:CL-ETH_DISABLED: true terra-env:CL-USE_LEGACY_ETH_ENV_VARS: false diff --git a/tests/e2e/chaos/README.md b/tests/e2e/chaos/README.md new file mode 100644 index 00000000..577dd87d --- /dev/null +++ b/tests/e2e/chaos/README.md @@ -0,0 +1,13 @@ +### Persistent K8s env chaos test run +In order to deploy contracts and env only once and then run chaos suite: +1. Paste version of image you want to run in `chainlink-relay-terra.yaml` +2. Spin up an environment +```shell +envcli new --preset chainlink-relay-terra.yaml +``` +3. Run initial test +```shell +SELECTED_NETWORKS="localterra" NETWORK_SETTINGS="${YOUR_NETWORKS_FILE}" ENVIRONMENT_FILE="${YOUR_ENV_YAML}" ginkgo tests/e2e/chaos +``` +4. Set `contracts_deployed: true` in `networks.yaml` +5. Run tests again \ No newline at end of file diff --git a/tests/e2e/chaos/chainlink-relay-terra.yaml b/tests/e2e/chaos/chainlink-relay-terra.yaml new file mode 100644 index 00000000..c3c0a98f --- /dev/null +++ b/tests/e2e/chaos/chainlink-relay-terra.yaml @@ -0,0 +1,33 @@ +namespace_prefix: chainlink +charts: + localterra: + index: 1 + mockserver-config: + index: 2 + mockserver: + index: 3 + chainlink: + index: 4 + values: + replicas: 5 + chainlink: + image: + image: "public.ecr.aws/z0b1w9r9/chainlink" + version: "candidate-develop-terr-qa-test-feb-02.ea461a08f1927b99f37fac4b078cd4b42a88995a" + db: + stateful: true + capacity: 2Gi + env: + EVM_ENABLED: "false" + EVM_RPC_ENABLED: "false" + TERRA_ENABLED: "true" + eth_disabled: "true" + CHAINLINK_DEV: "true" + USE_LEGACY_ETH_ENV_VARS: "true" + feature_external_initiators: "true" + FEATURE_OFFCHAIN_REPORTING2: "true" + P2P_NETWORKING_STACK: "V2" + P2PV2_LISTEN_ADDRESSES: "0.0.0.0:6690" + P2PV2_DELTA_DIAL: "5s" + P2PV2_DELTA_RECONCILE: "5s" + p2p_listen_port: "0" diff --git a/tests/e2e/chaos/chaos_test.go b/tests/e2e/chaos/chaos_test.go new file mode 100644 index 00000000..fd0b49b7 --- /dev/null +++ b/tests/e2e/chaos/chaos_test.go @@ -0,0 +1,51 @@ +package chaos + +import ( + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + tc "github.com/smartcontractkit/chainlink-terra/tests/e2e/common" + "github.com/smartcontractkit/chainlink-terra/tests/e2e/smoke/common" + "github.com/smartcontractkit/integrations-framework/actions" +) + +var _ = Describe("Solana chaos suite", func() { + var state = &common.OCRv2State{} + BeforeEach(func() { + By("Deploying OCRv2 cluster", func() { + state.DeployCluster(5, true) + state.LabelChaosGroups() + tc.ImitateSource(state.MockServer, common.SourceChangeInterval, 2, 10) + }) + }) + It("Can tolerate chaos experiments", func() { + By("Stable and working", func() { + state.ValidateRoundsAfter(time.Now(), 10) + }) + By("Can work with faulty nodes offline", func() { + state.CanWorkWithFaultyNodesOffline() + }) + By("Can't work when more than faulty nodes are offline", func() { + state.CantWorkWithMoreThanFaultyNodesSplit() + }) + By("Can't work with two parts network split, restored after", func() { + state.RestoredAfterNetworkSplit() + }) + By("Can recover from yellow group loss connection to validator", func() { + state.CanWorkYellowGroupNoValidatorConnection() + }) + By("Can recover after all nodes lost connection to validator", func() { + state.CanRecoverAllNodesValidatorConnectionLoss() + }) + By("Can work after all nodes restarted", func() { + state.CanWorkAfterAllNodesRestarted() + }) + }) + AfterEach(func() { + By("Tearing down the environment", func() { + err := actions.TeardownSuite(state.Env, nil, "logs") + Expect(err).ShouldNot(HaveOccurred()) + }) + }) +}) diff --git a/tests/e2e/chaos/suite_test.go b/tests/e2e/chaos/suite_test.go new file mode 100644 index 00000000..382f9616 --- /dev/null +++ b/tests/e2e/chaos/suite_test.go @@ -0,0 +1,13 @@ +package chaos_test + +import ( + "github.com/smartcontractkit/chainlink-terra/tests/e2e/utils" + "testing" + + . "github.com/onsi/ginkgo/v2" +) + +func Test_Suite(t *testing.T) { + utils.GinkgoSuite() + RunSpecs(t, "Chaos") +} diff --git a/tests/e2e/client.go b/tests/e2e/client.go index 2f3aab95..87a52e3c 100644 --- a/tests/e2e/client.go +++ b/tests/e2e/client.go @@ -33,7 +33,7 @@ const ( ) type NetworkConfig struct { - ContractDeployed bool `mapstructure:"contracts_deployed" yaml:"contract_deployed"` + ContractDeployed bool `mapstructure:"contracts_deployed" yaml:"contracts_deployed"` External bool `mapstructure:"external" yaml:"external"` Currency string `mapstructure:"currency" yaml:"currency"` Name string `mapstructure:"name" yaml:"name"` @@ -116,7 +116,7 @@ func ClientInitFunc() func(networkName string, networkConfig map[string]interfac return nil, err } var cfg *NetworkConfig - if err := yaml.Unmarshal(d, &cfg); err != nil { + if err = yaml.Unmarshal(d, &cfg); err != nil { return nil, err } cfg.ID = networkName diff --git a/tests/e2e/common/common.go b/tests/e2e/common/common.go index 8063e8f4..f62c5cc4 100644 --- a/tests/e2e/common/common.go +++ b/tests/e2e/common/common.go @@ -20,6 +20,8 @@ import ( "golang.org/x/crypto/curve25519" ) +// TODO: those should be moved as a common part of integrations-framework + const ( ChainName = "terra" ) diff --git a/tests/e2e/deployer.go b/tests/e2e/deployer.go index a4453594..577ab7a0 100644 --- a/tests/e2e/deployer.go +++ b/tests/e2e/deployer.go @@ -150,8 +150,8 @@ func (t *ContractDeployer) DeployOCRv2(paymentControllerAddr string, requesterCo return nil, err } return &OCRv2{ - client: t.client, - address: addr, + Client: t.client, + Addr: addr, }, nil } diff --git a/tests/e2e/env.go b/tests/e2e/env.go index 569083cb..d16395b6 100644 --- a/tests/e2e/env.go +++ b/tests/e2e/env.go @@ -3,8 +3,8 @@ package e2e import "github.com/smartcontractkit/helmenv/environment" // NewChainlinkTerraEnv returns a cluster config with LocalTerra node -func NewChainlinkTerraEnv() *environment.Config { - return &environment.Config{ +func NewChainlinkTerraEnv(nodes int, stateful bool) *environment.Config { + env := &environment.Config{ NamespacePrefix: "chainlink-terra", Charts: environment.Charts{ "localterra": { @@ -19,14 +19,17 @@ func NewChainlinkTerraEnv() *environment.Config { "chainlink": { Index: 4, Values: map[string]interface{}{ - "replicas": 5, + "replicas": nodes, "chainlink": map[string]interface{}{ "image": map[string]interface{}{ - "image": "public.ecr.aws/z0b1w9r9/chainlink", - "version": "develop.3abacbfc0761b4f6cf4d4d897bb4f94d05a4f793", + "image": "795953128386.dkr.ecr.us-west-2.amazonaws.com/chainlink", + "version": "develop.latest", }, }, "env": map[string]interface{}{ + "EVM_ENABLED": "false", + "EVM_RPC_ENABLED": "false", + "TERRA_ENABLED": "true", "eth_disabled": "true", "CHAINLINK_DEV": "true", "USE_LEGACY_ETH_ENV_VARS": "false", @@ -42,4 +45,11 @@ func NewChainlinkTerraEnv() *environment.Config { }, }, } + if stateful { + env.Charts["chainlink"].Values["db"] = map[string]interface{}{ + "stateful": true, + "capacity": "2Gi", + } + } + return env } diff --git a/tests/e2e/networks.yaml b/tests/e2e/networks.yaml index cb7966ce..c0cd0e78 100644 --- a/tests/e2e/networks.yaml +++ b/tests/e2e/networks.yaml @@ -5,6 +5,7 @@ networks: name: "localterra" type: terra currency: uluna + contracts_deployed: false mnemonics: # prefunded default LocalTerra mnemonics - satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn diff --git a/tests/e2e/ocr2.go b/tests/e2e/ocr2.go index 2e30cdfa..e4d0691e 100644 --- a/tests/e2e/ocr2.go +++ b/tests/e2e/ocr2.go @@ -5,6 +5,8 @@ import ( "encoding/json" "strconv" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink-terra/tests/e2e/ocr2types" "github.com/smartcontractkit/integrations-framework/contracts" "github.com/smartcontractkit/libocr/offchainreporting2/confighelper" @@ -14,52 +16,97 @@ import ( // OCRv2 represents a OVR v2 contract deployed on terra as WASM type OCRv2 struct { - client *TerraLCDClient - address msg.AccAddress -} - -func (t *OCRv2) ProgramAddress() string { - panic("implement me") + Client *TerraLCDClient + Addr msg.AccAddress } -func (t *OCRv2) TransmissionsAddr() string { - panic("implement me") -} - -func (t *OCRv2) DumpState() error { - panic("implement me") -} - -func (t *OCRv2) GetContractData(ctx context.Context) (*contracts.OffchainAggregatorData, error) { - panic("implement me") +// SetValidatorConfig sets validator config +func (t *OCRv2) SetValidatorConfig(gasLimit uint64, validatorAddr string) error { + sender := t.Client.DefaultWallet.AccAddress + executeMsg := ocr2types.ExecuteSetValidator{ + SetValidator: ocr2types.ExecuteSetValidatorConfig{ + Config: ocr2types.ExecuteSetValidatorConfigType{ + Address: validatorAddr, + GasLimit: gasLimit, + }, + }} + executeMsgBytes, err := json.Marshal(executeMsg) + if err != nil { + return err + } + _, err = t.Client.SendTX(terraClient.CreateTxOptions{ + Msgs: []msg.Msg{ + msg.NewMsgExecuteContract( + sender, + t.Addr, + executeMsgBytes, + msg.NewCoins(), + ), + }, + }, true) + if err != nil { + return err + } + return nil } -func (t *OCRv2) AuthorityAddr(s string) (string, error) { - panic("implement me") +func payeesTuple(transmitters []string, receiver string) [][]string { + payees := make([][]string, 0) + for _, t := range transmitters { + payees = append(payees, []string{t, receiver}) + } + return payees } -func (t *OCRv2) SetValidatorConfig(flaggingThreshold uint32, validatorAddr string) error { - panic("implement me") +// SetPayees sets payees for observations +func (t *OCRv2) SetPayees(transmitters []string) error { + sender := t.Client.DefaultWallet.AccAddress + payees := payeesTuple(transmitters, sender.String()) + executeMsg := ocr2types.ExecuteSetPayees{ + SetPayees: ocr2types.ExecuteSetPayeesConfig{ + Payees: payees, + }} + executeMsgBytes, err := json.Marshal(executeMsg) + if err != nil { + return err + } + _, err = t.Client.SendTX(terraClient.CreateTxOptions{ + Msgs: []msg.Msg{ + msg.NewMsgExecuteContract( + sender, + t.Addr, + executeMsgBytes, + msg.NewCoins(), + ), + }, + }, true) + if err != nil { + return err + } + return nil } -func (t *OCRv2) SetBilling(op uint32, tp uint32, controllerAddr string) error { - sender := t.client.DefaultWallet.AccAddress +// SetBilling sets billing params for OCR +func (t *OCRv2) SetBilling(baseGas uint64, op uint64, tp uint64, recommendedGasPriceULuna string, controllerAddr string) error { + sender := t.Client.DefaultWallet.AccAddress executeMsg := ocr2types.ExecuteSetBillingMsg{ SetBilling: ocr2types.ExecuteSetBillingMsgType{ Config: ocr2types.ExecuteSetBillingConfigMsgType{ + BaseGas: baseGas, + TransmissionPayment: tp, ObservationPayment: op, - RecommendedGasPrice: 1, + RecommendedGasPrice: recommendedGasPriceULuna, }, }} executeMsgBytes, err := json.Marshal(executeMsg) if err != nil { return err } - _, err = t.client.SendTX(terraClient.CreateTxOptions{ + _, err = t.Client.SendTX(terraClient.CreateTxOptions{ Msgs: []msg.Msg{ msg.NewMsgExecuteContract( sender, - t.address, + t.Addr, executeMsgBytes, msg.NewCoins(), ), @@ -73,19 +120,17 @@ func (t *OCRv2) SetBilling(op uint32, tp uint32, controllerAddr string) error { func (t *OCRv2) GetLatestRoundData() (uint64, uint64, uint64, error) { resp := ocr2types.QueryLatestRoundDataResponse{} - if err := t.client.QuerySmart(context.Background(), t.address, ocr2types.QueryLatestRoundData, &resp); err != nil { + log.Warn().Interface("Addr", t.Addr) + if err := t.Client.QuerySmart(context.Background(), t.Addr, ocr2types.QueryLatestRoundData, &resp); err != nil { return 0, 0, 0, err } answer, _ := strconv.Atoi(resp.QueryResult.Answer) return uint64(answer), resp.QueryResult.TransmissionTimestamp, resp.QueryResult.RoundID, nil } -func (t *OCRv2) SetOracles(ocParams contracts.OffChainAggregatorV2Config) error { - panic("implement me") -} - -func (t *OCRv2) SetOffChainConfig(cfg contracts.OffChainAggregatorV2Config) error { - sender := t.client.DefaultWallet.AccAddress +// SetOffChainConfig sets offchain config +func (t *OCRv2) SetOffChainConfig(cfg contracts.OffChainAggregatorV2Config) ([]string, error) { + sender := t.Client.DefaultWallet.AccAddress signers, transmitters, f, onChainCfg, version, offChainConfigBytes, err := confighelper.ContractSetConfigArgsForTests( cfg.DeltaProgress, cfg.DeltaResend, @@ -105,7 +150,7 @@ func (t *OCRv2) SetOffChainConfig(cfg contracts.OffChainAggregatorV2Config) erro cfg.OnchainConfig, ) if err != nil { - return err + return nil, err } // convert type for marshalling signerArray := [][]byte{} @@ -127,22 +172,22 @@ func (t *OCRv2) SetOffChainConfig(cfg contracts.OffChainAggregatorV2Config) erro } executeMsgBytes, err := json.Marshal(tx) if err != nil { - return err + return nil, err } - _, err = t.client.SendTX(terraClient.CreateTxOptions{ + _, err = t.Client.SendTX(terraClient.CreateTxOptions{ Msgs: []msg.Msg{ msg.NewMsgExecuteContract( sender, - t.address, + t.Addr, executeMsgBytes, msg.NewCoins(), ), }, }, true) if err != nil { - return err + return nil, err } - return nil + return transmitterArray, nil } func (t *OCRv2) RequestNewRound() error { @@ -152,9 +197,9 @@ func (t *OCRv2) RequestNewRound() error { func (t *OCRv2) GetOwedPayment(transmitter string) (map[string]interface{}, error) { transmitterAddr, _ := msg.AccAddressFromBech32(transmitter) resp := make(map[string]interface{}) - if err := t.client.QuerySmart( + if err := t.Client.QuerySmart( context.Background(), - t.address, + t.Addr, ocr2types.QueryOwedPaymentMsg{ OwedPayment: ocr2types.QueryOwedPaymentTypeMsg{ Transmitter: transmitterAddr, @@ -169,9 +214,9 @@ func (t *OCRv2) GetOwedPayment(transmitter string) (map[string]interface{}, erro func (t *OCRv2) GetRoundData(roundID uint32) (map[string]interface{}, error) { resp := make(map[string]interface{}) - if err := t.client.QuerySmart( + if err := t.Client.QuerySmart( context.Background(), - t.address, + t.Addr, ocr2types.QueryRoundDataMsg{ RoundData: ocr2types.QueryRoundDataTypeMsg{ RoundID: roundID, @@ -186,14 +231,14 @@ func (t *OCRv2) GetRoundData(roundID uint32) (map[string]interface{}, error) { func (t *OCRv2) GetLatestConfigDetails() (map[string]interface{}, error) { resp := make(map[string]interface{}) - if err := t.client.QuerySmart(context.Background(), t.address, ocr2types.QueryLatestConfigDetails, &resp); err != nil { + if err := t.Client.QuerySmart(context.Background(), t.Addr, ocr2types.QueryLatestConfigDetails, &resp); err != nil { return nil, err } return resp, nil } func (t *OCRv2) TransferOwnership(to string) error { - sender := t.client.DefaultWallet.AccAddress + sender := t.Client.DefaultWallet.AccAddress toAddr, _ := msg.AccAddressFromHex(to) executeMsg := ocr2types.ExecuteTransferOwnershipMsg{ TransferOwnership: ocr2types.ExecuteTransferOwnershipMsgType{ @@ -203,11 +248,11 @@ func (t *OCRv2) TransferOwnership(to string) error { if err != nil { return err } - _, err = t.client.SendTX(terraClient.CreateTxOptions{ + _, err = t.Client.SendTX(terraClient.CreateTxOptions{ Msgs: []msg.Msg{ msg.NewMsgExecuteContract( sender, - t.address, + t.Addr, executeMsgBytes, msg.NewCoins(), ), @@ -219,6 +264,7 @@ func (t *OCRv2) TransferOwnership(to string) error { return nil } +// Address gets OCR2 Address func (t *OCRv2) Address() string { - return t.address.String() + return t.Addr.String() } diff --git a/tests/e2e/ocr2types/ocr2.go b/tests/e2e/ocr2types/ocr2.go index d5b049ff..d8804220 100644 --- a/tests/e2e/ocr2types/ocr2.go +++ b/tests/e2e/ocr2types/ocr2.go @@ -1,9 +1,6 @@ package ocr2types import ( - "fmt" - "strings" - "github.com/smartcontractkit/terra.go/msg" ) @@ -57,6 +54,32 @@ type OCRv2InstantiateMsg struct { MaxAnswer string `json:"max_answer"` } +// ExecuteSetValidator execute set validator msg +type ExecuteSetValidator struct { + SetValidator ExecuteSetValidatorConfig `json:"set_validator_config"` +} + +// ExecuteSetValidatorConfig execute set validator msg +type ExecuteSetValidatorConfig struct { + Config ExecuteSetValidatorConfigType `json:"config"` +} + +// ExecuteSetValidatorConfigType execute set validator msg +type ExecuteSetValidatorConfigType struct { + Address string `json:"address"` + GasLimit uint64 `json:"gas_limit"` +} + +// ExecuteSetPayees set payees msg +type ExecuteSetPayees struct { + SetPayees ExecuteSetPayeesConfig `json:"set_payees"` +} + +// ExecuteSetPayeesConfig set payees msg +type ExecuteSetPayeesConfig struct { + Payees [][]string `json:"payees"` +} + type ExecuteSetBillingMsg struct { SetBilling ExecuteSetBillingMsgType `json:"set_billing"` } @@ -66,8 +89,10 @@ type ExecuteSetBillingMsgType struct { } type ExecuteSetBillingConfigMsgType struct { - ObservationPayment uint32 `json:"observation_payment"` - RecommendedGasPrice uint32 `json:"recommended_gas_price"` + BaseGas uint64 `json:"base_gas"` + TransmissionPayment uint64 `json:"transmission_payment_gjuels"` + ObservationPayment uint64 `json:"observation_payment_gjuels"` + RecommendedGasPrice string `json:"recommended_gas_price_uluna"` } type ExecuteTransferOwnershipMsg struct { @@ -78,52 +103,15 @@ type ExecuteTransferOwnershipMsgType struct { To msg.AccAddress `json:"to"` } -type ExecuteSetConfigMsg struct { - SetConfig ExecuteSetConfigMsgType `json:"set_config"` -} - -type ExecuteSetConfigMsgType struct { - Signers ByteArrayArray `json:"signers"` - Transmitters []string `json:"transmitters"` - F uint8 `json:"f"` - OnchainConfig ByteArray `json:"onchain_config"` - OffchainConfigVersion uint64 `json:"offchain_config_version"` - OffchainConfig ByteArray `json:"offchain_config"` -} - type ExecuteSetConfig struct { SetConfig SetConfigDetails `json:"set_config"` } type SetConfigDetails struct { - Signers ByteArrayArray `json:"signers"` - Transmitters []string `json:"transmitters"` - F uint8 `json:"f"` - OnchainConfig ByteArray `json:"onchain_config"` - OffchainConfigVersion uint64 `json:"offchain_config_version"` - OffchainConfig ByteArray `json:"offchain_config"` -} - -type ByteArray []byte - -func (b ByteArray) MarshalJSON() ([]byte, error) { - var result string - if b == nil { - result = "null" - } else { - result = strings.Join(strings.Fields(fmt.Sprintf("%d", b)), ",") - } - return []byte(result), nil -} - -type ByteArrayArray [][]byte - -func (b ByteArrayArray) MarshalJSON() ([]byte, error) { - var result string - if b == nil { - result = "null" - } else { - result = strings.Join(strings.Fields(fmt.Sprintf("%d", b)), ",") - } - return []byte(result), nil + Signers [][]byte `json:"signers"` + Transmitters []string `json:"transmitters"` + F uint8 `json:"f"` + OnchainConfig []byte `json:"onchain_config"` + OffchainConfigVersion uint64 `json:"offchain_config_version"` + OffchainConfig []byte `json:"offchain_config"` } diff --git a/tests/e2e/smoke/common/common.go b/tests/e2e/smoke/common/common.go new file mode 100644 index 00000000..c1667604 --- /dev/null +++ b/tests/e2e/smoke/common/common.go @@ -0,0 +1,229 @@ +package common + +import ( + "encoding/json" + "math/big" + "os" + "time" + + . "github.com/onsi/gomega" + "github.com/rs/zerolog/log" + "github.com/smartcontractkit/chainlink-terra/tests/e2e" + "github.com/smartcontractkit/chainlink-terra/tests/e2e/common" + "github.com/smartcontractkit/helmenv/environment" + "github.com/smartcontractkit/helmenv/tools" + "github.com/smartcontractkit/integrations-framework/client" + "github.com/smartcontractkit/integrations-framework/contracts" + "github.com/smartcontractkit/terra.go/msg" +) + +const ( + // ContractsStateFile JSON file to store addresses of already deployed contracts + ContractsStateFile = "contracts-chaos-state.json" + // NewRoundCheckTimeout how long to await a new round + NewRoundCheckTimeout = 3 * time.Minute + // NewRoundCheckPollInterval new round check interval + NewRoundCheckPollInterval = 1 * time.Second + // SourceChangeInterval EA value change interval + SourceChangeInterval = 1250 * time.Millisecond + // ChaosAwaitingApply time to wait for chaos experiment to apply + ChaosAwaitingApply = 60 * time.Second + // ChaosGroupFaulty Group of faulty nodes, even if they fail OCR must work + ChaosGroupFaulty = "chaosGroupFaulty" + // ChaosGroupYellow if nodes from that group fail we may not work while some experiments are going + // but after experiment it must recover + ChaosGroupYellow = "chaosGroupYellow" + // ChaosGroupLeftHalf an equal half of all nodes + ChaosGroupLeftHalf = "chaosGroupLeftHalf" + // ChaosGroupRightHalf an equal half of all nodes + ChaosGroupRightHalf = "chaosGroupRightHalf" + // ChaosGroupOnline a group of nodes that are working + ChaosGroupOnline = "chaosGroupOnline" + // UntilStop some chaos experiments doesn't respect absence of duration and got recovered immediately, so we enforce duration + UntilStop = 666 * time.Hour +) + +// OCRv2State OCR test state +type OCRv2State struct { + Env *environment.Environment + Addresses *ContractsAddresses + MockServer *client.MockserverClient + Nodes []client.Chainlink + Nets *client.Networks + LinkToken *e2e.LinkToken + BAC *e2e.AccessController + RAC *e2e.AccessController + Flags *e2e.OCRv2Flags + OCR2 *e2e.OCRv2 + Validator *e2e.OCRv2Validator + OCR2Proxy *e2e.OCRv2Proxy + ValidatorProxy *e2e.OCRv2Proxy + OCConfig contracts.OffChainAggregatorV2Config + NodeKeysBundle []common.NodeKeysBundle + Transmitters []string + RoundsFound int + LastRoundTime time.Time + Err error +} + +// ContractsAddresses deployed contract addresses +type ContractsAddresses struct { + OCR string `json:"ocr"` + LinkToken string `json:"link"` + BAC string `json:"bac"` + RAC string `json:"rac"` + Flags string `json:"flags"` + Validator string `json:"validator"` +} + +// DeployCluster deploys OCR cluster with or without contracts +func (m *OCRv2State) DeployCluster(nodes int, stateful bool) { + m.DeployEnv(nodes, stateful) + m.SetupClients() + if m.Nets.Default.ContractsDeployed() { + err := m.LoadContracts() + Expect(err).ShouldNot(HaveOccurred()) + return + } + m.DeployContracts() + err := m.DumpContracts() + Expect(err).ShouldNot(HaveOccurred()) + m.CreateJobs() +} + +// DeployEnv deploys and connects OCR environment +func (m *OCRv2State) DeployEnv(nodes int, stateful bool) { + m.Env, m.Err = environment.DeployOrLoadEnvironment( + e2e.NewChainlinkTerraEnv(nodes, stateful), + tools.ChartsRoot, + ) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = m.Env.ConnectAll() + Expect(m.Err).ShouldNot(HaveOccurred()) +} + +// SetupClients setting up clients +func (m *OCRv2State) SetupClients() { + networkRegistry := client.NewNetworkRegistry() + networkRegistry.RegisterNetwork( + "terra", + e2e.ClientInitFunc(), + e2e.ClientURLSFunc(), + ) + m.Nets, m.Err = networkRegistry.GetNetworks(m.Env) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.MockServer, m.Err = client.ConnectMockServer(m.Env) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Nodes, m.Err = client.ConnectChainlinkNodes(m.Env) + Expect(m.Err).ShouldNot(HaveOccurred()) +} + +// DeployContracts deploys contracts +func (m *OCRv2State) DeployContracts() { + m.OCConfig, m.NodeKeysBundle, m.Err = common.DefaultOffChainConfigParamsFromNodes(m.Nodes) + Expect(m.Err).ShouldNot(HaveOccurred()) + cd := e2e.NewTerraContractDeployer(m.Nets.Default) + Expect(m.Err).ShouldNot(HaveOccurred()) + + m.LinkToken, m.Err = cd.DeployLinkTokenContract() + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = common.FundOracles(m.Nets.Default, m.NodeKeysBundle, big.NewFloat(5e12)) + Expect(m.Err).ShouldNot(HaveOccurred()) + + m.BAC, m.Err = cd.DeployOCRv2AccessController() + Expect(m.Err).ShouldNot(HaveOccurred()) + m.RAC, m.Err = cd.DeployOCRv2AccessController() + Expect(m.Err).ShouldNot(HaveOccurred()) + m.OCR2, m.Err = cd.DeployOCRv2(m.BAC.Address(), m.RAC.Address(), m.LinkToken.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Flags, m.Err = cd.DeployOCRv2Flags(m.BAC.Address(), m.RAC.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Validator, m.Err = cd.DeployOCRv2Validator(uint32(80000), m.Flags.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + + m.Err = m.OCR2.SetBilling(uint64(2e5), uint64(1), uint64(1), "1", m.BAC.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Transmitters, m.Err = m.OCR2.SetOffChainConfig(m.OCConfig) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = m.OCR2.SetPayees(m.Transmitters) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = m.OCR2.SetValidatorConfig(uint64(2e18), m.Validator.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.OCR2Proxy, m.Err = cd.DeployOCRv2Proxy(m.OCR2.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.ValidatorProxy, m.Err = cd.DeployOCRv2Proxy(m.Validator.Address()) + Expect(m.Err).ShouldNot(HaveOccurred()) +} + +// CreateJobs creating OCR jobs and EA stubs +func (m *OCRv2State) CreateJobs() { + m.Err = m.MockServer.SetValuePath("/variable", 5) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = m.MockServer.SetValuePath("/juels", 1) + Expect(m.Err).ShouldNot(HaveOccurred()) + m.Err = common.CreateJobs(m.OCR2.Address(), m.Nodes, m.NodeKeysBundle, m.MockServer) + Expect(m.Err).ShouldNot(HaveOccurred()) +} + +// LoadContracts loads contracts if they are already deployed +func (m *OCRv2State) LoadContracts() error { + d, err := os.ReadFile(ContractsStateFile) + if err != nil { + return err + } + var contractsState *ContractsAddresses + if err = json.Unmarshal(d, &contractsState); err != nil { + return err + } + accAddr, err := msg.AccAddressFromBech32(contractsState.OCR) + if err != nil { + return err + } + m.OCR2 = &e2e.OCRv2{ + Client: m.Nets.Default.(*e2e.TerraLCDClient), + Addr: accAddr, + } + return nil +} + +// DumpContracts dumps contracts to a file +func (m *OCRv2State) DumpContracts() error { + s := &ContractsAddresses{OCR: m.OCR2.Address()} + d, err := json.Marshal(s) + if err != nil { + return err + } + return os.WriteFile(ContractsStateFile, d, os.ModePerm) +} + +// ValidateNoRoundsAfter validate to rounds are present after some point in time +func (m *OCRv2State) ValidateNoRoundsAfter(chaosStartTime time.Time) { + m.RoundsFound = 0 + m.LastRoundTime = chaosStartTime + Consistently(func(g Gomega) { + _, timestamp, _, err := m.OCR2.GetLatestRoundData() + g.Expect(err).ShouldNot(HaveOccurred()) + roundTime := time.Unix(int64(timestamp), 0) + g.Expect(roundTime.Before(m.LastRoundTime)).Should(BeTrue()) + }, NewRoundCheckTimeout, NewRoundCheckPollInterval).Should(Succeed()) +} + +// ValidateRoundsAfter validates there are new rounds after some point in time +func (m *OCRv2State) ValidateRoundsAfter(chaosStartTime time.Time, rounds int) { + m.RoundsFound = 0 + m.LastRoundTime = chaosStartTime + Eventually(func(g Gomega) { + answer, timestamp, _, err := m.OCR2.GetLatestRoundData() + g.Expect(err).ShouldNot(HaveOccurred()) + roundTime := time.Unix(int64(timestamp), 0) + g.Expect(roundTime.After(m.LastRoundTime)).Should(BeTrue()) + m.RoundsFound++ + m.LastRoundTime = roundTime + log.Debug(). + Int("Rounds", m.RoundsFound). + Interface("Answer", answer). + Time("Time", roundTime). + Msg("OCR Round") + g.Expect(m.RoundsFound).Should(Equal(rounds)) + }, NewRoundCheckTimeout, NewRoundCheckPollInterval).Should(Succeed()) +} diff --git a/tests/e2e/smoke/common/experiments.go b/tests/e2e/smoke/common/experiments.go new file mode 100644 index 00000000..de772f94 --- /dev/null +++ b/tests/e2e/smoke/common/experiments.go @@ -0,0 +1,189 @@ +package common + +import ( + "fmt" + "time" + + . "github.com/onsi/gomega" + "github.com/smartcontractkit/helmenv/chaos/experiments" +) + +// LabelChaosGroups sets labels for chaos groups +func (m *OCRv2State) LabelChaosGroups() { + m.LabelChaosGroup(0, 1, ChaosGroupFaulty) + m.LabelChaosGroup(2, 4, ChaosGroupOnline) + m.LabelChaosGroup(0, 2, ChaosGroupYellow) + m.LabelChaosGroup(0, 2, ChaosGroupLeftHalf) + m.LabelChaosGroup(3, 4, ChaosGroupRightHalf) +} + +// LabelChaosGroup sets label for a chaos group +func (m *OCRv2State) LabelChaosGroup(startInstance int, endInstance int, group string) { + for i := startInstance; i <= endInstance; i++ { + m.Err = m.Env.AddLabel(fmt.Sprintf("instance=%d,app=chainlink-node", i), fmt.Sprintf("%s=1", group)) + Expect(m.Err).ShouldNot(HaveOccurred()) + } +} + +func (m *OCRv2State) CanRecoverAllNodesValidatorConnectionLoss() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkPartition{ + FromMode: "all", + FromLabelKey: ChaosGroupOnline, + FromLabelValue: "1", + ToMode: "all", + ToLabelKey: "app", + ToLabelValue: "fcd-api", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + err = m.Env.ClearAllChaosExperiments() + Expect(err).ShouldNot(HaveOccurred()) + m.ValidateRoundsAfter(time.Now(), 10) +} + +func (m *OCRv2State) CanWorkYellowGroupNoValidatorConnection() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkPartition{ + FromMode: "all", + FromLabelKey: ChaosGroupYellow, + FromLabelValue: "1", + ToMode: "all", + ToLabelKey: "app", + ToLabelValue: "fcd-api", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateRoundsAfter(time.Now(), 10) +} + +func (m *OCRv2State) CantWorkWithFaultyNodesFailed() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.PodFailure{ + Mode: "all", + LabelKey: ChaosGroupYellow, + LabelValue: "1", + Duration: UntilStop, + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateNoRoundsAfter(time.Now()) +} + +func (m *OCRv2State) CanWorkWithFaultyNodesOffline() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkPartition{ + FromMode: "all", + FromLabelKey: ChaosGroupFaulty, + FromLabelValue: "1", + ToMode: "all", + ToLabelKey: ChaosGroupOnline, + ToLabelValue: "1", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateRoundsAfter(time.Now(), 10) +} + +func (m *OCRv2State) CantWorkWithMoreThanFaultyNodesOffline() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkLoss{ + Mode: "all", + LabelKey: ChaosGroupYellow, + Loss: 100, + Correlation: 100, + LabelValue: "1", + Duration: UntilStop, + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateRoundsAfter(time.Now(), 10) +} + +func (m *OCRv2State) CantWorkWithMoreThanFaultyNodesSplit() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkPartition{ + FromMode: "all", + FromLabelKey: ChaosGroupYellow, + FromLabelValue: "1", + ToMode: "all", + ToLabelKey: ChaosGroupOnline, + ToLabelValue: "1", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateNoRoundsAfter(time.Now()) +} + +func (m *OCRv2State) NetworkCorrupt(group string, corrupt int, rounds int) { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkCorrupt{ + Mode: "all", + LabelKey: group, + LabelValue: "1", + Corrupt: corrupt, + Correlation: 100, + Duration: UntilStop, + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateRoundsAfter(time.Now(), rounds) +} + +func (m *OCRv2State) CanWorkAfterAllNodesRestarted() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.ContainerKill{ + Mode: "all", + LabelKey: "app", + LabelValue: "chainlink-node", + Container: "node", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateRoundsAfter(time.Now(), 10) +} + +func (m *OCRv2State) RestoredAfterNetworkSplit() { + // nolint + defer m.Env.ClearAllChaosExperiments() + _, err := m.Env.ApplyChaosExperiment( + &experiments.NetworkPartition{ + FromMode: "all", + FromLabelKey: ChaosGroupLeftHalf, + FromLabelValue: "1", + ToMode: "all", + ToLabelKey: ChaosGroupRightHalf, + ToLabelValue: "1", + }, + ) + Expect(err).ShouldNot(HaveOccurred()) + time.Sleep(ChaosAwaitingApply) + m.ValidateNoRoundsAfter(time.Now()) + err = m.Env.ClearAllChaosExperiments() + Expect(err).ShouldNot(HaveOccurred()) + m.ValidateRoundsAfter(time.Now(), 10) +} diff --git a/tests/e2e/smoke/ocr2_test.go b/tests/e2e/smoke/ocr2_test.go index 7eea5788..2f05e8bf 100644 --- a/tests/e2e/smoke/ocr2_test.go +++ b/tests/e2e/smoke/ocr2_test.go @@ -1,117 +1,34 @@ package smoke_test import ( - "math/big" - "time" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/rs/zerolog/log" - "github.com/smartcontractkit/chainlink-terra/tests/e2e" "github.com/smartcontractkit/chainlink-terra/tests/e2e/common" - "github.com/smartcontractkit/helmenv/environment" - "github.com/smartcontractkit/helmenv/tools" + tc "github.com/smartcontractkit/chainlink-terra/tests/e2e/smoke/common" "github.com/smartcontractkit/integrations-framework/actions" - "github.com/smartcontractkit/integrations-framework/client" - "github.com/smartcontractkit/integrations-framework/contracts" + "time" ) var _ = Describe("Terra OCRv2", func() { - var ( - e *environment.Environment - mockServer *client.MockserverClient - nodes []client.Chainlink - nets *client.Networks - lt *e2e.LinkToken - bac *e2e.AccessController - rac *e2e.AccessController - flags *e2e.OCRv2Flags - ocr2 *e2e.OCRv2 - validator *e2e.OCRv2Validator - ocConfig contracts.OffChainAggregatorV2Config - nkb []common.NodeKeysBundle - err error - ) + var state *tc.OCRv2State BeforeEach(func() { - By("Deploying the environment", func() { - e, err = environment.DeployOrLoadEnvironment( - e2e.NewChainlinkTerraEnv(), - tools.ChartsRoot, - ) - Expect(err).ShouldNot(HaveOccurred()) - err = e.ConnectAll() - Expect(err).ShouldNot(HaveOccurred()) - }) - By("Setting up client", func() { - networkRegistry := client.NewNetworkRegistry() - networkRegistry.RegisterNetwork( - "terra", - e2e.ClientInitFunc(), - e2e.ClientURLSFunc(), - ) - nets, err = networkRegistry.GetNetworks(e) - Expect(err).ShouldNot(HaveOccurred()) - mockServer, err = client.ConnectMockServer(e) - Expect(err).ShouldNot(HaveOccurred()) - nodes, err = client.ConnectChainlinkNodes(e) - Expect(err).ShouldNot(HaveOccurred()) - }) - By("Deploying contracts", func() { - ocConfig, nkb, err = common.DefaultOffChainConfigParamsFromNodes(nodes) - Expect(err).ShouldNot(HaveOccurred()) - cd := e2e.NewTerraContractDeployer(nets.Default) - Expect(err).ShouldNot(HaveOccurred()) - - lt, err = cd.DeployLinkTokenContract() - Expect(err).ShouldNot(HaveOccurred()) - err = common.FundOracles(nets.Default, nkb, big.NewFloat(5e12)) - Expect(err).ShouldNot(HaveOccurred()) - - bac, err = cd.DeployOCRv2AccessController() - Expect(err).ShouldNot(HaveOccurred()) - rac, err = cd.DeployOCRv2AccessController() - Expect(err).ShouldNot(HaveOccurred()) - ocr2, err = cd.DeployOCRv2(bac.Address(), rac.Address(), lt.Address()) - Expect(err).ShouldNot(HaveOccurred()) - flags, err = cd.DeployOCRv2Flags(bac.Address(), rac.Address()) - Expect(err).ShouldNot(HaveOccurred()) - validator, err = cd.DeployOCRv2Validator(uint32(80000), flags.Address()) - Expect(err).ShouldNot(HaveOccurred()) - log.Debug().Str("Addr", validator.Address()).Msg("Validator address") - - err = ocr2.SetBilling(uint32(1), uint32(1), bac.Address()) - Expect(err).ShouldNot(HaveOccurred()) - err = ocr2.SetOffChainConfig(ocConfig) - Expect(err).ShouldNot(HaveOccurred()) - }) - By("Creating jobs", func() { - err = mockServer.SetValuePath("/variable", 5) - Expect(err).ShouldNot(HaveOccurred()) - err = mockServer.SetValuePath("/juels", 1) - Expect(err).ShouldNot(HaveOccurred()) - common.ImitateSource(mockServer, 1*time.Second, 2, 10) - err = common.CreateJobs(ocr2.Address(), nodes, nkb, mockServer) - Expect(err).ShouldNot(HaveOccurred()) + state = &tc.OCRv2State{} + By("Deoloying the cluster", func() { + state.DeployCluster(5, false) + common.ImitateSource(state.MockServer, 1*time.Second, 2, 10) }) }) Describe("with Terra OCR2", func() { It("performs OCR2 round", func() { - Eventually(func(g Gomega) { - answer, _, _, err := ocr2.GetLatestRoundData() - g.Expect(err).ShouldNot(HaveOccurred()) - log.Debug(). - Interface("Answer", answer). - Msg("OCR Round") - g.Expect(answer).Should(Equal(uint64(10))) - }, 3*time.Minute, 1*time.Second).Should(Succeed()) + state.ValidateRoundsAfter(time.Now(), 10) }) }) AfterEach(func() { By("Tearing down the environment", func() { - err = actions.TeardownSuite(e, nil, "logs") + err := actions.TeardownSuite(state.Env, nil, "logs") Expect(err).ShouldNot(HaveOccurred()) }) })