From b462015d2062fb0c2d2a5961f609b6671862528b Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Wed, 14 Feb 2024 15:42:13 +0100 Subject: [PATCH 01/39] Adding docker support --- .gitignore | 1 + integration-tests/.root_dir | 0 integration-tests/common/common.go | 324 +++++++++--------- integration-tests/common/gauntlet_common.go | 68 ++-- integration-tests/common/test_common.go | 270 ++++++++------- integration-tests/docker/test_env/stark.go | 105 ++++++ integration-tests/smoke/ocr2_new_test.go | 69 ++++ integration-tests/smoke/ocr2_test.go | 119 +++---- integration-tests/testconfig/default.toml | 22 ++ .../testconfig/ocr2/example.toml | 96 ++++++ integration-tests/testconfig/ocr2/ocr2.go | 136 ++++++++ integration-tests/testconfig/ocr2/ocr2.toml | 0 ops/gauntlet/gauntlet_starknet.go | 4 +- 13 files changed, 843 insertions(+), 371 deletions(-) create mode 100644 integration-tests/.root_dir create mode 100644 integration-tests/docker/test_env/stark.go create mode 100644 integration-tests/smoke/ocr2_new_test.go create mode 100644 integration-tests/testconfig/default.toml create mode 100644 integration-tests/testconfig/ocr2/example.toml create mode 100644 integration-tests/testconfig/ocr2/ocr2.go create mode 100644 integration-tests/testconfig/ocr2/ocr2.toml diff --git a/.gitignore b/.gitignore index ffd70d468..b917665d3 100644 --- a/.gitignore +++ b/.gitignore @@ -166,3 +166,4 @@ ztarrepo.tar.gz eslint-report.json .run.id .local-mock-server +overrides.toml diff --git a/integration-tests/.root_dir b/integration-tests/.root_dir new file mode 100644 index 000000000..e69de29bb diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index e7d4ac85d..b36225e96 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -1,9 +1,15 @@ package common import ( - "bytes" "fmt" - "io" + test_env_starknet "github.com/smartcontractkit/chainlink-starknet/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink-starknet/ops/devnet" + "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" + mock_adapter "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mock-adapter" + "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "os" "os/exec" "regexp" @@ -18,20 +24,24 @@ import ( "github.com/stretchr/testify/require" "gopkg.in/guregu/null.v4" + common_cfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink/integration-tests/client" + "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" + cl "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" "github.com/smartcontractkit/chainlink/v2/core/services/job" "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) var ( - serviceKeyL1 = "Hardhat" - serviceKeyL2 = "chainlink-starknet.starknet-devnet" - serviceKeyChainlink = "chainlink" chainName = "starknet" chainId = "SN_GOERLI" - defaultNodeUrl = "http://127.0.0.1:5050" + defaultNodeUrl = "http://starknet:5050" + testnetUrl = "https://starknet-testnet.public.blastapi.io" + DefaultNodeCount = 5 + DefaultTTL = "3h" + DefaultTestDuration = 50 * time.Minute ) type Common struct { @@ -51,6 +61,98 @@ type Common struct { Account string ChainlinkConfig string Env *environment.Environment + IsK8s bool + K8Config *environment.Config + NodeOpts []test_env.ClNodeOption + DockerEnv *StarknetClusterTestEnv +} + +type StarknetClusterTestEnv struct { + *test_env.CLClusterTestEnv + Starknet *test_env_starknet.Starknet + Killgrave *ctf_test_env.Killgrave +} + +func New(env string, isK8s bool) *Common { + var err error + var c *Common + if env == "testnet" { + c = &Common{ + IsK8s: isK8s, + ChainName: chainId, + ChainId: chainId, + L2RPCUrl: testnetUrl, + Testnet: true, + TestDuration: DefaultTestDuration, + } + } else { + c = &Common{ + IsK8s: isK8s, + ChainName: chainName, + ChainId: chainId, + L2RPCUrl: defaultNodeUrl, + TestDuration: DefaultTestDuration, + } + } + // Checking if count of OCR nodes is defined in ENV + nodeCountSet, nodeCountDefined := os.LookupEnv("NODE_COUNT") + if nodeCountDefined && nodeCountSet != "" { + c.NodeCount, err = strconv.Atoi(nodeCountSet) + if err != nil { + panic(fmt.Sprintf("Please define a proper node count for the test: %v", err)) + } + } else { + c.NodeCount = DefaultNodeCount + } + + // Checking if TTL env var is set in ENV + ttlValue, ttlDefined := os.LookupEnv("TTL") + if ttlDefined && ttlValue != "" { + duration, err := time.ParseDuration(ttlValue) + if err != nil { + panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) + } + c.TTL, err = time.ParseDuration(*alias.ShortDur(duration)) + if err != nil { + panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) + } + } else { + duration, err := time.ParseDuration(DefaultTTL) + if err != nil { + panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) + } + c.TTL, err = time.ParseDuration(*alias.ShortDur(duration)) + if err != nil { + panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) + } + } + + return c +} + +func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) { + c.K8Config = &environment.Config{ + NamespacePrefix: fmt.Sprintf("solana-%s", namespacePrefix), + TTL: c.TTL, + Test: t, + } + + if c.IsK8s { + toml := c.DefaultNodeConfig() + tomlString, err := toml.TOMLString() + if err != nil { + return nil, err + } + c.Env = environment.New(c.K8Config). + AddHelm(devnet.New(nil)). + AddHelm(mock_adapter.New(nil)). + AddHelm(chainlink.New(0, map[string]interface{}{ + "toml": tomlString, + "replicas": c.NodeCount, + })) + } + + return c, nil } // getEnv gets the environment variable if it exists and sets it for the remote runner @@ -107,129 +209,30 @@ func getTestDuration() time.Duration { return t } -func New(t *testing.T) *Common { - c := &Common{ - ChainName: chainName, - ChainId: chainId, - NodeCount: getNodeCount(), - TTL: getTTL(), - TestDuration: getTestDuration(), - ServiceKeyChainlink: serviceKeyChainlink, - ServiceKeyL1: serviceKeyL1, - ServiceKeyL2: serviceKeyL2, - L2RPCUrl: getEnv("L2_RPC_URL"), // Fetch L2 RPC url if defined - MockUrl: "http://host.containers.internal:6060", - PrivateKey: getEnv("PRIVATE_KEY"), - Account: getEnv("ACCOUNT"), - // P2PPort: "6690", - } - c.Testnet = c.L2RPCUrl != "" - - // TODO: HAXX: we force the URL to a local docker container - c.L2RPCUrl = defaultNodeUrl - - starknetUrl := fmt.Sprintf("http://%s:%d/rpc", serviceKeyL2, 5050) - if c.Testnet { - starknetUrl = c.L2RPCUrl - } - - chainlinkConfig := fmt.Sprintf(`[[Starknet]] -Enabled = true -ChainID = '%s' -[[Starknet.Nodes]] -Name = 'primary' -URL = '%s' - -[OCR2] -Enabled = true - -[P2P] -[P2P.V2] -Enabled = true -DeltaDial = '5s' -DeltaReconcile = '5s' -ListenAddresses = ['0.0.0.0:6690'] - -[WebServer] -HTTPPort = 6688 -[WebServer.TLS] -HTTPSPort = 0 -`, c.ChainId, starknetUrl) - - c.ChainlinkConfig = chainlinkConfig - log.Debug().Str("toml", chainlinkConfig).Msg("Created chainlink config") - - c.Env = &environment.Environment{} - return c -} - -// CapturingPassThroughWriter is a writer that remembers -// data written to it and passes it to w -type CapturingPassThroughWriter struct { - buf bytes.Buffer - w io.Writer -} - -// NewCapturingPassThroughWriter creates new CapturingPassThroughWriter -func NewCapturingPassThroughWriter(w io.Writer) *CapturingPassThroughWriter { - return &CapturingPassThroughWriter{ - w: w, - } -} - -func (w *CapturingPassThroughWriter) Write(d []byte) (int, error) { - w.buf.Write(d) - return w.w.Write(d) -} - -// Bytes returns bytes written to the writer -func (w *CapturingPassThroughWriter) Bytes() []byte { - return w.buf.Bytes() -} - -func debug(cmd *exec.Cmd) error { - stdout, err := cmd.StdoutPipe() - if err != nil { - panic(err) - } - stderr, err := cmd.StderrPipe() - if err != nil { - panic(err) - } - - if startErr := cmd.Start(); startErr != nil { - panic(startErr) - } - - doneStdOut := make(chan any) - doneStdErr := make(chan any) - osstdout := NewCapturingPassThroughWriter(os.Stdout) - osstderr := NewCapturingPassThroughWriter(os.Stderr) - go handleOutput(osstdout, stdout, doneStdOut) - go handleOutput(osstderr, stderr, doneStdErr) - - err = cmd.Wait() - - errStdOut := <-doneStdOut - if errStdOut != nil { - fmt.Println("error writing to standard out") - } - - errStdErr := <-doneStdErr - if errStdErr != nil { - fmt.Println("error writing to standard in") +func (c *Common) DefaultNodeConfig() *cl.Config { + starkConfig := config.TOMLConfig{ + Enabled: ptr.Ptr(true), + ChainID: ptr.Ptr(c.ChainId), + Nodes: []*config.Node{ + { + Name: ptr.Ptr("primary"), + URL: common_cfg.MustParseURL(defaultNodeUrl), + }, + }, } - - if err != nil { - fmt.Printf("Command finished with error: %v\n", err) + baseConfig := node.NewBaseConfig() + baseConfig.Starknet = config.TOMLConfigs{ + &starkConfig, } + baseConfig.OCR2.Enabled = ptr.Ptr(true) + baseConfig.P2P.V2.Enabled = ptr.Ptr(true) + fiveSecondDuration := common_cfg.MustNewDuration(5 * time.Second) - return err -} + baseConfig.P2P.V2.DeltaDial = fiveSecondDuration + baseConfig.P2P.V2.DeltaReconcile = fiveSecondDuration + baseConfig.P2P.V2.ListenAddresses = &[]string{"0.0.0.0:6690"} -func handleOutput(dst io.Writer, src io.Reader, done chan<- any) { - _, err := io.Copy(dst, src) - done <- err + return baseConfig } func (c *Common) SetLocalEnvironment(t *testing.T) { @@ -247,8 +250,9 @@ func (c *Common) SetLocalEnvironment(t *testing.T) { log.Info().Msg("Starting core nodes...") cmd := exec.Command("../../scripts/core.sh") cmd.Env = append(os.Environ(), fmt.Sprintf("CL_CONFIG=%s", c.ChainlinkConfig)) - // easy debug - err = debug(cmd) + // out, err := cmd.Output() + // fmt.Println(string(out)) + err = cmd.Run() require.NoError(t, err, "Could not start core nodes") log.Info().Msg("Set up local stack complete.") @@ -316,40 +320,35 @@ func parseHostname(s string) string { return r.FindStringSubmatch(s)[1] } -// CreateKeys Creates node keys and defines chain and nodes for each node -func (c *Common) CreateKeys(env *environment.Environment) ([]client.NodeKeysBundle, []*client.ChainlinkClient, error) { - nodes, err := connectChainlinkNodes(env) - if err != nil { - return nil, nil, err - } +func (c *Common) CreateNodeKeysBundle(nodes []*client.ChainlinkClient) ([]client.NodeKeysBundle, error) { + nkb := make([]client.NodeKeysBundle, 0) + for _, n := range nodes { + p2pkeys, err := n.MustReadP2PKeys() + if err != nil { + return nil, err + } - NKeys, _, err := client.CreateNodeKeysBundle(nodes, c.ChainName, c.ChainId) - if err != nil { - return nil, nil, err + peerID := p2pkeys.Data[0].Attributes.PeerID + txKey, _, err := n.CreateTxKey(chainName, c.ChainId) + if err != nil { + return nil, err + } + ocrKey, _, err := n.CreateOCR2Key(chainName) + if err != nil { + return nil, err + } + + nkb = append(nkb, client.NodeKeysBundle{ + PeerID: peerID, + OCR2Key: *ocrKey, + TXKey: *txKey, + }) } - // for _, n := range nodes { - // _, _, err = n.CreateStarkNetChain(&client.StarkNetChainAttributes{ - // Type: c.ChainName, - // ChainID: c.ChainId, - // Config: client.StarkNetChainConfig{}, - // }) - // if err != nil { - // return nil, nil, err - // } - // _, _, err = n.CreateStarkNetNode(&client.StarkNetNodeAttributes{ - // Name: c.ChainName, - // ChainID: c.ChainId, - // Url: "http://", // TODO: - // }) - // if err != nil { - // return nil, nil, err - // } - // } - return NKeys, nodes, nil + return nkb, nil } // CreateJobsForContract Creates and sets up the boostrap jobs as well as OCR jobs -func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, observationSource string, juelsPerFeeCoinSource string, ocrControllerAddress string, accountAddresses []string) error { +func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test_env.Killgrave, observationSource string, juelsPerFeeCoinSource string, ocrControllerAddress string, accountAddresses []string) error { // Define node[0] as bootstrap node cc.bootstrapPeers = []client.P2PData{ { @@ -362,8 +361,8 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse // Defining relay config bootstrapRelayConfig := job.JSONConfig{ "nodeName": fmt.Sprintf("starknet-OCRv2-%s-%s", "node", uuid.New().String()), - "accountAddress": accountAddresses[0], - "chainID": c.ChainId, + "accountAddress": fmt.Sprintf("%s", accountAddresses[0]), + "chainID": fmt.Sprintf("%s", c.ChainId), } oracleSpec := job.OCR2OracleSpec{ @@ -391,9 +390,8 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse } sourceValueBridge := &client.BridgeTypeAttributes{ - Name: "mockserver-bridge", - URL: fmt.Sprintf("%s/%s", mockUrl, "five"), - RequestData: "{}", + Name: "mockserver-bridge", + URL: fmt.Sprintf("%s/%s", mockserver.InternalEndpoint, strings.TrimPrefix("mockserver-bridge", "/")), } // Setting up job specs @@ -407,7 +405,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse } relayConfig := job.JSONConfig{ "nodeName": bootstrapRelayConfig["nodeName"], - "accountAddress": accountAddresses[nIdx], + "accountAddress": fmt.Sprintf("%s", accountAddresses[nIdx]), "chainID": bootstrapRelayConfig["chainID"], } @@ -431,7 +429,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockUrl string, obse OCR2OracleSpec: oracleSpec, ObservationSource: observationSource, } - _, _, err = n.CreateJob(jobSpec) + _, err = n.MustCreateJob(jobSpec) if err != nil { return err } diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index 606c7741a..f4556b77c 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -4,9 +4,8 @@ import ( "encoding/json" "errors" "fmt" - "os" - "github.com/smartcontractkit/chainlink-starknet/integration-tests/utils" + "os" ) var ( @@ -14,15 +13,15 @@ var ( nAccount string ) -func (testState *Test) fundNodes() ([]string, error) { - l := utils.GetTestLogger(testState.T) +func (m *OCRv2TestState) fundNodes() ([]string, error) { + l := utils.GetTestLogger(m.T) var nAccounts []string var err error - for _, key := range testState.GetNodeKeys() { + for _, key := range m.GetNodeKeys() { if key.TXKey.Data.Attributes.StarkKey == "" { return nil, errors.New("stark key can't be empty") } - nAccount, err = testState.Sg.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) + nAccount, err = m.Sg.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) if err != nil { return nil, err } @@ -33,54 +32,61 @@ func (testState *Test) fundNodes() ([]string, error) { return nil, err } - if testState.Common.Testnet { + if m.Common.Testnet { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = testState.Sg.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node + _, err = m.Sg.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node if err != nil { return nil, err } } } else { - err = testState.Devnet.FundAccounts(nAccounts) - if err != nil { - return nil, err + // The starknet provided mint method does not work so we send a req directly + for _, key := range nAccounts { + res, err := m.Resty.R().SetBody(map[string]any{ + "address": key, + "amount": 900000000000000000, + }).Post("/mint") + m.L.Info().Msg(fmt.Sprintf("Funding account: %s", string(res.Body()))) + if err != nil { + return nil, err + } } } return nAccounts, nil } -func (testState *Test) deployLinkToken() error { +func (m *OCRv2TestState) deployLinkToken() error { var err error - testState.LinkTokenAddr, err = testState.Sg.DeployLinkTokenContract() + m.LinkTokenAddr, err = m.Sg.DeployLinkTokenContract() if err != nil { return err } - err = os.Setenv("LINK", testState.LinkTokenAddr) + err = os.Setenv("LINK", m.LinkTokenAddr) if err != nil { return err } return nil } -func (testState *Test) deployAccessController() error { +func (m *OCRv2TestState) deployAccessController() error { var err error - testState.AccessControllerAddr, err = testState.Sg.DeployAccessControllerContract() + m.AccessControllerAddr, err = m.Sg.DeployAccessControllerContract() if err != nil { return err } - err = os.Setenv("BILLING_ACCESS_CONTROLLER", testState.AccessControllerAddr) + err = os.Setenv("BILLING_ACCESS_CONTROLLER", m.AccessControllerAddr) if err != nil { return err } return nil } -func (testState *Test) setConfigDetails(ocrAddress string) error { - cfg, err := testState.LoadOCR2Config() +func (m *OCRv2TestState) setConfigDetails(ocrAddress string) error { + cfg, err := m.LoadOCR2Config() if err != nil { return err } @@ -89,54 +95,54 @@ func (testState *Test) setConfigDetails(ocrAddress string) error { if err != nil { return err } - _, err = testState.Sg.SetConfigDetails(string(parsedConfig), ocrAddress) + _, err = m.Sg.SetConfigDetails(string(parsedConfig), ocrAddress) return err } -func (testState *Test) DeployGauntlet(minSubmissionValue int64, maxSubmissionValue int64, decimals int, name string, observationPaymentGjuels int64, transmissionPaymentGjuels int64) error { - err := testState.Sg.InstallDependencies() +func (m *OCRv2TestState) DeployGauntlet(minSubmissionValue int64, maxSubmissionValue int64, decimals int, name string, observationPaymentGjuels int64, transmissionPaymentGjuels int64) error { + err := m.Sg.InstallDependencies() if err != nil { return err } - testState.AccountAddresses, err = testState.fundNodes() + m.AccountAddresses, err = m.fundNodes() if err != nil { return err } - err = testState.deployLinkToken() + err = m.deployLinkToken() if err != nil { return err } - err = testState.deployAccessController() + err = m.deployAccessController() if err != nil { return err } - testState.OCRAddr, err = testState.Sg.DeployOCR2ControllerContract(minSubmissionValue, maxSubmissionValue, decimals, name, testState.LinkTokenAddr) + m.OCRAddr, err = m.Sg.DeployOCR2ControllerContract(minSubmissionValue, maxSubmissionValue, decimals, name, m.LinkTokenAddr) if err != nil { return err } - testState.ProxyAddr, err = testState.Sg.DeployOCR2ProxyContract(testState.OCRAddr) + m.ProxyAddr, err = m.Sg.DeployOCR2ProxyContract(m.OCRAddr) if err != nil { return err } - _, err = testState.Sg.AddAccess(testState.OCRAddr, testState.ProxyAddr) + _, err = m.Sg.AddAccess(m.OCRAddr, m.ProxyAddr) if err != nil { return err } - _, err = testState.Sg.MintLinkToken(testState.LinkTokenAddr, testState.OCRAddr, "100000000000000000000") + _, err = m.Sg.MintLinkToken(m.LinkTokenAddr, m.OCRAddr, "100000000000000000000") if err != nil { return err } - _, err = testState.Sg.SetOCRBilling(observationPaymentGjuels, transmissionPaymentGjuels, testState.OCRAddr) + _, err = m.Sg.SetOCRBilling(observationPaymentGjuels, transmissionPaymentGjuels, m.OCRAddr) if err != nil { return err } - err = testState.setConfigDetails(testState.OCRAddr) + err = m.setConfigDetails(m.OCRAddr) return err } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 81cced561..224c07f8e 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -3,27 +3,33 @@ package common import ( "context" "fmt" + starknetdevnet "github.com/NethermindEth/starknet.go/devnet" + "github.com/go-resty/resty/v2" + "github.com/smartcontractkit/chainlink-common/pkg/logger" + test_env_ctf "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + "net/http" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + test_env_starknet "github.com/smartcontractkit/chainlink-starknet/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "math/big" - "os" "testing" "time" "github.com/NethermindEth/juno/core/felt" - starknetdevnet "github.com/NethermindEth/starknet.go/devnet" starknetutils "github.com/NethermindEth/starknet.go/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/starknet" "github.com/smartcontractkit/chainlink-starknet/ops" - "github.com/smartcontractkit/chainlink-starknet/ops/devnet" "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" "github.com/smartcontractkit/chainlink/integration-tests/client" - - "github.com/smartcontractkit/chainlink-starknet/integration-tests/utils" ) var ( @@ -31,10 +37,14 @@ var ( dumpPath = "/dumps/dump.pkl" ) -type Test struct { - Devnet *devnet.StarknetDevnetClient +type OCRv2TestState struct { + Account string + PrivateKey string + StarknetClient *starknet.Client + DevnetClient *starknetdevnet.DevNet + Killgrave *test_env_ctf.Killgrave + ChainlinkNodesK8s []*client.ChainlinkK8sClient Cc *ChainlinkClient - Starknet *starknet.Client OCR2Client *ocr2.Client Sg *gauntlet.StarknetGauntlet L1RPCUrl string @@ -47,6 +57,10 @@ type Test struct { ObservationSource string JuelsPerFeeCoinSource string T *testing.T + L zerolog.Logger + TestConfig *testconfig.TestConfig + Resty *resty.Client + err error } type ChainlinkClient struct { @@ -56,82 +70,113 @@ type ChainlinkClient struct { bootstrapPeers []client.P2PData } -// DeployCluster Deploys and sets up config of the environment and nodes -func (testState *Test) DeployCluster() { - lggr := logger.Nop() - testState.Cc = &ChainlinkClient{} - testState.ObservationSource = testState.GetDefaultObservationSource() - testState.JuelsPerFeeCoinSource = testState.GetDefaultJuelsPerFeeCoinSource() - testState.DeployEnv() - if testState.Common.Env.WillUseRemoteRunner() { - return // short circuit here if using a remote runner +func NewOCRv2State(t *testing.T, env string, isK8s bool, namespacePrefix string, testConfig *testconfig.TestConfig) (*OCRv2TestState, error) { + c, err := New(env, isK8s).Default(t, namespacePrefix) + if err != nil { + return nil, err } - testState.SetupClients() - if testState.Common.Testnet { - testState.Common.Env.URLs[testState.Common.ServiceKeyL2][1] = testState.Common.L2RPCUrl + state := &OCRv2TestState{ + Common: c, + T: t, + L: log.Logger, + TestConfig: testConfig, + Cc: &ChainlinkClient{}, + StarknetClient: &starknet.Client{}, } - var err error - testState.Cc.NKeys, testState.Cc.ChainlinkNodes, err = testState.Common.CreateKeys(testState.Common.Env) - require.NoError(testState.T, err, "Creating chains and keys should not fail") // TODO; fails here - baseURL := testState.Common.L2RPCUrl - if !testState.Common.Testnet { // devnet! - // chainlink starknet client needs the RPC API url which is at /rpc on devnet - baseURL += "/rpc" + + if state.T != nil { + state.L = logging.GetTestLogger(state.T) } - testState.Starknet, err = starknet.NewClient(testState.Common.ChainId, baseURL, lggr, &rpcRequestTimeout) - require.NoError(testState.T, err, "Creating starknet client should not fail") - testState.OCR2Client, err = ocr2.NewClient(testState.Starknet, lggr) - require.NoError(testState.T, err, "Creating ocr2 client should not fail") - if !testState.Common.Testnet { + + return state, nil +} + +// DeployCluster Deploys and sets up config of the environment and nodes +func (m *OCRv2TestState) DeployCluster() { + if m.Common.IsK8s { + m.DeployEnv() + } else { + env, err := test_env.NewTestEnv() + require.NoError(m.T, err) + stark := test_env_starknet.NewStarknet([]string{env.Network.Name}) + err = stark.StartContainer() + require.NoError(m.T, err) + m.Common.L2RPCUrl = stark.ExternalHttpUrl + m.Resty = resty.New().SetBaseURL(m.Common.L2RPCUrl) + b, err := test_env.NewCLTestEnvBuilder(). + WithNonEVM(). + WithTestInstance(m.T). + WithTestConfig(m.TestConfig). + WithMockAdapter(). + WithCLNodeConfig(m.Common.DefaultNodeConfig()). + WithCLNodes(m.Common.NodeCount). + WithCLNodeOptions(m.Common.NodeOpts...). + WithStandardCleanup(). + WithTestEnv(env) + require.NoError(m.T, err) + env, err = b.Build() + require.NoError(m.T, err) + m.Common.DockerEnv = &StarknetClusterTestEnv{ + CLClusterTestEnv: env, + Starknet: stark, + Killgrave: env.MockAdapter, + } + m.Killgrave = env.MockAdapter + } + + m.ObservationSource = m.GetDefaultObservationSource() + m.JuelsPerFeeCoinSource = m.GetDefaultJuelsPerFeeCoinSource() + m.SetupClients() + if m.Common.IsK8s { + m.Cc.NKeys, m.err = m.Common.CreateNodeKeysBundle(m.GetChainlinkNodes()) + require.NoError(m.T, m.err) + } else { + m.Cc.NKeys, m.err = m.Common.CreateNodeKeysBundle(m.Common.DockerEnv.ClCluster.NodeAPIs()) + require.NoError(m.T, m.err) + } + lggr := logger.Nop() + m.StarknetClient, m.err = starknet.NewClient(m.Common.ChainId, m.Common.L2RPCUrl, lggr, &rpcRequestTimeout) + require.NoError(m.T, m.err, "Creating starknet client should not fail") + m.OCR2Client, m.err = ocr2.NewClient(m.StarknetClient, lggr) + require.NoError(m.T, m.err, "Creating ocr2 client should not fail") + if !m.Common.Testnet { // fetch predeployed account 0 to use as funder - devnet := starknetdevnet.NewDevNet(testState.Common.L2RPCUrl) - accounts, err := devnet.Accounts() - require.NoError(testState.T, err) + m.DevnetClient = starknetdevnet.NewDevNet(m.Common.L2RPCUrl) + accounts, err := m.DevnetClient.Accounts() + require.NoError(m.T, err) account := accounts[0] - - err = os.Setenv("PRIVATE_KEY", account.PrivateKey) - require.NoError(testState.T, err, "Setting private key should not fail") - err = os.Setenv("ACCOUNT", account.Address) - require.NoError(testState.T, err, "Setting account address should not fail") - testState.Devnet.AutoDumpState() // Auto dumping devnet state to avoid losing contracts on crash + m.Account = account.Address + m.PrivateKey = account.PrivateKey } } // DeployEnv Deploys the environment -func (testState *Test) DeployEnv() { - testState.Common.SetLocalEnvironment(testState.T) - // if testState.Common.Env.WillUseRemoteRunner() { - // return // short circuit here if using a remote runner - // } +func (m *OCRv2TestState) DeployEnv() { + err := m.Common.Env.Run() + require.NoError(m.T, err) } // SetupClients Sets up the starknet client -func (testState *Test) SetupClients() { - l := utils.GetTestLogger(testState.T) - if testState.Common.Testnet { - l.Debug().Msg(fmt.Sprintf("Overriding L2 RPC: %s", testState.Common.L2RPCUrl)) +func (m *OCRv2TestState) SetupClients() { + if m.Common.IsK8s { + m.ChainlinkNodesK8s, m.err = client.ConnectChainlinkNodes(m.Common.Env) + require.NoError(m.T, m.err) } else { - // TODO: HAXX: - // testState.Common.L2RPCUrl = testState.Common.Env.URLs[testState.Common.ServiceKeyL2][0] // For local runs setting local ip - // if testState.Common.Env.Cfg.InsideK8s { - // testState.Common.L2RPCUrl = testState.Common.Env.URLs[testState.Common.ServiceKeyL2][1] // For remote runner setting remote IP - // } - l.Debug().Msg(fmt.Sprintf("L2 RPC: %s", testState.Common.L2RPCUrl)) - testState.Devnet = testState.Devnet.NewStarknetDevnetClient(testState.Common.L2RPCUrl, dumpPath) + m.Cc.ChainlinkNodes = m.Common.DockerEnv.ClCluster.NodeAPIs() } } // LoadOCR2Config Loads and returns the default starknet gauntlet config -func (testState *Test) LoadOCR2Config() (*ops.OCR2Config, error) { +func (m *OCRv2TestState) LoadOCR2Config() (*ops.OCR2Config, error) { var offChaiNKeys []string var onChaiNKeys []string var peerIds []string var txKeys []string var cfgKeys []string - for i, key := range testState.Cc.NKeys { + for i, key := range m.Cc.NKeys { offChaiNKeys = append(offChaiNKeys, key.OCR2Key.Data.Attributes.OffChainPublicKey) peerIds = append(peerIds, key.PeerID) - txKeys = append(txKeys, testState.AccountAddresses[i]) + txKeys = append(txKeys, m.AccountAddresses[i]) onChaiNKeys = append(onChaiNKeys, key.OCR2Key.Data.Attributes.OnChainPublicKey) cfgKeys = append(cfgKeys, key.OCR2Key.Data.Attributes.ConfigPublicKey) } @@ -146,39 +191,29 @@ func (testState *Test) LoadOCR2Config() (*ops.OCR2Config, error) { return &payload, nil } -func (testState *Test) SetUpNodes() { - err := testState.Common.CreateJobsForContract(testState.GetChainlinkClient(), testState.Common.MockUrl, testState.ObservationSource, testState.JuelsPerFeeCoinSource, testState.OCRAddr, testState.AccountAddresses) - require.NoError(testState.T, err, "Creating jobs should not fail") +func (m *OCRv2TestState) SetUpNodes() { + err := m.Common.CreateJobsForContract(m.GetChainlinkClient(), m.Killgrave, m.ObservationSource, m.JuelsPerFeeCoinSource, m.OCRAddr, m.AccountAddresses) + require.NoError(m.T, err, "Creating jobs should not fail") } // GetNodeKeys Returns the node key bundles -func (testState *Test) GetNodeKeys() []client.NodeKeysBundle { - return testState.Cc.NKeys -} - -func (testState *Test) GetChainlinkNodes() []*client.ChainlinkClient { - return testState.Cc.ChainlinkNodes -} - -func (testState *Test) GetChainlinkClient() *ChainlinkClient { - return testState.Cc +func (m *OCRv2TestState) GetNodeKeys() []client.NodeKeysBundle { + return m.Cc.NKeys } -func (testState *Test) GetStarknetDevnetClient() *devnet.StarknetDevnetClient { - return testState.Devnet +func (m *OCRv2TestState) GetChainlinkNodes() []*client.ChainlinkClient { + return m.Cc.ChainlinkNodes } -func (testState *Test) SetBridgeTypeAttrs(attr *client.BridgeTypeAttributes) { - testState.Cc.bTypeAttr = attr +func (m *OCRv2TestState) GetChainlinkClient() *ChainlinkClient { + return m.Cc } -// ConfigureL1Messaging Sets the L1 messaging contract location and RPC url on L2 -func (testState *Test) ConfigureL1Messaging() { - err := testState.Devnet.LoadL1MessagingContract(testState.L1RPCUrl) - require.NoError(testState.T, err, "Setting up L1 messaging should not fail") +func (m *OCRv2TestState) SetBridgeTypeAttrs(attr *client.BridgeTypeAttributes) { + m.Cc.bTypeAttr = attr } -func (testState *Test) GetDefaultObservationSource() string { +func (m *OCRv2TestState) GetDefaultObservationSource() string { return ` val [type = "bridge" name="mockserver-bridge"] parse [type="jsonparse" path="data,result"] @@ -186,7 +221,7 @@ func (testState *Test) GetDefaultObservationSource() string { ` } -func (testState *Test) GetDefaultJuelsPerFeeCoinSource() string { +func (m *OCRv2TestState) GetDefaultJuelsPerFeeCoinSource() string { return `""" sum [type="sum" values=<[451000]> ] sum @@ -194,8 +229,7 @@ func (testState *Test) GetDefaultJuelsPerFeeCoinSource() string { ` } -func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { - l := utils.GetTestLogger(testState.T) +func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { ctx := context.Background() // context background used because timeout handled by requestTimeout param // assert new rounds are occurring details := ocr2.TransmissionDetails{} @@ -205,25 +239,25 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { var positive bool // validate balance in aggregator - linkContractAddress, err := starknetutils.HexToFelt(testState.LinkTokenAddr) + linkContractAddress, err := starknetutils.HexToFelt(m.LinkTokenAddr) if err != nil { return err } - contractAddress, err := starknetutils.HexToFelt(testState.OCRAddr) + contractAddress, err := starknetutils.HexToFelt(m.OCRAddr) if err != nil { return err } - resLINK, errLINK := testState.Starknet.CallContract(ctx, starknet.CallOps{ + resLINK, errLINK := m.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: linkContractAddress, Selector: starknetutils.GetSelectorFromNameFelt("balance_of"), Calldata: []*felt.Felt{contractAddress}, }) - require.NoError(testState.T, errLINK, "Reader balance from LINK contract should not fail", "err", errLINK) - resAgg, errAgg := testState.Starknet.CallContract(ctx, starknet.CallOps{ + require.NoError(m.T, errLINK, "Reader balance from LINK contract should not fail", "err", errLINK) + resAgg, errAgg := m.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: contractAddress, Selector: starknetutils.GetSelectorFromNameFelt("link_available_for_payment"), }) - require.NoError(testState.T, errAgg, "link_available_for_payment should not fail", "err", errAgg) + require.NoError(m.T, errAgg, "link_available_for_payment should not fail", "err", errAgg) balLINK := resLINK[0].BigInt(big.NewInt(0)) balAgg := resAgg[1].BigInt(big.NewInt(0)) isNegative := resAgg[0].BigInt(big.NewInt(0)) @@ -231,13 +265,15 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { balAgg = new(big.Int).Neg(balAgg) } - assert.Equal(testState.T, balLINK.Cmp(big.NewInt(0)), 1, "Aggregator should have non-zero balance") - assert.GreaterOrEqual(testState.T, balLINK.Cmp(balAgg), 0, "Aggregator payment balance should be <= actual LINK balance") + assert.Equal(m.T, balLINK.Cmp(big.NewInt(0)), 1, "Aggregator should have non-zero balance") + assert.GreaterOrEqual(m.T, balLINK.Cmp(balAgg), 0, "Aggregator payment balance should be <= actual LINK balance") - for start := time.Now(); time.Since(start) < testState.Common.TestDuration; { - l.Info().Msg(fmt.Sprintf("Elapsed time: %s, Round wait: %s ", time.Since(start), testState.Common.TestDuration)) - res, err2 := testState.OCR2Client.LatestTransmissionDetails(ctx, contractAddress) - require.NoError(testState.T, err2, "Failed to get latest transmission details") + err = m.Killgrave.SetAdapterBasedIntValuePath("/mockserver-bridge", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(m.T, err, "Failed to set mock adapter value") + for start := time.Now(); time.Since(start) < m.Common.TestDuration; { + m.L.Info().Msg(fmt.Sprintf("Elapsed time: %s, Round wait: %s ", time.Since(start), m.Common.TestDuration)) + res, err2 := m.OCR2Client.LatestTransmissionDetails(ctx, contractAddress) + require.NoError(m.T, err2, "Failed to get latest transmission details") // end condition: enough rounds have occurred if !isSoak && increasing >= rounds && positive { break @@ -245,7 +281,7 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { // end condition: rounds have been stuck if stuck && stuckCount > 50 { - l.Debug().Msg("failing to fetch transmissions means blockchain may have stopped") + m.L.Debug().Msg("failing to fetch transmissions means blockchain may have stopped") break } @@ -253,10 +289,10 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { time.Sleep(5 * time.Second) if err != nil { - l.Error().Msg(fmt.Sprintf("Transmission Error: %+v", err)) + m.L.Error().Msg(fmt.Sprintf("Transmission Error: %+v", err)) continue } - l.Info().Msg(fmt.Sprintf("Transmission Details: %+v", res)) + m.L.Info().Msg(fmt.Sprintf("Transmission Details: %+v", res)) // continue if no changes if res.Epoch == 0 && res.Round == 0 { @@ -269,21 +305,21 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { // if changes from zero values set (should only initially) if res.Epoch > 0 && details.Epoch == 0 { if !isSoak { - assert.Greater(testState.T, res.Epoch, details.Epoch) - assert.GreaterOrEqual(testState.T, res.Round, details.Round) - assert.NotEqual(testState.T, ansCmp, 0) // assert changed from 0 - assert.NotEqual(testState.T, res.Digest, details.Digest) - assert.Equal(testState.T, details.LatestTimestamp.Before(res.LatestTimestamp), true) + assert.Greater(m.T, res.Epoch, details.Epoch) + assert.GreaterOrEqual(m.T, res.Round, details.Round) + assert.NotEqual(m.T, ansCmp, 0) // assert changed from 0 + assert.NotEqual(m.T, res.Digest, details.Digest) + assert.Equal(m.T, details.LatestTimestamp.Before(res.LatestTimestamp), true) } details = res continue } // check increasing rounds if !isSoak { - assert.Equal(testState.T, res.Digest, details.Digest, "Config digest should not change") + assert.Equal(m.T, res.Digest, details.Digest, "Config digest should not change") } else { if res.Digest != details.Digest { - l.Error().Msg(fmt.Sprintf("Config digest should not change, expected %s got %s", details.Digest, res.Digest)) + m.L.Error().Msg(fmt.Sprintf("Config digest should not change, expected %s got %s", details.Digest, res.Digest)) } } if (res.Epoch > details.Epoch || (res.Epoch == details.Epoch && res.Round > details.Round)) && details.LatestTimestamp.Before(res.LatestTimestamp) { @@ -301,31 +337,31 @@ func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { } } if !isSoak { - assert.GreaterOrEqual(testState.T, increasing, rounds, "Round + epochs should be increasing") - assert.Equal(testState.T, positive, true, "Positive value should have been submitted") - assert.Equal(testState.T, stuck, false, "Round + epochs should not be stuck") + assert.GreaterOrEqual(m.T, increasing, rounds, "Round + epochs should be increasing") + assert.Equal(m.T, positive, true, "Positive value should have been submitted") + assert.Equal(m.T, stuck, false, "Round + epochs should not be stuck") } // Test proxy reading // TODO: would be good to test proxy switching underlying feeds - proxyAddress, err := starknetutils.HexToFelt(testState.ProxyAddr) + proxyAddress, err := starknetutils.HexToFelt(m.ProxyAddr) if err != nil { return err } - roundDataRaw, err := testState.Starknet.CallContract(ctx, starknet.CallOps{ + roundDataRaw, err := m.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: proxyAddress, Selector: starknetutils.GetSelectorFromNameFelt("latest_round_data"), }) if !isSoak { - require.NoError(testState.T, err, "Reading round data from proxy should not fail") - assert.Equal(testState.T, len(roundDataRaw), 5, "Round data from proxy should match expected size") + require.NoError(m.T, err, "Reading round data from proxy should not fail") + assert.Equal(m.T, len(roundDataRaw), 5, "Round data from proxy should match expected size") } valueBig := roundDataRaw[1].BigInt(big.NewInt(0)) - require.NoError(testState.T, err) + require.NoError(m.T, err) value := valueBig.Int64() if value < 0 { - assert.Equal(testState.T, value, int64(5), "Reading from proxy should return correct value") + assert.Equal(m.T, value, int64(5), "Reading from proxy should return correct value") } return nil diff --git a/integration-tests/docker/test_env/stark.go b/integration-tests/docker/test_env/stark.go new file mode 100644 index 000000000..8a2e262f6 --- /dev/null +++ b/integration-tests/docker/test_env/stark.go @@ -0,0 +1,105 @@ +package test_env + +import ( + "fmt" + "testing" + "time" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + tc "github.com/testcontainers/testcontainers-go" + tcwait "github.com/testcontainers/testcontainers-go/wait" + + "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/testcontext" +) + +const ( + STARK_HTTP_PORT = "5050" +) + +type Starknet struct { + test_env.EnvComponent + ExternalHttpUrl string + InternalHttpUrl string + t *testing.T + l zerolog.Logger +} + +func NewStarknet(networks []string, opts ...test_env.EnvComponentOption) *Starknet { + ms := &Starknet{ + EnvComponent: test_env.EnvComponent{ + ContainerName: "starknet", + Networks: networks, + }, + + l: log.Logger, + } + for _, opt := range opts { + opt(&ms.EnvComponent) + } + return ms +} + +func (s *Starknet) WithTestLogger(t *testing.T) *Starknet { + s.l = logging.GetTestLogger(t) + s.t = t + return s +} + +func (s *Starknet) StartContainer() error { + l := tc.Logger + if s.t != nil { + l = logging.CustomT{ + T: s.t, + L: s.l, + } + } + cReq, err := s.getContainerRequest() + if err != nil { + return err + } + c, err := tc.GenericContainer(testcontext.Get(s.t), tc.GenericContainerRequest{ + ContainerRequest: *cReq, + Reuse: true, + Started: true, + Logger: l, + }) + if err != nil { + return fmt.Errorf("cannot start Starknet container: %w", err) + } + s.Container = c + host, err := test_env.GetHost(testcontext.Get(s.t), c) + if err != nil { + return err + } + httpPort, err := c.MappedPort(testcontext.Get(s.t), test_env.NatPort(STARK_HTTP_PORT)) + if err != nil { + return err + } + + s.ExternalHttpUrl = fmt.Sprintf("http://%s:%s", host, httpPort.Port()) + s.InternalHttpUrl = fmt.Sprintf("http://%s:%s", s.ContainerName, STARK_HTTP_PORT) + + s.l.Info(). + Any("ExternalHttpUrl", s.ExternalHttpUrl). + Any("InternalHttpUrl", s.InternalHttpUrl). + Str("containerName", s.ContainerName). + Msgf("Started Starknet container") + + return nil +} + +func (ms *Starknet) getContainerRequest() (*tc.ContainerRequest, error) { + return &tc.ContainerRequest{ + Name: ms.ContainerName, + Image: "shardlabs/starknet-devnet-rs:latest", + ExposedPorts: []string{test_env.NatPortFormat(STARK_HTTP_PORT)}, + Networks: ms.Networks, + WaitingFor: tcwait.ForLog("Starknet Devnet listening"). + WithStartupTimeout(30 * time.Second). + WithPollInterval(100 * time.Millisecond), + Entrypoint: []string{"sh", "-c", "tini -- starknet-devnet --host 0.0.0.0 --port 5050 --seed 0 --account-class cairo1 --gas-price 1"}, + }, nil +} diff --git a/integration-tests/smoke/ocr2_new_test.go b/integration-tests/smoke/ocr2_new_test.go new file mode 100644 index 000000000..fd7e91557 --- /dev/null +++ b/integration-tests/smoke/ocr2_new_test.go @@ -0,0 +1,69 @@ +package smoke_test + +import ( + "flag" + "fmt" + "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" + "github.com/smartcontractkit/chainlink-starknet/ops/utils" + "maps" + "testing" + + "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/stretchr/testify/require" +) + +var ( + keepAlive bool + decimals = 9 +) + +func init() { + flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") +} + +func TestOCRBasicNew(t *testing.T) { + for _, test := range []struct { + name string + env map[string]string + }{ + {name: "embeded"}, + //{name: "plugins", env: map[string]string{ + // "CL_MEDIAN_CMD": "chainlink-feeds", + // "CL_SOLANA_CMD": "chainlink-solana", + //}}, + } { + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + logging.Init() + state, err := common.NewOCRv2State(t, "localnet", false, "test", &config) + require.NoError(t, err, "Could not setup the ocrv2 state") + if len(test.env) > 0 { + state.Common.NodeOpts = append(state.Common.NodeOpts, func(n *test_env.ClNode) { + if n.ContainerEnvs == nil { + n.ContainerEnvs = map[string]string{} + } + maps.Copy(n.ContainerEnvs, test.env) + }) + } + state.DeployCluster() + state.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) + err = state.Sg.SetupNetwork(state.Common.L2RPCUrl, state.Account, state.PrivateKey) + require.NoError(t, err, "Setting up gauntlet network should not fail") + err = state.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) + require.NoError(t, err, "Deploying contracts should not fail") + + state.SetUpNodes() + + err = state.ValidateRounds(10, false) + require.NoError(t, err, "Validating round should not fail") + }) + } +} diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 9a25fbc5f..c11c81d69 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -1,61 +1,62 @@ package smoke_test -import ( - "flag" - "fmt" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" - "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" - "github.com/smartcontractkit/chainlink-starknet/ops/utils" -) - -var ( - keepAlive bool -) - -func init() { - flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") -} - -var ( - err error - testState *common.Test - decimals = 9 -) - -func TestOCRBasic(t *testing.T) { - testState = &common.Test{ - T: t, - } - testState.Common = common.New(t) - // Setting this to the root of the repo for cmd exec func for Gauntlet - testState.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) - require.NoError(t, err, "Could not get a new gauntlet struct") - - testState.DeployCluster() - require.NoError(t, err, "Deploying cluster should not fail") - // if testState.Common.Env.WillUseRemoteRunner() { - // return // short circuit here if using a remote runner - // } - err = testState.Sg.SetupNetwork(testState.Common.L2RPCUrl) - require.NoError(t, err, "Setting up gauntlet network should not fail") - err = testState.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) - require.NoError(t, err, "Deploying contracts should not fail") - if !testState.Common.Testnet { - testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) - } - testState.SetUpNodes() - - err = testState.ValidateRounds(10, false) - require.NoError(t, err, "Validating round should not fail") - - t.Cleanup(func() { - testState.Common.TearDownLocalEnvironment(t) - // TODO: - // err = actions.TeardownSuite(t, testState.Common.Env, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel, nil, nil) - // require.NoError(t, err, "Error tearing down environment") - }) -} +// +//import ( +// "flag" +// "fmt" +// "testing" +// +// "github.com/stretchr/testify/require" +// +// "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" +// "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" +// "github.com/smartcontractkit/chainlink-starknet/ops/utils" +//) +// +//var ( +// keepAlive bool +//) +// +//func init() { +// flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") +//} +// +//var ( +// err error +// testState *common.Test +// decimals = 9 +//) +// +//func TestOCRBasic(t *testing.T) { +// testState = &common.Test{ +// T: t, +// } +// testState.Common = common.NewOCRv2State(t) +// // Setting this to the root of the repo for cmd exec func for Gauntlet +// testState.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) +// require.NoError(t, err, "Could not get a new gauntlet struct") +// +// testState.DeployCluster() +// require.NoError(t, err, "Deploying cluster should not fail") +// // if testState.Common.Env.WillUseRemoteRunner() { +// // return // short circuit here if using a remote runner +// // } +// err = testState.Sg.SetupNetwork(testState.Common.L2RPCUrl) +// require.NoError(t, err, "Setting up gauntlet network should not fail") +// err = testState.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) +// require.NoError(t, err, "Deploying contracts should not fail") +// if !testState.Common.Testnet { +// testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) +// } +// testState.SetUpNodes() +// +// err = testState.ValidateRounds(10, false) +// require.NoError(t, err, "Validating round should not fail") +// +// t.Cleanup(func() { +// testState.Common.TearDownLocalEnvironment(t) +// // TODO: +// // err = actions.TeardownSuite(t, testState.Common.Env, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel, nil, nil) +// // require.NoError(t, err, "Error tearing down environment") +// }) +//} diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml new file mode 100644 index 000000000..a65c23d70 --- /dev/null +++ b/integration-tests/testconfig/default.toml @@ -0,0 +1,22 @@ +[Logging] +test_log_collect=false + +[Logging.LogStream] +log_targets=["file"] +log_producer_timeout="10s" +log_producer_retry_limit=10 + +[Network] +selected_networks=["simulated"] + +[PrivateEthereumNetwork] +consensus_type="pow" +execution_layer="geth" + +[PrivateEthereumNetwork.EthereumChainConfig] +seconds_per_slot=3 +slots_per_epoch=2 +genesis_delay=15 +validator_count=4 +chain_id=1337 +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] \ No newline at end of file diff --git a/integration-tests/testconfig/ocr2/example.toml b/integration-tests/testconfig/ocr2/example.toml new file mode 100644 index 000000000..6cbdbef15 --- /dev/null +++ b/integration-tests/testconfig/ocr2/example.toml @@ -0,0 +1,96 @@ +# Example of full config with all fields +# General part +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.7.0" + +[Logging] +# if set to true will save logs even if test did not fail +test_log_collect=false + +[Logging.LogStream] +# supported targets: file, loki, in-memory. if empty no logs will be persistet +log_targets=["file"] +# context timeout for starting log producer and also time-frame for requesting logs +log_producer_timeout="10s" +# number of retries before log producer gives up and stops listening to logs +log_producer_retry_limit=10 + +[Logging.Loki] +tenant_id="tenant_id" +# full URL of Loki ingest endpoint +endpoint="https://loki.url/api/v3/push" +# currently only needed when using public instance +basic_auth="loki-basic-auth" +# only needed for cloud grafana +bearer_token="bearer_token" + +# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) +[Logging.Grafana] +# grafana url (trailing "/" will be stripped) +base_url="http://grafana.url" +# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard +dashboard_url="/d/your-dashboard" +bearer_token="my-awesome-token" + +# if you want to use polygon_mumbial +[Network] +selected_networks=["polygon_mumbai"] + +[Network.RpcHttpUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.RpcWsUrls] +polygon_mumbai = ["https://my-rpc-endpoint.io"] + +[Network.WalletKeys] +polygon_mumbai = ["change-me-to-your-PK"] + +[PrivateEthereumNetwork] +# pos or pow +consensus_type="pos" +# only prysm supported currently +consensus_layer="prysm" +# geth, besu, nethermind or erigon +execution_layer="geth" +# if true after env started it will wait for at least 1 epoch to be finalised before continuing +wait_for_finalization=false + +[PrivateEthereumNetwork.EthereumChainConfig] +# duration of single slot, lower => faster block production, must be >= 4 +seconds_per_slot=12 +# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 +slots_per_epoch=6 +# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts +genesis_delay=15 +# number of validators in the network +validator_count=8 +chain_id=1337 +# list of addresses to be prefunded in genesis +addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] + +# load test specific configuration +[Load.OCR] +[Load.OCR.Common] +eth_funds = 3 + +[Load.OCR.Load] +test_duration = "3m" +rate_limit_unit_duration = "1m" +rate = 3 +verification_interval = "5s" +verification_timeout = "3m" +ea_change_interval = "5s" + +# soak test specific configuration +[Soak.Common] +chainlink_node_funding = 100 + +[Soak.OCR] +[Soak.OCR.Common] +test_duration="15m" + +[Soak.OCR.Soak] +ocr_version="1" +number_of_contracts=2 +time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/ocr2/ocr2.go b/integration-tests/testconfig/ocr2/ocr2.go new file mode 100644 index 000000000..d5cc48ea5 --- /dev/null +++ b/integration-tests/testconfig/ocr2/ocr2.go @@ -0,0 +1,136 @@ +package ocr2 + +import ( + "errors" + + "github.com/smartcontractkit/chainlink-testing-framework/blockchain" +) + +type Config struct { + Soak *SoakConfig `toml:"Soak"` + Load *Load `toml:"Load"` + Volume *Volume `toml:"Volume"` + Common *Common `toml:"Common"` +} + +func (o *Config) Validate() error { + if o.Common != nil { + if err := o.Common.Validate(); err != nil { + return err + } + } + if o.Soak != nil { + if err := o.Soak.Validate(); err != nil { + return err + } + } + if o.Volume != nil { + if err := o.Volume.Validate(); err != nil { + return err + } + } + return nil +} + +type Common struct { + ETHFunds *int `toml:"eth_funds"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Common) Validate() error { + if o.ETHFunds != nil && *o.ETHFunds < 0 { + return errors.New("eth_funds must be set and cannot be negative") + } + return nil +} + +type Load struct { + Rate *int64 `toml:"rate"` + RequestsPerUnit *int `toml:"requests_per_unit"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` + VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` + EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Load) Validate() error { + if o.TestDuration == nil { + return errors.New("load test duration must be set") + } + if o.Rate == nil || *o.Rate <= 0 { + return errors.New("rate must be set and be a positive integer") + } + if o.RequestsPerUnit == nil || *o.RequestsPerUnit <= 0 { + return errors.New("vu_requests_per_unit must be set and be a positive integer") + } + if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { + return errors.New("rate_limit_unit_duration must be set and be a positive integer") + } + if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { + return errors.New("verification_interval must be set and be a positive integer") + } + if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { + return errors.New("verification_timeout must be set and be a positive integer") + } + if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { + return errors.New("ea_change_interval must be set and be a positive integer") + } + + return nil +} + +type Volume struct { + Rate *int64 `toml:"rate"` + VURequestsPerUnit *int `toml:"vu_requests_per_unit"` + RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` + VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` + VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` + EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` + TestDuration *blockchain.StrDuration `toml:"test_duration"` +} + +func (o *Volume) Validate() error { + if o.TestDuration == nil { + return errors.New("volume test duration must be set") + } + if o.Rate == nil || *o.Rate <= 0 { + return errors.New("rate must be set and be a positive integer") + } + if o.VURequestsPerUnit == nil || *o.VURequestsPerUnit <= 0 { + return errors.New("vu_requests_per_unit must be set and be a positive integer") + } + if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { + return errors.New("rate_limit_unit_duration must be set and be a positive integer") + } + if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { + return errors.New("verification_interval must be set and be a positive integer") + } + if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { + return errors.New("verification_timeout must be set and be a positive integer") + } + if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { + return errors.New("ea_change_interval must be set and be a positive integer") + } + + return nil +} + +type SoakConfig struct { + OCRVersion *string `toml:"ocr_version"` + NumberOfContracts *int `toml:"number_of_contracts"` + TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` +} + +func (o *SoakConfig) Validate() error { + if o.OCRVersion == nil || *o.OCRVersion == "" { + return errors.New("ocr_version must be set to either 1 or 2") + } + if o.NumberOfContracts == nil || *o.NumberOfContracts <= 1 { + return errors.New("number_of_contracts must be set and be greater than 1") + } + if o.TimeBetweenRounds == nil || o.TimeBetweenRounds.Duration == 0 { + return errors.New("time_between_rounds must be set and be a positive integer") + } + return nil +} diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml new file mode 100644 index 000000000..e69de29bb diff --git a/ops/gauntlet/gauntlet_starknet.go b/ops/gauntlet/gauntlet_starknet.go index 50bb8de5e..2b11779ce 100644 --- a/ops/gauntlet/gauntlet_starknet.go +++ b/ops/gauntlet/gauntlet_starknet.go @@ -72,8 +72,10 @@ func (sg *StarknetGauntlet) FetchGauntletJsonOutput() (*GauntletResponse, error) } // SetupNetwork Sets up a new network and sets the NODE_URL for Devnet / Starknet RPC -func (sg *StarknetGauntlet) SetupNetwork(addr string) error { +func (sg *StarknetGauntlet) SetupNetwork(addr string, account string, privateKey string) error { sg.G.AddNetworkConfigVar("NODE_URL", addr) + sg.G.AddNetworkConfigVar("ACCOUNT", account) + sg.G.AddNetworkConfigVar("PRIVATE_KEY", privateKey) err := sg.G.WriteNetworkConfigMap(sg.dir + "packages-ts/starknet-gauntlet-cli/networks/") if err != nil { return err From e9e62718bd5a25a6eebec6b4bb96677bd3041f96 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 08:01:02 +0100 Subject: [PATCH 02/39] Rebase --- integration-tests/go.mod | 6 +- integration-tests/smoke/ocr2_new_test.go | 69 ------------ integration-tests/smoke/ocr2_test.go | 127 ++++++++++++----------- 3 files changed, 70 insertions(+), 132 deletions(-) delete mode 100644 integration-tests/smoke/ocr2_new_test.go diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 89ec2a6b5..5b27d443e 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -5,6 +5,7 @@ go 1.21.4 require ( github.com/NethermindEth/juno v0.3.1 github.com/NethermindEth/starknet.go v0.6.1-0.20231218140327-915109ab5bc1 + github.com/go-resty/resty/v2 v2.7.0 github.com/google/uuid v1.4.0 github.com/lib/pq v1.10.9 github.com/rs/zerolog v1.30.0 @@ -15,7 +16,7 @@ require ( github.com/smartcontractkit/chainlink/integration-tests v0.0.0-20240215151806-009c99876c4c github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240215151806-009c99876c4c github.com/stretchr/testify v1.8.4 - go.uber.org/zap v1.26.0 + github.com/testcontainers/testcontainers-go v0.23.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -173,7 +174,6 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.15.5 // indirect github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/go-webauthn/webauthn v0.9.4 // indirect github.com/go-webauthn/x v0.1.5 // indirect @@ -375,7 +375,6 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tendermint/go-amino v0.16.0 // indirect github.com/teris-io/shortid v0.0.0-20201117134242-e59966efd125 // indirect - github.com/testcontainers/testcontainers-go v0.23.0 // indirect github.com/theodesp/go-heaps v0.0.0-20190520121037-88e35354fe0a // indirect github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/gjson v1.17.0 // indirect @@ -420,6 +419,7 @@ require ( go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect + go.uber.org/zap v1.26.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.19.0 // indirect diff --git a/integration-tests/smoke/ocr2_new_test.go b/integration-tests/smoke/ocr2_new_test.go deleted file mode 100644 index fd7e91557..000000000 --- a/integration-tests/smoke/ocr2_new_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package smoke_test - -import ( - "flag" - "fmt" - "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" - "github.com/smartcontractkit/chainlink-starknet/ops/utils" - "maps" - "testing" - - "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" - "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" - "github.com/stretchr/testify/require" -) - -var ( - keepAlive bool - decimals = 9 -) - -func init() { - flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") -} - -func TestOCRBasicNew(t *testing.T) { - for _, test := range []struct { - name string - env map[string]string - }{ - {name: "embeded"}, - //{name: "plugins", env: map[string]string{ - // "CL_MEDIAN_CMD": "chainlink-feeds", - // "CL_SOLANA_CMD": "chainlink-solana", - //}}, - } { - config, err := tc.GetConfig("Smoke", tc.OCR2) - if err != nil { - t.Fatal(err) - } - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - logging.Init() - state, err := common.NewOCRv2State(t, "localnet", false, "test", &config) - require.NoError(t, err, "Could not setup the ocrv2 state") - if len(test.env) > 0 { - state.Common.NodeOpts = append(state.Common.NodeOpts, func(n *test_env.ClNode) { - if n.ContainerEnvs == nil { - n.ContainerEnvs = map[string]string{} - } - maps.Copy(n.ContainerEnvs, test.env) - }) - } - state.DeployCluster() - state.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) - err = state.Sg.SetupNetwork(state.Common.L2RPCUrl, state.Account, state.PrivateKey) - require.NoError(t, err, "Setting up gauntlet network should not fail") - err = state.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) - require.NoError(t, err, "Deploying contracts should not fail") - - state.SetUpNodes() - - err = state.ValidateRounds(10, false) - require.NoError(t, err, "Validating round should not fail") - }) - } -} diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index c11c81d69..fd7e91557 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -1,62 +1,69 @@ package smoke_test -// -//import ( -// "flag" -// "fmt" -// "testing" -// -// "github.com/stretchr/testify/require" -// -// "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" -// "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" -// "github.com/smartcontractkit/chainlink-starknet/ops/utils" -//) -// -//var ( -// keepAlive bool -//) -// -//func init() { -// flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") -//} -// -//var ( -// err error -// testState *common.Test -// decimals = 9 -//) -// -//func TestOCRBasic(t *testing.T) { -// testState = &common.Test{ -// T: t, -// } -// testState.Common = common.NewOCRv2State(t) -// // Setting this to the root of the repo for cmd exec func for Gauntlet -// testState.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) -// require.NoError(t, err, "Could not get a new gauntlet struct") -// -// testState.DeployCluster() -// require.NoError(t, err, "Deploying cluster should not fail") -// // if testState.Common.Env.WillUseRemoteRunner() { -// // return // short circuit here if using a remote runner -// // } -// err = testState.Sg.SetupNetwork(testState.Common.L2RPCUrl) -// require.NoError(t, err, "Setting up gauntlet network should not fail") -// err = testState.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) -// require.NoError(t, err, "Deploying contracts should not fail") -// if !testState.Common.Testnet { -// testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) -// } -// testState.SetUpNodes() -// -// err = testState.ValidateRounds(10, false) -// require.NoError(t, err, "Validating round should not fail") -// -// t.Cleanup(func() { -// testState.Common.TearDownLocalEnvironment(t) -// // TODO: -// // err = actions.TeardownSuite(t, testState.Common.Env, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel, nil, nil) -// // require.NoError(t, err, "Error tearing down environment") -// }) -//} +import ( + "flag" + "fmt" + "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" + "github.com/smartcontractkit/chainlink-starknet/ops/utils" + "maps" + "testing" + + "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" + tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/stretchr/testify/require" +) + +var ( + keepAlive bool + decimals = 9 +) + +func init() { + flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") +} + +func TestOCRBasicNew(t *testing.T) { + for _, test := range []struct { + name string + env map[string]string + }{ + {name: "embeded"}, + //{name: "plugins", env: map[string]string{ + // "CL_MEDIAN_CMD": "chainlink-feeds", + // "CL_SOLANA_CMD": "chainlink-solana", + //}}, + } { + config, err := tc.GetConfig("Smoke", tc.OCR2) + if err != nil { + t.Fatal(err) + } + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + logging.Init() + state, err := common.NewOCRv2State(t, "localnet", false, "test", &config) + require.NoError(t, err, "Could not setup the ocrv2 state") + if len(test.env) > 0 { + state.Common.NodeOpts = append(state.Common.NodeOpts, func(n *test_env.ClNode) { + if n.ContainerEnvs == nil { + n.ContainerEnvs = map[string]string{} + } + maps.Copy(n.ContainerEnvs, test.env) + }) + } + state.DeployCluster() + state.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) + err = state.Sg.SetupNetwork(state.Common.L2RPCUrl, state.Account, state.PrivateKey) + require.NoError(t, err, "Setting up gauntlet network should not fail") + err = state.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) + require.NoError(t, err, "Deploying contracts should not fail") + + state.SetUpNodes() + + err = state.ValidateRounds(10, false) + require.NoError(t, err, "Validating round should not fail") + }) + } +} From 2b6cd76a2488e621f8d8ba362390f0405ec1882a Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 08:06:12 +0100 Subject: [PATCH 03/39] Added scarb to Smoke tests --- .github/workflows/integration-tests-smoke.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 7454a964d..31306b2d3 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -147,6 +147,17 @@ jobs: uses: cachix/install-nix-action@29bd9290ef037a3ecbdafe83cbd2185e9dd0fa0a # v20 with: nix_path: nixpkgs=channel:nixos-unstable + - name: Setup Scarb for Linux + id: install-scarb + shell: bash + run: | + wget https://github.com/software-mansion/scarb/releases/download/${{ inputs.scarb_version }}/scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz + tar -xvzf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz + mv -vf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl scarb-build + echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH + - name: Build contracts + run: | + cd contracts && scarb --profile release build - name: Run Tests ${{ matrix.image.name }} uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: From a2c87909a84fb3a056e0ce473ec5283d9db5af5d Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 08:10:18 +0100 Subject: [PATCH 04/39] Feedback from PR --- integration-tests/docker/test_env/stark.go | 2 +- .../infra_deployments/deployer_test.go | 100 ------------------ integration-tests/testconfig/ocr2/ocr2.toml | 0 3 files changed, 1 insertion(+), 101 deletions(-) delete mode 100644 integration-tests/infra_deployments/deployer_test.go delete mode 100644 integration-tests/testconfig/ocr2/ocr2.toml diff --git a/integration-tests/docker/test_env/stark.go b/integration-tests/docker/test_env/stark.go index 8a2e262f6..546454c75 100644 --- a/integration-tests/docker/test_env/stark.go +++ b/integration-tests/docker/test_env/stark.go @@ -94,7 +94,7 @@ func (s *Starknet) StartContainer() error { func (ms *Starknet) getContainerRequest() (*tc.ContainerRequest, error) { return &tc.ContainerRequest{ Name: ms.ContainerName, - Image: "shardlabs/starknet-devnet-rs:latest", + Image: "shardlabs/starknet-devnet-rs:b41e566a3f17aa0e51871f02d5165959e50ce358", ExposedPorts: []string{test_env.NatPortFormat(STARK_HTTP_PORT)}, Networks: ms.Networks, WaitingFor: tcwait.ForLog("Starknet Devnet listening"). diff --git a/integration-tests/infra_deployments/deployer_test.go b/integration-tests/infra_deployments/deployer_test.go deleted file mode 100644 index cb5753b16..000000000 --- a/integration-tests/infra_deployments/deployer_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package infra_deployments_test - -//import ( -//"fmt" -//"net/url" -//"testing" - -//"github.com/stretchr/testify/require" - -//"github.com/smartcontractkit/chainlink-starknet/integration-tests/common" -//"github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" -//"github.com/smartcontractkit/chainlink-starknet/ops/utils" -//"github.com/smartcontractkit/chainlink/integration-tests/client" -//) - -//const ( -//L2RpcUrl = "https://alpha4-2.starknet.io" -//P2pPort = "6691" -//) - -//var ( -//observationSource = ` -//val [type="bridge" name="bridge-coinmetrics" requestData=<{"data": {"from":"LINK","to":"USD"}}>] -//parse [type="jsonparse" path="result"] -//val -> parse -//` -//juelsPerFeeCoinSource = `""" -//sum [type="sum" values=<[451000]> ] -//sum -//""" -//` -//) - -//func createKeys(testState *testing.T) ([]*client.ChainlinkK8sClient, error) { -//urls := [][]string{ -//// Node access params -//{"NODE_URL", "NODE_USER", "NODE_PASS"}, -//} -//var clients []*client.ChainlinkK8sClient - -//for _, nodeUrl := range urls { -//u, _ := url.Parse(nodeUrl[0]) -//c, err := client.NewChainlinkK8sClient(&client.ChainlinkConfig{ -//URL: nodeUrl[0], -//Email: nodeUrl[1], -//Password: nodeUrl[2], -//InternalIP: u.Host, -//}, "", "") -//if err != nil { -//return nil, err -//} -//key, _ := c.MustReadP2PKeys() -//if key == nil { -//_, _, err = c.CreateP2PKey() -//require.NoError(testState, err) -//} -//clients = append(clients, c) -//} -//return clients, nil -//} -//func TestOCRBasic(testState *testing.T) { -//var err error -//t := &common.Test{} -//t.Common = common.New(testState) -//t.Cc = &common.ChainlinkClient{} -//t.Common.P2PPort = P2pPort -//t.Cc.ChainlinkNodes, err = createKeys(testState) -//require.NoError(testState, err) -//t.Cc.NKeys, _, err = client.CreateNodeKeysBundle(t.GetChainlinkNodes(), t.Common.ChainName, t.Common.ChainId) -//require.NoError(testState, err) -//for _, n := range t.Cc.ChainlinkNodes { -//_, _, err = n.CreateStarkNetChain(&client.StarkNetChainAttributes{ -//Type: t.Common.ChainName, -//ChainID: t.Common.ChainId, -//Config: client.StarkNetChainConfig{}, -//}) -//require.NoError(testState, err) -//_, _, err = n.CreateStarkNetNode(&client.StarkNetNodeAttributes{ -//Name: t.Common.ChainName, -//ChainID: t.Common.ChainId, -//Url: L2RpcUrl, -//}) -//require.NoError(testState, err) -//} -//t.Common.Testnet = true -//t.Common.L2RPCUrl = L2RpcUrl -//t.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) -//require.NoError(testState, err, "Could not get a new gauntlet struct") -//err = t.Sg.SetupNetwork(t.Common.L2RPCUrl) -//require.NoError(testState, err, "Setting up gauntlet network should not fail") -//err = t.DeployGauntlet(0, 100000000000, 9, "auto", 1, 1) -//require.NoError(testState, err, "Deploying contracts should not fail") -//t.SetBridgeTypeAttrs(&client.BridgeTypeAttributes{ -//Name: "bridge-coinmetrics", -//URL: "ADAPTER_URL", // ADAPTER_URL e.g https://adapters.main.sand.cldev.sh/coinmetrics -//}) - -//err = t.Common.CreateJobsForContract(t.Cc, observationSource, juelsPerFeeCoinSource, t.OCRAddr, t.AccountAddresses) -//require.NoError(testState, err) -//} diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml deleted file mode 100644 index e69de29bb..000000000 From c14eb9f267d404b9711ab10e31c1b0fd00bae7f1 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 08:27:00 +0100 Subject: [PATCH 05/39] Added inputs for cairo / scarb version --- .github/workflows/integration-tests-smoke.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 31306b2d3..5855febeb 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -9,6 +9,14 @@ on: required: true default: develop type: string + cairo_version: + description: Cairo release version + default: "v2.5.4" + required: false + scarb_version: + description: Scarb release version + default: "v2.5.4" + required: false # Only run 1 of this workflow at a time per PR concurrency: From af3bcafb3ec1a0c1c6276c73b571ee1e75a08030 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 08:43:51 +0100 Subject: [PATCH 06/39] Scarb fix --- .github/workflows/integration-tests-smoke.yml | 14 +++---------- integration-tests/testconfig/configs_embed.go | 1 + .../testconfig/configs_noembed.go | 21 +++++++++++++++++++ integration-tests/testconfig/testconfig.go | 1 + 4 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 integration-tests/testconfig/configs_embed.go create mode 100644 integration-tests/testconfig/configs_noembed.go create mode 100644 integration-tests/testconfig/testconfig.go diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 5855febeb..db57f1889 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -9,14 +9,6 @@ on: required: true default: develop type: string - cairo_version: - description: Cairo release version - default: "v2.5.4" - required: false - scarb_version: - description: Scarb release version - default: "v2.5.4" - required: false # Only run 1 of this workflow at a time per PR concurrency: @@ -159,9 +151,9 @@ jobs: id: install-scarb shell: bash run: | - wget https://github.com/software-mansion/scarb/releases/download/${{ inputs.scarb_version }}/scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - tar -xvzf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - mv -vf scarb-${{ inputs.scarb_version }}-x86_64-unknown-linux-musl scarb-build + wget https://github.com/software-mansion/scarb/releases/download/v2.5.4/scarb-v2.5.4-x86_64-unknown-linux-musl.tar.gz + tar -xvzf scarb-v2.5.4-x86_64-unknown-linux-musl.tar.gz + mv -vf scarb-v2.5.4-x86_64-unknown-linux-musl scarb-build echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH - name: Build contracts run: | diff --git a/integration-tests/testconfig/configs_embed.go b/integration-tests/testconfig/configs_embed.go new file mode 100644 index 000000000..e6950abe2 --- /dev/null +++ b/integration-tests/testconfig/configs_embed.go @@ -0,0 +1 @@ +package testconfig diff --git a/integration-tests/testconfig/configs_noembed.go b/integration-tests/testconfig/configs_noembed.go new file mode 100644 index 000000000..67e954ff5 --- /dev/null +++ b/integration-tests/testconfig/configs_noembed.go @@ -0,0 +1,21 @@ +//go:build embed +// +build embed + +package testconfig + +import "embed" + +//go:embed default.toml +//go:embed automation/automation.toml +//go:embed functions/functions.toml +//go:embed keeper/keeper.toml +//go:embed log_poller/log_poller.toml +//go:embed node/node.toml +//go:embed ocr/ocr.toml +//go:embed vrfv2/vrfv2.toml +//go:embed vrfv2plus/vrfv2plus.toml +var embeddedConfigsFs embed.FS + +func init() { + areConfigsEmbedded = true +} diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go new file mode 100644 index 000000000..e6950abe2 --- /dev/null +++ b/integration-tests/testconfig/testconfig.go @@ -0,0 +1 @@ +package testconfig From 3206fcd0ffac58f5b6c5ccf3edbf4259878c25f0 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 09:06:14 +0100 Subject: [PATCH 07/39] Added base64 override --- .github/workflows/integration-tests-smoke.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index db57f1889..4f1da6d5a 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -158,6 +158,19 @@ jobs: - name: Build contracts run: | cd contracts && scarb --profile release build + - name: Generate config overrides + run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md + cat << EOF > config.toml + [ChainlinkImage] + image="${{ env.CHAINLINK_IMAGE }}" + version="${{ github.sha }}" + EOF + # shellcheck disable=SC2002 + BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) + # shellcheck disable=SC2086 + echo ::add-mask::$BASE64_CONFIG_OVERRIDE + # shellcheck disable=SC2086 + echo "BASE64_CONFIG_OVERRIDE=$BASE64_CONFIG_OVERRIDE" >> $GITHUB_ENV - name: Run Tests ${{ matrix.image.name }} uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: From 3875478b963765bbc3ef641ef9c9d51fa25caa6a Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 10:19:10 +0100 Subject: [PATCH 08/39] Chaning makefile for realtime logs --- Makefile | 2 +- .../testconfig/ocr2/example.toml | 96 ------------------- integration-tests/testconfig/ocr2/ocr2.toml | 0 3 files changed, 1 insertion(+), 97 deletions(-) delete mode 100644 integration-tests/testconfig/ocr2/example.toml create mode 100644 integration-tests/testconfig/ocr2/ocr2.toml diff --git a/Makefile b/Makefile index 22937f60b..a2bc8c5be 100644 --- a/Makefile +++ b/Makefile @@ -200,7 +200,7 @@ test-integration-smoke: test-integration-prep .PHONY: test-integration-smoke-ci test-integration-smoke-ci: cd integration-tests/ && \ - go test --timeout=2h -v -count=1 -json ./smoke 2>&1 | tee /tmp/gotest.log | gotestfmt + go test --timeout=2h -v -count=1 -json ./smoke .PHONY: test-integration-soak test-integration-soak: test-integration-prep diff --git a/integration-tests/testconfig/ocr2/example.toml b/integration-tests/testconfig/ocr2/example.toml deleted file mode 100644 index 6cbdbef15..000000000 --- a/integration-tests/testconfig/ocr2/example.toml +++ /dev/null @@ -1,96 +0,0 @@ -# Example of full config with all fields -# General part -[ChainlinkImage] -image="public.ecr.aws/chainlink/chainlink" -version="2.7.0" - -[Logging] -# if set to true will save logs even if test did not fail -test_log_collect=false - -[Logging.LogStream] -# supported targets: file, loki, in-memory. if empty no logs will be persistet -log_targets=["file"] -# context timeout for starting log producer and also time-frame for requesting logs -log_producer_timeout="10s" -# number of retries before log producer gives up and stops listening to logs -log_producer_retry_limit=10 - -[Logging.Loki] -tenant_id="tenant_id" -# full URL of Loki ingest endpoint -endpoint="https://loki.url/api/v3/push" -# currently only needed when using public instance -basic_auth="loki-basic-auth" -# only needed for cloud grafana -bearer_token="bearer_token" - -# LogStream will try to shorten Grafana URLs by default (if all 3 variables are set) -[Logging.Grafana] -# grafana url (trailing "/" will be stripped) -base_url="http://grafana.url" -# url of your grafana dashboard (prefix and suffix "/" are stirpped), example: /d/ad61652-2712-1722/my-dashboard -dashboard_url="/d/your-dashboard" -bearer_token="my-awesome-token" - -# if you want to use polygon_mumbial -[Network] -selected_networks=["polygon_mumbai"] - -[Network.RpcHttpUrls] -polygon_mumbai = ["https://my-rpc-endpoint.io"] - -[Network.RpcWsUrls] -polygon_mumbai = ["https://my-rpc-endpoint.io"] - -[Network.WalletKeys] -polygon_mumbai = ["change-me-to-your-PK"] - -[PrivateEthereumNetwork] -# pos or pow -consensus_type="pos" -# only prysm supported currently -consensus_layer="prysm" -# geth, besu, nethermind or erigon -execution_layer="geth" -# if true after env started it will wait for at least 1 epoch to be finalised before continuing -wait_for_finalization=false - -[PrivateEthereumNetwork.EthereumChainConfig] -# duration of single slot, lower => faster block production, must be >= 4 -seconds_per_slot=12 -# numer of slots in epoch, lower => faster epoch finalisation, must be >= 4 -slots_per_epoch=6 -# extra genesis gelay, no need to modify, but it should be after all validators/beacon chain starts -genesis_delay=15 -# number of validators in the network -validator_count=8 -chain_id=1337 -# list of addresses to be prefunded in genesis -addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] - -# load test specific configuration -[Load.OCR] -[Load.OCR.Common] -eth_funds = 3 - -[Load.OCR.Load] -test_duration = "3m" -rate_limit_unit_duration = "1m" -rate = 3 -verification_interval = "5s" -verification_timeout = "3m" -ea_change_interval = "5s" - -# soak test specific configuration -[Soak.Common] -chainlink_node_funding = 100 - -[Soak.OCR] -[Soak.OCR.Common] -test_duration="15m" - -[Soak.OCR.Soak] -ocr_version="1" -number_of_contracts=2 -time_between_rounds="1m" \ No newline at end of file diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml new file mode 100644 index 000000000..e69de29bb From b09ca756695e1e9695e76d84bf26c4cd64fb6bc9 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 10:39:45 +0100 Subject: [PATCH 09/39] Added docker creds --- .github/workflows/integration-tests-smoke.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 4f1da6d5a..8ede0208a 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -178,6 +178,7 @@ jobs: test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download cl_repo: ${{ env.CL_ECR }} cl_image_tag: starknet.${{ github.sha }}${{ matrix.image.tag-suffix }} + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} From 70d8fe1130a71370319142a3e5a5ee9eeec5771a Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 11:39:27 +0100 Subject: [PATCH 10/39] Move to toml and refactor --- .github/workflows/integration-tests-smoke.yml | 21 +- integration-tests/common/common.go | 219 ++++------- integration-tests/common/gauntlet_common.go | 48 ++- integration-tests/common/test_common.go | 284 ++++++++------ integration-tests/smoke/ocr2_test.go | 31 +- integration-tests/testconfig/configs_embed.go | 20 + .../testconfig/configs_noembed.go | 15 +- integration-tests/testconfig/default.toml | 27 +- integration-tests/testconfig/ocr2/ocr2.go | 125 +----- integration-tests/testconfig/ocr2/ocr2.toml | 3 + integration-tests/testconfig/testconfig.go | 359 ++++++++++++++++++ ops/gauntlet/gauntlet_starknet.go | 4 + 12 files changed, 716 insertions(+), 440 deletions(-) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 8ede0208a..78ff320cb 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -125,7 +125,9 @@ jobs: TEST_DURATION: 15m NODE_COUNT: 5 CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink - CHAINLINK_VERSION: starknet.${{ github.sha }}${{ matrix.image.tag-suffix }} + CHAINLINK_VERSION: starknet.${{ github.sha }} + INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com + scarb_version: "v2.5.4" permissions: checks: write pull-requests: write @@ -151,19 +153,18 @@ jobs: id: install-scarb shell: bash run: | - wget https://github.com/software-mansion/scarb/releases/download/v2.5.4/scarb-v2.5.4-x86_64-unknown-linux-musl.tar.gz - tar -xvzf scarb-v2.5.4-x86_64-unknown-linux-musl.tar.gz - mv -vf scarb-v2.5.4-x86_64-unknown-linux-musl scarb-build + wget https://github.com/software-mansion/scarb/releases/download/${{ env.scarb_version }}/scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl.tar.gz + tar -xvzf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl.tar.gz + mv -vf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl scarb-build echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH - - name: Build contracts - run: | - cd contracts && scarb --profile release build - name: Generate config overrides run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml [ChainlinkImage] image="${{ env.CHAINLINK_IMAGE }}" - version="${{ github.sha }}" + version="${{ env.CHAINLINK_VERSION }}" + [Network] + selected_networks=["SIMULATED"] EOF # shellcheck disable=SC2002 BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) @@ -174,14 +175,14 @@ jobs: - name: Run Tests ${{ matrix.image.name }} uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: + aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} test_command_to_run: nix develop -c helm repo update && make test-integration-smoke-ci test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download cl_repo: ${{ env.CL_ECR }} cl_image_tag: starknet.${{ github.sha }}${{ matrix.image.tag-suffix }} - aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} token: ${{ secrets.GITHUB_TOKEN }} go_mod_path: ./integration-tests/go.mod QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} - artifacts_location: /home/runner/work/chainlink-starknet/chainlink-starknet/integration-tests/smoke/logs + artifacts_location: /home/runner/work/chainlink-starknet/chainlink-starknet/integration-tests/smoke/logs \ No newline at end of file diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index b36225e96..b99c381eb 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -2,10 +2,9 @@ package common import ( "fmt" - test_env_starknet "github.com/smartcontractkit/chainlink-starknet/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig" "github.com/smartcontractkit/chainlink-starknet/ops/devnet" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" - ctf_test_env "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" mock_adapter "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mock-adapter" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" @@ -26,7 +25,6 @@ import ( common_cfg "github.com/smartcontractkit/chainlink-common/pkg/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" - "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/alias" "github.com/smartcontractkit/chainlink/integration-tests/client" "github.com/smartcontractkit/chainlink/integration-tests/types/config/node" cl "github.com/smartcontractkit/chainlink/v2/core/services/chainlink" @@ -35,188 +33,100 @@ import ( ) var ( - chainName = "starknet" - chainId = "SN_GOERLI" - defaultNodeUrl = "http://starknet:5050" - testnetUrl = "https://starknet-testnet.public.blastapi.io" - DefaultNodeCount = 5 - DefaultTTL = "3h" - DefaultTestDuration = 50 * time.Minute + chainName = "starknet" + chainId = "SN_GOERLI" + DefaultL2RPCInternal = "http://starknet-dev:5000" ) type Common struct { - P2PPort string - ServiceKeyL1 string - ServiceKeyL2 string - ServiceKeyChainlink string - ChainName string - ChainId string - NodeCount int - TTL time.Duration - TestDuration time.Duration - Testnet bool - L2RPCUrl string - MockUrl string - PrivateKey string - Account string - ChainlinkConfig string - Env *environment.Environment - IsK8s bool - K8Config *environment.Config - NodeOpts []test_env.ClNodeOption - DockerEnv *StarknetClusterTestEnv + ChainDetails *ChainDetails + TestEnvDetails *TestEnvDetails + Env *environment.Environment + RPCDetails *RPCDetails + ChainlinkConfig string + TestConfig *testconfig.TestConfig } -type StarknetClusterTestEnv struct { - *test_env.CLClusterTestEnv - Starknet *test_env_starknet.Starknet - Killgrave *ctf_test_env.Killgrave +type ChainDetails struct { + ChainName string + ChainId string } -func New(env string, isK8s bool) *Common { - var err error +type TestEnvDetails struct { + TestDuration time.Duration + K8Config *environment.Config + NodeOpts []test_env.ClNodeOption +} + +type RPCDetails struct { + RPCL1Internal string + RPCL2Internal string + RPCL1External string + RPCL2External string + MockServerUrl string + MockServerEndpoint string + P2PPort string +} + +func New(testConfig *testconfig.TestConfig) *Common { var c *Common - if env == "testnet" { - c = &Common{ - IsK8s: isK8s, - ChainName: chainId, - ChainId: chainId, - L2RPCUrl: testnetUrl, - Testnet: true, - TestDuration: DefaultTestDuration, - } - } else { - c = &Common{ - IsK8s: isK8s, - ChainName: chainName, - ChainId: chainId, - L2RPCUrl: defaultNodeUrl, - TestDuration: DefaultTestDuration, - } - } - // Checking if count of OCR nodes is defined in ENV - nodeCountSet, nodeCountDefined := os.LookupEnv("NODE_COUNT") - if nodeCountDefined && nodeCountSet != "" { - c.NodeCount, err = strconv.Atoi(nodeCountSet) - if err != nil { - panic(fmt.Sprintf("Please define a proper node count for the test: %v", err)) - } - } else { - c.NodeCount = DefaultNodeCount + + duration, err := time.ParseDuration(*testConfig.OCR2.TestDuration) + if err != nil { + panic("Invalid test duration") } - // Checking if TTL env var is set in ENV - ttlValue, ttlDefined := os.LookupEnv("TTL") - if ttlDefined && ttlValue != "" { - duration, err := time.ParseDuration(ttlValue) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } - c.TTL, err = time.ParseDuration(*alias.ShortDur(duration)) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } - } else { - duration, err := time.ParseDuration(DefaultTTL) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } - c.TTL, err = time.ParseDuration(*alias.ShortDur(duration)) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } + c = &Common{ + TestConfig: testConfig, + ChainDetails: &ChainDetails{ + ChainName: chainName, + ChainId: chainId, + }, + TestEnvDetails: &TestEnvDetails{ + TestDuration: duration, + }, + RPCDetails: &RPCDetails{ + P2PPort: "6690", + RPCL2Internal: DefaultL2RPCInternal, + }, } return c } func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) { - c.K8Config = &environment.Config{ - NamespacePrefix: fmt.Sprintf("solana-%s", namespacePrefix), - TTL: c.TTL, + c.TestEnvDetails.K8Config = &environment.Config{ + NamespacePrefix: fmt.Sprintf("starknet-%s", namespacePrefix), + TTL: c.TestEnvDetails.TestDuration, Test: t, } - if c.IsK8s { + if *c.TestConfig.Common.InsideK8s { toml := c.DefaultNodeConfig() tomlString, err := toml.TOMLString() if err != nil { return nil, err } - c.Env = environment.New(c.K8Config). + c.Env = environment.New(c.TestEnvDetails.K8Config). AddHelm(devnet.New(nil)). AddHelm(mock_adapter.New(nil)). AddHelm(chainlink.New(0, map[string]interface{}{ "toml": tomlString, - "replicas": c.NodeCount, + "replicas": c.TestConfig.OCR2.NodeCount, })) } return c, nil } -// getEnv gets the environment variable if it exists and sets it for the remote runner -func getEnv(v string) string { - val := os.Getenv(v) - if val != "" { - os.Setenv(fmt.Sprintf("TEST_%s", v), val) - } - return val -} - -func getNodeCount() int { - // Checking if count of OCR nodes is defined in ENV - nodeCountSet := getEnv("NODE_COUNT") - if nodeCountSet == "" { - nodeCountSet = "4" - } - nodeCount, err := strconv.Atoi(nodeCountSet) - if err != nil { - panic(fmt.Sprintf("Please define a proper node count for the test: %v", err)) - } - return nodeCount -} - -func getTTL() time.Duration { - ttlValue := getEnv("TTL") - if ttlValue == "" { - ttlValue = "72h" - } - duration, err := time.ParseDuration(ttlValue) - if err != nil { - panic(fmt.Sprintf("Please define a proper TTL for the test: %v", err)) - } - t, err := time.ParseDuration(*alias.ShortDur(duration)) - if err != nil { - panic(fmt.Sprintf("Please define a proper TTL for the test: %v", err)) - } - return t -} - -func getTestDuration() time.Duration { - testDurationValue := getEnv("TEST_DURATION") - if testDurationValue == "" { - return time.Duration(time.Minute * 15) - } - duration, err := time.ParseDuration(testDurationValue) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } - t, err := time.ParseDuration(*alias.ShortDur(duration)) - if err != nil { - panic(fmt.Sprintf("Please define a proper duration for the test: %v", err)) - } - return t -} - func (c *Common) DefaultNodeConfig() *cl.Config { starkConfig := config.TOMLConfig{ Enabled: ptr.Ptr(true), - ChainID: ptr.Ptr(c.ChainId), + ChainID: ptr.Ptr(c.ChainDetails.ChainId), Nodes: []*config.Node{ { Name: ptr.Ptr("primary"), - URL: common_cfg.MustParseURL(defaultNodeUrl), + URL: common_cfg.MustParseURL(c.RPCDetails.RPCL2Internal), }, }, } @@ -250,8 +160,6 @@ func (c *Common) SetLocalEnvironment(t *testing.T) { log.Info().Msg("Starting core nodes...") cmd := exec.Command("../../scripts/core.sh") cmd.Env = append(os.Environ(), fmt.Sprintf("CL_CONFIG=%s", c.ChainlinkConfig)) - // out, err := cmd.Output() - // fmt.Println(string(out)) err = cmd.Run() require.NoError(t, err, "Could not start core nodes") log.Info().Msg("Set up local stack complete.") @@ -259,7 +167,7 @@ func (c *Common) SetLocalEnvironment(t *testing.T) { // Set ChainlinkNodeDetails var nodeDetails []*environment.ChainlinkNodeDetail var basePort = 50100 - for i := 0; i < c.NodeCount; i++ { + for i := 0; i < *c.TestConfig.OCR2.NodeCount; i++ { dbLocalIP := fmt.Sprintf("postgresql://postgres:postgres@chainlink.postgres:5432/starknet_test_%d?sslmode=disable", i+1) nodeDetails = append(nodeDetails, &environment.ChainlinkNodeDetail{ ChartName: "unused", @@ -329,7 +237,7 @@ func (c *Common) CreateNodeKeysBundle(nodes []*client.ChainlinkClient) ([]client } peerID := p2pkeys.Data[0].Attributes.PeerID - txKey, _, err := n.CreateTxKey(chainName, c.ChainId) + txKey, _, err := n.CreateTxKey(chainName, c.ChainDetails.ChainId) if err != nil { return nil, err } @@ -348,12 +256,12 @@ func (c *Common) CreateNodeKeysBundle(nodes []*client.ChainlinkClient) ([]client } // CreateJobsForContract Creates and sets up the boostrap jobs as well as OCR jobs -func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test_env.Killgrave, observationSource string, juelsPerFeeCoinSource string, ocrControllerAddress string, accountAddresses []string) error { +func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource string, juelsPerFeeCoinSource string, ocrControllerAddress string, accountAddresses []string) error { // Define node[0] as bootstrap node cc.bootstrapPeers = []client.P2PData{ { InternalIP: cc.ChainlinkNodes[0].InternalIP(), - InternalPort: c.P2PPort, + InternalPort: c.RPCDetails.P2PPort, PeerID: cc.NKeys[0].PeerID, }, } @@ -362,7 +270,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test bootstrapRelayConfig := job.JSONConfig{ "nodeName": fmt.Sprintf("starknet-OCRv2-%s-%s", "node", uuid.New().String()), "accountAddress": fmt.Sprintf("%s", accountAddresses[0]), - "chainID": fmt.Sprintf("%s", c.ChainId), + "chainID": fmt.Sprintf("%s", c.ChainDetails.ChainId), } oracleSpec := job.OCR2OracleSpec{ @@ -377,7 +285,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test JobType: "bootstrap", OCR2OracleSpec: oracleSpec, } - + fmt.Println(jobSpec.String()) _, _, err := cc.ChainlinkNodes[0].CreateJob(jobSpec) if err != nil { return err @@ -391,7 +299,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test sourceValueBridge := &client.BridgeTypeAttributes{ Name: "mockserver-bridge", - URL: fmt.Sprintf("%s/%s", mockserver.InternalEndpoint, strings.TrimPrefix("mockserver-bridge", "/")), + URL: fmt.Sprintf("%s/%s", c.RPCDetails.MockServerEndpoint, strings.TrimPrefix(c.RPCDetails.MockServerUrl, "/")), } // Setting up job specs @@ -399,7 +307,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test if nIdx == 0 { continue } - _, err := n.CreateBridge(sourceValueBridge) + err := n.MustCreateBridge(sourceValueBridge) if err != nil { return err } @@ -429,6 +337,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, mockserver *ctf_test OCR2OracleSpec: oracleSpec, ObservationSource: observationSource, } + fmt.Println(jobSpec.String()) _, err = n.MustCreateJob(jobSpec) if err != nil { return err diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index f4556b77c..5ed4479d2 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -14,14 +14,14 @@ var ( ) func (m *OCRv2TestState) fundNodes() ([]string, error) { - l := utils.GetTestLogger(m.T) + l := utils.GetTestLogger(m.TestConfig.T) var nAccounts []string var err error for _, key := range m.GetNodeKeys() { if key.TXKey.Data.Attributes.StarkKey == "" { return nil, errors.New("stark key can't be empty") } - nAccount, err = m.Sg.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) + nAccount, err = m.Clients.GauntletClient.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) if err != nil { return nil, err } @@ -32,27 +32,35 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { return nil, err } - if m.Common.Testnet { + if *m.Common.TestConfig.Common.Network == "testnet" { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = m.Sg.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node + _, err = m.Clients.GauntletClient.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node if err != nil { return nil, err } } - } else { // The starknet provided mint method does not work so we send a req directly for _, key := range nAccounts { - res, err := m.Resty.R().SetBody(map[string]any{ + res, err := m.TestConfig.Resty.R().SetBody(map[string]any{ + "address": key, + "amount": 900000000000000000, + }).Post("/mint") + if err != nil { + return nil, err + } + l.Info().Msg(fmt.Sprintf("Funding account (WEI): %s", string(res.Body()))) + res, err = m.TestConfig.Resty.R().SetBody(map[string]any{ "address": key, "amount": 900000000000000000, + "unit": "FRI", }).Post("/mint") - m.L.Info().Msg(fmt.Sprintf("Funding account: %s", string(res.Body()))) if err != nil { return nil, err } + l.Info().Msg(fmt.Sprintf("Funding account (FRI): %s", string(res.Body()))) } } @@ -61,11 +69,11 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { func (m *OCRv2TestState) deployLinkToken() error { var err error - m.LinkTokenAddr, err = m.Sg.DeployLinkTokenContract() + m.Contracts.LinkTokenAddr, err = m.Clients.GauntletClient.DeployLinkTokenContract() if err != nil { return err } - err = os.Setenv("LINK", m.LinkTokenAddr) + err = os.Setenv("LINK", m.Contracts.LinkTokenAddr) if err != nil { return err } @@ -74,11 +82,11 @@ func (m *OCRv2TestState) deployLinkToken() error { func (m *OCRv2TestState) deployAccessController() error { var err error - m.AccessControllerAddr, err = m.Sg.DeployAccessControllerContract() + m.Contracts.AccessControllerAddr, err = m.Clients.GauntletClient.DeployAccessControllerContract() if err != nil { return err } - err = os.Setenv("BILLING_ACCESS_CONTROLLER", m.AccessControllerAddr) + err = os.Setenv("BILLING_ACCESS_CONTROLLER", m.Contracts.AccessControllerAddr) if err != nil { return err } @@ -95,17 +103,17 @@ func (m *OCRv2TestState) setConfigDetails(ocrAddress string) error { if err != nil { return err } - _, err = m.Sg.SetConfigDetails(string(parsedConfig), ocrAddress) + _, err = m.Clients.GauntletClient.SetConfigDetails(string(parsedConfig), ocrAddress) return err } func (m *OCRv2TestState) DeployGauntlet(minSubmissionValue int64, maxSubmissionValue int64, decimals int, name string, observationPaymentGjuels int64, transmissionPaymentGjuels int64) error { - err := m.Sg.InstallDependencies() + err := m.Clients.GauntletClient.InstallDependencies() if err != nil { return err } - m.AccountAddresses, err = m.fundNodes() + m.Clients.ChainlinkClient.AccountAddresses, err = m.fundNodes() if err != nil { return err } @@ -120,29 +128,29 @@ func (m *OCRv2TestState) DeployGauntlet(minSubmissionValue int64, maxSubmissionV return err } - m.OCRAddr, err = m.Sg.DeployOCR2ControllerContract(minSubmissionValue, maxSubmissionValue, decimals, name, m.LinkTokenAddr) + m.Contracts.OCRAddr, err = m.Clients.GauntletClient.DeployOCR2ControllerContract(minSubmissionValue, maxSubmissionValue, decimals, name, m.Contracts.LinkTokenAddr) if err != nil { return err } - m.ProxyAddr, err = m.Sg.DeployOCR2ProxyContract(m.OCRAddr) + m.Contracts.ProxyAddr, err = m.Clients.GauntletClient.DeployOCR2ProxyContract(m.Contracts.OCRAddr) if err != nil { return err } - _, err = m.Sg.AddAccess(m.OCRAddr, m.ProxyAddr) + _, err = m.Clients.GauntletClient.AddAccess(m.Contracts.OCRAddr, m.Contracts.ProxyAddr) if err != nil { return err } - _, err = m.Sg.MintLinkToken(m.LinkTokenAddr, m.OCRAddr, "100000000000000000000") + _, err = m.Clients.GauntletClient.MintLinkToken(m.Contracts.LinkTokenAddr, m.Contracts.OCRAddr, "100000000000000000000") if err != nil { return err } - _, err = m.Sg.SetOCRBilling(observationPaymentGjuels, transmissionPaymentGjuels, m.OCRAddr) + _, err = m.Clients.GauntletClient.SetOCRBilling(observationPaymentGjuels, transmissionPaymentGjuels, m.Contracts.OCRAddr) if err != nil { return err } - err = m.setConfigDetails(m.OCRAddr) + err = m.setConfigDetails(m.Contracts.OCRAddr) return err } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 224c07f8e..a87d04a23 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -5,16 +5,16 @@ import ( "fmt" starknetdevnet "github.com/NethermindEth/starknet.go/devnet" "github.com/go-resty/resty/v2" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" "github.com/smartcontractkit/chainlink-common/pkg/logger" test_env_ctf "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" - "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "net/http" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" test_env_starknet "github.com/smartcontractkit/chainlink-starknet/integration-tests/docker/test_env" + "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig" "github.com/smartcontractkit/chainlink-testing-framework/logging" - "github.com/smartcontractkit/chainlink/integration-tests/testconfig" + "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "math/big" "testing" "time" @@ -37,55 +37,93 @@ var ( dumpPath = "/dumps/dump.pkl" ) +// OCRv2TestState Main testing state struct type OCRv2TestState struct { - Account string - PrivateKey string - StarknetClient *starknet.Client - DevnetClient *starknetdevnet.DevNet - Killgrave *test_env_ctf.Killgrave - ChainlinkNodesK8s []*client.ChainlinkK8sClient - Cc *ChainlinkClient - OCR2Client *ocr2.Client - Sg *gauntlet.StarknetGauntlet - L1RPCUrl string - Common *Common - AccountAddresses []string + Account *AccountDetails + Clients *Clients + ChainlinkNodesK8s []*client.ChainlinkK8sClient + Common *Common + TestConfig *TestConfig + Contracts *Contracts +} + +// AccountDetails for deployment and funding +type AccountDetails struct { + Account string + PrivateKey string +} + +// Clients to access internal methods +type Clients struct { + StarknetClient *starknet.Client + DevnetClient *starknetdevnet.DevNet + KillgraveClient *test_env_ctf.Killgrave + OCR2Client *ocr2.Client + ChainlinkClient *ChainlinkClient + GauntletClient *gauntlet.StarknetGauntlet + DockerEnv *StarknetClusterTestEnv +} + +// Contracts to store current deployed contract state +type Contracts struct { LinkTokenAddr string OCRAddr string AccessControllerAddr string ProxyAddr string ObservationSource string JuelsPerFeeCoinSource string - T *testing.T - L zerolog.Logger - TestConfig *testconfig.TestConfig - Resty *resty.Client - err error } +// ChainlinkClient core node configs type ChainlinkClient struct { - NKeys []client.NodeKeysBundle - ChainlinkNodes []*client.ChainlinkClient - bTypeAttr *client.BridgeTypeAttributes - bootstrapPeers []client.P2PData + NKeys []client.NodeKeysBundle + ChainlinkNodes []*client.ChainlinkClient + bTypeAttr *client.BridgeTypeAttributes + bootstrapPeers []client.P2PData + AccountAddresses []string } -func NewOCRv2State(t *testing.T, env string, isK8s bool, namespacePrefix string, testConfig *testconfig.TestConfig) (*OCRv2TestState, error) { - c, err := New(env, isK8s).Default(t, namespacePrefix) +type StarknetClusterTestEnv struct { + *test_env.CLClusterTestEnv + Starknet *test_env_starknet.Starknet + Killgrave *test_env_ctf.Killgrave +} + +type TestConfig struct { + T *testing.T + L zerolog.Logger + TestConfig *testconfig.TestConfig + Resty *resty.Client + err error +} + +func NewOCRv2State(t *testing.T, namespacePrefix string, testConfig *testconfig.TestConfig) (*OCRv2TestState, error) { + c, err := New(testConfig).Default(t, namespacePrefix) if err != nil { return nil, err } state := &OCRv2TestState{ - Common: c, - T: t, - L: log.Logger, - TestConfig: testConfig, - Cc: &ChainlinkClient{}, - StarknetClient: &starknet.Client{}, + Account: &AccountDetails{}, + Clients: &Clients{ + ChainlinkClient: &ChainlinkClient{}, + }, + Common: c, + TestConfig: &TestConfig{ + T: t, + L: log.Logger, + TestConfig: testConfig, + Resty: nil, + err: nil, + }, + Contracts: &Contracts{}, } - if state.T != nil { - state.L = logging.GetTestLogger(state.T) + // Setting default job configs + state.Contracts.ObservationSource = state.GetDefaultObservationSource() + state.Contracts.JuelsPerFeeCoinSource = state.GetDefaultJuelsPerFeeCoinSource() + + if state.TestConfig.T != nil { + state.TestConfig.L = logging.GetTestLogger(state.TestConfig.T) } return state, nil @@ -93,76 +131,95 @@ func NewOCRv2State(t *testing.T, env string, isK8s bool, namespacePrefix string, // DeployCluster Deploys and sets up config of the environment and nodes func (m *OCRv2TestState) DeployCluster() { - if m.Common.IsK8s { + // When running soak we need to use K8S + if *m.Common.TestConfig.Common.InsideK8s { m.DeployEnv() - } else { + // Setting RPC details + m.Common.RPCDetails.RPCL2External = m.Common.Env.URLs["starknet-dev"][0] + m.Common.RPCDetails.MockServerEndpoint = m.Common.Env.URLs["qa_mock_adapter_internal"][0] + m.Common.RPCDetails.MockServerUrl = "five" + + } else { // Otherwise use docker env, err := test_env.NewTestEnv() - require.NoError(m.T, err) + require.NoError(m.TestConfig.T, err) stark := test_env_starknet.NewStarknet([]string{env.Network.Name}) err = stark.StartContainer() - require.NoError(m.T, err) - m.Common.L2RPCUrl = stark.ExternalHttpUrl - m.Resty = resty.New().SetBaseURL(m.Common.L2RPCUrl) + require.NoError(m.TestConfig.T, err) + + // Setting RPC details + m.Common.RPCDetails.RPCL2External = stark.ExternalHttpUrl + m.Common.RPCDetails.RPCL2Internal = stark.InternalHttpUrl + + // Creating docker containers b, err := test_env.NewCLTestEnvBuilder(). WithNonEVM(). - WithTestInstance(m.T). - WithTestConfig(m.TestConfig). + WithTestInstance(m.TestConfig.T). + WithTestConfig(m.TestConfig.TestConfig). WithMockAdapter(). WithCLNodeConfig(m.Common.DefaultNodeConfig()). - WithCLNodes(m.Common.NodeCount). - WithCLNodeOptions(m.Common.NodeOpts...). + WithCLNodes(*m.Common.TestConfig.OCR2.NodeCount). + WithCLNodeOptions(m.Common.TestEnvDetails.NodeOpts...). WithStandardCleanup(). WithTestEnv(env) - require.NoError(m.T, err) + require.NoError(m.TestConfig.T, err) env, err = b.Build() - require.NoError(m.T, err) - m.Common.DockerEnv = &StarknetClusterTestEnv{ + require.NoError(m.TestConfig.T, err) + m.Clients.DockerEnv = &StarknetClusterTestEnv{ CLClusterTestEnv: env, Starknet: stark, Killgrave: env.MockAdapter, } - m.Killgrave = env.MockAdapter + + // Setting up Mock adapter + m.Clients.KillgraveClient = env.MockAdapter + m.Common.RPCDetails.MockServerEndpoint = m.Clients.KillgraveClient.InternalEndpoint + m.Common.RPCDetails.MockServerUrl = "mockserver-bridge" + err = m.Clients.KillgraveClient.SetAdapterBasedIntValuePath("/mockserver-bridge", []string{http.MethodGet, http.MethodPost}, 10) + require.NoError(m.TestConfig.T, err, "Failed to set mock adapter value") } - m.ObservationSource = m.GetDefaultObservationSource() - m.JuelsPerFeeCoinSource = m.GetDefaultJuelsPerFeeCoinSource() + m.TestConfig.Resty = resty.New().SetBaseURL(m.Common.RPCDetails.RPCL2External) m.SetupClients() - if m.Common.IsK8s { - m.Cc.NKeys, m.err = m.Common.CreateNodeKeysBundle(m.GetChainlinkNodes()) - require.NoError(m.T, m.err) + if *m.Common.TestConfig.Common.InsideK8s { + m.Clients.ChainlinkClient.ChainlinkNodes = m.GetChainlinkNodes() + m.Clients.ChainlinkClient.NKeys, m.TestConfig.err = m.Common.CreateNodeKeysBundle(m.Clients.ChainlinkClient.ChainlinkNodes) + require.NoError(m.TestConfig.T, m.TestConfig.err) } else { - m.Cc.NKeys, m.err = m.Common.CreateNodeKeysBundle(m.Common.DockerEnv.ClCluster.NodeAPIs()) - require.NoError(m.T, m.err) + m.Clients.ChainlinkClient.NKeys, m.TestConfig.err = m.Common.CreateNodeKeysBundle(m.Clients.DockerEnv.ClCluster.NodeAPIs()) + require.NoError(m.TestConfig.T, m.TestConfig.err) } lggr := logger.Nop() - m.StarknetClient, m.err = starknet.NewClient(m.Common.ChainId, m.Common.L2RPCUrl, lggr, &rpcRequestTimeout) - require.NoError(m.T, m.err, "Creating starknet client should not fail") - m.OCR2Client, m.err = ocr2.NewClient(m.StarknetClient, lggr) - require.NoError(m.T, m.err, "Creating ocr2 client should not fail") - if !m.Common.Testnet { + m.Clients.StarknetClient, m.TestConfig.err = starknet.NewClient(m.Common.ChainDetails.ChainId, m.Common.RPCDetails.RPCL2External, lggr, &rpcRequestTimeout) + require.NoError(m.TestConfig.T, m.TestConfig.err, "Creating starknet client should not fail") + m.Clients.OCR2Client, m.TestConfig.err = ocr2.NewClient(m.Clients.StarknetClient, lggr) + require.NoError(m.TestConfig.T, m.TestConfig.err, "Creating ocr2 client should not fail") + + // If we are using devnet fetch the default keys + if *m.Common.TestConfig.Common.Network == "localnet" { // fetch predeployed account 0 to use as funder - m.DevnetClient = starknetdevnet.NewDevNet(m.Common.L2RPCUrl) - accounts, err := m.DevnetClient.Accounts() - require.NoError(m.T, err) + m.Clients.DevnetClient = starknetdevnet.NewDevNet(m.Common.RPCDetails.RPCL2External) + accounts, err := m.Clients.DevnetClient.Accounts() + require.NoError(m.TestConfig.T, err) account := accounts[0] - m.Account = account.Address - m.PrivateKey = account.PrivateKey + m.Account.Account = account.Address + m.Account.PrivateKey = account.PrivateKey } } // DeployEnv Deploys the environment func (m *OCRv2TestState) DeployEnv() { err := m.Common.Env.Run() - require.NoError(m.T, err) + require.NoError(m.TestConfig.T, err) } // SetupClients Sets up the starknet client func (m *OCRv2TestState) SetupClients() { - if m.Common.IsK8s { - m.ChainlinkNodesK8s, m.err = client.ConnectChainlinkNodes(m.Common.Env) - require.NoError(m.T, m.err) + + if *m.Common.TestConfig.Common.InsideK8s { + m.ChainlinkNodesK8s, m.TestConfig.err = client.ConnectChainlinkNodes(m.Common.Env) + require.NoError(m.TestConfig.T, m.TestConfig.err) } else { - m.Cc.ChainlinkNodes = m.Common.DockerEnv.ClCluster.NodeAPIs() + m.Clients.ChainlinkClient.ChainlinkNodes = m.Clients.DockerEnv.ClCluster.NodeAPIs() } } @@ -173,10 +230,10 @@ func (m *OCRv2TestState) LoadOCR2Config() (*ops.OCR2Config, error) { var peerIds []string var txKeys []string var cfgKeys []string - for i, key := range m.Cc.NKeys { + for i, key := range m.Clients.ChainlinkClient.NKeys { offChaiNKeys = append(offChaiNKeys, key.OCR2Key.Data.Attributes.OffChainPublicKey) peerIds = append(peerIds, key.PeerID) - txKeys = append(txKeys, m.AccountAddresses[i]) + txKeys = append(txKeys, m.Clients.ChainlinkClient.AccountAddresses[i]) onChaiNKeys = append(onChaiNKeys, key.OCR2Key.Data.Attributes.OnChainPublicKey) cfgKeys = append(cfgKeys, key.OCR2Key.Data.Attributes.ConfigPublicKey) } @@ -192,25 +249,30 @@ func (m *OCRv2TestState) LoadOCR2Config() (*ops.OCR2Config, error) { } func (m *OCRv2TestState) SetUpNodes() { - err := m.Common.CreateJobsForContract(m.GetChainlinkClient(), m.Killgrave, m.ObservationSource, m.JuelsPerFeeCoinSource, m.OCRAddr, m.AccountAddresses) - require.NoError(m.T, err, "Creating jobs should not fail") + err := m.Common.CreateJobsForContract(m.GetChainlinkClient(), m.Contracts.ObservationSource, m.Contracts.JuelsPerFeeCoinSource, m.Contracts.OCRAddr, m.Clients.ChainlinkClient.AccountAddresses) + require.NoError(m.TestConfig.T, err, "Creating jobs should not fail") } // GetNodeKeys Returns the node key bundles func (m *OCRv2TestState) GetNodeKeys() []client.NodeKeysBundle { - return m.Cc.NKeys + return m.Clients.ChainlinkClient.NKeys } func (m *OCRv2TestState) GetChainlinkNodes() []*client.ChainlinkClient { - return m.Cc.ChainlinkNodes + // retrieve client from K8s client + var chainlinkNodes []*client.ChainlinkClient + for i := range m.ChainlinkNodesK8s { + chainlinkNodes = append(chainlinkNodes, m.ChainlinkNodesK8s[i].ChainlinkClient) + } + return chainlinkNodes } func (m *OCRv2TestState) GetChainlinkClient() *ChainlinkClient { - return m.Cc + return m.Clients.ChainlinkClient } func (m *OCRv2TestState) SetBridgeTypeAttrs(attr *client.BridgeTypeAttributes) { - m.Cc.bTypeAttr = attr + m.Clients.ChainlinkClient.bTypeAttr = attr } func (m *OCRv2TestState) GetDefaultObservationSource() string { @@ -239,25 +301,25 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { var positive bool // validate balance in aggregator - linkContractAddress, err := starknetutils.HexToFelt(m.LinkTokenAddr) + linkContractAddress, err := starknetutils.HexToFelt(m.Contracts.LinkTokenAddr) if err != nil { return err } - contractAddress, err := starknetutils.HexToFelt(m.OCRAddr) + contractAddress, err := starknetutils.HexToFelt(m.Contracts.OCRAddr) if err != nil { return err } - resLINK, errLINK := m.StarknetClient.CallContract(ctx, starknet.CallOps{ + resLINK, errLINK := m.Clients.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: linkContractAddress, Selector: starknetutils.GetSelectorFromNameFelt("balance_of"), Calldata: []*felt.Felt{contractAddress}, }) - require.NoError(m.T, errLINK, "Reader balance from LINK contract should not fail", "err", errLINK) - resAgg, errAgg := m.StarknetClient.CallContract(ctx, starknet.CallOps{ + require.NoError(m.TestConfig.T, errLINK, "Reader balance from LINK contract should not fail", "err", errLINK) + resAgg, errAgg := m.Clients.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: contractAddress, Selector: starknetutils.GetSelectorFromNameFelt("link_available_for_payment"), }) - require.NoError(m.T, errAgg, "link_available_for_payment should not fail", "err", errAgg) + require.NoError(m.TestConfig.T, errAgg, "link_available_for_payment should not fail", "err", errAgg) balLINK := resLINK[0].BigInt(big.NewInt(0)) balAgg := resAgg[1].BigInt(big.NewInt(0)) isNegative := resAgg[0].BigInt(big.NewInt(0)) @@ -265,15 +327,13 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { balAgg = new(big.Int).Neg(balAgg) } - assert.Equal(m.T, balLINK.Cmp(big.NewInt(0)), 1, "Aggregator should have non-zero balance") - assert.GreaterOrEqual(m.T, balLINK.Cmp(balAgg), 0, "Aggregator payment balance should be <= actual LINK balance") + assert.Equal(m.TestConfig.T, balLINK.Cmp(big.NewInt(0)), 1, "Aggregator should have non-zero balance") + assert.GreaterOrEqual(m.TestConfig.T, balLINK.Cmp(balAgg), 0, "Aggregator payment balance should be <= actual LINK balance") - err = m.Killgrave.SetAdapterBasedIntValuePath("/mockserver-bridge", []string{http.MethodGet, http.MethodPost}, 10) - require.NoError(m.T, err, "Failed to set mock adapter value") - for start := time.Now(); time.Since(start) < m.Common.TestDuration; { - m.L.Info().Msg(fmt.Sprintf("Elapsed time: %s, Round wait: %s ", time.Since(start), m.Common.TestDuration)) - res, err2 := m.OCR2Client.LatestTransmissionDetails(ctx, contractAddress) - require.NoError(m.T, err2, "Failed to get latest transmission details") + for start := time.Now(); time.Since(start) < m.Common.TestEnvDetails.TestDuration; { + m.TestConfig.L.Info().Msg(fmt.Sprintf("Elapsed time: %s, Round wait: %s ", time.Since(start), m.Common.TestEnvDetails.TestDuration)) + res, err2 := m.Clients.OCR2Client.LatestTransmissionDetails(ctx, contractAddress) + require.NoError(m.TestConfig.T, err2, "Failed to get latest transmission details") // end condition: enough rounds have occurred if !isSoak && increasing >= rounds && positive { break @@ -281,7 +341,7 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { // end condition: rounds have been stuck if stuck && stuckCount > 50 { - m.L.Debug().Msg("failing to fetch transmissions means blockchain may have stopped") + m.TestConfig.L.Debug().Msg("failing to fetch transmissions means blockchain may have stopped") break } @@ -289,10 +349,10 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { time.Sleep(5 * time.Second) if err != nil { - m.L.Error().Msg(fmt.Sprintf("Transmission Error: %+v", err)) + m.TestConfig.L.Error().Msg(fmt.Sprintf("Transmission Error: %+v", err)) continue } - m.L.Info().Msg(fmt.Sprintf("Transmission Details: %+v", res)) + m.TestConfig.L.Info().Msg(fmt.Sprintf("Transmission Details: %+v", res)) // continue if no changes if res.Epoch == 0 && res.Round == 0 { @@ -305,21 +365,21 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { // if changes from zero values set (should only initially) if res.Epoch > 0 && details.Epoch == 0 { if !isSoak { - assert.Greater(m.T, res.Epoch, details.Epoch) - assert.GreaterOrEqual(m.T, res.Round, details.Round) - assert.NotEqual(m.T, ansCmp, 0) // assert changed from 0 - assert.NotEqual(m.T, res.Digest, details.Digest) - assert.Equal(m.T, details.LatestTimestamp.Before(res.LatestTimestamp), true) + assert.Greater(m.TestConfig.T, res.Epoch, details.Epoch) + assert.GreaterOrEqual(m.TestConfig.T, res.Round, details.Round) + assert.NotEqual(m.TestConfig.T, ansCmp, 0) // assert changed from 0 + assert.NotEqual(m.TestConfig.T, res.Digest, details.Digest) + assert.Equal(m.TestConfig.T, details.LatestTimestamp.Before(res.LatestTimestamp), true) } details = res continue } // check increasing rounds if !isSoak { - assert.Equal(m.T, res.Digest, details.Digest, "Config digest should not change") + assert.Equal(m.TestConfig.T, res.Digest, details.Digest, "Config digest should not change") } else { if res.Digest != details.Digest { - m.L.Error().Msg(fmt.Sprintf("Config digest should not change, expected %s got %s", details.Digest, res.Digest)) + m.TestConfig.L.Error().Msg(fmt.Sprintf("Config digest should not change, expected %s got %s", details.Digest, res.Digest)) } } if (res.Epoch > details.Epoch || (res.Epoch == details.Epoch && res.Round > details.Round)) && details.LatestTimestamp.Before(res.LatestTimestamp) { @@ -337,31 +397,31 @@ func (m *OCRv2TestState) ValidateRounds(rounds int, isSoak bool) error { } } if !isSoak { - assert.GreaterOrEqual(m.T, increasing, rounds, "Round + epochs should be increasing") - assert.Equal(m.T, positive, true, "Positive value should have been submitted") - assert.Equal(m.T, stuck, false, "Round + epochs should not be stuck") + assert.GreaterOrEqual(m.TestConfig.T, increasing, rounds, "Round + epochs should be increasing") + assert.Equal(m.TestConfig.T, positive, true, "Positive value should have been submitted") + assert.Equal(m.TestConfig.T, stuck, false, "Round + epochs should not be stuck") } // Test proxy reading // TODO: would be good to test proxy switching underlying feeds - proxyAddress, err := starknetutils.HexToFelt(m.ProxyAddr) + proxyAddress, err := starknetutils.HexToFelt(m.Contracts.ProxyAddr) if err != nil { return err } - roundDataRaw, err := m.StarknetClient.CallContract(ctx, starknet.CallOps{ + roundDataRaw, err := m.Clients.StarknetClient.CallContract(ctx, starknet.CallOps{ ContractAddress: proxyAddress, Selector: starknetutils.GetSelectorFromNameFelt("latest_round_data"), }) if !isSoak { - require.NoError(m.T, err, "Reading round data from proxy should not fail") - assert.Equal(m.T, len(roundDataRaw), 5, "Round data from proxy should match expected size") + require.NoError(m.TestConfig.T, err, "Reading round data from proxy should not fail") + assert.Equal(m.TestConfig.T, len(roundDataRaw), 5, "Round data from proxy should match expected size") } valueBig := roundDataRaw[1].BigInt(big.NewInt(0)) - require.NoError(m.T, err) + require.NoError(m.TestConfig.T, err) value := valueBig.Int64() if value < 0 { - assert.Equal(m.T, value, int64(5), "Reading from proxy should return correct value") + assert.Equal(m.TestConfig.T, value, int64(5), "Reading from proxy should return correct value") } return nil diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index fd7e91557..846dc782e 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -3,16 +3,17 @@ package smoke_test import ( "flag" "fmt" + "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" + tc "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig" "github.com/smartcontractkit/chainlink-starknet/ops/gauntlet" "github.com/smartcontractkit/chainlink-starknet/ops/utils" - "maps" - "testing" - - "github.com/smartcontractkit/chainlink-starknet/integration-tests/common" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink/integration-tests/actions" "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" - tc "github.com/smartcontractkit/chainlink/integration-tests/testconfig" "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" + "maps" + "testing" ) var ( @@ -35,6 +36,7 @@ func TestOCRBasicNew(t *testing.T) { // "CL_SOLANA_CMD": "chainlink-solana", //}}, } { + config, err := tc.GetConfig("Smoke", tc.OCR2) if err != nil { t.Fatal(err) @@ -43,10 +45,19 @@ func TestOCRBasicNew(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() logging.Init() - state, err := common.NewOCRv2State(t, "localnet", false, "test", &config) + state, err := common.NewOCRv2State(t, "test", &config) require.NoError(t, err, "Could not setup the ocrv2 state") + + // Cleanup K8s + if *config.Common.InsideK8s { + t.Cleanup(func() { + if err := actions.TeardownSuite(t, state.Common.Env, state.ChainlinkNodesK8s, nil, zapcore.PanicLevel, nil); err != nil { + state.TestConfig.L.Error().Err(err).Msg("Error tearing down environment") + } + }) + } if len(test.env) > 0 { - state.Common.NodeOpts = append(state.Common.NodeOpts, func(n *test_env.ClNode) { + state.Common.TestEnvDetails.NodeOpts = append(state.Common.TestEnvDetails.NodeOpts, func(n *test_env.ClNode) { if n.ContainerEnvs == nil { n.ContainerEnvs = map[string]string{} } @@ -54,15 +65,15 @@ func TestOCRBasicNew(t *testing.T) { }) } state.DeployCluster() - state.Sg, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) - err = state.Sg.SetupNetwork(state.Common.L2RPCUrl, state.Account, state.PrivateKey) + state.Clients.GauntletClient, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) + err = state.Clients.GauntletClient.SetupNetwork(state.Common.RPCDetails.RPCL2External, state.Account.Account, state.Account.PrivateKey) require.NoError(t, err, "Setting up gauntlet network should not fail") err = state.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) require.NoError(t, err, "Deploying contracts should not fail") state.SetUpNodes() - err = state.ValidateRounds(10, false) + err = state.ValidateRounds(*config.OCR2.Smoke.NumberOfRounds, false) require.NoError(t, err, "Validating round should not fail") }) } diff --git a/integration-tests/testconfig/configs_embed.go b/integration-tests/testconfig/configs_embed.go index e6950abe2..67e954ff5 100644 --- a/integration-tests/testconfig/configs_embed.go +++ b/integration-tests/testconfig/configs_embed.go @@ -1 +1,21 @@ +//go:build embed +// +build embed + package testconfig + +import "embed" + +//go:embed default.toml +//go:embed automation/automation.toml +//go:embed functions/functions.toml +//go:embed keeper/keeper.toml +//go:embed log_poller/log_poller.toml +//go:embed node/node.toml +//go:embed ocr/ocr.toml +//go:embed vrfv2/vrfv2.toml +//go:embed vrfv2plus/vrfv2plus.toml +var embeddedConfigsFs embed.FS + +func init() { + areConfigsEmbedded = true +} diff --git a/integration-tests/testconfig/configs_noembed.go b/integration-tests/testconfig/configs_noembed.go index 67e954ff5..95572c4a0 100644 --- a/integration-tests/testconfig/configs_noembed.go +++ b/integration-tests/testconfig/configs_noembed.go @@ -1,21 +1,12 @@ -//go:build embed -// +build embed +//go:build !embed +// +build !embed package testconfig import "embed" -//go:embed default.toml -//go:embed automation/automation.toml -//go:embed functions/functions.toml -//go:embed keeper/keeper.toml -//go:embed log_poller/log_poller.toml -//go:embed node/node.toml -//go:embed ocr/ocr.toml -//go:embed vrfv2/vrfv2.toml -//go:embed vrfv2plus/vrfv2plus.toml var embeddedConfigsFs embed.FS func init() { - areConfigsEmbedded = true + areConfigsEmbedded = false } diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index a65c23d70..10eb6f84e 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -7,16 +7,21 @@ log_producer_timeout="10s" log_producer_retry_limit=10 [Network] -selected_networks=["simulated"] +selected_networks=["SIMULATED"] # Not needed for Starknet but mandatory from CTF -[PrivateEthereumNetwork] -consensus_type="pow" -execution_layer="geth" +[Network.RpcHttpUrls] +simulated = ["http://127.0.0.1"] # Not needed for Starknet but mandatory from CTF -[PrivateEthereumNetwork.EthereumChainConfig] -seconds_per_slot=3 -slots_per_epoch=2 -genesis_delay=15 -validator_count=4 -chain_id=1337 -addresses_to_fund=["0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"] \ No newline at end of file +[Network.RpcWsUrls] +simulated = ["wss://127.0.0.1"] # Not needed for Starknet but mandatory from CTF + +[Common] +inside_k8 = false +network = "localnet" + +[OCR2] +node_count = 6 +test_duration = "30m" + +[OCR2.Smoke] +number_of_rounds = 10 diff --git a/integration-tests/testconfig/ocr2/ocr2.go b/integration-tests/testconfig/ocr2/ocr2.go index d5cc48ea5..30438ba38 100644 --- a/integration-tests/testconfig/ocr2/ocr2.go +++ b/integration-tests/testconfig/ocr2/ocr2.go @@ -2,135 +2,40 @@ package ocr2 import ( "errors" - - "github.com/smartcontractkit/chainlink-testing-framework/blockchain" ) type Config struct { - Soak *SoakConfig `toml:"Soak"` - Load *Load `toml:"Load"` - Volume *Volume `toml:"Volume"` - Common *Common `toml:"Common"` + Smoke *SmokeConfig `toml:"Smoke"` + NodeCount *int `toml:"node_count"` + TestDuration *string `toml:"test_duration"` } func (o *Config) Validate() error { - if o.Common != nil { - if err := o.Common.Validate(); err != nil { - return err - } - } - if o.Soak != nil { - if err := o.Soak.Validate(); err != nil { - return err - } - } - if o.Volume != nil { - if err := o.Volume.Validate(); err != nil { - return err - } - } - return nil -} - -type Common struct { - ETHFunds *int `toml:"eth_funds"` - TestDuration *blockchain.StrDuration `toml:"test_duration"` -} - -func (o *Common) Validate() error { - if o.ETHFunds != nil && *o.ETHFunds < 0 { - return errors.New("eth_funds must be set and cannot be negative") + if o.NodeCount != nil && *o.NodeCount < 3 { + return errors.New("node_count must be set and cannot be less than 3") } - return nil -} - -type Load struct { - Rate *int64 `toml:"rate"` - RequestsPerUnit *int `toml:"requests_per_unit"` - RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` - VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` - VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` - EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` - TestDuration *blockchain.StrDuration `toml:"test_duration"` -} -func (o *Load) Validate() error { if o.TestDuration == nil { - return errors.New("load test duration must be set") - } - if o.Rate == nil || *o.Rate <= 0 { - return errors.New("rate must be set and be a positive integer") - } - if o.RequestsPerUnit == nil || *o.RequestsPerUnit <= 0 { - return errors.New("vu_requests_per_unit must be set and be a positive integer") - } - if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { - return errors.New("rate_limit_unit_duration must be set and be a positive integer") + return errors.New("test_duration must be set") } - if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { - return errors.New("verification_interval must be set and be a positive integer") - } - if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { - return errors.New("verification_timeout must be set and be a positive integer") - } - if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { - return errors.New("ea_change_interval must be set and be a positive integer") - } - - return nil -} - -type Volume struct { - Rate *int64 `toml:"rate"` - VURequestsPerUnit *int `toml:"vu_requests_per_unit"` - RateLimitUnitDuration *blockchain.StrDuration `toml:"rate_limit_unit_duration"` - VerificationInterval *blockchain.StrDuration `toml:"verification_interval"` - VerificationTimeout *blockchain.StrDuration `toml:"verification_timeout"` - EAChangeInterval *blockchain.StrDuration `toml:"ea_change_interval"` - TestDuration *blockchain.StrDuration `toml:"test_duration"` -} -func (o *Volume) Validate() error { - if o.TestDuration == nil { - return errors.New("volume test duration must be set") - } - if o.Rate == nil || *o.Rate <= 0 { - return errors.New("rate must be set and be a positive integer") - } - if o.VURequestsPerUnit == nil || *o.VURequestsPerUnit <= 0 { - return errors.New("vu_requests_per_unit must be set and be a positive integer") - } - if o.RateLimitUnitDuration == nil || o.RateLimitUnitDuration.Duration == 0 { - return errors.New("rate_limit_unit_duration must be set and be a positive integer") + if o.Smoke == nil { + return errors.New("smoke must be defined") } - if o.VerificationInterval == nil || o.VerificationInterval.Duration == 0 { - return errors.New("verification_interval must be set and be a positive integer") - } - if o.VerificationTimeout == nil || o.VerificationTimeout.Duration == 0 { - return errors.New("verification_timeout must be set and be a positive integer") - } - if o.EAChangeInterval == nil || o.EAChangeInterval.Duration == 0 { - return errors.New("ea_change_interval must be set and be a positive integer") + if err := o.Smoke.Validate(); err != nil { + return err } return nil } -type SoakConfig struct { - OCRVersion *string `toml:"ocr_version"` - NumberOfContracts *int `toml:"number_of_contracts"` - TimeBetweenRounds *blockchain.StrDuration `toml:"time_between_rounds"` +type SmokeConfig struct { + NumberOfRounds *int `toml:"number_of_rounds"` } -func (o *SoakConfig) Validate() error { - if o.OCRVersion == nil || *o.OCRVersion == "" { - return errors.New("ocr_version must be set to either 1 or 2") - } - if o.NumberOfContracts == nil || *o.NumberOfContracts <= 1 { - return errors.New("number_of_contracts must be set and be greater than 1") - } - if o.TimeBetweenRounds == nil || o.TimeBetweenRounds.Duration == 0 { - return errors.New("time_between_rounds must be set and be a positive integer") +func (o *SmokeConfig) Validate() error { + if o.NumberOfRounds == nil { + return errors.New("number_of_rounds must be set") } return nil } diff --git a/integration-tests/testconfig/ocr2/ocr2.toml b/integration-tests/testconfig/ocr2/ocr2.toml index e69de29bb..9d1493a89 100644 --- a/integration-tests/testconfig/ocr2/ocr2.toml +++ b/integration-tests/testconfig/ocr2/ocr2.toml @@ -0,0 +1,3 @@ +[Common] +node_count = 6 +test_duration = "30m" \ No newline at end of file diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index e6950abe2..603fa6ebf 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -1 +1,360 @@ package testconfig + +import ( + "embed" + "encoding/base64" + "fmt" + "github.com/smartcontractkit/chainlink-testing-framework/docker/test_env" + "os" + "strings" + + "github.com/barkimedes/go-deepcopy" + "github.com/google/uuid" + "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/text/cases" + "golang.org/x/text/language" + + ocr2_config "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig/ocr2" + ctf_config "github.com/smartcontractkit/chainlink-testing-framework/config" + k8s_config "github.com/smartcontractkit/chainlink-testing-framework/k8s/config" + "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" +) + +type GlobalTestConfig interface { + GetChainlinkImageConfig() *ctf_config.ChainlinkImageConfig + GetLoggingConfig() *ctf_config.LoggingConfig + GetNetworkConfig() *ctf_config.NetworkConfig +} + +type UpgradeableChainlinkTestConfig interface { + GetChainlinkUpgradeImageConfig() *ctf_config.ChainlinkImageConfig +} + +type CommonTestConfig interface { + GetCommonConfig() *Common +} + +type Ocr2TestConfig interface { + GetOCR2Config() *ocr2_config.Config +} + +type NamedConfiguration interface { + GetConfigurationName() string +} + +type TestConfig struct { + ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` + Logging *ctf_config.LoggingConfig `toml:"Logging"` + ChainlinkUpgradeImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkUpgradeImage"` + Network *ctf_config.NetworkConfig `toml:"Network"` + Common *Common `toml:"Common"` + OCR2 *ocr2_config.Config `toml:"OCR2"` + ConfigurationName string `toml:"-"` +} + +func (c *TestConfig) GetLoggingConfig() *ctf_config.LoggingConfig { + return c.Logging +} + +func (c *TestConfig) GetPrivateEthereumNetworkConfig() *test_env.EthereumNetwork { + //TODO implement me + return nil +} + +func (c *TestConfig) GetPyroscopeConfig() *ctf_config.PyroscopeConfig { + //TODO implement me + return nil +} + +var embeddedConfigs embed.FS +var areConfigsEmbedded bool + +func init() { + embeddedConfigs = embeddedConfigsFs +} + +// Saves Test Config to a local file +func (c *TestConfig) Save() (string, error) { + filePath := fmt.Sprintf("test_config-%s.toml", uuid.New()) + + content, err := toml.Marshal(*c) + if err != nil { + return "", errors.Wrapf(err, "error marshaling test config") + } + + err = os.WriteFile(filePath, content, 0600) + if err != nil { + return "", errors.Wrapf(err, "error writing test config") + } + + return filePath, nil +} + +// MustCopy Returns a deep copy of the Test Config or panics on error +func (c TestConfig) MustCopy() any { + return deepcopy.MustAnything(c).(TestConfig) +} + +// MustCopy Returns a deep copy of struct passed to it and returns a typed copy (or panics on error) +func MustCopy[T any](c T) T { + return deepcopy.MustAnything(c).(T) +} + +func (c TestConfig) GetNetworkConfig() *ctf_config.NetworkConfig { + return c.Network +} + +func (c TestConfig) GetChainlinkImageConfig() *ctf_config.ChainlinkImageConfig { + return c.ChainlinkImage +} + +func (c TestConfig) GetCommonConfig() *Common { + return c.Common +} + +func (c TestConfig) GetChainlinkUpgradeImageConfig() *ctf_config.ChainlinkImageConfig { + return c.ChainlinkUpgradeImage +} + +func (c TestConfig) GetConfigurationName() string { + return c.ConfigurationName +} + +func (c *TestConfig) AsBase64() (string, error) { + content, err := toml.Marshal(*c) + if err != nil { + return "", errors.Wrapf(err, "error marshaling test config") + } + + return base64.StdEncoding.EncodeToString(content), nil +} + +type Common struct { + Network *string `toml:"network"` + InsideK8s *bool `toml:"inside_k8"` + L2RPCUrl *string `toml:"l2_rpc_url"` +} + +func (c *Common) Validate() error { + if c.Network == nil { + return fmt.Errorf("network must be set") + } + + switch *c.Network { + case "localnet": + break + case "testnet": + break + default: + return fmt.Errorf("network must be either 'localnet' or 'testnet'") + } + + if c.InsideK8s == nil { + return fmt.Errorf("inside_k8 must be set") + } + if c.L2RPCUrl == nil && *c.Network == "testnet" { + return fmt.Errorf("l2_rpc_url must be set") + } + + return nil +} + +type Product string + +const ( + OCR2 Product = "ocr2" +) + +var TestTypesWithLoki = []string{"Soak", "Smoke"} + +const TestTypeEnvVarName = "TEST_TYPE" + +func GetConfigurationNameFromEnv() (string, error) { + testType := os.Getenv(TestTypeEnvVarName) + if testType == "" { + return "", fmt.Errorf("%s env var not set", TestTypeEnvVarName) + } + + return cases.Title(language.English, cases.NoLower).String(testType), nil +} + +const ( + Base64OverrideEnvVarName = k8s_config.EnvBase64ConfigOverride + NoKey = "NO_KEY" +) + +func GetConfig(configurationName string, product Product) (TestConfig, error) { + logger := logging.GetTestLogger(nil) + + configurationName = strings.ReplaceAll(configurationName, "/", "_") + configurationName = strings.ReplaceAll(configurationName, " ", "_") + configurationName = cases.Title(language.English, cases.NoLower).String(configurationName) + fileNames := []string{ + "default.toml", + fmt.Sprintf("%s.toml", product), + "overrides.toml", + } + + testConfig := TestConfig{} + testConfig.ConfigurationName = configurationName + logger.Debug().Msgf("Will apply configuration named '%s' if it is found in any of the configs", configurationName) + + var handleSpecialOverrides = func(logger zerolog.Logger, filename, configurationName string, target *TestConfig, content []byte, product Product) error { + switch product { + default: + err := ctf_config.BytesToAnyTomlStruct(logger, filename, configurationName, &testConfig, content) + if err != nil { + return errors.Wrapf(err, "error reading file %s", filename) + } + + return nil + } + } + + // read embedded configs is build tag "embed" is set + // this makes our life much easier when using a binary + if areConfigsEmbedded { + logger.Info().Msg("Reading embedded configs") + embeddedFiles := []string{"default.toml", fmt.Sprintf("%s/%s.toml", product, product)} + for _, fileName := range embeddedFiles { + file, err := embeddedConfigs.ReadFile(fileName) + if err != nil && errors.Is(err, os.ErrNotExist) { + logger.Debug().Msgf("Embedded config file %s not found. Continuing", fileName) + continue + } else if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading embedded config") + } + + err = handleSpecialOverrides(logger, fileName, configurationName, &testConfig, file, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshalling embedded config") + } + } + } + + logger.Info().Msg("Reading configs from file system") + for _, fileName := range fileNames { + logger.Debug().Msgf("Looking for config file %s", fileName) + filePath, err := osutil.FindFile(fileName, osutil.DEFAULT_STOP_FILE_NAME, 3) + + if err != nil && errors.Is(err, os.ErrNotExist) { + logger.Debug().Msgf("Config file %s not found", fileName) + continue + } else if err != nil { + return TestConfig{}, errors.Wrapf(err, "error looking for file %s", filePath) + } + logger.Debug().Str("location", filePath).Msgf("Found config file %s", fileName) + + content, err := readFile(filePath) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + } + + err = handleSpecialOverrides(logger, fileName, configurationName, &testConfig, content, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading file %s", filePath) + } + } + + logger.Info().Msg("Reading configs from Base64 override env var") + configEncoded, isSet := os.LookupEnv(Base64OverrideEnvVarName) + if isSet && configEncoded != "" { + logger.Debug().Msgf("Found base64 config override environment variable '%s' found", Base64OverrideEnvVarName) + decoded, err := base64.StdEncoding.DecodeString(configEncoded) + if err != nil { + return TestConfig{}, err + } + + err = handleSpecialOverrides(logger, Base64OverrideEnvVarName, configurationName, &testConfig, decoded, product) + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error unmarshaling base64 config") + } + } else { + logger.Debug().Msg("Base64 config override from environment variable not found") + } + + // it neede some custom logic, so we do it separately + err := testConfig.readNetworkConfiguration() + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error reading network config") + } + + logger.Debug().Msg("Validating test config") + err = testConfig.Validate() + if err != nil { + return TestConfig{}, errors.Wrapf(err, "error validating test config") + } + + if testConfig.Common == nil { + testConfig.Common = &Common{} + } + + logger.Debug().Msg("Correct test config constructed successfully") + return testConfig, nil +} + +func (c *TestConfig) readNetworkConfiguration() error { + // currently we need to read that kind of secrets only for network configuration + if c == nil { + c.Network = &ctf_config.NetworkConfig{} + } + + c.Network.UpperCaseNetworkNames() + err := c.Network.Default() + if err != nil { + return errors.Wrapf(err, "error reading default network config") + } + + return nil +} + +func (c *TestConfig) Validate() error { + defer func() { + if r := recover(); r != nil { + panic(fmt.Errorf("Panic during test config validation: '%v'. Most probably due to presence of partial product config", r)) + } + }() + if c.ChainlinkImage == nil { + return fmt.Errorf("chainlink image config must be set") + } + if err := c.ChainlinkImage.Validate(); err != nil { + return errors.Wrapf(err, "chainlink image config validation failed") + } + if c.ChainlinkUpgradeImage != nil { + if err := c.ChainlinkUpgradeImage.Validate(); err != nil { + return errors.Wrapf(err, "chainlink upgrade image config validation failed") + } + } + if err := c.Network.Validate(); err != nil { + return errors.Wrapf(err, "network config validation failed") + } + + if c.Common == nil { + return fmt.Errorf("common config must be set") + } + + if err := c.Common.Validate(); err != nil { + return errors.Wrapf(err, "Common config validation failed") + } + + if c.OCR2 == nil { + return fmt.Errorf("OCR2 config must be set") + } + + if err := c.OCR2.Validate(); err != nil { + return errors.Wrapf(err, "OCR2 config validation failed") + } + return nil +} + +func readFile(filePath string) ([]byte, error) { + content, err := os.ReadFile(filePath) + if err != nil { + return nil, errors.Wrapf(err, "error reading file %s", filePath) + } + + return content, nil +} diff --git a/ops/gauntlet/gauntlet_starknet.go b/ops/gauntlet/gauntlet_starknet.go index 2b11779ce..d2c1f4749 100644 --- a/ops/gauntlet/gauntlet_starknet.go +++ b/ops/gauntlet/gauntlet_starknet.go @@ -90,6 +90,10 @@ func (sg *StarknetGauntlet) InstallDependencies() error { if err != nil { return err } + _, err = sg.G.ExecCommand([]string{"build"}, *sg.options) + if err != nil { + return err + } sg.G.Command = "gauntlet" return nil } From c0a8b951800a4ba6871d8f3396429593667899ba Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 11:59:03 +0100 Subject: [PATCH 11/39] Go mod tidy --- integration-tests/common/common.go | 9 +++++++-- integration-tests/go.mod | 10 +++++----- integration-tests/smoke/ocr2_test.go | 2 +- integration-tests/testconfig/default.toml | 2 ++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index b99c381eb..aa5153795 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -70,12 +70,17 @@ type RPCDetails struct { func New(testConfig *testconfig.TestConfig) *Common { var c *Common + l2RpcUrl := DefaultL2RPCInternal duration, err := time.ParseDuration(*testConfig.OCR2.TestDuration) if err != nil { panic("Invalid test duration") } + if *testConfig.Common.Network == "testnet" { + l2RpcUrl = *testConfig.Common.L2RPCUrl + } + c = &Common{ TestConfig: testConfig, ChainDetails: &ChainDetails{ @@ -87,7 +92,7 @@ func New(testConfig *testconfig.TestConfig) *Common { }, RPCDetails: &RPCDetails{ P2PPort: "6690", - RPCL2Internal: DefaultL2RPCInternal, + RPCL2Internal: l2RpcUrl, }, } @@ -112,7 +117,7 @@ func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) AddHelm(mock_adapter.New(nil)). AddHelm(chainlink.New(0, map[string]interface{}{ "toml": tomlString, - "replicas": c.TestConfig.OCR2.NodeCount, + "replicas": *c.TestConfig.OCR2.NodeCount, })) } diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 5b27d443e..3437c1f10 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -5,9 +5,12 @@ go 1.21.4 require ( github.com/NethermindEth/juno v0.3.1 github.com/NethermindEth/starknet.go v0.6.1-0.20231218140327-915109ab5bc1 + github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df github.com/go-resty/resty/v2 v2.7.0 github.com/google/uuid v1.4.0 github.com/lib/pq v1.10.9 + github.com/pelletier/go-toml/v2 v2.1.1 + github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 github.com/smartcontractkit/chainlink-common v0.1.7-0.20240213113935-001c2f4befd4 github.com/smartcontractkit/chainlink-starknet/ops v0.0.0-20231205180940-ea2e3e916725 @@ -17,6 +20,8 @@ require ( github.com/smartcontractkit/chainlink/v2 v2.9.0-beta0.0.20240215151806-009c99876c4c github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.23.0 + go.uber.org/zap v1.26.0 + golang.org/x/text v0.14.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -66,7 +71,6 @@ require ( github.com/aws/constructs-go/constructs/v10 v10.1.255 // indirect github.com/aws/jsii-runtime-go v1.75.0 // indirect github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59 // indirect - github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect github.com/blendle/zapdriver v1.3.1 // indirect @@ -317,11 +321,9 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/alertmanager v0.26.0 // indirect @@ -419,7 +421,6 @@ require ( go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - go.uber.org/zap v1.26.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/crypto v0.19.0 // indirect @@ -430,7 +431,6 @@ require ( golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.17.0 // indirect golang.org/x/term v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.18.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 846dc782e..e90345ed6 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -45,7 +45,7 @@ func TestOCRBasicNew(t *testing.T) { t.Run(test.name, func(t *testing.T) { t.Parallel() logging.Init() - state, err := common.NewOCRv2State(t, "test", &config) + state, err := common.NewOCRv2State(t, "smoke-ocr2", &config) require.NoError(t, err, "Could not setup the ocrv2 state") // Cleanup K8s diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 10eb6f84e..7a4da2f36 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -1,3 +1,5 @@ +# This is the default configuration so OCR2 tests can run without issues + [Logging] test_log_collect=false From 25def0813d9f77ffc297f66624d09c822702c073 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 12:18:21 +0100 Subject: [PATCH 12/39] Build contracts --- .github/workflows/integration-tests-smoke.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 78ff320cb..4c1991095 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -157,6 +157,9 @@ jobs: tar -xvzf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl.tar.gz mv -vf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl scarb-build echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH + - name: Build contracts + run: | + cd contracts && scarb --profile release build - name: Generate config overrides run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml From d350489c9f76fdb38242f7501d87ddfcc0e88131 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 14:46:25 +0100 Subject: [PATCH 13/39] TOML feedback from Bartek --- contracts/.tool-versions | 1 + 1 file changed, 1 insertion(+) create mode 100644 contracts/.tool-versions diff --git a/contracts/.tool-versions b/contracts/.tool-versions new file mode 100644 index 000000000..49edbb536 --- /dev/null +++ b/contracts/.tool-versions @@ -0,0 +1 @@ +scarb 2.5.4 From cc1246a6acdedbe95e8c2b36c929bac1dcb9c358 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 14:48:03 +0100 Subject: [PATCH 14/39] TOML feedback from Bartek --- integration-tests/common/common.go | 4 ++++ integration-tests/common/test_common.go | 1 - integration-tests/smoke/ocr2_test.go | 15 +++++++++++-- integration-tests/testconfig/configs_embed.go | 9 +------- integration-tests/testconfig/default.toml | 1 + integration-tests/testconfig/testconfig.go | 21 ++++++------------- ops/devnet/environment.go | 2 +- 7 files changed, 26 insertions(+), 27 deletions(-) diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index aa5153795..3c0d04f5f 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -118,6 +118,10 @@ func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) AddHelm(chainlink.New(0, map[string]interface{}{ "toml": tomlString, "replicas": *c.TestConfig.OCR2.NodeCount, + "db": map[string]any{ + "image": map[string]any{ + "version": "15.5", + }}, })) } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index a87d04a23..6cd3a776e 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -34,7 +34,6 @@ import ( var ( rpcRequestTimeout = time.Second * 300 - dumpPath = "/dumps/dump.pkl" ) // OCRv2TestState Main testing state struct diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index e90345ed6..f28cb6f8d 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "maps" + "os" "testing" ) @@ -36,11 +37,21 @@ func TestOCRBasicNew(t *testing.T) { // "CL_SOLANA_CMD": "chainlink-solana", //}}, } { - config, err := tc.GetConfig("Smoke", tc.OCR2) if err != nil { t.Fatal(err) } + + // We have to export these since the charts read from env + if *config.Common.InsideK8s { + err = os.Setenv("CHAINLINK_IMAGE", *config.ChainlinkImage.Image) + require.NoError(t, err, "Could not pull image from config") + err = os.Setenv("CHAINLINK_VERSION", *config.ChainlinkImage.Version) + require.NoError(t, err, "Could not pull version from config") + err = os.Setenv("CHAINLINK_ENV_USER", *config.Common.User) + require.NoError(t, err, "Could not pull user from config") + } + test := test t.Run(test.name, func(t *testing.T) { t.Parallel() @@ -48,7 +59,7 @@ func TestOCRBasicNew(t *testing.T) { state, err := common.NewOCRv2State(t, "smoke-ocr2", &config) require.NoError(t, err, "Could not setup the ocrv2 state") - // Cleanup K8s + // K8s specific config and cleanup if *config.Common.InsideK8s { t.Cleanup(func() { if err := actions.TeardownSuite(t, state.Common.Env, state.ChainlinkNodesK8s, nil, zapcore.PanicLevel, nil); err != nil { diff --git a/integration-tests/testconfig/configs_embed.go b/integration-tests/testconfig/configs_embed.go index 67e954ff5..e9decaa4c 100644 --- a/integration-tests/testconfig/configs_embed.go +++ b/integration-tests/testconfig/configs_embed.go @@ -6,14 +6,7 @@ package testconfig import "embed" //go:embed default.toml -//go:embed automation/automation.toml -//go:embed functions/functions.toml -//go:embed keeper/keeper.toml -//go:embed log_poller/log_poller.toml -//go:embed node/node.toml -//go:embed ocr/ocr.toml -//go:embed vrfv2/vrfv2.toml -//go:embed vrfv2plus/vrfv2plus.toml +//go:embed ocr2/ocr2.toml var embeddedConfigsFs embed.FS func init() { diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 7a4da2f36..884f8d86b 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -20,6 +20,7 @@ simulated = ["wss://127.0.0.1"] # Not needed for Starknet but mandatory from CTF [Common] inside_k8 = false network = "localnet" +user = "satoshi" [OCR2] node_count = 6 diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 603fa6ebf..6faab8bb7 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -41,10 +41,6 @@ type Ocr2TestConfig interface { GetOCR2Config() *ocr2_config.Config } -type NamedConfiguration interface { - GetConfigurationName() string -} - type TestConfig struct { ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` Logging *ctf_config.LoggingConfig `toml:"Logging"` @@ -135,6 +131,7 @@ func (c *TestConfig) AsBase64() (string, error) { type Common struct { Network *string `toml:"network"` InsideK8s *bool `toml:"inside_k8"` + User *string `toml:"user"` L2RPCUrl *string `toml:"l2_rpc_url"` } @@ -155,6 +152,11 @@ func (c *Common) Validate() error { if c.InsideK8s == nil { return fmt.Errorf("inside_k8 must be set") } + + if c.User == nil { + return fmt.Errorf("user must be set") + } + if c.L2RPCUrl == nil && *c.Network == "testnet" { return fmt.Errorf("l2_rpc_url must be set") } @@ -168,19 +170,8 @@ const ( OCR2 Product = "ocr2" ) -var TestTypesWithLoki = []string{"Soak", "Smoke"} - const TestTypeEnvVarName = "TEST_TYPE" -func GetConfigurationNameFromEnv() (string, error) { - testType := os.Getenv(TestTypeEnvVarName) - if testType == "" { - return "", fmt.Errorf("%s env var not set", TestTypeEnvVarName) - } - - return cases.Title(language.English, cases.NoLower).String(testType), nil -} - const ( Base64OverrideEnvVarName = k8s_config.EnvBase64ConfigOverride NoKey = "NO_KEY" diff --git a/ops/devnet/environment.go b/ops/devnet/environment.go index c1ef5aa6c..990636a6c 100644 --- a/ops/devnet/environment.go +++ b/ops/devnet/environment.go @@ -73,7 +73,7 @@ func defaultProps() map[string]any { "starknet-dev": map[string]any{ "image": map[string]any{ "image": "shardlabs/starknet-devnet-rs", - "version": "latest", + "version": "b41e566a3f17aa0e51871f02d5165959e50ce358", }, "resources": map[string]any{ "requests": map[string]any{ From fae5cfa25562bf1c541807b3d48ee030ff7eb5a6 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 14:52:34 +0100 Subject: [PATCH 15/39] Removed unneeded interfaces --- integration-tests/testconfig/testconfig.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 6faab8bb7..4a427bfeb 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -23,24 +23,6 @@ import ( "github.com/smartcontractkit/chainlink-testing-framework/utils/osutil" ) -type GlobalTestConfig interface { - GetChainlinkImageConfig() *ctf_config.ChainlinkImageConfig - GetLoggingConfig() *ctf_config.LoggingConfig - GetNetworkConfig() *ctf_config.NetworkConfig -} - -type UpgradeableChainlinkTestConfig interface { - GetChainlinkUpgradeImageConfig() *ctf_config.ChainlinkImageConfig -} - -type CommonTestConfig interface { - GetCommonConfig() *Common -} - -type Ocr2TestConfig interface { - GetOCR2Config() *ocr2_config.Config -} - type TestConfig struct { ChainlinkImage *ctf_config.ChainlinkImageConfig `toml:"ChainlinkImage"` Logging *ctf_config.LoggingConfig `toml:"Logging"` From e849e64ecdab797c844ff10356f1d7f1b185e6dc Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 15:03:28 +0100 Subject: [PATCH 16/39] Added new helm deployment method --- integration-tests/common/common.go | 33 +++++++++++++++++++++------- integration-tests/smoke/ocr2_test.go | 11 ---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 3c0d04f5f..9db7b9ffb 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -5,6 +5,7 @@ import ( "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig" "github.com/smartcontractkit/chainlink-starknet/ops/devnet" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" + ctfconfig "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/chainlink" mock_adapter "github.com/smartcontractkit/chainlink-testing-framework/k8s/pkg/helm/mock-adapter" "github.com/smartcontractkit/chainlink-testing-framework/utils/ptr" @@ -112,17 +113,33 @@ func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) if err != nil { return nil, err } + var overrideFn = func(_ interface{}, target interface{}) { + ctfconfig.MustConfigOverrideChainlinkVersion(c.TestConfig.ChainlinkImage, target) + } + cd := chainlink.NewWithOverride(0, map[string]any{ + "toml": tomlString, + "replicas": *c.TestConfig.OCR2.NodeCount, + "chainlink": map[string]interface{}{ + "resources": map[string]interface{}{ + "requests": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + "limits": map[string]interface{}{ + "cpu": "2000m", + "memory": "4Gi", + }, + }, + }, + "db": map[string]any{ + "image": map[string]any{ + "version": "15.5", + }}, + }, c.TestConfig.ChainlinkImage, overrideFn) c.Env = environment.New(c.TestEnvDetails.K8Config). AddHelm(devnet.New(nil)). AddHelm(mock_adapter.New(nil)). - AddHelm(chainlink.New(0, map[string]interface{}{ - "toml": tomlString, - "replicas": *c.TestConfig.OCR2.NodeCount, - "db": map[string]any{ - "image": map[string]any{ - "version": "15.5", - }}, - })) + AddHelm(cd) } return c, nil diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index f28cb6f8d..6bca5b691 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -13,7 +13,6 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "maps" - "os" "testing" ) @@ -42,16 +41,6 @@ func TestOCRBasicNew(t *testing.T) { t.Fatal(err) } - // We have to export these since the charts read from env - if *config.Common.InsideK8s { - err = os.Setenv("CHAINLINK_IMAGE", *config.ChainlinkImage.Image) - require.NoError(t, err, "Could not pull image from config") - err = os.Setenv("CHAINLINK_VERSION", *config.ChainlinkImage.Version) - require.NoError(t, err, "Could not pull version from config") - err = os.Setenv("CHAINLINK_ENV_USER", *config.Common.User) - require.NoError(t, err, "Could not pull user from config") - } - test := test t.Run(test.name, func(t *testing.T) { t.Parallel() From 0a52985418a0313615a912239cdf52513035b289 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 15:19:28 +0100 Subject: [PATCH 17/39] Linting fixes --- integration-tests/common/common.go | 41 +++------------------- integration-tests/docker/test_env/stark.go | 6 ++-- integration-tests/smoke/ocr2_test.go | 5 +-- integration-tests/testconfig/ocr2/ocr2.go | 3 +- 4 files changed, 12 insertions(+), 43 deletions(-) diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 9db7b9ffb..678781c66 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -12,7 +12,6 @@ import ( "github.com/smartcontractkit/chainlink/integration-tests/docker/test_env" "os" "os/exec" - "regexp" "strconv" "strings" "testing" @@ -223,37 +222,6 @@ func (c *Common) TearDownLocalEnvironment(t *testing.T) { log.Info().Msg("Tear down local stack complete.") } -// connectChainlinkNodes creates a chainlink client for each node in the environment -// This is a non k8s version of the function in chainlink_k8s.go -// https://github.com/smartcontractkit/chainlink/blob/cosmos-test-keys/integration-tests/client/chainlink_k8s.go#L77 -func connectChainlinkNodes(e *environment.Environment) ([]*client.ChainlinkClient, error) { - var clients []*client.ChainlinkClient - for _, nodeDetails := range e.ChainlinkNodeDetails { - c, err := client.NewChainlinkClient(&client.ChainlinkConfig{ - URL: nodeDetails.LocalIP, - Email: "notreal@fakeemail.ch", - Password: "fj293fbBnlQ!f9vNs", - InternalIP: parseHostname(nodeDetails.InternalIP), - }, log.Logger) - if err != nil { - return nil, err - } - log.Debug(). - Str("URL", c.Config.URL). - Str("Internal IP", c.Config.InternalIP). - Str("Chart Name", nodeDetails.ChartName). - Str("Pod Name", nodeDetails.PodName). - Msg("Connected to Chainlink node") - clients = append(clients, c) - } - return clients, nil -} - -func parseHostname(s string) string { - r := regexp.MustCompile(`://(?P.*):`) - return r.FindStringSubmatch(s)[1] -} - func (c *Common) CreateNodeKeysBundle(nodes []*client.ChainlinkClient) ([]client.NodeKeysBundle, error) { nkb := make([]client.NodeKeysBundle, 0) for _, n := range nodes { @@ -295,8 +263,8 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st // Defining relay config bootstrapRelayConfig := job.JSONConfig{ "nodeName": fmt.Sprintf("starknet-OCRv2-%s-%s", "node", uuid.New().String()), - "accountAddress": fmt.Sprintf("%s", accountAddresses[0]), - "chainID": fmt.Sprintf("%s", c.ChainDetails.ChainId), + "accountAddress": accountAddresses[0], + "chainID": c.ChainDetails.ChainId, } oracleSpec := job.OCR2OracleSpec{ @@ -325,7 +293,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st sourceValueBridge := &client.BridgeTypeAttributes{ Name: "mockserver-bridge", - URL: fmt.Sprintf("%s/%s", c.RPCDetails.MockServerEndpoint, strings.TrimPrefix(c.RPCDetails.MockServerUrl, "/")), + URL: c.RPCDetails.MockServerEndpoint + "/" + strings.TrimPrefix(c.RPCDetails.MockServerUrl, "/"), } // Setting up job specs @@ -339,7 +307,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st } relayConfig := job.JSONConfig{ "nodeName": bootstrapRelayConfig["nodeName"], - "accountAddress": fmt.Sprintf("%s", accountAddresses[nIdx]), + "accountAddress": accountAddresses[nIdx], "chainID": bootstrapRelayConfig["chainID"], } @@ -363,7 +331,6 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st OCR2OracleSpec: oracleSpec, ObservationSource: observationSource, } - fmt.Println(jobSpec.String()) _, err = n.MustCreateJob(jobSpec) if err != nil { return err diff --git a/integration-tests/docker/test_env/stark.go b/integration-tests/docker/test_env/stark.go index 546454c75..a4cdcbfab 100644 --- a/integration-tests/docker/test_env/stark.go +++ b/integration-tests/docker/test_env/stark.go @@ -91,12 +91,12 @@ func (s *Starknet) StartContainer() error { return nil } -func (ms *Starknet) getContainerRequest() (*tc.ContainerRequest, error) { +func (s *Starknet) getContainerRequest() (*tc.ContainerRequest, error) { return &tc.ContainerRequest{ - Name: ms.ContainerName, + Name: s.ContainerName, Image: "shardlabs/starknet-devnet-rs:b41e566a3f17aa0e51871f02d5165959e50ce358", ExposedPorts: []string{test_env.NatPortFormat(STARK_HTTP_PORT)}, - Networks: ms.Networks, + Networks: s.Networks, WaitingFor: tcwait.ForLog("Starknet Devnet listening"). WithStartupTimeout(30 * time.Second). WithPollInterval(100 * time.Millisecond), diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 6bca5b691..d8f2aa990 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -30,7 +30,7 @@ func TestOCRBasicNew(t *testing.T) { name string env map[string]string }{ - {name: "embeded"}, + {name: "embedded"}, //{name: "plugins", env: map[string]string{ // "CL_MEDIAN_CMD": "chainlink-feeds", // "CL_SOLANA_CMD": "chainlink-solana", @@ -51,7 +51,7 @@ func TestOCRBasicNew(t *testing.T) { // K8s specific config and cleanup if *config.Common.InsideK8s { t.Cleanup(func() { - if err := actions.TeardownSuite(t, state.Common.Env, state.ChainlinkNodesK8s, nil, zapcore.PanicLevel, nil); err != nil { + if err = actions.TeardownSuite(t, state.Common.Env, state.ChainlinkNodesK8s, nil, zapcore.PanicLevel, nil); err != nil { state.TestConfig.L.Error().Err(err).Msg("Error tearing down environment") } }) @@ -66,6 +66,7 @@ func TestOCRBasicNew(t *testing.T) { } state.DeployCluster() state.Clients.GauntletClient, err = gauntlet.NewStarknetGauntlet(fmt.Sprintf("%s/", utils.ProjectRoot)) + require.NoError(t, err, "Setting up gauntlet should not fail") err = state.Clients.GauntletClient.SetupNetwork(state.Common.RPCDetails.RPCL2External, state.Account.Account, state.Account.PrivateKey) require.NoError(t, err, "Setting up gauntlet network should not fail") err = state.DeployGauntlet(0, 100000000000, decimals, "auto", 1, 1) diff --git a/integration-tests/testconfig/ocr2/ocr2.go b/integration-tests/testconfig/ocr2/ocr2.go index 30438ba38..7555fb6fa 100644 --- a/integration-tests/testconfig/ocr2/ocr2.go +++ b/integration-tests/testconfig/ocr2/ocr2.go @@ -22,7 +22,8 @@ func (o *Config) Validate() error { if o.Smoke == nil { return errors.New("smoke must be defined") } - if err := o.Smoke.Validate(); err != nil { + err := o.Smoke.Validate() + if err != nil { return err } From e612f67d83a6f717574ff52218fbd58d688b75b0 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 16:39:56 +0100 Subject: [PATCH 18/39] Fixing soak --- integration-tests/testconfig/testconfig.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 4a427bfeb..652a72a48 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -38,13 +38,11 @@ func (c *TestConfig) GetLoggingConfig() *ctf_config.LoggingConfig { } func (c *TestConfig) GetPrivateEthereumNetworkConfig() *test_env.EthereumNetwork { - //TODO implement me - return nil + return &test_env.EthereumNetwork{} } func (c *TestConfig) GetPyroscopeConfig() *ctf_config.PyroscopeConfig { - //TODO implement me - return nil + return &ctf_config.PyroscopeConfig{} } var embeddedConfigs embed.FS From eefe1891e34c80b0a8fb8a0aff6db95a76e0399c Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 27 Feb 2024 17:08:49 +0100 Subject: [PATCH 19/39] Helm repo update --- integration-tests/common/test_common.go | 3 +++ integration-tests/scripts/entrypoint | 1 + integration-tests/smoke/ocr2_test.go | 2 +- integration-tests/test.Dockerfile | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 6cd3a776e..235a92098 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -133,6 +133,9 @@ func (m *OCRv2TestState) DeployCluster() { // When running soak we need to use K8S if *m.Common.TestConfig.Common.InsideK8s { m.DeployEnv() + if m.Common.Env.WillUseRemoteRunner() { + return + } // Setting RPC details m.Common.RPCDetails.RPCL2External = m.Common.Env.URLs["starknet-dev"][0] m.Common.RPCDetails.MockServerEndpoint = m.Common.Env.URLs["qa_mock_adapter_internal"][0] diff --git a/integration-tests/scripts/entrypoint b/integration-tests/scripts/entrypoint index 94ea19cc2..462aaaf2f 100755 --- a/integration-tests/scripts/entrypoint +++ b/integration-tests/scripts/entrypoint @@ -14,4 +14,5 @@ cd "$SCRIPT_DIR"/../ || exit 1 # SUITE=${SUITE:=} the suite of tests you want to run # TEST_NAME=${TEST_NAME:=} The specific test to run # run the tests +nix develop -c helm repo update nix develop -c ./"${SUITE}".test -test.v -test.count 1 ${ARGS} -test.run ^${TEST_NAME}$ diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index d8f2aa990..553fa9a2f 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -25,7 +25,7 @@ func init() { flag.BoolVar(&keepAlive, "keep-alive", false, "enable to keep the cluster alive") } -func TestOCRBasicNew(t *testing.T) { +func TestOCRBasic(t *testing.T) { for _, test := range []struct { name string env map[string]string diff --git a/integration-tests/test.Dockerfile b/integration-tests/test.Dockerfile index aaf35e838..1c87a720e 100644 --- a/integration-tests/test.Dockerfile +++ b/integration-tests/test.Dockerfile @@ -6,5 +6,6 @@ ENV PATH="/repo/cairo-build/bin:/repo/scarb-build/bin:${PATH}" COPY . /repo/ WORKDIR /repo +RUN nix develop -c helm repo update RUN nix develop -c /repo/integration-tests/scripts/buildTests "${SUITES}" ENTRYPOINT ["/repo/integration-tests/scripts/entrypoint"] From 2fb4e9656cefc8bdcaae1d4caba05bc926d85803 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Wed, 28 Feb 2024 11:45:24 +0100 Subject: [PATCH 20/39] Added readme and testnet support --- .github/workflows/integration-tests-smoke.yml | 3 ++ integration-tests/README.md | 46 ++++++++----------- integration-tests/common/common.go | 4 +- integration-tests/common/gauntlet_common.go | 7 +-- integration-tests/common/test_common.go | 12 +++++ integration-tests/smoke/ocr2_test.go | 6 ++- integration-tests/testconfig/default.toml | 11 +++-- integration-tests/testconfig/testconfig.go | 28 +++++++++-- 8 files changed, 77 insertions(+), 40 deletions(-) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 4c1991095..9d385fb16 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -168,6 +168,9 @@ jobs: version="${{ env.CHAINLINK_VERSION }}" [Network] selected_networks=["SIMULATED"] + [Common] + internal_docker_repo = "${{ env.INTERNAL_DOCKER_REPO }}" + stateful_db = false EOF # shellcheck disable=SC2002 BASE64_CONFIG_OVERRIDE=$(cat config.toml | base64 -w 0) diff --git a/integration-tests/README.md b/integration-tests/README.md index cbf5276b5..84432a0e4 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -1,38 +1,28 @@ -# Local e2e testing +## Integration tests - HOWTO -Make sure to have `psql` installed locally. We use it to create a new database for each node. +### Prerequisites +1. `cd contracts && scarb --profile release build` +2. `yarn install` +3. `yarn build` -Create a new network for containers (only needs to be done once). A custom network allows containers to DNS resolve each other using container names. +#### TOML preparation +The integration tests are using TOML as the configuration input. The logic and parsing is located under [Test config](./testconfig) -``` -docker network create chainlink -``` +By default, the tests will be running with the default config set in [default.toml](./testconfig/default.toml). This configuration is set to run on devnet with local docker. -Build a custom core image with starknet relayer bumped to some commit. +Fields in the default toml can be overriden by creating an `overrides.toml`file. Any values specified here take precedence and will be overwritten if they overlap with `default.toml`. -``` -cd ../core -go get github.com/smartcontractkit/chainlink-starknet/relayer@ -docker build . -t smartcontract/chainlink:starknet -f ./core/chainlink.Dockerfile -``` +##### Testnet runs +In order to run the tests on Testnet, additional variables need to be specified in the TOML, these would also be pointed out if `network = "testnet"` is set. The additional variables are: -Compile contracts and gauntlet: +- `l2_rpc_url` - L2 RPC url +- `account` - Account address on L2 +- `private_key` - Private key for L2 account -``` -yarn build -cd contracts -scarb --profile release build -``` +##### Running in k8s -Run the tests! +Set `inside_k8 = true` under `[Common]`. -``` -cd integration-tests -go test -count 1 -v -timeout 30m --run OCRBasic ./smoke -``` +#### Run tests -Use `something.down.sh` scripts to teardown everything afterwards if the tests don't properly clean up. - -# Old docs - -For more information, see the [Chainlink Starknet Documentation | Integration Tests](../docs/integration-tests). +`cd integration-tests && go test --timeout=2h -v -count=1 -json ./smoke` \ No newline at end of file diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 678781c66..757034b96 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -133,7 +133,9 @@ func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) "db": map[string]any{ "image": map[string]any{ "version": "15.5", - }}, + }, + "stateful": c.TestConfig.Common.Stateful, + }, }, c.TestConfig.ChainlinkImage, overrideFn) c.Env = environment.New(c.TestEnvDetails.K8Config). AddHelm(devnet.New(nil)). diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index 5ed4479d2..ad056dc71 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -9,8 +9,9 @@ import ( ) var ( - ethAddressGoerli = "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" - nAccount string + ethAddressSepolia = "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" + strkAddressSepolia = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" + nAccount string ) func (m *OCRv2TestState) fundNodes() ([]string, error) { @@ -36,7 +37,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = m.Clients.GauntletClient.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node + _, err = m.Clients.GauntletClient.TransferToken(strkAddressSepolia, key, "100000000000000000") // Transferring 1 ETH to each node if err != nil { return nil, err } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 235a92098..308e5ca82 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -138,6 +138,10 @@ func (m *OCRv2TestState) DeployCluster() { } // Setting RPC details m.Common.RPCDetails.RPCL2External = m.Common.Env.URLs["starknet-dev"][0] + if *m.Common.TestConfig.Common.Network == "testnet" { + m.Common.RPCDetails.RPCL2External = *m.Common.TestConfig.Common.L2RPCUrl + m.Common.RPCDetails.RPCL2Internal = *m.Common.TestConfig.Common.L2RPCUrl + } m.Common.RPCDetails.MockServerEndpoint = m.Common.Env.URLs["qa_mock_adapter_internal"][0] m.Common.RPCDetails.MockServerUrl = "five" @@ -152,6 +156,11 @@ func (m *OCRv2TestState) DeployCluster() { m.Common.RPCDetails.RPCL2External = stark.ExternalHttpUrl m.Common.RPCDetails.RPCL2Internal = stark.InternalHttpUrl + if *m.Common.TestConfig.Common.Network == "testnet" { + m.Common.RPCDetails.RPCL2External = *m.Common.TestConfig.Common.L2RPCUrl + m.Common.RPCDetails.RPCL2Internal = *m.Common.TestConfig.Common.L2RPCUrl + } + // Creating docker containers b, err := test_env.NewCLTestEnvBuilder(). WithNonEVM(). @@ -205,6 +214,9 @@ func (m *OCRv2TestState) DeployCluster() { account := accounts[0] m.Account.Account = account.Address m.Account.PrivateKey = account.PrivateKey + } else { + m.Account.Account = *m.TestConfig.TestConfig.Common.Account + m.Account.PrivateKey = *m.TestConfig.TestConfig.Common.PrivateKey } } diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 553fa9a2f..58aa03277 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "maps" + "os" "testing" ) @@ -40,7 +41,10 @@ func TestOCRBasic(t *testing.T) { if err != nil { t.Fatal(err) } - + err = os.Setenv("CHAINLINK_ENV_USER", *config.Common.User) + require.NoError(t, err, "Could not set CHAINLINK_ENV_USER") + err = os.Setenv("INTERNAL_DOCKER_REPO", *config.Common.InternalDockerRepo) + require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") test := test t.Run(test.name, func(t *testing.T) { t.Parallel() diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index 884f8d86b..a3e26d4b8 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -1,4 +1,7 @@ # This is the default configuration so OCR2 tests can run without issues +[ChainlinkImage] +image="public.ecr.aws/chainlink/chainlink" +version="2.9.0" [Logging] test_log_collect=false @@ -9,18 +12,20 @@ log_producer_timeout="10s" log_producer_retry_limit=10 [Network] -selected_networks=["SIMULATED"] # Not needed for Starknet but mandatory from CTF +selected_networks=["SIMULATED"] # Not needed for Starknet but mandatory from CTF (do not change) [Network.RpcHttpUrls] -simulated = ["http://127.0.0.1"] # Not needed for Starknet but mandatory from CTF +simulated = ["http://127.0.0.1"] # Not needed for Starknet but mandatory from CTF (do not change) [Network.RpcWsUrls] -simulated = ["wss://127.0.0.1"] # Not needed for Starknet but mandatory from CTF +simulated = ["wss://127.0.0.1"] # Not needed for Starknet but mandatory from CTF (do not change) [Common] +internal_docker_repo = "public.ecr.aws/chainlink" inside_k8 = false network = "localnet" user = "satoshi" +stateful_db = false [OCR2] node_count = 6 diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 652a72a48..047aa2445 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -109,10 +109,14 @@ func (c *TestConfig) AsBase64() (string, error) { } type Common struct { - Network *string `toml:"network"` - InsideK8s *bool `toml:"inside_k8"` - User *string `toml:"user"` - L2RPCUrl *string `toml:"l2_rpc_url"` + Network *string `toml:"network"` + InsideK8s *bool `toml:"inside_k8"` + User *string `toml:"user"` + L2RPCUrl *string `toml:"l2_rpc_url"` + PrivateKey *string `toml:"private_key"` + Account *string `toml:"account"` + Stateful *bool `toml:"stateful_db"` + InternalDockerRepo *string `toml:"internal_docker_repo"` } func (c *Common) Validate() error { @@ -133,14 +137,30 @@ func (c *Common) Validate() error { return fmt.Errorf("inside_k8 must be set") } + if c.InternalDockerRepo == nil { + return fmt.Errorf("internal_docker_repo must be set") + } + if c.User == nil { return fmt.Errorf("user must be set") } + if c.Stateful == nil { + return fmt.Errorf("stateful_db state for db must be set") + } + if c.L2RPCUrl == nil && *c.Network == "testnet" { return fmt.Errorf("l2_rpc_url must be set") } + if c.Account == nil && *c.Network == "testnet" { + return fmt.Errorf("account must be set") + } + + if c.PrivateKey == nil && *c.Network == "testnet" { + return fmt.Errorf("private_key must be set") + } + return nil } From fb7fa0870a1bc30779ca75ed7efd929ffb61b468 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Wed, 28 Feb 2024 14:39:22 +0100 Subject: [PATCH 21/39] Added sepolia --- integration-tests/common/common.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 757034b96..fdfa2ceeb 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -34,7 +34,8 @@ import ( var ( chainName = "starknet" - chainId = "SN_GOERLI" + chainIdLocalNet = "SN_GOERLI" + chainIdTestnet = "SN_SEPOLIA" DefaultL2RPCInternal = "http://starknet-dev:5000" ) @@ -71,6 +72,7 @@ type RPCDetails struct { func New(testConfig *testconfig.TestConfig) *Common { var c *Common l2RpcUrl := DefaultL2RPCInternal + chainId := chainIdLocalNet duration, err := time.ParseDuration(*testConfig.OCR2.TestDuration) if err != nil { @@ -79,6 +81,7 @@ func New(testConfig *testconfig.TestConfig) *Common { if *testConfig.Common.Network == "testnet" { l2RpcUrl = *testConfig.Common.L2RPCUrl + chainId = chainIdTestnet } c = &Common{ From b78003c9d89eaa0fcd4ea4a8baaa81266a9e0c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Wed, 28 Feb 2024 22:55:34 +0900 Subject: [PATCH 22/39] txm: Increase logging --- relayer/pkg/chainlink/txm/txm.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index e5e84b9b9..16cd1783e 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -198,6 +198,12 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun simFlags := []starknetrpc.SimulationFlag{} feeEstimate, err := account.EstimateFee(ctx, []starknetrpc.BroadcastTxn{tx}, simFlags, starknetrpc.BlockID{Tag: "latest"}) if err != nil { + var data any + var rpcError *starknetrpc.RPCError + if errors.As(err, &rpcError) { + data = rpcError.Data() + } + txm.lggr.Errorw("failed to estimate fee", "error", err, "data", data) return txhash, fmt.Errorf("failed to estimate fee: %+w", err) } @@ -245,6 +251,12 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun res, err := account.AddInvokeTransaction(execCtx, tx) if err != nil { // TODO: handle initial broadcast errors - what kind of errors occur? + var data any + var rpcError *starknetrpc.RPCError + if errors.As(err, &rpcError) { + data = rpcError.Data() + } + txm.lggr.Errorw("failed to invoke tx", "error", err, "data", data) return txhash, fmt.Errorf("failed to invoke tx: %+w", err) } // handle nil pointer From 7ea334d835bdf12f5fcfb3c9bd302ad503d72bfa Mon Sep 17 00:00:00 2001 From: cfal Date: Wed, 28 Feb 2024 13:28:10 -0500 Subject: [PATCH 23/39] relayer/pkg/chainlink/txm/txm.go: set L1Gas instead --- relayer/pkg/chainlink/txm/txm.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index 16cd1783e..640989da9 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -225,12 +225,12 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun gasConsumed := friEstimate.GasConsumed.BigInt(new(big.Int)) expandedGas := new(big.Int).Mul(gasConsumed, big.NewInt(140)) maxGas := new(big.Int).Div(expandedGas, big.NewInt(100)) - tx.ResourceBounds.L2Gas.MaxAmount = starknetrpc.U64(starknetutils.BigIntToFelt(maxGas).String()) + tx.ResourceBounds.L1Gas.MaxAmount = starknetrpc.U64(starknetutils.BigIntToFelt(maxGas).String()) // TODO: add margin - tx.ResourceBounds.L2Gas.MaxPricePerUnit = starknetrpc.U128(friEstimate.GasPrice.String()) + tx.ResourceBounds.L1Gas.MaxPricePerUnit = starknetrpc.U128(friEstimate.GasPrice.String()) - txm.lggr.Infow("Set resource bounds", "L2MaxAmount", tx.ResourceBounds.L2Gas.MaxAmount, "L2MaxPricePerUnit", tx.ResourceBounds.L2Gas.MaxPricePerUnit) + txm.lggr.Infow("Set resource bounds", "L1MaxAmount", tx.ResourceBounds.L1Gas.MaxAmount, "L1MaxPricePerUnit", tx.ResourceBounds.L1Gas.MaxPricePerUnit) // Re-sign transaction now that we've determined MaxFee // TODO: SignInvokeTransaction for V3 is missing so we do it by hand From 491bcdd2a221c96eac4e1cb630ccda635c65ca57 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 29 Feb 2024 08:19:08 +0100 Subject: [PATCH 24/39] Enabled other tests as well --- integration-tests/smoke/ocr2_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 58aa03277..c9f0213fa 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -32,10 +32,10 @@ func TestOCRBasic(t *testing.T) { env map[string]string }{ {name: "embedded"}, - //{name: "plugins", env: map[string]string{ - // "CL_MEDIAN_CMD": "chainlink-feeds", - // "CL_SOLANA_CMD": "chainlink-solana", - //}}, + {name: "plugins", env: map[string]string{ + "CL_MEDIAN_CMD": "chainlink-feeds", + "CL_SOLANA_CMD": "chainlink-solana", + }}, } { config, err := tc.GetConfig("Smoke", tc.OCR2) if err != nil { From 03c45288d8d447e922568a75952d0b113caa2527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Thu, 29 Feb 2024 16:45:10 +0900 Subject: [PATCH 25/39] Improve error logging --- relayer/pkg/chainlink/txm/txm.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index 640989da9..7b2453983 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -19,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/utils" + ethrpc "github.com/ethereum/go-ethereum/rpc" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/starknet" ) @@ -199,12 +200,11 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun feeEstimate, err := account.EstimateFee(ctx, []starknetrpc.BroadcastTxn{tx}, simFlags, starknetrpc.BlockID{Tag: "latest"}) if err != nil { var data any - var rpcError *starknetrpc.RPCError - if errors.As(err, &rpcError) { - data = rpcError.Data() + if err, ok := err.(ethrpc.DataError); ok { + data = err.ErrorData() } txm.lggr.Errorw("failed to estimate fee", "error", err, "data", data) - return txhash, fmt.Errorf("failed to estimate fee: %+w", err) + return txhash, fmt.Errorf("failed to estimate fee: %T %+w", err, err) } txm.lggr.Infow("Account", "account", account.AccountAddress) @@ -252,9 +252,8 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun if err != nil { // TODO: handle initial broadcast errors - what kind of errors occur? var data any - var rpcError *starknetrpc.RPCError - if errors.As(err, &rpcError) { - data = rpcError.Data() + if err, ok := err.(ethrpc.DataError); ok { + data = err.ErrorData() } txm.lggr.Errorw("failed to invoke tx", "error", err, "data", data) return txhash, fmt.Errorf("failed to invoke tx: %+w", err) From 7c3458f3889d6a2267f2217f673474c7a7dced03 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Fri, 1 Mar 2024 07:33:21 +0100 Subject: [PATCH 26/39] Linting fix and plugins support --- integration-tests/common/gauntlet_common.go | 1 - integration-tests/smoke/ocr2_test.go | 4 ++++ packages-ts/starknet-gauntlet-cli/networks/.env.testnet | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index ad056dc71..da1d5ad1f 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -9,7 +9,6 @@ import ( ) var ( - ethAddressSepolia = "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7" strkAddressSepolia = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" nAccount string ) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index c9f0213fa..685433a28 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -46,6 +46,10 @@ func TestOCRBasic(t *testing.T) { err = os.Setenv("INTERNAL_DOCKER_REPO", *config.Common.InternalDockerRepo) require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") test := test + if test.name == "plugins" { + pluginImage := *config.ChainlinkImage.Image + "-plugins" + config.ChainlinkImage.Image = &pluginImage + } t.Run(test.name, func(t *testing.T) { t.Parallel() logging.Init() diff --git a/packages-ts/starknet-gauntlet-cli/networks/.env.testnet b/packages-ts/starknet-gauntlet-cli/networks/.env.testnet index 1011ef7a1..0ca97eb16 100644 --- a/packages-ts/starknet-gauntlet-cli/networks/.env.testnet +++ b/packages-ts/starknet-gauntlet-cli/networks/.env.testnet @@ -1 +1 @@ -NODE_URL=https://starknet-testnet.public.blastapi.io +NODE_URL=https://starknet-sepolia.public.blastapi.io/rpc/v0_6 \ No newline at end of file From 57cedea9c926a041ab341839311cc3dac57362c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Fri, 1 Mar 2024 15:58:18 +0900 Subject: [PATCH 27/39] scarb: OZ modified their v0.9.0 tag --- contracts/Scarb.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Scarb.lock b/contracts/Scarb.lock index 2ba9d1f72..9d2d099e8 100644 --- a/contracts/Scarb.lock +++ b/contracts/Scarb.lock @@ -11,4 +11,4 @@ dependencies = [ [[package]] name = "openzeppelin" version = "0.9.0" -source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.9.0#861fc416f87addbe23a3b47f9d19ab27c10d5dc8" +source = "git+https://github.com/OpenZeppelin/cairo-contracts.git?tag=v0.9.0#364db5b1aecc1335d2e65db887291d19aa28937d" From c1ca385017e89aceb75ab558c5a1d2ec8115b98b Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Mon, 4 Mar 2024 07:37:03 +0100 Subject: [PATCH 28/39] Adding plugins tests --- integration-tests/smoke/ocr2_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index 685433a28..f8759439e 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -46,11 +46,11 @@ func TestOCRBasic(t *testing.T) { err = os.Setenv("INTERNAL_DOCKER_REPO", *config.Common.InternalDockerRepo) require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") test := test - if test.name == "plugins" { - pluginImage := *config.ChainlinkImage.Image + "-plugins" - config.ChainlinkImage.Image = &pluginImage - } t.Run(test.name, func(t *testing.T) { + if test.name == "plugins" { + pluginImage := *config.ChainlinkImage.Version + "-plugins" + config.ChainlinkImage.Version = &pluginImage + } t.Parallel() logging.Init() state, err := common.NewOCRv2State(t, "smoke-ocr2", &config) From f34bd9df11609c1bc769aa811a13c30662706371 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Mon, 4 Mar 2024 12:08:39 +0100 Subject: [PATCH 29/39] Fixing plugins tests --- .github/workflows/integration-tests-smoke.yml | 6 +++--- Makefile | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 9d385fb16..8c38ac8d4 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -112,9 +112,9 @@ jobs: strategy: matrix: image: - - name: "" + - name: embedded tag-suffix: "" - - name: (plugins) + - name: plugins tag-suffix: -plugins env: TEST_SUITE: smoke @@ -182,7 +182,7 @@ jobs: uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - test_command_to_run: nix develop -c helm repo update && make test-integration-smoke-ci + test_command_to_run: nix develop -c helm repo update && make test=${{ matrix.image.name }} test-integration-smoke-ci test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download cl_repo: ${{ env.CL_ECR }} cl_image_tag: starknet.${{ github.sha }}${{ matrix.image.tag-suffix }} diff --git a/Makefile b/Makefile index a2bc8c5be..79cb1feca 100644 --- a/Makefile +++ b/Makefile @@ -200,12 +200,12 @@ test-integration-smoke: test-integration-prep .PHONY: test-integration-smoke-ci test-integration-smoke-ci: cd integration-tests/ && \ - go test --timeout=2h -v -count=1 -json ./smoke + go test --timeout=2h -v -count=1 -run TestOCRBasic/$(test) -json ./smoke .PHONY: test-integration-soak test-integration-soak: test-integration-prep cd integration-tests/ && \ - go test --timeout=1h -v -json./soak + go test --timeout=1h -v -json ./soak # CI Already has already ran test-integration-prep .PHONY: test-integration-soak-ci From 86db013e6ddc0d52e1ef69591588ec72adea0c7e Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Mon, 4 Mar 2024 17:17:55 +0100 Subject: [PATCH 30/39] Added some feedback --- .github/workflows/integration-tests-smoke.yml | 10 -- contracts/.tool-versions | 1 - integration-tests/common/common.go | 1 - ops/devnet/devnet.go | 137 ------------------ ops/gauntlet/gauntlet_starknet.go | 4 - 5 files changed, 153 deletions(-) delete mode 100644 contracts/.tool-versions delete mode 100644 ops/devnet/devnet.go diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 8c38ac8d4..3a32648c2 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -16,7 +16,6 @@ concurrency: cancel-in-progress: true env: - CHAINLINK_ENV_USER: ${{ github.actor }} TEST_LOG_LEVEL: debug CL_ECR: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-starknet-tests:${{ github.sha }} @@ -117,15 +116,6 @@ jobs: - name: plugins tag-suffix: -plugins env: - TEST_SUITE: smoke - TEST_ARGS: -test.timeout 1h - PRIVATE_KEY: ${{ secrets.GOERLI_PRIVATE_KEY }} - ACCOUNT: ${{ secrets.GOERLI_ACCOUNT }} - TTL: 3h - TEST_DURATION: 15m - NODE_COUNT: 5 - CHAINLINK_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink - CHAINLINK_VERSION: starknet.${{ github.sha }} INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com scarb_version: "v2.5.4" permissions: diff --git a/contracts/.tool-versions b/contracts/.tool-versions deleted file mode 100644 index 49edbb536..000000000 --- a/contracts/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -scarb 2.5.4 diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index fdfa2ceeb..2ff4e2acc 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -284,7 +284,6 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st JobType: "bootstrap", OCR2OracleSpec: oracleSpec, } - fmt.Println(jobSpec.String()) _, _, err := cc.ChainlinkNodes[0].CreateJob(jobSpec) if err != nil { return err diff --git a/ops/devnet/devnet.go b/ops/devnet/devnet.go deleted file mode 100644 index 8c54c83a7..000000000 --- a/ops/devnet/devnet.go +++ /dev/null @@ -1,137 +0,0 @@ -package devnet - -import ( - "context" - "fmt" - "strings" - "time" - - starknetutils "github.com/NethermindEth/starknet.go/utils" - "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2" - - "github.com/go-resty/resty/v2" - "github.com/rs/zerolog/log" -) - -type StarknetDevnetClient struct { - ctx context.Context - cancel context.CancelFunc - client *resty.Client - dumpPath string -} - -func (devnet *StarknetDevnetClient) NewStarknetDevnetClient(rpcUrl string, dumpPath string) *StarknetDevnetClient { - ctx, cancel := context.WithCancel(context.Background()) - return &StarknetDevnetClient{ - ctx: ctx, - cancel: cancel, - client: resty.New().SetBaseURL(rpcUrl), - dumpPath: dumpPath, - } -} - -// AutoSyncL1 auto calls /flush/ every 2 seconds to sync L1<>L2 -func (devnet *StarknetDevnetClient) AutoSyncL1() { - t := time.NewTicker(2 * time.Second) - go func() { - for { - select { - case <-devnet.ctx.Done(): - log.Debug().Msg("Shutting down L1 sync") - return - case <-t.C: - log.Debug().Msg("Syncing L1") - _, err := devnet.client.R().Post("/postman/flush") - if err != nil { - log.Error().Err(err).Msg("failed to sync L1") - } - } - } - }() -} - -// AutoDumpState dumps devnet state every 10 sec -func (devnet *StarknetDevnetClient) AutoDumpState() { - t := time.NewTicker(20 * time.Minute) - go func() { - for { - select { - case <-devnet.ctx.Done(): - log.Debug().Msg("Shutting down devnet dump") - return - case <-t.C: - log.Debug().Msg("Dumping state") - _, err := devnet.client.R().SetBody(map[string]any{ - "path": devnet.dumpPath, - }).Post("/dump") - if err != nil { - log.Error().Err(err).Msg("Failed to dump devnet state") - } - } - } - }() -} - -// AutoLoadState auto loads last saved devnet state on contract not found -func (devnet *StarknetDevnetClient) AutoLoadState(client *ocr2.Client, ocrAddress string) { - addr, _ := starknetutils.HexToFelt(ocrAddress) - t := time.NewTicker(15 * time.Second) - go func() { - for { - select { - case <-devnet.ctx.Done(): - log.Debug().Msg("Shutting down devnet dump") - return - case <-t.C: - log.Debug().Msg("Checking for devnet OCR contract errors") - - _, err := client.LatestTransmissionDetails(devnet.ctx, addr) - if err != nil && strings.Contains(err.Error(), "is not deployed") { - _, err = devnet.client.R().SetBody(map[string]any{ - "path": devnet.dumpPath, - }).Post("/load") - if err != nil { - log.Error().Err(err).Msg("Failed to dump devnet state") - } - } - - } - } - }() -} - -// FundAccounts Funds provided accounts with 100 eth each -func (devnet *StarknetDevnetClient) FundAccounts(l2AccList []string) error { - for _, key := range l2AccList { - res, err := devnet.client.R().SetBody(map[string]any{ - "address": key, - "amount": 900000000000000000, - }).Post("/mint") - if err != nil { - return err - } - log.Info().Msg(fmt.Sprintf("Funding account (WEI): %s", string(res.Body()))) - res, err = devnet.client.R().SetBody(map[string]any{ - "address": key, - "amount": 900000000000000000, - "unit": "FRI", - }).Post("/mint") - if err != nil { - return err - } - log.Info().Msg(fmt.Sprintf("Funding account (FRI): %s", string(res.Body()))) - } - return nil -} - -// LoadL1MessagingContract loads and sets up the L1 messaging contract and URL -func (devnet *StarknetDevnetClient) LoadL1MessagingContract(l1RpcUrl string) error { - resp, err := devnet.client.R().SetBody(map[string]any{ - "networkUrl": l1RpcUrl, - }).Post("/postman/load_l1_messaging_contract") - if err != nil { - return err - } - log.Warn().Interface("Response", resp.String()).Msg("Set up L1 messaging contract") - return nil -} diff --git a/ops/gauntlet/gauntlet_starknet.go b/ops/gauntlet/gauntlet_starknet.go index d2c1f4749..2b11779ce 100644 --- a/ops/gauntlet/gauntlet_starknet.go +++ b/ops/gauntlet/gauntlet_starknet.go @@ -90,10 +90,6 @@ func (sg *StarknetGauntlet) InstallDependencies() error { if err != nil { return err } - _, err = sg.G.ExecCommand([]string{"build"}, *sg.options) - if err != nil { - return err - } sg.G.Command = "gauntlet" return nil } From b1fc757f71d27be1ce5b9da85c8d805d92219c95 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 08:46:01 +0100 Subject: [PATCH 31/39] Feedback from PR --- .github/workflows/integration-tests-smoke.yml | 21 ++++------ integration-tests/common/common.go | 39 ++++++------------- integration-tests/common/gauntlet_common.go | 9 +---- integration-tests/common/test_common.go | 4 +- integration-tests/config/config.go | 32 +++++++++++++++ integration-tests/docker/test_env/stark.go | 6 ++- integration-tests/testconfig/default.toml | 1 + integration-tests/testconfig/testconfig.go | 26 +++++++------ 8 files changed, 75 insertions(+), 63 deletions(-) create mode 100644 integration-tests/config/config.go diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 3a32648c2..f81a2e82b 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -111,13 +111,14 @@ jobs: strategy: matrix: image: - - name: embedded + - name: "" tag-suffix: "" + test-name: embedded - name: plugins tag-suffix: -plugins + test-name: plugins env: INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com - scarb_version: "v2.5.4" permissions: checks: write pull-requests: write @@ -139,14 +140,8 @@ jobs: uses: cachix/install-nix-action@29bd9290ef037a3ecbdafe83cbd2185e9dd0fa0a # v20 with: nix_path: nixpkgs=channel:nixos-unstable - - name: Setup Scarb for Linux - id: install-scarb - shell: bash - run: | - wget https://github.com/software-mansion/scarb/releases/download/${{ env.scarb_version }}/scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - tar -xvzf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl.tar.gz - mv -vf scarb-${{ env.scarb_version }}-x86_64-unknown-linux-musl scarb-build - echo "$GITHUB_WORKSPACE/scarb-build/bin" >> $GITHUB_PATH + - name: Install Cairo + uses: ./.github/actions/install-cairo - name: Build contracts run: | cd contracts && scarb --profile release build @@ -154,8 +149,8 @@ jobs: run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml [ChainlinkImage] - image="${{ env.CHAINLINK_IMAGE }}" - version="${{ env.CHAINLINK_VERSION }}" + image="${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink" + version="starknet.${{ github.sha }}${{ matrix.image.tag-suffix }}" [Network] selected_networks=["SIMULATED"] [Common] @@ -172,7 +167,7 @@ jobs: uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 with: aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - test_command_to_run: nix develop -c helm repo update && make test=${{ matrix.image.name }} test-integration-smoke-ci + test_command_to_run: nix develop -c helm repo update && make test=${{ matrix.image.test-name }} test-integration-smoke-ci test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download cl_repo: ${{ env.CL_ECR }} cl_image_tag: starknet.${{ github.sha }}${{ matrix.image.tag-suffix }} diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index 2ff4e2acc..dd3cca152 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -2,6 +2,7 @@ package common import ( "fmt" + chainconfig "github.com/smartcontractkit/chainlink-starknet/integration-tests/config" "github.com/smartcontractkit/chainlink-starknet/integration-tests/testconfig" "github.com/smartcontractkit/chainlink-starknet/ops/devnet" "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/config" @@ -32,15 +33,8 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/services/relay" ) -var ( - chainName = "starknet" - chainIdLocalNet = "SN_GOERLI" - chainIdTestnet = "SN_SEPOLIA" - DefaultL2RPCInternal = "http://starknet-dev:5000" -) - type Common struct { - ChainDetails *ChainDetails + ChainDetails *chainconfig.Config TestEnvDetails *TestEnvDetails Env *environment.Environment RPCDetails *RPCDetails @@ -48,11 +42,6 @@ type Common struct { TestConfig *testconfig.TestConfig } -type ChainDetails struct { - ChainName string - ChainId string -} - type TestEnvDetails struct { TestDuration time.Duration K8Config *environment.Config @@ -71,8 +60,7 @@ type RPCDetails struct { func New(testConfig *testconfig.TestConfig) *Common { var c *Common - l2RpcUrl := DefaultL2RPCInternal - chainId := chainIdLocalNet + chainDetails := chainconfig.DevnetConfig() duration, err := time.ParseDuration(*testConfig.OCR2.TestDuration) if err != nil { @@ -80,22 +68,19 @@ func New(testConfig *testconfig.TestConfig) *Common { } if *testConfig.Common.Network == "testnet" { - l2RpcUrl = *testConfig.Common.L2RPCUrl - chainId = chainIdTestnet + chainDetails = chainconfig.SepoliaConfig() + chainDetails.L2RPCInternal = *testConfig.Common.L2RPCUrl } c = &Common{ - TestConfig: testConfig, - ChainDetails: &ChainDetails{ - ChainName: chainName, - ChainId: chainId, - }, + TestConfig: testConfig, + ChainDetails: chainDetails, TestEnvDetails: &TestEnvDetails{ TestDuration: duration, }, RPCDetails: &RPCDetails{ P2PPort: "6690", - RPCL2Internal: l2RpcUrl, + RPCL2Internal: chainDetails.L2RPCInternal, }, } @@ -152,7 +137,7 @@ func (c *Common) Default(t *testing.T, namespacePrefix string) (*Common, error) func (c *Common) DefaultNodeConfig() *cl.Config { starkConfig := config.TOMLConfig{ Enabled: ptr.Ptr(true), - ChainID: ptr.Ptr(c.ChainDetails.ChainId), + ChainID: ptr.Ptr(c.ChainDetails.ChainID), Nodes: []*config.Node{ { Name: ptr.Ptr("primary"), @@ -236,11 +221,11 @@ func (c *Common) CreateNodeKeysBundle(nodes []*client.ChainlinkClient) ([]client } peerID := p2pkeys.Data[0].Attributes.PeerID - txKey, _, err := n.CreateTxKey(chainName, c.ChainDetails.ChainId) + txKey, _, err := n.CreateTxKey(c.ChainDetails.ChainName, c.ChainDetails.ChainID) if err != nil { return nil, err } - ocrKey, _, err := n.CreateOCR2Key(chainName) + ocrKey, _, err := n.CreateOCR2Key(c.ChainDetails.ChainName) if err != nil { return nil, err } @@ -269,7 +254,7 @@ func (c *Common) CreateJobsForContract(cc *ChainlinkClient, observationSource st bootstrapRelayConfig := job.JSONConfig{ "nodeName": fmt.Sprintf("starknet-OCRv2-%s-%s", "node", uuid.New().String()), "accountAddress": accountAddresses[0], - "chainID": c.ChainDetails.ChainId, + "chainID": c.ChainDetails.ChainID, } oracleSpec := job.OCR2OracleSpec{ diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index da1d5ad1f..5ff336c49 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -8,11 +8,6 @@ import ( "os" ) -var ( - strkAddressSepolia = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" - nAccount string -) - func (m *OCRv2TestState) fundNodes() ([]string, error) { l := utils.GetTestLogger(m.TestConfig.T) var nAccounts []string @@ -21,7 +16,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { if key.TXKey.Data.Attributes.StarkKey == "" { return nil, errors.New("stark key can't be empty") } - nAccount, err = m.Clients.GauntletClient.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) + nAccount, err := m.Clients.GauntletClient.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) if err != nil { return nil, err } @@ -36,7 +31,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = m.Clients.GauntletClient.TransferToken(strkAddressSepolia, key, "100000000000000000") // Transferring 1 ETH to each node + _, err = m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "100000000000000000") // Transferring 1 ETH to each node if err != nil { return nil, err } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go index 308e5ca82..fcec8f904 100644 --- a/integration-tests/common/test_common.go +++ b/integration-tests/common/test_common.go @@ -148,7 +148,7 @@ func (m *OCRv2TestState) DeployCluster() { } else { // Otherwise use docker env, err := test_env.NewTestEnv() require.NoError(m.TestConfig.T, err) - stark := test_env_starknet.NewStarknet([]string{env.Network.Name}) + stark := test_env_starknet.NewStarknet([]string{env.Network.Name}, *m.Common.TestConfig.Common.DevnetImage) err = stark.StartContainer() require.NoError(m.TestConfig.T, err) @@ -200,7 +200,7 @@ func (m *OCRv2TestState) DeployCluster() { require.NoError(m.TestConfig.T, m.TestConfig.err) } lggr := logger.Nop() - m.Clients.StarknetClient, m.TestConfig.err = starknet.NewClient(m.Common.ChainDetails.ChainId, m.Common.RPCDetails.RPCL2External, lggr, &rpcRequestTimeout) + m.Clients.StarknetClient, m.TestConfig.err = starknet.NewClient(m.Common.ChainDetails.ChainID, m.Common.RPCDetails.RPCL2External, lggr, &rpcRequestTimeout) require.NoError(m.TestConfig.T, m.TestConfig.err, "Creating starknet client should not fail") m.Clients.OCR2Client, m.TestConfig.err = ocr2.NewClient(m.Clients.StarknetClient, lggr) require.NoError(m.TestConfig.T, m.TestConfig.err, "Creating ocr2 client should not fail") diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go new file mode 100644 index 000000000..5bab0225a --- /dev/null +++ b/integration-tests/config/config.go @@ -0,0 +1,32 @@ +package config + +var ( + starkTokenAddress = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d" +) + +type Config struct { + ChainName string + ChainID string + StarkTokenAddress string + L2RPCInternal string +} + +func SepoliaConfig() *Config { + return &Config{ + ChainName: "starknet", + ChainID: "SN_SEPOLIA", + StarkTokenAddress: starkTokenAddress, + // Will be overridden if set in toml + L2RPCInternal: "https://starknet-sepolia.public.blastapi.io/rpc/v0_6", + } +} + +func DevnetConfig() *Config { + return &Config{ + ChainName: "starknet", + ChainID: "SN_GOERLI", + StarkTokenAddress: starkTokenAddress, + // Will be overridden if set in toml + L2RPCInternal: "http://starknet-dev:5000", + } +} diff --git a/integration-tests/docker/test_env/stark.go b/integration-tests/docker/test_env/stark.go index a4cdcbfab..a1f3bd3a9 100644 --- a/integration-tests/docker/test_env/stark.go +++ b/integration-tests/docker/test_env/stark.go @@ -25,10 +25,12 @@ type Starknet struct { InternalHttpUrl string t *testing.T l zerolog.Logger + Image string } -func NewStarknet(networks []string, opts ...test_env.EnvComponentOption) *Starknet { +func NewStarknet(networks []string, image string, opts ...test_env.EnvComponentOption) *Starknet { ms := &Starknet{ + Image: image, EnvComponent: test_env.EnvComponent{ ContainerName: "starknet", Networks: networks, @@ -94,7 +96,7 @@ func (s *Starknet) StartContainer() error { func (s *Starknet) getContainerRequest() (*tc.ContainerRequest, error) { return &tc.ContainerRequest{ Name: s.ContainerName, - Image: "shardlabs/starknet-devnet-rs:b41e566a3f17aa0e51871f02d5165959e50ce358", + Image: s.Image, ExposedPorts: []string{test_env.NatPortFormat(STARK_HTTP_PORT)}, Networks: s.Networks, WaitingFor: tcwait.ForLog("Starknet Devnet listening"). diff --git a/integration-tests/testconfig/default.toml b/integration-tests/testconfig/default.toml index a3e26d4b8..c9883cc37 100644 --- a/integration-tests/testconfig/default.toml +++ b/integration-tests/testconfig/default.toml @@ -26,6 +26,7 @@ inside_k8 = false network = "localnet" user = "satoshi" stateful_db = false +devnet_image = "shardlabs/starknet-devnet-rs:b41e566a3f17aa0e51871f02d5165959e50ce358" [OCR2] node_count = 6 diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 047aa2445..1cc50dabf 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -117,6 +117,7 @@ type Common struct { Account *string `toml:"account"` Stateful *bool `toml:"stateful_db"` InternalDockerRepo *string `toml:"internal_docker_repo"` + DevnetImage *string `toml:"devnet_image"` } func (c *Common) Validate() error { @@ -126,8 +127,21 @@ func (c *Common) Validate() error { switch *c.Network { case "localnet": + if c.DevnetImage == nil { + return fmt.Errorf("devnet_image must be set") + } break case "testnet": + if c.PrivateKey == nil { + return fmt.Errorf("private_key must be set") + } + if c.L2RPCUrl == nil { + return fmt.Errorf("l2_rpc_url must be set") + } + + if c.Account == nil { + return fmt.Errorf("account must be set") + } break default: return fmt.Errorf("network must be either 'localnet' or 'testnet'") @@ -149,18 +163,6 @@ func (c *Common) Validate() error { return fmt.Errorf("stateful_db state for db must be set") } - if c.L2RPCUrl == nil && *c.Network == "testnet" { - return fmt.Errorf("l2_rpc_url must be set") - } - - if c.Account == nil && *c.Network == "testnet" { - return fmt.Errorf("account must be set") - } - - if c.PrivateKey == nil && *c.Network == "testnet" { - return fmt.Errorf("private_key must be set") - } - return nil } From 61ce4f5bd4dc91c9f7160ddf14990308c24cf8ae Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 08:47:02 +0100 Subject: [PATCH 32/39] Feedback from PR --- integration-tests/common/gauntlet_common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index 5ff336c49..df3e69c58 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -31,7 +31,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "100000000000000000") // Transferring 1 ETH to each node + _, err = m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "100000000000000000") // Transferring 0.1 STRK to each node if err != nil { return nil, err } From aa0e2b736e58088a3f740c064bb3611d980db8e4 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 09:11:53 +0100 Subject: [PATCH 33/39] Linting fixes --- .github/workflows/integration-tests-smoke.yml | 2 +- .github/workflows/integration-tests-soak.yml | 132 ------------------ integration-tests/common/gauntlet_common.go | 9 +- integration-tests/config/config.go | 2 + integration-tests/testconfig/testconfig.go | 2 - 5 files changed, 5 insertions(+), 142 deletions(-) delete mode 100644 .github/workflows/integration-tests-soak.yml diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index f81a2e82b..f8e35435d 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -144,7 +144,7 @@ jobs: uses: ./.github/actions/install-cairo - name: Build contracts run: | - cd contracts && scarb --profile release build + cd contracts && scarb --profile release build && yarn build - name: Generate config overrides run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml diff --git a/.github/workflows/integration-tests-soak.yml b/.github/workflows/integration-tests-soak.yml deleted file mode 100644 index 20696281f..000000000 --- a/.github/workflows/integration-tests-soak.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: Integration Tests - Soak - -on: - push: - branches: - - main - - develop - workflow_dispatch: - inputs: - cl_branch_ref: - description: Chainlink repo branch to integrate with - required: true - default: develop - type: string - l2_rpc_url: - description: Override default RPC url which points to local devnet (Optional) - required: false - type: string - node_count: - description: Number of ocr nodes - required: true - default: 5 - type: string - ttl: - description: TTL for namespace - required: true - default: 72h - type: string - test_duration: - description: Duration of soak - required: true - default: 72h - type: string - private_key: - description: Private key, ignore for devnet - required: false - type: string - account: - description: Account address, ignore for devnet - required: false - -# Only run 1 of this workflow at a time per PR -concurrency: - group: integration-tests-starknet-${{ github.ref }} - cancel-in-progress: true - -env: - ENV_JOB_IMAGE: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink-starknet-tests:${{ github.sha }} - -jobs: - build_custom_chainlink_image: - name: Build Custom CL Image - runs-on: ubuntu-latest - environment: integration - permissions: - id-token: write - contents: read - steps: - - name: Check if image exists - id: check-image - uses: smartcontractkit/chainlink-github-actions/docker/image-exists@2c9f401149f6c25fb632067b7e6626aebeee5d69 # v2.1.6 - with: - repository: chainlink - tag: starknet.${{ github.sha }} - AWS_REGION: ${{ secrets.QA_AWS_REGION }} - AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - - name: Build Image - if: steps.check-image.outputs.exists == 'false' - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/build-image@2c9f401149f6c25fb632067b7e6626aebeee5d69 - with: - cl_repo: smartcontractkit/chainlink - # By default we are integrating with CL develop - cl_ref: ${{ github.event.inputs.cl_branch_ref }} - # commit of the caller branch - dep_starknet_sha: ${{ github.event.pull_request.head.sha || github.sha }} - push_tag: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink:starknet.${{ github.sha }} - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_PRIVATE_GHA_PULL: ${{ secrets.QA_PRIVATE_GHA_PULL }} - - name: Print Chainlink Image Built - run: | - echo "### chainlink image tag used for this test run :link:" >>$GITHUB_STEP_SUMMARY - echo "\`starknet.${{ github.sha }}\`" >>$GITHUB_STEP_SUMMARY - - run_tests: - name: Run Soak Tests - runs-on: ubuntu-latest - needs: [ build_custom_chainlink_image ] - environment: integration - env: - CHAINLINK_ENV_USER: ${{ github.actor }} - L2_RPC_URL: ${{ github.event.inputs.l2_rpc_url }} - TEST_DURATION: ${{ github.event.inputs.test_duration }} - NODE_COUNT: ${{ github.event.inputs.node_count }} - PRIVATE_KEY: ${{ github.event.inputs.private_key }} - ACCOUNT: ${{ github.event.inputs.account }} - TTL: ${{ github.event.inputs.ttl }} - DETACH_RUNNER: true - TEST_SUITE: soak - TEST_ARGS: -test.timeout ${{ github.event.inputs.ttl }} - TEST_LOG_LEVEL: debug - INTERNAL_DOCKER_REPO: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com - permissions: - checks: write - pull-requests: write - id-token: write - contents: read - steps: - - name: Checkout the repo - uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 - - name: Install Nix - uses: cachix/install-nix-action@29bd9290ef037a3ecbdafe83cbd2185e9dd0fa0a # v20 - with: - nix_path: nixpkgs=channel:nixos-unstable - - name: Build Image - uses: ./.github/actions/build-test-image - with: - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} - - name: Run Tests - uses: smartcontractkit/chainlink-github-actions/chainlink-testing-framework/run-tests@ea889b3133bd7f16ab19ba4ba130de5d9162c669 # v2.3.4 - with: - test_command_to_run: nix develop -c make test-integration-soak-ci - test_download_vendor_packages_command: cd integration-tests && nix develop -c go mod download - cl_repo: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/chainlink - cl_image_tag: starknet.${{ github.sha }} - token: ${{ secrets.GITHUB_TOKEN }} - go_mod_path: ./integration-tests/go.mod - QA_AWS_REGION: ${{ secrets.QA_AWS_REGION }} - QA_AWS_ROLE_TO_ASSUME: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - QA_KUBECONFIG: ${{ secrets.QA_KUBECONFIG }} diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/common/gauntlet_common.go index df3e69c58..f293f4a5f 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/common/gauntlet_common.go @@ -11,7 +11,6 @@ import ( func (m *OCRv2TestState) fundNodes() ([]string, error) { l := utils.GetTestLogger(m.TestConfig.T) var nAccounts []string - var err error for _, key := range m.GetNodeKeys() { if key.TXKey.Data.Attributes.StarkKey == "" { return nil, errors.New("stark key can't be empty") @@ -23,15 +22,11 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { nAccounts = append(nAccounts, nAccount) } - if err != nil { - return nil, err - } - if *m.Common.TestConfig.Common.Network == "testnet" { for _, key := range nAccounts { // We are not deploying in parallel here due to testnet limitations (429 too many requests) l.Debug().Msg(fmt.Sprintf("Funding node with address: %s", key)) - _, err = m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "100000000000000000") // Transferring 0.1 STRK to each node + _, err := m.Clients.GauntletClient.TransferToken(m.Common.ChainDetails.StarkTokenAddress, key, "100000000000000000") // Transferring 0.1 STRK to each node if err != nil { return nil, err } @@ -50,7 +45,7 @@ func (m *OCRv2TestState) fundNodes() ([]string, error) { res, err = m.TestConfig.Resty.R().SetBody(map[string]any{ "address": key, "amount": 900000000000000000, - "unit": "FRI", + "unit": m.Common.ChainDetails.TokenName, }).Post("/mint") if err != nil { return nil, err diff --git a/integration-tests/config/config.go b/integration-tests/config/config.go index 5bab0225a..d54e05c12 100644 --- a/integration-tests/config/config.go +++ b/integration-tests/config/config.go @@ -9,6 +9,7 @@ type Config struct { ChainID string StarkTokenAddress string L2RPCInternal string + TokenName string } func SepoliaConfig() *Config { @@ -28,5 +29,6 @@ func DevnetConfig() *Config { StarkTokenAddress: starkTokenAddress, // Will be overridden if set in toml L2RPCInternal: "http://starknet-dev:5000", + TokenName: "FRI", } } diff --git a/integration-tests/testconfig/testconfig.go b/integration-tests/testconfig/testconfig.go index 1cc50dabf..c47684018 100644 --- a/integration-tests/testconfig/testconfig.go +++ b/integration-tests/testconfig/testconfig.go @@ -130,7 +130,6 @@ func (c *Common) Validate() error { if c.DevnetImage == nil { return fmt.Errorf("devnet_image must be set") } - break case "testnet": if c.PrivateKey == nil { return fmt.Errorf("private_key must be set") @@ -142,7 +141,6 @@ func (c *Common) Validate() error { if c.Account == nil { return fmt.Errorf("account must be set") } - break default: return fmt.Errorf("network must be either 'localnet' or 'testnet'") } From 53da15062cad633460d0abe78fe1ad0080942a10 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 09:15:16 +0100 Subject: [PATCH 34/39] Linting fixes --- .github/workflows/integration-tests-smoke.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index f8e35435d..bc0abd1b5 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -145,6 +145,9 @@ jobs: - name: Build contracts run: | cd contracts && scarb --profile release build && yarn build + - name: Build gauntlet + run: | + yarn build - name: Generate config overrides run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml From ef06dc9c12195c9d2ac4aa5f543203b05e549a57 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 09:15:32 +0100 Subject: [PATCH 35/39] Linting fixes --- .github/workflows/integration-tests-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index bc0abd1b5..9ee174a1b 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -144,7 +144,7 @@ jobs: uses: ./.github/actions/install-cairo - name: Build contracts run: | - cd contracts && scarb --profile release build && yarn build + cd contracts && scarb --profile release build - name: Build gauntlet run: | yarn build From 2f97df7f7cffc0f8f7ce0a23fcf386ba60fb5323 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 09:28:12 +0100 Subject: [PATCH 36/39] Yarn step added --- .github/workflows/integration-tests-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration-tests-smoke.yml b/.github/workflows/integration-tests-smoke.yml index 9ee174a1b..aa43faabd 100644 --- a/.github/workflows/integration-tests-smoke.yml +++ b/.github/workflows/integration-tests-smoke.yml @@ -147,7 +147,7 @@ jobs: cd contracts && scarb --profile release build - name: Build gauntlet run: | - yarn build + yarn install && yarn build - name: Generate config overrides run: | # https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/config/README.md cat << EOF > config.toml From a6fd9e3597cc73b3fe1e0276088943e79be864bd Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Tue, 5 Mar 2024 10:50:49 +0100 Subject: [PATCH 37/39] Removed plugins logic --- integration-tests/smoke/ocr2_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go index f8759439e..c9f0213fa 100644 --- a/integration-tests/smoke/ocr2_test.go +++ b/integration-tests/smoke/ocr2_test.go @@ -47,10 +47,6 @@ func TestOCRBasic(t *testing.T) { require.NoError(t, err, "Could not set INTERNAL_DOCKER_REPO") test := test t.Run(test.name, func(t *testing.T) { - if test.name == "plugins" { - pluginImage := *config.ChainlinkImage.Version + "-plugins" - config.ChainlinkImage.Version = &pluginImage - } t.Parallel() logging.Init() state, err := common.NewOCRv2State(t, "smoke-ocr2", &config) From c8179ff411b00e362048810af104605c93303285 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Mon, 11 Mar 2024 15:12:35 +0100 Subject: [PATCH 38/39] Added overrides to ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b917665d3..1ec5b7415 100644 --- a/.gitignore +++ b/.gitignore @@ -166,4 +166,4 @@ ztarrepo.tar.gz eslint-report.json .run.id .local-mock-server -overrides.toml +override*.toml From a780d1aadb490861b1a966536b8b57c02f907419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Tue, 12 Mar 2024 14:03:51 +0900 Subject: [PATCH 39/39] minor: Fix broken lint --- relayer/pkg/chainlink/txm/txm.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/relayer/pkg/chainlink/txm/txm.go b/relayer/pkg/chainlink/txm/txm.go index 7b2453983..54f68433d 100644 --- a/relayer/pkg/chainlink/txm/txm.go +++ b/relayer/pkg/chainlink/txm/txm.go @@ -200,8 +200,9 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun feeEstimate, err := account.EstimateFee(ctx, []starknetrpc.BroadcastTxn{tx}, simFlags, starknetrpc.BlockID{Tag: "latest"}) if err != nil { var data any - if err, ok := err.(ethrpc.DataError); ok { - data = err.ErrorData() + var dataErr ethrpc.DataError + if errors.As(err, &dataErr) { + data = dataErr.ErrorData() } txm.lggr.Errorw("failed to estimate fee", "error", err, "data", data) return txhash, fmt.Errorf("failed to estimate fee: %T %+w", err, err) @@ -252,8 +253,9 @@ func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accoun if err != nil { // TODO: handle initial broadcast errors - what kind of errors occur? var data any - if err, ok := err.(ethrpc.DataError); ok { - data = err.ErrorData() + var dataErr ethrpc.DataError + if errors.As(err, &dataErr) { + data = dataErr.ErrorData() } txm.lggr.Errorw("failed to invoke tx", "error", err, "data", data) return txhash, fmt.Errorf("failed to invoke tx: %+w", err)