Skip to content

Commit

Permalink
resurrect update don changeset
Browse files Browse the repository at this point in the history
  • Loading branch information
krehermann committed Nov 5, 2024
1 parent cdf067f commit 913427d
Show file tree
Hide file tree
Showing 7 changed files with 485 additions and 3 deletions.
32 changes: 32 additions & 0 deletions deployment/environment/clo/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package clo

import (
jd "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/node"
"github.com/smartcontractkit/chainlink/deployment/environment/clo/models"
)

// NewChainConfig creates a new JobDistributor ChainConfig from a clo model NodeChainConfig
func NewChainConfig(chain *models.NodeChainConfig) *jd.ChainConfig {
return &jd.ChainConfig{
Chain: &jd.Chain{
Id: chain.Network.ChainID,
Type: jd.ChainType_CHAIN_TYPE_EVM, // TODO: support other chain types
},

AccountAddress: chain.AccountAddress,
AdminAddress: chain.AdminAddress,
Ocr2Config: &jd.OCR2Config{
Enabled: chain.Ocr2Config.Enabled,
P2PKeyBundle: &jd.OCR2Config_P2PKeyBundle{
PeerId: chain.Ocr2Config.P2pKeyBundle.PeerID,
PublicKey: chain.Ocr2Config.P2pKeyBundle.PublicKey,
},
OcrKeyBundle: &jd.OCR2Config_OCRKeyBundle{
BundleId: chain.Ocr2Config.OcrKeyBundle.BundleID,
OnchainSigningAddress: chain.Ocr2Config.OcrKeyBundle.OnchainSigningAddress,
OffchainPublicKey: chain.Ocr2Config.OcrKeyBundle.OffchainPublicKey,
ConfigPublicKey: chain.Ocr2Config.OcrKeyBundle.ConfigPublicKey,
},
},
}
}
55 changes: 54 additions & 1 deletion deployment/keystone/changeset/internal/test/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import (

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

capabilitiespb "github.com/smartcontractkit/chainlink-common/pkg/capabilities/pb"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink-common/pkg/values"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/deployment/environment/memory"

Expand All @@ -19,10 +22,15 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
)

type Don struct {
Name string
P2PIDs []p2pkey.PeerID
CapabilityConfigs []internal.CapabilityConfig
}
type SetupTestRegistryRequest struct {
P2pToCapabilities map[p2pkey.PeerID][]kcr.CapabilitiesRegistryCapability
NopToNodes map[kcr.CapabilitiesRegistryNodeOperator][]*internal.P2PSignerEnc
DonToNodes map[string][]*internal.P2PSignerEnc
Dons []Don
}

type SetupTestRegistryResponse struct {
Expand Down Expand Up @@ -74,6 +82,8 @@ func SetupTestRegistry(t *testing.T, lggr logger.Logger, req *SetupTestRegistryR
nodeParams, err := phonyRequest.NodeParams()
require.NoError(t, err)
addNodes(t, lggr, chain, registry, nodeParams)
addDons(t, lggr, chain, registry, capCache, req.Dons)

return &SetupTestRegistryResponse{
Registry: registry,
Chain: chain,
Expand Down Expand Up @@ -108,6 +118,45 @@ func addNodes(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry
require.NoError(t, err)
}

func addDons(t *testing.T, lggr logger.Logger, chain deployment.Chain, registry *kcr.CapabilitiesRegistry, cc *CapabilityCache, dons []Don) {
for _, don := range dons {
acceptsWorkflows := false
// lookup the capabilities
var capConfigs []kcr.CapabilitiesRegistryCapabilityConfiguration
for _, ccfg := range don.CapabilityConfigs {
if ccfg.Config == nil {
ccfg.Config = defaultCapConfig(t, ccfg.Capability)
}
var exists bool
ccfg.CapabilityId, exists = cc.Get(ccfg.Capability)
require.True(t, exists, "capability not found in cache %v", ccfg.Capability)
capConfigs = append(capConfigs, ccfg.CapabilitiesRegistryCapabilityConfiguration)
if ccfg.Capability.CapabilityType == 2 { // ocr3 capabilities
acceptsWorkflows = true
}
}
// add the don
isPublic := true
f := len(don.P2PIDs)/3 + 1
tx, err := registry.AddDON(chain.DeployerKey, internal.PeerIDsToBytes(don.P2PIDs), capConfigs, isPublic, acceptsWorkflows, uint8(f))
if err != nil {
err2 := kslib.DecodeErr(kcr.CapabilitiesRegistryABI, err)
require.Fail(t, fmt.Sprintf("failed to call AddDON: %s: %s", err, err2))
}
_, err = chain.Confirm(tx)
require.NoError(t, err)
}
}

func defaultCapConfig(t *testing.T, cap kcr.CapabilitiesRegistryCapability) []byte {
empty := &capabilitiespb.CapabilityConfig{
DefaultConfig: values.Proto(values.EmptyMap()).GetMapValue(),
}
emptyb, err := proto.Marshal(empty)
require.NoError(t, err)
return emptyb
}

// CapabilityCache tracks registered capabilities by name
type CapabilityCache struct {
t *testing.T
Expand All @@ -120,6 +169,10 @@ func NewCapabiltyCache(t *testing.T) *CapabilityCache {
nameToId: make(map[string][32]byte),
}
}
func (cc *CapabilityCache) Get(cap kcr.CapabilitiesRegistryCapability) ([32]byte, bool) {
id, exists := cc.nameToId[kslib.CapabilityID(cap)]
return id, exists
}

// AddCapabilities adds the capabilities to the registry and returns the registered capabilities
// if the capability is already registered, it will not be re-registered
Expand Down
146 changes: 146 additions & 0 deletions deployment/keystone/changeset/internal/update_don.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package internal

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/smartcontractkit/chainlink-common/pkg/logger"
"github.com/smartcontractkit/chainlink/deployment"
"github.com/smartcontractkit/chainlink/v2/core/services/keystore/keys/p2pkey"
"google.golang.org/protobuf/proto"

kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/capabilities_registry"

kslib "github.com/smartcontractkit/chainlink/deployment/keystone"
)

// CapabilityConfig is a struct that holds a capability and its configuration
type CapabilityConfig struct {
Capability kcr.CapabilitiesRegistryCapability
kcr.CapabilitiesRegistryCapabilityConfiguration // embed bc otherwise with ~ config.config
}

type UpdateDonRequest struct {
Registry *kcr.CapabilitiesRegistry
Chain deployment.Chain

Name string
P2PIDs []p2pkey.PeerID // this is the unique identifier for the done
// CapabilityId field is ignored. it is determined dynamically by the registry
// If the Config is nil, a default config is used
CapabilityConfigs []CapabilityConfig
}

func (r *UpdateDonRequest) Validate() error {
if r.Registry == nil {
return fmt.Errorf("registry is required")
}
if r.Name == "" {
return fmt.Errorf("name is required")
}
if len(r.P2PIDs) == 0 {
return fmt.Errorf("p2pIDs is required")
}
return nil
}

type UpdateDonResponse struct {
DonInfo kcr.CapabilitiesRegistryDONInfo
}

func UpdateDon(lggr logger.Logger, req *UpdateDonRequest) (*UpdateDonResponse, error) {
if err := req.Validate(); err != nil {
return nil, fmt.Errorf("failed to validate request: %w", err)
}

getDonsResp, err := req.Registry.GetDONs(&bind.CallOpts{})
if err != nil {
return nil, fmt.Errorf("failed to get Dons: %w", err)
}
wantedDonID := SortedHash(PeerIDsToBytes(req.P2PIDs))
var don kcr.CapabilitiesRegistryDONInfo
found := false
for i, di := range getDonsResp {
gotID := SortedHash(di.NodeP2PIds)
if gotID == wantedDonID {
don = getDonsResp[i]
found = true
break
}
}
if !found {
type debugDonInfo struct {
OnchainID uint32
P2PIDsHash string
}
debugIds := make([]debugDonInfo, len(getDonsResp))
for i, di := range getDonsResp {
debugIds[i] = debugDonInfo{
OnchainID: di.Id,
P2PIDsHash: SortedHash(di.NodeP2PIds),
}
}
return nil, fmt.Errorf("don not found by p2pIDs %s in %v", wantedDonID, debugIds)
}
cfgs, err := computeConfigs(req.Registry, req.CapabilityConfigs, don)
if err != nil {
return nil, fmt.Errorf("failed to compute configs: %w", err)
}
tx, err := req.Registry.UpdateDON(req.Chain.DeployerKey, don.Id, don.NodeP2PIds, cfgs, don.IsPublic, don.F)
if err != nil {
err = kslib.DecodeErr(kcr.CapabilitiesRegistryABI, err)
return nil, fmt.Errorf("failed to call UpdateDON: %w", err)
}

_, err = req.Chain.Confirm(tx)
if err != nil {
return nil, fmt.Errorf("failed to confirm UpdateDON transaction %s: %w", tx.Hash().String(), err)
}
out := don
out.CapabilityConfigurations = cfgs
return &UpdateDonResponse{DonInfo: out}, nil
}

func PeerIDsToBytes(p2pIDs []p2pkey.PeerID) [][32]byte {
out := make([][32]byte, len(p2pIDs))
for i, p2pID := range p2pIDs {
out[i] = p2pID
}
return out
}

func computeConfigs(registry *kcr.CapabilitiesRegistry, caps []CapabilityConfig, donInfo kcr.CapabilitiesRegistryDONInfo) ([]kcr.CapabilitiesRegistryCapabilityConfiguration, error) {
out := make([]kcr.CapabilitiesRegistryCapabilityConfiguration, len(caps))
for i, cap := range caps {
out[i] = cap.CapabilitiesRegistryCapabilityConfiguration
id, err := registry.GetHashedCapabilityId(&bind.CallOpts{}, cap.Capability.LabelledName, cap.Capability.Version)
if err != nil {
return nil, fmt.Errorf("failed to get capability id: %w", err)
}
out[i].CapabilityId = id
if out[i].Config == nil {
c := kslib.DefaultCapConfig(cap.Capability.CapabilityType, int(donInfo.F))
cb, err := proto.Marshal(c)
if err != nil {
return nil, fmt.Errorf("failed to marshal capability config for %v: %w", c, err)
}
out[i].Config = cb
}
}
return out, nil
}

func SortedHash(p2pids [][32]byte) string {
sha256Hash := sha256.New()
sort.Slice(p2pids, func(i, j int) bool {
return bytes.Compare(p2pids[i][:], p2pids[j][:]) < 0
})
for _, id := range p2pids {
sha256Hash.Write(id[:])
}
return hex.EncodeToString(sha256Hash.Sum(nil))
}
Loading

0 comments on commit 913427d

Please sign in to comment.