Skip to content

Commit 4e4f115

Browse files
committed
cre: tighten aptos local setup and key wiring
1 parent cc0c24e commit 4e4f115

21 files changed

Lines changed: 350 additions & 445 deletions

File tree

core/scripts/cre/environment/configs/workflow-gateway-don-aptos.toml

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@
1616
# change to your version
1717
image = "job-distributor:0.22.1"
1818

19-
[fake]
20-
port = 8171
21-
22-
[fake_http]
23-
port = 8666
24-
2519
#[s3provider]
2620
# # use all defaults
2721
# port = 9000
@@ -39,8 +33,8 @@
3933
http_port_range_start = 10100
4034

4135
supported_evm_chains = [1337]
42-
env_vars = { CL_EVM_CMD = "", HOME = "/tmp", CL_CRE_SETTINGS_DEFAULT = '{"PerWorkflow":{"CapabilityCallTimeout":"5m0s","ChainAllowed":{"Default":"false","Values":{"1337":"true","4457093679053095497":"true"}},"ChainWrite":{"EVM":{"GasLimit":{"Default":"5000000","Values":{"1337":"10000000"}}}}}}' }
43-
capabilities = ["ocr3", "custom-compute", "web-api-trigger", "cron", "http-action", "http-trigger", "consensus", "don-time", "write-evm-1337", "read-contract-1337", "read-contract-4", "write-aptos-4", "evm-1337"]
36+
env_vars = { CL_CRE_SETTINGS_DEFAULT = '{"PerWorkflow":{"CapabilityCallTimeout":"5m0s","ChainAllowed":{"Default":"false","Values":{"1337":"true","4457093679053095497":"true"}},"ChainWrite":{"EVM":{"GasLimit":{"Default":"5000000","Values":{"1337":"10000000"}}}}}}' }
37+
capabilities = ["cron", "consensus", "read-contract-4", "write-aptos-4"]
4438
registry_based_launch_allowlist = ["cron-trigger@1.0.0"]
4539

4640
[nodesets.db]
@@ -63,7 +57,6 @@
6357
override_mode = "each"
6458
http_port_range_start = 10300
6559

66-
env_vars = { CL_EVM_CMD = "", HOME = "/tmp" }
6760
supported_evm_chains = [1337]
6861

6962
[nodesets.db]

core/scripts/cre/environment/environment/environment.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,16 @@ func startCmd() *cobra.Command {
336336
}
337337

338338
features := feature_set.New()
339+
extraAllowedPorts := append([]int(nil), extraAllowedGatewayPorts...)
340+
if in.Fake != nil {
341+
extraAllowedPorts = append(extraAllowedPorts, in.Fake.Port)
342+
}
343+
if in.FakeHTTP != nil {
344+
extraAllowedPorts = append(extraAllowedPorts, in.FakeHTTP.Port)
345+
}
346+
339347
gatewayWhitelistConfig := gateway.WhitelistConfig{
340-
ExtraAllowedPorts: append(extraAllowedGatewayPorts, in.Fake.Port, in.FakeHTTP.Port),
348+
ExtraAllowedPorts: extraAllowedPorts,
341349
ExtraAllowedIPsCIDR: []string{"0.0.0.0/0"},
342350
}
343351
output, startErr := StartCLIEnvironment(cmdContext, relativePathToRepoRoot, in, nil, features, nil, envDependencies, gatewayWhitelistConfig)

deployment/cre/jobs/propose_job_spec.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func (u ProposeJobSpec) Apply(e cldf.Environment, input ProposeJobSpecInput) (cl
9191
switch input.Template {
9292
// This will hold all standard capabilities jobs as we add support for them.
9393
case job_types.EVM, job_types.Cron, job_types.HTTPTrigger, job_types.HTTPAction, job_types.ConfidentialHTTP, job_types.Consensus, job_types.WebAPITrigger, job_types.WebAPITarget, job_types.CustomCompute, job_types.LogEventTrigger, job_types.ReadContract, job_types.Solana:
94-
job, err := input.Inputs.ToStandardCapabilityJob(input.JobName, false)
94+
job, err := input.Inputs.ToStandardCapabilityJob(input.JobName)
9595
if err != nil {
9696
return cldf.ChangesetOutput{}, fmt.Errorf("failed to convert inputs to standard capability job: %w", err)
9797
}

deployment/cre/jobs/types/job_spec.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ func (j JobSpecInput) UnmarshalFrom(source any) error {
3030
return yaml.Unmarshal(bytes, &j)
3131
}
3232

33-
func (j JobSpecInput) ToStandardCapabilityJob(jobName string, generateOracleFactory bool) (pkg.StandardCapabilityJob, error) {
33+
func (j JobSpecInput) ToStandardCapabilityJob(jobName string) (pkg.StandardCapabilityJob, error) {
3434
out := pkg.StandardCapabilityJob{
35-
JobName: jobName,
36-
GenerateOracleFactory: generateOracleFactory,
35+
JobName: jobName,
3736
}
3837
err := j.UnmarshalTo(&out)
3938
if err != nil {

deployment/cre/jobs/types/job_spec_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
3434
},
3535
}
3636

37-
job, err := input.ToStandardCapabilityJob(jobName, false)
37+
job, err := input.ToStandardCapabilityJob(jobName)
3838
require.NoError(t, err)
3939
assert.Equal(t, jobName, job.JobName)
4040
assert.Equal(t, "run", job.Command)
@@ -56,7 +56,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
5656
"externalJobID": "123",
5757
"oracleFactory": pkg.OracleFactory{},
5858
}
59-
_, err := input.ToStandardCapabilityJob(jobName, false)
59+
_, err := input.ToStandardCapabilityJob(jobName)
6060
require.Error(t, err)
6161
assert.Contains(t, err.Error(), "command is required")
6262
})
@@ -68,7 +68,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
6868
"externalJobID": "123",
6969
"oracleFactory": pkg.OracleFactory{},
7070
}
71-
_, err := input.ToStandardCapabilityJob(jobName, false)
71+
_, err := input.ToStandardCapabilityJob(jobName)
7272
require.Error(t, err)
7373
assert.Contains(t, err.Error(), "command is required and must be a string")
7474
})
@@ -80,7 +80,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
8080
"externalJobID": "123",
8181
"oracleFactory": pkg.OracleFactory{},
8282
}
83-
_, err := input.ToStandardCapabilityJob(jobName, false)
83+
_, err := input.ToStandardCapabilityJob(jobName)
8484
require.NoError(t, err)
8585
})
8686

@@ -91,7 +91,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
9191
"externalJobID": "123",
9292
"oracleFactory": pkg.OracleFactory{},
9393
}
94-
_, err := input.ToStandardCapabilityJob(jobName, false)
94+
_, err := input.ToStandardCapabilityJob(jobName)
9595
require.Error(t, err)
9696
assert.Contains(t, err.Error(), "cannot unmarshal !!map into string")
9797
})
@@ -103,7 +103,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
103103
"externalJobID": struct{}{},
104104
"oracleFactory": pkg.OracleFactory{},
105105
}
106-
_, err := input.ToStandardCapabilityJob(jobName, false)
106+
_, err := input.ToStandardCapabilityJob(jobName)
107107
require.Error(t, err)
108108
assert.Contains(t, err.Error(), "cannot unmarshal !!map into string")
109109
})
@@ -115,7 +115,7 @@ func TestJobSpecInput_ToStandardCapabilityJob(t *testing.T) {
115115
"externalJobID": "123",
116116
"oracleFactory": "not a factory",
117117
}
118-
_, err := input.ToStandardCapabilityJob(jobName, false)
118+
_, err := input.ToStandardCapabilityJob(jobName)
119119
require.Error(t, err)
120120
assert.Contains(t, err.Error(), "cannot unmarshal !!str")
121121
})

system-tests/lib/cre/contracts/keystone.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,11 +214,11 @@ func (d *dons) mustToV2ConfigureInput(chainSelector uint64, contractAddress stri
214214
for i, nop := range don.Nops {
215215
nopName := nop.Name
216216

217-
ns, err := deployment.NodeInfo(nop.Nodes, d.offChain)
218-
if err != nil {
219-
panic(err)
220-
}
221217
if _, exists := nopMap[nopName]; !exists {
218+
ns, err := deployment.NodeInfo(nop.Nodes, d.offChain)
219+
if err != nil {
220+
panic(err)
221+
}
222222
nopMap[nopName] = capabilities_registry_v2.CapabilitiesRegistryNodeOperatorParams{
223223
Admin: adminAddrs[i],
224224
Name: nopName,

system-tests/lib/cre/don.go

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cre
33
import (
44
"context"
55
"fmt"
6+
"net/http"
67
"net/url"
78
"slices"
89
"strconv"
@@ -426,10 +427,6 @@ func NewNode(ctx context.Context, name string, nodeMetadata *NodeMetadata, ctfNo
426427
}
427428
}
428429

429-
if account := nodeMetadata.AptosAccount(); account != "" {
430-
node.Addresses.AptosAddresses = []string{account}
431-
}
432-
433430
return node, nil
434431
}
435432

@@ -439,9 +436,8 @@ type JobDistributorDetails struct {
439436
}
440437

441438
type Addresses struct {
442-
AdminAddress string `toml:"admin_address" json:"admin_address"` // address used to pay for transactions, applicable only for worker nodes
443-
MultiAddress string `toml:"multi_address" json:"multi_address"` // multi address used by OCR2, applicable only for bootstrap nodes
444-
AptosAddresses []string `toml:"aptos_addresses" json:"aptos_addresses"` // Aptos public addresses cached from node metadata or the node key API
439+
AdminAddress string `toml:"admin_address" json:"admin_address"` // address used to pay for transactions, applicable only for worker nodes
440+
MultiAddress string `toml:"multi_address" json:"multi_address"` // multi address used by OCR2, applicable only for bootstrap nodes
445441
}
446442

447443
type NodeClients struct {
@@ -496,11 +492,11 @@ func createJDChainConfigs(ctx context.Context, n *Node, supportedChains []blockc
496492
account = accounts[0]
497493
}
498494
case chainselectors.FamilyAptos:
499-
accounts, err := AptosAccountsForNode(ctx, n)
500-
if err != nil {
501-
return fmt.Errorf("failed to fetch aptos account address for node %s: %w", n.Name, err)
495+
aptosAccount, aptosErr := aptosAccountForNode(ctx, n)
496+
if aptosErr != nil {
497+
return fmt.Errorf("failed to fetch aptos account address for node %s: %w", n.Name, aptosErr)
502498
}
503-
account = accounts[0]
499+
account = aptosAccount
504500
// Deployment parsing prefers AccountAddressPublicKey for Aptos chain configs.
505501
// Mirror transmitter into this field so OCRConfigForChainSelector always resolves it.
506502
accountAddrPubKey = account
@@ -586,15 +582,47 @@ func createJDChainConfigs(ctx context.Context, n *Node, supportedChains []blockc
586582
return nil
587583
}
588584

589-
func AptosAccountsForNode(_ context.Context, n *Node) ([]string, error) {
590-
if len(n.Addresses.AptosAddresses) > 0 {
591-
return append([]string(nil), n.Addresses.AptosAddresses...), nil
592-
}
585+
func aptosAccountForNode(ctx context.Context, n *Node) (string, error) {
593586
if n.Keys != nil && n.Keys.AptosAccount() != "" {
594-
n.Addresses.AptosAddresses = []string{n.Keys.AptosAccount()}
595-
return append([]string(nil), n.Addresses.AptosAddresses...), nil
587+
return n.Keys.AptosAccount(), nil
588+
}
589+
590+
var runtimeKeys struct {
591+
Data []struct {
592+
Attributes struct {
593+
Account string `json:"account"`
594+
PublicKey string `json:"publicKey"`
595+
} `json:"attributes"`
596+
} `json:"data"`
597+
}
598+
resp, err := n.Clients.RestClient.APIClient.R().
599+
SetContext(ctx).
600+
SetResult(&runtimeKeys).
601+
Get("/v2/keys/aptos")
602+
if err != nil {
603+
return "", fmt.Errorf("failed to read Aptos keys from node API: %w", err)
604+
}
605+
if resp.StatusCode() != http.StatusOK {
606+
return "", fmt.Errorf("aptos keys endpoint returned status %d", resp.StatusCode())
596607
}
597-
return nil, fmt.Errorf("missing cached aptos addresses for node %s", n.Name)
608+
if len(runtimeKeys.Data) == 0 {
609+
return "", fmt.Errorf("no Aptos keys found on node %s", n.Name)
610+
}
611+
612+
account, err := crypto.NormalizeAptosAccount(runtimeKeys.Data[0].Attributes.Account)
613+
if err != nil {
614+
return "", fmt.Errorf("invalid Aptos account returned by node API: %w", err)
615+
}
616+
617+
if n.Keys != nil {
618+
if n.Keys.Aptos == nil {
619+
n.Keys.Aptos = &crypto.AptosKey{}
620+
}
621+
n.Keys.Aptos.Account = account
622+
n.Keys.Aptos.PublicKey = runtimeKeys.Data[0].Attributes.PublicKey
623+
}
624+
625+
return account, nil
598626
}
599627

600628
// AcceptJob accepts the job proposal for the given job proposal spec

system-tests/lib/cre/don/config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ func addWorkerNodeConfig(
543543
}
544544

545545
gateways := []coretoml.ConnectorGateway{}
546-
if topology != nil && len(topology.GatewayConnectors.Configurations) > 0 {
546+
if topology != nil && topology.GatewayConnectors != nil && len(topology.GatewayConnectors.Configurations) > 0 {
547547
for _, gateway := range topology.GatewayConnectors.Configurations {
548548
gateways = append(gateways, coretoml.ConnectorGateway{
549549
ID: ptr.Ptr(gateway.AuthGatewayID),

system-tests/lib/cre/don_test.go

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,74 @@ package cre
22

33
import (
44
"context"
5+
"net/http"
6+
"net/http/httptest"
7+
"sync/atomic"
58
"testing"
69

10+
"github.com/go-resty/resty/v2"
711
"github.com/stretchr/testify/require"
812

13+
"github.com/smartcontractkit/chainlink-testing-framework/framework/clclient"
914
"github.com/smartcontractkit/chainlink/system-tests/lib/cre/don/secrets"
10-
"github.com/smartcontractkit/chainlink/system-tests/lib/crypto"
15+
crecrypto "github.com/smartcontractkit/chainlink/system-tests/lib/crypto"
1116
)
1217

13-
func TestAptosAccountsForNode_ReturnsCachedAddresses(t *testing.T) {
18+
func TestAptosAccountForNode_UsesMetadataKeyWithoutCallingNodeAPI(t *testing.T) {
1419
t.Parallel()
1520

16-
node := &Node{
17-
Name: "node-1",
18-
Addresses: Addresses{
19-
AptosAddresses: []string{"0x1", "0x2"},
20-
},
21-
}
21+
var hits atomic.Int32
22+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
23+
hits.Add(1)
24+
http.NotFound(w, r)
25+
}))
26+
t.Cleanup(server.Close)
2227

23-
addresses, err := AptosAccountsForNode(context.Background(), node)
28+
expected, err := crecrypto.NormalizeAptosAccount("0x1")
2429
require.NoError(t, err)
25-
require.Equal(t, []string{"0x1", "0x2"}, addresses)
26-
27-
addresses[0] = "0xdead"
28-
require.Equal(t, []string{"0x1", "0x2"}, node.Addresses.AptosAddresses)
29-
}
30-
31-
func TestAptosAccountsForNode_RequiresCachedMetadataWhenCacheMissing(t *testing.T) {
32-
t.Parallel()
3330

3431
node := &Node{
3532
Name: "node-1",
33+
Keys: &secrets.NodeKeys{
34+
Aptos: &crecrypto.AptosKey{Account: expected},
35+
},
36+
Clients: NodeClients{
37+
RestClient: &clclient.ChainlinkClient{APIClient: resty.New().SetBaseURL(server.URL)},
38+
},
3639
}
3740

38-
_, err := AptosAccountsForNode(context.Background(), node)
39-
require.Error(t, err)
40-
require.ErrorContains(t, err, "missing cached aptos addresses for node node-1")
41+
account, err := aptosAccountForNode(context.Background(), node)
42+
require.NoError(t, err)
43+
require.Equal(t, expected, account)
44+
require.Zero(t, hits.Load(), "node API must not be called when metadata already has the Aptos key")
4145
}
4246

43-
func TestAptosAccountsForNode_ReturnsMetadataKeyWhenCacheMissing(t *testing.T) {
47+
func TestAptosAccountForNode_FallsBackToNodeAPIAndCachesKey(t *testing.T) {
4448
t.Parallel()
4549

50+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
51+
require.Equal(t, "/v2/keys/aptos", r.URL.Path)
52+
w.Header().Set("Content-Type", "application/json")
53+
_, err := w.Write([]byte(`{"data":[{"attributes":{"account":"0x1","publicKey":"0xabc123"}}]}`))
54+
require.NoError(t, err)
55+
}))
56+
t.Cleanup(server.Close)
57+
4658
node := &Node{
4759
Name: "node-1",
48-
Keys: &secrets.NodeKeys{
49-
Aptos: &crypto.AptosKey{
50-
Account: "0x1",
51-
},
60+
Keys: &secrets.NodeKeys{},
61+
Clients: NodeClients{
62+
RestClient: &clclient.ChainlinkClient{APIClient: resty.New().SetBaseURL(server.URL)},
5263
},
5364
}
5465

55-
addresses, err := AptosAccountsForNode(context.Background(), node)
66+
account, err := aptosAccountForNode(context.Background(), node)
67+
require.NoError(t, err)
68+
69+
expected, err := crecrypto.NormalizeAptosAccount("0x1")
5670
require.NoError(t, err)
57-
require.Equal(t, []string{"0x1"}, addresses)
58-
require.Equal(t, []string{"0x1"}, node.Addresses.AptosAddresses)
71+
require.Equal(t, expected, account)
72+
require.NotNil(t, node.Keys.Aptos)
73+
require.Equal(t, expected, node.Keys.Aptos.Account)
74+
require.Equal(t, "0xabc123", node.Keys.Aptos.PublicKey)
5975
}

0 commit comments

Comments
 (0)