diff --git a/integration-tests/common/client.go b/integration-tests/common/client.go index 44971cab..671af67e 100644 --- a/integration-tests/common/client.go +++ b/integration-tests/common/client.go @@ -1,6 +1,8 @@ package common import ( + "fmt" + "github.com/smartcontractkit/chainlink-env/environment" "github.com/smartcontractkit/chainlink/integration-tests/client" ) @@ -18,10 +20,12 @@ func NewChainlinkClient(env *environment.Environment, chainName string, chainId if err != nil { return nil, err } + fmt.Println(nodes) nodeKeys, _, err := client.CreateNodeKeysBundle(nodes, chainName, chainId) if err != nil { return nil, err } + fmt.Println(nodeKeys) for _, n := range nodes { _, _, err = n.CreateCosmosChain(&client.CosmosChainAttributes{ ChainID: chainId, @@ -47,23 +51,23 @@ func NewChainlinkClient(env *environment.Environment, chainName string, chainId } func (cc *ChainlinkClient) LoadOCR2Config(accountAddresses []string) (*OCR2Config, error) { - var offChaiNKeys []string - var onChaiNKeys []string + 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) + 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) + onChainKeys = append(onChainKeys, key.OCR2Key.Data.Attributes.OnChainPublicKey) cfgKeys = append(cfgKeys, key.OCR2Key.Data.Attributes.ConfigPublicKey) } var payload = TestOCR2Config - payload.Signers = onChaiNKeys + payload.Signers = onChainKeys payload.Transmitters = txKeys - payload.OffchainConfig.OffchainPublicKeys = offChaiNKeys + 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 dd9de27c..f9855850 100644 --- a/integration-tests/common/common.go +++ b/integration-tests/common/common.go @@ -26,34 +26,19 @@ 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 { - IsSoak bool - P2PPort string - ChainName string - ChainId string - NodeCount int - TTL time.Duration - NodeUrl string - Mnemonic string - Account string - ObservationSource string - JuelsPerFeeCoinSource string - ClConfig map[string]any - K8Config *environment.Config - Env *environment.Environment + IsSoak bool + P2PPort string + ChainName string + ChainId string + NodeCount int + TTL time.Duration + NodeUrl string + Mnemonic string + Account 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 @@ -96,16 +81,14 @@ func getTTL() time.Duration { func NewCommon() *Common { c := &Common{ - IsSoak: getEnv("SOAK") != "", - ChainName: chainName, - ChainId: chainID, - NodeCount: getNodeCount(), - TTL: getTTL(), - NodeUrl: getEnv("NODE_URL"), - Mnemonic: getEnv("MNEMONIC"), - Account: getEnv("ACCOUNT"), - ObservationSource: observationSource, - JuelsPerFeeCoinSource: juelsPerFeeCoinSource, + IsSoak: getEnv("SOAK") != "", + ChainName: chainName, + ChainId: chainID, + NodeCount: getNodeCount(), + TTL: getTTL(), + NodeUrl: getEnv("NODE_URL"), + Mnemonic: getEnv("MNEMONIC"), + Account: getEnv("ACCOUNT"), } return c } @@ -146,9 +129,13 @@ ListenAddresses = ['0.0.0.0:6690'] "replicas": c.NodeCount, "toml": baseTOML, } + fmt.Println(c.ClConfig) + // replace this env with local docker c.Env = environment.New(c.K8Config). AddHelm(wasmd.New(nil)). AddHelm(mockservercfg.New(nil)). AddHelm(mockserver.New(nil)). AddHelm(chainlink.New(0, c.ClConfig)) + + fmt.Println(c.Env) } diff --git a/integration-tests/common/test_common.go b/integration-tests/common/test_common.go new file mode 100644 index 00000000..d15fbd32 --- /dev/null +++ b/integration-tests/common/test_common.go @@ -0,0 +1,358 @@ +package common + +import ( + "context" + "fmt" + "math/big" + "testing" + "time" + + cosmosSDK "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/adapters/cosmwasm" + cosmosClient "github.com/smartcontractkit/chainlink-cosmos/pkg/cosmos/client" + "github.com/smartcontractkit/chainlink-relay/pkg/logger" + "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2" + + // "github.com/smartcontractkit/chainlink-starknet/relayer/pkg/starknet" + "github.com/smartcontractkit/chainlink/integration-tests/client" + + "github.com/smartcontractkit/chainlink-cosmos/ops/wasmd" + // "github.com/smartcontractkit/chainlink-starknet/ops" + "github.com/smartcontractkit/chainlink-cosmos/integration-tests/gauntlet" + ctfClient "github.com/smartcontractkit/chainlink-testing-framework/client" + "github.com/smartcontractkit/chainlink-testing-framework/utils" + // "github.com/smartcontractkit/chainlink-starknet/integration-tests/utils" +) + +var ( + // These are one of the default addresses based on the seed we pass to devnet which is 0 + // defaultWalletPrivKey = ops.PrivateKeys0Seed[0] + defaultWalletAddress string // derived in init() + rpcRequestTimeout = time.Second * 300 + dumpPath = "/dumps/dump.pkl" + mockServerValue = 900000 +) + +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 init() { +// // wallet contract derivation +// var keyBytes []byte +// keyBytes, err := hex.DecodeString(strings.TrimPrefix(defaultWalletPrivKey, "0x")) +// if err != nil { +// panic(err) +// } +// accountBytes, err := pubKeyToDevnetAccount(starkkey.Raw(keyBytes).Key().PublicKey()) +// if err != nil { +// panic(err) +// } +// defaultWalletAddress = "0x" + hex.EncodeToString(accountBytes) +// } + +type Test struct { + Devnet *wasmd.CosmosDevnetClient + Cc *ChainlinkClient + CosmosClient *cosmosClient.Client + OCR2Client *cosmwasm.OCR2Reader + Sg *gauntlet.StarknetGauntlet + mockServer *ctfClient.MockserverClient + Common *Common + AccountAddresses []string + LinkTokenAddr string + OCRAddr string + AccessControllerAddr string + ProxyAddr string + ObservationSource string + JuelsPerFeeCoinSource string + T *testing.T +} + +// 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() + testState.T.Log("deploy env success") + if testState.Common.Env.WillUseRemoteRunner() { + return // short circuit here if using a remote runner + } + testState.SetupClients() + if testState.Common.Testnet { + testState.Common.Env.URLs[testState.Common.ServiceKeyL2][1] = testState.Common.NodeUrl + } + 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.CosmosClient, err = cosmosClient.NewClient(testState.Common.ChainId, testState.Common.NodeUrl, lggr, &rpcRequestTimeout) + require.NoError(testState.T, err, "Creating starknet client should not fail") + testState.OCR2Client = cosmwasm.NewOCR2Reader(cosmosSDK.AccAddress(testState.OCRAddr), testState.CosmosClient, lggr) + // 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") +} + +// 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)) + } else { + 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) + } +} + +// LoadOCR2Config Loads and returns the default starknet gauntlet config +// func (testState *Test) 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 { +// offChaiNKeys = append(offChaiNKeys, key.OCR2Key.Data.Attributes.OffChainPublicKey) +// peerIds = append(peerIds, key.PeerID) +// txKeys = append(txKeys, testState.AccountAddresses[i]) +// onChaiNKeys = append(onChaiNKeys, key.OCR2Key.Data.Attributes.OnChainPublicKey) +// cfgKeys = append(cfgKeys, key.OCR2Key.Data.Attributes.ConfigPublicKey) +// } + +// var payload = ops.TestOCR2Config +// payload.Signers = onChaiNKeys +// payload.Transmitters = txKeys +// payload.OffchainConfig.OffchainPublicKeys = offChaiNKeys +// payload.OffchainConfig.PeerIds = peerIds +// payload.OffchainConfig.ConfigPublicKeys = cfgKeys + +// return &payload, nil +// } + +func (testState *Test) SetUpNodes(mockServerVal int) { + testState.SetBridgeTypeAttrs(&client.BridgeTypeAttributes{ + Name: "bridge-mockserver", + URL: testState.GetMockServerURL(), + }) + err := testState.SetMockServerValue("", mockServerVal) + require.NoError(testState.T, err, "Setting mock server value should not fail") + err = testState.Common.CreateJobsForContract(testState.GetChainlinkClient(), testState.ObservationSource, testState.JuelsPerFeeCoinSource, testState.OCRAddr, testState.AccountAddresses) + require.NoError(testState.T, err, "Creating jobs should not fail") +} + +// GetStarknetAddress Returns the local StarkNET address +func (testState *Test) GetStarknetAddress() string { + return testState.Common.Env.URLs[testState.Common.ServiceKeyL2][0] +} + +// GetStarknetAddressRemote Returns the remote StarkNET address +func (testState *Test) GetStarknetAddressRemote() string { + return testState.Common.Env.URLs[testState.Common.ServiceKeyL2][1] +} + +// GetNodeKeys Returns the node key bundles +func (testState *Test) GetNodeKeys() []client.NodeKeysBundle { + return testState.Cc.NKeys +} + +func (testState *Test) GetChainlinkNodes() []*client.Chainlink { + return testState.Cc.ChainlinkNodes +} + +// func (testState *Test) GetDefaultPrivateKey() string { +// return defaultWalletPrivKey +// } + +func (testState *Test) GetDefaultWalletAddress() string { + return defaultWalletAddress +} + +func (testState *Test) GetChainlinkClient() *ChainlinkClient { + return testState.Cc +} + +func (testState *Test) GetCosmosDevnetClient() *wasmd.CosmosDevnetClient { + return testState.Devnet +} + +func (testState *Test) SetBridgeTypeAttrs(attr *client.BridgeTypeAttributes) { + testState.Cc.bTypeAttr = attr +} + +func (testState *Test) GetMockServerURL() string { + return testState.mockServer.Config.ClusterURL +} + +func (testState *Test) SetMockServerValue(path string, val int) error { + return testState.mockServer.SetValuePath(path, val) +} + +func (testState *Test) GetDefaultObservationSource() string { + return observationSource +} + +func (testState *Test) GetDefaultJuelsPerFeeCoinSource() string { + return juelsPerFeeCoinSource +} + +func (testState *Test) ValidateRounds(rounds int, isSoak bool) error { + l := utils.GetTestLogger(testState.T) + ctx := context.Background() // context background used because timeout handled by requestTimeout param + // assert new rounds are occurring + details := ocr2.TransmissionDetails{} + increasing := 0 // track number of increasing rounds + var stuck bool + stuckCount := 0 + var positive bool + + // validate balance in aggregator + // resLINK, errLINK := testState.Starknet.CallContract(ctx, starknet.CallOps{ + // ContractAddress: caigotypes.HexToHash(testState.LinkTokenAddr), + // Selector: "balance_of", + // Calldata: []string{caigotypes.HexToBN(testState.OCRAddr).String()}, + // }) + // require.NoError(testState.T, errLINK, "Reader balance from LINK contract should not fail") + // resAgg, errAgg := testState.Starknet.CallContract(ctx, starknet.CallOps{ + // ContractAddress: caigotypes.HexToHash(testState.OCRAddr), + // Selector: "link_available_for_payment", + // }) + // require.NoError(testState.T, errAgg, "Reader balance from LINK contract should not fail") + // balLINK, _ := new(big.Int).SetString(resLINK[0], 0) + // balAgg, _ := new(big.Int).SetString(resAgg[1], 0) + // isNegative, _ := new(big.Int).SetString(resAgg[0], 0) + // if isNegative.Sign() > 0 { + // 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") + + 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)) + digest, epoch, round, latestAnswer, latestTimestamp, err := testState.OCR2Client.LatestTransmissionDetails(ctx, caigotypes.HexToHash(testState.OCRAddr)) + require.NoError(testState.T, err, "Failed to get latest transmission details") + // end condition: enough rounds have occurred + if !isSoak && increasing >= rounds && positive { + break + } + + // end condition: rounds have been stuck + if stuck && stuckCount > 50 { + l.Debug().Msg("failing to fetch transmissions means blockchain may have stopped") + break + } + + l.Info().Msg(fmt.Sprintf("Setting adapter value to %d", mockServerValue)) + err = testState.SetMockServerValue("", mockServerValue) + if err != nil { + l.Error().Msg(fmt.Sprintf("Setting mock server value error: %+v", err)) + } + // try to fetch rounds + time.Sleep(5 * time.Second) + + if err != nil { + l.Error().Msg(fmt.Sprintf("Transmission Error: %+v", err)) + continue + } + l.Info().Msg(fmt.Sprintf("Transmission Details: %+v", res)) + + // continue if no changes + if epoch == 0 && round == 0 { + continue + } + + ansCmp := latestAnswer.Cmp(big.NewInt(0)) + positive = ansCmp == 1 || positive + + // if changes from zero values set (should only initially) + if epoch > 0 && details.Epoch == 0 { + if !isSoak { + assert.Greater(testState.T, epoch, details.Epoch) + assert.GreaterOrEqual(testState.T, round, details.Round) + assert.NotEqual(testState.T, ansCmp, 0) // assert changed from 0 + assert.NotEqual(testState.T, digest, details.Digest) + assert.Equal(testState.T, details.LatestTimestamp.Before(latestTimestamp), true) + } + details = res + continue + } + // check increasing rounds + if !isSoak { + assert.Equal(testState.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)) + } + } + if (res.Epoch > details.Epoch || (res.Epoch == details.Epoch && res.Round > details.Round)) && details.LatestTimestamp.Before(res.LatestTimestamp) { + increasing++ + stuck = false + stuckCount = 0 // reset counter + continue + } + + // reach this point, answer has not changed + stuckCount++ + if stuckCount > 30 { + stuck = true + increasing = 0 + } + } + 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") + } + + // Test proxy reading + // TODO: would be good to test proxy switching underlying feeds + // roundDataRaw, err := testState.Starknet.CallContract(ctx, starknet.CallOps{ + // ContractAddress: caigotypes.HexToHash(testState.ProxyAddr), + // Selector: "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") + // } + // valueBig, err := starknet.HexToUnsignedBig(roundDataRaw[1]) + // require.NoError(testState.T, err) + // value := valueBig.Int64() + // if value < 0 { + // assert.Equal(testState.T, value, int64(mockServerValue), "Reading from proxy should return correct value") + // } + + return nil +} diff --git a/integration-tests/gauntlet/gauntlet.go b/integration-tests/gauntlet/gauntlet.go index 70ff3f20..880b38e3 100644 --- a/integration-tests/gauntlet/gauntlet.go +++ b/integration-tests/gauntlet/gauntlet.go @@ -23,20 +23,23 @@ type CosmosGauntlet struct { 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"` + Logs interface{} `json:"logs"` + Height int `json:"height"` + TransactionHash string `json:"transactionHash"` + Events []struct { + Type string `json:"type"` + Attributes []struct { + Key string `json:"key"` + Value string `json:"value"` + } `json:"attributes"` + } `json:"events"` + GasWanted int `json:"gasWanted"` + GasUsed int `json:"gasUsed"` + CodeId int `json:"codeId"` // only present in upload commands } `json:"tx"` Contract string `json:"contract"` } `json:"responses"` + Data map[string]interface{} `json:"data"` } // NewCosmosGauntlet Creates a default gauntlet config @@ -214,8 +217,7 @@ func (cg *CosmosGauntlet) BeginProposal(ocrAddress string) (string, error) { if err != nil { return "", err } - // TODO: return proposal id - return cg.gr.Responses[0].Contract, nil + return cg.gr.Data["proposalId"].(string), nil } func (cg *CosmosGauntlet) ProposeOffchainConfig(proposalId string, cfg string, ocrAddress string) (string, error) { diff --git a/integration-tests/ocr2_test.go b/integration-tests/ocr2_test.go index bf79fabf..75a7e71a 100644 --- a/integration-tests/ocr2_test.go +++ b/integration-tests/ocr2_test.go @@ -18,8 +18,19 @@ import ( func TestOCRBasic(t *testing.T) { // Set up test logger := common.GetTestLogger(t) - commonConfig := common.NewCommon() - + t.Common := common.NewCommon() + t.Common.SetDefaultEnvironment(t) + t.Log(commonConfig.Env) + t.Log(commonConfig.Env.ChainlinkNodeDetails) + + // Set up CL nodes + t.DeployCluster() + require.NoError(t, err, "Deploying cluster should not fail") + if testState.Common.Env.WillUseRemoteRunner() { + return // short circuit here if using a remote runner + } + + // Set up gauntlet gauntletWorkingDir := fmt.Sprintf("%s/", utils.ProjectRoot) logger.Info().Str("working dir", gauntletWorkingDir).Msg("Initializing gauntlet") @@ -32,8 +43,6 @@ func TestOCRBasic(t *testing.T) { err = cg.SetupNetwork(commonConfig.NodeUrl, commonConfig.Mnemonic) require.NoError(t, err, "Setting up gauntlet network should not fail") - commonConfig.SetDefaultEnvironment(t) - // TODO: fund nodes if necessary // Upload contracts diff --git a/pkg/cosmos/adapters/cosmwasm/contract_cache.go b/pkg/cosmos/adapters/cosmwasm/contract_cache.go index f86d688d..916defb8 100644 --- a/pkg/cosmos/adapters/cosmwasm/contract_cache.go +++ b/pkg/cosmos/adapters/cosmwasm/contract_cache.go @@ -129,18 +129,18 @@ func (cc *ContractCache) updateConfig(ctx context.Context) error { } func (cc *ContractCache) updateTransmission(ctx context.Context) error { - digest, epoch, round, latestAnswer, latestTimestamp, err := cc.reader.LatestTransmissionDetails(ctx) + res, err := cc.reader.LatestTransmissionDetails(ctx) if err != nil { return fmt.Errorf("fetch latest transmission: %w", err) } now := time.Now() cc.transMu.Lock() cc.transTS = now - cc.digest = digest - cc.epoch = epoch - cc.round = round - cc.latestAnswer = latestAnswer - cc.latestTimestamp = latestTimestamp + cc.digest = res.LatestConfigDigest + cc.epoch = res.Epoch + cc.round = res.Round + cc.latestAnswer = res.LatestAnswer + cc.latestTimestamp = res.LatestTimestamp cc.transMu.Unlock() cc.lggr.Infof("updated transmission details. [epoch %v, round %v, answer %v, ts %v]", epoch, round, latestAnswer, latestTimestamp) diff --git a/pkg/cosmos/adapters/cosmwasm/contract_reader.go b/pkg/cosmos/adapters/cosmwasm/contract_reader.go index aec1707c..1172e836 100644 --- a/pkg/cosmos/adapters/cosmwasm/contract_reader.go +++ b/pkg/cosmos/adapters/cosmwasm/contract_reader.go @@ -8,7 +8,6 @@ import ( "math/big" "strconv" "strings" - "time" cosmosSDK "github.com/cosmos/cosmos-sdk/types" @@ -219,11 +218,7 @@ func (e ErrAttrDupe) Error() string { // LatestTransmissionDetails fetches the latest transmission details from address state func (r *OCR2Reader) LatestTransmissionDetails(ctx context.Context) ( - configDigest types.ConfigDigest, - epoch uint32, - round uint8, - latestAnswer *big.Int, - latestTimestamp time.Time, + transmissionDetails LatestTransmissionDetails, err error, ) { resp, err := r.chainReader.ContractState(r.address, []byte(`"latest_transmission_details"`)) @@ -242,28 +237,32 @@ func (r *OCR2Reader) LatestTransmissionDetails(ctx context.Context) ( if epoch != 0 { r.lggr.Errorf("unexpected non-zero epoch %v and no transmissions found contract %v", epoch, r.address) } - return digest, epoch, 0, big.NewInt(0), time.Unix(0, 0), nil + return LatestTransmissionDetails{ + LatestConfigDigest: digest, + Epoch: epoch, + }, nil } r.lggr.Errorf("error reading latest config digest and epoch err %v contract %v", err2, r.address) } // default response if there actually is an error - return types.ConfigDigest{}, 0, 0, big.NewInt(0), time.Now(), err + return LatestTransmissionDetails{}, err } // unmarshal var details LatestTransmissionDetails if err := json.Unmarshal(resp, &details); err != nil { - return types.ConfigDigest{}, 0, 0, big.NewInt(0), time.Now(), err + return LatestTransmissionDetails{}, err } // set answer big int ans := new(big.Int) if _, success := ans.SetString(details.LatestAnswer, 10); !success { - return types.ConfigDigest{}, 0, 0, big.NewInt(0), time.Now(), fmt.Errorf("Could not create *big.Int from %s", details.LatestAnswer) + return LatestTransmissionDetails{}, fmt.Errorf("Could not create *big.Int from %s", details.LatestAnswer) } + details.LatestAnswer = ans - return details.LatestConfigDigest, details.Epoch, details.Round, ans, time.Unix(details.LatestTimestamp, 0), nil + return details, nil } // LatestRoundRequested fetches the latest round requested by filtering event logs diff --git a/pkg/cosmos/adapters/cosmwasm/types.go b/pkg/cosmos/adapters/cosmwasm/types.go index 36844fd7..7d86d095 100644 --- a/pkg/cosmos/adapters/cosmwasm/types.go +++ b/pkg/cosmos/adapters/cosmwasm/types.go @@ -1,6 +1,9 @@ package cosmwasm import ( + "math/big" + "time" + "github.com/smartcontractkit/libocr/offchainreporting2/types" ) @@ -23,8 +26,8 @@ type LatestTransmissionDetails struct { LatestConfigDigest types.ConfigDigest `json:"latest_config_digest"` Epoch uint32 `json:"epoch"` Round uint8 `json:"round"` - LatestAnswer string `json:"latest_answer"` - LatestTimestamp int64 `json:"latest_timestamp"` + LatestAnswer *big.Int `json:"latest_answer"` + LatestTimestamp time.Time `json:"latest_timestamp"` } type LatestConfigDigestAndEpoch struct {