-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core/services/ccipcapability: oracle creator impls (#1180)
## 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
1 parent
f420af8
commit be120a2
Showing
45 changed files
with
4,272 additions
and
257 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1,096 changes: 1,096 additions & 0 deletions
1,096
core/gethwrappers/ccip/generated/multi_ocr3_helper/multi_ocr3_helper.go
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
190
core/services/ccipcapability/configs/evm/contract_reader.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.