Skip to content

Commit 91c102c

Browse files
committed
Add orderer endpoint
Signed-off-by: Liran Funaro <[email protected]>
1 parent 96a137a commit 91c102c

File tree

18 files changed

+550
-165
lines changed

18 files changed

+550
-165
lines changed

api/types/orderer_endpoint.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package types
8+
9+
import (
10+
"encoding/json"
11+
"errors"
12+
"fmt"
13+
"slices"
14+
"strconv"
15+
"strings"
16+
17+
"gopkg.in/yaml.v3"
18+
)
19+
20+
type (
21+
// OrdererEndpoint defines an orderer party's endpoint.
22+
OrdererEndpoint struct {
23+
Host string `mapstructure:"host" json:"host,omitempty" yaml:"host,omitempty"`
24+
Port int `mapstructure:"port" json:"port,omitempty" yaml:"port,omitempty"`
25+
// ID is the party ID.
26+
ID uint32 `mapstructure:"id" json:"id,omitempty" yaml:"id,omitempty"`
27+
MspID string `mapstructure:"msp-id" json:"msp-id,omitempty" yaml:"msp-id,omitempty"`
28+
// API should be broadcast and/or deliver. Empty value means all API is supported.
29+
API []string `mapstructure:"api" json:"api,omitempty" yaml:"api,omitempty"`
30+
}
31+
)
32+
33+
const (
34+
// Broadcast support by endpoint.
35+
Broadcast = "broadcast"
36+
// Deliver support by endpoint.
37+
Deliver = "deliver"
38+
// NoID indicates that a party ID is not specified (default).
39+
// This allows backward compatibility with orderers that doesn't support this syntax.
40+
NoID = uint32(0x100000)
41+
)
42+
43+
// ErrInvalidEndpoint orderer endpoints error.
44+
var ErrInvalidEndpoint = errors.New("invalid endpoint")
45+
46+
// Address returns a string representation of the endpoint's address.
47+
func (e *OrdererEndpoint) Address() string {
48+
return fmt.Sprintf("%s:%d", e.Host, e.Port)
49+
}
50+
51+
// String returns a deterministic representation of the endpoint.
52+
func (e *OrdererEndpoint) String() string {
53+
var output strings.Builder
54+
isFirst := true
55+
if e.ID < NoID {
56+
output.WriteString("id=")
57+
output.WriteString(strconv.FormatUint(uint64(e.ID), 10))
58+
isFirst = false
59+
}
60+
if len(e.MspID) > 0 {
61+
if !isFirst {
62+
output.WriteRune(',')
63+
}
64+
output.WriteString("msp-id=")
65+
output.WriteString(e.MspID)
66+
isFirst = false
67+
}
68+
for _, api := range e.API {
69+
if !isFirst {
70+
output.WriteRune(',')
71+
}
72+
output.WriteString(api)
73+
isFirst = false
74+
}
75+
if len(e.Host) > 0 || e.Port > 0 {
76+
if !isFirst {
77+
output.WriteRune(',')
78+
}
79+
output.WriteString(e.Host)
80+
output.WriteRune(':')
81+
output.WriteString(strconv.FormatInt(int64(e.Port), 10))
82+
}
83+
return output.String()
84+
}
85+
86+
// SupportsAPI returns true if this endpoint supports API.
87+
// It also returns true if no APIs are specified, as we cannot know.
88+
func (e *OrdererEndpoint) SupportsAPI(api string) bool {
89+
return len(e.API) == 0 || slices.Contains(e.API, api)
90+
}
91+
92+
// ParseOrdererEndpoint parses a string according to the following schema order (the first that succeeds).
93+
// Schema 1: YAML.
94+
// Schema 2: JSON.
95+
// Schema 3: [id=ID,][msp-id=MspID,][broadcast,][deliver,][host=Host,][port=Port,][Host:Port].
96+
func ParseOrdererEndpoint(valueRaw string) (*OrdererEndpoint, error) {
97+
ret := &OrdererEndpoint{ID: NoID}
98+
if len(valueRaw) == 0 {
99+
return ret, nil
100+
}
101+
if err := yaml.Unmarshal([]byte(valueRaw), ret); err == nil {
102+
return ret, nil
103+
}
104+
if err := json.Unmarshal([]byte(valueRaw), ret); err == nil {
105+
return ret, nil
106+
}
107+
err := unmarshalOrdererEndpoint(valueRaw, ret)
108+
return ret, err
109+
}
110+
111+
func unmarshalOrdererEndpoint(valueRaw string, out *OrdererEndpoint) error {
112+
metaParts := strings.Split(valueRaw, ",")
113+
for _, item := range metaParts {
114+
item = strings.TrimSpace(item)
115+
equalIdx := strings.Index(item, "=")
116+
colonIdx := strings.Index(item, ":")
117+
var err error
118+
switch {
119+
case item == Broadcast || item == Deliver:
120+
out.API = append(out.API, item)
121+
case equalIdx >= 0:
122+
key, value := strings.TrimSpace(item[:equalIdx]), strings.TrimSpace(item[equalIdx+1:])
123+
switch key {
124+
case "msp-id":
125+
out.MspID = value
126+
case "host":
127+
out.Host = value
128+
case "id":
129+
err = out.setID(value)
130+
case "port":
131+
err = out.setPort(value)
132+
default:
133+
return fmt.Errorf("invalid key '%s' for item '%s': %w", key, item, ErrInvalidEndpoint)
134+
}
135+
case colonIdx >= 0:
136+
out.Host = strings.TrimSpace(item[:colonIdx])
137+
err = out.setPort(strings.TrimSpace(item[colonIdx+1:]))
138+
default:
139+
return fmt.Errorf("invalid item '%s': %w", item, ErrInvalidEndpoint)
140+
}
141+
if err != nil {
142+
return err
143+
}
144+
}
145+
return nil
146+
}
147+
148+
func (e *OrdererEndpoint) setPort(portStr string) error {
149+
port, err := strconv.ParseInt(portStr, 10, 32)
150+
if err != nil {
151+
return fmt.Errorf("failed to parse port: %w", err)
152+
}
153+
e.Port = int(port)
154+
return nil
155+
}
156+
157+
func (e *OrdererEndpoint) setID(idStr string) error {
158+
id, err := strconv.ParseUint(idStr, 10, 32)
159+
if err != nil {
160+
return fmt.Errorf("invalid id value: %w", err)
161+
}
162+
e.ID = uint32(id)
163+
return nil
164+
}

api/types/orderer_endpoint_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package types
8+
9+
import (
10+
"encoding/json"
11+
"testing"
12+
13+
"github.com/stretchr/testify/require"
14+
"gopkg.in/yaml.v3"
15+
)
16+
17+
func TestReadWrite(t *testing.T) {
18+
t.Parallel()
19+
valSchema := "id=5,msp-id=org,broadcast,deliver,localhost:5050"
20+
valJSON := `{"id":5,"msp-id":"org","api":["broadcast","deliver"],"host":"localhost","port":5050}`
21+
valYAML := `
22+
id: 5
23+
msp-id: org
24+
api:
25+
- broadcast
26+
- deliver
27+
host: localhost
28+
port: 5050
29+
`
30+
expected := &OrdererEndpoint{
31+
ID: 5,
32+
MspID: "org",
33+
API: []string{"broadcast", "deliver"},
34+
Host: "localhost",
35+
Port: 5050,
36+
}
37+
require.Equal(t, "localhost:5050", expected.Address())
38+
require.Equal(t, valSchema, expected.String())
39+
40+
valJSONRaw, err := json.Marshal(expected)
41+
require.NoError(t, err)
42+
require.JSONEq(t, valJSON, string(valJSONRaw))
43+
44+
valYamlRaw, err := yaml.Marshal(expected)
45+
require.NoError(t, err)
46+
require.YAMLEq(t, valYAML, string(valYamlRaw))
47+
48+
e, err := ParseOrdererEndpoint(valSchema)
49+
require.NoError(t, err)
50+
require.Equal(t, expected, e)
51+
52+
e, err = ParseOrdererEndpoint(valJSON)
53+
require.NoError(t, err)
54+
require.Equal(t, expected, e)
55+
56+
e, err = ParseOrdererEndpoint(valYAML)
57+
require.NoError(t, err)
58+
require.Equal(t, expected, e)
59+
60+
valJSONNoID := `{"msp-id":"org","api":["broadcast","deliver"],"host":"localhost","port":5050}`
61+
e, err = ParseOrdererEndpoint(valJSONNoID)
62+
require.NoError(t, err)
63+
require.Equal(t, &OrdererEndpoint{
64+
ID: NoID,
65+
MspID: "org",
66+
API: []string{"broadcast", "deliver"},
67+
Host: "localhost",
68+
Port: 5050,
69+
}, e)
70+
}

cmd/common/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"os"
1111

1212
"github.com/pkg/errors"
13-
"gopkg.in/yaml.v2"
13+
"gopkg.in/yaml.v3"
1414

1515
"github.com/hyperledger/fabric-x-common/cmd/common/comm"
1616
"github.com/hyperledger/fabric-x-common/cmd/common/signer"

cmd/cryptogen/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
"github.com/hyperledger/fabric-x-common/internaltools/cryptogen/msp"
2020

2121
"github.com/alecthomas/kingpin/v2"
22-
"gopkg.in/yaml.v2"
22+
"gopkg.in/yaml.v3"
2323
)
2424

2525
const (

cmd/cryptogen/main_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"testing"
1010

1111
"github.com/stretchr/testify/require"
12-
yaml "gopkg.in/yaml.v2"
12+
"gopkg.in/yaml.v3"
1313
)
1414

1515
func TestDefaultConfigParsing(t *testing.T) {

common/channelconfig/realconfig_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/hyperledger/fabric-protos-go-apiv2/common"
1414
"github.com/stretchr/testify/require"
1515

16+
"github.com/hyperledger/fabric-x-common/api/types"
1617
"github.com/hyperledger/fabric-x-common/common/channelconfig"
1718
"github.com/hyperledger/fabric-x-common/core/config/configtest"
1819
"github.com/hyperledger/fabric-x-common/internaltools/configtxgen/encoder"
@@ -57,7 +58,7 @@ func TestOrgSpecificOrdererEndpoints(t *testing.T) {
5758
require.Nil(t, cg)
5859
require.EqualError(t, err, "could not create orderer group: failed to create orderer org: orderer endpoints for organization SampleOrg are missing and must be configured when capability V3_0 is enabled")
5960

60-
conf.Orderer.Organizations[0].OrdererEndpoints = []string{"127.0.0.1:7050"}
61+
conf.Orderer.Organizations[0].OrdererEndpoints = []*types.OrdererEndpoint{{Host: "127.0.0.1", Port: 7050}}
6162
cg, err = encoder.NewChannelGroup(conf)
6263
require.NoError(t, err)
6364

common/configtx/test/helper.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,12 @@ func MakeGenesisBlockFromMSPs(channelID string, appMSPConf, ordererMSPConf *mspp
6767
ModPolicy: channelconfig.AdminsPolicyKey,
6868
}
6969

70+
endpoints := profile.Orderer.Organizations[0].OrdererEndpoints
7071
ordererOrgProtos := &cb.OrdererAddresses{
71-
Addresses: profile.Orderer.Organizations[0].OrdererEndpoints,
72+
Addresses: make([]string, len(endpoints)),
73+
}
74+
for i, e := range endpoints {
75+
ordererOrgProtos.Addresses[i] = e.String()
7276
}
7377
ordererOrg.Values[channelconfig.EndpointsKey] = &cb.ConfigValue{
7478
Value: protoutil.MarshalOrPanic(ordererOrgProtos),

0 commit comments

Comments
 (0)