From 1ef5a9e950ffc0fa053e6209790db54f1c0b9f93 Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Thu, 2 Apr 2026 15:57:03 -0700 Subject: [PATCH 1/9] wip --- build/devenv/environment.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 7dfbeeb8d..e90ffce9d 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -471,6 +471,9 @@ func enrichEnvironmentTopology(cfg *ccipOffchain.EnvironmentTopology, verifiers if nop.SignerAddressByFamily[chainsel.FamilyStellar] == "" { cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyStellar, ver.Out.BootstrapKeys.EdDSAPublicKey) } + if nop.SignerAddressByFamily[chainsel.FamilySolana] == "" { + cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilySolana, ver.Out.BootstrapKeys.ECDSAAddress) + } seenAliases[ver.NOPAlias] = struct{}{} } } From 3d7fb247dc3fbedf763b8feffc8c0c5eb3016067 Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Thu, 2 Apr 2026 17:24:36 -0700 Subject: [PATCH 2/9] dedupe lanes --- build/devenv/implcommon.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build/devenv/implcommon.go b/build/devenv/implcommon.go index 53bbbfd75..3181f11b2 100644 --- a/build/devenv/implcommon.go +++ b/build/devenv/implcommon.go @@ -71,12 +71,22 @@ func connectAllChains( } laneConfigs := make([]lanes.LaneConfig, 0) + seen := make(map[[2]uint64]struct{}) for _, sel := range orderedSelectors { entry := entries[sel] cvInputs := buildCommitteeVerifierInputs(topology, entry.remoteSelectors, entries) for _, rs := range entry.remoteSelectors { + lo, hi := sel, rs + if lo > hi { + lo, hi = hi, lo + } + if _, dup := seen[[2]uint64{lo, hi}]; dup { + continue + } + seen[[2]uint64{lo, hi}] = struct{}{} + remote, ok := entries[rs] if !ok { return fmt.Errorf("missing chain definition for remote selector %d (referenced from chain %d)", rs, sel) @@ -88,6 +98,7 @@ func connectAllChains( chainB := remote.chainDef chainB.Selector = rs + chainB.CommitteeVerifierInputs = buildCommitteeVerifierInputs(topology, remote.remoteSelectors, entries) laneConfigs = append(laneConfigs, lanes.LaneConfig{ ChainA: chainA, From c0a57a3dc005699785ff159a1e88a8877dc67bff Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Sun, 5 Apr 2026 14:49:43 -0700 Subject: [PATCH 3/9] add comment --- build/devenv/implcommon.go | 1 + 1 file changed, 1 insertion(+) diff --git a/build/devenv/implcommon.go b/build/devenv/implcommon.go index 3181f11b2..d509af9db 100644 --- a/build/devenv/implcommon.go +++ b/build/devenv/implcommon.go @@ -78,6 +78,7 @@ func connectAllChains( cvInputs := buildCommitteeVerifierInputs(topology, entry.remoteSelectors, entries) for _, rs := range entry.remoteSelectors { + // Normalize selector pairs to avoid duplicate lane configs for (A,B) vs (B,A). lo, hi := sel, rs if lo > hi { lo, hi = hi, lo From 4dd5b945378c0adb3e7f33f171c018a978381f9b Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Mon, 6 Apr 2026 09:45:22 -0700 Subject: [PATCH 4/9] lint --- build/devenv/implcommon.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/build/devenv/implcommon.go b/build/devenv/implcommon.go index d509af9db..9cccf9c90 100644 --- a/build/devenv/implcommon.go +++ b/build/devenv/implcommon.go @@ -79,10 +79,8 @@ func connectAllChains( for _, rs := range entry.remoteSelectors { // Normalize selector pairs to avoid duplicate lane configs for (A,B) vs (B,A). - lo, hi := sel, rs - if lo > hi { - lo, hi = hi, lo - } + lo := min(sel, rs) + hi := max(sel, rs) if _, dup := seen[[2]uint64{lo, hi}]; dup { continue } From dc3cb3da94a3fa6603da6e7c23e8166ae6630bb2 Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Mon, 6 Apr 2026 14:17:09 -0700 Subject: [PATCH 5/9] populate jd node info --- build/devenv/environment.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index c3b097688..49a0cc4ee 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -989,7 +989,7 @@ func NewEnvironment() (in *Cfg, err error) { // Register standalone verifiers with JD so they can receive job proposals. if jdInfra != nil && jdInfra.OffchainClient != nil { - if err := registerStandaloneVerifiersWithJD(ctx, in.Verifier, jdInfra.OffchainClient); err != nil { + if err := registerStandaloneVerifiersWithJD(ctx, in.Verifier, jdInfra); err != nil { return nil, err } } @@ -2090,8 +2090,16 @@ func slicesEqual(a, b []string) bool { // registerStandaloneVerifiersWithJD registers standalone verifiers with JD in parallel // and waits for them to establish their WSRPC connections. +// It also records each verifier's JD node ID in jdInfra.NodeIDMap (keyed by NOP alias) so that +// deployment.Environment.NodeIDs is populated for changesets that list nodes by ID (e.g. +// fetch-nop-signing-keys). CL-mode NOPs get the same map via RegisterNodesWithJD; standalone +// verifiers do not, so we merge here. // TODO: this is common for all bootstrapped apps, make more general? -func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committeeverifier.Input, jdClient offchain.Client) error { +func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committeeverifier.Input, jdInfra *jobs.JDInfrastructure) error { + if jdInfra == nil || jdInfra.OffchainClient == nil { + return nil + } + jdClient := jdInfra.OffchainClient // Filter to standalone verifiers only var standaloneVerifiers []*committeeverifier.Input for _, ver := range verifiers { @@ -2122,9 +2130,15 @@ func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committ return fmt.Errorf("failed to register bootstrap %s with JD: %w", ver.ContainerName, err) } - // Store the JD node ID in the verifier output for later use when proposing jobs. + // Store the JD node ID on the verifier and in JDInfra.NodeIDMap so GetNodeIDs() is non-empty + // for CLDF deployment.Environment (fetch-nop-signing-keys, etc.). mu.Lock() ver.Out.JDNodeID = reg.NodeID + alias := ver.NOPAlias + if alias == "" { + alias = ver.ContainerName + } + jdInfra.NodeIDMap[alias] = reg.NodeID mu.Unlock() // Wait for bootstrap to connect to JD From ef83dd30870ebbd462c2f57096a026da033a57c6 Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Mon, 6 Apr 2026 14:18:20 -0700 Subject: [PATCH 6/9] remove legacy dedupe logic --- build/devenv/implcommon.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/build/devenv/implcommon.go b/build/devenv/implcommon.go index 48aceb570..084cc2c26 100644 --- a/build/devenv/implcommon.go +++ b/build/devenv/implcommon.go @@ -240,19 +240,10 @@ func connectAllChainsLegacy( } laneConfigs := make([]lanes.LaneConfig, 0) - seen := make(map[[2]uint64]struct{}) for _, sel := range orderedSelectors { entry := entries[sel] cvInputs := buildCommitteeVerifierInputs(topology, entry.remoteSelectors, entries) for _, rs := range entry.remoteSelectors { - // Normalize selector pairs to avoid duplicate lane configs for (A,B) vs (B,A). - lo := min(sel, rs) - hi := max(sel, rs) - if _, dup := seen[[2]uint64{lo, hi}]; dup { - continue - } - seen[[2]uint64{lo, hi}] = struct{}{} - remote, ok := entries[rs] if !ok { return fmt.Errorf("missing chain definition for remote selector %d (referenced from chain %d)", rs, sel) @@ -263,7 +254,6 @@ func connectAllChainsLegacy( chainB := remote.chainDef chainB.Selector = rs - chainB.CommitteeVerifierInputs = buildCommitteeVerifierInputs(topology, remote.remoteSelectors, entries) laneConfigs = append(laneConfigs, lanes.LaneConfig{ ChainA: chainA, From 093e7f6313fecbd25d21011707f6e74bf4ceb57f Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Mon, 6 Apr 2026 15:07:37 -0700 Subject: [PATCH 7/9] use evm signer for solana --- build/devenv/environment.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 49a0cc4ee..6cddc5020 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -470,8 +470,10 @@ func enrichEnvironmentTopology(cfg *ccipOffchain.EnvironmentTopology, verifiers // For CL mode the signer address should be fetch from JD, or the NOP is not found continue } - if nop.SignerAddressByFamily[chainsel.FamilyEVM] == "" { - cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyEVM, ver.Out.BootstrapKeys.ECDSAAddress) + evmSigner := nop.SignerAddressByFamily[chainsel.FamilyEVM] + if evmSigner == "" { + evmSigner = ver.Out.BootstrapKeys.ECDSAAddress + cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyEVM, evmSigner) } if nop.SignerAddressByFamily[chainsel.FamilyCanton] == "" { cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyCanton, ver.Out.BootstrapKeys.ECDSAPublicKey) @@ -479,8 +481,9 @@ func enrichEnvironmentTopology(cfg *ccipOffchain.EnvironmentTopology, verifiers if nop.SignerAddressByFamily[chainsel.FamilyStellar] == "" { cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyStellar, ver.Out.BootstrapKeys.EdDSAPublicKey) } + // Solana committee verification uses ECDSA offchai since we don't have separate Solana key infra if nop.SignerAddressByFamily[chainsel.FamilySolana] == "" { - cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilySolana, ver.Out.BootstrapKeys.ECDSAAddress) + cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilySolana, evmSigner) } seenAliases[ver.NOPAlias] = struct{}{} } From 52e1904e2b0273bbe033e9df05ace4af9ccf3f2d Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Mon, 6 Apr 2026 16:23:41 -0700 Subject: [PATCH 8/9] remove registerStandaloneVerifiersWithJD edits --- build/devenv/environment.go | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 6cddc5020..3a2e50d57 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -992,7 +992,7 @@ func NewEnvironment() (in *Cfg, err error) { // Register standalone verifiers with JD so they can receive job proposals. if jdInfra != nil && jdInfra.OffchainClient != nil { - if err := registerStandaloneVerifiersWithJD(ctx, in.Verifier, jdInfra); err != nil { + if err := registerStandaloneVerifiersWithJD(ctx, in.Verifier, jdInfra.OffchainClient); err != nil { return nil, err } } @@ -2093,16 +2093,8 @@ func slicesEqual(a, b []string) bool { // registerStandaloneVerifiersWithJD registers standalone verifiers with JD in parallel // and waits for them to establish their WSRPC connections. -// It also records each verifier's JD node ID in jdInfra.NodeIDMap (keyed by NOP alias) so that -// deployment.Environment.NodeIDs is populated for changesets that list nodes by ID (e.g. -// fetch-nop-signing-keys). CL-mode NOPs get the same map via RegisterNodesWithJD; standalone -// verifiers do not, so we merge here. // TODO: this is common for all bootstrapped apps, make more general? -func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committeeverifier.Input, jdInfra *jobs.JDInfrastructure) error { - if jdInfra == nil || jdInfra.OffchainClient == nil { - return nil - } - jdClient := jdInfra.OffchainClient +func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committeeverifier.Input, jdClient offchain.Client) error { // Filter to standalone verifiers only var standaloneVerifiers []*committeeverifier.Input for _, ver := range verifiers { @@ -2133,15 +2125,8 @@ func registerStandaloneVerifiersWithJD(ctx context.Context, verifiers []*committ return fmt.Errorf("failed to register bootstrap %s with JD: %w", ver.ContainerName, err) } - // Store the JD node ID on the verifier and in JDInfra.NodeIDMap so GetNodeIDs() is non-empty - // for CLDF deployment.Environment (fetch-nop-signing-keys, etc.). mu.Lock() ver.Out.JDNodeID = reg.NodeID - alias := ver.NOPAlias - if alias == "" { - alias = ver.ContainerName - } - jdInfra.NodeIDMap[alias] = reg.NodeID mu.Unlock() // Wait for bootstrap to connect to JD From dece727de75b030be410ee419c22cd9d76d2f969 Mon Sep 17 00:00:00 2001 From: Oliver Townsend Date: Thu, 9 Apr 2026 14:34:28 -0700 Subject: [PATCH 9/9] typo --- build/devenv/environment.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 620f64de2..2169e76d9 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -478,7 +478,7 @@ func enrichEnvironmentTopology(cfg *ccipOffchain.EnvironmentTopology, verifiers if nop.SignerAddressByFamily[chainsel.FamilyStellar] == "" { cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilyStellar, ver.Out.BootstrapKeys.EdDSAPublicKey) } - // Solana committee verification uses ECDSA offchai since we don't have separate Solana key infra + // Solana committee verification uses ECDSA offchain since we don't have separate Solana key infra if nop.SignerAddressByFamily[chainsel.FamilySolana] == "" { cfg.NOPTopology.SetNOPSignerAddress(ver.NOPAlias, chainsel.FamilySolana, evmSigner) }