-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathchain_id.go
More file actions
105 lines (87 loc) · 3.22 KB
/
Copy pathchain_id.go
File metadata and controls
105 lines (87 loc) · 3.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package main
import (
"context"
"fmt"
"strings"
"time"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
)
type detectedEndpointChainID struct {
endpoint string
chainID string
}
type chainIDDetector func(context.Context, string) (string, error)
var chainIDEndpointDetectTimeout = 15 * time.Second
func detectChainIDFromEndpoints(ctx context.Context, grpcAddrs, nodeRPCAddrs []string) (string, string, string, error) {
grpcChainIDs, err := detectEndpointChainIDs(ctx, "gRPC", grpcAddrs, chainIDFromGRPC)
if err != nil {
return "", "", "", err
}
rpcChainIDs, err := detectEndpointChainIDs(ctx, "node RPC", nodeRPCAddrs, chainIDFromRPC)
if err != nil {
return "", "", "", err
}
if err := validateReachableChainIDs("gRPC", grpcChainIDs); err != nil {
return "", "", "", err
}
if err := validateReachableChainIDs("node RPC", rpcChainIDs); err != nil {
return "", "", "", err
}
if grpcChainIDs[0].chainID != rpcChainIDs[0].chainID {
return "", "", "", fmt.Errorf("chain ID mismatch: gRPC returned %q, node RPC returned %q", grpcChainIDs[0].chainID, rpcChainIDs[0].chainID)
}
return grpcChainIDs[0].chainID, grpcChainIDs[0].endpoint, rpcChainIDs[0].endpoint, nil
}
func detectEndpointChainIDs(ctx context.Context, endpointType string, endpoints []string, detector chainIDDetector) ([]detectedEndpointChainID, error) {
var detected []detectedEndpointChainID
var errs []string
for _, endpoint := range endpoints {
endpointCtx, cancel := context.WithTimeout(ctx, chainIDEndpointDetectTimeout)
chainID, err := detector(endpointCtx, endpoint)
cancel()
if err != nil {
errs = append(errs, fmt.Sprintf("%s: %v", endpoint, err))
continue
}
detected = append(detected, detectedEndpointChainID{endpoint: endpoint, chainID: chainID})
}
if len(detected) == 0 {
return nil, fmt.Errorf("failed to detect chain ID via any %s endpoint: %s", endpointType, strings.Join(errs, "; "))
}
return detected, nil
}
func validateReachableChainIDs(endpointType string, detected []detectedEndpointChainID) error {
expected := detected[0].chainID
for _, item := range detected[1:] {
if item.chainID != expected {
return fmt.Errorf("%s endpoints disagree on chain ID: %s returned %q, %s returned %q", endpointType, detected[0].endpoint, expected, item.endpoint, item.chainID)
}
}
return nil
}
func chainIDFromGRPC(ctx context.Context, grpcAddr string) (string, error) {
conn, err := grpc.DialContext(ctx, grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return "", fmt.Errorf("dial: %w", err)
}
defer conn.Close()
resp, err := cmtservice.NewServiceClient(conn).GetNodeInfo(ctx, &cmtservice.GetNodeInfoRequest{})
if err != nil {
return "", fmt.Errorf("GetNodeInfo: %w", err)
}
return resp.DefaultNodeInfo.Network, nil
}
func chainIDFromRPC(ctx context.Context, nodeRPCAddr string) (string, error) {
rpcClient, err := rpchttp.New(nodeRPCAddr, "/websocket")
if err != nil {
return "", fmt.Errorf("create client: %w", err)
}
status, err := rpcClient.Status(ctx)
if err != nil {
return "", fmt.Errorf("status: %w", err)
}
return status.NodeInfo.Network, nil
}