diff --git a/controllers/testutils/channel.go b/controllers/testutils/channel.go index 17f0300d..f02506bf 100644 --- a/controllers/testutils/channel.go +++ b/controllers/testutils/channel.go @@ -8,6 +8,9 @@ import ( "github.com/hyperledger/fabric-config/configtx/membership" "github.com/hyperledger/fabric-config/configtx/orderer" cb "github.com/hyperledger/fabric-protos-go/common" + sb "github.com/hyperledger/fabric-protos-go/orderer/smartbft" + "github.com/kfsoftware/hlf-operator/controllers/utils" + hlfv1alpha1 "github.com/kfsoftware/hlf-operator/pkg/apis/hlf.kungfusoftware.es/v1alpha1" "github.com/pkg/errors" "time" ) @@ -25,9 +28,11 @@ type PeerOrg struct { } type Consenter struct { - host string - port int - tlsCert *x509.Certificate + host string + port int + tlsCert *x509.Certificate + signCert *x509.Certificate + mspId string } type channelStore struct { } @@ -38,6 +43,7 @@ type CreateChannelOptions struct { name string batchSize *orderer.BatchSize batchDuration *time.Duration + consensus hlfv1alpha1.OrdererConsensusType } func (o CreateChannelOptions) validate() error { @@ -87,11 +93,18 @@ func WithPeerOrgs(peerOrgs ...PeerOrg) ChannelOption { o.peerOrgs = peerOrgs } } -func CreateConsenter(host string, port int, tlsCert *x509.Certificate) Consenter { +func WithConsensus(consensus hlfv1alpha1.OrdererConsensusType) ChannelOption { + return func(o *CreateChannelOptions) { + o.consensus = consensus + } +} +func CreateConsenter(host string, port int, tlsCert, signCert *x509.Certificate, mspId string) Consenter { return Consenter{ - host: host, - port: port, - tlsCert: tlsCert, + host: host, + port: port, + tlsCert: tlsCert, + signCert: signCert, + mspId: mspId, } } func CreateOrdererOrg(mspID string, tlsRootCert *x509.Certificate, signRootCert *x509.Certificate, ordererUrls []string) OrdererOrg { @@ -145,32 +158,75 @@ func (s channelStore) GetApplicationChannelBlock(ctx context.Context, opts ...Ch } peerOrgs = append(peerOrgs, genesisOrdererOrg) } - var consenters []orderer.Consenter - for _, consenter := range o.consenters { - genesisConsenter := orderer.Consenter{ - Address: orderer.EtcdAddress{ - Host: consenter.host, - Port: consenter.port, + var consenters []orderer.Consenter // raft consenter + consenterMapping := []cb.Consenter{} // bft consenter + var etcdRaft orderer.EtcdRaft + var smartBFTOptions *sb.Options + var ordererType string + channelCapabilities := []string{"V2_0"} + if o.consensus == hlfv1alpha1.OrdererConsensusEtcdraft { + ordererType = orderer.ConsensusTypeEtcdRaft + for _, consenter := range o.consenters { + genesisConsenter := orderer.Consenter{ + Address: orderer.EtcdAddress{ + Host: consenter.host, + Port: consenter.port, + }, + ClientTLSCert: consenter.tlsCert, + ServerTLSCert: consenter.tlsCert, + } + consenters = append(consenters, genesisConsenter) + } + etcdRaft = orderer.EtcdRaft{ + Consenters: consenters, + Options: orderer.EtcdRaftOptions{ + TickInterval: "500ms", + ElectionTick: 10, + HeartbeatTick: 1, + MaxInflightBlocks: 5, + SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB }, - ClientTLSCert: consenter.tlsCert, - ServerTLSCert: consenter.tlsCert, } - consenters = append(consenters, genesisConsenter) + } else if o.consensus == hlfv1alpha1.OrdererConsensusBFT { + ordererType = orderer.ConsensusTypeBFT + channelCapabilities = append(channelCapabilities, "V3_0") + for i, consenter := range o.consenters { + consenterMapping = append(consenterMapping, cb.Consenter{ + Host: consenter.host, + Port: uint32(consenter.port), + MspId: consenter.mspId, + Id: uint32(i + 1), + Identity: utils.EncodeX509Certificate(consenter.signCert), + ClientTlsCert: utils.EncodeX509Certificate(consenter.tlsCert), + ServerTlsCert: utils.EncodeX509Certificate(consenter.tlsCert), + }) + } + smartBFTOptions = &sb.Options{ + RequestBatchMaxCount: 100, + RequestBatchMaxInterval: "50ms", + RequestForwardTimeout: "2s", + RequestComplainTimeout: "20s", + RequestAutoRemoveTimeout: "3m0s", + ViewChangeResendInterval: "5s", + ViewChangeTimeout: "20s", + LeaderHeartbeatTimeout: "1m0s", + CollectTimeout: "1s", + RequestBatchMaxBytes: 10485760, + IncomingMessageBufferSize: 200, + RequestPoolSize: 100000, + LeaderHeartbeatCount: 10, + } + } else { + return nil, fmt.Errorf("orderer type %s not supported", ordererType) } + channelConfig := configtx.Channel{ Orderer: configtx.Orderer{ - OrdererType: "etcdraft", - Organizations: ordererOrgs, - EtcdRaft: orderer.EtcdRaft{ - Consenters: consenters, - Options: orderer.EtcdRaftOptions{ - TickInterval: "500ms", - ElectionTick: 10, - HeartbeatTick: 1, - MaxInflightBlocks: 5, - SnapshotIntervalSize: 16 * 1024 * 1024, // 16 MB - }, - }, + ConsenterMapping: consenterMapping, + OrdererType: ordererType, + Organizations: ordererOrgs, + EtcdRaft: etcdRaft, + SmartBFT: smartBFTOptions, Policies: map[string]configtx.Policy{ "Readers": { Type: "ImplicitMeta", @@ -196,7 +252,7 @@ func (s channelStore) GetApplicationChannelBlock(ctx context.Context, opts ...Ch PreferredMaxBytes: 512 * 1024, }, BatchTimeout: 2 * time.Second, - State: "STATE_NORMAL", + State: orderer.ConsensusStateNormal, }, Application: configtx.Application{ Organizations: peerOrgs, @@ -225,7 +281,7 @@ func (s channelStore) GetApplicationChannelBlock(ctx context.Context, opts ...Ch }, ACLs: defaultACLs(), }, - Capabilities: []string{"V2_0"}, + Capabilities: channelCapabilities, Policies: map[string]configtx.Policy{ "Readers": { Type: "ImplicitMeta", diff --git a/kubectl-hlf/cmd/channel/generate.go b/kubectl-hlf/cmd/channel/generate.go index 85a6a294..9a590f65 100644 --- a/kubectl-hlf/cmd/channel/generate.go +++ b/kubectl-hlf/cmd/channel/generate.go @@ -2,17 +2,20 @@ package channel import ( "context" + "fmt" "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-config/configtx/orderer" "github.com/kfsoftware/hlf-operator/controllers/testutils" "github.com/kfsoftware/hlf-operator/controllers/utils" "github.com/kfsoftware/hlf-operator/kubectl-hlf/cmd/helpers" + hlfv1alpha1 "github.com/kfsoftware/hlf-operator/pkg/apis/hlf.kungfusoftware.es/v1alpha1" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" "io" "io/ioutil" "strings" + "time" ) type generateChannelCmd struct { @@ -24,8 +27,12 @@ type generateChannelCmd struct { maxMessageCount int absoluteMaxBytes int preferredMaxBytes int + batchTimeout int + consensus hlfv1alpha1.OrdererConsensusType } +var validConsensusTypes = []hlfv1alpha1.OrdererConsensusType{hlfv1alpha1.OrdererConsensusBFT, hlfv1alpha1.OrdererConsensusEtcdraft} + func (c generateChannelCmd) validate() error { if c.channelName == "" { return errors.Errorf("--channelName is required") @@ -82,6 +89,10 @@ func (c generateChannelCmd) run() error { if err != nil { return err } + signCert, err := utils.ParseX509Certificate([]byte(consenter.Status.SignCert)) + if err != nil { + return err + } consenterHost, consenterPort, err := helpers.GetOrdererHostAndPort( clientSet, consenter.Spec, @@ -94,6 +105,8 @@ func (c generateChannelCmd) run() error { consenterHost, consenterPort, tlsCert, + signCert, + consenter.Spec.MspID, ) consenters = append(consenters, createConsenter) _, ok := ordererMap[consenter.Spec.MspID] @@ -189,6 +202,8 @@ func (c generateChannelCmd) run() error { AbsoluteMaxBytes: uint32(c.absoluteMaxBytes), PreferredMaxBytes: uint32(c.preferredMaxBytes), }), + testutils.WithBatchTimeout(time.Duration(c.batchTimeout)*time.Second), + testutils.WithConsensus(c.consensus), ) if err != nil { return err @@ -223,6 +238,8 @@ func newGenerateChannelCMD(io.Writer, io.Writer) *cobra.Command { persistentFlags.IntVarP(&c.maxMessageCount, "maxMessageCount", "", 100, "Max transactions per block") persistentFlags.IntVarP(&c.absoluteMaxBytes, "absoluteMaxBytes", "", 1024*1024, "Max size per block") persistentFlags.IntVarP(&c.preferredMaxBytes, "preferredMaxBytes", "", 512*1024, "Max size per block") + persistentFlags.IntVarP(&c.batchTimeout, "batchTimeout", "", 2, "Batch timeout in seconds") + persistentFlags.Var(hlfv1alpha1.NewConsensusTypeValue(hlfv1alpha1.OrdererConsensusEtcdraft, validConsensusTypes, &c.consensus), "consensus", fmt.Sprintf("Consensus type ( %s, %s), default etcdraft", hlfv1alpha1.OrdererConsensusBFT, hlfv1alpha1.OrdererConsensusEtcdraft)) cmd.MarkPersistentFlagRequired("name") cmd.MarkPersistentFlagRequired("organizations") cmd.MarkPersistentFlagRequired("ordererOrganizations") diff --git a/pkg/apis/hlf.kungfusoftware.es/v1alpha1/hlf_types.go b/pkg/apis/hlf.kungfusoftware.es/v1alpha1/hlf_types.go index e4073925..8a322240 100644 --- a/pkg/apis/hlf.kungfusoftware.es/v1alpha1/hlf_types.go +++ b/pkg/apis/hlf.kungfusoftware.es/v1alpha1/hlf_types.go @@ -18,6 +18,7 @@ package v1alpha1 import ( "fmt" + "strings" sb "github.com/hyperledger/fabric-protos-go/orderer/smartbft" "github.com/kfsoftware/hlf-operator/pkg/status" @@ -2579,6 +2580,48 @@ const ( ConsensusStateMaintenance FabricMainChannelConsensusState = "STATE_MAINTENANCE" ) +type ConsensusTypeValue struct { + value *OrdererConsensusType + allowed []OrdererConsensusType +} + +func (c *ConsensusTypeValue) String() string { + if c.value == nil { + return "" + } + return string(*c.value) +} + +func (c *ConsensusTypeValue) Set(s string) error { + for _, v := range c.allowed { + if string(v) == s { + *c.value = v + return nil + } + } + return fmt.Errorf("invalid consensus type: %s. Valid types are: %s", s, strings.Join(c.allowedStrings(), ", ")) +} + +func (c *ConsensusTypeValue) Type() string { + return "ConsensusType" +} + +func (c *ConsensusTypeValue) allowedStrings() []string { + var allowedStrs []string + for _, v := range c.allowed { + allowedStrs = append(allowedStrs, string(v)) + } + return allowedStrs +} + +func NewConsensusTypeValue(val OrdererConsensusType, allowed []OrdererConsensusType, p *OrdererConsensusType) *ConsensusTypeValue { + *p = val + return &ConsensusTypeValue{ + value: p, + allowed: allowed, + } +} + type FabricMainChannelOrdererBatchSize struct { // The number of transactions that can fit in a block. // +kubebuilder:default:=100