Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions framework/.changeset/v0.13.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add Canton JWT authentication
39 changes: 39 additions & 0 deletions framework/components/blockchain/canton.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ package blockchain
import (
"context"
"fmt"
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/testcontainers/testcontainers-go"

"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain/canton"
)

const (
DefaultCantonPort = "8080"
TokenExpiry = time.Hour * 24
)

type CantonEndpoints struct {
ScanAPIURL string // https://docs.sync.global/app_dev/scan_api/index.html
RegistryAPIURL string // https://docs.sync.global/app_dev/token_standard/index.html#api-references
Expand All @@ -27,6 +34,8 @@ type CantonParticipantEndpoints struct {

HTTPHealthCheckURL string // responds on GET /health
GRPCHealthCheckURL string // grpc.health.v1.Health/Check

JWT string // JWT for this participant
}

// newCanton sets up a Canton blockchain network with the specified number of validators.
Expand Down Expand Up @@ -54,6 +63,9 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
if in.NumberOfCantonValidators >= 100 {
return nil, fmt.Errorf("number of validators too high: %d, valid range is 0-99", in.NumberOfCantonValidators)
}
if in.Port == "" {
in.Port = DefaultCantonPort
}

// Set up Postgres container
postgresReq := canton.PostgresContainerRequest(in.NumberOfCantonValidators)
Expand Down Expand Up @@ -100,6 +112,18 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
return nil, err
}

svToken, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: "",
Subject: "user-sv",
Audience: []string{canton.AuthProviderAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpiry)),
NotBefore: jwt.NewNumericDate(time.Now()),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: "",
}).SignedString([]byte(canton.AuthProviderSecret))
if err != nil {
return nil, fmt.Errorf("failed to create token for sv: %w", err)
}
endpoints := &CantonEndpoints{
ScanAPIURL: fmt.Sprintf("http://scan.%s:%s/api/scan", host, in.Port),
RegistryAPIURL: fmt.Sprintf("http://scan.%s:%s/registry", host, in.Port),
Expand All @@ -110,17 +134,31 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
ValidatorAPIURL: fmt.Sprintf("http://sv.validator-api.%s:%s/api/validator", host, in.Port),
HTTPHealthCheckURL: fmt.Sprintf("http://sv.http-health-check.%s:%s", host, in.Port),
GRPCHealthCheckURL: fmt.Sprintf("sv.grpc-health-check.%s:%s", host, in.Port),
JWT: svToken,
},
Participants: nil,
}
for i := 1; i <= in.NumberOfCantonValidators; i++ {
token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: "",
Subject: fmt.Sprintf("user-participant%v", i),
Audience: []string{canton.AuthProviderAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpiry)),
NotBefore: jwt.NewNumericDate(time.Now()),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: "",
}).SignedString([]byte(canton.AuthProviderSecret))
if err != nil {
return nil, fmt.Errorf("failed to create token for participant%v: %w", i, err)
}
participantEndpoints := CantonParticipantEndpoints{
JSONLedgerAPIURL: fmt.Sprintf("http://participant%d.json-ledger-api.%s:%s", i, host, in.Port),
GRPCLedgerAPIURL: fmt.Sprintf("participant%d.grpc-ledger-api.%s:%s", i, host, in.Port),
AdminAPIURL: fmt.Sprintf("participant%d.admin-api.%s:%s", i, host, in.Port),
ValidatorAPIURL: fmt.Sprintf("http://participant%d.validator-api.%s:%s/api/validator", i, host, in.Port),
HTTPHealthCheckURL: fmt.Sprintf("http://participant%d.http-health-check.%s:%s", i, host, in.Port),
GRPCHealthCheckURL: fmt.Sprintf("participant%d.grpc-health-check.%s:%s", i, host, in.Port),
JWT: token,
}
endpoints.Participants = append(endpoints.Participants, participantEndpoints)
}
Expand All @@ -129,6 +167,7 @@ func newCanton(ctx context.Context, in *Input) (*Output, error) {
UseCache: false,
Type: in.Type,
Family: FamilyCanton,
ChainID: in.ChainID,
ContainerName: nginxReq.Name,
NetworkSpecificData: &NetworkSpecificData{
CantonEndpoints: endpoints,
Expand Down
3 changes: 0 additions & 3 deletions framework/components/blockchain/canton/nginx.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,6 @@ func NginxContainerRequest(
spliceContainerName string,
) testcontainers.ContainerRequest {
nginxContainerName := framework.DefaultTCName("nginx")
if port == "" {
port = "8080"
}
nginxReq := testcontainers.ContainerRequest{
Image: DefaultNginxImage,
Name: nginxContainerName,
Expand Down
2 changes: 1 addition & 1 deletion framework/components/blockchain/canton/splice.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ func SpliceContainerRequest(
WaitingFor: wait.ForExec([]string{
"/bin/bash",
"/app/health-check.sh",
}).WithStartupTimeout(time.Minute * 3),
}).WithStartupTimeout(time.Minute * 5),
Env: map[string]string{
"DB_SERVER": postgresContainerName,
"DB_USER": DefaultPostgresUser,
Expand Down
2 changes: 2 additions & 0 deletions framework/components/fake/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
Expand Down
2 changes: 1 addition & 1 deletion framework/examples/myproject/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.3 // indirect
github.com/gogo/status v1.1.1 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.1.3 // indirect
Expand Down
21 changes: 4 additions & 17 deletions framework/examples/myproject/smoke_canton_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import (
"fmt"
"strings"
"testing"
"time"

"github.com/fullstorydev/grpcurl"
"github.com/go-resty/resty/v2"
"github.com/golang-jwt/jwt/v5"
"github.com/jhump/protoreflect/grpcreflect"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand All @@ -19,7 +17,6 @@ import (

"github.com/smartcontractkit/chainlink-testing-framework/framework"
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain"
"github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain/canton"
)

type CfgCanton struct {
Expand Down Expand Up @@ -48,38 +45,28 @@ func TestCantonSmoke(t *testing.T) {

testParticipant := func(t *testing.T, name string, endpoints blockchain.CantonParticipantEndpoints) {
t.Run(fmt.Sprintf("Test %s endpoints", name), func(t *testing.T) {
j, _ := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
Issuer: "",
Subject: fmt.Sprintf("user-%s", name),
Audience: []string{canton.AuthProviderAudience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)),
NotBefore: jwt.NewNumericDate(time.Now()),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: "",
}).SignedString([]byte(canton.AuthProviderSecret))

// JSON Ledger API
fmt.Println("Calling JSON Ledger API")
resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(j).R().
resp, err := resty.New().SetBaseURL(endpoints.JSONLedgerAPIURL).SetAuthToken(endpoints.JWT).R().
Get("/v2/packages")
assert.NoError(t, err)
fmt.Println(resp)

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

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

// Validator API
fmt.Println("Calling Validator API")
resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(j).R().
resp, err = resty.New().SetBaseURL(endpoints.ValidatorAPIURL).SetAuthToken(endpoints.JWT).R().
Get("/v0/admin/users")
assert.NoError(t, err)
fmt.Println(resp)
Expand Down
1 change: 1 addition & 0 deletions framework/examples/myproject_cll/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ require (
github.com/go-resty/resty/v2 v2.16.5 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions framework/examples/myproject_cll/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
Expand Down
2 changes: 1 addition & 1 deletion framework/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.22.1
github.com/go-resty/resty/v2 v2.16.3
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/google/go-cmp v0.7.0
github.com/google/uuid v1.6.0
github.com/hashicorp/consul/sdk v0.16.2
Expand Down Expand Up @@ -120,7 +121,6 @@ require (
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/gogo/status v1.1.1 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/btree v1.1.3 // indirect
Expand Down
Loading