Skip to content

Commit

Permalink
core/services/ccipcapability: oracle creator impls (#1180)
Browse files Browse the repository at this point in the history
## Motivation

We still need to create the plugin and bootstrap oracles, meaning there
needs to be a concrete implementation of the `OracleCreator` interface.

## Solution

Implement the `OracleCreator` interface. An end to end Integration test
is also included in this PR, which tests commit reports. However, these
commit reports do NOT contain merkle roots, some issues need to be
resolved in upcoming PRs which involve changes in chainlink-common.

## Notes

* This PR is using `legacyevm` instead of relayers because chain writer
changes have not yet fully propagated to this repo. Once they have we
can update the implementation to use the relayers.

## Requires

* smartcontractkit/chainlink-ccip#20

---------

Co-authored-by: Abdelrahman Soliman (Boda) <[email protected]>
Co-authored-by: asoliman <[email protected]>
  • Loading branch information
3 people authored Jul 18, 2024
1 parent f420af8 commit be120a2
Show file tree
Hide file tree
Showing 45 changed files with 4,272 additions and 257 deletions.
1 change: 1 addition & 0 deletions contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ compileContract ccip/test/helpers/CommitStoreHelper.sol
compileContract ccip/test/helpers/MessageHasher.sol
compileContract ccip/test/helpers/ReportCodec.sol
compileContract ccip/test/helpers/receivers/MaybeRevertMessageReceiver.sol
compileContract ccip/test/helpers/MultiOCR3Helper.sol
compileContract ccip/test/mocks/MockRMN1_0.sol
compileContract ccip/test/mocks/MockE2EUSDCTokenMessenger.sol
compileContract ccip/test/mocks/MockE2EUSDCTransmitter.sol
Expand Down
1,096 changes: 1,096 additions & 0 deletions core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mock_usdc_token_messenger: ../../../contracts/solc/v0.8.24/MockE2EUSDCTokenMesse
mock_usdc_token_transmitter: ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.abi ../../../contracts/solc/v0.8.24/MockE2EUSDCTransmitter/MockE2EUSDCTransmitter.bin 33bdad70822e889de7c720ed20085cf9cd3f8eba8b68f26bd6535197749595fe
mock_v3_aggregator_contract: ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.abi ../../../contracts/solc/v0.8.24/MockV3Aggregator/MockV3Aggregator.bin 518e19efa2ff52b0fefd8e597b05765317ee7638189bfe34ca43de2f6599faf4
multi_aggregate_rate_limiter: ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.abi ../../../contracts/solc/v0.8.24/MultiAggregateRateLimiter/MultiAggregateRateLimiter.bin abb0ecb1ed8621f26e43b39f5fa25f3d0b6d6c184fa37c404c4389605ecb74e7
multi_ocr3_helper: ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.abi ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.bin aa299e0c2659d53aad4eace4d66be0e734b1366008593669cf30361ff529da6a
nonce_manager: ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.abi ../../../contracts/solc/v0.8.24/NonceManager/NonceManager.bin 78b58f4f192db7496e2b6de805d6a2c918b98d4fa62f3c7ed145ef3b5657a40d
ocr3_config_encoder: ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.abi ../../../contracts/solc/v0.8.24/IOCR3ConfigEncoder/IOCR3ConfigEncoder.bin e21180898e1ad54a045ee20add85a2793c681425ea06f66d1a9e5cab128b6487
ping_pong_demo: ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin 1588313bb5e781d181a825247d30828f59007700f36b4b9b00391592b06ff4b4
Expand Down
1 change: 1 addition & 0 deletions core/gethwrappers/ccip/go_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ package ccip
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.abi ../../../contracts/solc/v0.8.24/PingPongDemo/PingPongDemo.bin PingPongDemo ping_pong_demo
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.abi ../../../contracts/solc/v0.8.24/SelfFundedPingPong/SelfFundedPingPong.bin SelfFundedPingPong self_funded_ping_pong
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.abi ../../../contracts/solc/v0.8.24/MessageHasher/MessageHasher.bin MessageHasher message_hasher
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.abi ../../../contracts/solc/v0.8.24/MultiOCR3Helper/MultiOCR3Helper.bin MultiOCR3Helper multi_ocr3_helper
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin ReportCodec report_codec
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.abi ../../../contracts/solc/v0.8.24/EtherSenderReceiver/EtherSenderReceiver.bin EtherSenderReceiver ether_sender_receiver
//go:generate go run ../generation/generate/wrap.go ../../../contracts/solc/v0.8.24/WETH9/WETH9.abi ../../../contracts/solc/v0.8.24/WETH9/WETH9.bin WETH9 weth9
Expand Down
1 change: 1 addition & 0 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shirou/gopsutil/v3 v3.24.3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240717202359-0bcfc8ee468d // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 // indirect
github.com/smartcontractkit/chainlink-data-streams v0.0.0-20240702144807-761f63e7b527 // indirect
github.com/smartcontractkit/chainlink-feeds v0.0.0-20240522213638-159fb2d99917 // indirect
Expand Down
2 changes: 2 additions & 0 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,8 @@ github.com/smartcontractkit/chain-selectors v1.0.18 h1:ackCMDOlWuwULAyBNj9fQeQme
github.com/smartcontractkit/chain-selectors v1.0.18/go.mod h1:d4Hi+E1zqjy9HqMkjBE5q1vcG9VGgxf5VxiRHfzi2kE=
github.com/smartcontractkit/chainlink-automation v1.0.4 h1:iyW181JjKHLNMnDleI8umfIfVVlwC7+n5izbLSFgjw8=
github.com/smartcontractkit/chainlink-automation v1.0.4/go.mod h1:u4NbPZKJ5XiayfKHD/v3z3iflQWqvtdhj13jVZXj/cM=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240717202359-0bcfc8ee468d h1:fOdEh9zHNT+QQBZWSRv0uxqyTMnw3rCPR4lr2DtYvhw=
github.com/smartcontractkit/chainlink-ccip v0.0.0-20240717202359-0bcfc8ee468d/go.mod h1:gyODeD1uMobe5VWRwVRiEXzL9wUzFTI80VvyCNLqWcw=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240708180634-24440372521a h1:0HUP3qmHejg7FyFdY+R+8iFg0kNtrvnAxaQ//+fuZfc=
github.com/smartcontractkit/chainlink-common v0.1.7-0.20240708180634-24440372521a/go.mod h1:fh9eBbrReCmv31bfz52ENCAMa7nTKQbdhb2B3+S2VGo=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20240621143432-85370a54b141 h1:TMOoYaeSDkkI3jkCH7lKHOZaLkeDuxFTNC+XblD6M0M=
Expand Down
23 changes: 23 additions & 0 deletions core/services/ccipcapability/common/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package common

import (
"fmt"

"github.com/ethereum/go-ethereum/crypto"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/utils"
)

// HashedCapabilityID returns the hashed capability id in a manner equivalent to the capability registry.
func HashedCapabilityID(capabilityLabelledName, capabilityVersion string) (r [32]byte, err error) {
// TODO: investigate how to avoid parsing the ABI everytime.
tabi := `[{"type": "string"}, {"type": "string"}]`
abiEncoded, err := utils.ABIEncode(tabi, capabilityLabelledName, capabilityVersion)
if err != nil {
return r, fmt.Errorf("failed to ABI encode capability version and labelled name: %w", err)
}

h := crypto.Keccak256(abiEncoded)
copy(r[:], h)
return r, nil
}
50 changes: 50 additions & 0 deletions core/services/ccipcapability/common/common_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package common_test

import (
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/stretchr/testify/require"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
capcommon "github.com/smartcontractkit/chainlink/v2/core/services/ccipcapability/common"
)

func Test_HashedCapabilityId(t *testing.T) {
transactor := testutils.MustNewSimTransactor(t)
sb := backends.NewSimulatedBackend(core.GenesisAlloc{
transactor.From: {Balance: assets.Ether(1000).ToInt()},
}, 30e6)

crAddress, _, _, err := kcr.DeployCapabilitiesRegistry(transactor, sb)
require.NoError(t, err)
sb.Commit()

cr, err := kcr.NewCapabilitiesRegistry(crAddress, sb)
require.NoError(t, err)

// add a capability, ignore cap config for simplicity.
_, err = cr.AddCapabilities(transactor, []kcr.CapabilitiesRegistryCapability{
{
LabelledName: "ccip",
Version: "v1.0.0",
CapabilityType: 0,
ResponseType: 0,
ConfigurationContract: common.Address{},
},
})
require.NoError(t, err)
sb.Commit()

hidExpected, err := cr.GetHashedCapabilityId(nil, "ccip", "v1.0.0")
require.NoError(t, err)

hid, err := capcommon.HashedCapabilityID("ccip", "v1.0.0")
require.NoError(t, err)

require.Equal(t, hidExpected, hid)
}
67 changes: 67 additions & 0 deletions core/services/ccipcapability/configs/evm/chain_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package evm

import (
"encoding/json"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"

"github.com/smartcontractkit/chainlink-ccip/pkg/consts"
"github.com/smartcontractkit/chainlink/v2/common/txmgr"

"github.com/smartcontractkit/chainlink/v2/core/chains/evm/assets"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp"
evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
)

var (
offrampABI = evmtypes.MustGetABI(evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI)
)

func MustChainWriterConfig(fromAddress common.Address, maxGasPrice *assets.Wei) []byte {
rawConfig := ChainWriterConfigRaw(fromAddress, maxGasPrice)
encoded, err := json.Marshal(rawConfig)
if err != nil {
panic(fmt.Errorf("failed to marshal ChainWriterConfig: %w", err))
}

return encoded
}

// ChainWriterConfigRaw returns a ChainWriterConfig that can be used to transmit commit and execute reports.
func ChainWriterConfigRaw(fromAddress common.Address, maxGasPrice *assets.Wei) evmrelaytypes.ChainWriterConfig {
return evmrelaytypes.ChainWriterConfig{
Contracts: map[string]*evmrelaytypes.ContractConfig{
consts.ContractNameOffRamp: {
ContractABI: evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI,
Configs: map[string]*evmrelaytypes.ChainWriterDefinition{
consts.MethodCommit: {
ChainSpecificName: mustGetMethodName("commit", offrampABI),
FromAddress: fromAddress,
// TODO: inject this into the method, should be fetched from the OCR config.
GasLimit: 500_000,
},
consts.MethodExecute: {
ChainSpecificName: mustGetMethodName("execute", offrampABI),
FromAddress: fromAddress,
// TODO: inject this into the method, should be fetched from the OCR config.
GasLimit: 6_500_000,
},
},
},
},
SendStrategy: txmgr.NewSendEveryStrategy(),
MaxGasPrice: maxGasPrice,
}
}

// mustGetMethodName panics if the method name is not found in the provided ABI.
func mustGetMethodName(name string, tabi abi.ABI) (methodName string) {
m, ok := tabi.Methods[name]
if !ok {
panic(fmt.Sprintf("missing method %s in offrampABI", name))
}
return m.Name
}
190 changes: 190 additions & 0 deletions core/services/ccipcapability/configs/evm/contract_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package evm

import (
"encoding/json"
"fmt"

"github.com/ethereum/go-ethereum/accounts/abi"

"github.com/smartcontractkit/chainlink-ccip/pkg/consts"

evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/ccip_config"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_offramp"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/evm_2_evm_multi_onramp"
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"
evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types"
)

var (
onrampABI = evmtypes.MustGetABI(evm_2_evm_multi_onramp.EVM2EVMMultiOnRampABI)
capabilitiesRegsitryABI = evmtypes.MustGetABI(kcr.CapabilitiesRegistryABI)
ccipConfigABI = evmtypes.MustGetABI(ccip_config.CCIPConfigABI)
)

// MustSourceReaderConfig returns a ChainReaderConfig that can be used to read from the onramp.
// The configuration is marshaled into JSON so that it can be passed to the relayer NewContractReader() method.
func MustSourceReaderConfig() []byte {
rawConfig := SourceReaderConfig()
encoded, err := json.Marshal(rawConfig)
if err != nil {
panic(fmt.Errorf("failed to marshal ChainReaderConfig into JSON: %w", err))
}

return encoded
}

// MustDestReaderConfig returns a ChainReaderConfig that can be used to read from the offramp.
// The configuration is marshaled into JSON so that it can be passed to the relayer NewContractReader() method.
func MustDestReaderConfig() []byte {
rawConfig := DestReaderConfig()
encoded, err := json.Marshal(rawConfig)
if err != nil {
panic(fmt.Errorf("failed to marshal ChainReaderConfig into JSON: %w", err))
}

return encoded
}

// DestReaderConfig returns a ChainReaderConfig that can be used to read from the offramp.
func DestReaderConfig() evmrelaytypes.ChainReaderConfig {
return evmrelaytypes.ChainReaderConfig{
Contracts: map[string]evmrelaytypes.ChainContractReader{
consts.ContractNameOffRamp: {
ContractABI: evm_2_evm_multi_offramp.EVM2EVMMultiOffRampABI,
ContractPollingFilter: evmrelaytypes.ContractPollingFilter{
GenericEventNames: []string{
mustGetEventName(consts.EventNameExecutionStateChanged, offrampABI),
mustGetEventName(consts.EventNameCommitReportAccepted, offrampABI),
},
},
Configs: map[string]*evmrelaytypes.ChainReaderDefinition{
consts.MethodNameGetExecutionState: {
ChainSpecificName: mustGetMethodName("getExecutionState", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetMerkleRoot: {
ChainSpecificName: mustGetMethodName("getMerkleRoot", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameIsBlessed: {
ChainSpecificName: mustGetMethodName("isBlessed", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetLatestPriceSequenceNumber: {
ChainSpecificName: mustGetMethodName("getLatestPriceSequenceNumber", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameOfframpGetStaticConfig: {
ChainSpecificName: mustGetMethodName("getStaticConfig", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameOfframpGetDynamicConfig: {
ChainSpecificName: mustGetMethodName("getDynamicConfig", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetSourceChainConfig: {
ChainSpecificName: mustGetMethodName("getSourceChainConfig", offrampABI),
ReadType: evmrelaytypes.Method,
},
consts.EventNameCommitReportAccepted: {
ChainSpecificName: mustGetEventName(consts.EventNameCommitReportAccepted, offrampABI),
ReadType: evmrelaytypes.Event,
},
consts.EventNameExecutionStateChanged: {
ChainSpecificName: mustGetEventName(consts.EventNameExecutionStateChanged, offrampABI),
ReadType: evmrelaytypes.Event,
},
},
},
},
}
}

// SourceReaderConfig returns a ChainReaderConfig that can be used to read from the onramp.
func SourceReaderConfig() evmrelaytypes.ChainReaderConfig {
return evmrelaytypes.ChainReaderConfig{
Contracts: map[string]evmrelaytypes.ChainContractReader{
consts.ContractNameOnRamp: {
ContractABI: evm_2_evm_multi_onramp.EVM2EVMMultiOnRampABI,
ContractPollingFilter: evmrelaytypes.ContractPollingFilter{
GenericEventNames: []string{
mustGetEventName(consts.EventNameCCIPSendRequested, onrampABI),
},
},
Configs: map[string]*evmrelaytypes.ChainReaderDefinition{
// all "{external|public} view" functions in the onramp except for getFee and getPoolBySourceToken are here.
// getFee is not expected to get called offchain and is only called by end-user contracts.
consts.MethodNameGetExpectedNextSequenceNumber: {
ChainSpecificName: mustGetMethodName("getExpectedNextSequenceNumber", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameOnrampGetStaticConfig: {
ChainSpecificName: mustGetMethodName("getStaticConfig", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameOnrampGetDynamicConfig: {
ChainSpecificName: mustGetMethodName("getDynamicConfig", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetDestChainConfig: {
ChainSpecificName: mustGetMethodName("getDestChainConfig", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetPremiumMultiplierWeiPerEth: {
ChainSpecificName: mustGetMethodName("getPremiumMultiplierWeiPerEth", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.MethodNameGetTokenTransferFeeConfig: {
ChainSpecificName: mustGetMethodName("getTokenTransferFeeConfig", onrampABI),
ReadType: evmrelaytypes.Method,
},
consts.EventNameCCIPSendRequested: {
ChainSpecificName: mustGetEventName(consts.EventNameCCIPSendRequested, onrampABI),
ReadType: evmrelaytypes.Event,
EventDefinitions: &evmrelaytypes.EventDefinitions{
GenericDataWordNames: map[string]uint8{
consts.EventAttributeSequenceNumber: 5,
},
},
},
},
},
},
}
}

// HomeChainReaderConfigRaw returns a ChainReaderConfig that can be used to read from the home chain.
func HomeChainReaderConfigRaw() evmrelaytypes.ChainReaderConfig {
return evmrelaytypes.ChainReaderConfig{
Contracts: map[string]evmrelaytypes.ChainContractReader{
consts.ContractNameCapabilitiesRegistry: {
ContractABI: kcr.CapabilitiesRegistryABI,
Configs: map[string]*evmrelaytypes.ChainReaderDefinition{
consts.MethodNameGetCapability: {
ChainSpecificName: mustGetMethodName("getCapability", capabilitiesRegsitryABI),
},
},
},
consts.ContractNameCCIPConfig: {
ContractABI: ccip_config.CCIPConfigABI,
Configs: map[string]*evmrelaytypes.ChainReaderDefinition{
consts.MethodNameGetAllChainConfigs: {
ChainSpecificName: mustGetMethodName("getAllChainConfigs", ccipConfigABI),
},
consts.MethodNameGetOCRConfig: {
ChainSpecificName: mustGetMethodName("getOCRConfig", ccipConfigABI),
},
},
},
},
}
}

func mustGetEventName(event string, tabi abi.ABI) string {
e, ok := tabi.Events[event]
if !ok {
panic(fmt.Sprintf("missing event %s in onrampABI", event))
}
return e.Name
}
Loading

0 comments on commit be120a2

Please sign in to comment.