diff --git a/integration-tests/common/client.go b/integration-tests/common/client.go new file mode 100644 index 00000000..44971cab --- /dev/null +++ b/integration-tests/common/client.go @@ -0,0 +1,70 @@ +package common + +import ( + "github.com/smartcontractkit/chainlink-env/environment" + "github.com/smartcontractkit/chainlink/integration-tests/client" +) + +type ChainlinkClient struct { + ChainlinkNodes []*client.Chainlink + NodeKeys []client.NodeKeysBundle + bTypeAttr *client.BridgeTypeAttributes + bootstrapPeers []client.P2PData +} + +// CreateKeys Creates node keys and defines chain and nodes for each node +func NewChainlinkClient(env *environment.Environment, chainName string, chainId string, tendermintURL string) (*ChainlinkClient, error) { + nodes, err := client.ConnectChainlinkNodes(env) + if err != nil { + return nil, err + } + nodeKeys, _, err := client.CreateNodeKeysBundle(nodes, chainName, chainId) + if err != nil { + return nil, err + } + for _, n := range nodes { + _, _, err = n.CreateCosmosChain(&client.CosmosChainAttributes{ + ChainID: chainId, + Config: client.CosmosChainConfig{}, + }) + if err != nil { + return nil, err + } + _, _, err = n.CreateCosmosNode(&client.CosmosNodeAttributes{ + Name: chainName, + CosmosChainID: chainId, + TendermintURL: tendermintURL, + }) + if err != nil { + return nil, err + } + } + + return &ChainlinkClient{ + ChainlinkNodes: nodes, + NodeKeys: nodeKeys, + }, nil +} + +func (cc *ChainlinkClient) LoadOCR2Config(accountAddresses []string) (*OCR2Config, error) { + var offChaiNKeys []string + var onChaiNKeys []string + var peerIds []string + var txKeys []string + var cfgKeys []string + for i, key := range cc.NodeKeys { + offChaiNKeys = append(offChaiNKeys, key.OCR2Key.Data.Attributes.OffChainPublicKey) + peerIds = append(peerIds, key.PeerID) + txKeys = append(txKeys, accountAddresses[i]) + onChaiNKeys = append(onChaiNKeys, key.OCR2Key.Data.Attributes.OnChainPublicKey) + cfgKeys = append(cfgKeys, key.OCR2Key.Data.Attributes.ConfigPublicKey) + } + + var payload = TestOCR2Config + payload.Signers = onChaiNKeys + payload.Transmitters = txKeys + payload.OffchainConfig.OffchainPublicKeys = offChaiNKeys + payload.OffchainConfig.PeerIds = peerIds + payload.OffchainConfig.ConfigPublicKeys = cfgKeys + return &payload, nil +} diff --git a/integration-tests/common/common.go b/integration-tests/common/common.go index c4b2911e..9734207d 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -26,19 +26,34 @@ const ( ChainBlockTimeSoak = "2s" ) +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 + """ + ` +) + type Common struct { - P2PPort string - ChainName string - ChainId string - NodeCount int - TTL time.Duration - Testnet bool - RPCUrl string - PrivateKey string - Account string - ClConfig map[string]any - K8Config *environment.Config - Env *environment.Environment + IsSoak bool + P2PPort string + ChainName string + ChainId string + NodeCount int + TTL time.Duration + RPCUrl string + PrivateKey string + Account string + ObservationSource string + JuelsPerFeeCoinSource string + ClConfig map[string]any + K8Config *environment.Config + Env *environment.Environment } // getEnv gets the environment variable if it exists and sets it for the remote runner @@ -50,48 +65,52 @@ func getEnv(v string) string { return val } -func New() *Common { - var err error - c := &Common{ - ChainName: chainName, - ChainId: chainID, - } +func getNodeCount() int { // Checking if count of OCR nodes is defined in ENV nodeCountSet := getEnv("NODE_COUNT") - if 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 { + if nodeCountSet == "" { panic("Please define NODE_COUNT") } + nodeCount, err := strconv.Atoi(nodeCountSet) + if err != nil { + panic(fmt.Sprintf("Please define a proper node count for the test: %v", err)) + } + return nodeCount +} - // Checking if TTL env var is set in ENV +func getTTL() time.Duration { ttlValue := getEnv("TTL") - if 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 { + if ttlValue == "" { panic("Please define TTL of env") } + duration, err := time.ParseDuration(ttlValue) + 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 +} - // Setting optional parameters - c.L2RPCUrl = getEnv("L2_RPC_URL") // Fetch L2 RPC url if defined - c.Testnet = c.L2RPCUrl != "" - c.PrivateKey = getEnv("PRIVATE_KEY") - c.Account = getEnv("ACCOUNT") - +func NewCommon() *Common { + c := &Common{ + IsSoak: getEnv("SOAK") != "", + ChainName: chainName, + ChainId: chainID, + NodeCount: getNodeCount(), + TTL: getTTL(), + RPCUrl: getEnv("RPC_URL"), + PrivateKey: getEnv("PRIVATE_KEY"), + Account: getEnv("ACCOUNT"), + ObservationSource: observationSource, + JuelsPerFeeCoinSource: juelsPerFeeCoinSource, + } return c } -func (c *Common) Default(t *testing.T) { +func (c *Common) SetDefaultEnvironment(t *testing.T) { c.K8Config = &environment.Config{ NamespacePrefix: "cosmos-ocr", TTL: c.TTL, @@ -99,8 +118,11 @@ func (c *Common) Default(t *testing.T) { } // These can be uncommented when toml configuration is supposrted for cosmos in the chainlink node wasmdUrl := fmt.Sprintf("http://%s:%d", "tendermint-rpc", 26657) - if c.Testnet { - wasmdUrl = c.L2RPCUrl + //if c.Testnet { + //wasmdUrl = c.RPCUrl + //} + if c.RPCUrl != "" { + wasmdUrl = c.RPCUrl } baseTOML := fmt.Sprintf(`[[Cosmos]] Enabled = true diff --git a/integration-tests/common/log.go b/integration-tests/common/log.go new file mode 100644 index 00000000..4cdbe4ae --- /dev/null +++ b/integration-tests/common/log.go @@ -0,0 +1,24 @@ +package common + +import ( + "os" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + + envConf "github.com/smartcontractkit/chainlink-env/config" +) + +// GetTestLogger TODO: This is a duplicate of the same function in chainlink-testing-framework. We should replace this with a call to the ctf version when chainlink-starknet is updated to use the latest ctf version. +// GetTestLogger instantiates a logger that takes into account the test context and the log level +func GetTestLogger(t *testing.T) zerolog.Logger { + lvlStr := os.Getenv(envConf.EnvVarLogLevel) + if lvlStr == "" { + lvlStr = "info" + } + lvl, err := zerolog.ParseLevel(lvlStr) + require.NoError(t, err, "error parsing log level") + l := zerolog.New(zerolog.NewTestWriter(t)).Output(zerolog.ConsoleWriter{Out: os.Stderr}).Level(lvl).With().Timestamp().Logger() + return l +} diff --git a/integration-tests/common/ocr2_config.go b/integration-tests/common/ocr2_config.go new file mode 100644 index 00000000..fce0ddc3 --- /dev/null +++ b/integration-tests/common/ocr2_config.go @@ -0,0 +1,105 @@ +package common + +import ( + "math/big" +) + +// OCR2Config Default config for OCR2 for starknet +// TODO: validate for cosmos +type OCR2Config struct { + F int `json:"f"` + Signers []string `json:"signers"` + Transmitters []string `json:"transmitters"` + OnchainConfig string `json:"onchainConfig"` + OffchainConfig *OffchainConfig `json:"offchainConfig"` + OffchainConfigVersion int `json:"offchainConfigVersion"` + Secret string `json:"secret"` +} + +type OffchainConfig struct { + DeltaProgressNanoseconds int64 `json:"deltaProgressNanoseconds"` + DeltaResendNanoseconds int64 `json:"deltaResendNanoseconds"` + DeltaRoundNanoseconds int64 `json:"deltaRoundNanoseconds"` + DeltaGraceNanoseconds int `json:"deltaGraceNanoseconds"` + DeltaStageNanoseconds int64 `json:"deltaStageNanoseconds"` + RMax int `json:"rMax"` + S []int `json:"s"` + OffchainPublicKeys []string `json:"offchainPublicKeys"` + PeerIds []string `json:"peerIds"` + ReportingPluginConfig *ReportingPluginConfig `json:"reportingPluginConfig"` + MaxDurationQueryNanoseconds int `json:"maxDurationQueryNanoseconds"` + MaxDurationObservationNanoseconds int `json:"maxDurationObservationNanoseconds"` + MaxDurationReportNanoseconds int `json:"maxDurationReportNanoseconds"` + MaxDurationShouldAcceptFinalizedReportNanoseconds int `json:"maxDurationShouldAcceptFinalizedReportNanoseconds"` + MaxDurationShouldTransmitAcceptedReportNanoseconds int `json:"maxDurationShouldTransmitAcceptedReportNanoseconds"` + ConfigPublicKeys []string `json:"configPublicKeys"` +} + +type ReportingPluginConfig struct { + AlphaReportInfinite bool `json:"alphaReportInfinite"` + AlphaReportPpb int `json:"alphaReportPpb"` + AlphaAcceptInfinite bool `json:"alphaAcceptInfinite"` + AlphaAcceptPpb int `json:"alphaAcceptPpb"` + DeltaCNanoseconds int `json:"deltaCNanoseconds"` +} + +var TestOCR2Config = OCR2Config{ + F: 1, + // Signers: onChainKeys, // user defined + // Transmitters: txKeys, // user defined + OnchainConfig: "", + OffchainConfig: &OffchainConfig{ + DeltaProgressNanoseconds: 8000000000, + DeltaResendNanoseconds: 30000000000, + DeltaRoundNanoseconds: 3000000000, + DeltaGraceNanoseconds: 1000000000, + DeltaStageNanoseconds: 20000000000, + RMax: 5, + S: []int{1, 2}, + // OffchainPublicKeys: offChainKeys, // user defined + // PeerIds: peerIds, // user defined + ReportingPluginConfig: &ReportingPluginConfig{ + AlphaReportInfinite: false, + AlphaReportPpb: 0, + AlphaAcceptInfinite: false, + AlphaAcceptPpb: 0, + DeltaCNanoseconds: 1000000000, + }, + MaxDurationQueryNanoseconds: 2000000000, + MaxDurationObservationNanoseconds: 1000000000, + MaxDurationReportNanoseconds: 2000000000, + MaxDurationShouldAcceptFinalizedReportNanoseconds: 2000000000, + MaxDurationShouldTransmitAcceptedReportNanoseconds: 2000000000, + // ConfigPublicKeys: cfgKeys, // user defined + }, + OffchainConfigVersion: 2, + Secret: "awe accuse polygon tonic depart acuity onyx inform bound gilbert expire", +} + +var TestOnKeys = []string{ + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603730", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603731", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603732", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603733", +} + +var TestTxKeys = []string{ + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603734", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603735", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603736", + "0x04cc1bfa99e282e434aef2815ca17337a923cd2c61cf0c7de5b326d7a8603737", +} + +var TestOffKeys = []string{ + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852090", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852091", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852092", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852093", +} + +var TestCfgKeys = []string{ + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852094", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852095", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852096", + "af400004fa5d02cd5170b5261032e71f2847ead36159cf8dee68affc3c852097", +} diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go deleted file mode 100644 index d24fa644..00000000 --- a/integration-tests/common/test_common.go +++ /dev/null @@ -1,95 +0,0 @@ -package common - -import ( - "testing" - - "github.com/stretchr/testify/require" - - ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" - "github.com/smartcontractkit/chainlink/integration-tests/client" - - "github.com/smartcontractkit/chainlink-cosmos/ops/gauntlet" -) - -type Test struct { - // Devnet *devnet.CosmosDevnetClient - Cc *ChainlinkClient - // Cosmos *cosmos.Client //comes from relayer - // OCR2Client *ocr2.Client - Cg *gauntlet.CosmosGauntlet - mockServer *ctfClient.MockserverClient - L1RPCUrl string - Common *Common - LinkTokenAddr string - OCRAddr string - AccessControllerAddr string - ProxyAddr string - ObservationSource string - JuelsPerFeeCoinSource string - T *testing.T -} - -type ChainlinkClient struct { - NKeys []client.NodeKeysBundle - ChainlinkNodes []*client.Chainlink - bTypeAttr *client.BridgeTypeAttributes - 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 - } - // from starknet, may be useful later - // testState.SetupClients() - // if testState.Common.Testnet { - // testState.Common.Env.URLs[testState.Common.ServiceKeyL2][1] = testState.Common.L2RPCUrl - // } - // 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") - // testState.Cosmos, err = starknet.NewClient(testState.Common.ChainId, testState.Common.L2RPCUrl, 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 { - // err = os.Setenv("PRIVATE_KEY", testState.GetDefaultPrivateKey()) - // require.NoError(testState.T, err, "Setting private key should not fail") - // err = os.Setenv("ACCOUNT", testState.GetDefaultWalletAddress()) - // require.NoError(testState.T, err, "Setting account address should not fail") - // testState.Devnet.AutoDumpState() // Auto dumping devnet state to avoid losing contracts on crash - // } -} - -// DeployEnv Deploys the environment -func (testState *Test) DeployEnv() { - err := testState.Common.Env.Run() - require.NoError(testState.T, err) - if testState.Common.Env.WillUseRemoteRunner() { - return // short circuit here if using a remote runner - } - testState.mockServer, err = ctfClient.ConnectMockServer(testState.Common.Env) - require.NoError(testState.T, err, "Creating mockserver clients shouldn't fail") -} - -func (testState *Test) GetDefaultObservationSource() string { - return ` - val [type = "bridge" name="bridge-mockserver"] - parse [type="jsonparse" path="data,result"] - val -> parse - ` -} - -func (testState *Test) GetDefaultJuelsPerFeeCoinSource() string { - return `""" - sum [type="sum" values=<[451000]> ] - sum - """ - ` -} diff --git a/integration-tests/gauntlet/gauntlet.go b/integration-tests/gauntlet/gauntlet.go new file mode 100644 index 00000000..3113972a --- /dev/null +++ b/integration-tests/gauntlet/gauntlet.go @@ -0,0 +1,234 @@ +package gauntlet + +import ( + "encoding/json" + "errors" + "fmt" + "os" + + "github.com/smartcontractkit/chainlink-testing-framework/gauntlet" +) + +var ( + cg *CosmosGauntlet +) + +type CosmosGauntlet struct { + dir string + G *gauntlet.Gauntlet + gr *GauntletResponse + options *gauntlet.ExecCommandOptions +} + +// GauntletResponse Default response output for cosmos gauntlet commands +type GauntletResponse struct { + Responses []struct { + Tx struct { + Hash string `json:"hash"` + Address string `json:"address"` + Status string `json:"status"` + CodeId int `json:"codeId"` + + Tx struct { + Address string `json:"address"` + Code string `json:"code"` + Result []string `json:"result"` + TransactionHash string `json:"transaction_hash"` + } `json:"tx"` + } `json:"tx"` + Contract string `json:"contract"` + } `json:"responses"` +} + +// NewCosmosGauntlet Creates a default gauntlet config +func NewCosmosGauntlet(workingDir string) (*CosmosGauntlet, error) { + g, err := gauntlet.NewGauntlet() + g.SetWorkingDir(workingDir) + if err != nil { + return nil, err + } + cg = &CosmosGauntlet{ + dir: workingDir, + G: g, + gr: &GauntletResponse{}, + options: &gauntlet.ExecCommandOptions{ + ErrHandling: []string{}, + CheckErrorsInRead: true, + }, + } + return cg, nil +} + +// FetchGauntletJsonOutput Parse gauntlet json response that is generated after yarn gauntlet command execution +func (cg *CosmosGauntlet) FetchGauntletJsonOutput() (*GauntletResponse, error) { + var payload = &GauntletResponse{} + gauntletOutput, err := os.ReadFile(cg.dir + "report.json") + if err != nil { + return payload, err + } + err = json.Unmarshal(gauntletOutput, &payload) + if err != nil { + return payload, err + } + return payload, nil +} + +// SetupNetwork Sets up a new network and sets the NODE_URL for Devnet / Cosmos RPC +func (cg *CosmosGauntlet) SetupNetwork(addr string) error { + cg.G.AddNetworkConfigVar("NODE_URL", addr) + err := cg.G.WriteNetworkConfigMap(cg.dir + "packages-ts/gauntlet-cosmos-contracts/networks/") + if err != nil { + return err + } + + return nil +} + +func (cg *CosmosGauntlet) InstallDependencies() error { + cg.G.Command = "yarn" + _, err := cg.G.ExecCommand([]string{"install"}, *cg.options) + if err != nil { + return err + } + cg.G.Command = "gauntlet" + return nil +} + +func (cg *CosmosGauntlet) UploadContract(name string) (int, error) { + _, err := cg.G.ExecCommand([]string{"upload", name}, *cg.options) + if err != nil { + return 0, err + } + cg.gr, err = cg.FetchGauntletJsonOutput() + if err != nil { + return 0, err + } + return cg.gr.Responses[0].Tx.CodeId, nil +} + +//func (cg *CosmosGauntlet) DeployAccountContract(salt int64, pubKey string) (string, error) { +//_, err := cg.G.ExecCommand([]string{"account:deploy", fmt.Sprintf("--salt=%d", salt), fmt.Sprintf("--publicKey=%s", pubKey)}, *cg.options) +//if err != nil { +//return "", err +//} +//cg.gr, err = cg.FetchGauntletJsonOutput() +//if err != nil { +//return "", err +//} +//return cg.gr.Responses[0].Contract, nil +//} + +func (cg *CosmosGauntlet) DeployLinkTokenContract() (string, error) { + _, err := cg.G.ExecCommand([]string{"token:deploy"}, *cg.options) + if err != nil { + return "", err + } + cg.gr, err = cg.FetchGauntletJsonOutput() + if err != nil { + return "", err + } + return cg.gr.Responses[0].Contract, nil +} + +func (cg *CosmosGauntlet) MintLinkToken(token, to, amount string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"ERC20:mint", fmt.Sprintf("--account=%s", to), fmt.Sprintf("--amount=%s", amount), token}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} + +func (cg *CosmosGauntlet) TransferToken(token, to, amount string) (string, error) { + _, err := cg.G.ExecCommand([]string{"ERC20:transfer", fmt.Sprintf("--recipient=%s", to), fmt.Sprintf("--amount=%s", amount), token}, *cg.options) + if err != nil { + return "", err + } + cg.gr, err = cg.FetchGauntletJsonOutput() + if err != nil { + return "", err + } + return cg.gr.Responses[0].Contract, nil +} + +func (cg *CosmosGauntlet) DeployOCR2ControllerContract(minSubmissionValue int64, maxSubmissionValue int64, decimals int, name string, linkTokenAddress string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"ocr2:deploy", fmt.Sprintf("--minSubmissionValue=%d", minSubmissionValue), fmt.Sprintf("--maxSubmissionValue=%d", maxSubmissionValue), fmt.Sprintf("--decimals=%d", decimals), fmt.Sprintf("--name=%s", name), fmt.Sprintf("--link=%s", linkTokenAddress)}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} + +func (cg *CosmosGauntlet) DeployAccessControllerContract() (string, error) { + return "", errors.New("TODO") + // _, err := cg.G.ExecCommand([]string{"access_controller:deploy"}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil +} + +func (cg *CosmosGauntlet) DeployOCR2ProxyContract(aggregator string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"proxy:deploy", fmt.Sprintf("--address=%s", aggregator)}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} + +func (cg *CosmosGauntlet) SetOCRBilling(observationPaymentGjuels int64, transmissionPaymentGjuels int64, ocrAddress string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"ocr2:set_billing", fmt.Sprintf("--observationPaymentGjuels=%d", observationPaymentGjuels), fmt.Sprintf("--transmissionPaymentGjuels=%d", transmissionPaymentGjuels), ocrAddress}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} + +func (cg *CosmosGauntlet) SetConfigDetails(cfg string, ocrAddress string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"ocr2:set_config", "--input=" + cfg, ocrAddress}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} + +func (cg *CosmosGauntlet) AddOCR2Access(aggregator, address string) (string, error) { + // _, err := cg.G.ExecCommand([]string{"ocr2:add_access", fmt.Sprintf("--address=%s", address), aggregator}, *cg.options) + // if err != nil { + // return "", err + // } + // cg.gr, err = cg.FetchGauntletJsonOutput() + // if err != nil { + // return "", err + // } + // return cg.gr.Responses[0].Contract, nil + return "", errors.New("TODO") +} diff --git a/integration-tests/common/gauntlet_common.go b/integration-tests/gauntlet/utils.go similarity index 71% rename from integration-tests/common/gauntlet_common.go rename to integration-tests/gauntlet/utils.go index 0505e2d0..dad2fffd 100644 --- a/integration-tests/common/gauntlet_common.go +++ b/integration-tests/gauntlet/utils.go @@ -1,12 +1,10 @@ -package common +package gauntlet import ( "encoding/json" - "errors" - "fmt" "os" - "github.com/smartcontractkit/chainlink-starknet/integration-tests/utils" + "github.com/smartcontractkit/chainlink-cosmos/integration-tests/utils" ) var ( @@ -15,34 +13,35 @@ var ( ) func (testState *Test) fundNodes() ([]string, error) { - l := utils.GetTestLogger(testState.T) - var nAccounts []string - var err error - for _, key := range testState.GetNodeKeys() { - if key.TXKey.Data.Attributes.StarkKey == "" { - return nil, errors.New("stark key can't be empty") - } - nAccount, err = testState.Cg.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) - if err != nil { - return nil, err - } - nAccounts = append(nAccounts, nAccount) - } - - if err != nil { - return nil, err - } - - 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.Cg.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node - if err != nil { - return nil, err - } - } - - return nAccounts, nil + //l := utils.GetTestLogger(testState.T) + //return []string{testState.Common.Account}, nil + //var nAccounts []string + //var err error + //for _, key := range testState.GetNodeKeys() { + //if key.TXKey.Data.Attributes.StarkKey == "" { + //return nil, errors.New("stark key can't be empty") + //} + //nAccount, err = testState.Cg.DeployAccountContract(100, key.TXKey.Data.Attributes.StarkKey) + //if err != nil { + //return nil, err + //} + //nAccounts = append(nAccounts, nAccount) + //} + + //if err != nil { + //return nil, err + //} + + //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.Cg.TransferToken(ethAddressGoerli, key, "100000000000000000") // Transferring 1 ETH to each node + //if err != nil { + //return nil, err + //} + //} + + //return nAccounts, nil } func (testState *Test) deployLinkToken() error { diff --git a/integration-tests/ocr2_test.go b/integration-tests/ocr2_test.go new file mode 100644 index 00000000..5281acdc --- /dev/null +++ b/integration-tests/ocr2_test.go @@ -0,0 +1,104 @@ +package ocr2_test + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/smartcontractkit/chainlink-cosmos/integration-tests/common" + "github.com/smartcontractkit/chainlink-cosmos/integration-tests/gauntlet" + "github.com/smartcontractkit/chainlink-cosmos/ops/utils" + "github.com/smartcontractkit/chainlink/integration-tests/actions" + + "github.com/stretchr/testify/require" + "go.uber.org/zap/zapcore" +) + +func TestOCRBasic(t *testing.T) { + logger := common.GetTestLogger(t) + commonConfig := common.NewCommon() + + gauntletWorkingDir := fmt.Sprintf("%s/", utils.ProjectRoot) + logger.Info().Str("working dir", gauntletWorkingDir).Msg("Initializing gauntlet") + + cg, err := gauntlet.NewCosmosGauntlet(gauntletWorkingDir) + require.NoError(t, err, "Could not create cosmos gauntlet") + + err = cg.InstallDependencies() + require.NoError(t, err, "Failed to install gauntlet dependencies") + + err = cg.SetupNetwork(commonConfig.RPCUrl) + require.NoError(t, err, "Setting up gauntlet network should not fail") + + // TODO: uncomment once we are ready to test rounds + // commonConfig.SetDefaultEnvironment(t) + + // TODO: fund nodes if necessary + + // store the cw20_base contract so we have the token contract, and then deploy the LINK token. + _, err = cg.UploadContract("cw20_base") + require.NoError(t, err, "Could not upload cw20_base contract") + + linkTokenAddress, err := cg.DeployLinkTokenContract() + require.NoError(t, err, "Could not deploy link token contract") + logger.Info().Str("address", linkTokenAddress).Msg("Deployed LINK token") + os.Setenv("LINK", linkTokenAddress) + + accessControllerAddress, err := cg.DeployAccessControllerContract() + require.NoError(t, err, "Could not deploy access controller") + logger.Info().Str("address", accessControllerAddress).Msg("Deployed access controller") + os.Setenv("BILLING_ACCESS_CONTROLLER", accessControllerAddress) + + minSubmissionValue := int64(0) + maxSubmissionValue := int64(100000000000) + decimals := 9 + name := "auto" + ocrAddress, err := cg.DeployOCR2ControllerContract(minSubmissionValue, maxSubmissionValue, decimals, name, linkTokenAddress) + require.NoError(t, err, "Could not deploy OCR2 controller contract") + logger.Info().Str("address", ocrAddress).Msg("Deployed OCR2 Controller contract") + + ocrProxyAddress, err := cg.DeployOCR2ProxyContract(ocrAddress) + require.NoError(t, err, "Could not deploy OCR2 proxy contract") + logger.Info().Str("address", ocrProxyAddress).Msg("Deployed OCR2 proxy contract") + + _, err = cg.AddOCR2Access(ocrAddress, ocrProxyAddress) + require.NoError(t, err, "Could not add OCR2 access") + logger.Info().Msg("Added OCR2 access") + + _, err = cg.MintLinkToken(linkTokenAddress, ocrAddress, "100000000000000000000") + require.NoError(t, err, "Could not mint LINK token") + + observationPaymentGjuels := int64(1) + transmissionPaymentGjuels := int64(1) + _, err = cg.SetOCRBilling(observationPaymentGjuels, transmissionPaymentGjuels, ocrAddress) + require.NoError(t, err, "Could not set OCR billing") + + chainlinkClient, err := common.NewChainlinkClient(commonConfig.Env, commonConfig.ChainName, commonConfig.ChainId, commonConfig.RPCUrl) + require.NoError(t, err, "Could not create chainlink client") + + cfg, err := chainlinkClient.LoadOCR2Config([]string{commonConfig.Account}) + require.NoError(t, err, "Could not load OCR2 config") + + var parsedConfig []byte + parsedConfig, err = json.Marshal(cfg) + require.NoError(t, err, "Could not parse JSON config") + + _, err = cg.SetConfigDetails(string(parsedConfig), ocrAddress) + require.NoError(t, err, "Could not set config details") + + //if !testState.Common.Testnet { + //testState.Devnet.AutoLoadState(testState.OCR2Client, testState.OCRAddr) + //} + //mockServerVal = 900000000 + //testState.SetUpNodes(mockServerVal) + + //err = testState.ValidateRounds(10, false) + //require.NoError(t, err, "Validating round should not fail") + + t.Cleanup(func() { + err = actions.TeardownSuite(t, commonConfig.Env, "./", nil, nil, zapcore.DPanicLevel, nil) + // err = actions.TeardownSuite(t, testState.Common.Env, utils.ProjectRoot, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel) + require.NoError(t, err, "Error tearing down environment") + }) +} diff --git a/integration-tests/smoke/ocr2_test.go b/integration-tests/smoke/ocr2_test.go deleted file mode 100644 index 7262aecf..00000000 --- a/integration-tests/smoke/ocr2_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package smoke_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - "go.uber.org/zap/zapcore" - - "github.com/smartcontractkit/chainlink-cosmos/integration-tests/common" - "github.com/smartcontractkit/chainlink-cosmos/ops/gauntlet" - "github.com/smartcontractkit/chainlink-cosmos/ops/utils" - "github.com/smartcontractkit/chainlink/integration-tests/actions" -) - -var ( - err error - testState *common.Test - decimals = 9 - mockServerVal = 900000000 -) - -func TestOCRBasic(t *testing.T) { - testState = &common.Test{ - T: t, - } - testState.Common = common.New() - testState.Common.Default(t) - // Setting this to the root of the repo for cmd exec func for Gauntlet - testState.Cg, err = gauntlet.NewCosmosGauntlet(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.Cg.SetupNetwork(testState.Common.RPCUrl) - 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(mockServerVal) - - err = testState.ValidateRounds(10, false) - require.NoError(t, err, "Validating round should not fail") - - t.Cleanup(func() { - err = actions.TeardownSuite(testState.T, testState.Common.Env, "./", nil, nil, zapcore.DPanicLevel, nil) - // err = actions.TeardownSuite(t, testState.Common.Env, utils.ProjectRoot, testState.Cc.ChainlinkNodes, nil, zapcore.ErrorLevel) - require.NoError(t, err, "Error tearing down environment") - }) -} diff --git a/ops/gauntlet/gauntlet_cosmos.go b/ops/gauntlet/gauntlet_cosmos.go deleted file mode 100644 index 3a520025..00000000 --- a/ops/gauntlet/gauntlet_cosmos.go +++ /dev/null @@ -1,225 +0,0 @@ -package gauntlet - -import ( - "encoding/json" - "os" - - "github.com/smartcontractkit/chainlink-testing-framework/gauntlet" -) - -var ( - cg *CosmosGauntlet -) - -type CosmosGauntlet struct { - dir string - G *gauntlet.Gauntlet - gr *GauntletResponse - options *gauntlet.ExecCommandOptions -} - -// GauntletResponse Default response output for cosmos gauntlet commands -type GauntletResponse struct { - Responses []struct { - Tx struct { - Hash string `json:"hash"` - Address string `json:"address"` - Status string `json:"status"` - CodeId int `json:"codeId"` - - Tx struct { - Address string `json:"address"` - Code string `json:"code"` - Result []string `json:"result"` - TransactionHash string `json:"transaction_hash"` - } `json:"tx"` - } `json:"tx"` - Contract string `json:"contract"` - } `json:"responses"` -} - -// NewCosmosGauntlet Creates a default gauntlet config -func NewCosmosGauntlet(workingDir string) (*CosmosGauntlet, error) { - g, err := gauntlet.NewGauntlet() - g.SetWorkingDir(workingDir) - if err != nil { - return nil, err - } - cg = &CosmosGauntlet{ - dir: workingDir, - G: g, - gr: &GauntletResponse{}, - options: &gauntlet.ExecCommandOptions{ - ErrHandling: []string{}, - CheckErrorsInRead: true, - }, - } - return cg, nil -} - -// FetchGauntletJsonOutput Parse gauntlet json response that is generated after yarn gauntlet command execution -func (cg *CosmosGauntlet) FetchGauntletJsonOutput() (*GauntletResponse, error) { - var payload = &GauntletResponse{} - gauntletOutput, err := os.ReadFile(cg.dir + "report.json") - if err != nil { - return payload, err - } - err = json.Unmarshal(gauntletOutput, &payload) - if err != nil { - return payload, err - } - return payload, nil -} - -// SetupNetwork Sets up a new network and sets the NODE_URL for Devnet / Cosmos RPC -func (sg *CosmosGauntlet) SetupNetwork(addr string) error { - sg.G.AddNetworkConfigVar("NODE_URL", addr) - err := sg.G.WriteNetworkConfigMap(sg.dir + "packages-ts/gauntlet-cosmos-contracts/networks/") - if err != nil { - return err - } - - return nil -} - -func (sg *CosmosGauntlet) InstallDependencies() error { - sg.G.Command = "yarn" - _, err := sg.G.ExecCommand([]string{"install"}, *sg.options) - if err != nil { - return err - } - sg.G.Command = "gauntlet" - return nil -} - -func (sg *CosmosGauntlet) UploadContract(name string) (int, error) { - _, err := sg.G.ExecCommand([]string{"upload", name}, *sg.options) - if err != nil { - return 0, err - } - sg.gr, err = sg.FetchGauntletJsonOutput() - if err != nil { - return 0, err - } - return sg.gr.Responses[0].Tx.CodeId, nil -} - -// func (sg *CosmosGauntlet) DeployAccountContract(salt int64, pubKey string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"account:deploy", fmt.Sprintf("--salt=%d", salt), fmt.Sprintf("--publicKey=%s", pubKey)}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -func (sg *CosmosGauntlet) DeployLinkTokenContract() (string, error) { - _, err := sg.G.ExecCommand([]string{"token:deploy"}, *sg.options) - if err != nil { - return "", err - } - sg.gr, err = sg.FetchGauntletJsonOutput() - if err != nil { - return "", err - } - return sg.gr.Responses[0].Contract, nil -} - -// func (sg *CosmosGauntlet) MintLinkToken(token, to, amount string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"ERC20:mint", fmt.Sprintf("--account=%s", to), fmt.Sprintf("--amount=%s", amount), token}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -func (sg *CosmosGauntlet) TransferToken(token, to, amount string) (string, error) { - _, err := sg.G.ExecCommand([]string{"ERC20:transfer", fmt.Sprintf("--recipient=%s", to), fmt.Sprintf("--amount=%s", amount), token}, *sg.options) - if err != nil { - return "", err - } - sg.gr, err = sg.FetchGauntletJsonOutput() - if err != nil { - return "", err - } - return sg.gr.Responses[0].Contract, nil -} - -// func (sg *CosmosGauntlet) DeployOCR2ControllerContract(minSubmissionValue int64, maxSubmissionValue int64, decimals int, name string, linkTokenAddress string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"ocr2:deploy", fmt.Sprintf("--minSubmissionValue=%d", minSubmissionValue), fmt.Sprintf("--maxSubmissionValue=%d", maxSubmissionValue), fmt.Sprintf("--decimals=%d", decimals), fmt.Sprintf("--name=%s", name), fmt.Sprintf("--link=%s", linkTokenAddress)}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -// func (sg *CosmosGauntlet) DeployAccessControllerContract() (string, error) { -// _, err := sg.G.ExecCommand([]string{"access_controller:deploy"}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -// func (sg *CosmosGauntlet) DeployOCR2ProxyContract(aggregator string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"proxy:deploy", fmt.Sprintf("--address=%s", aggregator)}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -// func (sg *CosmosGauntlet) SetOCRBilling(observationPaymentGjuels int64, transmissionPaymentGjuels int64, ocrAddress string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"ocr2:set_billing", fmt.Sprintf("--observationPaymentGjuels=%d", observationPaymentGjuels), fmt.Sprintf("--transmissionPaymentGjuels=%d", transmissionPaymentGjuels), ocrAddress}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -// func (sg *CosmosGauntlet) SetConfigDetails(cfg string, ocrAddress string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"ocr2:set_config", "--input=" + cfg, ocrAddress}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// } - -// func (sg *CosmosGauntlet) AddAccess(aggregator, address string) (string, error) { -// _, err := sg.G.ExecCommand([]string{"ocr2:add_access", fmt.Sprintf("--address=%s", address), aggregator}, *sg.options) -// if err != nil { -// return "", err -// } -// sg.gr, err = sg.FetchGauntletJsonOutput() -// if err != nil { -// return "", err -// } -// return sg.gr.Responses[0].Contract, nil -// }