-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'develop' into BCF-3139-chreader-lp-filter-config
- Loading branch information
Showing
67 changed files
with
2,621 additions
and
856 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"chainlink": minor | ||
--- | ||
|
||
#internal Add RegistrySyncer |
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,5 @@ | ||
--- | ||
"chainlink": patch | ||
--- | ||
|
||
#updated Fix verb formatting in the log outputs. |
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,110 @@ | ||
package capabilities | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/types" | ||
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/keystone_capability_registry" | ||
p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" | ||
evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" | ||
) | ||
|
||
type remoteRegistryReader struct { | ||
r types.ContractReader | ||
} | ||
|
||
var _ reader = (*remoteRegistryReader)(nil) | ||
|
||
type hashedCapabilityID [32]byte | ||
type donID uint32 | ||
|
||
type state struct { | ||
IDsToDONs map[donID]kcr.CapabilityRegistryDONInfo | ||
IDsToNodes map[p2ptypes.PeerID]kcr.CapabilityRegistryNodeInfo | ||
IDsToCapabilities map[hashedCapabilityID]kcr.CapabilityRegistryCapability | ||
} | ||
|
||
func (r *remoteRegistryReader) state(ctx context.Context) (state, error) { | ||
dons := []kcr.CapabilityRegistryDONInfo{} | ||
err := r.r.GetLatestValue(ctx, "capabilityRegistry", "getDONs", nil, &dons) | ||
if err != nil { | ||
return state{}, err | ||
} | ||
|
||
idsToDONs := map[donID]kcr.CapabilityRegistryDONInfo{} | ||
for _, d := range dons { | ||
idsToDONs[donID(d.Id)] = d | ||
} | ||
|
||
caps := kcr.GetCapabilities{} | ||
err = r.r.GetLatestValue(ctx, "capabilityRegistry", "getCapabilities", nil, &caps) | ||
if err != nil { | ||
return state{}, err | ||
} | ||
|
||
idsToCapabilities := map[hashedCapabilityID]kcr.CapabilityRegistryCapability{} | ||
for i, c := range caps.Capabilities { | ||
idsToCapabilities[caps.HashedCapabilityIds[i]] = c | ||
} | ||
|
||
nodes := &kcr.GetNodes{} | ||
err = r.r.GetLatestValue(ctx, "capabilityRegistry", "getNodes", nil, &nodes) | ||
if err != nil { | ||
return state{}, err | ||
} | ||
|
||
idsToNodes := map[p2ptypes.PeerID]kcr.CapabilityRegistryNodeInfo{} | ||
for _, node := range nodes.NodeInfo { | ||
idsToNodes[node.P2pId] = node | ||
} | ||
|
||
return state{IDsToDONs: idsToDONs, IDsToCapabilities: idsToCapabilities, IDsToNodes: idsToNodes}, nil | ||
} | ||
|
||
type contractReaderFactory interface { | ||
NewContractReader(context.Context, []byte) (types.ContractReader, error) | ||
} | ||
|
||
func newRemoteRegistryReader(ctx context.Context, relayer contractReaderFactory, remoteRegistryAddress string) (*remoteRegistryReader, error) { | ||
contractReaderConfig := evmrelaytypes.ChainReaderConfig{ | ||
Contracts: map[string]evmrelaytypes.ChainContractReader{ | ||
"capabilityRegistry": { | ||
ContractABI: kcr.CapabilityRegistryABI, | ||
Configs: map[string]*evmrelaytypes.ChainReaderDefinition{ | ||
"getDONs": { | ||
ChainSpecificName: "getDONs", | ||
}, | ||
"getCapabilities": { | ||
ChainSpecificName: "getCapabilities", | ||
}, | ||
"getNodes": { | ||
ChainSpecificName: "getNodes", | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
contractReaderConfigEncoded, err := json.Marshal(contractReaderConfig) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
cr, err := relayer.NewContractReader(ctx, contractReaderConfigEncoded) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
err = cr.Bind(ctx, []types.BoundContract{ | ||
{ | ||
Address: remoteRegistryAddress, | ||
Name: "capabilityRegistry", | ||
}, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &remoteRegistryReader{r: cr}, err | ||
} |
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,210 @@ | ||
package capabilities | ||
|
||
import ( | ||
"context" | ||
"crypto/rand" | ||
"encoding/json" | ||
"fmt" | ||
"math/big" | ||
"testing" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/core" | ||
"github.com/ethereum/go-ethereum/eth/ethconfig" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/smartcontractkit/chainlink-common/pkg/types" | ||
evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" | ||
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" | ||
kcr "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/keystone/generated/keystone_capability_registry" | ||
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils" | ||
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/pgtest" | ||
"github.com/smartcontractkit/chainlink/v2/core/logger" | ||
p2ptypes "github.com/smartcontractkit/chainlink/v2/core/services/p2p/types" | ||
"github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" | ||
evmrelaytypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" | ||
) | ||
|
||
var writeChainCapability = kcr.CapabilityRegistryCapability{ | ||
LabelledName: "write-chain", | ||
Version: "1.0.1", | ||
ResponseType: uint8(1), | ||
} | ||
|
||
func startNewChainWithRegistry(t *testing.T) (*kcr.CapabilityRegistry, common.Address, *bind.TransactOpts, *backends.SimulatedBackend) { | ||
owner := testutils.MustNewSimTransactor(t) | ||
|
||
oneEth, _ := new(big.Int).SetString("100000000000000000000", 10) | ||
gasLimit := ethconfig.Defaults.Miner.GasCeil * 2 // 60 M blocks | ||
|
||
simulatedBackend := backends.NewSimulatedBackend(core.GenesisAlloc{owner.From: { | ||
Balance: oneEth, | ||
}}, gasLimit) | ||
simulatedBackend.Commit() | ||
|
||
capabilityRegistryAddress, _, capabilityRegistry, err := kcr.DeployCapabilityRegistry(owner, simulatedBackend) | ||
require.NoError(t, err, "DeployCapabilityRegistry failed") | ||
|
||
fmt.Println("Deployed CapabilityRegistry at", capabilityRegistryAddress.Hex()) | ||
simulatedBackend.Commit() | ||
|
||
return capabilityRegistry, capabilityRegistryAddress, owner, simulatedBackend | ||
} | ||
|
||
type crFactory struct { | ||
lggr logger.Logger | ||
logPoller logpoller.LogPoller | ||
client evmclient.Client | ||
} | ||
|
||
func (c *crFactory) NewContractReader(ctx context.Context, cfg []byte) (types.ContractReader, error) { | ||
crCfg := &evmrelaytypes.ChainReaderConfig{} | ||
if err := json.Unmarshal(cfg, crCfg); err != nil { | ||
return nil, err | ||
} | ||
svc, err := evm.NewChainReaderService(ctx, c.lggr, c.logPoller, c.client, *crCfg) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return svc, svc.Start(ctx) | ||
} | ||
|
||
func newContractReaderFactory(t *testing.T, simulatedBackend *backends.SimulatedBackend) *crFactory { | ||
lggr := logger.TestLogger(t) | ||
client := evmclient.NewSimulatedBackendClient( | ||
t, | ||
simulatedBackend, | ||
testutils.SimulatedChainID, | ||
) | ||
db := pgtest.NewSqlxDB(t) | ||
lp := logpoller.NewLogPoller( | ||
logpoller.NewORM(testutils.SimulatedChainID, db, lggr), | ||
client, | ||
lggr, | ||
logpoller.Opts{ | ||
PollPeriod: 100 * time.Millisecond, | ||
FinalityDepth: 2, | ||
BackfillBatchSize: 3, | ||
RpcBatchSize: 2, | ||
KeepFinalizedBlocksDepth: 1000, | ||
}, | ||
) | ||
return &crFactory{ | ||
lggr: lggr, | ||
client: client, | ||
logPoller: lp, | ||
} | ||
} | ||
|
||
func randomWord() [32]byte { | ||
word := make([]byte, 32) | ||
_, err := rand.Read(word) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return [32]byte(word) | ||
} | ||
|
||
func TestReader_Integration(t *testing.T) { | ||
ctx := testutils.Context(t) | ||
reg, regAddress, owner, sim := startNewChainWithRegistry(t) | ||
|
||
_, err := reg.AddCapabilities(owner, []kcr.CapabilityRegistryCapability{writeChainCapability}) | ||
require.NoError(t, err, "AddCapability failed for %s", writeChainCapability.LabelledName) | ||
sim.Commit() | ||
|
||
cid, err := reg.GetHashedCapabilityId(&bind.CallOpts{}, writeChainCapability.LabelledName, writeChainCapability.Version) | ||
require.NoError(t, err) | ||
|
||
_, err = reg.AddNodeOperators(owner, []kcr.CapabilityRegistryNodeOperator{ | ||
{ | ||
Admin: owner.From, | ||
Name: "TEST_NOP", | ||
}, | ||
}) | ||
require.NoError(t, err) | ||
|
||
nodeSet := [][32]byte{ | ||
randomWord(), | ||
randomWord(), | ||
randomWord(), | ||
} | ||
|
||
nodes := []kcr.CapabilityRegistryNodeInfo{ | ||
{ | ||
// The first NodeOperatorId has id 1 since the id is auto-incrementing. | ||
NodeOperatorId: uint32(1), | ||
Signer: randomWord(), | ||
P2pId: nodeSet[0], | ||
HashedCapabilityIds: [][32]byte{cid}, | ||
}, | ||
{ | ||
// The first NodeOperatorId has id 1 since the id is auto-incrementing. | ||
NodeOperatorId: uint32(1), | ||
Signer: randomWord(), | ||
P2pId: nodeSet[1], | ||
HashedCapabilityIds: [][32]byte{cid}, | ||
}, | ||
{ | ||
// The first NodeOperatorId has id 1 since the id is auto-incrementing. | ||
NodeOperatorId: uint32(1), | ||
Signer: randomWord(), | ||
P2pId: nodeSet[2], | ||
HashedCapabilityIds: [][32]byte{cid}, | ||
}, | ||
} | ||
_, err = reg.AddNodes(owner, nodes) | ||
require.NoError(t, err) | ||
|
||
cfgs := []kcr.CapabilityRegistryCapabilityConfiguration{ | ||
{ | ||
CapabilityId: cid, | ||
Config: []byte(`{"hello": "world"}`), | ||
}, | ||
} | ||
_, err = reg.AddDON( | ||
owner, | ||
nodeSet, | ||
cfgs, | ||
true, | ||
true, | ||
1, | ||
) | ||
sim.Commit() | ||
|
||
require.NoError(t, err) | ||
|
||
factory := newContractReaderFactory(t, sim) | ||
reader, err := newRemoteRegistryReader(ctx, factory, regAddress.Hex()) | ||
require.NoError(t, err) | ||
|
||
s, err := reader.state(ctx) | ||
require.NoError(t, err) | ||
assert.Len(t, s.IDsToCapabilities, 1) | ||
|
||
gotCap := s.IDsToCapabilities[cid] | ||
assert.Equal(t, writeChainCapability, gotCap) | ||
|
||
assert.Len(t, s.IDsToDONs, 1) | ||
assert.Equal(t, kcr.CapabilityRegistryDONInfo{ | ||
Id: 1, // initial Id | ||
ConfigCount: 1, // initial Count | ||
IsPublic: true, | ||
AcceptsWorkflows: true, | ||
F: 1, | ||
NodeP2PIds: nodeSet, | ||
CapabilityConfigurations: cfgs, | ||
}, s.IDsToDONs[1]) | ||
|
||
assert.Len(t, s.IDsToNodes, 3) | ||
assert.Equal(t, map[p2ptypes.PeerID]kcr.CapabilityRegistryNodeInfo{ | ||
nodeSet[0]: nodes[0], | ||
nodeSet[1]: nodes[1], | ||
nodeSet[2]: nodes[2], | ||
}, s.IDsToNodes) | ||
} |
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 |
---|---|---|
|
@@ -2,6 +2,7 @@ package capabilities_test | |
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"testing" | ||
|
||
|
@@ -85,7 +86,7 @@ func TestRegistry_NoDuplicateIDs(t *testing.T) { | |
c2 := &mockCapability{CapabilityInfo: ci} | ||
|
||
err = r.Add(ctx, c2) | ||
assert.ErrorContains(t, err, "capability with id: [email protected] already exists") | ||
assert.True(t, errors.Is(err, coreCapabilities.ErrCapabilityAlreadyExists)) | ||
} | ||
|
||
func TestRegistry_ChecksExecutionAPIByType(t *testing.T) { | ||
|
Oops, something went wrong.