Skip to content

Commit 86bec59

Browse files
authored
Canton - support Docker-internal networking (NONEVM-3773) (#2456)
* Canton - support Docker-internal networking (NONEVM-3773) * fix fmt verb * Fix port * Add struct tags
1 parent 3e02f48 commit 86bec59

File tree

7 files changed

+144
-95
lines changed

7 files changed

+144
-95
lines changed

book/src/framework/components/blockchains/canton.md

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,9 @@ import (
6666
"fmt"
6767
"strings"
6868
"testing"
69-
"time"
7069

7170
"github.com/fullstorydev/grpcurl"
7271
"github.com/go-resty/resty/v2"
73-
"github.com/golang-jwt/jwt/v5"
7472
"github.com/jhump/protoreflect/grpcreflect"
7573
"github.com/stretchr/testify/assert"
7674
"github.com/stretchr/testify/require"
@@ -79,7 +77,6 @@ import (
7977

8078
"github.com/smartcontractkit/chainlink-testing-framework/framework"
8179
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
82-
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain/canton"
8380
)
8481

8582
type CfgCanton struct {
@@ -94,52 +91,42 @@ func TestCantonSmoke(t *testing.T) {
9491
require.NoError(t, err)
9592

9693
t.Run("Test scan endpoint", func(t *testing.T) {
97-
resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.ScanAPIURL).R().
94+
resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.ScanAPIURL).R().
9895
Get("/v0/dso-party-id")
9996
assert.NoError(t, err)
10097
fmt.Println(resp)
10198
})
10299
t.Run("Test registry endpoint", func(t *testing.T) {
103-
resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonEndpoints.RegistryAPIURL).R().
104-
Get("/metadata/v1/instruments")
100+
resp, err := resty.New().SetBaseURL(bc.NetworkSpecificData.CantonData.ExternalEndpoints.RegistryAPIURL).R().
101+
Get("/registry/metadata/v1/instruments")
105102
assert.NoError(t, err)
106103
fmt.Println(resp)
107104
})
108105

109106
testParticipant := func(t *testing.T, name string, endpoints blockchain.CantonParticipantEndpoints) {
110107
t.Run(fmt.Sprintf("Test %s endpoints", name), func(t *testing.T) {
111-
j, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
112-
Issuer: "",
113-
Subject: fmt.Sprintf("user-%s", name),
114-
Audience: []string{canton.AuthProviderAudience},
115-
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
116-
NotBefore: jwt.NewNumericDate(time.Now()),
117-
IssuedAt: jwt.NewNumericDate(time.Now()),
118-
ID: "",
119-
}).SignedString([]byte(canton.AuthProviderSecret))
120-
121108
// JSON Ledger API
122109
fmt.Println("Calling JSON Ledger API")
123-
resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(j).R().
110+
resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(endpoints.JWT).R().
124111
Get("/v2/packages")
125112
assert.NoError(t, err)
126113
fmt.Println(resp)
127114

128115
// gRPC Ledger API - use reflection
129116
fmt.Println("Calling gRPC Ledger API")
130-
res, err := callGRPC(t.Context(), endpoints.GRPCLedgerAPIURL, "com.daml.ledger.api.v2.admin.PartyManagementService/GetParties", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", j)})
117+
res, err := callGRPC(t.Context(), endpoints.GRPCLedgerAPIURL, "com.daml.ledger.api.v2.admin.PartyManagementService/GetParties", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", endpoints.JWT)})
131118
assert.NoError(t, err)
132119
fmt.Println(res)
133120

134121
// gRPC Admin API - use reflection
135122
fmt.Println("Calling gRPC Admin API")
136-
res, err = callGRPC(t.Context(), endpoints.AdminAPIURL, "com.digitalasset.canton.admin.participant.v30.PackageService/ListDars", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", j)})
123+
res, err = callGRPC(t.Context(), endpoints.AdminAPIURL, "com.digitalasset.canton.admin.participant.v30.PackageService/ListDars", `{}`, []string{fmt.Sprintf("Authorization: Bearer %s", endpoints.JWT)})
137124
assert.NoError(t, err)
138125
fmt.Println(res)
139126

140127
// Validator API
141128
fmt.Println("Calling Validator API")
142-
resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(j).R().
129+
resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(endpoints.JWT).R().
143130
Get("/v0/admin/users")
144131
assert.NoError(t, err)
145132
fmt.Println(resp)
@@ -160,9 +147,9 @@ func TestCantonSmoke(t *testing.T) {
160147
}
161148

162149
// Call all participants, starting with the SV
163-
testParticipant(t, "sv", bc.NetworkSpecificData.CantonEndpoints.SuperValidator)
150+
testParticipant(t, "sv", bc.NetworkSpecificData.CantonData.ExternalEndpoints.SuperValidator)
164151
for i := 1; i <= in.BlockchainA.NumberOfCantonValidators; i++ {
165-
testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonEndpoints.Participants[i-1])
152+
testParticipant(t, fmt.Sprintf("participant%d", i), bc.NetworkSpecificData.CantonData.ExternalEndpoints.Participants[i-1])
166153
}
167154
}
168155

@@ -199,4 +186,4 @@ func callGRPC(ctx context.Context, url string, method string, jsonRequest string
199186
return output.String(), nil
200187
}
201188

202-
```
189+
```

framework/.changeset/v0.15.0.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- Update the Canton blockchain containers to support Docker-internal networking
2+
- Change the `NetworkSpecificData` for Canton to be of type `CantonData` and split internal/external endpoints into separate fields
3+
- Bump the Canton and Splice images to `0.5.13`

framework/components/blockchain/blockchain.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ type Output struct {
9292
}
9393

9494
type NetworkSpecificData struct {
95-
SuiAccount *SuiWalletInfo `toml:"sui_account" comment:"Sui network account info"`
96-
CantonEndpoints *CantonEndpoints `toml:"canton_endpoints" comment:"Canton network endpoints info"`
97-
StellarNetwork *StellarNetworkInfo `toml:"stellar_network" comment:"Stellar network info"`
95+
SuiAccount *SuiWalletInfo `toml:"sui_account" comment:"Sui network account info"`
96+
CantonData *CantonData `toml:"canton_data" comment:"Canton network data"`
97+
StellarNetwork *StellarNetworkInfo `toml:"stellar_network" comment:"Stellar network info"`
9898
}
9999

100100
// Node represents blockchain node output, URLs required for connection locally and inside docker network

framework/components/blockchain/canton.go

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,21 @@ const (
1919
TokenExpiry = time.Hour * 24 * 365 * 10 // 10 years
2020
)
2121

22+
type CantonData struct {
23+
// Docker internal endpoints, only reachable if connected to the same Docker network (framework.DefaultNetworkName)
24+
InternalEndpoints CantonEndpoints `toml:"internal_endpoints" comment:"Docker-internal endpoints, only reachable from containers connected to the same networks"`
25+
// External endpoints, reachable from the Docker host
26+
ExternalEndpoints CantonEndpoints `toml:"external_endpoints" comment:"Docker-external endpoints, only reachable from the Docker host"`
27+
}
28+
2229
type CantonEndpoints struct {
2330
// ScanAPIURL https://docs.sync.global/app_dev/scan_api/index.html
2431
ScanAPIURL string `toml:"scan_api_url" comment:"https://docs.sync.global/app_dev/scan_api/index.html"`
2532
// RegistryAPIURL https://docs.sync.global/app_dev/token_standard/index.html#api-references
2633
RegistryAPIURL string `toml:"registry_api_url" comment:"https://docs.sync.global/app_dev/token_standard/index.html#api-references"`
2734

2835
// SuperValidator The endpoints for the super validator
29-
SuperValidator CantonParticipantEndpoints `toml:"super_validator" comment:"Canton network super validator"`
36+
SuperValidator CantonParticipantEndpoints `toml:"super_validator" comment:"Canton super validator endpoints"`
3037
// Participants The endpoints for the participants, in order from participant1 to participantN - depending on the number of validators requested
3138
Participants []CantonParticipantEndpoints `toml:"participants" comment:"Canton participant endpoints"`
3239
}
@@ -119,7 +126,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
119126
}
120127

121128
// Set up Nginx container
122-
nginxReq := canton.NginxContainerRequest(in.NumberOfCantonValidators, in.Port, cantonReq.Name, spliceReq.Name)
129+
nginxReq, nginxContainerName := canton.NginxContainerRequest(in.NumberOfCantonValidators, in.Port, cantonReq.Name, spliceReq.Name)
123130
nginxContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
124131
ContainerRequest: nginxReq,
125132
Started: true,
@@ -133,6 +140,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
133140
return nil, err
134141
}
135142

143+
// Add SV info to output
136144
svUser := "user-sv"
137145
svToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
138146
Issuer: "",
@@ -146,21 +154,40 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
146154
if err != nil {
147155
return nil, fmt.Errorf("failed to create token for sv: %w", err)
148156
}
149-
endpoints := &CantonEndpoints{
150-
ScanAPIURL: fmt.Sprintf("http://scan.%s:%s/api/scan", host, in.Port),
151-
RegistryAPIURL: fmt.Sprintf("http://scan.%s:%s", host, in.Port), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root
152-
SuperValidator: CantonParticipantEndpoints{
153-
JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%s", host, in.Port),
154-
GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%s", host, in.Port),
155-
AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%s", host, in.Port),
156-
ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%s/api/validator", host, in.Port),
157-
HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%s", host, in.Port),
158-
GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%s", host, in.Port),
159-
UserID: svUser,
160-
JWT: svToken,
157+
data := &CantonData{
158+
InternalEndpoints: CantonEndpoints{
159+
ScanAPIURL: fmt.Sprintf("http://scan.%s:%d/api/scan", nginxContainerName, canton.DefaultNginxInternalPort),
160+
RegistryAPIURL: fmt.Sprintf("http://scan.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root
161+
SuperValidator: CantonParticipantEndpoints{
162+
JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort),
163+
GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort),
164+
AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort),
165+
ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%d/api/validator", nginxContainerName, canton.DefaultNginxInternalPort),
166+
HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort),
167+
GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%d", nginxContainerName, canton.DefaultNginxInternalPort),
168+
UserID: svUser,
169+
JWT: svToken,
170+
},
171+
Participants: make([]CantonParticipantEndpoints, 0, in.NumberOfCantonValidators),
172+
},
173+
ExternalEndpoints: CantonEndpoints{
174+
ScanAPIURL: fmt.Sprintf("http://scan.%s:%s/api/scan", host, in.Port),
175+
RegistryAPIURL: fmt.Sprintf("http://scan.%s:%s", host, in.Port), // Don't add /registry to URL as this is part of the OpenAPI spec and the base URL should point to the root
176+
SuperValidator: CantonParticipantEndpoints{
177+
JSONLedgerAPIURL: fmt.Sprintf("http://sv.json-ledger-api.%s:%s", host, in.Port),
178+
GRPCLedgerAPIURL: fmt.Sprintf("sv.grpc-ledger-api.%s:%s", host, in.Port),
179+
AdminAPIURL: fmt.Sprintf("sv.admin-api.%s:%s", host, in.Port),
180+
ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%s/api/validator", host, in.Port),
181+
HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%s", host, in.Port),
182+
GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%s", host, in.Port),
183+
UserID: svUser,
184+
JWT: svToken,
185+
},
186+
Participants: make([]CantonParticipantEndpoints, 0, in.NumberOfCantonValidators),
161187
},
162-
Participants: nil,
163188
}
189+
190+
// Add Participant info to output
164191
for i := 1; i <= in.NumberOfCantonValidators; i++ {
165192
participantUser := fmt.Sprintf("user-participant%v", i)
166193
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
@@ -175,7 +202,17 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
175202
if err != nil {
176203
return nil, fmt.Errorf("failed to create token for participant%v: %w", i, err)
177204
}
178-
participantEndpoints := CantonParticipantEndpoints{
205+
data.InternalEndpoints.Participants = append(data.InternalEndpoints.Participants, CantonParticipantEndpoints{
206+
JSONLedgerAPIURL: fmt.Sprintf("http://participant%d.json-ledger-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort),
207+
GRPCLedgerAPIURL: fmt.Sprintf("participant%d.grpc-ledger-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort),
208+
AdminAPIURL: fmt.Sprintf("participant%d.admin-api.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort),
209+
ValidatorAPIURL: fmt.Sprintf("http://participant%d.validator-api.%s:%d/api/validator", i, nginxContainerName, canton.DefaultNginxInternalPort),
210+
HTTPHealthCheckURL: fmt.Sprintf("http://participant%d.http-health-check.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort),
211+
GRPCHealthCheckURL: fmt.Sprintf("participant%d.grpc-health-check.%s:%d", i, nginxContainerName, canton.DefaultNginxInternalPort),
212+
UserID: participantUser,
213+
JWT: token,
214+
})
215+
data.ExternalEndpoints.Participants = append(data.ExternalEndpoints.Participants, CantonParticipantEndpoints{
179216
JSONLedgerAPIURL: fmt.Sprintf("http://participant%d.json-ledger-api.%s:%s", i, host, in.Port),
180217
GRPCLedgerAPIURL: fmt.Sprintf("participant%d.grpc-ledger-api.%s:%s", i, host, in.Port),
181218
AdminAPIURL: fmt.Sprintf("participant%d.admin-api.%s:%s", i, host, in.Port),
@@ -184,8 +221,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
184221
GRPCHealthCheckURL: fmt.Sprintf("participant%d.grpc-health-check.%s:%s", i, host, in.Port),
185222
UserID: participantUser,
186223
JWT: token,
187-
}
188-
endpoints.Participants = append(endpoints.Participants, participantEndpoints)
224+
})
189225
}
190226

191227
return &Output{
@@ -195,7 +231,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
195231
ChainID: in.ChainID,
196232
ContainerName: nginxReq.Name,
197233
NetworkSpecificData: &NetworkSpecificData{
198-
CantonEndpoints: endpoints,
234+
CantonData: data,
199235
},
200236
}, nil
201237
}

framework/components/blockchain/canton/canton.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
// Canton Defaults
1515
const (
16-
SpliceVersion = "0.5.11"
16+
SpliceVersion = "0.5.13"
1717
Image = "ghcr.io/digital-asset/decentralized-canton-sync/docker/canton"
1818
)
1919

0 commit comments

Comments
 (0)