From 80550df19cc94982d6d88ed30c79e0688443efd2 Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:49:57 -0700 Subject: [PATCH] Refactor: Cleanup logging, add more tx caching --- core/scripts/go.mod | 1 + core/scripts/go.sum | 2 + .../keystone/src/01_provision_keystone.go | 60 +++++++++- .../keystone/src/02_fund_transmitters.go | 18 ++- .../src/02_provision_capabilities_registry.go | 1 + .../scripts/keystone/src/02_provision_crib.go | 2 +- .../src/02_provision_forwarder_contract.go | 5 +- .../src/02_provision_ocr3_capability.go | 12 +- ...02_provision_streams_trigger_capability.go | 106 +++++++++++++----- .../src/88_capabilities_registry_helpers.go | 5 + .../keystone/src/88_contracts_helpers.go | 84 ++++++++++---- .../keystone/src/88_jobspecs_helpers.go | 3 +- core/scripts/keystone/src/88_ocr_helpers.go | 37 +++++- core/scripts/keystone/src/99_app.go | 101 ++++++++++------- core/scripts/keystone/src/99_crib_client.go | 13 --- 15 files changed, 326 insertions(+), 124 deletions(-) diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 60ffe8473b2..3463e4125aa 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -297,6 +297,7 @@ require ( github.com/smartcontractkit/chainlink-protos/orchestrator v0.3.0 // indirect github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4 // indirect github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 // indirect + github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.12 github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20241009055228-33d0c0bf38de // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20241009055228-33d0c0bf38de // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index 9b0336635bb..904df770f88 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1108,6 +1108,8 @@ github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c github.com/smartcontractkit/chainlink-solana v1.1.1-0.20241024132041-a3eb2e31b4c4/go.mod h1:iZugccCLpPWtcGiR/8gurre2j3RtyKnqd1FcVR0NzQw= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8 h1:B4DFdk6MGcQnoCjjMBCx7Z+GWQpxRWJ4O8W/dVJyWGA= github.com/smartcontractkit/chainlink-starknet/relayer v0.1.1-0.20241017135645-176a23722fd8/go.mod h1:WkBqgBo+g34Gm5vWkDDl8Fh3Mzd7bF5hXp7rryg0t5o= +github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.12 h1:B3+KM0cNxEtynbKnvixHnW5oUCq1Dv3b0gTKYRt3dOs= +github.com/smartcontractkit/chainlink-testing-framework/lib v1.50.12/go.mod h1:Mi8q2e6gSnBhVdYehNesfh3aJMibGpS/nzgfN1yHoFY= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12ijqMM9tvYVEm+nR826WsrNi6zCKpwBhuApq127wHs= github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20241007185508-adbe57025f12 h1:NzZGjaqez21I3DU7objl3xExTH4fxYvzTqar8DC6360= diff --git a/core/scripts/keystone/src/01_provision_keystone.go b/core/scripts/keystone/src/01_provision_keystone.go index d86b833a9e5..9faa950744b 100644 --- a/core/scripts/keystone/src/01_provision_keystone.go +++ b/core/scripts/keystone/src/01_provision_keystone.go @@ -50,7 +50,13 @@ func (g *provisionKeystone) Run(args []string) { } if *preprovison { - fmt.Printf("Preprovisioning crib with %d nodes per set\n", *nodeSetSize) + fmt.Println() + fmt.Println() + fmt.Println("========================") + fmt.Println("Writing Preprovisioning Config") + fmt.Println("========================") + fmt.Println() + fmt.Println() writePreprovisionConfig(*nodeSetSize, filepath.Join(*artefactsDir, *preprovisionConfigName)) return } @@ -82,16 +88,16 @@ func (g *provisionKeystone) Run(args []string) { os.Setenv("INSECURE_SKIP_VERIFY", "true") env := helpers.SetupEnv(false) - provisionStreamsDON( env, nodeSets.StreamsTrigger, *chainID, *p2pPort, *ocrConfigFile, + *artefactsDir, ) - reg := provisionCapabillitiesRegistry( + reg := provisionCapabilitiesRegistry( env, nodeSets, *chainID, @@ -108,29 +114,68 @@ func (g *provisionKeystone) Run(args []string) { reg, ) + fmt.Println() + fmt.Println() + fmt.Println("========================") + fmt.Println("Writing Postprovision Config") + fmt.Println("========================") + fmt.Println() + fmt.Println() + writePostProvisionConfig( nodeSets, *chainID, *capabilitiesP2PPort, - onchainMeta.ForwarderContract.Address().Hex(), + onchainMeta.Forwarder.Address().Hex(), onchainMeta.CapabilitiesRegistry.Address().Hex(), filepath.Join(*artefactsDir, *postprovisionConfigName), ) } +func provisionCapabilitiesRegistry( + env helpers.Environment, + nodeSets NodeSets, + chainID int64, + artefactsDir string, +) kcr.CapabilitiesRegistryInterface { + fmt.Println() + fmt.Println() + fmt.Println("========================") + fmt.Println("Provisioning Capabilities Registry DON") + fmt.Println("========================") + fmt.Println() + fmt.Println() + reg := provisionCapabillitiesRegistry( + env, + nodeSets, + chainID, + artefactsDir, + ) + return reg +} + func provisionStreamsDON( env helpers.Environment, nodeSet NodeSet, chainID int64, p2pPort int64, ocrConfigFilePath string, + artefactsDir string, ) { + fmt.Println() + fmt.Println() + fmt.Println("========================") + fmt.Println("Provisioning streams DON") + fmt.Println("========================") + fmt.Println() + fmt.Println() setupStreamsTrigger( env, nodeSet, chainID, p2pPort, ocrConfigFilePath, + artefactsDir, ) } @@ -143,6 +188,13 @@ func provisionWorkflowDON( artefactsDir string, reg kcr.CapabilitiesRegistryInterface, ) (onchainMeta *onchainMeta) { + fmt.Println() + fmt.Println() + fmt.Println("========================") + fmt.Println("Provisioning workflow DON") + fmt.Println("========================") + fmt.Println() + fmt.Println() deployForwarder(env, artefactsDir) onchainMeta, _ = provisionOCR3( diff --git a/core/scripts/keystone/src/02_fund_transmitters.go b/core/scripts/keystone/src/02_fund_transmitters.go index dcbf91d6aca..0a8403c8b3c 100644 --- a/core/scripts/keystone/src/02_fund_transmitters.go +++ b/core/scripts/keystone/src/02_fund_transmitters.go @@ -1,14 +1,16 @@ package src import ( + "context" "flag" "fmt" "math/big" "os" - "context" - helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/ethereum/go-ethereum/common" + + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/conversions" + helpers "github.com/smartcontractkit/chainlink/core/scripts/common" ) type fundTransmitters struct{} @@ -53,7 +55,8 @@ func (g *fundTransmitters) Run(args []string) { func distributeFunds(nodeSet NodeSet, env helpers.Environment) { fmt.Println("Funding transmitters...") transmittersStr := []string{} - minThreshold := big.NewInt(50000000000000000) // 0.05 ETH + fundingAmount := big.NewInt(500000000000000000) // 0.5 ETH + minThreshold := big.NewInt(50000000000000000) // 0.05 ETH for _, n := range nodeSet.NodeKeys { balance, err := getBalance(n.EthAddress, env) @@ -62,12 +65,19 @@ func distributeFunds(nodeSet NodeSet, env helpers.Environment) { continue } if balance.Cmp(minThreshold) < 0 { + fmt.Printf( + "Transmitter %s has insufficient funds, funding with %s ETH. Current balance: %s, threshold: %s\n", + n.EthAddress, + conversions.WeiToEther(fundingAmount).String(), + conversions.WeiToEther(balance).String(), + conversions.WeiToEther(minThreshold).String(), + ) transmittersStr = append(transmittersStr, n.EthAddress) } } if len(transmittersStr) > 0 { - helpers.FundNodes(env, transmittersStr, minThreshold) + helpers.FundNodes(env, transmittersStr, fundingAmount) } else { fmt.Println("All transmitters have sufficient funds.") } diff --git a/core/scripts/keystone/src/02_provision_capabilities_registry.go b/core/scripts/keystone/src/02_provision_capabilities_registry.go index 14bce1c391e..74f0f7ac4f2 100644 --- a/core/scripts/keystone/src/02_provision_capabilities_registry.go +++ b/core/scripts/keystone/src/02_provision_capabilities_registry.go @@ -63,6 +63,7 @@ func (c *provisionCR) Run(args []string) { } func provisionCapabillitiesRegistry(env helpers.Environment, nodeSets NodeSets, chainID int64, artefactsDir string) kcr.CapabilitiesRegistryInterface { + fmt.Printf("Provisioning capabilities registry on chain %d\n", chainID) ctx := context.Background() reg := deployCR(ctx, artefactsDir, env) crProvisioner := NewCapabilityRegistryProvisioner(reg, env) diff --git a/core/scripts/keystone/src/02_provision_crib.go b/core/scripts/keystone/src/02_provision_crib.go index 20ab0c648b8..e98bfa6e6c8 100644 --- a/core/scripts/keystone/src/02_provision_crib.go +++ b/core/scripts/keystone/src/02_provision_crib.go @@ -201,7 +201,7 @@ func (g *postprovisionCrib) Run(args []string) { nodeSets, *chainID, *capabilitiesP2PPort, - contracts.ForwarderContract.Address().Hex(), + contracts.Forwarder.Address().Hex(), contracts.CapabilitiesRegistry.Address().Hex(), *outputPath, ) diff --git a/core/scripts/keystone/src/02_provision_forwarder_contract.go b/core/scripts/keystone/src/02_provision_forwarder_contract.go index 711a90b3bc1..054286a6fd9 100644 --- a/core/scripts/keystone/src/02_provision_forwarder_contract.go +++ b/core/scripts/keystone/src/02_provision_forwarder_contract.go @@ -52,13 +52,14 @@ func deployForwarder( artefacts string, ) { o := LoadOnchainMeta(artefacts, env) - if o.ForwarderContract != nil { + if o.Forwarder != nil { fmt.Println("Forwarder contract already deployed, skipping") + return } fmt.Println("Deploying forwarder contract...") forwarderContract := DeployForwarder(env) - o.ForwarderContract = forwarderContract + o.Forwarder = forwarderContract WriteOnchainMeta(o, artefacts) } diff --git a/core/scripts/keystone/src/02_provision_ocr3_capability.go b/core/scripts/keystone/src/02_provision_ocr3_capability.go index 75b92a87017..0fc39c80c0c 100644 --- a/core/scripts/keystone/src/02_provision_ocr3_capability.go +++ b/core/scripts/keystone/src/02_provision_ocr3_capability.go @@ -105,17 +105,17 @@ func deployOCR3Contract( env.ChainID, ) - if o.OCRContract != nil { + if o.OCR3 != nil { // types.ConfigDigestPrefixKeystoneOCR3Capability fmt.Println("OCR3 Contract already deployed, checking config...") - latestConfigDigestBytes, err := o.OCRContract.LatestConfigDetails(nil) + latestConfigDigestBytes, err := o.OCR3.LatestConfigDetails(nil) PanicErr(err) latestConfigDigest, err := types.BytesToConfigDigest(latestConfigDigestBytes.ConfigDigest[:]) cc := ocrConfToContractConfig(ocrConf, latestConfigDigestBytes.ConfigCount) digester := evm.OCR3CapabilityOffchainConfigDigester{ ChainID: uint64(env.ChainID), - ContractAddress: o.OCRContract.Address(), + ContractAddress: o.OCR3.Address(), } digest, err := digester.ConfigDigest(context.Background(), cc) PanicErr(err) @@ -134,7 +134,7 @@ func deployOCR3Contract( _, tx, ocrContract, err := ocr3_capability.DeployOCR3Capability(env.Owner, env.Ec) PanicErr(err) helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID) - o.OCRContract = ocrContract + o.OCR3 = ocrContract setOCRConfig(o, env, ocrConf, artefacts) return o, true @@ -149,7 +149,7 @@ func generateOCR3Config(nodeSet NodeSet, configFile string, chainID int64) ksdep } func setOCRConfig(o *onchainMeta, env helpers.Environment, ocrConf ksdeploy.Orc2drOracleConfig, artefacts string) { - tx, err := o.OCRContract.SetConfig(env.Owner, + tx, err := o.OCR3.SetConfig(env.Owner, ocrConf.Signers, ocrConf.Transmitters, ocrConf.F, @@ -170,7 +170,7 @@ func deployOCR3JobSpecsTo( artefactsDir string, onchainMeta *onchainMeta, ) { - ocrAddress := onchainMeta.OCRContract.Address().Hex() + ocrAddress := onchainMeta.OCR3.Address().Hex() nodeKeys := nodeSet.NodeKeys nodes := nodeSet.Nodes diff --git a/core/scripts/keystone/src/02_provision_streams_trigger_capability.go b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go index 84bc059340b..e0c37bb1359 100644 --- a/core/scripts/keystone/src/02_provision_streams_trigger_capability.go +++ b/core/scripts/keystone/src/02_provision_streams_trigger_capability.go @@ -42,6 +42,7 @@ import ( helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/bridges" "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/mercury" verifierContract "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy" @@ -67,7 +68,7 @@ func (g *provisionStreamsTrigger) Run(args []string) { ocrConfigFile := fs.String("ocrfile", "ocr_config.json", "path to OCR config file") nodeSetsPath := fs.String("nodesets", "", "Custom node sets location") nodeSetSize := fs.Int("nodesetsize", 5, "number of nodes in a nodeset") - + artefactsDir := fs.String("artefacts", defaultArtefactsDir, "Custom artefacts directory location") ethUrl := fs.String("ethurl", "", "URL of the Ethereum node") accountKey := fs.String("accountkey", "", "private key of the account to deploy from") @@ -99,6 +100,7 @@ func (g *provisionStreamsTrigger) Run(args []string) { *chainID, *p2pPort, *ocrConfigFile, + *artefactsDir, ) } @@ -144,12 +146,13 @@ func setupStreamsTrigger( chainId int64, p2pPort int64, ocrConfigFilePath string, + artefactsDir string, ) { fmt.Printf("Deploying streams trigger for chain %d\n", chainId) fmt.Printf("Using OCR config file: %s\n", ocrConfigFilePath) fmt.Printf("Deploying Mercury V0.3 contracts\n") - verifier := deployMercuryV03Contracts(env) + verifier := deployMercuryV03Contracts(env, artefactsDir) fmt.Printf("Generating Mercury OCR config\n") ocrConfig := generateMercuryOCR2Config(nodeSet.NodeKeys[1:]) // skip the bootstrap node @@ -161,25 +164,45 @@ func setupStreamsTrigger( fmt.Printf("BridgeName: %s\n", feed.bridgeName) fmt.Printf("BridgeURL: %s\n", feed.bridgeUrl) - fmt.Printf("Setting verifier config\n") - fmt.Printf("Signers: %v\n", ocrConfig.Signers) - fmt.Printf("Transmitters: %v\n", ocrConfig.Transmitters) - fmt.Printf("F: %d\n", ocrConfig.F) + latestConfigDetails, err := verifier.LatestConfigDetails(nil, feed.id) + PanicErr(err) + latestConfigDigest, err := ocrtypes.BytesToConfigDigest(latestConfigDetails.ConfigDigest[:]) + PanicErr(err) - tx, err := verifier.SetConfig( - env.Owner, + digester := mercury.NewOffchainConfigDigester( feed.id, - ocrConfig.Signers, - ocrConfig.Transmitters, - ocrConfig.F, - ocrConfig.OnchainConfig, - ocrConfig.OffchainConfigVersion, - ocrConfig.OffchainConfig, - nil, + big.NewInt(chainId), + verifier.Address(), + ocrtypes.ConfigDigestPrefixMercuryV02, + ) + configDigest, err := digester.ConfigDigest( + context.Background(), + mercuryOCRConfigToContractConfig( + ocrConfig, + latestConfigDetails.ConfigCount, + ), ) - helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID) PanicErr(err) + if configDigest.Hex() == latestConfigDigest.Hex() { + fmt.Printf("Verifier already deployed with the same config (digest: %s), skipping...\n", configDigest.Hex()) + } else { + fmt.Printf("Verifier contains a different config, updating...\nOld digest: %s\nNew digest: %s\n", latestConfigDigest.Hex(), configDigest.Hex()) + tx, err := verifier.SetConfig( + env.Owner, + feed.id, + ocrConfig.Signers, + ocrConfig.Transmitters, + ocrConfig.F, + ocrConfig.OnchainConfig, + ocrConfig.OffchainConfigVersion, + ocrConfig.OffchainConfig, + nil, + ) + helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID) + PanicErr(err) + } + fmt.Printf("Deploying OCR2 job specs for feed %s\n", feed.name) deployOCR2JobSpecsForFeed(nodeSet, verifier, feed, chainId, p2pPort) } @@ -187,29 +210,50 @@ func setupStreamsTrigger( fmt.Println("Finished deploying streams trigger") } -func deployMercuryV03Contracts(env helpers.Environment) (verifier *verifierContract.Verifier) { +func deployMercuryV03Contracts(env helpers.Environment, artefactsDir string) verifierContract.VerifierInterface { var confirmDeploy = func(tx *types.Transaction, err error) { helpers.ConfirmContractDeployed(context.Background(), env.Ec, tx, env.ChainID) PanicErr(err) } - var confirmTx = func(tx *types.Transaction, err error) { - helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID) - PanicErr(err) + o := LoadOnchainMeta(artefactsDir, env) + + if o.VerifierProxy != nil { + fmt.Printf("Verifier proxy contract already deployed at %s\n", o.VerifierProxy.Address()) + } else { + fmt.Printf("Deploying verifier proxy contract\n") + _, tx, verifierProxy, err := verifier_proxy.DeployVerifierProxy(env.Owner, env.Ec, common.Address{}) // zero address for access controller disables access control + confirmDeploy(tx, err) + o.VerifierProxy = verifierProxy + WriteOnchainMeta(o, artefactsDir) } - verifierProxyAddr, tx, verifierProxy, err := verifier_proxy.DeployVerifierProxy(env.Owner, env.Ec, common.Address{}) // zero address for access controller disables access control - confirmDeploy(tx, err) - - verifierAddress, tx, verifier, err := verifierContract.DeployVerifier(env.Owner, env.Ec, verifierProxyAddr) - confirmDeploy(tx, err) + if o.Verifier == nil { + fmt.Printf("Deploying verifier contract\n") + _, tx, verifier, err := verifierContract.DeployVerifier(env.Owner, env.Ec, o.VerifierProxy.Address()) + confirmDeploy(tx, err) + o.Verifier = verifier + WriteOnchainMeta(o, artefactsDir) + } else { + fmt.Printf("Verifier contract already deployed at %s\n", o.Verifier.Address().Hex()) + } - tx, err = verifierProxy.InitializeVerifier(env.Owner, verifierAddress) - confirmTx(tx, err) + if o.InitializedVerifierAddress != o.Verifier.Address() { + fmt.Printf("Current initialized verifier address (%s) differs from the new verifier address (%s). Initializing verifier.\n", o.InitializedVerifierAddress.Hex(), o.Verifier.Address().Hex()) + tx, err := o.VerifierProxy.InitializeVerifier(env.Owner, o.Verifier.Address()) + receipt := helpers.ConfirmTXMined(context.Background(), env.Ec, tx, env.ChainID) + PanicErr(err) + inited, err := o.VerifierProxy.ParseVerifierInitialized(*receipt.Logs[0]) + PanicErr(err) + o.InitializedVerifierAddress = inited.VerifierAddress + WriteOnchainMeta(o, artefactsDir) + } else { + fmt.Printf("Verifier %s already initialized\n", o.Verifier.Address().Hex()) + } - return + return o.Verifier } -func deployOCR2JobSpecsForFeed(nodeSet NodeSet, verifier *verifierContract.Verifier, feed feed, chainId int64, p2pPort int64) { +func deployOCR2JobSpecsForFeed(nodeSet NodeSet, verifier verifierContract.VerifierInterface, feed feed, chainId int64, p2pPort int64) { // we assign the first node as the bootstrap node for i, n := range nodeSet.NodeKeys { // parallel arrays @@ -217,7 +261,7 @@ func deployOCR2JobSpecsForFeed(nodeSet NodeSet, verifier *verifierContract.Verif jobSpecName := "" jobSpecStr := "" - createBridgeIfDoesNotExist(api, feed.bridgeName, feed.bridgeUrl) + upsertBridge(api, feed.bridgeName, feed.bridgeUrl) if i == 0 { // Prepare data for Bootstrap Job @@ -377,7 +421,7 @@ func strToBytes32(str string) [32]byte { return pkBytesFixed } -func createBridgeIfDoesNotExist(api *nodeAPI, name string, eaURL string) { +func upsertBridge(api *nodeAPI, name string, eaURL string) { u, err := url.Parse(eaURL) url := models.WebURL(*u) // Confirmations and MinimumContractPayment are not used, so we can leave them as 0 diff --git a/core/scripts/keystone/src/88_capabilities_registry_helpers.go b/core/scripts/keystone/src/88_capabilities_registry_helpers.go index 613b2a2d015..3e958f72ff9 100644 --- a/core/scripts/keystone/src/88_capabilities_registry_helpers.go +++ b/core/scripts/keystone/src/88_capabilities_registry_helpers.go @@ -99,6 +99,7 @@ func (c *CapabilityRegistryProvisioner) testCallContract(method string, args ... // AddCapabilities takes a capability set and provisions it in the registry. func (c *CapabilityRegistryProvisioner) AddCapabilities(ctx context.Context, capSet CapabilitySet) { + fmt.Printf("Adding capabilities to registry: %s\n", capSet.IDs()) tx, err := c.reg.AddCapabilities(c.env.Owner, capSet.Capabilities()) helpers.PanicErr(err) @@ -116,6 +117,7 @@ func (c *CapabilityRegistryProvisioner) AddCapabilities(ctx context.Context, cap // The ID is then used when adding nodes to the registry such that the registry knows which nodes belong to which // node operator. func (c *CapabilityRegistryProvisioner) AddNodeOperator(ctx context.Context, nop *NodeOperator) { + fmt.Printf("Adding NodeOperator to registry: %s\n", nop.Name) nop.BindToRegistry(c.reg) nops, err := c.reg.GetNodeOperators(&bind.CallOpts{}) @@ -157,6 +159,7 @@ func (c *CapabilityRegistryProvisioner) AddNodeOperator(ctx context.Context, nop // Note that in terms of the provisioning process, this is not the last step. A capability is only active once // there is a DON servicing it. This is done via `AddDON`. func (c *CapabilityRegistryProvisioner) AddNodes(ctx context.Context, nop *NodeOperator, donNames ...string) { + fmt.Printf("Adding nodes to registry for NodeOperator %s with DONs: %v\n", nop.Name, donNames) var params []kcr.CapabilitiesRegistryNodeParams for _, donName := range donNames { don, exists := nop.DONs[donName] @@ -171,6 +174,7 @@ func (c *CapabilityRegistryProvisioner) AddNodes(ctx context.Context, nop *NodeO } node.HashedCapabilityIds = capSet.HashedIDs(c.reg) node.EncryptionPublicKey = [32]byte{2: byte(i + 1)} + fmt.Printf("Adding node %s to registry with capabilities: %s\n", peer.PeerID, capSet.IDs()) params = append(params, node) } } @@ -207,6 +211,7 @@ func (c *CapabilityRegistryProvisioner) AddNodes(ctx context.Context, nop *NodeO // // Another important distinction is that DON can comprise of nodes from different node operators, but for now, we're keeping it simple and restricting it to a single node operator. We also hard code F to 1. func (c *CapabilityRegistryProvisioner) AddDON(ctx context.Context, nop *NodeOperator, donName string, isPublic bool, acceptsWorkflows bool) { + fmt.Printf("Adding DON %s to registry for NodeOperator %s with isPublic: %t and acceptsWorkflows: %t\n", donName, nop.Name, isPublic, acceptsWorkflows) don, exists := nop.DONs[donName] if !exists { log.Fatalf("DON with name %s does not exist in NodeOperator %s", donName, nop.Name) diff --git a/core/scripts/keystone/src/88_contracts_helpers.go b/core/scripts/keystone/src/88_contracts_helpers.go index 3a6a365d23d..a54a9f5e8f2 100644 --- a/core/scripts/keystone/src/88_contracts_helpers.go +++ b/core/scripts/keystone/src/88_contracts_helpers.go @@ -14,28 +14,37 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/forwarder" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/ocr3_capability" + verifierContract "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/llo-feeds/generated/verifier_proxy" ) var ZeroAddress = common.Address{} type OnChainMetaSerialized struct { - OCRContract common.Address `json:"ocrContract"` - ForwarderContract common.Address `json:"forwarderContract"` + OCR common.Address `json:"ocrContract"` + Forwarder common.Address `json:"forwarderContract"` // The block number of the transaction that set the config on the OCR3 contract. We use this to replay blocks from this point on // when we load the OCR3 job specs on the nodes. SetConfigTxBlock uint64 `json:"setConfigTxBlock"` CapabilitiesRegistry common.Address `json:"CapabilitiesRegistry"` + Verifier common.Address `json:"VerifierContract"` + VerifierProxy common.Address `json:"VerifierProxy"` + // Stores the address that has been initialized by the proxy, if any + InitializedVerifierAddress common.Address `json:"InitializedVerifierAddress"` } type onchainMeta struct { - OCRContract ocr3_capability.OCR3CapabilityInterface - ForwarderContract forwarder.KeystoneForwarderInterface + OCR3 ocr3_capability.OCR3CapabilityInterface + Forwarder forwarder.KeystoneForwarderInterface // The block number of the transaction that set the config on the OCR3 contract. We use this to replay blocks from this point on // when we load the OCR3 job specs on the nodes. SetConfigTxBlock uint64 - CapabilitiesRegistry capabilities_registry.CapabilitiesRegistryInterface + CapabilitiesRegistry capabilities_registry.CapabilitiesRegistryInterface + Verifier verifierContract.VerifierInterface + VerifierProxy verifier_proxy.VerifierProxyInterface + InitializedVerifierAddress common.Address `json:"InitializedVerifierAddress"` } func WriteOnchainMeta(o *onchainMeta, artefactsDir string) { @@ -44,20 +53,29 @@ func WriteOnchainMeta(o *onchainMeta, artefactsDir string) { fmt.Println("Writing deployed contract addresses to file...") serialzed := OnChainMetaSerialized{} - if o.OCRContract != nil { - serialzed.OCRContract = o.OCRContract.Address() + if o.OCR3 != nil { + serialzed.OCR = o.OCR3.Address() } - if o.ForwarderContract != nil { - serialzed.ForwarderContract = o.ForwarderContract.Address() + if o.Forwarder != nil { + serialzed.Forwarder = o.Forwarder.Address() } serialzed.SetConfigTxBlock = o.SetConfigTxBlock + serialzed.InitializedVerifierAddress = o.InitializedVerifierAddress if o.CapabilitiesRegistry != nil { serialzed.CapabilitiesRegistry = o.CapabilitiesRegistry.Address() } + if o.Verifier != nil { + serialzed.Verifier = o.Verifier.Address() + } + + if o.VerifierProxy != nil { + serialzed.VerifierProxy = o.VerifierProxy.Address() + } + jsonBytes, err := json.Marshal(serialzed) PanicErr(err) @@ -85,26 +103,26 @@ func LoadOnchainMeta(artefactsDir string, env helpers.Environment) *onchainMeta } hydrated.SetConfigTxBlock = s.SetConfigTxBlock - if s.OCRContract != ZeroAddress { - if !contractExists(s.OCRContract, env) { - fmt.Printf("OCR contract at %s does not exist, setting to zero address\n", s.OCRContract.Hex()) - s.OCRContract = ZeroAddress + if s.OCR != ZeroAddress { + if !contractExists(s.OCR, env) { + fmt.Printf("OCR contract at %s does not exist, setting to zero address\n", s.OCR.Hex()) + s.OCR = ZeroAddress } - ocr3, err := ocr3_capability.NewOCR3Capability(s.OCRContract, env.Ec) + ocr3, err := ocr3_capability.NewOCR3Capability(s.OCR, env.Ec) PanicErr(err) - hydrated.OCRContract = ocr3 + hydrated.OCR3 = ocr3 } - if s.ForwarderContract != ZeroAddress { - if !contractExists(s.ForwarderContract, env) { - fmt.Printf("Forwarder contract at %s does not exist, setting to zero address\n", s.ForwarderContract.Hex()) - s.ForwarderContract = ZeroAddress + if s.Forwarder != ZeroAddress { + if !contractExists(s.Forwarder, env) { + fmt.Printf("Forwarder contract at %s does not exist, setting to zero address\n", s.Forwarder.Hex()) + s.Forwarder = ZeroAddress } - fwdr, err := forwarder.NewKeystoneForwarder(s.ForwarderContract, env.Ec) + fwdr, err := forwarder.NewKeystoneForwarder(s.Forwarder, env.Ec) PanicErr(err) - hydrated.ForwarderContract = fwdr + hydrated.Forwarder = fwdr } if s.CapabilitiesRegistry != ZeroAddress { @@ -118,6 +136,30 @@ func LoadOnchainMeta(artefactsDir string, env helpers.Environment) *onchainMeta hydrated.CapabilitiesRegistry = cr } + if s.Verifier != ZeroAddress { + if !contractExists(s.Verifier, env) { + fmt.Printf("Verifier contract at %s does not exist, setting to zero address\n", s.Verifier.Hex()) + s.Verifier = ZeroAddress + } + + verifier, err := verifierContract.NewVerifier(s.Verifier, env.Ec) + PanicErr(err) + hydrated.Verifier = verifier + } + + if s.VerifierProxy != ZeroAddress { + if !contractExists(s.VerifierProxy, env) { + fmt.Printf("VerifierProxy contract at %s does not exist, setting to zero address\n", s.VerifierProxy.Hex()) + s.VerifierProxy = ZeroAddress + } + + verifierProxy, err := verifier_proxy.NewVerifierProxy(s.VerifierProxy, env.Ec) + PanicErr(err) + hydrated.VerifierProxy = verifierProxy + } + + hydrated.InitializedVerifierAddress = s.InitializedVerifierAddress + blkNum, err := env.Ec.BlockNumber(context.Background()) PanicErr(err) diff --git a/core/scripts/keystone/src/88_jobspecs_helpers.go b/core/scripts/keystone/src/88_jobspecs_helpers.go index 5d5b782cddb..4c106ead505 100644 --- a/core/scripts/keystone/src/88_jobspecs_helpers.go +++ b/core/scripts/keystone/src/88_jobspecs_helpers.go @@ -31,12 +31,11 @@ func upsertJob(api *nodeAPI, jobSpecName string, jobSpecStr string) { if job.Name == jobSpecName { fmt.Printf("Job already exists: %s, replacing..\n", jobSpecName) api.withArg(job.Id).mustExec(api.methods.DeleteJob) - fmt.Printf("Deleted job: %s\n", jobSpecName) break } } - fmt.Printf("Deploying jobspec: %s\n... \n", jobSpecStr) + fmt.Printf("Deploying jobspec: %s\n", jobSpecName) _, err := api.withArg(jobSpecStr).exec(api.methods.CreateJob) if err != nil { panic(fmt.Sprintf("Failed to deploy job spec: %s Error: %s", jobSpecStr, err)) diff --git a/core/scripts/keystone/src/88_ocr_helpers.go b/core/scripts/keystone/src/88_ocr_helpers.go index 90fb7b9b538..369c4e81c4c 100644 --- a/core/scripts/keystone/src/88_ocr_helpers.go +++ b/core/scripts/keystone/src/88_ocr_helpers.go @@ -1,8 +1,9 @@ package src import ( + "encoding/hex" + "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" ksdeploy "github.com/smartcontractkit/chainlink/integration-tests/deployment/keystone" "github.com/smartcontractkit/libocr/offchainreporting2/types" ) @@ -20,6 +21,30 @@ func ocrConfToContractConfig(ocrConf ksdeploy.Orc2drOracleConfig, configCount ui return cc } + +func mercuryOCRConfigToContractConfig(ocrConf MercuryOCR2Config, configCount uint32) types.ContractConfig { + cc := types.ContractConfig{ + Signers: convertAddressesToOnchainPublicKeys(ocrConf.Signers), + Transmitters: convertBytes32sToAccounts(ocrConf.Transmitters), + F: ocrConf.F, + OnchainConfig: ocrConf.OnchainConfig, + OffchainConfigVersion: ocrConf.OffchainConfigVersion, + OffchainConfig: ocrConf.OffchainConfig, + ConfigCount: uint64(configCount), + } + + return cc +} + +func convertAddressesToOnchainPublicKeys(addresses []common.Address) []types.OnchainPublicKey { + keys := make([]types.OnchainPublicKey, len(addresses)) + for i, addr := range addresses { + keys[i] = types.OnchainPublicKey(addr.Bytes()) + } + return keys +} + + func convertAddressesToAccounts(addresses []common.Address) []types.Account { accounts := make([]types.Account, len(addresses)) for i, addr := range addresses { @@ -28,10 +53,18 @@ func convertAddressesToAccounts(addresses []common.Address) []types.Account { return accounts } +func convertBytes32sToAccounts(bs [][32]byte) []types.Account { + accounts := make([]types.Account, len(bs)) + for i, b := range bs { + accounts[i] = types.Account(hex.EncodeToString(b[:])) + } + return accounts +} + func convertByteSliceToOnchainPublicKeys(bs [][]byte) []types.OnchainPublicKey { keys := make([]types.OnchainPublicKey, len(bs)) for i, b := range bs { - keys[i] = types.OnchainPublicKey(hexutil.Encode(b)) + keys[i] = types.OnchainPublicKey(hex.EncodeToString(b)) } return keys } diff --git a/core/scripts/keystone/src/99_app.go b/core/scripts/keystone/src/99_app.go index 93305c9462f..87ae9aada01 100644 --- a/core/scripts/keystone/src/99_app.go +++ b/core/scripts/keystone/src/99_app.go @@ -2,22 +2,33 @@ package src import ( "bytes" + "context" "encoding/json" "errors" "flag" - "fmt" "io" + "net/url" "reflect" "runtime" "strings" + "sync" "time" "github.com/jpillora/backoff" + clsessions "github.com/smartcontractkit/chainlink/v2/core/sessions" "github.com/urfave/cli" + "go.uber.org/zap/zapcore" helpers "github.com/smartcontractkit/chainlink/core/scripts/common" "github.com/smartcontractkit/chainlink/v2/core/cmd" clcmd "github.com/smartcontractkit/chainlink/v2/core/cmd" + "github.com/smartcontractkit/chainlink/v2/core/logger" +) + +// Package-level cache and mutex +var ( + nodeAPICache = make(map[string]*nodeAPI) + cacheMutex = &sync.Mutex{} ) // NewRedialBackoff is a standard backoff to use for redialling or reconnecting to @@ -31,23 +42,45 @@ func NewRedialBackoff() backoff.Backoff { } func newApp(n NodeWthCreds, writer io.Writer) (*clcmd.Shell, *cli.App) { + loggingCfg := logger.Config{ + LogLevel: zapcore.InfoLevel, + JsonConsole: true, + } + logger, closeLggr := loggingCfg.New() + u, err := url.Parse(n.RemoteURL.String()) + PanicErr(err) + + clientOpts := clcmd.ClientOpts{RemoteNodeURL: *u, InsecureSkipVerify: true} + sr := clsessions.SessionRequest{Email: n.APILogin, Password: n.APIPassword} + + // Set the log level to error for the HTTP client, we don't care about + // the ssl warnings it emits for CRIB + logger.SetLogLevel(zapcore.ErrorLevel) + cookieAuth := cmd.NewSessionCookieAuthenticator( + clientOpts, + &cmd.MemoryCookieStore{}, + logger, + ) + cookieAuth.Authenticate(context.Background(), sr) + http := cmd.NewAuthenticatedHTTPClient( + logger, + clientOpts, + cookieAuth, + sr, + ) + // Set the log level back to info for the shell + logger.SetLogLevel(zapcore.InfoLevel) + client := &clcmd.Shell{ - Renderer: clcmd.RendererJSON{Writer: writer}, - AppFactory: clcmd.ChainlinkAppFactory{}, - KeyStoreAuthenticator: clcmd.TerminalKeyStoreAuthenticator{Prompter: n}, - FallbackAPIInitializer: clcmd.NewPromptingAPIInitializer(n), - Runner: clcmd.ChainlinkRunner{}, - PromptingSessionRequestBuilder: clcmd.NewPromptingSessionRequestBuilder(n), - ChangePasswordPrompter: clcmd.NewChangePasswordPrompter(), - PasswordPrompter: clcmd.NewPasswordPrompter(), + Logger: logger, + Renderer: clcmd.RendererJSON{Writer: writer}, + AppFactory: clcmd.ChainlinkAppFactory{}, + Runner: clcmd.ChainlinkRunner{}, + HTTP: http, + CookieAuthenticator: cookieAuth, + CloseLogger: closeLggr, } app := clcmd.NewApp(client) - fs := flag.NewFlagSet("blah", flag.ContinueOnError) - fs.String("remote-node-url", n.RemoteURL.String(), "") - fs.Bool("insecure-skip-verify", true, "") - helpers.PanicErr(app.Before(cli.NewContext(nil, fs, nil))) - // overwrite renderer since it's set to stdout after Before() is called - client.Renderer = clcmd.RendererJSON{Writer: writer} return client, app } @@ -60,6 +93,17 @@ type nodeAPI struct { } func newNodeAPI(n NodeWthCreds) *nodeAPI { + // Create a unique key for the cache + key := n.RemoteURL.String() + + // Check if the nodeAPI exists in the cache + cacheMutex.Lock() + if api, exists := nodeAPICache[key]; exists { + cacheMutex.Unlock() + return api + } + cacheMutex.Unlock() + output := &bytes.Buffer{} methods, app := newApp(n, output) @@ -70,29 +114,10 @@ func newNodeAPI(n NodeWthCreds) *nodeAPI { fs: flag.NewFlagSet("test", flag.ContinueOnError), } - fmt.Println("Logging in:", n.RemoteURL) - loginFs := flag.NewFlagSet("test", flag.ContinueOnError) - loginFs.Bool("bypass-version-check", true, "") - loginCtx := cli.NewContext(app, loginFs, nil) - - redial := NewRedialBackoff() - - for { - err := methods.RemoteLogin(loginCtx) - if err == nil { - break - } - - fmt.Println("Error logging in:", err) - if strings.Contains(err.Error(), "invalid character '<' looking for beginning of value") { - fmt.Println("Likely a transient network error, retrying...") - } else { - helpers.PanicErr(err) - } - - time.Sleep(redial.Duration()) - } - output.Reset() + // Store the new nodeAPI in the cache + cacheMutex.Lock() + nodeAPICache[key] = api + cacheMutex.Unlock() return api } diff --git a/core/scripts/keystone/src/99_crib_client.go b/core/scripts/keystone/src/99_crib_client.go index 0d946805904..0bf1610a4dd 100644 --- a/core/scripts/keystone/src/99_crib_client.go +++ b/core/scripts/keystone/src/99_crib_client.go @@ -83,16 +83,3 @@ func (m *CribClient) getCLNodes() ([]NodeWthCreds, error) { return nodes, nil } - -// Implements Prompter interface -func (n NodeWthCreds) IsTerminal() bool { - return false -} - -func (n NodeWthCreds) PasswordPrompt(p string) string { - return n.APIPassword -} - -func (n NodeWthCreds) Prompt(p string) string { - return n.APILogin -}