Skip to content

Commit

Permalink
[KS-54] OCR3 contract + config tooling (#12078)
Browse files Browse the repository at this point in the history
* [KS-54] OCR3 contract + config tooling

* Update CODEOWNERS

Co-authored-by: Blaž Hrastnik <[email protected]>

---------

Co-authored-by: Blaž Hrastnik <[email protected]>
  • Loading branch information
bolekk and archseer authored Feb 19, 2024
1 parent 66a4a6b commit 3fd52ff
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ core/scripts/gateway @smartcontractkit/functions
/contracts/**/*llo-feeds* @smartcontrackit/mercury-team
/contracts/**/*vrf* @smartcontractkit/vrf-team
/contracts/**/*l2ep* @smartcontractkit/integrations
# TODO: replace with a team tag when ready
/contracts/**/*keystone* @archseer @bolekk @patrick-dowell

/contracts/src/v0.8/automation @smartcontractkit/keepers
/contracts/src/v0.8/functions @smartcontractkit/functions
Expand Down
37 changes: 37 additions & 0 deletions contracts/src/v0.8/keystone/OCR3Capability.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.6;

import {OCR2Base} from "../shared/ocr2/OCR2Base.sol";

// OCR2Base provides config management compatible with OCR3
contract OCR3Capability is OCR2Base {
error ReportingUnsupported();

constructor() OCR2Base(true) {}

function typeAndVersion() external pure override returns (string memory) {
return "Keystone 0.0.0";
}

function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal override {}

function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal override {}

function _validateReport(
bytes32 /* configDigest */,
uint40 /* epochAndRound */,
bytes memory /* report */
) internal pure override returns (bool) {
return true;
}

function _report(
uint256 /* initialGas */,
address /* transmitter */,
uint8 /* signerCount */,
address[MAX_NUM_ORACLES] memory /* signers */,
bytes calldata /* report */
) internal virtual override {
revert ReportingUnsupported();
}
}
27 changes: 27 additions & 0 deletions core/scripts/keystone/config_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"OracleConfig": {
"MaxQueryLengthBytes": 1000000,
"MaxObservationLengthBytes": 1000000,
"MaxReportLengthBytes": 1000000,
"MaxRequestBatchSize": 1000,
"UniqueReports": true,

"DeltaProgressMillis": 5000,
"DeltaResendMillis": 5000,
"DeltaInitialMillis": 5000,
"DeltaRoundMillis": 2000,
"DeltaGraceMillis": 500,
"DeltaCertifiedCommitRequestMillis": 1000,
"DeltaStageMillis": 30000,
"MaxRoundsPerEpoch": 10,
"TransmissionSchedule": [1, 1, 1, 1],

"MaxDurationQueryMillis": 1000,
"MaxDurationObservationMillis": 1000,
"MaxDurationReportMillis": 1000,
"MaxDurationAcceptMillis": 1000,
"MaxDurationTransmitMillis": 1000,

"MaxFaultyOracles": 1
}
}
207 changes: 207 additions & 0 deletions core/scripts/keystone/gen_ocr3_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package main

import (
"crypto/ed25519"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"time"

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

"github.com/smartcontractkit/libocr/offchainreporting2plus/confighelper"
"github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3confighelper"
"github.com/smartcontractkit/libocr/offchainreporting2plus/types"

helpers "github.com/smartcontractkit/chainlink/core/scripts/common"
)

type TopLevelConfigSource struct {
OracleConfig OracleConfigSource
}

type OracleConfigSource struct {
MaxQueryLengthBytes uint32
MaxObservationLengthBytes uint32
MaxReportLengthBytes uint32
MaxRequestBatchSize uint32
UniqueReports bool

DeltaProgressMillis uint32
DeltaResendMillis uint32
DeltaInitialMillis uint32
DeltaRoundMillis uint32
DeltaGraceMillis uint32
DeltaCertifiedCommitRequestMillis uint32
DeltaStageMillis uint32
MaxRoundsPerEpoch uint64
TransmissionSchedule []int

MaxDurationQueryMillis uint32
MaxDurationObservationMillis uint32
MaxDurationAcceptMillis uint32
MaxDurationTransmitMillis uint32

MaxFaultyOracles int
}

type NodeKeys struct {
EthAddress string
P2PPeerID string // p2p_<key>
OCR2BundleID string // used only in job spec
OCR2OnchainPublicKey string // ocr2on_evm_<key>
OCR2OffchainPublicKey string // ocr2off_evm_<key>
OCR2ConfigPublicKey string // ocr2cfg_evm_<key>
CSAPublicKey string
}

type orc2drOracleConfig struct {
Signers []string `json:"signers"`
Transmitters []string `json:"transmitters"`
F uint8 `json:"f"`
OnchainConfig string `json:"onchainConfig"`
OffchainConfigVersion uint64 `json:"offchainConfigVersion"`
OffchainConfig string `json:"offchainConfig"`
}

type generateOCR2Config struct {
}

func NewGenerateOCR2ConfigCommand() *generateOCR2Config {
return &generateOCR2Config{}
}

func (g *generateOCR2Config) Name() string {
return "generate-ocr2config"
}

func mustParseJSONConfigFile(fileName string) (output TopLevelConfigSource) {
return mustParseJSON[TopLevelConfigSource](fileName)
}

func mustParseKeysFile(fileName string) (output []NodeKeys) {
return mustParseJSON[[]NodeKeys](fileName)
}

func mustParseJSON[T any](fileName string) (output T) {
jsonFile, err := os.Open(fileName)
if err != nil {
panic(err)
}
defer jsonFile.Close()
bytes, err := io.ReadAll(jsonFile)
if err != nil {
panic(err)
}
err = json.Unmarshal(bytes, &output)
if err != nil {
panic(err)
}
return
}

func main() {
fs := flag.NewFlagSet("config_gen", flag.ExitOnError)
configFile := fs.String("config", "config_example.json", "a file containing JSON config")
keysFile := fs.String("keys", "public_keys_example.json", "a file containing node public keys")
if err := fs.Parse(os.Args[1:]); err != nil || *keysFile == "" || *configFile == "" {
fs.Usage()
os.Exit(1)
}

topLevelCfg := mustParseJSONConfigFile(*configFile)
cfg := topLevelCfg.OracleConfig
nca := mustParseKeysFile(*keysFile)

onchainPubKeys := []common.Address{}
for _, n := range nca {
onchainPubKeys = append(onchainPubKeys, common.HexToAddress(n.OCR2OnchainPublicKey))
}

offchainPubKeysBytes := []types.OffchainPublicKey{}
for _, n := range nca {
pkBytes, err := hex.DecodeString(n.OCR2OffchainPublicKey)
if err != nil {
panic(err)
}

pkBytesFixed := [ed25519.PublicKeySize]byte{}
nCopied := copy(pkBytesFixed[:], pkBytes)
if nCopied != ed25519.PublicKeySize {
panic("wrong num elements copied from ocr2 offchain public key")
}

offchainPubKeysBytes = append(offchainPubKeysBytes, types.OffchainPublicKey(pkBytesFixed))
}

configPubKeysBytes := []types.ConfigEncryptionPublicKey{}
for _, n := range nca {
pkBytes, err := hex.DecodeString(n.OCR2ConfigPublicKey)
helpers.PanicErr(err)

pkBytesFixed := [ed25519.PublicKeySize]byte{}
n := copy(pkBytesFixed[:], pkBytes)
if n != ed25519.PublicKeySize {
panic("wrong num elements copied")
}

configPubKeysBytes = append(configPubKeysBytes, types.ConfigEncryptionPublicKey(pkBytesFixed))
}

identities := []confighelper.OracleIdentityExtra{}
for index := range nca {
identities = append(identities, confighelper.OracleIdentityExtra{
OracleIdentity: confighelper.OracleIdentity{
OnchainPublicKey: onchainPubKeys[index][:],
OffchainPublicKey: offchainPubKeysBytes[index],
PeerID: nca[index].P2PPeerID,
TransmitAccount: types.Account(nca[index].EthAddress),
},
ConfigEncryptionPublicKey: configPubKeysBytes[index],
})
}

signers, transmitters, f, onchainConfig, offchainConfigVersion, offchainConfig, err := ocr3confighelper.ContractSetConfigArgsForTests(
time.Duration(cfg.DeltaProgressMillis)*time.Millisecond,
time.Duration(cfg.DeltaResendMillis)*time.Millisecond,
time.Duration(cfg.DeltaInitialMillis)*time.Millisecond,
time.Duration(cfg.DeltaRoundMillis)*time.Millisecond,
time.Duration(cfg.DeltaGraceMillis)*time.Millisecond,
time.Duration(cfg.DeltaCertifiedCommitRequestMillis)*time.Millisecond,
time.Duration(cfg.DeltaStageMillis)*time.Millisecond,
cfg.MaxRoundsPerEpoch,
cfg.TransmissionSchedule,
identities,
nil, // empty plugin config
time.Duration(cfg.MaxDurationQueryMillis)*time.Millisecond,
time.Duration(cfg.MaxDurationObservationMillis)*time.Millisecond,
time.Duration(cfg.MaxDurationAcceptMillis)*time.Millisecond,
time.Duration(cfg.MaxDurationTransmitMillis)*time.Millisecond,
cfg.MaxFaultyOracles,
nil, // empty onChain config
)
helpers.PanicErr(err)

var signersStr []string
var transmittersStr []string
for i := range transmitters {
signersStr = append(signersStr, "0x"+hex.EncodeToString(signers[i]))
transmittersStr = append(transmittersStr, string(transmitters[i]))
}

config := orc2drOracleConfig{
Signers: signersStr,
Transmitters: transmittersStr,
F: f,
OnchainConfig: "0x" + hex.EncodeToString(onchainConfig),
OffchainConfigVersion: offchainConfigVersion,
OffchainConfig: "0x" + hex.EncodeToString(offchainConfig),
}

js, err := json.MarshalIndent(config, "", " ")
helpers.PanicErr(err)
fmt.Println("Config:", string(js))
}
38 changes: 38 additions & 0 deletions core/scripts/keystone/public_keys_example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"EthAddress": "0x9639dCc7D0ca4468B5f684ef89F12F0B365c9F6d",
"P2PPeerID": "12D3KooWBCF1XT5Wi8FzfgNCqRL76Swv8TRU3TiD4QiJm8NMNX7N",
"OCR2BundleID": "29e8dd8c916eac1fc6df8ac6f449481f5db1cf3714e7cb629e16d7b499af5603",
"OCR2OnchainPublicKey": "049b55674e2485b9a6788c0796b74f2c33667670",
"OCR2OffchainPublicKey": "f4f13ca3053e0f44f869648857f0c0ebc20045f9b51d6a5712c867f5920def01",
"OCR2ConfigPublicKey": "17a60b694f7cfb0a02da3da5e79f87b071e518d1f4a27aeb181776969b766e1e",
"CSAPublicKey": "csa_2245bbbee39f75f22e12c345a0ac76bad1f3214b7020a47324a1634dee285761"
},
{
"EthAddress": "0x8f0fAE64f5f75067833ed5deDC2804B62b21383d",
"P2PPeerID": "12D3KooWG1AyvwmCpZ93J8pBQUE1SuzrjDXnT4BeouncHR3jWLCG",
"OCR2BundleID": "9b19ff8196aec2cdf70f6ac2c3617ea9b03dfb426e0938aee0af2b37459f86eb",
"OCR2OnchainPublicKey": "8405c2b28fcab284f08ed0028f1f511cd57a16b7",
"OCR2OffchainPublicKey": "29e41861f005a0b1bc3156ecd40992a5b552b3182f73f4adec43b4e3052ac2c7",
"OCR2ConfigPublicKey": "0a0c21b14924ee28afad15c96b3a5be372870cdf9b13f002b63fb4ba8942c461",
"CSAPublicKey": "csa_d329bfff7b7835aec205a6ac09bb217e46c754c3ab6730d09b709c7bc395dc3f"
},
{
"EthAddress": "0xf09A863D920840c13277e76F43CFBdfB22b8FB7C",
"P2PPeerID": "12D3KooWGeUKZBRMbx27FUTgBwZa9Ap9Ym92mywwpuqkEtz8XWyv",
"OCR2BundleID": "200e5d187b4fc8e2d263c13cd28d37c914f275aba1aee91199d5c7766ada9d63",
"OCR2OnchainPublicKey": "4a5199a4e65acfd07b690493ec02bdba3c44c1ec",
"OCR2OffchainPublicKey": "f535037b9ca113d61eb0548e62c28d5abc201bfe36a803ea5f1e94f66da64c9e",
"OCR2ConfigPublicKey": "d62623257ebf3e805ee77162a1b1df0d1b3f57c7dc1dc28b88acb6c9b4f5455f",
"CSAPublicKey": "csa_35ff548c85ada4dbd09b5f02ea3421217d15ebec00a63cab7e529f8bb90542a1"
},
{
"EthAddress": "0x7eD90b519bC3054a575C464dBf39946b53Ff90EF",
"P2PPeerID": "12D3KooW9zYWQv3STmDeNDidyzxsJSTxoCTLicafgfeEz9nhwhC4",
"OCR2BundleID": "25b40149256109e6036f27b11969c61ba9e765fc60a13324410588c4801f0466",
"OCR2OnchainPublicKey": "07183b1fe36c8f3885a3ce4131b9151a2c7e86b4",
"OCR2OffchainPublicKey": "c64252067d48efe341745cff9aad2f20990158dfe31548322a0bf2921e9428b8",
"OCR2ConfigPublicKey": "893f61aa755f356a248d66b58974742b22518db962ab036b440a33c3eb86ea5b",
"CSAPublicKey": "csa_541f97026a2be5e460276ba4eddebce6e74ccb1d1cfb1550fe54ef615c0534c9"
}
]

0 comments on commit 3fd52ff

Please sign in to comment.