diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index f57f638..c9cbd5b 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -20,7 +20,7 @@ jobs: uses: golangci/golangci-lint-action@v3 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. - version: v1.52 + version: v1.57 # Optional: working directory, useful for monorepos # working-directory: somedir diff --git a/api/client.go b/api/client.go index babd8e6..4425131 100644 --- a/api/client.go +++ b/api/client.go @@ -7,6 +7,8 @@ import ( "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/msp" + + hlfproto "github.com/s7techlab/hlf-sdk-go/block" ) type CurrentIdentity interface { @@ -43,6 +45,16 @@ type BlocksDeliverer interface { ) (blockChan <-chan *common.Block, closer func() error, err error) } +type ParsedBlocksDeliverer interface { + // ParsedBlocks the same as BlocksDeliverer.Blocks, but returns a channel with parsed blocks + ParsedBlocks( + ctx context.Context, + channel string, + identity msp.SigningIdentity, + blockRange ...int64, + ) (parsedBlockChan <-chan *hlfproto.Block, parsedCloser func() error, err error) +} + type Querier interface { CurrentIdentity // Query - shortcut for querying chaincodes diff --git a/api/config/config_yaml.go b/api/config/config_yaml.go index aef7290..661616c 100644 --- a/api/config/config_yaml.go +++ b/api/config/config_yaml.go @@ -1,14 +1,14 @@ package config import ( - "io/ioutil" + "os" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) func NewYamlConfig(configPath string) (*Config, error) { - if configBytes, err := ioutil.ReadFile(configPath); err != nil { + if configBytes, err := os.ReadFile(configPath); err != nil { return nil, errors.Wrap(err, `failed to read config file`) } else { var c Config diff --git a/api/peer.go b/api/peer.go index 9ed43a9..017ffa4 100644 --- a/api/peer.go +++ b/api/peer.go @@ -35,12 +35,14 @@ type Peer interface { BlocksDeliverer + ParsedBlocksDeliverer + EventsDeliverer // DeliverClient returns DeliverClient DeliverClient(identity msp.SigningIdentity) (DeliverClient, error) - // Uri returns url used for grpc connection - Uri() string + // URI returns url used for grpc connection + URI() string // Conn returns instance of grpc connection Conn() *grpc.ClientConn // Close terminates peer connection diff --git a/block/block.go b/block/block.go index 1e093ee..cb2a431 100644 --- a/block/block.go +++ b/block/block.go @@ -1,6 +1,7 @@ package block import ( + "errors" "fmt" "github.com/golang/protobuf/proto" @@ -14,6 +15,11 @@ import ( "github.com/s7techlab/hlf-sdk-go/block/txflags" ) +var ( + ErrNilBlock = errors.New("nil block") + ErrNilConfigBlock = errors.New("nil config block") +) + type ( parseBlockOpts struct { configBlock *common.Block @@ -71,6 +77,10 @@ func ParseBlock(block *common.Block, opts ...ParseBlockOpt) (*Block, error) { } func ParseOrdererIdentity(cb *common.Block) (*msp.SerializedIdentity, error) { + if cb == nil { + return nil, ErrNilBlock + } + meta, err := protoutil.GetMetadataFromBlock(cb, common.BlockMetadataIndex_SIGNATURES) if err != nil { return nil, fmt.Errorf("get metadata from block: %w", err) @@ -96,6 +106,14 @@ func ParseOrdererIdentity(cb *common.Block) (*msp.SerializedIdentity, error) { } func ParseBTFOrderersIdentities(block *common.Block, configBlock *common.Block) ([]*OrdererSignature, error) { + if block == nil { + return nil, ErrNilBlock + } + + if configBlock == nil { + return nil, ErrNilConfigBlock + } + bftMeta := &bftcommon.BFTMetadata{} if err := proto.Unmarshal(block.Metadata.Metadata[common.BlockMetadataIndex_SIGNATURES], bftMeta); err != nil { return nil, fmt.Errorf("unmarshaling bft block metadata from metadata: %w", err) diff --git a/block/block.pb.go b/block/block.pb.go index 15f58bd..f2b186c 100644 --- a/block/block.pb.go +++ b/block/block.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: block.proto diff --git a/block/chan_config.pb.go b/block/chan_config.pb.go index 0213c23..d8abd21 100644 --- a/block/chan_config.pb.go +++ b/block/chan_config.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: chan_config.proto diff --git a/block/smartbft/common/common.pb.go b/block/smartbft/common/common.pb.go index edee15a..0bdaa5a 100644 --- a/block/smartbft/common/common.pb.go +++ b/block/smartbft/common/common.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: smartbft/common/common.proto diff --git a/block/smartbft/configuration.pb.go b/block/smartbft/configuration.pb.go index 432cc2c..09a4a4d 100644 --- a/block/smartbft/configuration.pb.go +++ b/block/smartbft/configuration.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: smartbft/configuration.proto diff --git a/observer/transform/action.go b/block/transform/action.go similarity index 76% rename from observer/transform/action.go rename to block/transform/action.go index 66657fb..832a4f3 100644 --- a/observer/transform/action.go +++ b/block/transform/action.go @@ -4,10 +4,9 @@ import ( "fmt" "regexp" - "github.com/mohae/deepcopy" + "google.golang.org/protobuf/proto" hlfproto "github.com/s7techlab/hlf-sdk-go/block" - "github.com/s7techlab/hlf-sdk-go/observer" ) type ( @@ -68,77 +67,63 @@ func NewAction(actionMach TxActionMatch, opts ...ActionOpt) *Action { return a } -func (s *Action) Transform(block *observer.ParsedBlock) error { - if block.Block == nil { - return nil +func (s *Action) Transform(block *hlfproto.Block) (*hlfproto.Block, error) { + if block == nil { + return nil, hlfproto.ErrNilBlock } - // if block is transformed, copy of block will be saved to block.BlockOriginal - blockCopy := deepcopy.Copy(block.Block).(*hlfproto.Block) - blockIsTransformed := false + // make block copy not to change original + blockCopy := proto.Clone(block).(*hlfproto.Block) - for _, envelope := range block.Block.Data.Envelopes { - if envelope.Payload.Transaction == nil { + for _, envelope := range blockCopy.GetData().GetEnvelopes() { + if envelope.GetPayload().GetTransaction() == nil { continue } - for _, txAction := range envelope.Payload.Transaction.Actions { + for _, txAction := range envelope.GetPayload().GetTransaction().GetActions() { if !s.match(txAction) { continue } for _, argsTransformer := range s.inputArgsTransformers { - if err := argsTransformer.Transform(txAction.ChaincodeSpec().Input.Args); err != nil { - return fmt.Errorf(`transform input args: %w`, err) + if err := argsTransformer.Transform(txAction.ChaincodeSpec().GetInput().GetArgs()); err != nil { + return nil, fmt.Errorf(`transform input args: %w`, err) } } for _, eventTransformer := range s.eventTransformers { if err := eventTransformer.Transform(txAction.Event()); err != nil { - return fmt.Errorf(`transform event: %w`, err) + return nil, fmt.Errorf(`transform event: %w`, err) } } for _, rwSet := range txAction.NsReadWriteSet() { - for _, write := range rwSet.Rwset.Writes { + for _, write := range rwSet.GetRwset().GetWrites() { for _, kvWriteTransformer := range s.kvWriteTransformers { - origKey := write.Key if err := kvWriteTransformer.Transform(write); err != nil { - return fmt.Errorf(`transform KV write with key: %s: %w`, write.Key, err) - } - - if origKey != write.Key { - blockIsTransformed = true + return nil, fmt.Errorf(`transform KV write with key: %s: %w`, write.Key, err) } } } - for _, read := range rwSet.Rwset.Reads { + for _, read := range rwSet.GetRwset().GetReads() { for _, kvReadTransform := range s.kvReadTransformers { - origKey := read.Key if err := kvReadTransform.Transform(read); err != nil { - return fmt.Errorf(`transform KV read with key: %s: %w`, read.Key, err) - } - if origKey != read.Key { - blockIsTransformed = true + return nil, fmt.Errorf(`transform KV read with key: %s: %w`, read.Key, err) } } } for _, actionPayloadTransform := range s.actionPayloadTransformers { if err := actionPayloadTransform.Transform(txAction); err != nil { - return fmt.Errorf(`transform action payload: %w`, err) + return nil, fmt.Errorf(`transform action payload: %w`, err) } } } } } - if blockIsTransformed { - block.BlockOriginal = blockCopy - } - - return nil + return blockCopy, nil } func TxChaincodeIDMatch(chaincode string) TxActionMatch { diff --git a/observer/transform/action_payload.go b/block/transform/action_payload.go similarity index 100% rename from observer/transform/action_payload.go rename to block/transform/action_payload.go diff --git a/observer/transform/args.go b/block/transform/args.go similarity index 100% rename from observer/transform/args.go rename to block/transform/args.go diff --git a/observer/transform/event.go b/block/transform/event.go similarity index 100% rename from observer/transform/event.go rename to block/transform/event.go diff --git a/observer/transform/kvread.go b/block/transform/kvread.go similarity index 100% rename from observer/transform/kvread.go rename to block/transform/kvread.go diff --git a/observer/transform/kvwrite.go b/block/transform/kvwrite.go similarity index 100% rename from observer/transform/kvwrite.go rename to block/transform/kvwrite.go diff --git a/observer/transform/lifecycle.go b/block/transform/lifecycle.go similarity index 96% rename from observer/transform/lifecycle.go rename to block/transform/lifecycle.go index 8685829..b4c1197 100644 --- a/observer/transform/lifecycle.go +++ b/block/transform/lifecycle.go @@ -6,7 +6,7 @@ import ( "github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset" - "github.com/s7techlab/hlf-sdk-go/observer" + hlfproto "github.com/s7techlab/hlf-sdk-go/block" ) const ( @@ -53,7 +53,7 @@ func keyReplace(key string) string { return key } -var LifecycleTransformers = []observer.BlockTransformer{ +var LifecycleTransformers = []hlfproto.Transformer{ NewAction( TxChaincodeIDMatch(LifecycleChaincodeName), WithKVWriteTransformer( diff --git a/observer/transform/map_prefix.go b/block/transform/map_prefix.go similarity index 100% rename from observer/transform/map_prefix.go rename to block/transform/map_prefix.go diff --git a/observer/transform/proto.go b/block/transform/proto.go similarity index 100% rename from observer/transform/proto.go rename to block/transform/proto.go diff --git a/observer/transform/replace_bytes.go b/block/transform/replace_bytes.go similarity index 100% rename from observer/transform/replace_bytes.go rename to block/transform/replace_bytes.go diff --git a/block/transformer.go b/block/transformer.go new file mode 100644 index 0000000..3823630 --- /dev/null +++ b/block/transformer.go @@ -0,0 +1,6 @@ +package block + +// Transformer transforms parsed observer data. For example decrypt, or transformer protobuf state to json +type Transformer interface { + Transform(*Block) (*Block, error) +} diff --git a/client/ca/http/client.go b/client/ca/http/client.go index 9d2bf45..c41744c 100644 --- a/client/ca/http/client.go +++ b/client/ca/http/client.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "github.com/golang/protobuf/proto" @@ -92,7 +92,7 @@ func (c *Client) setAuthToken(req *http.Request, body []byte) error { func (c *Client) processResponse(resp *http.Response, out interface{}, expectedHTTPStatuses ...int) error { defer func() { _ = resp.Body.Close() }() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return errors.Wrap(err, `failed to read response body`) } diff --git a/client/chaincode/invoke_test.go b/client/chaincode/invoke_test.go index ceb7cc8..6d1ab49 100644 --- a/client/chaincode/invoke_test.go +++ b/client/chaincode/invoke_test.go @@ -160,8 +160,8 @@ package chaincode_test // return p.deliver, nil //} // -//// Uri returns url used for grpc connection -//func (p *mockPeer) Uri() string { +//// URI returns url used for grpc connection +//func (p *mockPeer) URI() string { // return "localhost:7051" //} // diff --git a/client/core_opts.go b/client/core_opts.go index 1607c9f..d4d08a4 100644 --- a/client/core_opts.go +++ b/client/core_opts.go @@ -2,7 +2,7 @@ package client import ( "fmt" - "io/ioutil" + "os" "github.com/hyperledger/fabric/msp" "github.com/pkg/errors" @@ -51,7 +51,7 @@ func WithOrderer(orderer api.Orderer) Opt { // WithConfigYaml allows passing path to YAML configuration file func WithConfigYaml(configPath string) Opt { return func(c *Client) error { - configBytes, err := ioutil.ReadFile(configPath) + configBytes, err := os.ReadFile(configPath) if err != nil { return errors.Wrap(err, `failed to read config file`) } diff --git a/client/core_public.go b/client/core_public.go index 934ac40..a63c8dc 100644 --- a/client/core_public.go +++ b/client/core_public.go @@ -10,6 +10,7 @@ import ( "github.com/hyperledger/fabric/msp" "github.com/s7techlab/hlf-sdk-go/api" + "github.com/s7techlab/hlf-sdk-go/block" "github.com/s7techlab/hlf-sdk-go/client/chaincode" "github.com/s7techlab/hlf-sdk-go/client/chaincode/txwaiter" "github.com/s7techlab/hlf-sdk-go/client/tx" @@ -108,7 +109,7 @@ func (c *Client) Blocks( channel string, identity msp.SigningIdentity, blockRange ...int64, -) (blocks <-chan *common.Block, closer func() error, _ error) { +) (<-chan *common.Block, func() error, error) { if identity == nil { identity = c.CurrentIdentity() } @@ -120,3 +121,21 @@ func (c *Client) Blocks( return peer.Blocks(ctx, channel, identity, blockRange...) } + +func (c *Client) ParsedBlocks( + ctx context.Context, + channel string, + identity msp.SigningIdentity, + blockRange ...int64, +) (<-chan *block.Block, func() error, error) { + if identity == nil { + identity = c.CurrentIdentity() + } + + peer, err := c.PeerPool().FirstReadyPeer(identity.GetMSPIdentifier()) + if err != nil { + return nil, nil, err + } + + return peer.ParsedBlocks(ctx, channel, identity, blockRange...) +} diff --git a/client/deliver/testing/block_deliverer_mock.go b/client/deliver/testing/block_deliverer_mock.go index babc03e..e8c8526 100644 --- a/client/deliver/testing/block_deliverer_mock.go +++ b/client/deliver/testing/block_deliverer_mock.go @@ -3,7 +3,6 @@ package testing import ( "context" "fmt" - "io/ioutil" "math" "os" "path/filepath" @@ -14,11 +13,14 @@ import ( "github.com/hyperledger/fabric-protos-go/common" "github.com/hyperledger/fabric/msp" "github.com/pkg/errors" + + hlfproto "github.com/s7techlab/hlf-sdk-go/block" ) type BlocksDelivererMock struct { // => [,...] data map[string][]*common.Block + parsedData map[string][]*hlfproto.Block closeWhenAllRead bool } @@ -31,6 +33,7 @@ func NewBlocksDelivererMock(rootPath string, closeWhenAllRead bool) (*BlocksDeli dc := &BlocksDelivererMock{ data: make(map[string][]*common.Block), + parsedData: make(map[string][]*hlfproto.Block), closeWhenAllRead: closeWhenAllRead, } @@ -78,7 +81,7 @@ func NewBlocksDelivererMock(rootPath string, closeWhenAllRead bool) (*BlocksDeli return err } - block, err := ioutil.ReadFile(path) + block, err := os.ReadFile(path) if err != nil { return err } @@ -95,15 +98,23 @@ func NewBlocksDelivererMock(rootPath string, closeWhenAllRead bool) (*BlocksDeli for channelID, data := range channels { channelBlocks := make([]*common.Block, len(data)) + parsedChannelBlocks := make([]*hlfproto.Block, len(data)) for blockID, blockData := range data { block := &common.Block{} - err := proto.Unmarshal(blockData, block) + err = proto.Unmarshal(blockData, block) if err != nil { return nil, err } channelBlocks[blockID] = block + + parsedBlock, err := hlfproto.ParseBlock(block) + if err != nil { + return nil, err + } + parsedChannelBlocks[blockID] = parsedBlock } dc.data[channelID] = channelBlocks + dc.parsedData[channelID] = parsedChannelBlocks println("fill channel '"+channelID+"' blocks from", 0, "...", len(channelBlocks)-1) } @@ -111,15 +122,30 @@ func NewBlocksDelivererMock(rootPath string, closeWhenAllRead bool) (*BlocksDeli } func (m *BlocksDelivererMock) Blocks( - ctx context.Context, + _ context.Context, channelName string, - identity msp.SigningIdentity, + _ msp.SigningIdentity, blockRange ...int64, -) (blockChan <-chan *common.Block, closer func() error, err error) { - if _, ok := m.data[channelName]; !ok { +) (<-chan *common.Block, func() error, error) { + + return blocks[*common.Block](m.data, channelName, m.closeWhenAllRead, blockRange...) +} + +func (m *BlocksDelivererMock) ParsedBlocks( + _ context.Context, + channelName string, + _ msp.SigningIdentity, + blockRange ...int64, +) (<-chan *hlfproto.Block, func() error, error) { + + return blocks[*hlfproto.Block](m.parsedData, channelName, m.closeWhenAllRead, blockRange...) +} + +func blocks[T any](data map[string][]T, channelName string, closeWhenAllRead bool, blockRange ...int64) (<-chan T, func() error, error) { + if _, ok := data[channelName]; !ok { return nil, nil, fmt.Errorf("have no mocked data for this channel") } - closer = func() error { return nil } + closer := func() error { return nil } var ( blockRangeFrom int64 = 0 @@ -134,27 +160,27 @@ func (m *BlocksDelivererMock) Blocks( } if blockRangeFrom < 0 { - blockRangeFrom = int64(len(m.data[channelName])) + blockRangeFrom + blockRangeFrom = int64(len(data[channelName])) + blockRangeFrom } if blockRangeTo < 0 { - blockRangeTo = int64(len(m.data[channelName])) + blockRangeTo + blockRangeTo = int64(len(data[channelName])) + blockRangeTo } - if blockRangeFrom > int64(len(m.data[channelName])) { - blockRangeFrom = int64(len(m.data[channelName])) - 1 + if blockRangeFrom > int64(len(data[channelName])) { + blockRangeFrom = int64(len(data[channelName])) - 1 } - if blockRangeTo > int64(len(m.data[channelName])) { - blockRangeTo = int64(len(m.data[channelName])) - 1 + if blockRangeTo > int64(len(data[channelName])) { + blockRangeTo = int64(len(data[channelName])) - 1 } - ch := make(chan *common.Block, (blockRangeTo-blockRangeFrom)+1) + ch := make(chan T, (blockRangeTo-blockRangeFrom)+1) for i := blockRangeFrom; i <= blockRangeTo; i++ { - ch <- m.data[channelName][i] + ch <- data[channelName][i] } - if m.closeWhenAllRead { + if closeWhenAllRead { close(ch) } diff --git a/client/deliver/testing/deliver.go b/client/deliver/testing/deliver.go index 9071ae4..bb7e372 100644 --- a/client/deliver/testing/deliver.go +++ b/client/deliver/testing/deliver.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "io/ioutil" "os" "path/filepath" "strconv" @@ -75,7 +74,7 @@ func NewDeliverClient(rootPath string, closeWhenAllRead bool) (peer.DeliverClien return err } - block, err := ioutil.ReadFile(path) + block, err := os.ReadFile(path) if err != nil { return err } diff --git a/client/grpc/grpc.go b/client/grpc/grpc.go index 88e5084..df53093 100644 --- a/client/grpc/grpc.go +++ b/client/grpc/grpc.go @@ -6,8 +6,8 @@ import ( "crypto/tls" "crypto/x509" "fmt" - "io/ioutil" "net" + "os" "time" grpcretry "github.com/grpc-ecosystem/go-grpc-middleware/retry" @@ -80,7 +80,7 @@ func OptionsFromConfig(c config.ConnectionConfig, logger *zap.Logger) (*Opts, er if len(c.Tls.CACert) != 0 { caCert = c.Tls.CACert } else { - caCert, err = ioutil.ReadFile(c.Tls.CACertPath) + caCert, err = os.ReadFile(c.Tls.CACertPath) if err != nil { return nil, fmt.Errorf(`read CA certificate: %w`, err) } diff --git a/client/peer.go b/client/peer.go index 6dce33a..8c361b6 100644 --- a/client/peer.go +++ b/client/peer.go @@ -3,6 +3,7 @@ package client import ( "context" "fmt" + "sync" "time" "github.com/golang/protobuf/ptypes/timestamp" @@ -16,6 +17,7 @@ import ( "github.com/s7techlab/hlf-sdk-go/api" "github.com/s7techlab/hlf-sdk-go/api/config" + "github.com/s7techlab/hlf-sdk-go/block" "github.com/s7techlab/hlf-sdk-go/client/channel" "github.com/s7techlab/hlf-sdk-go/client/deliver" grpcclient "github.com/s7techlab/hlf-sdk-go/client/grpc" @@ -39,11 +41,14 @@ type peer struct { endorseDefaultTimeout time.Duration + configBlocks map[string]*common.Block + mu sync.RWMutex + logger *zap.Logger } // NewPeer returns new peer instance based on peer config -func NewPeer(dialCtx context.Context, c config.ConnectionConfig, identity msp.SigningIdentity, logger *zap.Logger) (api.Peer, error) { +func NewPeer(ctx context.Context, c config.ConnectionConfig, identity msp.SigningIdentity, logger *zap.Logger) (api.Peer, error) { opts, err := grpcclient.OptionsFromConfig(c, logger) if err != nil { return nil, fmt.Errorf(`peer grpc options from config: %w`, err) @@ -55,10 +60,11 @@ func NewPeer(dialCtx context.Context, c config.ConnectionConfig, identity msp.Si } // Dial should always have timeout - ctxDeadline, exists := dialCtx.Deadline() + var dialCtx context.Context + ctxDeadline, exists := ctx.Deadline() if !exists { var cancel context.CancelFunc - dialCtx, cancel = context.WithTimeout(dialCtx, dialTimeout) + dialCtx, cancel = context.WithTimeout(ctx, dialTimeout) defer cancel() ctxDeadline, _ = dialCtx.Deadline() @@ -83,16 +89,15 @@ func NewFromGRPC(conn *grpc.ClientConn, identity msp.SigningIdentity, tlsCertHas endorseDefaultTimeout = PeerDefaultEndorseTimeout } - p := &peer{ + return &peer{ conn: conn, client: fabricPeer.NewEndorserClient(conn), identity: identity, tlsCertHash: tlsCertHash, endorseDefaultTimeout: endorseDefaultTimeout, + configBlocks: make(map[string]*common.Block), logger: logger.Named(`peer`), - } - - return p, nil + }, nil } func (p *peer) Query( @@ -140,9 +145,9 @@ func (p *peer) Query( return response.Response, nil } -func (p *peer) Blocks(ctx context.Context, channel string, identity msp.SigningIdentity, blockRange ...int64) (blockChan <-chan *common.Block, closer func() error, err error) { +func (p *peer) Blocks(ctx context.Context, channel string, identity msp.SigningIdentity, blockRange ...int64) (<-chan *common.Block, func() error, error) { p.logger.Debug(`peer blocks request`, - zap.String(`uri`, p.Uri()), + zap.String(`uri`, p.URI()), zap.String(`channel`, channel), zap.Reflect(`range`, blockRange)) @@ -169,6 +174,82 @@ func (p *peer) Blocks(ctx context.Context, channel string, identity msp.SigningI return bs.Blocks(), bs.Close, nil } +func (p *peer) addConfigBlock(ctx context.Context, channel string) error { + p.mu.RLock() + _, exist := p.configBlocks[channel] + p.mu.RUnlock() + if exist { + return nil + } + + configBlock, err := qscc.NewQSCC(p).GetBlockByNumber(ctx, &qscc.GetBlockByNumberRequest{ChannelName: channel, BlockNumber: 0}) + if err != nil { + return fmt.Errorf("get block by number from channel %s: %w", channel, err) + } + + if configBlock != nil { + p.mu.Lock() + p.configBlocks[channel] = configBlock + p.mu.Unlock() + } + + return nil +} + +func (p *peer) ParsedBlocks(ctx context.Context, channel string, identity msp.SigningIdentity, blockRange ...int64) (<-chan *block.Block, func() error, error) { + commonBlocks, commonCloser, err := p.Blocks(ctx, channel, identity, blockRange...) + if err != nil { + return nil, nil, err + } + + if err = p.addConfigBlock(ctx, channel); err != nil { + return nil, nil, err + } + + parsedBlockChan := make(chan *block.Block) + go func() { + defer func() { + close(parsedBlockChan) + }() + + p.mu.RLock() + configBlock := p.configBlocks[channel] + p.mu.RUnlock() + + for { + select { + case <-ctx.Done(): + return + + case b, ok := <-commonBlocks: + if !ok { + return + } + if b == nil { + return + } + + parsedBlock, err := block.ParseBlock(b, block.WithConfigBlock(configBlock)) + if err != nil { + p.logger.Error("parse block", zap.String("channel", channel), zap.Uint64("number", b.Header.Number)) + continue + } + + parsedBlockChan <- parsedBlock + } + } + }() + + parsedCloser := func() error { + if closerErr := commonCloser(); closerErr != nil { + return closerErr + } + return nil + } + + return parsedBlockChan, parsedCloser, nil +} + func (p *peer) Events(ctx context.Context, channel string, chaincode string, identity msp.SigningIdentity, blockRange ...int64) (events chan interface { Event() *fabricPeer.ChaincodeEvent Block() uint64 @@ -176,7 +257,7 @@ func (p *peer) Events(ctx context.Context, channel string, chaincode string, ide }, closer func() error, err error) { p.logger.Debug(`peer events request`, - zap.String(`uri`, p.Uri()), + zap.String(`uri`, p.URI()), zap.String(`channel`, channel), zap.Reflect(`range`, blockRange)) @@ -217,7 +298,7 @@ func (p *peer) Endorse(ctx context.Context, proposal *fabricPeer.SignedProposal) defer cancel() } - p.logger.Debug(`endorse`, zap.String(`uri`, p.Uri())) + p.logger.Debug(`endorse`, zap.String(`uri`, p.URI())) resp, err := p.client.ProcessProposal(ctx, proposal) if err != nil { @@ -247,7 +328,7 @@ func (p *peer) Conn() *grpc.ClientConn { return p.conn } -func (p *peer) Uri() string { +func (p *peer) URI() string { return p.conn.Target() } diff --git a/client/peer_pool.go b/client/peer_pool.go index f0fd3bb..5ad580f 100644 --- a/client/peer_pool.go +++ b/client/peer_pool.go @@ -76,7 +76,7 @@ func (p *PeerPool) GetMSPPeers(mspID string) []api.Peer { func (p *PeerPool) Add(mspId string, peer api.Peer, peerChecker api.PeerPoolCheckStrategy) error { p.logger.Debug(`add peer`, zap.String(`msp_id`, mspId), - zap.String(`peerUri`, peer.Uri())) + zap.String(`peer_URI`, peer.URI())) p.storeMx.Lock() defer p.storeMx.Unlock() @@ -101,7 +101,7 @@ func (p *PeerPool) addPeer(peer api.Peer, peerSet []*peerPoolPeer, peerChecker a func (p *PeerPool) isPeerInPool(peer api.Peer, peerSet []*peerPoolPeer) bool { for _, pp := range peerSet { - if peer.Uri() == pp.peer.Uri() { + if peer.URI() == pp.peer.URI() { return true } } @@ -119,13 +119,13 @@ func (p *PeerPool) poolChecker(ctx context.Context, aliveChan chan bool, peer *p return case alive, ok := <-aliveChan: - //log.Debug(`Got alive data about peer`, zap.String(`peerUri`, peer.peer.Uri()), zap.Bool(`alive`, alive)) + //log.Debug(`Got alive data about peer`, zap.String(`peerUri`, peer.peer.URI()), zap.Bool(`alive`, alive)) if !ok { return } if !alive { - p.logger.Warn(`peer connection is dead`, zap.String(`peerUri`, peer.peer.Uri())) + p.logger.Warn(`peer connection is dead`, zap.String(`peerUri`, peer.peer.URI())) } p.storeMx.Lock() @@ -157,13 +157,13 @@ func (p *PeerPool) EndorseOnMSP(ctx context.Context, mspID string, proposal *pee for pos, poolPeer := range peers { if !poolPeer.ready { - p.logger.Debug(ErrPeerNotReady.Error(), zap.String(`uri`, poolPeer.peer.Uri())) + p.logger.Debug(ErrPeerNotReady.Error(), zap.String(`uri`, poolPeer.peer.URI())) continue } log.Debug(`Sending endorse to peer...`, zap.String(`mspId`, mspID), - zap.String(`uri`, poolPeer.peer.Uri()), + zap.String(`uri`, poolPeer.peer.URI()), zap.Int(`peerPos`, pos), zap.Int(`peers in msp pool`, len(peers))) @@ -172,29 +172,29 @@ func (p *PeerPool) EndorseOnMSP(ctx context.Context, mspID string, proposal *pee // GRPC error if s, ok := status.FromError(err); ok { if s.Code() == codes.Unavailable { - log.Debug(`peer GRPC unavailable`, zap.String(`mspId`, mspID), zap.String(`peer_uri`, poolPeer.peer.Uri())) + log.Debug(`peer GRPC unavailable`, zap.String(`mspId`, mspID), zap.String(`peer_uri`, poolPeer.peer.URI())) //poolPeer.ready = false } else { log.Debug(`unexpected GRPC error code from peer`, - zap.String(`peer_uri`, poolPeer.peer.Uri()), zap.Uint32(`code`, uint32(s.Code())), + zap.String(`peer_uri`, poolPeer.peer.URI()), zap.Uint32(`code`, uint32(s.Code())), zap.String(`code_str`, s.Code().String()), zap.Error(s.Err())) // not mark as not ready } // next mspId peer - lastError = fmt.Errorf("peer %s: %w", poolPeer.peer.Uri(), err) + lastError = fmt.Errorf("peer %s: %w", poolPeer.peer.URI(), err) continue } log.Debug(`peer endorsement failed`, zap.String(`mspId`, mspID), - zap.String(`peer_uri`, poolPeer.peer.Uri()), + zap.String(`peer_uri`, poolPeer.peer.URI()), zap.String(`error`, err.Error())) - return propResp, errors.Wrap(err, poolPeer.peer.Uri()) + return propResp, errors.Wrap(err, poolPeer.peer.URI()) } - log.Debug(`endorse complete on peer`, zap.String(`mspId`, mspID), zap.String(`uri`, poolPeer.peer.Uri())) + log.Debug(`endorse complete on peer`, zap.String(`mspId`, mspID), zap.String(`uri`, poolPeer.peer.URI())) return propResp, nil } @@ -282,6 +282,8 @@ func (p *PeerPool) Close() error { func StrategyGRPC(d time.Duration) api.PeerPoolCheckStrategy { return func(ctx context.Context, peer api.Peer, alive chan bool) { t := time.NewTicker(d) + defer t.Stop() + for { select { case <-ctx.Done(): diff --git a/examples/caclient/main.go b/examples/caclient/main.go index cb9f05e..4436b7a 100644 --- a/examples/caclient/main.go +++ b/examples/caclient/main.go @@ -66,7 +66,7 @@ func main() { SerialNumber, CommonName string Names []pkix.AttributeTypeAndValue ExtraNames []pkix.AttributeTypeAndValue - }{Country: []string{`RU`}, Organization: []string{`S7`}, OrganizationalUnit: []string{`ORG`}, Locality: []string{`Moscow`}, Province: []string{`Moscow`}, StreetAddress: []string{`Пушкина 7`}, PostalCode: []string{`100001`}, CommonName: name}, + }{Country: []string{`RU`}, Organization: []string{`S7`}, OrganizationalUnit: []string{`ORG`}, Locality: []string{`Moscow`}, Province: []string{`Moscow`}, StreetAddress: []string{`Pushkin 7`}, PostalCode: []string{`100001`}, CommonName: name}, SignatureAlgorithm: x509.ECDSAWithSHA512}, )) } diff --git a/go.mod b/go.mod index 7a3dfbf..60471a4 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.19 require ( github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e - github.com/envoyproxy/protoc-gen-validate v1.0.2 - github.com/golang/protobuf v1.5.3 + github.com/envoyproxy/protoc-gen-validate v1.0.4 + github.com/golang/protobuf v1.5.4 github.com/google/certificate-transparency-go v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -14,18 +14,17 @@ require ( github.com/hyperledger/fabric-chaincode-go v0.0.0-20201119163726-f8ef75b17719 github.com/hyperledger/fabric-protos-go v0.0.0-20201028172056-a3136dde2354 github.com/mitchellh/mapstructure v1.5.0 - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/onsi/ginkgo v1.14.0 github.com/onsi/gomega v1.27.10 github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.8.1 - go.opencensus.io v0.22.0 + go.opencensus.io v0.22.5 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.18.0 golang.org/x/sync v0.4.0 - google.golang.org/genproto v0.0.0-20210122163508-8081c04a3579 - google.golang.org/grpc v1.33.1 - google.golang.org/protobuf v1.30.0 + google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 + google.golang.org/grpc v1.35.0 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v2 v2.3.0 ) @@ -93,7 +92,7 @@ require ( github.com/golang-jwt/jwt v3.2.1+incompatible // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.3.1 // indirect + github.com/golang/mock v1.4.4 // indirect github.com/golang/snappy v0.0.2 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect @@ -128,7 +127,6 @@ require ( github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.2 // indirect github.com/hashicorp/go-version v1.2.0 // indirect - github.com/hashicorp/golang-lru v0.5.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hyperledger/fabric-amcl v0.0.0-20200128223036-d1aa2665426a // indirect github.com/hyperledger/fabric-lib-go v1.0.0 // indirect @@ -169,7 +167,7 @@ require ( github.com/skeema/knownhosts v1.2.0 // indirect github.com/soheilhy/cmux v0.1.4 // indirect github.com/sourcegraph/go-diff v0.5.1 // indirect - github.com/spf13/afero v1.3.3 // indirect + github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v0.0.5 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect @@ -189,9 +187,9 @@ require ( go.etcd.io/etcd v3.3.13+incompatible // indirect go.uber.org/multierr v1.10.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect golang.org/x/tools v0.13.0 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect diff --git a/go.sum b/go.sum index 5fe18b5..1b3367e 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,50 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= code.cloudfoundry.org/clock v1.0.0/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/zstd v1.3.6-0.20190409195224-796139022798/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/DataDog/zstd v1.4.0 h1:vhoV+DUHnRZdKW1i5UMjAk2G4JY8wN4ayRfYDNdEhwo= github.com/DataDog/zstd v1.4.0/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= @@ -61,6 +97,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e h1:ZtyhUG4s94BMUCdgvRZySr/AXYL5CDcjxhIV/83xJog= github.com/cloudflare/cfssl v0.0.0-20190510060611-9c027c93ba9e/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= @@ -120,8 +159,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= -github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -143,6 +182,9 @@ github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgF github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -189,13 +231,20 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -242,16 +291,31 @@ github.com/google/certificate-transparency-go v1.1.0 h1:10MlrYzh5wfkToxWI4yJzffs github.com/google/certificate-transparency-go v1.1.0/go.mod h1:i+Q7XY+ArBveOUT36jiHGfuSK1fHICIg6sUkRxPAbCs= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/monologue v0.0.0-20190606152607-4b11a32b5934 h1:0+3qDY6030dpAiEdmBqIsz3lg2SgXAvPEEq2sjm5UBk= github.com/google/monologue v0.0.0-20190606152607-4b11a32b5934/go.mod h1:6NTfaQoUpg5QmPsCUWLR3ig33FHrKXhTtWzF0DVdmuk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/trillian v1.2.2-0.20190612132142-05461f4df60a/go.mod h1:YPmUVn5NGwgnDUgqlVyFGMTgaWlnSvH7W5p+NdOG8UA= @@ -260,6 +324,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -302,7 +368,6 @@ github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjG github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -325,6 +390,8 @@ github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mo github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= github.com/hyperledger/fabric-protos-go v0.0.0-20201028172056-a3136dde2354 h1:6vLLEpvDbSlmUJFjg1hB5YMBpI+WgKguztlONcAFBoY= github.com/hyperledger/fabric-protos-go v0.0.0-20201028172056-a3136dde2354/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd h1:anPrsicrIi2ColgWTVPk+TrN42hJIWlfPHSBP9S0ZkM= github.com/ijc/Gotty v0.0.0-20170406111628-a8b993ba6abd/go.mod h1:3LVOLeyx9XVvwPgrt2be44XgSqndprz1G18rSk8KD84= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= @@ -339,6 +406,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -408,7 +476,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -457,6 +524,7 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -521,8 +589,8 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.3.3 h1:p5gZEKLYoL7wh8VrJesMaYeNxdEd1v3cb4irOk9zB54= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= @@ -592,7 +660,9 @@ github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -601,8 +671,12 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd v0.5.0-alpha.5.0.20211015134708-72d3e382e73c h1:AntSYpWALzcqs1D6ZpSPIxhpob2hFnwAfEmfr9tEhSg= go.etcd.io/etcd v0.5.0-alpha.5.0.20211015134708-72d3e382e73c/go.mod h1:t1cqOhpjW3SEYhH7Wzlg51xzyIM2c5HMB9kvPO5k4gY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -626,25 +700,51 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -662,33 +762,59 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= @@ -708,23 +834,45 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200819091447-39769834ee22/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -733,29 +881,32 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI= golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -767,20 +918,57 @@ golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200131233409-575de47986ce/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= @@ -792,8 +980,30 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -801,18 +1011,47 @@ google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190605220351-eb0b1bdb6ae6/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210122163508-8081c04a3579 h1:Iwh0ba2kTgq2Q6mJiXhzrrjD7h11nEVnbMHFmp0/HsQ= -google.golang.org/genproto v0.0.0-20210122163508-8081c04a3579/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705 h1:PYBmACG+YEv8uQPW0r1kJj8tR+gkF0UWq7iFdUezwEw= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo= google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -847,12 +1086,17 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34 h1:duVSyluuJA+u0BnkcLR01smoLrGgDTfWt5c8ODYG8fU= mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= diff --git a/identity/msp.go b/identity/msp.go index dcd457e..8fddca6 100644 --- a/identity/msp.go +++ b/identity/msp.go @@ -182,9 +182,9 @@ func MSPFromPath(mspID, mspPath string, opts ...MSPOpt) (*MSP, error) { } } - if mspOpts.validateCertChain { - // todo: validate - } + // todo: validate + //if mspOpts.validateCertChain { + //} return mspInstance, nil } diff --git a/observer/README.md b/observer/README.md index ddf9f5b..5da5eb7 100644 --- a/observer/README.md +++ b/observer/README.md @@ -2,7 +2,8 @@ Main features: -* Block parsing to components (transactions, events, states etc) +* Stream of channel blocks from peer +* Stream of all channels blocks from peer * Auto reconnection when block or event stream interrupted -* Block and event transformation if needed +Every feature can be used for common block, also for parsed block from [block](../block/block.proto) diff --git a/observer/block.go b/observer/block.go new file mode 100644 index 0000000..39394f6 --- /dev/null +++ b/observer/block.go @@ -0,0 +1,6 @@ +package observer + +type Block[T any] struct { + Channel string + Block T +} diff --git a/observer/block_channel_common.go b/observer/block_channel_common.go deleted file mode 100644 index c5f579c..0000000 --- a/observer/block_channel_common.go +++ /dev/null @@ -1,201 +0,0 @@ -package observer - -import ( - "context" - "fmt" - - "github.com/hyperledger/fabric-protos-go/common" - "go.uber.org/zap" - - "github.com/s7techlab/hlf-sdk-go/api" -) - -type ( - BlockChannel struct { - *Channel - blocksDeliverer api.BlocksDeliverer - createStreamWithRetry CreateBlockStreamWithRetry - stopRecreateStream bool - - blocks chan *Block - - isWork bool - cancelObserve context.CancelFunc - } - - BlockChannelOpts struct { - *Opts - - createStreamWithRetry CreateBlockStreamWithRetry - - // don't recreate stream if it has not any blocks - stopRecreateStream bool - } - - BlockChannelOpt func(*BlockChannelOpts) -) - -func WithChannelBlockLogger(logger *zap.Logger) BlockChannelOpt { - return func(opts *BlockChannelOpts) { - opts.Opts.logger = logger - } -} - -func WithChannelStopRecreateStream(stop bool) BlockChannelOpt { - return func(opts *BlockChannelOpts) { - opts.stopRecreateStream = stop - } -} - -var DefaultBlockChannelOpts = &BlockChannelOpts{ - createStreamWithRetry: CreateBlockStreamWithRetryDelay(DefaultConnectRetryDelay), - Opts: DefaultOpts, -} - -func NewBlockChannel(channel string, blocksDeliver api.BlocksDeliverer, seekFromFetcher SeekFromFetcher, opts ...BlockChannelOpt) *BlockChannel { - blockChannelOpts := DefaultBlockChannelOpts - for _, opt := range opts { - opt(blockChannelOpts) - } - - observer := &BlockChannel{ - Channel: &Channel{ - channel: channel, - seekFromFetcher: seekFromFetcher, - identity: blockChannelOpts.identity, - logger: blockChannelOpts.logger.With(zap.String(`channel`, channel)), - }, - - blocksDeliverer: blocksDeliver, - createStreamWithRetry: blockChannelOpts.createStreamWithRetry, - stopRecreateStream: blockChannelOpts.stopRecreateStream, - } - - return observer -} - -func (c *BlockChannel) Observe(ctx context.Context) (<-chan *Block, error) { - c.mu.Lock() - defer c.mu.Unlock() - - if c.isWork { - return c.blocks, nil - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(ctx) - c.cancelObserve = cancel - - if err := c.allowToObserve(); err != nil { - return nil, err - } - - // Double check - if err := c.allowToObserve(); err != nil { - return nil, err - } - - c.blocks = make(chan *Block) - - go func() { - c.isWork = true - - c.logger.Debug(`creating block stream`) - incomingBlocks, errCreateStream := c.createStreamWithRetry(ctxObserve, c.createStream) - if errCreateStream != nil { - return - } - - c.logger.Info(`block stream created`) - for { - select { - case incomingBlock, hasMore := <-incomingBlocks: - - var err error - if !hasMore && !c.stopRecreateStream { - c.logger.Debug(`block stream interrupted, recreate`) - incomingBlocks, err = c.createStreamWithRetry(ctx, c.createStream) - if err != nil { - return - } - - c.logger.Debug(`block stream recreated`) - continue - } - - if incomingBlock == nil { - continue - } - - c.blocks <- &Block{ - Block: incomingBlock, - Channel: c.channel, - } - - case <-ctxObserve.Done(): - if err := c.Stop(); err != nil { - c.lastError = err - } - return - } - } - }() - - return c.blocks, nil -} - -func (c *BlockChannel) Stop() error { - c.mu.Lock() - defer c.mu.Unlock() - - // c.blocks mustn't be closed here, because it is closed elsewhere - - err := c.Channel.stop() - - // If primary context is done then cancel ctxObserver - if c.cancelObserve != nil { - c.cancelObserve() - } - - c.isWork = false - return err -} - -func (c *BlockChannel) createStream(ctx context.Context) (<-chan *common.Block, error) { - c.preCreateStream() - - c.logger.Debug(`connecting to blocks stream, receiving seek offset`, - zap.Uint64(`attempt`, c.connectAttempt)) - - seekFrom, err := c.processSeekFrom(ctx) - if err != nil { - c.logger.Warn(`seek from failed`, zap.Error(err)) - return nil, err - } - c.logger.Info(`block seek offset received`, zap.Uint64(`seek from`, seekFrom)) - - var ( - blocks <-chan *common.Block - closer func() error - ) - c.logger.Debug(`subscribing to blocks stream`) - blocks, closer, err = c.blocksDeliverer.Blocks(ctx, c.channel, c.identity, int64(seekFrom)) - if err != nil { - c.logger.Warn(`subscribing to blocks stream failed`, zap.Error(err)) - c.setError(err) - return nil, fmt.Errorf(`blocks deliverer: %w`, err) - } - c.logger.Info(`subscribed to blocks stream`) - - c.afterCreateStream(closer) - - // Check close context - select { - case <-ctx.Done(): - err = closer() - return nil, err - default: - } - - return blocks, nil -} diff --git a/observer/block_channel_parsed.go b/observer/block_channel_parsed.go deleted file mode 100644 index f75a659..0000000 --- a/observer/block_channel_parsed.go +++ /dev/null @@ -1,127 +0,0 @@ -package observer - -import ( - "context" - "fmt" - "sync" - - "github.com/hyperledger/fabric-protos-go/common" - "go.uber.org/zap" - - hlfproto "github.com/s7techlab/hlf-sdk-go/block" -) - -type ( - ParsedBlockChannel struct { - BlockChannel *BlockChannel - - transformers []BlockTransformer - configBlock *common.Block - - blocks chan *ParsedBlock - isWork bool - cancelObserve context.CancelFunc - mu sync.Mutex - } - - ParsedBlockChannelOpt func(*ParsedBlockChannel) -) - -func WithParsedChannelBlockTransformers(transformers []BlockTransformer) ParsedBlockChannelOpt { - return func(pbc *ParsedBlockChannel) { - pbc.transformers = transformers - } -} - -func WithParsedChannelConfigBlock(configBlock *common.Block) ParsedBlockChannelOpt { - return func(pbc *ParsedBlockChannel) { - pbc.configBlock = configBlock - } -} - -func NewParsedBlockChannel(blockChannel *BlockChannel, opts ...ParsedBlockChannelOpt) *ParsedBlockChannel { - parsedBlockChannel := &ParsedBlockChannel{ - BlockChannel: blockChannel, - } - - for _, opt := range opts { - opt(parsedBlockChannel) - } - - return parsedBlockChannel -} - -func (p *ParsedBlockChannel) Observe(ctx context.Context) (<-chan *ParsedBlock, error) { - p.mu.Lock() - defer p.mu.Unlock() - - if p.isWork { - return p.blocks, nil - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(ctx) - p.cancelObserve = cancel - - incomingBlocks, err := p.BlockChannel.Observe(ctxObserve) - if err != nil { - return nil, fmt.Errorf("observe common blocks: %w", err) - } - - p.blocks = make(chan *ParsedBlock) - - go func() { - p.isWork = true - - for { - select { - case incomingBlock, hasMore := <-incomingBlocks: - if !hasMore { - continue - } - - if incomingBlock == nil { - continue - } - - block := &ParsedBlock{ - Channel: p.BlockChannel.channel, - } - block.Block, block.Error = hlfproto.ParseBlock(incomingBlock.Block, hlfproto.WithConfigBlock(p.configBlock)) - - for pos, transformer := range p.transformers { - if err = transformer.Transform(block); err != nil { - p.BlockChannel.logger.Warn(`transformer`, zap.Int(`pos`, pos), zap.Error(err)) - } - } - - p.blocks <- block - - case <-ctxObserve.Done(): - if err = p.Stop(); err != nil { - p.BlockChannel.lastError = err - } - return - } - } - }() - - return p.blocks, nil -} - -func (p *ParsedBlockChannel) Stop() error { - p.mu.Lock() - defer p.mu.Unlock() - - // p.blocks mustn't be closed here, because it is closed elsewhere - - err := p.BlockChannel.Stop() - - // If primary context is done then cancel ctxObserver - if p.cancelObserve != nil { - p.cancelObserve() - } - - p.isWork = false - return err -} diff --git a/observer/block_peer_common.go b/observer/block_peer_common.go deleted file mode 100644 index f46de51..0000000 --- a/observer/block_peer_common.go +++ /dev/null @@ -1,246 +0,0 @@ -package observer - -import ( - "context" - "sync" - "time" - - "go.uber.org/zap" - - "github.com/s7techlab/hlf-sdk-go/api" -) - -const DefaultBlockPeerObservePeriod = 10 * time.Second - -type ( - BlockPeer struct { - mu sync.RWMutex - - peerChannels PeerChannels - blockDeliverer api.BlocksDeliverer - channelObservers map[string]*BlockPeerChannel - // seekFrom has a higher priority than seekFromFetcher (look getSeekFrom method) - seekFrom map[string]uint64 - seekFromFetcher SeekFromFetcher - observePeriod time.Duration - stopRecreateStream bool - logger *zap.Logger - - blocks chan *Block - blocksByChannels map[string]chan *Block - - isWork bool - cancelObserve context.CancelFunc - } - - BlockPeerChannel struct { - Observer *BlockChannel - err error - } - - BlockPeerOpts struct { - seekFrom map[string]uint64 - seekFromFetcher SeekFromFetcher - observePeriod time.Duration - stopRecreateStream bool - logger *zap.Logger - } - - BlockPeerOpt func(*BlockPeerOpts) - - ChannelStatus struct { - Status ChannelObserverStatus - Err error - } -) - -var DefaultBlockPeerOpts = &BlockPeerOpts{ - observePeriod: DefaultBlockPeerObservePeriod, - logger: zap.NewNop(), -} - -func WithBlockPeerLogger(logger *zap.Logger) BlockPeerOpt { - return func(opts *BlockPeerOpts) { - opts.logger = logger - } -} - -func WithSeekFrom(seekFrom map[string]uint64) BlockPeerOpt { - return func(opts *BlockPeerOpts) { - opts.seekFrom = seekFrom - } -} - -func WithSeekFromFetcher(seekFromFetcher SeekFromFetcher) BlockPeerOpt { - return func(opts *BlockPeerOpts) { - opts.seekFromFetcher = seekFromFetcher - } -} - -func WithBlockPeerObservePeriod(observePeriod time.Duration) BlockPeerOpt { - return func(opts *BlockPeerOpts) { - if observePeriod != 0 { - opts.observePeriod = observePeriod - } - } -} - -func WithBlockStopRecreateStream(stop bool) BlockPeerOpt { - return func(opts *BlockPeerOpts) { - opts.stopRecreateStream = stop - } -} - -func NewBlockPeer(peerChannels PeerChannels, blockDeliverer api.BlocksDeliverer, opts ...BlockPeerOpt) *BlockPeer { - blockPeerOpts := DefaultBlockPeerOpts - for _, opt := range opts { - opt(blockPeerOpts) - } - - blockPeer := &BlockPeer{ - peerChannels: peerChannels, - blockDeliverer: blockDeliverer, - channelObservers: make(map[string]*BlockPeerChannel), - blocks: make(chan *Block), - blocksByChannels: make(map[string]chan *Block), - seekFrom: blockPeerOpts.seekFrom, - seekFromFetcher: blockPeerOpts.seekFromFetcher, - observePeriod: blockPeerOpts.observePeriod, - stopRecreateStream: blockPeerOpts.stopRecreateStream, - logger: blockPeerOpts.logger, - } - - return blockPeer -} - -func (bp *BlockPeer) ChannelObservers() map[string]*BlockPeerChannel { - bp.mu.RLock() - defer bp.mu.RUnlock() - - var copyChannelObservers = make(map[string]*BlockPeerChannel, len(bp.channelObservers)) - for key, value := range bp.channelObservers { - copyChannelObservers[key] = value - } - - return copyChannelObservers -} - -func (bp *BlockPeer) Observe(ctx context.Context) <-chan *Block { - if bp.isWork { - return bp.blocks - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(ctx) - bp.cancelObserve = cancel - - bp.initChannels(ctxObserve) - - // init new channels if they are fetched - go func() { - bp.isWork = true - - ticker := time.NewTicker(bp.observePeriod) - for { - select { - case <-ctxObserve.Done(): - bp.Stop() - return - - case <-ticker.C: - bp.initChannels(ctxObserve) - } - } - }() - - return bp.blocks -} - -func (bp *BlockPeer) Stop() { - bp.mu.Lock() - defer bp.mu.Unlock() - - // bp.blocks and bp.blocksByChannels mustn't be closed here, because they are closed elsewhere - - for _, c := range bp.channelObservers { - if err := c.Observer.Stop(); err != nil { - zap.Error(err) - } - } - - bp.channelObservers = make(map[string]*BlockPeerChannel) - - if bp.cancelObserve != nil { - bp.cancelObserve() - } - - bp.isWork = false -} - -func (bp *BlockPeer) initChannels(ctx context.Context) { - for channel := range bp.peerChannels.Channels() { - bp.mu.RLock() - _, ok := bp.channelObservers[channel] - bp.mu.RUnlock() - - if !ok { - bp.logger.Info(`add channel observer`, zap.String(`channel`, channel)) - - blockPeerChannel := bp.peerChannel(ctx, channel) - - bp.mu.Lock() - bp.channelObservers[channel] = blockPeerChannel - bp.mu.Unlock() - } - } -} - -func (bp *BlockPeer) getSeekFrom(channel string) SeekFromFetcher { - seekFrom := ChannelSeekOldest() - // at first check seekFrom var, if it is empty, check seekFromFetcher - bp.mu.RLock() - seekFromNum, exist := bp.seekFrom[channel] - bp.mu.RUnlock() - if exist { - seekFrom = ChannelSeekFrom(seekFromNum - 1) - } else { - // if seekFromFetcher is also empty, use ChannelSeekOldest - if bp.seekFromFetcher != nil { - seekFrom = bp.seekFromFetcher - } - } - - return seekFrom -} - -func (bp *BlockPeer) peerChannel(ctx context.Context, channel string) *BlockPeerChannel { - seekFrom := bp.getSeekFrom(channel) - - peerChannel := &BlockPeerChannel{} - peerChannel.Observer = NewBlockChannel( - channel, - bp.blockDeliverer, - seekFrom, - WithChannelBlockLogger(bp.logger), - WithChannelStopRecreateStream(bp.stopRecreateStream)) - - _, peerChannel.err = peerChannel.Observer.Observe(ctx) - if peerChannel.err != nil { - bp.logger.Warn(`init channel observer`, zap.Error(peerChannel.err)) - } - - // channel merger - go func() { - for b := range peerChannel.Observer.blocks { - bp.blocks <- b - } - - // after all reads peerParsedChannel.observer.blocks close channels - close(bp.blocks) - for _, blocks := range bp.blocksByChannels { - close(blocks) - } - }() - - return peerChannel -} diff --git a/observer/block_peer_common_concurrently.go b/observer/block_peer_common_concurrently.go deleted file mode 100644 index 4cb1192..0000000 --- a/observer/block_peer_common_concurrently.go +++ /dev/null @@ -1,102 +0,0 @@ -package observer - -import ( - "context" - "time" - - "go.uber.org/zap" -) - -type ChannelCommonBlocks struct { - Name string - Blocks <-chan *Block -} - -type BlocksByChannels struct { - channels chan *ChannelCommonBlocks -} - -func (b *BlocksByChannels) Observe() chan *ChannelCommonBlocks { - return b.channels -} - -func (bp *BlockPeer) ObserveByChannels(ctx context.Context) *BlocksByChannels { - blocksByChannels := &BlocksByChannels{ - channels: make(chan *ChannelCommonBlocks), - } - - bp.initChannelsConcurrently(ctx, blocksByChannels) - - // init new channels if they are fetched - go func() { - ticker := time.NewTicker(bp.observePeriod) - for { - select { - case <-ctx.Done(): - return - - case <-ticker.C: - bp.initChannelsConcurrently(ctx, blocksByChannels) - } - } - }() - - // closer - go func() { - <-ctx.Done() - bp.Stop() - }() - - return blocksByChannels -} - -func (bp *BlockPeer) initChannelsConcurrently(ctx context.Context, blocksByChannels *BlocksByChannels) { - for channel := range bp.peerChannels.Channels() { - bp.mu.RLock() - _, ok := bp.channelObservers[channel] - bp.mu.RUnlock() - - if !ok { - bp.logger.Info(`add channel observer concurrently`, zap.String(`channel`, channel)) - - blockPeerChannel := bp.peerChannelConcurrently(ctx, channel, blocksByChannels) - - bp.mu.Lock() - bp.channelObservers[channel] = blockPeerChannel - bp.mu.Unlock() - } - } -} - -func (bp *BlockPeer) peerChannelConcurrently(ctx context.Context, channel string, blocksByChannels *BlocksByChannels) *BlockPeerChannel { - seekFrom := bp.getSeekFrom(channel) - - peerChannel := &BlockPeerChannel{} - peerChannel.Observer = NewBlockChannel( - channel, - bp.blockDeliverer, - seekFrom, - WithChannelBlockLogger(bp.logger), - WithChannelStopRecreateStream(bp.stopRecreateStream)) - - _, peerChannel.err = peerChannel.Observer.Observe(ctx) - if peerChannel.err != nil { - bp.logger.Warn(`init channel observer`, zap.Error(peerChannel.err)) - } - - blocks := make(chan *Block) - bp.blocksByChannels[channel] = blocks - - go func() { - blocksByChannels.channels <- &ChannelCommonBlocks{Name: channel, Blocks: blocks} - }() - - // channel merger - go func() { - for b := range peerChannel.Observer.blocks { - blocks <- b - } - }() - - return peerChannel -} diff --git a/observer/block_peer_parsed.go b/observer/block_peer_parsed.go deleted file mode 100644 index 704b32a..0000000 --- a/observer/block_peer_parsed.go +++ /dev/null @@ -1,188 +0,0 @@ -package observer - -import ( - "context" - "sync" - "time" - - "github.com/hyperledger/fabric-protos-go/common" - "go.uber.org/zap" -) - -type ( - ParsedBlockPeer struct { - mu sync.RWMutex - - blockPeer *BlockPeer - transformers []BlockTransformer - configBlocks map[string]*common.Block - - blocks chan *ParsedBlock - blocksByChannels map[string]chan *ParsedBlock - - parsedChannelObservers map[string]*ParsedBlockPeerChannel - - isWork bool - cancelObserve context.CancelFunc - } - - ParsedBlockPeerChannel struct { - Observer *ParsedBlockChannel - err error - } - - ParsedBlockPeerOpt func(*ParsedBlockPeer) -) - -func WithBlockPeerTransformer(transformers ...BlockTransformer) ParsedBlockPeerOpt { - return func(pbp *ParsedBlockPeer) { - pbp.transformers = transformers - } -} - -// WithConfigBlocks just for correct parsing of BFT at hlfproto.ParseBlock -func WithConfigBlocks(configBlocks map[string]*common.Block) ParsedBlockPeerOpt { - return func(pbp *ParsedBlockPeer) { - pbp.configBlocks = configBlocks - } -} - -func NewParsedBlockPeer(blocksPeer *BlockPeer, opts ...ParsedBlockPeerOpt) *ParsedBlockPeer { - parsedBlockPeer := &ParsedBlockPeer{ - blockPeer: blocksPeer, - parsedChannelObservers: make(map[string]*ParsedBlockPeerChannel), - blocks: make(chan *ParsedBlock), - blocksByChannels: make(map[string]chan *ParsedBlock), - } - - for _, opt := range opts { - opt(parsedBlockPeer) - } - - return parsedBlockPeer -} - -func (pbp *ParsedBlockPeer) ChannelObservers() map[string]*ParsedBlockPeerChannel { - pbp.mu.RLock() - defer pbp.mu.RUnlock() - - var copyChannelObservers = make(map[string]*ParsedBlockPeerChannel, len(pbp.parsedChannelObservers)) - for key, value := range pbp.parsedChannelObservers { - copyChannelObservers[key] = value - } - - return copyChannelObservers -} - -func (pbp *ParsedBlockPeer) Observe(ctx context.Context) <-chan *ParsedBlock { - if pbp.isWork { - return pbp.blocks - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(ctx) - pbp.cancelObserve = cancel - - pbp.initParsedChannels(ctxObserve) - - // init new channels if they are fetched - go func() { - pbp.isWork = true - - time.Sleep(time.Second) - - ticker := time.NewTicker(pbp.blockPeer.observePeriod) - for { - select { - case <-ctxObserve.Done(): - pbp.Stop() - return - - case <-ticker.C: - pbp.initParsedChannels(ctxObserve) - } - } - }() - - return pbp.blocks -} - -func (pbp *ParsedBlockPeer) Stop() { - pbp.mu.Lock() - defer pbp.mu.Unlock() - - // pbp.blocks and pbp.blocksByChannels mustn't be closed here, because they are closed elsewhere - - pbp.blockPeer.Stop() - - for _, c := range pbp.parsedChannelObservers { - if err := c.Observer.Stop(); err != nil { - zap.Error(err) - } - } - - pbp.parsedChannelObservers = make(map[string]*ParsedBlockPeerChannel) - - if pbp.cancelObserve != nil { - pbp.cancelObserve() - } - - pbp.isWork = false -} - -func (pbp *ParsedBlockPeer) initParsedChannels(ctx context.Context) { - for channel := range pbp.blockPeer.peerChannels.Channels() { - pbp.mu.RLock() - _, ok := pbp.parsedChannelObservers[channel] - pbp.mu.RUnlock() - - if !ok { - pbp.blockPeer.logger.Info(`add parsed channel observer`, zap.String(`channel`, channel)) - - parsedBlockPeerChannel := pbp.peerParsedChannel(ctx, channel) - - pbp.mu.Lock() - pbp.parsedChannelObservers[channel] = parsedBlockPeerChannel - pbp.mu.Unlock() - } - } -} - -func (pbp *ParsedBlockPeer) peerParsedChannel(ctx context.Context, channel string) *ParsedBlockPeerChannel { - seekFrom := pbp.blockPeer.getSeekFrom(channel) - - commonBlockChannel := NewBlockChannel( - channel, - pbp.blockPeer.blockDeliverer, - seekFrom, - WithChannelBlockLogger(pbp.blockPeer.logger), - WithChannelStopRecreateStream(pbp.blockPeer.stopRecreateStream)) - - configBlock := pbp.configBlocks[channel] - - peerParsedChannel := &ParsedBlockPeerChannel{} - peerParsedChannel.Observer = NewParsedBlockChannel( - commonBlockChannel, - WithParsedChannelBlockTransformers(pbp.transformers), - WithParsedChannelConfigBlock(configBlock)) - - _, peerParsedChannel.err = peerParsedChannel.Observer.Observe(ctx) - if peerParsedChannel.err != nil { - pbp.blockPeer.logger.Warn(`init parsed channel observer`, zap.Error(peerParsedChannel.err)) - } - - // channel merger - go func() { - for b := range peerParsedChannel.Observer.blocks { - pbp.blocks <- b - } - - // after all reads peerParsedChannel.observer.blocks close channels - close(pbp.blocks) - for _, blocks := range pbp.blocksByChannels { - close(blocks) - } - }() - - return peerParsedChannel -} diff --git a/observer/block_peer_parsed_concurrently.go b/observer/block_peer_parsed_concurrently.go deleted file mode 100644 index 4311f66..0000000 --- a/observer/block_peer_parsed_concurrently.go +++ /dev/null @@ -1,111 +0,0 @@ -package observer - -import ( - "context" - "time" - - "go.uber.org/zap" -) - -type ChannelParsedBlocks struct { - Name string - Blocks <-chan *ParsedBlock -} - -type ParsedBlocksByChannels struct { - channels chan *ChannelParsedBlocks -} - -func (p *ParsedBlocksByChannels) Observe() chan *ChannelParsedBlocks { - return p.channels -} - -func (pbp *ParsedBlockPeer) ObserveByChannels(ctx context.Context) *ParsedBlocksByChannels { - blocksByChannels := &ParsedBlocksByChannels{ - channels: make(chan *ChannelParsedBlocks), - } - - pbp.initParsedChannelsConcurrently(ctx, blocksByChannels) - - // init new channels if they are fetched - go func() { - pbp.isWork = true - - ticker := time.NewTicker(pbp.blockPeer.observePeriod) - for { - select { - case <-ctx.Done(): - return - - case <-ticker.C: - pbp.initParsedChannelsConcurrently(ctx, blocksByChannels) - } - } - }() - - // closer - go func() { - <-ctx.Done() - pbp.Stop() - }() - - return blocksByChannels -} - -func (pbp *ParsedBlockPeer) initParsedChannelsConcurrently(ctx context.Context, blocksByChannels *ParsedBlocksByChannels) { - for channel := range pbp.blockPeer.peerChannels.Channels() { - pbp.mu.RLock() - _, ok := pbp.parsedChannelObservers[channel] - pbp.mu.RUnlock() - - if !ok { - pbp.blockPeer.logger.Info(`add parsed channel observer concurrently`, zap.String(`channel`, channel)) - - parsedBlockPeerChannel := pbp.peerParsedChannelConcurrently(ctx, channel, blocksByChannels) - - pbp.mu.Lock() - pbp.parsedChannelObservers[channel] = parsedBlockPeerChannel - pbp.mu.Unlock() - } - } -} - -func (pbp *ParsedBlockPeer) peerParsedChannelConcurrently(ctx context.Context, channel string, blocksByChannels *ParsedBlocksByChannels) *ParsedBlockPeerChannel { - seekFrom := pbp.blockPeer.getSeekFrom(channel) - - commonBlockChannel := NewBlockChannel( - channel, - pbp.blockPeer.blockDeliverer, - seekFrom, - WithChannelBlockLogger(pbp.blockPeer.logger), - WithChannelStopRecreateStream(pbp.blockPeer.stopRecreateStream)) - - configBlock := pbp.configBlocks[channel] - - peerParsedChannel := &ParsedBlockPeerChannel{} - peerParsedChannel.Observer = NewParsedBlockChannel( - commonBlockChannel, - WithParsedChannelBlockTransformers(pbp.transformers), - WithParsedChannelConfigBlock(configBlock)) - - _, peerParsedChannel.err = peerParsedChannel.Observer.Observe(ctx) - if peerParsedChannel.err != nil { - pbp.blockPeer.logger.Warn(`init parsed channel observer`, zap.Error(peerParsedChannel.err)) - } - - blocks := make(chan *ParsedBlock) - pbp.blocksByChannels[channel] = blocks - - go func() { - blocksByChannels.channels <- &ChannelParsedBlocks{Name: channel, Blocks: blocks} - }() - - // channel merger - go func() { - for b := range peerParsedChannel.Observer.blocks { - blocks <- b - } - }() - - return peerParsedChannel -} diff --git a/observer/block_stream_common.go b/observer/block_stream_common.go deleted file mode 100644 index 7e21388..0000000 --- a/observer/block_stream_common.go +++ /dev/null @@ -1,38 +0,0 @@ -package observer - -import ( - "context" - "time" - - "github.com/hyperledger/fabric-protos-go/common" -) - -type ( - Block struct { - Block *common.Block - Channel string - } - - CreateBlockStream func(context.Context) (<-chan *common.Block, error) - - CreateBlockStreamWithRetry func(context.Context, CreateBlockStream) (<-chan *common.Block, error) -) - -func CreateBlockStreamWithRetryDelay(delay time.Duration) CreateBlockStreamWithRetry { - return func(ctx context.Context, createBlockStream CreateBlockStream) (<-chan *common.Block, error) { - for { - select { - case <-ctx.Done(): - return nil, nil - default: - } - - blocks, err := createBlockStream(ctx) - if err == nil { - return blocks, nil - } - - time.Sleep(delay) - } - } -} diff --git a/observer/block_stream_parsed.go b/observer/block_stream_parsed.go deleted file mode 100644 index 884f80b..0000000 --- a/observer/block_stream_parsed.go +++ /dev/null @@ -1,14 +0,0 @@ -package observer - -import ( - hlfproto "github.com/s7techlab/hlf-sdk-go/block" -) - -type ( - ParsedBlock struct { - Block *hlfproto.Block // parsed block - BlockOriginal *hlfproto.Block // here is original block before transformation if it is, otherwise it's nil - Channel string - Error error - } -) diff --git a/observer/channel_blocks.go b/observer/channel_blocks.go new file mode 100644 index 0000000..644a068 --- /dev/null +++ b/observer/channel_blocks.go @@ -0,0 +1,220 @@ +package observer + +import ( + "context" + "fmt" + + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/msp" + "go.uber.org/zap" + + hlfproto "github.com/s7techlab/hlf-sdk-go/block" +) + +type ( + ChannelBlocks[T any] struct { + // pointer is to use Channel's data, which can be changed + *Channel + + channelWithBlocks chan *Block[T] + blocksDeliverer func(context.Context, string, msp.SigningIdentity, ...int64) (<-chan T, func() error, error) + createStreamWithRetry CreateBlockStreamWithRetry[T] + + stopRecreateStream bool + + isWork bool + cancelObserve context.CancelFunc + } + + ChannelBlocksOpts struct { + *Opts + + // don't recreate stream if it has not any blocks + stopRecreateStream bool + } + + ChannelBlocksOpt func(*ChannelBlocksOpts) +) + +func WithChannelBlockLogger(logger *zap.Logger) ChannelBlocksOpt { + return func(opts *ChannelBlocksOpts) { + opts.Opts.logger = logger + } +} + +func WithChannelStopRecreateStream(stop bool) ChannelBlocksOpt { + return func(opts *ChannelBlocksOpts) { + opts.stopRecreateStream = stop + } +} + +var DefaultChannelBlocksOpts = &ChannelBlocksOpts{ + Opts: DefaultOpts, + stopRecreateStream: false, +} + +func NewChannelBlocks[T any]( + channel string, + deliverer func(context.Context, string, msp.SigningIdentity, ...int64) (<-chan T, func() error, error), + createStreamWithRetry CreateBlockStreamWithRetry[T], + seekFromFetcher SeekFromFetcher, + opts ...ChannelBlocksOpt, +) *ChannelBlocks[T] { + + channelBlocksOpts := DefaultChannelBlocksOpts + for _, opt := range opts { + opt(channelBlocksOpts) + } + + return &ChannelBlocks[T]{ + Channel: &Channel{ + channel: channel, + seekFromFetcher: seekFromFetcher, + identity: channelBlocksOpts.identity, + logger: channelBlocksOpts.logger.With(zap.String(`channel`, channel)), + }, + + blocksDeliverer: deliverer, + createStreamWithRetry: createStreamWithRetry, + stopRecreateStream: channelBlocksOpts.stopRecreateStream, + } +} + +func (cb *ChannelBlocks[T]) Stop() error { + cb.mu.Lock() + defer cb.mu.Unlock() + + // cb.channelWithBlocks mustn't be closed here, because it is closed elsewhere + + err := cb.Channel.stop() + + // If primary context is done then cancel ctxObserver + if cb.cancelObserve != nil { + cb.cancelObserve() + } + + cb.isWork = false + return err +} + +func (cb *ChannelBlocks[T]) Observe(ctx context.Context) (<-chan *Block[T], error) { + cb.mu.Lock() + defer cb.mu.Unlock() + + if cb.isWork { + return cb.channelWithBlocks, nil + } + + // ctxObserve using for nested control process without stopped primary context + ctxObserve, cancel := context.WithCancel(ctx) + cb.cancelObserve = cancel + + if err := cb.allowToObserve(); err != nil { + return nil, err + } + + // Double check + if err := cb.allowToObserve(); err != nil { + return nil, err + } + + cb.channelWithBlocks = make(chan *Block[T]) + + go func() { + cb.isWork = true + + defer close(cb.channelWithBlocks) + + cb.logger.Debug(`creating block stream`) + incomingBlocks, errCreateStream := cb.createStreamWithRetry(ctxObserve, cb.createStream) + if errCreateStream != nil { + return + } + + cb.logger.Info(`block stream created`) + for { + select { + case incomingBlock, hasMore := <-incomingBlocks: + + var err error + if !hasMore && !cb.stopRecreateStream { + cb.logger.Debug(`block stream interrupted, recreate`) + incomingBlocks, err = cb.createStreamWithRetry(ctx, cb.createStream) + if err != nil { + return + } + + cb.logger.Debug(`block stream recreated`) + continue + } + + switch t := any(incomingBlock).(type) { + case *common.Block: + if t == nil { + continue + } + + case *hlfproto.Block: + if t == nil { + continue + } + + default: + continue + } + + cb.channelWithBlocks <- &Block[T]{ + Channel: cb.channel, + Block: incomingBlock, + } + + case <-ctxObserve.Done(): + if err := cb.Stop(); err != nil { + cb.lastError = err + } + return + } + } + }() + + return cb.channelWithBlocks, nil +} + +func (cb *ChannelBlocks[T]) createStream(ctx context.Context) (<-chan T, error) { + cb.preCreateStream() + + cb.logger.Debug(`connecting to blocks stream, receiving seek offset`, + zap.Uint64(`attempt`, cb.connectAttempt)) + + seekFrom, err := cb.processSeekFrom(ctx) + if err != nil { + cb.logger.Warn(`seek from failed`, zap.Error(err)) + return nil, err + } + cb.logger.Info(`block seek offset received`, zap.Uint64(`seek from`, seekFrom)) + + var ( + blocks <-chan T + closer func() error + ) + cb.logger.Debug(`subscribing to blocks stream`) + blocks, closer, err = cb.blocksDeliverer(ctx, cb.channel, cb.identity, int64(seekFrom)) + if err != nil { + cb.logger.Warn(`subscribing to blocks stream failed`, zap.Error(err)) + cb.setError(err) + return nil, fmt.Errorf(`blocks deliverer: %w`, err) + } + cb.logger.Info(`subscribed to blocks stream`) + + cb.afterCreateStream(closer) + + // Check close context + select { + case <-ctx.Done(): + err = closer() + return nil, err + default: + } + + return blocks, nil +} diff --git a/observer/channel_blocks_common.go b/observer/channel_blocks_common.go new file mode 100644 index 0000000..f421885 --- /dev/null +++ b/observer/channel_blocks_common.go @@ -0,0 +1,21 @@ +package observer + +import ( + "github.com/hyperledger/fabric-protos-go/common" + + "github.com/s7techlab/hlf-sdk-go/api" +) + +type ( + ChannelBlocksCommon struct { + *ChannelBlocks[*common.Block] + } +) + +func NewChannelBlocksCommon(channel string, blocksDeliver api.BlocksDeliverer, seekFromFetcher SeekFromFetcher, opts ...ChannelBlocksOpt) *ChannelBlocksCommon { + createStreamWithRetry := CreateBlockStreamWithRetryDelay[*common.Block](DefaultConnectRetryDelay) + + chBlocks := NewChannelBlocks[*common.Block](channel, blocksDeliver.Blocks, createStreamWithRetry, seekFromFetcher, opts...) + + return &ChannelBlocksCommon{ChannelBlocks: chBlocks} +} diff --git a/observer/channel_blocks_parsed.go b/observer/channel_blocks_parsed.go new file mode 100644 index 0000000..eb3dc3c --- /dev/null +++ b/observer/channel_blocks_parsed.go @@ -0,0 +1,20 @@ +package observer + +import ( + "github.com/s7techlab/hlf-sdk-go/api" + hlfproto "github.com/s7techlab/hlf-sdk-go/block" +) + +type ( + ChannelBlocksParsed struct { + *ChannelBlocks[*hlfproto.Block] + } +) + +func NewChannelBlocksParsed(channel string, blocksDeliver api.ParsedBlocksDeliverer, seekFromFetcher SeekFromFetcher, opts ...ChannelBlocksOpt) *ChannelBlocksParsed { + createStreamWithRetry := CreateBlockStreamWithRetryDelay[*hlfproto.Block](DefaultConnectRetryDelay) + + chBlocks := NewChannelBlocks[*hlfproto.Block](channel, blocksDeliver.ParsedBlocks, createStreamWithRetry, seekFromFetcher, opts...) + + return &ChannelBlocksParsed{ChannelBlocks: chBlocks} +} diff --git a/observer/channel_blocks_stream.go b/observer/channel_blocks_stream.go new file mode 100644 index 0000000..90040e8 --- /dev/null +++ b/observer/channel_blocks_stream.go @@ -0,0 +1,31 @@ +package observer + +import ( + "context" + "time" +) + +type ( + CreateBlockStream[T any] func(context.Context) (<-chan T, error) + + CreateBlockStreamWithRetry[T any] func(context.Context, CreateBlockStream[T]) (<-chan T, error) +) + +func CreateBlockStreamWithRetryDelay[T any](delay time.Duration) CreateBlockStreamWithRetry[T] { + return func(ctx context.Context, createBlockStream CreateBlockStream[T]) (<-chan T, error) { + for { + select { + case <-ctx.Done(): + return nil, nil + default: + } + + blocks, err := createBlockStream(ctx) + if err == nil { + return blocks, nil + } + + time.Sleep(delay) + } + } +} diff --git a/observer/channel_peer.go b/observer/channel_peer.go deleted file mode 100644 index 28aee14..0000000 --- a/observer/channel_peer.go +++ /dev/null @@ -1,204 +0,0 @@ -package observer - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/golang/protobuf/ptypes" - "github.com/golang/protobuf/ptypes/timestamp" - "github.com/hyperledger/fabric-protos-go/common" - "go.uber.org/zap" - "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/s7techlab/hlf-sdk-go/api" -) - -const DefaultChannelPeerObservePeriod = 30 * time.Second - -type ( - ChannelInfo struct { - Channel string - Height uint64 - UpdatedAt *timestamppb.Timestamp - } - - // ChannelPeer observes for peer channels - ChannelPeer struct { - channelFetcher PeerChannelsFetcher - - channelsMatcher *ChannelsMatcher - - channels map[string]*ChannelInfo - observePeriod time.Duration - - lastError error - mu sync.Mutex - logger *zap.Logger - - isWork bool - cancelObserve context.CancelFunc - } - - PeerChannelsFetcher interface { - Uri() string - api.ChannelListGetter - api.ChainInfoGetter - } - - PeerChannels interface { - Uri() string - Channels() map[string]*ChannelInfo - } - - ChannelPeerOpts struct { - channels []ChannelToMatch - observePeriod time.Duration - logger *zap.Logger - } - - ChannelPeerOpt func(*ChannelPeerOpts) -) - -var DefaultChannelPeerOpts = &ChannelPeerOpts{ - channels: MatchAllChannels, - observePeriod: DefaultChannelPeerObservePeriod, - logger: zap.NewNop(), -} - -func WithChannels(channels []ChannelToMatch) ChannelPeerOpt { - return func(opts *ChannelPeerOpts) { - opts.channels = channels - } -} - -func WithChannelPeerLogger(logger *zap.Logger) ChannelPeerOpt { - return func(opts *ChannelPeerOpts) { - opts.logger = logger - } -} - -func NewChannelPeer(peerChannelsFetcher PeerChannelsFetcher, opts ...ChannelPeerOpt) (*ChannelPeer, error) { - channelPeerOpts := DefaultChannelPeerOpts - for _, opt := range opts { - opt(channelPeerOpts) - } - - channelsMatcher, err := NewChannelsMatcher(channelPeerOpts.channels) - if err != nil { - return nil, fmt.Errorf(`channels matcher: %w`, err) - } - - channelPeer := &ChannelPeer{ - channelFetcher: peerChannelsFetcher, - channelsMatcher: channelsMatcher, - channels: make(map[string]*ChannelInfo), - observePeriod: channelPeerOpts.observePeriod, - logger: channelPeerOpts.logger, - } - - return channelPeer, nil -} - -func (cp *ChannelPeer) Stop() { - cp.cancelObserve() - cp.isWork = false -} - -func (cp *ChannelPeer) Observe(ctx context.Context) { - if cp.isWork { - return - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(context.Background()) - cp.cancelObserve = cancel - - go func() { - cp.isWork = true - cp.updateChannels(ctxObserve) - - ticker := time.NewTicker(cp.observePeriod) - for { - select { - case <-ctx.Done(): - // If primary context is done then cancel ctxObserver - cp.cancelObserve() - return - - case <-ctxObserve.Done(): - return - - case <-ticker.C: - cp.updateChannels(ctxObserve) - } - } - }() -} - -func (cp *ChannelPeer) Uri() string { - return cp.channelFetcher.Uri() -} - -func (cp *ChannelPeer) Channels() map[string]*ChannelInfo { - cp.mu.Lock() - defer cp.mu.Unlock() - - var copyChannelInfo = make(map[string]*ChannelInfo, len(cp.channels)) - for key, value := range cp.channels { - copyChannelInfo[key] = value - } - - return copyChannelInfo -} - -func (cp *ChannelPeer) updateChannels(ctx context.Context) { - cp.logger.Debug(`fetching channels`) - channelsInfo, err := cp.channelFetcher.GetChannels(ctx) - if err != nil { - cp.logger.Warn(`error while fetching channels`, zap.Error(err)) - cp.lastError = err - return - } - - channels := ChannelsInfoToStrings(channelsInfo.Channels) - cp.logger.Debug(`channels fetched`, zap.Strings(`channels`, channels)) - - channelsMatched, err := cp.channelsMatcher.Match(channels) - if err != nil { - cp.logger.Warn(`channel matching error`, zap.Error(err)) - cp.lastError = err - return - } - cp.logger.Debug(`channels matched`, zap.Reflect(`channels`, channelsMatched)) - - channelHeights := make(map[string]uint64) - - for _, channel := range channelsMatched { - var channelInfo *common.BlockchainInfo - channelInfo, err = cp.channelFetcher.GetChainInfo(ctx, channel.Name) - if err != nil { - cp.lastError = err - continue - } - channelHeights[channel.Name] = channelInfo.Height - } - - cp.mu.Lock() - defer cp.mu.Unlock() - - for channel, height := range channelHeights { - var updatedAt *timestamp.Timestamp - updatedAt, err = ptypes.TimestampProto(time.Now()) - if err != nil { - cp.lastError = err - } - - cp.channels[channel] = &ChannelInfo{ - Channel: channel, - Height: height, - UpdatedAt: updatedAt, - } - } -} diff --git a/observer/channel_peer_fetcher_mock.go b/observer/channel_peer_fetcher_mock.go deleted file mode 100644 index ba7c1a1..0000000 --- a/observer/channel_peer_fetcher_mock.go +++ /dev/null @@ -1,45 +0,0 @@ -package observer - -import ( - "context" - "fmt" - - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/peer" -) - -type ChannelPeerFetcherMock struct { - channels map[string]uint64 -} - -func NewChannelPeerFetcherMock(channels map[string]uint64) *ChannelPeerFetcherMock { - return &ChannelPeerFetcherMock{ - channels: channels, - } -} - -func (c *ChannelPeerFetcherMock) Uri() string { - return "mock" -} - -func (c *ChannelPeerFetcherMock) GetChannels(context.Context) (*peer.ChannelQueryResponse, error) { - var channels []*peer.ChannelInfo - for channelName := range c.channels { - channels = append(channels, &peer.ChannelInfo{ChannelId: channelName}) - } - - return &peer.ChannelQueryResponse{ - Channels: channels, - }, nil -} - -func (c *ChannelPeerFetcherMock) GetChainInfo(_ context.Context, channel string) (*common.BlockchainInfo, error) { - chHeight, exists := c.channels[channel] - if !exists { - return nil, fmt.Errorf("channel '%s' does not exist", channel) - } - - return &common.BlockchainInfo{ - Height: chHeight, - }, nil -} diff --git a/observer/channel_peer_mock.go b/observer/channel_peer_mock.go deleted file mode 100644 index db86bb8..0000000 --- a/observer/channel_peer_mock.go +++ /dev/null @@ -1,44 +0,0 @@ -package observer - -import ( - "sync" -) - -type ChannelPeerMock struct { - mu sync.Mutex - channelsInfo map[string]*ChannelInfo -} - -func (m *ChannelPeerMock) Uri() string { - return "mock" -} - -func NewChannelPeerMock(channelsInfo ...*ChannelInfo) *ChannelPeerMock { - channels := make(map[string]*ChannelInfo, len(channelsInfo)) - for _, channelInfo := range channelsInfo { - channels[channelInfo.Channel] = channelInfo - } - - return &ChannelPeerMock{ - channelsInfo: channels, - } -} - -func (m *ChannelPeerMock) Channels() map[string]*ChannelInfo { - m.mu.Lock() - defer m.mu.Unlock() - - var copyChannelInfo = make(map[string]*ChannelInfo, len(m.channelsInfo)) - for key, value := range m.channelsInfo { - copyChannelInfo[key] = value - } - - return copyChannelInfo -} - -func (m *ChannelPeerMock) UpdateChannelInfo(channelInfo *ChannelInfo) { - m.mu.Lock() - defer m.mu.Unlock() - - m.channelsInfo[channelInfo.Channel] = channelInfo -} diff --git a/observer/channels_blocks_peer.go b/observer/channels_blocks_peer.go new file mode 100644 index 0000000..686d573 --- /dev/null +++ b/observer/channels_blocks_peer.go @@ -0,0 +1,251 @@ +package observer + +import ( + "context" + "sync" + "time" + + "github.com/hyperledger/fabric/msp" + "go.uber.org/zap" +) + +const DefaultChannelsBLocksPeerRefreshPeriod = 10 * time.Second + +type ( + ChannelsBlocksPeer[T any] struct { + channelObservers map[string]*ChannelBlocks[T] + + blocks chan *Block[T] + + peerChannelsGetter PeerChannelsGetter + deliverer func(context.Context, string, msp.SigningIdentity, ...int64) (<-chan T, func() error, error) + createStreamWithRetry CreateBlockStreamWithRetry[T] + + refreshPeriod time.Duration + + // seekFrom has a higher priority than seekFromFetcher (look getSeekFrom method) + seekFrom map[string]uint64 + seekFromFetcher SeekFromFetcher + stopRecreateStream bool + + isWork bool + cancelObserve context.CancelFunc + + mu sync.RWMutex + logger *zap.Logger + } + + ChannelsBlocksPeerOpts struct { + seekFrom map[string]uint64 + seekFromFetcher SeekFromFetcher + refreshPeriod time.Duration + stopRecreateStream bool + logger *zap.Logger + } + + ChannelsBlocksPeerOpt func(*ChannelsBlocksPeerOpts) +) + +var DefaultChannelsBlocksPeerOpts = &ChannelsBlocksPeerOpts{ + refreshPeriod: DefaultChannelsBLocksPeerRefreshPeriod, + logger: zap.NewNop(), +} + +func WithChannelsBlocksPeerLogger(logger *zap.Logger) ChannelsBlocksPeerOpt { + return func(opts *ChannelsBlocksPeerOpts) { + opts.logger = logger + } +} + +func WithSeekFrom(seekFrom map[string]uint64) ChannelsBlocksPeerOpt { + return func(opts *ChannelsBlocksPeerOpts) { + opts.seekFrom = seekFrom + } +} + +func WithSeekFromFetcher(seekFromFetcher SeekFromFetcher) ChannelsBlocksPeerOpt { + return func(opts *ChannelsBlocksPeerOpts) { + opts.seekFromFetcher = seekFromFetcher + } +} + +func WithChannelsBlocksPeerRefreshPeriod(refreshPeriod time.Duration) ChannelsBlocksPeerOpt { + return func(opts *ChannelsBlocksPeerOpts) { + if refreshPeriod != 0 { + opts.refreshPeriod = refreshPeriod + } + } +} + +func WithBlockStopRecreateStream(stop bool) ChannelsBlocksPeerOpt { + return func(opts *ChannelsBlocksPeerOpts) { + opts.stopRecreateStream = stop + } +} + +func NewChannelsBlocksPeer[T any]( + peerChannelsGetter PeerChannelsGetter, + deliverer func(context.Context, string, msp.SigningIdentity, ...int64) (<-chan T, func() error, error), + createStreamWithRetry CreateBlockStreamWithRetry[T], + opts ...ChannelsBlocksPeerOpt, +) *ChannelsBlocksPeer[T] { + + channelsBlocksPeerOpts := DefaultChannelsBlocksPeerOpts + for _, opt := range opts { + opt(channelsBlocksPeerOpts) + } + + return &ChannelsBlocksPeer[T]{ + channelObservers: make(map[string]*ChannelBlocks[T]), + blocks: make(chan *Block[T]), + + peerChannelsGetter: peerChannelsGetter, + deliverer: deliverer, + createStreamWithRetry: createStreamWithRetry, + refreshPeriod: channelsBlocksPeerOpts.refreshPeriod, + + seekFrom: channelsBlocksPeerOpts.seekFrom, + seekFromFetcher: channelsBlocksPeerOpts.seekFromFetcher, + stopRecreateStream: channelsBlocksPeerOpts.stopRecreateStream, + logger: channelsBlocksPeerOpts.logger, + } +} + +func (acb *ChannelsBlocksPeer[T]) Channels() map[string]*Channel { + acb.mu.RLock() + defer acb.mu.RUnlock() + + var copyChannels = make(map[string]*Channel, len(acb.channelObservers)) + for key, value := range acb.channelObservers { + copyChannels[key] = value.Channel + } + + return copyChannels +} + +func (acb *ChannelsBlocksPeer[T]) Stop() { + // acb.blocks and acb.blocksByChannels mustn't be closed here, because they are closed elsewhere + + acb.mu.RLock() + for _, c := range acb.channelObservers { + if err := c.Stop(); err != nil { + zap.Error(err) + } + } + acb.mu.RUnlock() + + acb.mu.Lock() + acb.channelObservers = make(map[string]*ChannelBlocks[T]) + acb.mu.Unlock() + + if acb.cancelObserve != nil { + acb.cancelObserve() + } + + acb.isWork = false +} + +func (acb *ChannelsBlocksPeer[T]) Observe(ctx context.Context) <-chan *Block[T] { + if acb.isWork { + return acb.blocks + } + + // ctxObserve using for nested control process without stopped primary context + ctxObserve, cancel := context.WithCancel(ctx) + acb.cancelObserve = cancel + + acb.startNotObservedChannels(ctxObserve, acb.initChannelsObservers()) + + acb.blocks = make(chan *Block[T]) + + // init new channels if they are fetched + go func() { + acb.isWork = true + + ticker := time.NewTicker(acb.refreshPeriod) + defer func() { + ticker.Stop() + close(acb.blocks) + }() + + for { + select { + case <-ctxObserve.Done(): + acb.Stop() + return + + case <-ticker.C: + acb.startNotObservedChannels(ctxObserve, acb.initChannelsObservers()) + } + } + }() + + return acb.blocks +} + +func (acb *ChannelsBlocksPeer[T]) startNotObservedChannels(ctx context.Context, notObservedChannels []*ChannelBlocks[T]) { + for _, notObservedChannel := range notObservedChannels { + chBlocks := notObservedChannel + + if _, err := chBlocks.Observe(ctx); err != nil { + acb.logger.Warn(`init channel observer`, zap.String("channel", notObservedChannel.channel), zap.Error(err)) + } + + // channel merger + go func() { + for b := range chBlocks.channelWithBlocks { + acb.blocks <- b + } + }() + } +} + +func (acb *ChannelsBlocksPeer[T]) initChannelsObservers() []*ChannelBlocks[T] { + var notObservedChannels []*ChannelBlocks[T] + + for channel := range acb.peerChannelsGetter.Channels() { + acb.mu.RLock() + _, ok := acb.channelObservers[channel] + acb.mu.RUnlock() + + if !ok { + acb.logger.Info(`add channel observer`, zap.String(`channel`, channel)) + + seekFrom := acb.getSeekFrom(channel) + + chBlocks := NewChannelBlocks[T]( + channel, + acb.deliverer, + acb.createStreamWithRetry, + seekFrom, + WithChannelBlockLogger(acb.logger), + WithChannelStopRecreateStream(acb.stopRecreateStream)) + + acb.mu.Lock() + acb.channelObservers[channel] = chBlocks + acb.mu.Unlock() + + notObservedChannels = append(notObservedChannels, chBlocks) + } + } + + return notObservedChannels +} + +func (acb *ChannelsBlocksPeer[T]) getSeekFrom(channel string) SeekFromFetcher { + seekFrom := ChannelSeekOldest() + // at first check seekFrom var, if it is empty, check seekFromFetcher + acb.mu.RLock() + seekFromNum, exist := acb.seekFrom[channel] + acb.mu.RUnlock() + if exist { + seekFrom = ChannelSeekFrom(seekFromNum - 1) + } else { + // if seekFromFetcher is also empty, use ChannelSeekOldest + if acb.seekFromFetcher != nil { + seekFrom = acb.seekFromFetcher + } + } + + return seekFrom +} diff --git a/observer/channels_blocks_peer_common.go b/observer/channels_blocks_peer_common.go new file mode 100644 index 0000000..98aeae5 --- /dev/null +++ b/observer/channels_blocks_peer_common.go @@ -0,0 +1,19 @@ +package observer + +import ( + "github.com/hyperledger/fabric-protos-go/common" + + "github.com/s7techlab/hlf-sdk-go/api" +) + +type ChannelsBlocksPeerCommon struct { + *ChannelsBlocksPeer[*common.Block] +} + +func NewChannelsBlocksPeerCommon(peerChannels PeerChannelsGetter, blocksDeliver api.BlocksDeliverer, opts ...ChannelsBlocksPeerOpt) *ChannelsBlocksPeerCommon { + createStreamWithRetry := CreateBlockStreamWithRetryDelay[*common.Block](DefaultConnectRetryDelay) + + channelsBlocksPeerCommon := NewChannelsBlocksPeer[*common.Block](peerChannels, blocksDeliver.Blocks, createStreamWithRetry, opts...) + + return &ChannelsBlocksPeerCommon{ChannelsBlocksPeer: channelsBlocksPeerCommon} +} diff --git a/observer/block_peer_common_test.go b/observer/channels_blocks_peer_common_test.go similarity index 60% rename from observer/block_peer_common_test.go rename to observer/channels_blocks_peer_common_test.go index 363b76c..e2bbcec 100644 --- a/observer/block_peer_common_test.go +++ b/observer/channels_blocks_peer_common_test.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/hyperledger/fabric-protos-go/common" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -16,60 +17,60 @@ import ( var ( ctx = context.Background() - channelPeerMockForCommon *observer.ChannelPeerMock - commonBlockPeer *observer.BlockPeer - commonBlocks <-chan *observer.Block + peerChannelsMockForCommon *observer.PeerChannelsMock + channelsBlocksPeerCommon *observer.ChannelsBlocksPeerCommon + commonBlocks <-chan *observer.Block[*common.Block] - channelPeerMockConcurrentlyForCommon *observer.ChannelPeerMock - commonBlockPeerConcurrently *observer.BlockPeer - commonBlocksByChannels *observer.BlocksByChannels + peerChannelsMockConcurrentlyForCommon *observer.PeerChannelsMock + channelsBlocksPeerConcurrentlyCommon *observer.ChannelsBlocksPeerCommon + channelWithChannelsCommon *observer.ChannelWithChannels[*common.Block] ) -func blockPeerCommonTestBeforeSuit() { +func channelsBlocksPeerCommonTestBeforeSuit() { const closeChannelWhenAllRead = true blockDelivererMock, err := sdkmocks.NewBlocksDelivererMock(fmt.Sprintf("../%s", testdata.Path), closeChannelWhenAllRead) Expect(err).ShouldNot(HaveOccurred()) - channelPeerMockForCommon = observer.NewChannelPeerMock() + peerChannelsMockForCommon = observer.NewPeerChannelsMock() for _, channel := range testdata.Channels { - channelPeerMockForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - commonBlockPeer = observer.NewBlockPeer(channelPeerMockForCommon, blockDelivererMock, - observer.WithBlockStopRecreateStream(true), observer.WithBlockPeerObservePeriod(time.Nanosecond)) + channelsBlocksPeerCommon = observer.NewChannelsBlocksPeerCommon(peerChannelsMockForCommon, blockDelivererMock, + observer.WithBlockStopRecreateStream(true), observer.WithChannelsBlocksPeerRefreshPeriod(time.Nanosecond)) - commonBlocks = commonBlockPeer.Observe(ctx) + commonBlocks = channelsBlocksPeerCommon.Observe(ctx) - channelPeerMockConcurrentlyForCommon = observer.NewChannelPeerMock() + peerChannelsMockConcurrentlyForCommon = observer.NewPeerChannelsMock() for _, channel := range testdata.Channels { - channelPeerMockConcurrentlyForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockConcurrentlyForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - commonBlockPeerConcurrently = observer.NewBlockPeer(channelPeerMockConcurrentlyForCommon, blockDelivererMock, - observer.WithBlockStopRecreateStream(true), observer.WithBlockPeerObservePeriod(time.Nanosecond)) + channelsBlocksPeerConcurrentlyCommon = observer.NewChannelsBlocksPeerCommon(peerChannelsMockConcurrentlyForCommon, blockDelivererMock, + observer.WithBlockStopRecreateStream(true), observer.WithChannelsBlocksPeerRefreshPeriod(time.Nanosecond)) - commonBlocksByChannels = commonBlockPeerConcurrently.ObserveByChannels(ctx) + channelWithChannelsCommon = channelsBlocksPeerConcurrentlyCommon.ObserveByChannels(ctx) } -var _ = Describe("Block Peer", func() { - Context("Block peer", func() { +var _ = Describe("All channels blocks common", func() { + Context("Sequentially", func() { It("should return current number of channels", func() { - channelObservers := commonBlockPeer.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels))) + channels := channelsBlocksPeerCommon.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels))) }) - It("should add channels to channelPeerMock", func() { + It("should add channels to peerChannelsMock", func() { newChannels := map[string]struct{}{"channel1": {}, "channel2": {}, "channel3": {}} for channel := range newChannels { - channelPeerMockForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - // wait to commonBlockPeer observer + // wait to channelsBlocksPeerCommon observer time.Sleep(time.Millisecond * 10) - channelObservers := commonBlockPeer.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels) + len(newChannels))) + channels := channelsBlocksPeerCommon.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels) + len(newChannels))) }) It("should return correct channels heights", func() { @@ -100,12 +101,12 @@ var _ = Describe("Block Peer", func() { }) }) - Context("Block peer concurrently", func() { + Context("Concurrently", func() { It("should return current number of channels", func() { - channelObservers := commonBlockPeerConcurrently.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels))) + channels := channelsBlocksPeerConcurrentlyCommon.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels))) - channelsWithBlocks := commonBlocksByChannels.Observe() + channelsWithBlocks := channelWithChannelsCommon.Observe() for i := 0; i < len(testdata.Channels); i++ { sampleOrFabcarChannelBlocks := <-channelsWithBlocks @@ -140,20 +141,20 @@ var _ = Describe("Block Peer", func() { } }) - It("should add channels to channelPeerMock", func() { + It("should add channels to peerChannelsMock", func() { channel4, channel5, channel6 := "channel4", "channel5", "channel6" newChannels := []string{channel4, channel5, channel6} for _, channel := range newChannels { - channelPeerMockConcurrentlyForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockConcurrentlyForCommon.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - // wait to commonBlockPeer observer + // wait to channelsBlocksPeerCommon observer time.Sleep(time.Millisecond * 200) - channelObservers := commonBlockPeerConcurrently.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels) + len(newChannels))) + channels := channelsBlocksPeerConcurrentlyCommon.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels) + len(newChannels))) - channelsWithBlocks := commonBlocksByChannels.Observe() + channelsWithBlocks := channelWithChannelsCommon.Observe() for i := 0; i < len(newChannels); i++ { channel4Or5Or6Blocks := <-channelsWithBlocks diff --git a/observer/channels_blocks_peer_concurrently.go b/observer/channels_blocks_peer_concurrently.go new file mode 100644 index 0000000..172e217 --- /dev/null +++ b/observer/channels_blocks_peer_concurrently.go @@ -0,0 +1,79 @@ +package observer + +import ( + "context" + "time" + + "go.uber.org/zap" +) + +type ChannelBlocksWithName[T any] struct { + Name string + Blocks <-chan *Block[T] +} + +type ChannelWithChannels[T any] struct { + channels chan *ChannelBlocksWithName[T] +} + +func (cwc *ChannelWithChannels[T]) Observe() <-chan *ChannelBlocksWithName[T] { + return cwc.channels +} + +func (acb *ChannelsBlocksPeer[T]) ObserveByChannels(ctx context.Context) *ChannelWithChannels[T] { + channelWithChannels := &ChannelWithChannels[T]{ + channels: make(chan *ChannelBlocksWithName[T]), + } + + // ctxObserve using for nested control process without stopped primary context + ctxObserve, cancel := context.WithCancel(ctx) + acb.cancelObserve = cancel + + acb.startNotObservedChannelsConcurrently(ctxObserve, acb.initChannelsObservers(), channelWithChannels) + + // init new channels if they are fetched + go func() { + ticker := time.NewTicker(acb.refreshPeriod) + defer func() { + ticker.Stop() + close(channelWithChannels.channels) + }() + + for { + select { + case <-ctx.Done(): + return + + case <-ticker.C: + acb.startNotObservedChannelsConcurrently(ctxObserve, acb.initChannelsObservers(), channelWithChannels) + } + } + }() + + // closer + go func() { + <-ctx.Done() + acb.Stop() + }() + + return channelWithChannels +} + +func (acb *ChannelsBlocksPeer[T]) startNotObservedChannelsConcurrently( + ctx context.Context, + notObservedChannels []*ChannelBlocks[T], + channelWithChannels *ChannelWithChannels[T], +) { + + for _, notObservedChannel := range notObservedChannels { + chBlocks := notObservedChannel + + if _, err := chBlocks.Observe(ctx); err != nil { + acb.logger.Warn(`init channel observer concurrently`, zap.String("channel", notObservedChannel.channel), zap.Error(err)) + } + + go func() { + channelWithChannels.channels <- &ChannelBlocksWithName[T]{Name: chBlocks.channel, Blocks: chBlocks.channelWithBlocks} + }() + } +} diff --git a/observer/channels_blocks_peer_parsed.go b/observer/channels_blocks_peer_parsed.go new file mode 100644 index 0000000..5fa7dbe --- /dev/null +++ b/observer/channels_blocks_peer_parsed.go @@ -0,0 +1,18 @@ +package observer + +import ( + "github.com/s7techlab/hlf-sdk-go/api" + hlfproto "github.com/s7techlab/hlf-sdk-go/block" +) + +type ChannelsBlocksPeerParsed struct { + *ChannelsBlocksPeer[*hlfproto.Block] +} + +func NewChannelsBlocksPeerParsed(peerChannels PeerChannelsGetter, blocksDeliver api.ParsedBlocksDeliverer, opts ...ChannelsBlocksPeerOpt) *ChannelsBlocksPeerParsed { + createStreamWithRetry := CreateBlockStreamWithRetryDelay[*hlfproto.Block](DefaultConnectRetryDelay) + + channelsBlocksPeerParsed := NewChannelsBlocksPeer[*hlfproto.Block](peerChannels, blocksDeliver.ParsedBlocks, createStreamWithRetry, opts...) + + return &ChannelsBlocksPeerParsed{ChannelsBlocksPeer: channelsBlocksPeerParsed} +} diff --git a/observer/block_peer_parsed_test.go b/observer/channels_blocks_peer_parsed_test.go similarity index 60% rename from observer/block_peer_parsed_test.go rename to observer/channels_blocks_peer_parsed_test.go index 3f6df79..e434a42 100644 --- a/observer/block_peer_parsed_test.go +++ b/observer/channels_blocks_peer_parsed_test.go @@ -7,66 +7,67 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + hlfproto "github.com/s7techlab/hlf-sdk-go/block" sdkmocks "github.com/s7techlab/hlf-sdk-go/client/deliver/testing" "github.com/s7techlab/hlf-sdk-go/observer" testdata "github.com/s7techlab/hlf-sdk-go/testdata/blocks" ) var ( - channelPeerMockForParsed *observer.ChannelPeerMock - parsedBlockPeer *observer.ParsedBlockPeer - parsedBlocks <-chan *observer.ParsedBlock + peerChannelsMockForParsed *observer.PeerChannelsMock + channelsBlocksPeerParsed *observer.ChannelsBlocksPeerParsed + parsedBlocks <-chan *observer.Block[*hlfproto.Block] - channelPeerMockConcurrentlyForParsed *observer.ChannelPeerMock - parsedBlockPeerConcurrently *observer.ParsedBlockPeer - parsedBlocksByChannels *observer.ParsedBlocksByChannels + peerChannelsMockConcurrentlyForParsed *observer.PeerChannelsMock + channelsBlocksPeerConcurrentlyParsed *observer.ChannelsBlocksPeerParsed + channelWithChannelsParsed *observer.ChannelWithChannels[*hlfproto.Block] ) -func blockPeerParsedTestBeforeSuit() { +func channelsBlocksPeerParsedTestBeforeSuit() { const closeChannelWhenAllRead = true blockDelivererMock, err := sdkmocks.NewBlocksDelivererMock(fmt.Sprintf("../%s", testdata.Path), closeChannelWhenAllRead) Expect(err).ShouldNot(HaveOccurred()) - channelPeerMockForParsed = observer.NewChannelPeerMock() + peerChannelsMockForParsed = observer.NewPeerChannelsMock() for _, channel := range testdata.Channels { - channelPeerMockForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - commonBlockPeerForParsed := observer.NewBlockPeer(channelPeerMockForParsed, blockDelivererMock, observer.WithBlockStopRecreateStream(true), observer.WithBlockPeerObservePeriod(time.Nanosecond)) - parsedBlockPeer = observer.NewParsedBlockPeer(commonBlockPeerForParsed) + channelsBlocksPeerParsed = observer.NewChannelsBlocksPeerParsed(peerChannelsMockForParsed, blockDelivererMock, + observer.WithBlockStopRecreateStream(true), observer.WithChannelsBlocksPeerRefreshPeriod(time.Nanosecond)) - parsedBlocks = parsedBlockPeer.Observe(ctx) + parsedBlocks = channelsBlocksPeerParsed.Observe(ctx) - channelPeerMockConcurrentlyForParsed = observer.NewChannelPeerMock() + peerChannelsMockConcurrentlyForParsed = observer.NewPeerChannelsMock() for _, channel := range testdata.Channels { - channelPeerMockConcurrentlyForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockConcurrentlyForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - commonBlockPeerConcurrentlyForParsed := observer.NewBlockPeer(channelPeerMockConcurrentlyForParsed, blockDelivererMock, observer.WithBlockStopRecreateStream(true), observer.WithBlockPeerObservePeriod(time.Nanosecond)) - parsedBlockPeerConcurrently = observer.NewParsedBlockPeer(commonBlockPeerConcurrentlyForParsed) + channelsBlocksPeerConcurrentlyParsed = observer.NewChannelsBlocksPeerParsed(peerChannelsMockConcurrentlyForParsed, blockDelivererMock, + observer.WithBlockStopRecreateStream(true), observer.WithChannelsBlocksPeerRefreshPeriod(time.Nanosecond)) - parsedBlocksByChannels = parsedBlockPeerConcurrently.ObserveByChannels(ctx) + channelWithChannelsParsed = channelsBlocksPeerConcurrentlyParsed.ObserveByChannels(ctx) } -var _ = Describe("Block Peer", func() { - Context("Block peer", func() { +var _ = Describe("All channels blocks parsed", func() { + Context("Sequentially", func() { It("should return current number of channels", func() { - channelObservers := parsedBlockPeer.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels))) + channels := channelsBlocksPeerParsed.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels))) }) - It("should add channels to channelPeerMock", func() { + It("should add channels to peerChannelsMock", func() { newChannels := map[string]struct{}{"channel1": {}, "channel2": {}, "channel3": {}} for channel := range newChannels { - channelPeerMockForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - // wait to parsedBlockPeer observer + // wait to channelsBlocksPeerParsed observer time.Sleep(time.Second + time.Millisecond*10) - channelObservers := parsedBlockPeer.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels) + len(newChannels))) + channels := channelsBlocksPeerParsed.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels) + len(newChannels))) }) It("should return correct channels heights", func() { @@ -97,12 +98,12 @@ var _ = Describe("Block Peer", func() { }) }) - Context("Block peer concurrently", func() { + Context("Concurrently", func() { It("should return current number of channels", func() { - channelObservers := parsedBlockPeerConcurrently.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels))) + channels := channelsBlocksPeerConcurrentlyParsed.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels))) - channelsWithBlocks := parsedBlocksByChannels.Observe() + channelsWithBlocks := channelWithChannelsParsed.Observe() for i := 0; i < len(testdata.Channels); i++ { sampleOrFabcarChannelBlocks := <-channelsWithBlocks @@ -137,20 +138,20 @@ var _ = Describe("Block Peer", func() { } }) - It("should add channels to channelPeerMock", func() { + It("should add channels to peerChannelsMock", func() { channel4, channel5, channel6 := "channel4", "channel5", "channel6" newChannels := []string{channel4, channel5, channel6} for _, channel := range newChannels { - channelPeerMockConcurrentlyForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) + peerChannelsMockConcurrentlyForParsed.UpdateChannelInfo(&observer.ChannelInfo{Channel: channel}) } - // wait to blockPeer observer + // wait to channelsBlocksPeerParsed observer time.Sleep(time.Millisecond * 200) - channelObservers := parsedBlockPeerConcurrently.ChannelObservers() - Expect(channelObservers).To(HaveLen(len(testdata.Channels) + len(newChannels))) + channels := channelsBlocksPeerConcurrentlyParsed.Channels() + Expect(channels).To(HaveLen(len(testdata.Channels) + len(newChannels))) - channelsWithBlocks := parsedBlocksByChannels.Observe() + channelsWithBlocks := channelWithChannelsParsed.Observe() for i := 0; i < len(newChannels); i++ { channel4Or5Or6Blocks := <-channelsWithBlocks diff --git a/observer/channel_matcher.go b/observer/channels_matcher.go similarity index 100% rename from observer/channel_matcher.go rename to observer/channels_matcher.go diff --git a/observer/event.go b/observer/event.go deleted file mode 100644 index 870f3ef..0000000 --- a/observer/event.go +++ /dev/null @@ -1,23 +0,0 @@ -package observer - -import ( - "context" - - "github.com/hyperledger/fabric-protos-go/peer" -) - -type ( - Event struct { - Block *peer.ChaincodeEvent - Channel string - Error error - } - - CreateEventStream func(context.Context) (<-chan *peer.ChaincodeEvent, error) - - CreateEventStreamWithRetry func(context.Context, CreateEventStream) (<-chan *peer.ChaincodeEvent, error) - - EventTransformer interface { - Transform(*Event) - } -) diff --git a/observer/old_files/event.go b/observer/old_files/event.go new file mode 100644 index 0000000..da0a6b1 --- /dev/null +++ b/observer/old_files/event.go @@ -0,0 +1,23 @@ +package old_files + +//import ( +// "context" +// +// "github.com/hyperledger/fabric-protos-go/peer" +//) +// +//type ( +// Event struct { +// Block *peer.ChaincodeEvent +// Channel string +// Error error +// } +// +// CreateEventStream func(context.Context) (<-chan *peer.ChaincodeEvent, error) +// +// CreateEventStreamWithRetry func(context.Context, CreateEventStream) (<-chan *peer.ChaincodeEvent, error) +// +// EventTransformer interface { +// Transform(*Event) +// } +//) diff --git a/observer/event_channel.go b/observer/old_files/event_channel.go similarity index 94% rename from observer/event_channel.go rename to observer/old_files/event_channel.go index 4eee327..0333d39 100644 --- a/observer/event_channel.go +++ b/observer/old_files/event_channel.go @@ -1,4 +1,4 @@ -package observer +package old_files // type ( // EventChannel struct { diff --git a/observer/event_peer.go b/observer/old_files/event_peer.go similarity index 86% rename from observer/event_peer.go rename to observer/old_files/event_peer.go index b0edc85..a1fe666 100644 --- a/observer/event_peer.go +++ b/observer/old_files/event_peer.go @@ -1,4 +1,4 @@ -package observer +package old_files // type ( // EventPeer struct { diff --git a/observer/opts.go b/observer/old_files/opts.go similarity index 99% rename from observer/opts.go rename to observer/old_files/opts.go index b380d44..3808181 100644 --- a/observer/opts.go +++ b/observer/old_files/opts.go @@ -1,4 +1,4 @@ -package observer +package old_files // import ( // "context" diff --git a/observer/peer_channels.go b/observer/peer_channels.go new file mode 100644 index 0000000..980d0d0 --- /dev/null +++ b/observer/peer_channels.go @@ -0,0 +1,206 @@ +package observer + +import ( + "context" + "fmt" + "sync" + "time" + + "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/timestamp" + "github.com/hyperledger/fabric-protos-go/common" + "go.uber.org/zap" + "google.golang.org/protobuf/types/known/timestamppb" + + "github.com/s7techlab/hlf-sdk-go/api" +) + +const DefaultPeerChannelsRefreshPeriod = 30 * time.Second + +type ( + PeerChannelsGetter interface { + URI() string + Channels() map[string]*ChannelInfo + } + + PeerChannelsFetcher interface { + URI() string + api.ChannelListGetter + api.ChainInfoGetter + } + + ChannelInfo struct { + Channel string + Height uint64 + UpdatedAt *timestamppb.Timestamp + } + + // PeerChannels observes for peer channels + PeerChannels struct { + channels map[string]*ChannelInfo + + channelFetcher PeerChannelsFetcher + channelsMatcher *ChannelsMatcher + refreshPeriod time.Duration + + mu sync.RWMutex + logger *zap.Logger + + lastError error + + isWork bool + cancelObserve context.CancelFunc + } + + PeerChannelsOpts struct { + channels []ChannelToMatch + refreshPeriod time.Duration + logger *zap.Logger + } + + PeerChannelsOpt func(*PeerChannelsOpts) +) + +var DefaultPeerChannelsOpts = &PeerChannelsOpts{ + channels: MatchAllChannels, + refreshPeriod: DefaultPeerChannelsRefreshPeriod, + logger: zap.NewNop(), +} + +func WithChannels(channels []ChannelToMatch) PeerChannelsOpt { + return func(opts *PeerChannelsOpts) { + opts.channels = channels + } +} + +func WithPeerChannelsLogger(logger *zap.Logger) PeerChannelsOpt { + return func(opts *PeerChannelsOpts) { + opts.logger = logger + } +} + +func NewPeerChannels(peerChannelsFetcher PeerChannelsFetcher, opts ...PeerChannelsOpt) (*PeerChannels, error) { + peerChannelsOpts := DefaultPeerChannelsOpts + for _, opt := range opts { + opt(peerChannelsOpts) + } + + channelsMatcher, err := NewChannelsMatcher(peerChannelsOpts.channels) + if err != nil { + return nil, fmt.Errorf(`channels matcher: %w`, err) + } + + peerChannels := &PeerChannels{ + channelFetcher: peerChannelsFetcher, + channelsMatcher: channelsMatcher, + channels: make(map[string]*ChannelInfo), + refreshPeriod: peerChannelsOpts.refreshPeriod, + logger: peerChannelsOpts.logger, + } + + return peerChannels, nil +} + +func (pc *PeerChannels) Stop() { + pc.cancelObserve() + pc.isWork = false +} + +func (pc *PeerChannels) Observe(ctx context.Context) { + if pc.isWork { + return + } + + // ctxObserve using for nested control process without stopped primary context + ctxObserve, cancel := context.WithCancel(context.Background()) + pc.cancelObserve = cancel + + go func() { + pc.isWork = true + pc.updateChannels(ctxObserve) + + ticker := time.NewTicker(pc.refreshPeriod) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + // If primary context is done then cancel ctxObserver + pc.cancelObserve() + return + + case <-ctxObserve.Done(): + return + + case <-ticker.C: + pc.updateChannels(ctxObserve) + } + } + }() +} + +func (pc *PeerChannels) URI() string { + return pc.channelFetcher.URI() +} + +func (pc *PeerChannels) Channels() map[string]*ChannelInfo { + pc.mu.RLock() + defer pc.mu.RUnlock() + + var copyChannelInfo = make(map[string]*ChannelInfo, len(pc.channels)) + for key, value := range pc.channels { + copyChannelInfo[key] = value + } + + return copyChannelInfo +} + +func (pc *PeerChannels) updateChannels(ctx context.Context) { + pc.logger.Debug(`fetching channels`) + channelsInfo, err := pc.channelFetcher.GetChannels(ctx) + if err != nil { + pc.logger.Warn(`error while fetching channels`, zap.Error(err)) + pc.lastError = err + return + } + + channels := ChannelsInfoToStrings(channelsInfo.Channels) + pc.logger.Debug(`channels fetched`, zap.Strings(`channels`, channels)) + + channelsMatched, err := pc.channelsMatcher.Match(channels) + if err != nil { + pc.logger.Warn(`channel matching error`, zap.Error(err)) + pc.lastError = err + return + } + pc.logger.Debug(`channels matched`, zap.Reflect(`channels`, channelsMatched)) + + channelHeights := make(map[string]uint64) + + for _, channel := range channelsMatched { + var channelInfo *common.BlockchainInfo + channelInfo, err = pc.channelFetcher.GetChainInfo(ctx, channel.Name) + if err != nil { + pc.lastError = err + continue + } + channelHeights[channel.Name] = channelInfo.Height + } + + pc.mu.Lock() + defer pc.mu.Unlock() + + for channel, height := range channelHeights { + var updatedAt *timestamp.Timestamp + updatedAt, err = ptypes.TimestampProto(time.Now()) + if err != nil { + pc.lastError = err + } + + pc.channels[channel] = &ChannelInfo{ + Channel: channel, + Height: height, + UpdatedAt: updatedAt, + } + } +} diff --git a/observer/peer_channels_fetcher_mock.go b/observer/peer_channels_fetcher_mock.go new file mode 100644 index 0000000..758d484 --- /dev/null +++ b/observer/peer_channels_fetcher_mock.go @@ -0,0 +1,39 @@ +package observer + +import ( + "context" + "fmt" + + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/peer" +) + +type PeerChannelsFetcherMock struct { + channels map[string]uint64 +} + +func NewPeerChannelsFetcherMock(channels map[string]uint64) *PeerChannelsFetcherMock { + return &PeerChannelsFetcherMock{channels: channels} +} + +func (p *PeerChannelsFetcherMock) URI() string { + return "mock" +} + +func (p *PeerChannelsFetcherMock) GetChannels(context.Context) (*peer.ChannelQueryResponse, error) { + var channels []*peer.ChannelInfo + for channelName := range p.channels { + channels = append(channels, &peer.ChannelInfo{ChannelId: channelName}) + } + + return &peer.ChannelQueryResponse{Channels: channels}, nil +} + +func (p *PeerChannelsFetcherMock) GetChainInfo(_ context.Context, channel string) (*common.BlockchainInfo, error) { + chHeight, exists := p.channels[channel] + if !exists { + return nil, fmt.Errorf("channel '%s' does not exist", channel) + } + + return &common.BlockchainInfo{Height: chHeight}, nil +} diff --git a/observer/peer_channels_mock.go b/observer/peer_channels_mock.go new file mode 100644 index 0000000..4ad55ed --- /dev/null +++ b/observer/peer_channels_mock.go @@ -0,0 +1,42 @@ +package observer + +import ( + "sync" +) + +type PeerChannelsMock struct { + mu sync.Mutex + channelsInfo map[string]*ChannelInfo +} + +func NewPeerChannelsMock(channelsInfo ...*ChannelInfo) *PeerChannelsMock { + channels := make(map[string]*ChannelInfo, len(channelsInfo)) + for _, channelInfo := range channelsInfo { + channels[channelInfo.Channel] = channelInfo + } + + return &PeerChannelsMock{channelsInfo: channels} +} + +func (p *PeerChannelsMock) URI() string { + return "mock" +} + +func (p *PeerChannelsMock) Channels() map[string]*ChannelInfo { + p.mu.Lock() + defer p.mu.Unlock() + + var copyChannelInfo = make(map[string]*ChannelInfo, len(p.channelsInfo)) + for key, value := range p.channelsInfo { + copyChannelInfo[key] = value + } + + return copyChannelInfo +} + +func (p *PeerChannelsMock) UpdateChannelInfo(channelInfo *ChannelInfo) { + p.mu.Lock() + defer p.mu.Unlock() + + p.channelsInfo[channelInfo.Channel] = channelInfo +} diff --git a/observer/channel_peer_test.go b/observer/peer_channels_test.go similarity index 68% rename from observer/channel_peer_test.go rename to observer/peer_channels_test.go index 1e61264..07cfed6 100644 --- a/observer/channel_peer_test.go +++ b/observer/peer_channels_test.go @@ -10,22 +10,22 @@ import ( testdata "github.com/s7techlab/hlf-sdk-go/testdata/blocks" ) -var _ = Describe("Channel peer", func() { +var _ = Describe("Peer channels", func() { var ( - channelPeerFetcherMock observer.PeerChannelsFetcher + peerChannelsFetcherMock observer.PeerChannelsFetcher ) BeforeEach(func() { - channelPeerFetcherMock = observer.NewChannelPeerFetcherMock(testdata.ChannelsHeights) + peerChannelsFetcherMock = observer.NewPeerChannelsFetcherMock(testdata.ChannelsHeights) }) - It("default channel peer, no channel matcher", func() { - channelPeer, err := observer.NewChannelPeer(channelPeerFetcherMock) + It("default peer channels, no channel matcher", func() { + peerChannels, err := observer.NewPeerChannels(peerChannelsFetcherMock) Expect(err).To(BeNil()) - channelPeer.Observe(ctx) + peerChannels.Observe(ctx) time.Sleep(time.Millisecond * 100) - channelsMap := channelPeer.Channels() + channelsMap := peerChannels.Channels() sampleChannelInfo, exist := channelsMap[testdata.SampleChannel] Expect(exist).To(BeTrue()) @@ -38,15 +38,15 @@ var _ = Describe("Channel peer", func() { Expect(fabcarChannelInfo.Height).To(Equal(testdata.FabcarChannelHeight)) }) - It("default channel peer, with channel matcher, exclude Fabcar", func() { - channelPeer, err := observer.NewChannelPeer(channelPeerFetcherMock, + It("default peer channels, with channel matcher, exclude Fabcar", func() { + peerChannels, err := observer.NewPeerChannels(peerChannelsFetcherMock, observer.WithChannels([]observer.ChannelToMatch{{MatchPattern: testdata.SampleChannel}})) Expect(err).To(BeNil()) - channelPeer.Observe(ctx) + peerChannels.Observe(ctx) time.Sleep(time.Millisecond * 100) - channelsMap := channelPeer.Channels() + channelsMap := peerChannels.Channels() sampleChannelInfo, exist := channelsMap[testdata.SampleChannel] Expect(exist).To(BeTrue()) diff --git a/observer/stream.go b/observer/stream.go index 11b6c12..560cb3a 100644 --- a/observer/stream.go +++ b/observer/stream.go @@ -6,26 +6,26 @@ import ( "sync" ) -type Stream interface { - Subscribe() (ch chan *Block, closer func()) +type Stream[T any] interface { + Subscribe() (ch <-chan *Block[T], closer func()) } -type BlocksStream struct { - connections map[string]chan *Block +type BlocksStream[T any] struct { + connections map[string]chan *Block[T] mu *sync.RWMutex isWork bool cancelObserve context.CancelFunc } -func NewBlocksStream() *BlocksStream { - return &BlocksStream{ - connections: make(map[string]chan *Block), +func NewBlocksStream[T any]() *BlocksStream[T] { + return &BlocksStream[T]{ + connections: make(map[string]chan *Block[T]), mu: &sync.RWMutex{}, } } -func (b *BlocksStream) Observe(ctx context.Context, blocks <-chan *Block) { +func (b *BlocksStream[T]) Observe(ctx context.Context, blocks <-chan *Block[T]) { if b.isWork { return } @@ -36,9 +36,11 @@ func (b *BlocksStream) Observe(ctx context.Context, blocks <-chan *Block) { go func() { defer func() { + b.mu.Lock() for connName := range b.connections { b.closeChannel(connName) } + b.mu.Unlock() }() b.isWork = true @@ -65,9 +67,9 @@ func (b *BlocksStream) Observe(ctx context.Context, blocks <-chan *Block) { }() } -func (b *BlocksStream) Subscribe() (chan *Block, func()) { +func (b *BlocksStream[T]) Subscribe() (<-chan *Block[T], func()) { b.mu.Lock() - newConnection := make(chan *Block) + newConnection := make(chan *Block[T]) name := "channel-" + strconv.Itoa(len(b.connections)) b.connections[name] = newConnection b.mu.Unlock() @@ -77,14 +79,14 @@ func (b *BlocksStream) Subscribe() (chan *Block, func()) { return newConnection, closer } -func (b *BlocksStream) closeChannel(name string) { +func (b *BlocksStream[T]) closeChannel(name string) { b.mu.Lock() close(b.connections[name]) delete(b.connections, name) b.mu.Unlock() } -func (b *BlocksStream) Stop() { +func (b *BlocksStream[T]) Stop() { if b.cancelObserve != nil { b.cancelObserve() } diff --git a/observer/stream_parsed.go b/observer/stream_parsed.go deleted file mode 100644 index b128171..0000000 --- a/observer/stream_parsed.go +++ /dev/null @@ -1,104 +0,0 @@ -package observer - -import ( - "context" - "strconv" - "sync" -) - -type StreamParsed interface { - Subscribe() (ch chan *ParsedBlock, closer func()) -} - -type ParsedBlocksStream struct { - connectionsParsed map[string]chan *ParsedBlock - mu *sync.RWMutex - - isWork bool - cancelObserve context.CancelFunc -} - -func NewParsedBlocksStream() *ParsedBlocksStream { - return &ParsedBlocksStream{ - connectionsParsed: make(map[string]chan *ParsedBlock), - mu: &sync.RWMutex{}, - } -} - -func (b *ParsedBlocksStream) Observe(ctx context.Context, blocks <-chan *ParsedBlock) { - if b.isWork { - return - } - - // ctxObserve using for nested control process without stopped primary context - ctxObserve, cancel := context.WithCancel(ctx) - b.cancelObserve = cancel - - go func() { - defer func() { - for connName := range b.connectionsParsed { - b.closeChannel(connName) - } - }() - - b.isWork = true - - for { - select { - case <-ctxObserve.Done(): - // If primary context is done then cancel ctxObserver - b.cancelObserve() - return - - case block, ok := <-blocks: - if !ok { - return - } - - b.mu.RLock() - for _, connection := range b.connectionsParsed { - connection <- block - } - b.mu.RUnlock() - } - } - }() -} - -func (b *ParsedBlocksStream) Subscribe() (chan *ParsedBlock, func()) { - b.mu.Lock() - newConnection := make(chan *ParsedBlock) - name := "channel-" + strconv.Itoa(len(b.connectionsParsed)) - b.connectionsParsed[name] = newConnection - b.mu.Unlock() - - closer := func() { b.closeChannel(name) } - - return newConnection, closer -} - -func (b *ParsedBlocksStream) SubscribeParsed() (chan *ParsedBlock, func()) { - b.mu.Lock() - newConnection := make(chan *ParsedBlock) - name := "channel-" + strconv.Itoa(len(b.connectionsParsed)) - b.connectionsParsed[name] = newConnection - b.mu.Unlock() - - closer := func() { b.closeChannel(name) } - - return newConnection, closer -} - -func (b *ParsedBlocksStream) closeChannel(name string) { - b.mu.Lock() - close(b.connectionsParsed[name]) - delete(b.connectionsParsed, name) - b.mu.Unlock() -} - -func (b *ParsedBlocksStream) Stop() { - if b.cancelObserve != nil { - b.cancelObserve() - } - b.isWork = false -} diff --git a/observer/suite_test.go b/observer/suite_test.go index 0738dfb..5117f0a 100644 --- a/observer/suite_test.go +++ b/observer/suite_test.go @@ -13,6 +13,6 @@ func TestObservers(t *testing.T) { } var _ = BeforeSuite(func() { - blockPeerCommonTestBeforeSuit() - blockPeerParsedTestBeforeSuit() + channelsBlocksPeerCommonTestBeforeSuit() + channelsBlocksPeerParsedTestBeforeSuit() }) diff --git a/observer/transformer.go b/observer/transformer.go deleted file mode 100644 index dc9c87a..0000000 --- a/observer/transformer.go +++ /dev/null @@ -1,6 +0,0 @@ -package observer - -// BlockTransformer transforms parsed observer data. For example decrypt, or transformer protobuf state to json -type BlockTransformer interface { - Transform(*ParsedBlock) error -} diff --git a/service/ccpackage/packages.pb.go b/service/ccpackage/packages.pb.go index 5b479eb..03f8d91 100644 --- a/service/ccpackage/packages.pb.go +++ b/service/ccpackage/packages.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: ccpackage/packages.proto diff --git a/service/systemcc/cscc/cscc.pb.go b/service/systemcc/cscc/cscc.pb.go index 01e2158..1f67193 100644 --- a/service/systemcc/cscc/cscc.pb.go +++ b/service/systemcc/cscc/cscc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: systemcc/cscc/cscc.proto diff --git a/service/systemcc/lifecycle/lifecycle.pb.go b/service/systemcc/lifecycle/lifecycle.pb.go index 08c4942..c1e509b 100644 --- a/service/systemcc/lifecycle/lifecycle.pb.go +++ b/service/systemcc/lifecycle/lifecycle.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: systemcc/lifecycle/lifecycle.proto diff --git a/service/systemcc/lscc/lscc.pb.go b/service/systemcc/lscc/lscc.pb.go index b414d49..da42d1e 100644 --- a/service/systemcc/lscc/lscc.pb.go +++ b/service/systemcc/lscc/lscc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: systemcc/lscc/lscc.proto diff --git a/service/systemcc/qscc/qscc.pb.go b/service/systemcc/qscc/qscc.pb.go index 936825c..ddd13fc 100644 --- a/service/systemcc/qscc/qscc.pb.go +++ b/service/systemcc/qscc/qscc.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: systemcc/qscc/qscc.proto diff --git a/service/wallet/wallet.pb.go b/service/wallet/wallet.pb.go index 90cfe3d..fe160ab 100644 --- a/service/wallet/wallet.pb.go +++ b/service/wallet/wallet.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 +// protoc-gen-go v1.32.0 // protoc (unknown) // source: wallet/wallet.proto @@ -828,19 +828,19 @@ const _ = grpc.SupportPackageIsVersion6 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type WalletServiceClient interface { - // Получить identity + // get identity IdentityGet(ctx context.Context, in *IdentityLabel, opts ...grpc.CallOption) (*IdentityInWallet, error) - // Получить identity в виде текста + // Get identity like text IdentityGetText(ctx context.Context, in *IdentityLabel, opts ...grpc.CallOption) (*IdentityInWalletText, error) - // Записать identity + // set identity IdentitySet(ctx context.Context, in *Identity, opts ...grpc.CallOption) (*IdentityInWallet, error) - // Записать identity в зашифрованном виде + // set identity in encrypted form IdentitySetWithPassword(ctx context.Context, in *IdentityWithPassword, opts ...grpc.CallOption) (*IdentityInWallet, error) - // Получить identity из зашифрованного вида + // get identity from encrypted view IdentityGetWithPassword(ctx context.Context, in *IdentityPassword, opts ...grpc.CallOption) (*IdentityInWallet, error) - // Список identity + // identity list IdentityList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*IdentityLabels, error) - // Удалить identity + // delete identity IdentityDelete(ctx context.Context, in *IdentityLabel, opts ...grpc.CallOption) (*IdentityInWallet, error) } @@ -917,19 +917,19 @@ func (c *walletServiceClient) IdentityDelete(ctx context.Context, in *IdentityLa // WalletServiceServer is the server API for WalletService service. type WalletServiceServer interface { - // Получить identity + // get identity IdentityGet(context.Context, *IdentityLabel) (*IdentityInWallet, error) - // Получить identity в виде текста + // Get identity like text IdentityGetText(context.Context, *IdentityLabel) (*IdentityInWalletText, error) - // Записать identity + // set identity IdentitySet(context.Context, *Identity) (*IdentityInWallet, error) - // Записать identity в зашифрованном виде + // set identity in encrypted form IdentitySetWithPassword(context.Context, *IdentityWithPassword) (*IdentityInWallet, error) - // Получить identity из зашифрованного вида + // get identity from encrypted view IdentityGetWithPassword(context.Context, *IdentityPassword) (*IdentityInWallet, error) - // Список identity + // identity list IdentityList(context.Context, *emptypb.Empty) (*IdentityLabels, error) - // Удалить identity + // delete identity IdentityDelete(context.Context, *IdentityLabel) (*IdentityInWallet, error) } diff --git a/service/wallet/wallet.proto b/service/wallet/wallet.proto index 584659f..d92b0df 100644 --- a/service/wallet/wallet.proto +++ b/service/wallet/wallet.proto @@ -10,21 +10,21 @@ import "validate/validate.proto"; service WalletService { - // Получить identity + // get identity rpc IdentityGet (IdentityLabel) returns (IdentityInWallet) { option (google.api.http) = { get: "/wallet/identities/{label}" }; } - // Получить identity в виде текста + // Get identity like text rpc IdentityGetText (IdentityLabel) returns (IdentityInWalletText) { option (google.api.http) = { get: "/wallet/identities/{label}/text" }; } - // Записать identity + // set identity rpc IdentitySet (Identity) returns (IdentityInWallet) { option (google.api.http) = { put: "/wallet/identities" @@ -32,7 +32,7 @@ service WalletService { }; } - // Записать identity в зашифрованном виде + // set identity in encrypted form rpc IdentitySetWithPassword (IdentityWithPassword) returns (IdentityInWallet) { option (google.api.http) = { put: "/wallet/identities/withpassword" @@ -47,7 +47,7 @@ service WalletService { // }; // } - // Получить identity из зашифрованного вида + // get identity from encrypted view rpc IdentityGetWithPassword (IdentityPassword) returns (IdentityInWallet) { option (google.api.http) = { post: "/wallet/identities/withpassword" @@ -55,14 +55,14 @@ service WalletService { }; } - // Список identity + // identity list rpc IdentityList (google.protobuf.Empty) returns (IdentityLabels) { option (google.api.http) = { get: "/wallet/identities" }; } - // Удалить identity + // delete identity rpc IdentityDelete (IdentityLabel) returns (IdentityInWallet) { option (google.api.http) = { delete: "/wallet/identities/{label}" diff --git a/service/wallet/wallet.swagger.json b/service/wallet/wallet.swagger.json index a7e686e..3809360 100644 --- a/service/wallet/wallet.swagger.json +++ b/service/wallet/wallet.swagger.json @@ -13,7 +13,7 @@ "paths": { "/wallet/identities": { "get": { - "summary": "Список identity", + "summary": "identity list", "operationId": "WalletService_IdentityList", "responses": { "200": { @@ -34,7 +34,7 @@ ] }, "put": { - "summary": "Записать identity", + "summary": "set identity", "operationId": "WalletService_IdentitySet", "responses": { "200": { @@ -67,7 +67,7 @@ }, "/wallet/identities/withpassword": { "post": { - "summary": "Получить identity из зашифрованного вида", + "summary": "get identity from encrypted view", "operationId": "WalletService_IdentityGetWithPassword", "responses": { "200": { @@ -98,7 +98,7 @@ ] }, "put": { - "summary": "Записать identity в зашифрованном виде", + "summary": "set identity in encrypted form", "operationId": "WalletService_IdentitySetWithPassword", "responses": { "200": { @@ -131,7 +131,7 @@ }, "/wallet/identities/{label}": { "get": { - "summary": "Получить identity", + "summary": "get identity", "operationId": "WalletService_IdentityGet", "responses": { "200": { @@ -160,7 +160,7 @@ ] }, "delete": { - "summary": "Удалить identity", + "summary": "delete identity", "operationId": "WalletService_IdentityDelete", "responses": { "200": { @@ -191,7 +191,7 @@ }, "/wallet/identities/{label}/text": { "get": { - "summary": "Получить identity в виде текста", + "summary": "Get identity like text", "operationId": "WalletService_IdentityGetText", "responses": { "200": {