From c43d321972491b3fd19a46337ae0c8c94a9be2c8 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Mon, 17 Jan 2022 18:54:14 -0500 Subject: [PATCH 1/6] Added query endpoints; refactored query handling into syncdispatcher Signed-off-by: Jim Zhang --- internal/events/submanager_test.go | 5 +- internal/events/subscription.go | 13 ++- internal/events/subscription_test.go | 3 +- internal/events/test_helper.go | 70 +---------- internal/fabric/client/ledger.go | 2 +- internal/fabric/ledger.go | 56 --------- internal/fabric/query.go | 62 ---------- internal/fabric/test/helper.go | 90 ++++++++++++++ internal/fabric/utils/block.go | 35 ++++-- internal/fabric/utils/blockdecoder.go | 118 ++++++++++++------- internal/fabric/utils/blockdecoder_test.go | 5 +- internal/fabric/utils/rawblock.go | 13 +-- internal/messages/messages.go | 13 ++- internal/rest/restgateway_test.go | 130 +++++++++++++++++++++ internal/rest/router.go | 31 ++--- internal/rest/sync/syncdispatcher.go | 108 +++++++++++++++++ internal/rest/utils/params.go | 60 +++++++++- internal/tx/txprocessor.go | 61 +--------- internal/utils/payload_decoder.go | 36 ++++++ mocks/rest/sync/sync_dispatcher.go | 22 ++++ mocks/tx/tx_processor.go | 16 +++ 21 files changed, 623 insertions(+), 326 deletions(-) delete mode 100644 internal/fabric/ledger.go delete mode 100644 internal/fabric/query.go create mode 100644 internal/fabric/test/helper.go create mode 100644 internal/utils/payload_decoder.go diff --git a/internal/events/submanager_test.go b/internal/events/submanager_test.go index c444b21..0a00e82 100644 --- a/internal/events/submanager_test.go +++ b/internal/events/submanager_test.go @@ -23,6 +23,7 @@ import ( "github.com/hyperledger/firefly-fabconnect/internal/events/api" eventsapi "github.com/hyperledger/firefly-fabconnect/internal/events/api" + "github.com/hyperledger/firefly-fabconnect/internal/fabric/test" "github.com/hyperledger/firefly-fabconnect/internal/kvstore" "github.com/julienschmidt/httprouter" "github.com/stretchr/testify/assert" @@ -145,7 +146,7 @@ func TestActionChildCleanup(t *testing.T) { dir := tempdir(t) defer cleanup(t, dir) sm := newTestSubscriptionManager() - sm.rpc = mockRPCClient("") + sm.rpc = test.MockRPCClient("") sm.db = kvstore.NewLDBKeyValueStore(path.Join(dir, "db")) _ = sm.db.Init() defer sm.db.Close() @@ -177,7 +178,7 @@ func TestStreamAndSubscriptionErrors(t *testing.T) { dir := tempdir(t) defer cleanup(t, dir) sm := newTestSubscriptionManager() - sm.rpc = mockRPCClient("") + sm.rpc = test.MockRPCClient("") sm.db = kvstore.NewLDBKeyValueStore(path.Join(dir, "db")) _ = sm.db.Init() defer sm.db.Close() diff --git a/internal/events/subscription.go b/internal/events/subscription.go index 5507c49..6496b0a 100644 --- a/internal/events/subscription.go +++ b/internal/events/subscription.go @@ -155,7 +155,8 @@ func (s *subscription) getEventTimestamp(evt *eventsapi.EventEntry) { blockNumber := strconv.FormatUint(evt.BlockNumber, 10) if ts, ok := s.ep.stream.blockTimestampCache.Get(blockNumber); ok { // we found the timestamp for the block in our local cache, assert it's type and return, no need to query the chain - evt.Timestamp = ts.(int64) + timestamps := ts.(map[int]int64) + evt.Timestamp = timestamps[evt.TransactionIndex] return } // we didn't find the timestamp in our cache, query the node for the block header where we can find the timestamp @@ -165,8 +166,14 @@ func (s *subscription) getEventTimestamp(evt *eventsapi.EventEntry) { evt.Timestamp = 0 // set to 0, we were not able to retrieve the timestamp. return } - evt.Timestamp = block.Timestamp - s.ep.stream.blockTimestampCache.Add(blockNumber, evt.Timestamp) + // blocks in Fabric does not have a timestamp. instead only transactions have their own timestamps + // so each entry in the cache is a map of (tx index, tx timestamp) + timestamps := make(map[int]int64) + for idx, tx := range block.Transactions { + timestamps[idx] = tx.Timestamp + } + s.ep.stream.blockTimestampCache.Add(blockNumber, timestamps) + evt.Timestamp = timestamps[evt.TransactionIndex] } func (s *subscription) unsubscribe(deleting bool) { diff --git a/internal/events/subscription_test.go b/internal/events/subscription_test.go index 8d1b3f7..f74e994 100644 --- a/internal/events/subscription_test.go +++ b/internal/events/subscription_test.go @@ -18,13 +18,14 @@ import ( "fmt" "testing" + "github.com/hyperledger/firefly-fabconnect/internal/fabric/test" "github.com/stretchr/testify/assert" ) func TestCreateWebhookSub(t *testing.T) { assert := assert.New(t) - rpc := mockRPCClient("") + rpc := test.MockRPCClient("") m := &mockSubMgr{} m.stream = newTestStream(m) diff --git a/internal/events/test_helper.go b/internal/events/test_helper.go index d33eb9d..d82aea5 100644 --- a/internal/events/test_helper.go +++ b/internal/events/test_helper.go @@ -1,4 +1,4 @@ -// Copyright 2019 Kaleido +// Copyright 2022 Kaleido // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,16 +22,10 @@ import ( "os" "testing" - "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" - eventmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/events/service/mocks" - - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/firefly-fabconnect/internal/conf" eventsapi "github.com/hyperledger/firefly-fabconnect/internal/events/api" - "github.com/hyperledger/firefly-fabconnect/internal/fabric/utils" + "github.com/hyperledger/firefly-fabconnect/internal/fabric/test" "github.com/hyperledger/firefly-fabconnect/internal/kvstore" - mockfabric "github.com/hyperledger/firefly-fabconnect/mocks/fabric/client" mockkvstore "github.com/hyperledger/firefly-fabconnect/mocks/kvstore" "github.com/stretchr/testify/mock" ) @@ -115,7 +109,7 @@ func newTestStream(submgr subscriptionManager) *eventStream { func newTestSubscriptionManager() *subscriptionMGR { smconf := &conf.EventstreamConf{} - rpc := mockRPCClient("") + rpc := test.MockRPCClient("") sm := NewSubscriptionManager(smconf, rpc, newMockWebSocket()).(*subscriptionMGR) sm.db = &mockkvstore.KVStore{} sm.config.WebhooksAllowPrivateIPs = true @@ -180,57 +174,8 @@ func testEvent(subID string) *eventData { } } -func mockRPCClient(fromBlock string, withReset ...bool) *mockfabric.RPCClient { - rpc := &mockfabric.RPCClient{} - blockEventChan := make(chan *fab.BlockEvent) - ccEventChan := make(chan *fab.CCEvent) - var roBlockEventChan <-chan *fab.BlockEvent = blockEventChan - var roCCEventChan <-chan *fab.CCEvent = ccEventChan - res := &fab.BlockchainInfoResponse{ - BCI: &common.BlockchainInfo{ - Height: 10, - }, - } - rawBlock := &utils.RawBlock{ - Header: &common.BlockHeader{ - Number: uint64(20), - }, - } - block := &utils.Block{ - Number: uint64(20), - Timestamp: int64(1000000), - } - rpc.On("SubscribeEvent", mock.Anything, mock.Anything).Return(nil, roBlockEventChan, roCCEventChan, nil) - rpc.On("QueryChainInfo", mock.Anything, mock.Anything).Return(res, nil) - rpc.On("QueryBlock", mock.Anything, mock.Anything, mock.Anything).Return(rawBlock, block, nil) - rpc.On("Unregister", mock.Anything).Return() - - go func() { - if fromBlock == "0" { - blockEventChan <- &fab.BlockEvent{ - Block: constructBlock(1), - } - } - blockEventChan <- &fab.BlockEvent{ - Block: constructBlock(11), - } - ccEventChan <- &fab.CCEvent{ - BlockNumber: uint64(10), - TxID: "3144a3ad43dcc11374832bbb71561320de81fd80d69cc8e26a9ea7d3240a5e84", - ChaincodeID: "asset_transfer", - } - if len(withReset) > 0 { - blockEventChan <- &fab.BlockEvent{ - Block: constructBlock(11), - } - } - }() - - return rpc -} - func setupTestSubscription(sm *subscriptionMGR, stream *eventStream, subscriptionName, fromBlock string, withReset ...bool) *eventsapi.SubscriptionInfo { - rpc := mockRPCClient(fromBlock, withReset...) + rpc := test.MockRPCClient(fromBlock, withReset...) sm.rpc = rpc spec := &eventsapi.SubscriptionInfo{ Name: subscriptionName, @@ -243,10 +188,3 @@ func setupTestSubscription(sm *subscriptionMGR, stream *eventStream, subscriptio return spec } - -func constructBlock(number uint64) *common.Block { - mockTx := eventmocks.NewTransactionWithCCEvent("testTxID", peer.TxValidationCode_VALID, "testChaincodeID", "testCCEventName", []byte("testPayload")) - mockBlock := eventmocks.NewBlock("testChannelID", mockTx) - mockBlock.Header.Number = number - return mockBlock -} diff --git a/internal/fabric/client/ledger.go b/internal/fabric/client/ledger.go index a39dd00..598b464 100644 --- a/internal/fabric/client/ledger.go +++ b/internal/fabric/client/ledger.go @@ -90,7 +90,7 @@ func (l *ledgerClientWrapper) queryTransaction(channelId, signer, txId string) ( } ret := make(map[string]interface{}) - ret["tx"] = tx + ret["transaction"] = tx ret["raw"] = envelope return ret, nil } diff --git a/internal/fabric/ledger.go b/internal/fabric/ledger.go deleted file mode 100644 index 9646738..0000000 --- a/internal/fabric/ledger.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2021 Kaleido -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fabric - -import ( - "context" - "time" - - "github.com/hyperledger/firefly-fabconnect/internal/fabric/client" - "github.com/hyperledger/firefly-fabconnect/internal/messages" - - log "github.com/sirupsen/logrus" -) - -type LedgerQuery struct { - ChannelID string - Signer string - TxId string -} - -func NewLedgerQuery(msg *messages.GetTxById, signer string) *LedgerQuery { - return &LedgerQuery{ - ChannelID: msg.Headers.ChannelID, - Signer: msg.Headers.Signer, - TxId: msg.TxId, - } -} - -// Send sends an individual query -func (q *LedgerQuery) Send(ctx context.Context, rpc client.RPCClient) (map[string]interface{}, error) { - start := time.Now().UTC() - - var err error - result, err := rpc.QueryTransaction(q.ChannelID, q.Signer, q.TxId) - callTime := time.Now().UTC().Sub(start) - if err != nil { - log.Warnf("Query transaction %s failed to send: %s [%.2fs]", q.TxId, err, callTime.Seconds()) - } else { - log.Infof("Query transaction %s [%.2fs]", q.TxId, callTime.Seconds()) - } - return result, err -} diff --git a/internal/fabric/query.go b/internal/fabric/query.go deleted file mode 100644 index 3861b51..0000000 --- a/internal/fabric/query.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 Kaleido -// -// SPDX-License-Identifier: Apache-2.0 -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package fabric - -import ( - "context" - "time" - - "github.com/hyperledger/firefly-fabconnect/internal/fabric/client" - "github.com/hyperledger/firefly-fabconnect/internal/messages" - - log "github.com/sirupsen/logrus" -) - -// Query wraps a Fabric transaction, along with the logic to send it over -// JSON/RPC to a node -type Query struct { - ChannelID string - ChaincodeName string - Function string - Args []string - Signer string -} - -func NewQuery(msg *messages.QueryChaincode, signer string) *Query { - return &Query{ - ChannelID: msg.Headers.ChannelID, - ChaincodeName: msg.Headers.ChaincodeName, - Function: msg.Function, - Args: msg.Args, - Signer: msg.Headers.Signer, - } -} - -// Send sends an individual query -func (tx *Query) Send(ctx context.Context, rpc client.RPCClient) ([]byte, error) { - start := time.Now().UTC() - - var err error - result, err := rpc.Query(tx.ChannelID, tx.Signer, tx.ChaincodeName, tx.Function, tx.Args) - callTime := time.Now().UTC().Sub(start) - if err != nil { - log.Warnf("Query [chaincode=%s, func=%s] failed to send: %s [%.2fs]", tx.ChaincodeName, tx.Function, err, callTime.Seconds()) - } else { - log.Infof("Query [chaincode=%s, func=%s] [%.2fs]", tx.ChaincodeName, tx.Function, callTime.Seconds()) - } - return result, err -} diff --git a/internal/fabric/test/helper.go b/internal/fabric/test/helper.go new file mode 100644 index 0000000..0dfba86 --- /dev/null +++ b/internal/fabric/test/helper.go @@ -0,0 +1,90 @@ +// Copyright 2019 Kaleido + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package test + +import ( + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + eventmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/events/service/mocks" + "github.com/hyperledger/firefly-fabconnect/internal/fabric/utils" + mockfabric "github.com/hyperledger/firefly-fabconnect/mocks/fabric/client" + "github.com/stretchr/testify/mock" +) + +func MockRPCClient(fromBlock string, withReset ...bool) *mockfabric.RPCClient { + rpc := &mockfabric.RPCClient{} + blockEventChan := make(chan *fab.BlockEvent) + ccEventChan := make(chan *fab.CCEvent) + var roBlockEventChan <-chan *fab.BlockEvent = blockEventChan + var roCCEventChan <-chan *fab.CCEvent = ccEventChan + res := &fab.BlockchainInfoResponse{ + BCI: &common.BlockchainInfo{ + Height: 10, + }, + } + rawBlock := &utils.RawBlock{ + Header: &common.BlockHeader{ + Number: uint64(20), + }, + } + tx1 := &utils.Transaction{ + Timestamp: int64(1000000), + TxId: "3144a3ad43dcc11374832bbb71561320de81fd80d69cc8e26a9ea7d3240a5e84", + } + block := &utils.Block{ + Number: uint64(20), + Transactions: []*utils.Transaction{tx1}, + } + txResult := make(map[string]interface{}) + chaincodeResult := []byte(`{"AppraisedValue":123000,"Color":"red","ID":"asset01","Owner":"Tom","Size":10}`) + txResult["transaction"] = tx1 + rpc.On("SubscribeEvent", mock.Anything, mock.Anything).Return(nil, roBlockEventChan, roCCEventChan, nil) + rpc.On("Query", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(chaincodeResult, nil) + rpc.On("QueryChainInfo", mock.Anything, mock.Anything).Return(res, nil) + rpc.On("QueryBlock", mock.Anything, mock.Anything, mock.Anything).Return(rawBlock, block, nil) + rpc.On("QueryTransaction", mock.Anything, mock.Anything, mock.Anything).Return(txResult, nil) + rpc.On("Unregister", mock.Anything).Return() + + go func() { + if fromBlock == "0" { + blockEventChan <- &fab.BlockEvent{ + Block: constructBlock(1), + } + } + blockEventChan <- &fab.BlockEvent{ + Block: constructBlock(11), + } + ccEventChan <- &fab.CCEvent{ + BlockNumber: uint64(10), + TxID: "3144a3ad43dcc11374832bbb71561320de81fd80d69cc8e26a9ea7d3240a5e84", + ChaincodeID: "asset_transfer", + } + if len(withReset) > 0 { + blockEventChan <- &fab.BlockEvent{ + Block: constructBlock(11), + } + } + }() + + return rpc +} + +func constructBlock(number uint64) *common.Block { + mockTx := eventmocks.NewTransactionWithCCEvent("testTxID", peer.TxValidationCode_VALID, "testChaincodeID", "testCCEventName", []byte("testPayload")) + mockBlock := eventmocks.NewBlock("testChannelID", mockTx) + mockBlock.Header.Number = number + return mockBlock +} diff --git a/internal/fabric/utils/block.go b/internal/fabric/utils/block.go index df21fa8..ad47262 100644 --- a/internal/fabric/utils/block.go +++ b/internal/fabric/utils/block.go @@ -16,17 +16,38 @@ package utils +import "github.com/hyperledger/fabric-protos-go/peer" + type Block struct { - Number uint64 - Timestamp int64 // unix nano - Transactions []*Transaction + Number uint64 `json:"block_number"` + DataHash string `json:"data_hash"` // hex string + PreviousHash string `json:"previous_hash"` // hex string + + Transactions []*Transaction `json:"transactions"` +} + +type Creator struct { + MspID string `json:"msp_id"` + Cert string `json:"cert"` } type Transaction struct { - Type string - Status string - Proposals []*Proposal + Type string `json:"type"` + TxId string `json:"tx_id"` + Nonce string `json:"nonce"` // hex string + Creator *Creator `json:"creator"` + Status string `json:"status"` + Signature string `json:"signature"` + Timestamp int64 `json:"timestamp"` // unix nano + Actions []*TransactionAction `json:"actions"` } -type Proposal struct { +type TransactionAction struct { + Nonce string `json:"nonce"` // hex string + Creator *Creator `json:"creator"` + TransientMap *map[string][]byte `json:"transient_map"` + ChaincodeId *peer.ChaincodeID `json:"chaincode_id"` + Input *ChaincodeSpecInput `json:"input"` + ProposalHash string `json:"proposal_hash"` // hex string + Event *ChaincodeEvent `json:"event"` } diff --git a/internal/fabric/utils/blockdecoder.go b/internal/fabric/utils/blockdecoder.go index b56b7fe..64803eb 100644 --- a/internal/fabric/utils/blockdecoder.go +++ b/internal/fabric/utils/blockdecoder.go @@ -17,6 +17,8 @@ package utils import ( + "encoding/base64" + "encoding/hex" "strconv" "github.com/golang/protobuf/proto" //nolint @@ -24,6 +26,7 @@ import ( "github.com/hyperledger/fabric-protos-go/msp" "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/firefly-fabconnect/internal/events/api" + "github.com/hyperledger/firefly-fabconnect/internal/utils" "github.com/pkg/errors" ) @@ -34,6 +37,7 @@ func GetEvents(block *common.Block) []*api.EventEntry { return events } for idx, entry := range rawBlock.Data.Data { + timestamp := entry.Payload.Header.ChannelHeader.Timestamp actions := entry.Payload.Data.Actions for _, action := range actions { event := action.Payload.Action.ProposalResponsePayload.Extension.Events @@ -47,7 +51,7 @@ func GetEvents(block *common.Block) []*api.EventEntry { TransactionIndex: idx, EventName: event.EventName, Payload: event.Payload, - Timestamp: rawBlock.timestamp, + Timestamp: timestamp, } events = append(events, &eventEntry) } @@ -89,7 +93,8 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) { } bloc.Number = rawblock.Header.Number - bloc.Timestamp = rawblock.timestamp + bloc.DataHash = hex.EncodeToString(rawblock.Header.DataHash) + bloc.PreviousHash = hex.EncodeToString(rawblock.Header.PreviousHash) return rawblock, bloc, nil } @@ -97,10 +102,11 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) { func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockDataEnvelope, *Transaction, error) { // used for the raw block dataEnv := &BlockDataEnvelope{} - dataEnv.Signature = string(env.Signature) + dataEnv.Signature = base64.StdEncoding.EncodeToString(env.Signature) // used in the user-friendly block transaction := &Transaction{} + transaction.Signature = dataEnv.Signature _payload := &Payload{} dataEnv.Payload = _payload @@ -127,8 +133,27 @@ func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload, return err } - if err := block.decodePayloadData(payload.Data, _payloadData, _transaction); err != nil { - return err + timestamp := _payloadHeader.ChannelHeader.Timestamp + _transaction.Type = _payloadHeader.ChannelHeader.Type + _transaction.Timestamp = timestamp + creator := _payloadHeader.SignatureHeader.Creator + _transaction.Creator = &Creator{ + MspID: creator.Mspid, + Cert: string(creator.IdBytes), + } + _transaction.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce)) + _transaction.TxId = _payloadHeader.ChannelHeader.TxId + + // TODO: support other block types (1=ConfigEvelope, 2=ConfigUpdateEnvelope) + if _payloadHeader.ChannelHeader.Type == common.HeaderType_name[3] { + if err := block.decodeTxPayloadData(payload.Data, _payloadData, _transaction); err != nil { + return err + } + for _, action := range _payloadData.Actions { + if action.Payload.Action.ProposalResponsePayload.Extension.Events != nil { + action.Payload.Action.ProposalResponsePayload.Extension.Events.Timestamp = strconv.FormatInt(timestamp, 10) + } + } } return nil @@ -136,6 +161,7 @@ func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload, func (block *RawBlock) decodePayloadHeader(header *common.Header, _header *PayloadHeader) error { _channelHeader := &ChannelHeader{} + _header.ChannelHeader = _channelHeader channelHeader := &common.ChannelHeader{} if err := proto.Unmarshal(header.ChannelHeader, channelHeader); err != nil { return errors.Wrap(err, "error decoding ChannelHeader from payload") @@ -146,12 +172,13 @@ func (block *RawBlock) decodePayloadHeader(header *common.Header, _header *Paylo _channelHeader.TxId = channelHeader.TxId _channelHeader.Type = common.HeaderType_name[channelHeader.Type] _channelHeader.Version = int(channelHeader.Version) - _header.ChannelHeader = _channelHeader - block.timestamp = _channelHeader.Timestamp - block.channelHeader = _channelHeader _signatureHeader := &SignatureHeader{} _header.SignatureHeader = _signatureHeader + err := block.decodeSignatureHeader(_signatureHeader, header.SignatureHeader) + if err != nil { + return errors.Wrap(err, "error decoding SignatureHeader from payload") + } return nil } @@ -161,7 +188,7 @@ func (block *RawBlock) decodeSignatureHeader(_signatureHeader *SignatureHeader, if err := proto.Unmarshal(bytes, signatureHeader); err != nil { return errors.Wrap(err, "error decoding SignatureHeader from payload") } - _signatureHeader.Nonce = string(signatureHeader.Nonce) + _signatureHeader.Nonce = base64.StdEncoding.EncodeToString(signatureHeader.Nonce) creator := &msp.SerializedIdentity{} if err := proto.Unmarshal(signatureHeader.Creator, creator); err != nil { return errors.Wrap(err, "error decoding Creator from signature header") @@ -170,44 +197,52 @@ func (block *RawBlock) decodeSignatureHeader(_signatureHeader *SignatureHeader, return nil } -func (block *RawBlock) decodePayloadData(payloadData []byte, _payloadData *PayloadData, _transaction *Transaction) error { - _transaction.Type = block.channelHeader.Type - - // TODO: support other block types (1=ConfigEvelope, 2=ConfigUpdateEnvelope) - if block.channelHeader.Type == common.HeaderType_name[3] { - tx := &peer.Transaction{} - if err := proto.Unmarshal(payloadData, tx); err != nil { - return errors.Wrap(err, "error decoding transaction payload data") - } +func (block *RawBlock) decodeTxPayloadData(payloadData []byte, _payloadData *PayloadData, _transaction *Transaction) error { + tx := &peer.Transaction{} + if err := proto.Unmarshal(payloadData, tx); err != nil { + return errors.Wrap(err, "error decoding transaction payload data") + } - // each transaction may pack multiple proposals, for instance a DvP transaction that - // includes both a proposal for the payment and a proposal for the delivery. - _actions := make([]*Action, len(tx.Actions)) - _payloadData.Actions = _actions + // each transaction may pack multiple proposals, for instance a DvP transaction that + // includes both a proposal for the payment and a proposal for the delivery. + _actions := make([]*Action, len(tx.Actions)) + _payloadData.Actions = _actions - proposals := make([]*Proposal, len(tx.Actions)) - _transaction.Proposals = proposals + txActions := make([]*TransactionAction, len(tx.Actions)) + _transaction.Actions = txActions - for i, action := range tx.Actions { - _action := &Action{} - _signatureHeader := &SignatureHeader{} - _action.Header = _signatureHeader - if err := block.decodeSignatureHeader(_signatureHeader, action.Header); err != nil { - return err - } - _actions[i] = _action + for i, action := range tx.Actions { + _action := &Action{} + _signatureHeader := &SignatureHeader{} + _action.Header = _signatureHeader + if err := block.decodeSignatureHeader(_signatureHeader, action.Header); err != nil { + return err + } + _actions[i] = _action - proposal := &Proposal{} - proposals[i] = proposal + txAction := &TransactionAction{} + txActions[i] = txAction - _actionPayload := &ActionPayload{} - _action.Payload = _actionPayload + _actionPayload := &ActionPayload{} + _action.Payload = _actionPayload - if err := block.decodeActionPayload(_actionPayload, action.Payload); err != nil { - return err - } + if err := block.decodeActionPayload(_actionPayload, action.Payload); err != nil { + return err + } + txAction.Nonce = hex.EncodeToString([]byte(_signatureHeader.Nonce)) + txAction.ChaincodeId = _actionPayload.Action.ProposalResponsePayload.Extension.ChaincodeId + creator := _signatureHeader.Creator + txAction.Creator = &Creator{ + MspID: creator.Mspid, + Cert: string(creator.IdBytes), + } + txAction.Event = _actionPayload.Action.ProposalResponsePayload.Extension.Events + if _actionPayload.ChaincodeProposalPayload.Input.ChaincodeSpec != nil { + txAction.Input = _actionPayload.ChaincodeProposalPayload.Input.ChaincodeSpec.Input } + txAction.ProposalHash = hex.EncodeToString([]byte(_actionPayload.Action.ProposalResponsePayload.ProposalHash)) + txAction.TransientMap = _actionPayload.ChaincodeProposalPayload.TransientMap } return nil @@ -288,7 +323,7 @@ func (block *RawBlock) decodeProposalResponsePayload(_proposalResponsePayload *P return errors.Wrap(err, "error decoding chaincode proposal response payload") } - _proposalResponsePayload.ProposalHash = string(prp.ProposalHash) + _proposalResponsePayload.ProposalHash = base64.StdEncoding.EncodeToString(prp.ProposalHash) _extension := &Extension{} _proposalResponsePayload.Extension = _extension @@ -316,9 +351,8 @@ func (block *RawBlock) decodeProposalResponsePayloadExtension(_extension *Extens _extension.Events = _chaincodeEvent _chaincodeEvent.ChaincodeId = ccevt.ChaincodeId _chaincodeEvent.TxId = ccevt.TxId - _chaincodeEvent.Timestamp = strconv.FormatInt(block.timestamp, 10) _chaincodeEvent.EventName = ccevt.EventName - _chaincodeEvent.Payload = ccevt.Payload + _chaincodeEvent.Payload = utils.DecodePayload(ccevt.Payload) return nil } diff --git a/internal/fabric/utils/blockdecoder_test.go b/internal/fabric/utils/blockdecoder_test.go index 7018a59..50eb67f 100644 --- a/internal/fabric/utils/blockdecoder_test.go +++ b/internal/fabric/utils/blockdecoder_test.go @@ -50,7 +50,10 @@ func TestDecodeEndorserBlockWithEvents(t *testing.T) { assert.Regexp("[0-9a-f]{64}", event.TxId) assert.Regexp("[0-9]+", event.Timestamp) assert.Equal("AssetCreated", event.EventName) - assert.Equal("{\"ID\":\"asset05\",\"color\":\"red\",\"size\":10,\"owner\":\"Tom\",\"appraisedValue\":123000}", string(event.Payload)) + m, ok := event.Payload.(map[string]interface{}) + assert.Equal(true, ok) + assert.Equal("asset05", m["ID"]) + assert.Equal(float64(123000), m["appraisedValue"]) cpp := action.Payload.ChaincodeProposalPayload assert.Equal("asset_transfer", cpp.Input.ChaincodeSpec.ChaincodeId.Name) diff --git a/internal/fabric/utils/rawblock.go b/internal/fabric/utils/rawblock.go index 15c24b0..8ba5fd6 100644 --- a/internal/fabric/utils/rawblock.go +++ b/internal/fabric/utils/rawblock.go @@ -26,9 +26,6 @@ type RawBlock struct { Data *BlockData `json:"data"` Header *common.BlockHeader `json:"header"` Metadata *common.BlockMetadata `json:"metadata"` - - channelHeader *ChannelHeader - timestamp int64 } type BlockData struct { @@ -77,11 +74,11 @@ type Extension struct { } type ChaincodeEvent struct { - ChaincodeId string - TxId string - Timestamp string - EventName string - Payload []byte + ChaincodeId string `json:"chaincodeId"` + TxId string `json:"transactionId"` + Timestamp string `json:"timestamp"` + EventName string `json:"eventName"` + Payload interface{} `json:"payload"` } type ChaincodeProposalPayload struct { diff --git a/internal/messages/messages.go b/internal/messages/messages.go index 0daa171..32b5b2a 100644 --- a/internal/messages/messages.go +++ b/internal/messages/messages.go @@ -31,10 +31,6 @@ const ( MsgTypeDeployContract = "DeployContract" // MsgTypeSendTransaction - send a transaction MsgTypeSendTransaction = "SendTransaction" - // MsgTypeQueryChaincode - query a chaincode method - MsgTypeQueryChaincode = "QueryChaincode" - // MsgTypeTxById - get transaction by Id - MsgTypeGetTxById = "GetTxById" MsgTypeTransactionSuccess = "TransactionSuccess" MsgTypeTransactionFailure = "TransactionFailure" @@ -107,6 +103,15 @@ type GetTxById struct { TxId string `json:"txId"` } +type GetChainInfo struct { + RequestCommon +} + +type GetBlock struct { + RequestCommon + BlockNumber uint64 `json:"blockNumber"` +} + // SendTransaction message instructs the bridge to install a contract type SendTransaction struct { RequestCommon diff --git a/internal/rest/restgateway_test.go b/internal/rest/restgateway_test.go index 2e9156e..e45fd6d 100644 --- a/internal/rest/restgateway_test.go +++ b/internal/rest/restgateway_test.go @@ -17,8 +17,11 @@ package rest import ( + "bytes" "encoding/json" "fmt" + "io" + "io/ioutil" "net/http" "net/http/httptest" "net/url" @@ -32,7 +35,9 @@ import ( "github.com/hyperledger/firefly-fabconnect/internal/auth/authtest" "github.com/hyperledger/firefly-fabconnect/internal/conf" "github.com/hyperledger/firefly-fabconnect/internal/errors" + fabtest "github.com/hyperledger/firefly-fabconnect/internal/fabric/test" "github.com/hyperledger/firefly-fabconnect/internal/rest/test" + "github.com/hyperledger/firefly-fabconnect/internal/utils" "github.com/julienschmidt/httprouter" "github.com/stretchr/testify/assert" ) @@ -108,6 +113,131 @@ func TestStartStatusStopNoKafkaHandlerAccessToken(t *testing.T) { } +func TestQueryEndpoints(t *testing.T) { + assert := assert.New(t) + + auth.RegisterSecurityModule(&authtest.TestSecurityModule{}) + + testConfig.HTTP.Port = lastPort + testConfig.HTTP.LocalAddr = "127.0.0.1" + testConfig.RPC.ConfigPath = path.Join(tmpdir, "ccp.yml") + g := NewRESTGateway(testConfig) + err := g.Init() + testRPC := fabtest.MockRPCClient("") + g.processor.Init(testRPC) + assert.NoError(err) + + lastPort++ + var wg sync.WaitGroup + wg.Add(1) + go func() { + err = g.Start() + wg.Done() + }() + + url, _ := url.Parse(fmt.Sprintf("http://localhost:%d/chainInfo?fly-channel=default-channel&fly-signer=user1", g.config.HTTP.Port)) + var resp *http.Response + for i := 0; i < 5; i++ { + time.Sleep(200 * time.Millisecond) + req := &http.Request{URL: url, Method: http.MethodGet, Header: http.Header{ + "authorization": []string{"bearer testat"}, + }} + resp, err = http.DefaultClient.Do(req) + if err == nil { + break + } + } + assert.NoError(err) + assert.Equal(200, resp.StatusCode) + bodyBytes, _ := io.ReadAll(resp.Body) + result := utils.DecodePayload(bodyBytes).(map[string]interface{}) + rr := result["result"].(map[string]interface{}) + assert.Equal(float64(10), rr["height"]) + + url, _ = url.Parse(fmt.Sprintf("http://localhost:%d/chainInfo", g.config.HTTP.Port)) + req := &http.Request{URL: url, Method: http.MethodGet, Header: http.Header{ + "authorization": []string{"bearer testat"}, + }} + resp, err = http.DefaultClient.Do(req) + assert.Equal(400, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + assert.Equal("{\"error\":\"Must specify the channel\"}", string(bodyBytes)) + + url, _ = url.Parse(fmt.Sprintf("http://localhost:%d/chainInfo?fly-channel=default-channel", g.config.HTTP.Port)) + req = &http.Request{URL: url, Method: http.MethodGet, Header: http.Header{ + "authorization": []string{"bearer testat"}, + }} + resp, err = http.DefaultClient.Do(req) + assert.Equal(400, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + assert.Equal("{\"error\":\"Must specify the signer\"}", string(bodyBytes)) + + url, _ = url.Parse(fmt.Sprintf("http://localhost:%d/transactions/3144a3ad43dcc11374832bbb71561320de81fd80d69cc8e26a9ea7d3240a5e84?fly-channel=default-channel&fly-signer=user1", g.config.HTTP.Port)) + req = &http.Request{URL: url, Method: http.MethodGet, Header: http.Header{ + "authorization": []string{"bearer testat"}, + }} + resp, err = http.DefaultClient.Do(req) + assert.Equal(200, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + result = utils.DecodePayload(bodyBytes).(map[string]interface{}) + rr = result["result"].(map[string]interface{}) + tx := rr["transaction"].(map[string]interface{}) + assert.Equal("3144a3ad43dcc11374832bbb71561320de81fd80d69cc8e26a9ea7d3240a5e84", tx["tx_id"]) + assert.Equal(float64(1000000), tx["timestamp"]) + + url, _ = url.Parse(fmt.Sprintf("http://localhost:%d/blocks/20?fly-channel=default-channel&fly-signer=user1", g.config.HTTP.Port)) + req = &http.Request{URL: url, Method: http.MethodGet, Header: http.Header{ + "authorization": []string{"bearer testat"}, + }} + resp, err = http.DefaultClient.Do(req) + assert.Equal(200, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + result = utils.DecodePayload(bodyBytes).(map[string]interface{}) + rr = result["result"].(map[string]interface{}) + block := rr["block"].(map[string]interface{}) + assert.Equal(float64(20), block["block_number"]) + + url, _ = url.Parse(fmt.Sprintf("http://localhost:%d/query?fly-channel=default-channel&fly-signer=user1&fly-chaincode=asset_transfer", g.config.HTTP.Port)) + req = &http.Request{ + URL: url, + Method: http.MethodPost, + Header: http.Header{"authorization": []string{"bearer testat"}}, + Body: ioutil.NopCloser(bytes.NewReader([]byte("{}"))), + } + resp, err = http.DefaultClient.Do(req) + assert.Equal(400, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + assert.Equal("{\"error\":\"Must specify target chaincode function\"}", string(bodyBytes)) + + req.Body = ioutil.NopCloser(bytes.NewReader([]byte("{\"func\":\"\"}"))) + resp, err = http.DefaultClient.Do(req) + assert.Equal(400, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + assert.Equal("{\"error\":\"Target chaincode function must not be empty\"}", string(bodyBytes)) + + req.Body = ioutil.NopCloser(bytes.NewReader([]byte("{\"func\":\"CreateAsset\"}"))) + resp, err = http.DefaultClient.Do(req) + assert.Equal(400, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + assert.Equal("{\"error\":\"must specify args\"}", string(bodyBytes)) + + req.Body = ioutil.NopCloser(bytes.NewReader([]byte("{\"func\":\"CreateAsset\",\"args\":[]}"))) + resp, err = http.DefaultClient.Do(req) + assert.Equal(200, resp.StatusCode) + bodyBytes, _ = io.ReadAll(resp.Body) + result = utils.DecodePayload(bodyBytes).(map[string]interface{}) + rr = result["result"].(map[string]interface{}) + assert.Equal(float64(123000), rr["AppraisedValue"]) + assert.Equal("asset01", rr["ID"]) + + g.srv.Close() + wg.Wait() + assert.EqualError(err, "http: Server closed") + + auth.RegisterSecurityModule(nil) + +} + func TestStartStatusStopNoKafkaHandlerMissingToken(t *testing.T) { assert := assert.New(t) diff --git a/internal/rest/router.go b/internal/rest/router.go index 08c3b37..efcb06c 100644 --- a/internal/rest/router.go +++ b/internal/rest/router.go @@ -73,6 +73,9 @@ func (r *router) addRoutes() { r.httpRouter.GET("/identities", r.listUsers) r.httpRouter.GET("/identities/:username", r.getUser) + r.httpRouter.GET("/chainInfo", r.queryChainInfo) + r.httpRouter.GET("/blocks/:blockNumber", r.queryBlock) + r.httpRouter.POST("/query", r.queryChaincode) r.httpRouter.POST("/transactions", r.sendTransaction) r.httpRouter.GET("/transactions/:txId", r.getTransaction) @@ -139,28 +142,28 @@ func (r *router) serveSwaggerUI(res http.ResponseWriter, req *http.Request, para _, _ = res.Write(utils.SwaggerUIHTML(req.Context())) } -func (r *router) queryChaincode(res http.ResponseWriter, req *http.Request, params httprouter.Params) { +func (r *router) queryChainInfo(res http.ResponseWriter, req *http.Request, params httprouter.Params) { log.Infof("--> %s %s", req.Method, req.URL) + // query requests are always synchronous + r.syncDispatcher.GetChainInfo(res, req, params) +} - msg, err := restutil.BuildQueryMessage(res, req, params) - if err != nil { - errors.RestErrReply(res, req, err.Error, err.StatusCode) - return - } +func (r *router) queryBlock(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + log.Infof("--> %s %s", req.Method, req.URL) // query requests are always synchronous - r.syncDispatcher.DispatchMsgSync(req.Context(), res, req, msg) + r.syncDispatcher.GetBlock(res, req, params) } -func (r *router) getTransaction(res http.ResponseWriter, req *http.Request, params httprouter.Params) { +func (r *router) queryChaincode(res http.ResponseWriter, req *http.Request, params httprouter.Params) { log.Infof("--> %s %s", req.Method, req.URL) + // query requests are always synchronous + r.syncDispatcher.QueryChaincode(res, req, params) +} - msg, err := restutil.BuildTxByIdMessage(res, req, params) - if err != nil { - errors.RestErrReply(res, req, err.Error, err.StatusCode) - return - } +func (r *router) getTransaction(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + log.Infof("--> %s %s", req.Method, req.URL) // query requests are always synchronous - r.syncDispatcher.DispatchMsgSync(req.Context(), res, req, msg) + r.syncDispatcher.GetTxById(res, req, params) } func (r *router) sendTransaction(res http.ResponseWriter, req *http.Request, params httprouter.Params) { diff --git a/internal/rest/sync/syncdispatcher.go b/internal/rest/sync/syncdispatcher.go index 9cb6c16..5a0b78f 100644 --- a/internal/rest/sync/syncdispatcher.go +++ b/internal/rest/sync/syncdispatcher.go @@ -27,8 +27,10 @@ import ( "github.com/hyperledger/firefly-fabconnect/internal/errors" "github.com/hyperledger/firefly-fabconnect/internal/messages" + restutil "github.com/hyperledger/firefly-fabconnect/internal/rest/utils" "github.com/hyperledger/firefly-fabconnect/internal/tx" "github.com/hyperledger/firefly-fabconnect/internal/utils" + "github.com/julienschmidt/httprouter" log "github.com/sirupsen/logrus" ) @@ -37,6 +39,10 @@ import ( // synchronously. We perform those within this package. type SyncDispatcher interface { DispatchMsgSync(ctx context.Context, res http.ResponseWriter, req *http.Request, msg interface{}) + QueryChaincode(res http.ResponseWriter, req *http.Request, params httprouter.Params) + GetTxById(res http.ResponseWriter, req *http.Request, params httprouter.Params) + GetChainInfo(res http.ResponseWriter, req *http.Request, params httprouter.Params) + GetBlock(res http.ResponseWriter, req *http.Request, params httprouter.Params) } type syncDispatcher struct { @@ -148,6 +154,7 @@ func (i *syncResponder) ReplyWithReceipt(receipt messages.ReplyWithHeaders) { i.waiter.Broadcast() } +// handles transactions that require tracking transaction results func (d *syncDispatcher) DispatchMsgSync(ctx context.Context, res http.ResponseWriter, req *http.Request, msg interface{}) { responder := &syncResponder{ res: res, @@ -167,3 +174,104 @@ func (d *syncDispatcher) DispatchMsgSync(ctx context.Context, res http.ResponseW responder.waiter.Wait() } } + +// +// synchronous request to Fabric API endpoints +// + +func (d *syncDispatcher) QueryChaincode(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + start := time.Now().UTC() + msg, err := restutil.BuildQueryMessage(res, req, params) + if err != nil { + errors.RestErrReply(res, req, err.Error, err.StatusCode) + return + } + + result, err1 := d.processor.GetRPCClient().Query(msg.Headers.ChannelID, msg.Headers.Signer, msg.Headers.ChaincodeName, msg.Function, msg.Args) + callTime := time.Now().UTC().Sub(start) + if err1 != nil { + log.Warnf("Query [chaincode=%s, func=%s] failed to send: %s [%.2fs]", msg.Headers.ChaincodeName, msg.Function, err1, callTime.Seconds()) + errors.RestErrReply(res, req, err1, 500) + return + } + log.Infof("Query [chaincode=%s, func=%s] [%.2fs]", msg.Headers.ChaincodeName, msg.Function, callTime.Seconds()) + var reply messages.QueryResult + reply.Headers.ChannelID = msg.Headers.ChannelID + reply.Headers.ID = msg.Headers.ID + reply.Result = utils.DecodePayload(result) + sendReply(res, req, reply) +} + +func (d *syncDispatcher) GetTxById(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + start := time.Now().UTC() + msg, err := restutil.BuildTxByIdMessage(res, req, params) + if err != nil { + errors.RestErrReply(res, req, err.Error, err.StatusCode) + return + } + + result, err1 := d.processor.GetRPCClient().QueryTransaction(msg.Headers.ChannelID, msg.Headers.Signer, msg.TxId) + callTime := time.Now().UTC().Sub(start) + if err1 != nil { + log.Warnf("Query transaction %s failed to send: %s [%.2fs]", msg.TxId, err1, callTime.Seconds()) + errors.RestErrReply(res, req, err1, 500) + return + } + log.Infof("Query transaction %s [%.2fs]", msg.TxId, callTime.Seconds()) + var reply messages.LedgerQueryResult + reply.Result = result + + sendReply(res, req, reply) +} + +func (d *syncDispatcher) GetChainInfo(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + msg, err := restutil.BuildGetChainInfoMessage(res, req, params) + if err != nil { + errors.RestErrReply(res, req, err.Error, err.StatusCode) + return + } + + result, err1 := d.processor.GetRPCClient().QueryChainInfo(msg.Headers.ChannelID, msg.Headers.Signer) + if err1 != nil { + errors.RestErrReply(res, req, err1, 500) + return + } + var reply messages.LedgerQueryResult + m := make(map[string]interface{}) + m["height"] = result.BCI.Height + m["current_block_hash"] = result.BCI.CurrentBlockHash + m["previous_block_hash"] = result.BCI.PreviousBlockHash + reply.Result = m + + sendReply(res, req, reply) +} + +func (d *syncDispatcher) GetBlock(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + msg, err := restutil.BuildGetBlockMessage(res, req, params) + if err != nil { + errors.RestErrReply(res, req, err.Error, err.StatusCode) + return + } + + rawblock, block, err1 := d.processor.GetRPCClient().QueryBlock(msg.Headers.ChannelID, msg.BlockNumber, msg.Headers.Signer) + if err1 != nil { + errors.RestErrReply(res, req, err1, 500) + return + } + var reply messages.LedgerQueryResult + result := make(map[string]interface{}) + result["raw"] = rawblock + result["block"] = block + reply.Result = result + + sendReply(res, req, reply) +} + +func sendReply(res http.ResponseWriter, req *http.Request, content interface{}) { + reply, _ := json.MarshalIndent(content, "", " ") + log.Infof("<-- %s %s [%d]", req.Method, req.URL, 200) + log.Debugf("<-- %s", reply) + res.Header().Set("Content-Type", "application/json") + res.WriteHeader(200) + _, _ = res.Write(reply) +} diff --git a/internal/rest/utils/params.go b/internal/rest/utils/params.go index 2b35e8b..58eb6a7 100644 --- a/internal/rest/utils/params.go +++ b/internal/rest/utils/params.go @@ -103,13 +103,15 @@ func BuildQueryMessage(res http.ResponseWriter, req *http.Request, params httpro msg := messages.QueryChaincode{} msg.Headers.ID = msgId // this could be empty - msg.Headers.MsgType = messages.MsgTypeQueryChaincode msg.Headers.ChannelID = channel msg.Headers.Signer = signer msg.Headers.ChaincodeName = chaincode + if body["func"] == nil { + return nil, NewRestError("Must specify target chaincode function", 400) + } msg.Function = body["func"].(string) if msg.Function == "" { - return nil, NewRestError("Must specify target chaincode function", 400) + return nil, NewRestError("Target chaincode function must not be empty", 400) } argsVal, err := processArgs(body) if err != nil { @@ -138,7 +140,6 @@ func BuildTxByIdMessage(res http.ResponseWriter, req *http.Request, params httpr msg := messages.GetTxById{} msg.Headers.ID = msgId // this could be empty - msg.Headers.MsgType = messages.MsgTypeGetTxById msg.Headers.ChannelID = channel msg.Headers.Signer = signer msg.TxId = params.ByName("txId") @@ -146,6 +147,59 @@ func BuildTxByIdMessage(res http.ResponseWriter, req *http.Request, params httpr return &msg, nil } +func BuildGetChainInfoMessage(res http.ResponseWriter, req *http.Request, params httprouter.Params) (*messages.GetChainInfo, *RestError) { + var body map[string]interface{} + err := req.ParseForm() + if err != nil { + return nil, NewRestError(err.Error(), 400) + } + msgId := getFlyParam("id", body, req) + channel := getFlyParam("channel", body, req) + if channel == "" { + return nil, NewRestError("Must specify the channel", 400) + } + signer := getFlyParam("signer", body, req) + if signer == "" { + return nil, NewRestError("Must specify the signer", 400) + } + + msg := messages.GetChainInfo{} + msg.Headers.ID = msgId // this could be empty + msg.Headers.ChannelID = channel + msg.Headers.Signer = signer + + return &msg, nil +} + +func BuildGetBlockMessage(res http.ResponseWriter, req *http.Request, params httprouter.Params) (*messages.GetBlock, *RestError) { + var body map[string]interface{} + err := req.ParseForm() + if err != nil { + return nil, NewRestError(err.Error(), 400) + } + msgId := getFlyParam("id", body, req) + channel := getFlyParam("channel", body, req) + if channel == "" { + return nil, NewRestError("Must specify the channel", 400) + } + signer := getFlyParam("signer", body, req) + if signer == "" { + return nil, NewRestError("Must specify the signer", 400) + } + blockNumber, err := strconv.ParseUint(params.ByName("blockNumber"), 10, 64) + if err != nil { + return nil, NewRestError("Invalid block number", 400) + } + + msg := messages.GetBlock{} + msg.Headers.ID = msgId // this could be empty + msg.Headers.ChannelID = channel + msg.Headers.Signer = signer + msg.BlockNumber = blockNumber + + return &msg, nil +} + func BuildTxMessage(res http.ResponseWriter, req *http.Request, params httprouter.Params) (*messages.SendTransaction, *TxOpts, *RestError) { body, err := utils.ParseJSONPayload(req) if err != nil { diff --git a/internal/tx/txprocessor.go b/internal/tx/txprocessor.go index d3ddd93..bd85d7f 100644 --- a/internal/tx/txprocessor.go +++ b/internal/tx/txprocessor.go @@ -17,7 +17,6 @@ package tx import ( - "encoding/json" "fmt" "sync" "time" @@ -39,6 +38,7 @@ const ( type TxProcessor interface { OnMessage(TxContext) Init(client.RPCClient) + GetRPCClient() client.RPCClient } var highestID = 1000000 @@ -91,6 +91,10 @@ func (p *txProcessor) Init(rpc client.RPCClient) { p.maxTXWaitTime = time.Duration(p.config.MaxTXWaitTime) * time.Second } +func (p *txProcessor) GetRPCClient() client.RPCClient { + return p.rpc +} + // OnMessage checks the type and dispatches to the correct logic // ** From this point on the processor MUST ensure Reply is called // on txnContext eventually in all scenarios. @@ -107,18 +111,6 @@ func (p *txProcessor) OnMessage(txContext TxContext) { break } p.OnSendTransactionMessage(txContext, &sendTransactionMsg) - case messages.MsgTypeQueryChaincode: - var queryChaincodeMsg messages.QueryChaincode - if unmarshalErr = txContext.Unmarshal(&queryChaincodeMsg); unmarshalErr != nil { - break - } - p.OnQueryChaincodeMessage(txContext, &queryChaincodeMsg) - case messages.MsgTypeGetTxById: - var getTxByIdMsg messages.GetTxById - if unmarshalErr = txContext.Unmarshal(&getTxByIdMsg); unmarshalErr != nil { - break - } - p.OnGetTxByIdMessage(txContext, &getTxByIdMsg) default: unmarshalErr = errors.Errorf(errors.TransactionSendMsgTypeUnknown, headers.MsgType) } @@ -286,49 +278,6 @@ func (p *txProcessor) trackMining(inflight *inflightTx, tx *fabric.Tx) { // p.sendTransactionCommon(txnContext, inflight, tx) // } -func (p *txProcessor) OnQueryChaincodeMessage(txContext TxContext, msg *messages.QueryChaincode) { - - query := fabric.NewQuery(msg, txContext.Headers().Signer) - result, err := query.Send(txContext.Context(), p.rpc) - if err != nil { - txContext.SendErrorReply(500, err) - return - } - var reply messages.QueryResult - reply.Headers.MsgType = messages.MsgTypeQuerySuccess - // first attempt to parse for JSON, if not successful then just decode to string - var structuredArray []map[string]interface{} - err = json.Unmarshal(result, &structuredArray) - if err != nil { - structuredMap := make(map[string]interface{}) - err = json.Unmarshal(result, &structuredMap) - if err != nil { - reply.Result = string(result) - } else { - reply.Result = structuredMap - } - } else { - reply.Result = structuredArray - } - - txContext.Reply(&reply) -} - -func (p *txProcessor) OnGetTxByIdMessage(txContext TxContext, msg *messages.GetTxById) { - - query := fabric.NewLedgerQuery(msg, txContext.Headers().Signer) - result, err := query.Send(txContext.Context(), p.rpc) - if err != nil { - txContext.SendErrorReply(500, err) - return - } - var reply messages.LedgerQueryResult - reply.Headers.MsgType = messages.MsgTypeQuerySuccess - reply.Result = result - - txContext.Reply(&reply) -} - func (p *txProcessor) OnSendTransactionMessage(txContext TxContext, msg *messages.SendTransaction) { inflight, err := p.addInflightWrapper(txContext, &msg.RequestCommon) diff --git a/internal/utils/payload_decoder.go b/internal/utils/payload_decoder.go new file mode 100644 index 0000000..916ad5d --- /dev/null +++ b/internal/utils/payload_decoder.go @@ -0,0 +1,36 @@ +// Copyright 2022 Kaleido +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import "encoding/json" + +func DecodePayload(payload []byte) interface{} { + // first attempt to parse for JSON, if not successful then just decode to string + var structuredArray []map[string]interface{} + err2 := json.Unmarshal(payload, &structuredArray) + if err2 != nil { + structuredMap := make(map[string]interface{}) + err2 = json.Unmarshal(payload, &structuredMap) + if err2 != nil { + return string(payload) + } else { + return structuredMap + } + } else { + return structuredArray + } +} diff --git a/mocks/rest/sync/sync_dispatcher.go b/mocks/rest/sync/sync_dispatcher.go index 3db73d8..9159b4c 100644 --- a/mocks/rest/sync/sync_dispatcher.go +++ b/mocks/rest/sync/sync_dispatcher.go @@ -6,6 +6,8 @@ import ( context "context" http "net/http" + httprouter "github.com/julienschmidt/httprouter" + mock "github.com/stretchr/testify/mock" ) @@ -18,3 +20,23 @@ type SyncDispatcher struct { func (_m *SyncDispatcher) DispatchMsgSync(ctx context.Context, res http.ResponseWriter, req *http.Request, msg interface{}) { _m.Called(ctx, res, req, msg) } + +// GetBlock provides a mock function with given fields: res, req, params +func (_m *SyncDispatcher) GetBlock(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + _m.Called(res, req, params) +} + +// GetChainInfo provides a mock function with given fields: res, req, params +func (_m *SyncDispatcher) GetChainInfo(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + _m.Called(res, req, params) +} + +// GetTxById provides a mock function with given fields: res, req, params +func (_m *SyncDispatcher) GetTxById(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + _m.Called(res, req, params) +} + +// QueryChaincode provides a mock function with given fields: res, req, params +func (_m *SyncDispatcher) QueryChaincode(res http.ResponseWriter, req *http.Request, params httprouter.Params) { + _m.Called(res, req, params) +} diff --git a/mocks/tx/tx_processor.go b/mocks/tx/tx_processor.go index 22951c8..9b33bba 100644 --- a/mocks/tx/tx_processor.go +++ b/mocks/tx/tx_processor.go @@ -14,6 +14,22 @@ type TxProcessor struct { mock.Mock } +// GetRPCClient provides a mock function with given fields: +func (_m *TxProcessor) GetRPCClient() client.RPCClient { + ret := _m.Called() + + var r0 client.RPCClient + if rf, ok := ret.Get(0).(func() client.RPCClient); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(client.RPCClient) + } + } + + return r0 +} + // Init provides a mock function with given fields: _a0 func (_m *TxProcessor) Init(_a0 client.RPCClient) { _m.Called(_a0) From f96216af3216c2a39a1d9384b3f932c508c131d2 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 18 Jan 2022 11:47:44 -0500 Subject: [PATCH 2/6] Updated openapi yaml Signed-off-by: Jim Zhang --- internal/rest/router.go | 2 +- internal/rest/sync/syncdispatcher.go | 5 +- openapi/spec.yaml | 290 +++++++++++++++++++++++++++ 3 files changed, 294 insertions(+), 3 deletions(-) diff --git a/internal/rest/router.go b/internal/rest/router.go index efcb06c..677c5c9 100644 --- a/internal/rest/router.go +++ b/internal/rest/router.go @@ -73,7 +73,7 @@ func (r *router) addRoutes() { r.httpRouter.GET("/identities", r.listUsers) r.httpRouter.GET("/identities/:username", r.getUser) - r.httpRouter.GET("/chainInfo", r.queryChainInfo) + r.httpRouter.GET("/chaininfo", r.queryChainInfo) r.httpRouter.GET("/blocks/:blockNumber", r.queryBlock) r.httpRouter.POST("/query", r.queryChaincode) diff --git a/internal/rest/sync/syncdispatcher.go b/internal/rest/sync/syncdispatcher.go index 5a0b78f..acbd9fc 100644 --- a/internal/rest/sync/syncdispatcher.go +++ b/internal/rest/sync/syncdispatcher.go @@ -18,6 +18,7 @@ package sync import ( "context" + "encoding/hex" "encoding/json" "fmt" "net/http" @@ -239,8 +240,8 @@ func (d *syncDispatcher) GetChainInfo(res http.ResponseWriter, req *http.Request var reply messages.LedgerQueryResult m := make(map[string]interface{}) m["height"] = result.BCI.Height - m["current_block_hash"] = result.BCI.CurrentBlockHash - m["previous_block_hash"] = result.BCI.PreviousBlockHash + m["current_block_hash"] = hex.EncodeToString(result.BCI.CurrentBlockHash) + m["previous_block_hash"] = hex.EncodeToString(result.BCI.PreviousBlockHash) reply.Result = m sendReply(res, req, reply) diff --git a/openapi/spec.yaml b/openapi/spec.yaml index ef22022..a9c37e9 100644 --- a/openapi/spec.yaml +++ b/openapi/spec.yaml @@ -68,6 +68,33 @@ paths: application/json: schema: $ref: "#/components/schemas/identity_enroll_output" + /chaininfo: + get: + summary: Return information of the ledger for a specified channel + parameters: + - $ref: "#/components/parameters/channel" + - $ref: "#/components/parameters/signer" + responses: + 200: + description: Chain info retrieved + content: + application/json: + schema: + $ref: "#/components/schemas/chaininfo" + /blocks/{blockNumber}: + get: + summary: "Query the block by number" + parameters: + - $ref: "#/components/parameters/blockNumber" + - $ref: "#/components/parameters/channel" + - $ref: "#/components/parameters/signer" + responses: + 200: + description: "Transaction retrieved" + content: + application/json: + schema: + $ref: "#/components/schemas/get_block_output" /transactions: post: summary: "Send proposal to peers then send the transaction with the endorsements to the orderer" @@ -94,6 +121,10 @@ paths: responses: 200: description: "Transaction retrieved" + content: + application/json: + schema: + $ref: "#/components/schemas/get_transaction_output" /query: post: summary: "Send query request to the target chaincode" @@ -470,6 +501,259 @@ components: type: string default: "" description: "Optionally specify a regular expression for the event names" + chaininfo: + type: object + properties: + height: + type: integer + current_block_hash: + type: string + description: hexidecimal string for the hash of the current block + previous_block_hash: + type: string + description: hexidecimal string for the hash of the previous block + get_transaction_output: + type: object + properties: + raw: + $ref: "#/components/schemas/raw_transaction" + transaction: + $ref: "#/components/schemas/transaction" + raw_transaction: + type: object + description: The transaction object defined by Fabric protobuf type `common.Envelope` + properties: + payload: + type: object + properties: + data: + type: object + properties: + actions: + type: array + items: + $ref: "#/components/schemas/payload_action" + header: + type: object + properties: + channel_header: + type: object + properties: + channel_id: + type: string + epoch: + type: string + timestamp: + type: integer + description: unix nano time for when the transaction was submitted + tx_id: + type: string + type: + type: string + version: + type: integer + signature_header: + $ref: "#/components/schemas/signature_header" + siganture: + type: string + description: base64 encoded signature bytes + payload_action: + type: object + properties: + header: + $ref: "#/components/schemas/signature_header" + payload: + type: object + properties: + action: + type: object + properties: + proposal_response_payload: + type: object + properties: + extension: + type: object + properties: + chaincode_id: + $ref: "#/components/schemas/chaincode_id" + events: + type: object + properties: + chaincodeId: + type: string + transactionId: + type: string + timestamp: + type: string + description: unix nano of the time when the transaction was submitted + eventName: + type: string + payload: + oneOf: + - type: string + - type: object + - type: array + proposal_hash: + type: string + description: base64 encoded bytes for the proposal's hash + chaincode_proposal_payload: + type: object + properties: + TransientMap: + type: object + input: + type: object + properties: + chaincode_spec: + type: object + properties: + chaincode_id: + $ref: "#/components/schemas/chaincode_id" + input: + $ref: "#/components/schemas/chaincode_input" + chaincode_input: + type: object + properties: + args: + type: array + items: + type: string + is_init: + type: boolean + signature_header: + type: object + properties: + creator: + type: object + properties: + mspid: + type: string + id_bytes: + type: string + description: base64 encoded bytes for the signer's enrollment certificate + nonce: + type: string + description: base64 encoded bytes for the transaction nonce + creator: + type: object + properties: + msp_id: + type: string + cert: + type: string + chaincode_id: + type: object + properties: + name: + type: string + version: + type: string + transaction: + type: object + properties: + type: + type: string + tx_id: + type: string + nonce: + type: string + description: hexidecimal encoded bytes for the transaction nonce + creator: + $ref: "#/components/schemas/creator" + status: + type: string + signature: + type: string + description: base64 encoded bytes for the transaction signature + timestamp: + type: integer + description: unix nano time for when the transaction was submitted + actions: + type: array + items: + $ref: "#/components/schemas/transaction_action" + transaction_action: + type: object + properties: + nonce: + type: string + description: hexidecimal encoded bytes for the transaction action's nonce + creator: + $ref: "#/components/schemas/creator" + transient_map: + type: object + chaincode_id: + $ref: "#/components/schemas/chaincode_id" + input: + $ref: "#/components/schemas/chaincode_input" + proposal_hash: + type: string + description: hexidecimal encoded bytes of the proposal's hash + event: + type: object + properties: + chaincodeId: + type: string + transactionId: + type: string + timestamp: + type: string + eventName: + type: string + payload: + oneOf: + - type: string + - type: object + - type: array + get_block_output: + type: object + properties: + block: + type: object + description: the optimized data structure for the retrieved block + properties: + block_numer: + type: integer + data_hash: + type: string + description: base64 encoded bytes of the hash of the block content + previous_hash: + type: string + description: base64 encoded bytes of the hash of the previous block + transactions: + type: array + items: + $ref: "#/components/schemas/transaction" + raw: + type: object + description: the raw data structure defined by Fabric protobuf type `common.Block` + properties: + data: + type: object + properties: + data: + type: array + items: + $ref: "#/components/schemas/raw_transaction" + header: + type: object + properties: + number: + type: integer + data_hash: + type: string + description: base64 encoded bytes of the hash of the block content + previous_hash: + type: string + description: base64 encoded bytes of the hash of the previous block + metadata: + type: object + properties: + metadata: + description: metadata about the block content, details see Fabric protobuf type `common.BlockMetadataIndex` + type: array + items: + type: string parameters: username: required: true @@ -516,3 +800,9 @@ components: in: "query" schema: type: "string" + blockNumber: + required: true + name: blockNumber + in: path + schema: + type: string From 27f9472c85977224e90843e210c127b5039b1130 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 18 Jan 2022 14:41:42 -0500 Subject: [PATCH 3/6] Updated README Signed-off-by: Jim Zhang --- README.md | 53 ++++++++++++++++++++++++++++++++++-------- images/swagger-ui.png | Bin 0 -> 158404 bytes 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 images/swagger-ui.png diff --git a/README.md b/README.md index b3bb743..c66b608 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,30 @@ # firefly-fabconnect + A reliable REST and websocket API to interact with a Fabric network and stream events. ## Architecture + ### High Level Components + ![high level architecture](/images/arch-1.jpg) ### Objects and Flows + ![objects and flows architecture](/images/arch-2.png) ![kafkal handler architecture](/images/arch-3.png) - The component provides 3 high level sets of API endpoints: + - Client MSPs (aka the wallet): registering and enrolling identities to be used for signing transactions - Transactions: submit transactions and query for transaction result/receipts - Events: subscribe to events with regex based filter and stream to the client app via websocket ## Getting Started + After checking out the repo, simply run `make` to build and test. To launch, first prepare the 2 configurations files: + - sample main config file: ```json @@ -54,12 +60,21 @@ To launch, first prepare the 2 configurations files: - the standard Fabric common connection profile (CCP) file that describes the target Fabric network, at the location specified in the main config file above under `rpc.configPath`. For details on the CCP file, see [Fabric SDK documentation](https://hyperledger.github.io/fabric-sdk-node/release-1.4/tutorial-network-config.html). Note that the CCP file must contain the `client` section, which is required for the fabconnect to act as a client to Fabric networks. Use the following command to launch the connector: + ``` ./fabconnect -f "/Users/me/Documents/ff-test/config.json" ``` +### API Specification + +The API spec can be accessed at the endpoint `/api`, which is presented in the Swagger UI. + +![swagger ui](/images/swagger-ui.png) + ### Hierarchical Configurations + Every configuration parameter can be specified in one of the following ways: + - configuration file that is specified with the `-f` command line parameter. this is overriden by... - environment variables that follows the naming convention: - given a configuration property in the configuration JSON "prop1.prop2" @@ -71,6 +86,7 @@ Every configuration parameter can be specified in one of the following ways: - the command line parameter should be `--prop1-prop2` or a shorthand variation ### Support for both Static and Dynamic Network Topology + There is support for using a full connection profile that describes the entire network, without relying on the peer's discovery service to discover the list of peers to send transaction proposals to. A sample connection profile can be seen in the folder [test/fixture/ccp.yml](/test/fixture/ccp.yml). This mode will be running if both `rpc.useGatewayClient` and `rpc.useGatewayServer` are missing or set to `false`. There is also support for using the dynamic gateway client by relying on the peer's discovery service with a minimal connection profile. A sample connection profile can be seen in the folder [test/fixture/ccp-short.yml](/test/fixture/ccp-short.yml). This mode will be running if `rpc.useGatewayClient` is set to `true`. @@ -78,8 +94,11 @@ There is also support for using the dynamic gateway client by relying on the pee Support for server-based gateway support, available in Fabric 2.4, is coming soon. ### Structured Data Support for Transaction Input with Schema Validation + When calling the `POST /transactions` endpoint, input data can be provided in any of the following formats: + - in the "traditional" array of strings corresponding to the target function's list of input parameters: + ```json POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-channel=default-channel&fly-chaincode=asset_transfer { @@ -90,7 +109,9 @@ POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-cha "args": ["asset204", "red", "10", "Tom", "123000"] } ``` + - provide a `payloadSchema` property in the input payload `headers`, using [JSON Schema](https://json-schema.org/) to define the list of parameters. The root type must be an `array`, with `prefixItems` to define the sequence of parameters: + ```json POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-channel=default-channel&fly-chaincode=asset_transfer { @@ -121,7 +142,9 @@ POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-cha } } ``` + - when using `payloadSchema`, complex parameter structures are supported. Suppose the `CreateAsset` function has the following signature: + ```golang type Asset struct { ID string `json:"ID"` @@ -141,7 +164,9 @@ func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, // implementation... } ``` + Note that the `appraisal` parameter is a complex type, the transaction input data can be specified as follows: + ```json POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-channel=default-channel&fly-chaincode=asset_transfer { @@ -185,9 +210,11 @@ POST http://localhost:3000/transactions?fly-sync=true&fly-signer=user001&fly-cha ``` ### JSON Data Support in Events + If a chaincode publishes events with string or JSON data, fabconnect can be instructed to decode them from the byte array before sending the event to the listening client application. The decoding instructions can be provided during subscription. For example, the following chaincode publishes an event containing a JSON structure in the payload: + ```golang asset := Asset{ ID: id, @@ -202,22 +229,25 @@ For example, the following chaincode publishes an event containing a JSON struct assetJSON, _ := json.Marshal(asset) ctx.GetStub().SetEvent("AssetCreated", assetJSON) ``` + An event subscription can be created as follows which contains instructions to decode the payload bytes: + ```json { - "stream": "es-31e85b01-6440-4cc3-63e9-2aafc0d06466", - "channel": "default-channel", - "name": "sub-1", - "signer": "user001", - "fromBlock": "100", - "filter": { - "chaincodeId": "assettransfercomplex" - }, - "payloadType": "stringifiedJSON" + "stream": "es-31e85b01-6440-4cc3-63e9-2aafc0d06466", + "channel": "default-channel", + "name": "sub-1", + "signer": "user001", + "fromBlock": "100", + "filter": { + "chaincodeId": "assettransfercomplex" + }, + "payloadType": "stringifiedJSON" } ``` Notice the `payloadType` property, which instructs fabconnect to decode the payload bytes into a JSON structure. As a result the client will receive the event JSON as follows: + ```json [ { @@ -243,11 +273,13 @@ Notice the `payloadType` property, which instructs fabconnect to decode the payl Besides `stringifiedJSON`, `string` is also supported as the payload type which represents UTF-8 encoded strings. ### Fixes Needed for multiple subscriptions under the same event stream + The current `fabric-sdk-go` uses an internal cache for event services, which builds keys only using the channel ID. This means if there are multiple subscriptions targeting the same channel, but specify different `fromBlock` parameters, only the first instance will be effective. All subsequent subscriptions will share the same event service, rendering their own `fromBlock` configuration ineffective. A fix has been provided for this in the forked repository [https://github.com/kaleido-io/fabric-sdk-go](https://github.com/kaleido-io/fabric-sdk-go). Follow these simple steps to integrate this fix (until it's contributed back to the official repo): + - clone the repository https://github.com/kaleido-io/fabric-sdk-go and place it peer to the `firefly-fabconnect` folder: ``` workspace-root @@ -262,4 +294,5 @@ Follow these simple steps to integrate this fix (until it's contributed back to - rebuild with `make` ### License + This project is licensed under the Apache 2 License - see the [`LICENSE`](LICENSE) file for details. diff --git a/images/swagger-ui.png b/images/swagger-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..90542d95837bb048c92d9daf30df166293a3c879 GIT binary patch literal 158404 zcmeEubzEIZwk{zgI2<5Ia&Qd<2?W=JySqDt;O4UVBUgr%tSx2LO{SR z81eE-3G?#eOIev47@6opKnMjy$3n|R&Y^moyu9qa;Xx(DC%Fu{5z z6elO?W4VjvI73$_EiZ~c;rZcSK}gZ{=ka{U2X0}=X~OEAV?%@y<*qnrhnPf}XADMV zAf_)e_3v#w>PN};VDSP20|L$k=b#`UzmLS`yatsCJ{_uYvnKeV29!6+eGE@e3N5bz z4PvGTO}}BBo4XKngwThl*LVvN9Y9BRCCM`J62T=OojM|^cy`;|?E}DP5O zTZu@{O3KytrQoMT4&b4J=V)<`zvz&o6iY z_~POSVC>%9!E!3!a^Si1!IW#g~(+BCJBaw zfL8E(Ja5A!v9NtTwRKHsQ{n((lmZ^g9?Buge&jvat@K*Mx@Ow-wBB*%N)!s1#=%#u z=cTZ-HitYW1l8|UgE>N)Jes_Zx|(~|EGUy$h7mEnL_76YV^*iv?AG@!scMnbkj-F1 zdyF@V&NOae*zr=mX*$h&@Y++`RyuFixlntdUV9M{_=oX@p(>z=z(T|3z%s#(z;^I` z7>nBF4#I~E2n+!FI|mGP9&~D~e-`%ElQ<{r3ZoPHB&?swI{s#yXk3*p-h|JNY=ls> z_imH!9OazEis=sRyF^*~t!xCjNWW9Rn+W_C=N4782t&rbOk^(00g?rE1t{o;=Xxy9?VD-4ph35Vc@WE0dWee}E6%gU92N`lJv%BeZK;%gEp;>v7UX8l9t zc$L}T?!E<7{;1rqI{xZ9ZD0)j&G4J~xY?vzq4!wXczjlclws`fn`F8ugQ;We-l#6-6#y_rPbPC%4zWo;9AGT7hlv zy)jM18Do>XsC82fkw5Hz@WrA)yuqplA_3!pT_1|*@|m1W#OSuMa^9QMnla$g(|(TE zE}zVt-t;6Ni_)cFTr!w9xNjR=q+B)$R?bjPt&3q9VIOInehhyz)!w7y$)D9_Dw;&n zLpNcSvxWF+w!cfnL|)r==|i<$RgBU}<#B1-eC>jeDTOJkDS5Tpg4%-0f*D(kdhHTL zgFu7ggjhC-_yT>A={%D&<^$zJ(={^AF49k=0_+5wWo8u?IP6c@=4{f*UP%hf3@lA{ z)pi&xCsxfn?H2KC<=t6RTLlG)1&dX~RVzcCR9&Wis$_nB0h@&zYF0h<4|OFLTzk)b z9aBU-8@WArDb*?QDbO4b*@=>v;wx-TJp;XgCsjQK1Bpk;>rWZ>v7tYaS8~R8@=D4| zMr-D8SY{#;+c-L1w+v6nZrF}*+EzN;}&R( z*xq0_44^G=7W<5OoZX6hlcvI=g3wTY&J=&ItmpNwN3n+)`uXD?H)SRTza4DFU)Q8_}V zl_x%{jA0gGt(Mt^*&x#+v zxVYM(NmwDMh(YhK$8eyWt=E#mB`r|^{b2c#jZMn>QC(K$V~ecaj?<39rkfg8&FL&* zF#kCJ42CL|M#HV6hSYL1NgRn6Ns%aGY_ZyWQ&5A+h}Boe{qt$`?oV(Em)p=gcuK-5 z@pb1j^4A3-3QejwE%kMv68x65orcwuZ}3xaTi=t7*=G))aW9%SAFit;jB84s6o@J7 zWlS{HR37z{wU(msu-!gBXzRJo-(MpZ1MN&ZO{2^j8~O|o%fZmx&&KMw!$&FvL0=2zRr$K>n7>iawR@DZZK~+ zNWY#et<^4-UYwnZnw_*HyyP>kFuXX^Jan6LA3xRWA|H|kN<>OvCgHL~+8gBZPt#d2 zKAA`!G*0{Npbjr_aviuld@qHMrGBfsrG`}#@Z2EE`(X%w$aKjqO)^H4aaCF7eK}X|OLyQLjgw}-1`_hSXWk7wHVx&*`#a>$Jp|c7|q0%@2<}D5gc@c8v}Q` z84z9+NilC*fi@Cpq>!z(uON)BZ(TS@M{nJ|Xdzz$dJIo#E#IW&zwwB3foPX*z^O9G z&3}Ld1bhpk@kmaRyQh)PWMK)}A!JI=5Z>$URkd;JRE0dPvewh9wba`*W|%*G4WY6e zI&zi4!v^JX0?+j@d#cL#(7K}Uvi&lF`(oDi6?%B}jC0QRB?ds?&2^)xu zLr{RzFc6ThP$6D}Q?I~35Pn7wP=BN$AV|S)FhGv*g@6XXQNh1bX^?-GzUfGN{b%}h z)US%%vb@5=;J2)Rq`hQ{9Bp4$EkwYilc4J```3(aRb8ag^Ea1APJ zM>8932P!jbqJMPqXFq)U*1A?k7B)uaX86DQ)z&e$wc#Kn{6*-mfBzV#zJt-fD4ALR zb6VgD()=o+p{4#z^Sfp?Mh5?(*{_m+H2Y&-{~*Wyt20(9BL{sGWj-TQFjv7t<7A;_ zWdDQA|0wy_K>yKF-df*^*W47`(1!D0%kt00|6cgt8~#C3>0cyi>6sY*o#ww4{ae#t zb6}OUG6Iv+{>4O2T6UWMk@wH~>@>eP{BIop$8!FW3tmr7ICh%9WP=lK7D}lX0s;gf z%*QR~@ai!6P13t9U=QAU2zz~~S$J5)I}`vsZEHKvs||B+D!5rs9Rj91;F*ND#1_4v_CUtRGbeUq$@Q(82Xa z@?qd&hd)KDY{t$fqh%>=vCEV?Xpk!rhHice6GS>>rhu--i+o}W=^NLS(L0LjD0l4& zFJygp9S9}l@t+_3VW@LrGBUhCYdo(68haf#qp_N_$=x^$b{vjDYZo-*9yt|U!TD@= zq=Cawr2ch0Suc32Ke`+jPBVETl&R1aCYK?VMuj`$PO#D0H2Xb6(qIpltc2`}IPifl za|IgCE;$idou}Kb@WKNJa3Y+3NVrtt8Giid6kIo3EqF2SPmW>{E94=Ae+_(G7=p*4 z;?t*WYx84){~!aa{1EB4kJ5>H3+GQ7{B$H7qCUdp&OB~=^tq)`d9gH0-aY`Nf@Rv4 zNoFeH>?TJLDe?`b!8hdFR45^QGC*z?d%HGAq9)mqaVnMY8_`W4ZA*mT%p}Yd=F+i5 zw|egrTLL2_e?PI%R!o-*X2-_!qPsWlo6YsmNIGI4HtEDsa+oTsF+bs9%Hl}yx`HO5 z;0i`Reui69v{X zwco=g--wi*3-a{i3A2z66_bAqbQgo|gO8OeRHCihO%L4cM3ao7kk8*ue|fwJUA?*( z;TkAbZ)iUnX8%2_{%L(YDS28|!-q4G2-)FCYc9)aJ47ff*%(3`tU984b#Akv;xkEC zeTE*h{V$y?W&QC}XlO&DiX$aIEAKppLpLQLiTFW0cVTM6Kv~WnFEx;mlMByR8fDZF z_}=VI2$o#n{jHe*O_Q!RU@glze#iV6(-Zjvr9IJZrNHOZHDyaoU@n1t+;o*O{!%)^ zL^&m`sZ;d|?rN6PNhtG-WGrCq6q1SOcD2oWYlvxue#i*- z>FT_0CuKrZcG*Xpd%^wXsRk5g4`iP~N$;8)xx3hbM*ng}h_WSiXl;^FA~9lDn8=yH zytgZOw%%nsN2}Z2E6MfbqyF0NtPAtN_R#Nn(Xt1}vv&V$;D53Re`ttufJccAv(N5e z`KG{_c!+Wnuh-+9&8>!(mslK1c9RG%lwII_w#iDfYZXMWDNNsRALEdlKN}L;#o(9M z%iDk|>1yo`Usjy-FMPJT*_1kQy#BslL+FGA6XI7Vq6Be$EQuU=R|Q;8-I%t50U>S# z#0Z_%6QR|n3;Xxd-9hNKKw9jh_%J#Hya~ow;xHg=p=OH)Skg1e=8op8jLrB~S+;v{ zoyFcFVdsvh8gRZHX&?NJYxQJC`>2vu+i0=Jr0ra0aslah=m+eG5jNh2j}3`QfAgcS zTitl`xuits;FuE67d&T&cvcD@*raBTq>dWOfyZMl$Pq7+^IlJ!<>j5z-V}PH2#gK_ zB;@yW#GMyFF#;>zufAG`mz@H*oklRDfaLI#%R)v%ws}wSBxY&&A+s@TjWs|qL1AL; zR);U*1fgAlLJ=e++x+wS08N(d!Sp=ufgT_=tN)H4J1aw#p-5oD&4!^oia-mw)YMA1ky43=!Ejkzbg5tmV>GX7;jm`Y zSF)Wip|t7(Rez@2MH@ag7nc){jhEQhA3++J&H!adq?6dc))q_$_?hhM8W9lz30XH$ z0o)Sf5I_**0K_LRZ+tX_ve@8;{rTal`K%jApdT!BWHQjnX%DxMi|Kowi)q_FN*455 zWBK?TFrp2ZPJUbF3SQWE?a5NLDF?7F7eh=|ZErAz*4n>YbHGFXZfTQvv=)}d&mYpw zc{ExEU&e0;w)-{;G7lbZ=_=X=?YrzQo2AGbP@5f<(}NmIDzO(66+b&}!>V8JU4@!c zk;UOx%mgOdP_1yPs7OZ49eJ8Uo;go5@#~NG2wUCbuw4=-h&P->$7clgvbj?9f=#jT z3|lpu3Rkm=Ou!n@0EaV0OWFG~qWL!mJ|cFQ7zYAM7(Q(xJ>9u9?$=bLiS#+M;IbT0 zaCR7Si6vS{*Lb4@-Cg&^qW^mJc{gxTxf^Eb>!*l#PN4LXGA`w3q?+AOx)IKY!itYG z!uX14vlWJ+AOx_!-!S-*h{p6nmw#m!EG94Ak2mY;Gr-$1FEF z57b(2=uPHIM{v2jxe^@@vzF{!~FuM>=z=7%Y7+`ft2buK<*SsVX6!#0P^T%dU0#t>NMkSoS*mqZI=0mLsCO z^X*u{VP`=&(swbuf--umxdmGZ)%F9h=!paQN1}Qg!wlo)-9|FZWw|EvRSFs(9z0Cu zo2FH`P$NYr!xGszD6#^hW^6Nt^%HjnE?Sm8k^3RqIN{U+p`;rn5bp$_{5X?h9 z5+7a&KTyj4TCyZfU^Ew@@iwW*4~iOo@d#L2?3B$}^P?n46$C$jBwFlh_G)|~U-gf5?kSybC{K0*c1f*cox6Z`gO&~>tM_Aq`$ZauGI#4ce@d)4^}Ta*LxeBW0-V$4F==sD6lslxINq0 zT6R8(K3b?5h?L~aJE%Wxhu>N-Z;9>>1`dE7Qwi6=Nz+Y=SUerSO0`MecOxHh@H-x3 z`sQeX4D4P(}_Y0r(o1eNazEaOr&EKZkSJwc(?!7MA6yJNfpA=u>ZPgja*P1YkEiZ?*YYO^JZ zERkp{JMV+v0nH+qC~_6)`Ds?plGMG&&8?Qc`{V+>oo7>-A{%vbjoeDL(aKO_bV`D) zz^rdBXfe;ZZ_YL&7_yXpMvRw^_Kla0_hrN7$)(q`J3Tv(zg)oUkF=?K+EmC?$(cIl zE*yxMa2=zhgwaUv%QFl6UO%dtJsi5Q*d68)#}@uPkfe~~*;o9UZ%-4~QktZ4qgIzc z1RU!VOi!xz!*cH92 zE)}@6^wtcm5#Og{7^hafez}?V+cT!X)}$NTrCSlTB!^Hp!l$1DQzot;70qVVl7;6Z zC8HC+qC#QsSjKf7UM#ORHf)1VEeG?Cpjp#SLm~_U8@NH3IZ_-Wf8h|P`>O*o`2vNh zgIUIPrv6s^Vxkb{eUCV#Gi)9-o?5K%WrxTN_GxJ(Q=(0{{`4f}9=B|E(rKSHq?n^O zmUzo+0_-#2??S;l0DlM{9hm;D{^$v>n{4sK&EtlX7_t-x9%tp2i($5VYs3-@-H&Xh z^Yq*%ZZ6&U79D6V7u!Sg6F{C@b^9ff3WK4DSPds(`0?4Bc*B%2`PC!{g2$>E4YbLZ zIm6Vxf|6#6ug>j0u-Wmg=amx@6JAv;f&?2kEABIQit4t}8g931eohF1m^2t-P66eo zx4tzs6M$;iBM|!wcqov{yKIeXU<+rN%xJ#c2*OqT4P3)#L(MJpIpjP#fp?6iR1-kn zsna};AQF@%l1`c`jkU?O4v|=~w#aE@qrGBkv|d!bF^Nijxf4Uep3jL~6L!-N({gRJ zS&_wsfmwSgOw2y$crGRUmOg|#By%NF;U{xb$=vL;&_bs>m1<=)kZ3c#mf^Q~VMMgq zz=F4X`;U>eyc-hZ?e0XE!YUTV}I148diIF<&|bac{Sudf5KNO%YbqMN+6AqAq+~5 zA0w2B|NQB&?@ks<2dxn)!a6Ja0D(#a*-t)Y{}~Bh9GLH3Hk&8QO{(zo-Lgx{7zep% zDcgX~GrFy@;xCNM_pr!zJ{+`=Ee+IW;60fGnnw*ipRNzkGnz_Ofd=f8q;bxnb|8HC z+Tg3djuAFBp7(#U^+z!wCMMpsZlBDPt%9z*3$oiA=T|6H`Z=(H$5}u+;*de47eG51 zkRvvThC_-YTImU(plQBMr_^j#`!<>T^Sje#+Xc37DcNU-nhbUSb%(XbHTtBJL1yEb z;-U)bmPadcsWcA_uD#62%W?8&wwoQ+-{5~edq}~M?EN4>6IU{;njfKOF9KWkGawyG zV?l!V-h1UT#@p{^pr&FaHrCm-q@v){h*hFIg@!m;hw&p=3MLOKX2hbjG2M%166HDg zo0FcMMvF)Ct>C9e0RC5)HMP<^+@O(EChqU@EV=v+J{Eau`39qIEUq1jv@R{@2sF`T zS6Zc>MRVI8NY<#-odKp?nem@Lbwy#}_Sxxy5CoUH+2$z;MB=4B0&)r5c-0u+o?&}2 zHFmFYBheUFl%Vts>BSr1m1LpxHg%fcxB*(nnEZFv_{=6WVRqK~>kf7p!CPdCSBvkw zm0X5d^6cGECV;r}r~A!&*^_LgV0;;^YTm?98q5*wZ^BHd2WJ8Xp{(q%)0P)Q27pz9 z{KP&h@%LiOM)%s|NduzPb$o$qr`x{(Th(pwxj!_L_i6C$Dpn+|gJ%O3IK&y|Ud!{Fcmm7!*1{?Gz1roq?k{$0Ggf9B1va^;QM`-C=&G{GYe^4BqH`JFEi#X| zDw*)bFeMP4j?^nbXC8{T-_UCoXN#W}Zd>wO_Do4Xyf7s&;rmMtX)1lS7CTiEuLT3@-U3j2&FI6*b-OA4EM^d~gM^PZ5b>EMi19T?L|j)BYL+V#J-u1!_#3 z_A-i2;PR2f^pR!;sOAcAc2aCC?Y$caR%BVX&oHZmJ~4NQSl~0dguO~bB1-s)mt6v= zO7^WLaA6tHH8Jp#l*K*bx#hu=!f40258}jTvlH`E^0YWX+FCaWJRlBBgk7!h5he?E zJ#B;b0+7Q5_>l3O@+=-9;jn%kG2VE^X|`CKHiE9>(*a<^j?`-hcsHmw*f~Azj%AD& zsWNfU?&L~mK;iD5uKM0CH9A_Wzol)lMDhfgUjRlDnM#nN&wGhNd*Nvs({5%jM>Bg9Y02}*$Wv!D zppI+Wj&Ju<^CL1huBbz1!veoDQIal4#80kTBZw2(Nl_xtBngN}r4QWib&qDtGPHR5 z!QNTL*;_GRPFTZn3q5$v3!WOVf$|Iv&Ghmjb7bCh@x`1pKNRD)*zAqpUj#Tpu4*^i z9jWALG}Q-T(egOQAcHp-GjC|j7JGSW(ZurWz>u--<@wlWsOy(|d>O%n=2+ijR4t9m^!6)pm?{iN%$KQ#Hi>QZ{qv-(WFHu3nMYhf zC4!+MqF9>aw6dX*^oMqOgjC_q4}RRBm!q1M&GG^239otyxfcswB-$qJQ-l{V)_=2u z0&`mW3$WultiB>wsmxvOHY}4PXK*g-D8*}MjR%^O!vB=?MZ(F~O$5^7xccO<@mY&O z!Es~9^w0v!t(PXZB)%*d81-8U|HjcMheM>3@aZB(O2+s`OHA{lynYPaDgUu}a~P zqOO1Df~c@emAG5kP!_D^!-Ak_-?8`|ah>*$OP6y8HJ~cy^dQwoqLWWW6$y4LKn3=f z#u>A@M*Mmys_N?X__x}KkSvWA$&hsol7J=v-76i2_GA#Cr+x^(IRnAR$;o^HhL(y6 zjd`PN-s#p{{YlXG^QsZRhmBmRG;K0pUX(&EH|?OKGxq9WW+qkIA#5c6iy(kCi!T6` z;E3SuZTok4+d<1tV!xT%g``NMggTf-{lx{CIml3JhscIE-zJ%irzw|qB1mM()@Pet z8?L|l2}u(D#sdwKj_x#t#4q`jTp{m0SIbc=%YOFUrb@Lzb4ZMpA-wTI{Ib;fy3qeg_|=W-YGW*xy3bqMtNy`lGyp-uk;CClggJud*wS#>HNGj4ZRX!GVx28um! zVtJ8fYTj=+OFP=9_Nr(mggv0ri6BDHhLhR+sDc7MCo&jsE0faFN`GK7JB$xSnA9GA zr464imV}9KL&SJHK+}Rv=#zM_Qu!4{4xP`ECk?6<~Nlm|=#xiF-T`%>)=_JKqTieaTy<>Xfnf{-lVA-18hIGk+wLQe*%qd|o_zL8|V z0q$X-dzj72xV62Eu!@_0blQ$&^EGF~AMnJKl` zOPc(A#`yyzuUZh%xvrdVN^964FMIZNE6{(efq<2F|-#Q*r z9}V(hbjkhUa5WFJkG}4(ZKQ#!j+Gb|pRwWWJD4#C0tIH1R*zTQ>-6Jc$h;{N_`Vs` zo#=R941$Ken1ksj{Q}s;RRL?^zNVLsyH z6^_)F^tZ>bIkRjy_T+72Y6mFguph4Z5n;V?!%z4@^f{`lqs9GVxLI*2<| z9I)+AWS=%*k0pTVp_pdu)Xe#KcznT+ss8Tt(5|>)pst31y5nNr!6orA)%{snvYf?o zo$u&-Fr9?=E&JEeG>?zRgWmXvuc0T*Gr3`Uu*5j~u`Cz;=Y+m&QyPwgEeFn(79N>q zP}||_aNLxQzdalDf8$9>?#l*UZQF;^HX!NUYQp0KDz*L47`uiNyoF=GNTBITA-_?FOd6T z7Vjzpc^E(jh#$*FNhSCO9SAB=SjoG7*h>_4>1A;Ea$T zw+#~$8dHk&_}s*Gp$U4wefuG>&; z!46F_#r0a*u-q1h(0(L^qsqgyy*$oKsAm9XO{hm{)I6x=+f@F%@lsR|0_Y7+Lq@jB!4I{)MART=g1Q-QdAcntRr-f4Yc}I)JONise0;LJl1Mmtr z@Ub+}#nIQwMP3kMS{}9xcUu= z`Kwf*q4i%z=f5PvI$%IhD^=GXCXa?CPDf|1Ht#wn{1=bP4wof#ZBYVwlYFJbX!iVb zmHzJT1w2}>CVmMvrvtqiMM;p&tl(hf1p}p&PA{_b;lKSTq)&)Ft92Q3_wEu%<{&Soy zFj5FNf%FRmzZ>^or)d7bS3lM)c}IM~$m#bA@<;w7A&7SI|AlGi*s>Tk({rDHC#nA? zySE^Zlr>I}0os3&h<|gX|HwZh0pB)iew_PVK<{6w;DHQYs|Sd9@qcCg8>zoCJ7d8Y zj&9Ele|J{!Z%L{-L|Nl6G zFs|mI3fupI6FdtAw6>Gplz;8BQf@GdJY23248i4yIi}toN)(KU_#tA|WhGN0foL>; z>ti#cZs2No!lf&ax+{aOo<9Wexo%VBL_x%JGk*n!*Gy6|`&Z-Gs~`UPVn8Cnc- zj5@TUzRJtEjAfbERHo}y+qn+oA-{y)_u3<)ymeG1ao#qWQP{!ct) zh5D-HyG3atyKVL3X9E{Gtcr3#$g4r6hlt1e&FkFmcAmd5%o$N@+}fG$NX&l+Q~1YF z2E!VFXh8>B^c$cwqy5Poux#WzJbILmkB|SF0x3Cnqs;emE15Xvb!+*r?-B(asL(5b zC8*!O^%uYXq&HWcs(-c}KHVHmJr1HEO)k86Ub=5O%JRO`|ylB`}ZcGP7p5nQLMzpYbv( z)Cer6(W(MV2^skiA1yZnkeKCd|$&*jcZ9ut*$pG1t_ z9~;f?c+NK=mmRHKVb`=;Wx0Vd*Q}d!xZ)0iBQFUnpgsxDzM-CA?fp>!<(cG?Bo@yo ziiF3B+F7U9w{FGLG|z2R9ej1b)PEjr%v1h>sgQ<05Cc}&kmnMLR5Cu#y^-xES2f2s zid?-v{@sl&`0H1x9f_Q1N@bpJ)4BU%>-X+ZF(pb=3)QA0pF`RA=hrhy$wV}+aPs*R zrMddYhy=a&P3O`BCi9i4K2jB!=)HvwqAGAVqcdJCbJ4sxULb?Vq(&^3!7(IbFvGt) z--4G29h>&_hSs0TuY%+ph$)Ha5{>Qid<~tY7qBidiBUGo-z#9BAaluOu-KG%?lR8xAPQHQl$7k zL@4Riy_U3%E@RoR2wX0f9m@@NG-awBpt;rMMQp+%3l>Q#rkf~sl5g|wi+f8N`)l&` zz1}I}ZW^YZh7`^|2Of&gr7bMjYGn;BoN_jGFd#nvGxq0MD2P80u;z{HD4|fOW=e83 zZjCHD5&1#o!X!Wv5ob*~wy06~lUi*Uz3I4A@xjEvh2Kf#d+W`$9$qq`x|id`t-~*D z@%iJGV&?ARM7QDA-FrDAE5_9Fm}z;-aHKR9vA1hi`v-Wvv6?Shru*ju)M;2|B#{lM z)*Y7PFUNko%W_FI7SBKQT7r`COIog^g~}>Z9(X8vw_pXMp%*-y@nC<3gC=ih zSl%wsxo$6}&CgexQA=_?gQPw%$-u5&obPDL>UeGBm~%y?^TQ4N?C^VY;=S;8(Jgt0 z!Ly-oWg)(efX&5>PIs})?F55TiK1qOfjM*DyxHN?(l8o9^i!= z=Vh#5V*w86{<2xF-fj(s5GL>dcONO*tl?lc~@m zx;XOXFFRI_;Yms2+d(ue3Ioju!*7JrF%jsr()@Hjvm$#{urQ~-8S`=#L--v&r-pUq z+MDb;M>9y_DTOo?p?w0!j5&}^{10Cd)&;wo*PoM$ z^OKY)>AkJwOkPenuH*WfI|K2GU~Y#$nM|3Ig;g*}}EK0=xrz zarpTnyFKgI3#YAi^GR1HZ+*`5NhBld4rIGd-)=eY>3phwq39EV@=IIJo_9T;TD-8i zzhBBT1x9zGUe+}-Wc|eEuoH?E?p0J;PMPO}-Et<*1EKFpUC<^Yy;`E0 zHMh7@#@yuk6DxR9a^suF>~^^)-^vFptsTykxx?YsTud}StSLI~McY4_&~=y=X;er! z-5iwQg$`NhOUrpAV%0i=h&(Nz9Flx8I{y6dKnU-Ze;NmM6SX)`ZADgm#dhD3JyN)O z!wqv}UCI!Qi4+qbsdzlf<%fqJP3d4AMW50Lm=`s>$g8BwhI_`4BV+#v7j&ky-XkV%Hv6UYTXZ?lU8F$c zD&m&r5$gDU(tA@E;BY%-u2?$v>Acx{^i_$c=xFCbP-$T7t4a4P5n?V zm(WV%3Bn0de-T6bI@KhYQ|16P2S!oIx-f`hG&l@Oz*R3Ti@gqy7jf z1a9qUcF+A7P_w1tyuNA46wSGwb|KwXgY1gxlmjkIf9rbjhz&&$Tk zMckFk^`jDqD>70(qD5-Du~&Fg%T|BY-J9EGLq{c179X4wD^(}Yi#}v773(5DDdf#s z9CeBhp;~V4*19tqv-PYWGyL!;7zRxfhv|1(_<#bs-SC*CuMg)w@lO?Q?$JJeuD$DC zo>r$dn$AgeCn{;WlNRD+H#q&O)$Yy}!waV;!xP;f&HiAVpjP@dZ;??e?5YRQXqkcD zv4$9xZs6jF521~(vQVb0C^TlZFR18uUBolqL6TYXWMRvCaeB#3ztMl`_Ho$pqO)5T zH#AL0%uz>5(;$>hp$B$(qUqtfFNSI%%jJ$vrWADP6?@^}Ux|BkhMI@Fy#T9sedc?E zoEoU)!Z-+nlrPwMk%xD4cY*ifX?|WljtZ$v*<;!r6TcC9_3*tOj+3cF>^=7sqC=mUmA2qn@*<)Gf>TF1mfl?n~}*Qt>aBtKv~&vagrLoEw^ zzFc+~pa)ikEhUBqt^w^N<+X(;?X8yijMUCP#Fsnef{_LOchr8N36YrtiqF#9`#3EH z0aObG0J1KddD<-q^v`NUxh*1HDtRY^E;5Ra{!AqWqIv?`Qv-NU!Dx#7_}EwhnX6G z?F^?#JdRm@_;8+feqB1&n`t(>$*H$+?xJW-X}WmfheDC2-O9vX3rk;@#!k$3i#r`P^17^ zZx_c1;-e7EUfd_`qk2RqugR>JVn7g%Wv%v9fqeL;2zq84tnFr_nR=Gb{TWKIzRhCz zy#JJ?m*oa_nckk2a<17kvwN$e3|G>A!3d}*VJdS$IXTN_FCt~>$JE*S9EjAgP!UZn z_})%T=#%`Uo_yTl%z)diQDLE46(h6iQs|!ZkiOT&a^c)d?@FX0)YeR%y1FBD3L!KS zc34UCo$zx4vp5*es=*PHezV-_d~2JfJmI_lI7<1o#RB-AW7)32p&HNa+*S&$_Wfju zBn?%R*7@p4%cDXJm1b7z`TO%#h&>mThO~2|xz;l}xAu3oi&PUk05KVa4sm%06}$ae z1&O0k)_vHRoll{IeX@pm^)bDaSH!3Y*ojQ0nJMW;0KgeA^K*L^M zEBl}+z>;I@Qbuvo)pr+PS@k_xq0<%Iz1cAgk*40w5b^niO<4G(1)d%Qd zxZ7u=tm1G9wyL75dBSle3)jY?Ysp8p$3Mw}u?E}n;VQ!ndPgJ4zCd0) zhPBGLpClh)+B8P_s(#_e)eZ*JUC%S8bPToX&mIICSFGiM%}u!bho28DIR^(EhMK1= z^w!ivaq&*-$Adlf7fz3t!#O@`6B%MntPFb1TTZx+V@e|z_4&Xse!aT;q{fT2nf>{- zG&k0rY`3OMOGIsK^Q#qEgYQMf8p-?9k1AOWed|L#p*OlcleMvn6o!+|@{&GxE!D=G zt3QvNI3!c#{8G4LxiUL4wIal@I?oJyFlC;E&^wJ|I0YSDLltI_@1oO|1r3Va)f42K zmEkv=RK;Rn`&I8f!I5S@o5fY`8^yPr5mEUPW#59mPTps7=V<)Y{U}=JC)N7KJ4My2 z_`iOKmWy9=I_@V_<9NOwjgl)E&*vnEdjKwts7MQL; z*iX9?T9$=-!iZP8ZA>d?TBD)#6`|Je3*10oT^^c7lH`rsfIn@`j$Wizdn(91>}p2l z$pK}Ycz4N{EmfMGx*txuHY8h2@?lig``$Eu4&MWsyHjGSJA^DcEXha2#R24oO*+3V zW^0 zZvkM%3HP^Nw>_A{KR_XMrbtf83c}QERp(6@z-yE=pZ#n+Jp|a>&6+mWoaBN(csOy# zl|kY`*T`rA5+N_!C$2hAllyBy!&oxXvMX%d96Z6jcX^BU`5Bpug}Od&pF-{fdj``Mqdu+o4!_pk=;?^VV8?J{&~UFkcb@I2AbXbfvBzt zg<9XzxhciV%XjVKj*Fn~yN8}scP3#^5oM?cuv-{PJaI1lr z;xUO=)nxM5pUN(a?70vtjTGiGHf~e}9j$QLOYciTcMzuVd5EGTy@Qqn)X$ig_u$sg+j{ z4wtf{Uyd(x*_1au%>3Hmru*$&jnxo3=I()%S|H(*AMM5^agxSE?8g*hG6v4Jgr(f} zsq>lhw(ZfQd`1r($yO6>8Xl zYPXW6dKA_yqb#7_b^Xj%!<{0zr>jo_sN!oHXOw!?2J)TrR)$hES1dBB4VHQ}FS*pC7Xjof&n8~2oXQ9aUP)7iwxv3a^*8k9_6_@$nY7Y}XHgUM z%jHhn*Q$*oc5RivatG1DFgI{?(FtvQ7v~=byEU22-`%MrP^SYqK^c49ukE1mhwGuw zwN-DmiNZOLbf*5o6T;mk*-n22?)Nem+Qm7&miw_d$RsDdGxb);v0C!M*!{(iRV^st zm7O0jv`FAA{$R9bQ%-C9!`{k`t-hYBkm$y%KbuIEt)2sV%t&#*{^w%xda8jsmBon5CHJBKOJ?p|YHS1Xu1^v&(1(-KPs}$RR~O zzb6Yo@Le~C57rOE-N>LCzG+KG-~qhb3N4Ylr+Rs=q*-~+R#*4>`jDjS#lHsN9vw4n-zZUP%))w)xDh+^XEC`*0A>Mi<#q9G_~>2z#xeWa@csnU#FqBs=dNkJoVMbPsmgow)w=zOR(&Ys~8>o zfm37SUiT5gw$1!){*2_z&;!19AGVxjcUD)inT;^K{Y&}O%WY!vJ3Y8x#f{5AGX|v*CsWF~tTjN4ELJfv?KhattDWL1h%$9yDXB@$4e-g1`o! ztswTBw#N|858>`_xo1opNRnjQEK^(N!v&=(cgom*FHSw*FYR{2&9~iu8Wn#o@c^Lf zAl;%7!>GNmS|Hr+5#2qS4%$rrPTsm?0zX}14ES)#O@HXg{ofG--KMm{Mu$s8he$jQ1;&0oC z7!7)MnQ|-QVD0>;_X&}i%7bbP+b}G8D`Zt#!*703J`;a~cX#d(k-&lBJkL6k$M19S z!f#Q7rdTL1KDklMYN?=KX>2X8uXB)$njcGq1w_OO6CzA9mL8g>JM3}=$PrI*Be&uV zi?UTe*+)M`9X89^gaj$fMRqUUuvZYcjF-5991mr<%bWvB`5X!yJyn?3fZL@@x^dL9 zPTUlR?#x+VbI(`V6XDV!Ndo7`0RQjFdVL0mZq| z)xo~Zf`HPTo7J+OX^o?e9iX1IE$|4pYJZCfq@YVw0&SPh;(-Ek=V%0y9caC>jEwPs zObs2cds8pK;a#ouuZ%G&zY^c5seHt`KZOL0M0N5NsHaOf2I`8*6YRP$zDH%>zSUkc zkaLXSy?i5cR>Idb-nu2j(-zsYk_yP{SiMiq4c09a?I~mip(gIteF2_vjPaykvc3e| zx7n?e9$eNib1Mwru7&$Xm&OVD0ESoW%7dj#cfrhI4D*G9dtgcA!G93h(N|n}tD|`9 zv!A{!iIQzkr@w7RF?ASpc$FJJoNh!I-0QdtJ)9>e;?&z;X^K(w)Eu^ea9IM>M-Z zGaahNPz7HH;NmmIoxZ=)h<3UrED5A|!1E&jJ3^$~5_%yN-r{low446XsbO2ZHKIFC zxBw&73O>3!(-o(>&dC-8vY37JaqpKrz1$5E-)NTYoX!^}BN>~1F}~)AzI9%^B*`LT z3~SS2n!~L>Nx~XEwjUp7rbshCon{(*DDM0&xgFai8r*f4loahqUPR6O&(mS39jSRP zeM>8KjhRT!Qy)|9E-9juNyyoXB(QgP-};ccbFxK0;&&^CZr)$3J#HNB)}k+eGv{PV zyn{b8Iy5*^LHdocgxfs^Uf)u$VjG{kSz{(kgf_>rdkpYTAv3(uRBbVbvD%1K4S4Ez;GZ`{B8P3I2p zEd7kjzw-t}w74oQb;+=)Rr4>`iJB5JN`+PX#GNzQ3Qi}QBv~@drd8qtqomAH0L5qB zk^48>su94i!l35#db%%2QrOY@!^k|&G{YW&mx1!S=)JaD^6I`3Kh>U=*A3i$HveND zx}UbNdDyrd#B#ij+uc^Cyp*W+e3;)b@BPs=C&)vS zv8K<=&WlpKN_h~Lrrj)wz=I%+odtWM^-Jq*F2vzr=KjEg+5Oi&x#oc@ORMPv?%-cG zU!Ef3B{i~Sv3rvBJ+oUg$mLRG;9BN02zX``0$jLp&*|G;tWWMalK=TeE77%H?>-}s z7HvVGe*&2Rd+8a?A{p=h*eg$Y#>}8daoex_RB9FEe0`91b7I-y<&*X}#)9loyp6NJ zy_Mak1tUbDeEwD{yrmqnDXxH!Cbq7Ozcn_=^yt3aov7tX@3}EmukOw9ZStiE_ek(t z7EO!qYQ+qn>rRjN9!JFu``?ssn~vRxzC5y5%?-k-^?A5<(xL#bYFR3e$>`6$gnIj} z`CpA*os+!wU}Mx@NQ4iwsY&)5gw`9tm$rzNFV8_nnD}R~Bm$cRRWcO#%qHY?F_1z@ zs_*yeswT=D$Y1GpWFl%B!%`wCssKfNuGc;a0-dvkwa~gNAU0PODn|-j1LSWz2xN7= zpc$uhT^C@^LaVTjR~3e&rWc!x8tUaq9YkIDeDY@NeP&>>5shTB_&e^mt`4=PgO%r> z#|6;JvA2H|WqIi$-fE`zo382spI=Z#ueH-vr0UQNY5XqF{J(StD-Ev7oJDJa>Na78 z!=*wXTFe08wH0}d9pFvVdBgQ4EFpXJ%X`iz3%3qRCG7Ld*=IwRbrdQ!?+2kNd<<)b5@?i&-li?hewCNTsfrzlR*TmUf79^gs!e` ze`&@AuUtYTwX643j;_N(W#O}BIc!GWBW~{JsC5jC^24j{SkyoYe;|S8D)&-3EB7av zAKe>sId8YGt7Z|EBEooIdE!43+`B)*UG%3E8dlUMU7rOt{%#Vb^iLG_~JA^(&TvG*^k3d>Heqpx!r88m*w|5D4+jPgLzEvPF6ubiqVj)ME!D? z)F?nwR2s{Ua&dFo+}}9lCNBEAe)t*5Mh}x_>m_>}Y`T{SjZ3E)N+?sGn2EPz%hu^E zOQ6Xu)A&x}O1V&3MHE|ZZWifJ7+aFNRhpc*-#mp}bC^G6KZt=xw=SJ%mss&B*D?Mv z_oVz|>2N1&sqWUmptg<@?gi;04KXAbyDo!nxTEbkmzTryfwvw#S%H2OH$LY2v*ShJ z9WJzZ6g;QS1ueJY(b7uCnYn0s8S?=H2)h4(O_J5c+Ly^?q^X*qc2Um5LDVl!)%yPo zFF&Ut;2rB!t#6WW4@xF&mPWVnBpvM`JIM1}+GQADyTe{1MY-zrxXlM`fA0V}(A-5d z#us^iLi)+y#GhGSTBP3r&czqB%LaVQ81LRpzq#bvx1@~!B2p-N9ww?!I@>_H`OqYX z9?r9R=UY^)w6OsJfo3*4ozu{|u5aSRlj*bc%HCWJ&Y%0WoYCpP+SnE3%-0X25-wAB z>wppr2c34?0U_SoW{@Y@s~DKP>nA*R@4|CT$>w%c)jOam4 z;kUF@DPD}tb7)gam9Z7`T=u1XJs4E``LL0^jjOaOnI>i%*%BJY=J!)kPeTXlf29cT zIaOIA*WP?>K1-sij;?Qy-U>z@3rfQ8)m~ofjv2`DTeZCn`R!wpB7UiF_jZ}LQ5C~w ziEr)i08lNu)<9-_38x=wW|w;~&`6BX6>tg&eje~-zRH~(E(pyYoi1%^w%Z+2Js zMV3lId%#QVz^9>#%_6&x=L-CT;7lLJtDP?YljM^Crtn~FUlg|A%V>`fv>N!cQdg?S z7ZJVKbMCVduEE$Yyr@z2aftp3^7lq6Cyqw%w?s#nPv8R?q=ks=siB7g*ag1br2yfS z8El-zrPYbbui6un<>jJ>-OP#Kyc0ng_DhlXC2Q zl-1)v42|EgsFBz z-PSd)1Q5(&~OhO)a4lcZCzwbLX(dRzCbB85{WctA8B-DEP~+=)ar{SNRV;o4cQv^bsP0E@4fjYH`gttGfHUQerIzl+j{mSc$Sd1KTbTZRF#pV$J+TvclK~cT*7iQQYn*NcVqC~QS3&R z1|%1RgN~cFs3P%8S{m#32T$(AYgB=mUTNe~mm36g3i7M7f84~Q>NrLbtzKlX33w3_`i<9l0F zj>&f$@kZ4o2~;$|@K!P_S;#=5{0{zMzv^z~z!uG(g|p)1613j(-wL?RpG3G;wp_1W zKKg%zA8&s5shyvHJZY(l>yw``W|b`vNrpnoCFiwWE$NSMy;CErK?F}pO^;Pe2V#c3 zy*s^_H5j&R0g|ABHKe%wJCe|S)lr{uT@x5~YWva5BMe67=x(0xew zrW^&Fpepo_2)V+S^f~U+U!nFUc{8*8{&wT%Y9_mT|K5B3i-OpAr9*{Wib?haF1(Jf z^HMeK8OkEWb1_+nMXC-{%e$+JsniCsX!QAg4Y+mNgAC0P&JxZB)1h3C=>DXPmG#)% zJFqtH$LWl{@;RCYD}mi^%nX5FSltvvglD`PvfDOPkGD82F1)4%6D1esCeX8 zo!RlLpeDb1<}L5p3vvb#ub&$}DCQRH%LbVWCGt>rZ>g}L^+v`+@e#Czkw4US8P-4>vyRR@2~6v1>_YMVEByM@*q;?# za?hnc5{}yIZ+|@GHzn;1(n=~QN)rbzVwjpxo^~zftzQ--f)Fz=LjzHnA1y2_)!PWWzE(HLVXPEn>LqU9~67F9Q%ICcm^wu zeT)xgx8du4fDkfVjbw30`vja_iT)#jE^Uc#Pe%>>5-A)zv@wA}PJ2F*w`&q#&GgF| z*&wa|vkU&0CN^lun#e7CG4o3CXiTI-7!en5d`(-udAiW0oe^gQk7c7u(-_exde>~c zlE83{Azhd+wqb+(vZ@f*w5J1^CT1R5zQdjEW}yDH%$MJ5M^mrhu|aGZSvxbb}a z;AXpUNM!0_TJ(N48*Yb8XMh%CYzDV?A)QUCYrOsTza_#0`kW$pV_d%JFub_69ex8rO}w#oHwpNH@QJ4wtCN z#Ccck4UoAP<;3(CnM+i=A~#Dr#@XX-$X>uI;Eeje><_N5FCvc8uW9z36t6aD5S z2m7h{Z>aj>&7R6jyIuK9h_jS4PI}1M+4h)Wt93=g#CH2NK$-;l(EQYRdHTi6js$tX zr=5_pn|krRga}$#KvS~@{>zEcWb2SEA zWYQ$9BYY{p>LuyU=;fQAV2Ng*B}Hkfj+)g|Dxaz-rI6`{VS{l}+8ICP3gtUKyCKdW zzUeYsd_XXYdthwsshCLLvKM(!BCu z0RiReyhOSP$%W$=O{?s}gwOhzduUBfDYZ1-pzTXmb28!sf}sx&<_cFUcSQV((dC{s zB;yHv3I4*)6EE6Ih(CvWvt5gF)>4!gQh>yHwV{Q0G^R6X^*?o9qQAc1vXbifkQ>5o zzsbQ-Ex)w?w!Ni^r|~3E_Xp}r{g=BLhTp^G3nIrfr_`T@_;;_h_^9+>&D3B0@X4^EWmMA<{x%svTR z`c6Z|dVtVH$}KdUGw4QJr^NA-tKv?EiUm&?|AS$XFYYn+a5lVhQU8nOtJ%ZI>dd5w z&yQJ&qxEZ=m3Vn&qd0q}D?7^>(#)g!0R*jPeD_IxR>S>cBT@A(WMt}ev5N=0dx!N*7w`O&KwFH(Ivy0whELY6g0i!D3DoZBo z9jS`qGu@hu$>{q?!gu=XEPTNM=B3S9{EvqG^sSVxA`$Jrt`ibKW4$BmBWOe^s(1b-ZIbG!J z2kW41gvgQ<@->Zce9Wdy`3PF( zfk!%xI%7_BImv7b%9CGv1pA=VR);;ESpK?h;=N1&vRu z#>-6jb%p=S0en`;O)hY(u!`Ezhnc~>e-^F!pUPKL$hAp}i%|0l{g1KNXQ^+X`)0 z)<=_S-QJu6656DAMYe~2h20GCq*J`T-+a=uK#0Ws`rEi2RVN|nD%sejO)--rRX2YY zCE4t$Uka!glmfWWpv-uBW}L_wmo)9g23P>d#_ zN={T;(t}3R(^jMgAUPttdVBm$HwDOxJPe80%j-n^avm_KYKgeucszj_vI};Fsq-!c z`tPT@*`hOVLaK%T!?+^XNmu@bdiofiYzPeWzR9jwXjb;)Bz`Ia@J+YtT>5dTjHlH% z5!=^BQT0}x8F~9m18lUHHuvvzRI*jUMdaa^4`Rrx@BRO~`uc^M1^a7PZTwtkNSym7 zT!VxO|IAE+ng-A*9-Z(B)y-Pj`^XDyyb&I;vqNi2nzG+K;3Ch&-;^Vm7P#wc$x?2o zFnO=NE6H3h=v`mgMiN?f?>TR5f2t!(+rB_Z+J7NyP7NVaBj;S)N8aR2|LTs%nNry&=n ziu&kppLuEU;5yg!vQ*~dAr1fr65wfUeC%mkt{iX>fl}t_X{uhh?fLvrv|^?C=Hfb` zE4p}W(+o>?{WmjPY?8rgXMO@g@MXD%YV#RaB`*CPYx)Jo=!K1PS@~moyMR86CU0uc!+^51AzR&<$}`?v1^3{IXl% z9R4!ub#&1m!+6CE2`}sPtp0|UtG#xa%M(pQ*)nzm43F11T`d-i^u;1^lVrvB8H;5| zkzkdvUGCP0CoKn4zwOQw-n{qEw$@FR}d7g~4Gz;IDV-fCF4ojZzplvC*7MI2C&UK4&EPR z;*E{}QgZQ%)k-o`9Cyyq-q5w({XAsP&P`1sz}CeYnoQusi1WTL$q(DL3tw}p^-B7+ zs8>bK!Dz2O8m5FIBQX98QXi;SqPCju+~Vlma7(F{n|H~juS?9?Da6|=#ve+ZyAU27 zqrk&Bq9g(SeE}N*&F50Q%CL6nMlvgm zdQK|rm(Oc>;!1sLZjyRH4nN#?Q&^KKB|`4cVVBcsH5fs`4l4Hp7ftw!Hm9g=m~&m> zE2C!qGcTa_ z>AtVYzP^hGYcVI@s=HhA$B}JMMsbe3wYbly0g&w5X6dSD&Y`$C_UdwFZtzj;ELh!q zi1*Q)57+z=hBIz>j~TW%i9pXws={=by-z}J#6j6Voomi&p`jF`#KBn;>J1l{!T~ZD!$p1&toX3&~By0 z{;+#R?Sk8RWLzfUg)>b_4S0!1pliGwpjTRE7=AkmRDq(W9eO>MaD!}k3~K)}JWVO8 z@f;Id%sjIu)t>d`#?4wJDUBrEhac59jm0l9I$3=$b`T)X*(GW3wSf-?&bK~I;d?QS;{iy>qs0v7Q1ZmMNh6k7eRNBcop9@gu>uq?la2l>cWJFU|2ctO zm+>Tfl3-;o+zdu*qDo_)w=X!v5{{H*S}$pG6?=4b)S>_(_C0VS0#|ht^8(IIRr97z z$0t}!TN4mHq{!3XG5drjKa9Z!c9Df&*8TT;i?1)T?1c~MTq&$YyfQIvb-?di$noU{ z_nWOY%_dMFO8W{AuR(%SDY)Zhm2shNPk5GNNyt*9X3eaJfUZ(7=kKQIt>scZeyZ#b z-zy~H$>g}%H!~yQ79%IlS*~8LU=UNNo|@Fxdz&iJV(GG49l5&WjpfEqZiRjuLNJMZyQg&f*W0IG4J;oB zXT7gw3Z;6jL9XR*|Ek#d$CYUp?K=Z+!lo+7A^xx6S%(DukH+ygL_PkZa5SBWDj{txO)caC)$4zBl8`(3P!&cBB>Y+w$MF=G@|G4eW-cPa+~4r6Z2mYrOrBH*toA;y7}N)!hMG@6L%2ku$|-% zWtQTCT?WZY%`Uow!onFqLp=UCbGW!SkLX;jgy{5{p2BoBwkxK>5|jwgnEZyfsmWLYmy)O;oYd9hOH`1&z;m3VBJ7lP2!pr1qDUIr5gAdU0H z(bG{kFI|PBX1Pm|a7{iuFE$D;F;@JiGAkpGWo2mUM+K?5X~B=SN^@ z@WE^~p11E7Qt279bR`@c#K}w2E|k|K(u&c2k>-SocLOQrw3 z>e+?RfE!$Q07B&&x5zJh6!+fvT5jOv4y31T?se^j{`yB0NWt2IIq$hn$h-{cYRy6bKd8ZTaky6aP=5Ai9ydzVa=)_o>2vgz%R*-tbbyt-{{%zO}mdFWIOt z`r#uCHtA6IN99Jf6}CvKaubx`WHV8zrPIv^t~x>~rFgow*O1cuw)i@vIq(8+$tl&# zPj@$wZRV9_v6k&GXL@wtoS0x8(0A_^jSVpEN4E^e-EI=}bc`==bMXn*fMW!ICarr`b1B zIR59Ezgy{$N1;L=i8yY*<-AZ6bo@2G>&I~9KxH&Hn zjNIl2+yR=?WxEb!iq()N@fPUH@vh2X|4*733h2x68IJ9|?+mL32R7T-8(|j$tTT0l zy9EPWlY$j&8pXYqHtJrRpo8Ol6==<2TY~=JB+V=dG}GpIzt&ViA^|@mm)LB8$h3mC zNKfJw+%28w>%I)6iFY5sgMYABKUXzzU1S2!az+mQln`J=^C0UXl&EzJr_M^tNt4Fp z<^z%qB8v!L3ECZtk+9j&z>QR=n>9NdIO}_NYC!xx{F7$hynm)+5(%GwvEj^oIzLX8 zx#jQHM8|G!iE$b0JP{Zfz1*TOSp>H@+_L7|$j&XADV1mbX)TGj34!J9HM<%XGSDm( zhXis;p~L`Q&t0i1hJviZ1;#5&vTOY96|>pq_P;wOcN$L}mDFwya26j`Eu49jp3j1+ zsW$qURnfy9x52X&=ObMlB){(@X-M1ns7a_l)%KxbezUmicn>v>*9Gj6uu7@o5DdVxOIm1LFx zSf`P6ixUN|y1>srT=AXvH9Pgw4wmSJS{zdDQ2)bleU|4!70Y@pWIeMN;#AdlVNaON zhm7vBTNXE-V<9gtuKNT+ci&*a4+;RZ61CND?T%AsPF#nHOQiE{qE#iC>ryH1Pb*1B z)s~#HHrhWp=iB=9)vBJr=$6kCzx&xr?idgujwUX!8r_%O+_@n!L50a|#2eJS)t00$ zq{PCU@0q_ zX77#7a^x0I2nv1mYoCh-^c}20&9d|OAp0ym*rS70{{>5MBj1X6Sbm z%&XYX4@_98!e7j|>QpA~KU9iWAHPTRXO_xcryLn@S;_2D z*b25nrJ`Hy-)8gwHKys5^Be@)&Cl#&3G4H1)DSA+@=zwt@39VTdq0~6r4Qu>i;cQY0ZC5A(X2l-Ze6Tg1W_OFPg`0ld83blb zDOC=6QsGblryGhIv$+nLJ1_AUGavk;P1t305bG|~6`1rx>Gf6R+of^*VBs+J|I>cI zyu$e+xHRy`YIMx)7Ck@jhw9@mmSh_r*ItA-j?q zlVl^1DVf2`Pon18j+e0C8 z?Vk+c1&aFHKM*`q^=2Yn|MGDD>+!$4nfLh;O(g2ZgT&{5-}~RIN9Gs97FXb|h9du{ zA(@+G*GDx$s750$V%vERypBx*oo#M72@Adz(n~e)7%h}Kts;mMaX(gA`jZ? zqkz>w##uA}pdP(FMpbm|gjIZIQ#(+=DUL0*+hXJaKsH1Q<%N*8 zI_M*#O2=Jxib94X;N2vP6S@XORo?rhl$Sg&VLBdR4GP`QO8 zw!^nVT$oKgE1F5zCGm_2`41emy@|zBRG6%H`8#EzUj-j*fVOx1RdqP7%VWi4-52Iu z4$YtgvZgJZSJTg~6ldAXZvIT(tS25E^Ca#O;rQT=9Pj}`Iz)E&XE1PR?oaXIYzUwl zHSdSQw`^u)jSZ(RG;;MzW6W(EpM7I|%l?EP2yZo%Fralmj6^$eujskGnrdWPFr%Nc4*6bA^-sgJ~~b#b)$Y zz_9oGz@6jbNrw}9xzi+)b%X-s)KeYhaN5Y~xMqGoh~$BbOEt>14IC1kK0H4xG4t6J zI9RZ!9@sMxM!?R$@op}v<_<_BgAZqVug`>lID=x#-Ubj%$MNtzW5qZOz;8mZ;dJ3F z_y_Z?2U0sQ<%$JXesqaFz$)Y#ONJNtWH;QJzFM4EVMS#34Ab;z-G{q2%Lv~Ayps2y zRl}Sd1KbCD{|dS7R&%R3CPlVjBqj(^FxiJlF1=uFU=AuHVM+o$1+^dd%{>8G9wpq@ zu15;Wv`+%O>NRqlPFCY27of<>#*wYuL~KvSVE(NoTa@sGQRK`x=(tZmEo9W=jpn3V z$$?qD$5oDqrunDauFe?2&)J@TZbV!I)LHfwDpUT1L=~O2Ea9V0RXWN9 zWzpY#9MXI%Q74t=3qeFm*(LK-qcVK~-143KuIJ_km|N3Y_0ovG9DQL0lpXO=*X{&H z&Z(xV^y7*Smmy|7UctTA2RBn_oLIgE{Ao<1i5x3UAu;G4HXyEk{DT;tgK z&r|BMVioOZgl0N?Q30hol#Zuw7LSFnixO5F|zne@1F`b7{nrt=|2 zrH4B{ZKwU#AiGsj|Gry+^HlZJ^8vXe>rMr*EGqsg13xS`TS?uq{BVKrL-asy zbeYdg12e#yH^(=$`QVSTFGj$i5r`Bgf!245&z3MdM<1YunGzKw&_Jdn6gq#LOCPRC zJTen+#C2&u+QpC&jO|eOmA#1b76pT+a@Twt;ZNl(+WTnb7x4kgMm@6_g$FjiCfAS(L}5Wm z&1YSKf=@^0p|?Ikl-`yN25yDSf=L8=lXXty^ciu(N?l+YS+!C-1RsU&5)a_Hrl}W0 zW>PZUmJ#$JMOxW*&{>5Z@7vk}F_JSDKy+X{dyx7Iy1gA<^QmBZ1@gJ1adqOvsy~zu zEKQpgj{84WQthixepm5ATh-ESc5_mrPjCmIiurTPMj)=5_k8Jt%+;Rq>Hu@u{k-O& zvhO2!-@}jw{ei#+e|bWG2ylJ=BQZ#&n0+4VI?=ZP;Z#sHpU@4?r<<&yn=~1#*$ddz z!x-tFiB-VXqx>IM6yHuYm$;VUKT&apb>%WY`IWASqfp2VJ|oN9ve}0?4KV?JWid54 z@d?}R_Y!JA4`l?H*Wa2D=sTP}PvgXYdK*7zdTm%Hp1D+w-&!sTMh+CP~Xf}1zJud zHkAd&No9iOi9OUL8`&P9BHtFSyuOj$Ydkbv@ZKK?ItW>~sWzss>yKh*^+2ZAWjEuB zd~(ZVlRnGf%lp|m8&?c3?iv6B(G#=-z*Kf<%_d}eoL;hbj>Qv!OiWSU0|1jlra0h0 z3}tKzYR;T_L#xdi^L*b}f|G<>V8~nO26X zX*wKpaK5)U1mzgteQid`?)jSoJiU*Zl;!yOCI2P5wNiy3Q0XdtaHErTXZl{=cX&&Y^30RiCz1%M1K%`UKqIRjPq<=?$ z>}7Z~<3{xG=}RbOS|DSqN%Y~)FQ^*ywy=MGb;KlULf=J=w^f)tq$(kptMTqMzA5yc z??g@Qbl=^8%9~}4dMAxj4Jc~M3|*IY(8l2oq}A3{pz{+CJM~RmTu^iFQS16W807Dof~1* z4)GB{B2aS3bgIYxsF&A#@NKt7n$$>+--(Jjv{cR&N+>!LeE)P|yL2*Pnvm^ErO$Hm zyIs3N6Q_@V)NsS>-?ad=e$br?Yd?~;oLzICRZyr@pdIUhg1siHuwNfV8WxDCnH!ln8Qq~IbL@Dbf5J}QL-{6~DR|GmNnBcapq%_LTh&3aiWb_)RYoGc z#S5yjL$R|~dzz9xt97OXkS4@SQ)>KMW7*RMunAhV@Qj@WxpfNCHIT@Sk({a^Eh?9K`0BugLNY zOvoEHxNvIDNgIA6=$OhIW&&j#=fL|r%x7|2ryGG~oTl~Egcl)i@;0Z+x9 zBwAC-#5t0`(X9W6NspG`WbhbFhI72-Z8z_tsWzfBS({X8o$h=d3^!NER&`w)6!$Sr z%m$Nl;)sfpS%YH-Q$Fw}LUv;lvYyUPTL^zDZotme#VP8Wl^u*vhAb$^68F^Wl@!TN zOy*#%&yyeC4IgXwJ>zVB=NxfO$dUl{$FAs+Ef0)_hpqY&&$M$|!aFFRo#(F>_{ko6 zJJ?Utco6257(}Gkly%LV+?tr<pkq1{+m+iS&|%rJw|i!iOG+i55*9cqWD23O5n z!&!^hZzgxY9fb8zr>6Mwx*m_F2`_0-myDdtJbM#x9+G?Xta;uZBmOvqRrRs!NwRVp z+2lPn-PglOHTDNoW#@ctl+aNhSQq3}<*Y_*p$x$@ED;5VrTb8yP>O}V3929D^@HVn zPqI?cf-2RkJ~ox$C#}6rXS{y}D7br);*tdn6NAJn^5YfJGZ07X?z)k_VjE%Z$D<`)Vx;Eu_ zHPURD8(l+hH6fD)oBv!6T;9P14L@VnduQk7e(~)?AA@c30zzaOQ-%G8_s7dGnoEpP z#U{GED;gh&gUAC7W-gBld@_dfJp@9JQ=CU>MxL{OIFPDmT*mNuX^Mc!)NRzYedhK3%rMUQ#*d7R?nM^R>*acGkJha|jd}|9 zK!PT`Nz8g7@9;kF_sEtXeSmgZ75rI&rEHP0)H!LN^DHsR@!@X~+*r5a5e$)8gcDm- zMQ{cfXYmz`}fjlqap@Q=_p z?;jhKy0Y|((@lIi_gFDiYn@2g5BAOeK7<{7;dlj_&9`2l0@Vi}l7?DZCOTi%e;&A= za}-*8=Ivw4@9k0B+UR^jt1Qzyd7fa`@C$~$ERW)P+zas6;R>PzeS4>7Ce!h-f}VY! zf89!5w@(n%c;wq5Y!9O~1)3$`AK~DmyI_-RWBoY*Q}(y7=5K$}x6l{Zod}+|(Vat% zrgFcQ@D4U_J1rz93+g^v`$_KPF>RJ?16S?AAz$t5`>LOJ&;VzU<-L;6T-{LnpyWFhu{`?PwB_K()?5qGnoZ)mBOZY<3x)$If~t<6afqP z3b*))(?Y`{FT_ua<0~Fz0+;~-*^gf(XV>7iGF&kku0NCs3JYLa z15(C2gkJHdHEFhhN?mHy9lN=D;xVn32SLBG(G;1R{b%s;JM*>gZT7h#OG4LO*YHIX zCp3ijMYlm(UvN|W0pR4VG_gPyLW3zJ9y_k;L9hTq)(d>#+ihl~>TwjFqV}tx)cVxx zs@F1G!x#?yAFomW50OLAs?nWjD=L}Gr^U`a-%gh9fE4nry9+ioyC+l|ty$S=**95* z)>*`6BpneqHTleC4s`C*W@~p%fEpmJYjyhy>!;%I>mNT?c`YF#=?;Y{g3k$(ihin_ z)X8=9ItE2fQ{=e_wm9S^+l8n9vJ3pZ3jO9)Gxk`DFjh8fNaZbPmIrYbMv7WxglZaq zLQH!+zOb4->Z%U=D6oF|#)GdACbxD7TZ$~$J5cHiUHz8iwxD~=A9CZ7Tj@J4TyKo4L^e*5kD5FL(6PuiIAK+)13qYATJhoATJN7_>er@qt5R|VcM4blq)N?t~igiY!BZK*-$Zp+!s({ zR@jR{2l(c7R=En7m|^9XywlL(Dq$wH3PWJ8jStZD^n2sYyBIpfw70eSYzi&-myvoL zzC~XX`N;NXcZW<5PcX}U%}zVpeRAhxfVjyI70C4{3^=0|6COH%$PZ0XTcA^*HwO$f z&(eGjdKhD*m?_2#uN{oI>P{ETujy9 zcOkRCYO;U5qXh6ftp)1&E^YmjlwrW)yTG-*&8~eXWb)=~d_-~^q|5$nb&3ZTm69== z71&IwwxM`cxH8l7t{1I$EA|o2<`du|L%=zaDv-wgZG2$1=esxc-1Yc5<#}T;-7`z0 z`6nt`c=ubjoC%iBW%`uND)ewQGlVLrNvoTj(79$MV#}W>41knilg+F}d`ncFWPb8Z z9hj7yiS+hU&G+F*pgFA_ywuueJ#BXU<)W;5TNza|?%ftE)+VruAX*yDDnXIviRqPk z3sE=f*J>65O*n93UC{4vH(e6zTW!-Y>PEmbRqI+#q4kBGTL*<+dlN(!5@OrS)) zB-Ip)z_=7S!1MkfBgXrcJVRlqCM~dm^YEfcw(QMdYls=+_<3tUUj3en;I4A>GSO(o zihpCiJlq{%U{*mim?eJtK_&@ekvzkczJuakS)-n)-a7V#twsh_XU>LQLh7D)k$oRn zR!iu*!q(J!sDh#&;dQzhg(0`%M!rb#ti-pf$Mt6f2&ilKRU|0bukdFwBhIGaE7vUv zWzk%(q1*0`6o82JstH&&imTb&p|Q5)JTfk>qUWRES|tHW&C@Pa0t?QbT3b3{UlpA( zqGj*@11-ABZuSH<#6%{`YV$36cNojwj+K>k865URPvSq9dji5=#b59;YmKh$1aAD3ju-jUm>%>L#e;H|_oZ@_dAdGU( z1I?qWg|iq_6Jp>qr1`l)i~=}GIGTwE6A5@jAanS|O*_k22Ht%5} zS=cbwY`h=n-Y*ldB{jz`wpJ3qj&e4)p@B^H(- z>|3h&$xiiLXMVOPy#Q>_`myifSHqa$mrWT3m#iHBcTR zIRxno3p)+z9Qw?H8J7Jo-(cZM6Ry#hP8PufD-`H&al3 zJtIkp_Hq(Kc4&W4r%g_0^u>~Io1lkr4DtT*^(~&sLW0>fE+um!hK`Zg;|BFZ^?CtA zKKkYfL*fC%dgpXI-xkAYvC0A`_A8dD8OY^3^v=_*P&(lz5tH3x7vMW4SRriz=oJu{ z&fR6CGHI%!i9KYbQXm+WEiV67%u=zfe!Yb%gp=|$co`f+dZr0DIiGvmyk8{)Rd1LJ zuqQ#6F^`Hyd=HpnB3DDJiP|?al(?SEK^r8MEQ^Ch8yk2`aW?dknjNo4p9hw5K8t4q zsc4LpHN9+DlJaQ$SpU&=j){-p6KSh)1!s{~Yy;vfM1YH!xJGx)lTY8kS8bu>KJ+IC zU*dzWZC0eCIR?)3Ek!3|qkELTGqY2u%DSrQaSXgfw1?IHN4OJq&ybP(QW-AYDLdg(88<ZPaw>dChCp=xjPd+V;U=36gHFQC+nS~BZB>a1Q?)~+#xa>!Ou zvquu;Gw)Kc2~R0buqYW~(7aall!|W*KGN6fxJAt1D^%@z8Y%iOlShq7qoz++M_l%t zu45jcuhv?4B*$LgjnofXz#r~@osyejTK@tXV+rlrwoF_qm;Du(mXDj|T4>?r1S)5? zNZNL|!O!c3CId_I!Kk4TPrf0~4F5d)>%lS!Yv&`GL4iA26b`Un-lQw2ZN1j@K~WVe z)R&yZth6^qqHFV`2Bvo&Yzzy=6kv&$6I1?W&o0EWt`CKKcce#n40tb?b)0~OP+L1^ zJ^!}%<_MmwxZrIr%4!ShSPZV3 z7@~{T6g^`D-P{dLd1Zd9Y^brH3h!pTymXz*Tr;j`|H~Vth#a)WLd}=CgC&kCyJySy zOjoSJf+Yov&0nG>Z8{WO-162l3#SJ^B_OgxW*=zTyS|l+d{u{SW_2<%SI{0se=2I2 zj={~gFDVTQ=NPpH#0D`NaIa+p?L|Guyx*Wc8!~is2UWM9yRyuZN_Vy*iPVsXxc$1b z%>1CTyY+@FH|dXCbYbm959v+CM-GR!t?s*SS&g7gYm||rw<;&}rqmcdl&)AH<$BU& z+6d1oN%d;!M4GY*w58qLJ-$MJcU&aWw?!bg(!}t#poE7e@^NqSwAu1gGdUJv@EvMf z$98Cz5lFdWLe<3-bgra_7-g)*?84Y3*+x}9cCL8Ds0DDkegp2*r=q<9tqLXH$#$|y zTw|ZHn%hXk7^hKp*JNWw>c*pZb43*A%mTAb5PrytvL5lJ2R{?`9+?=a>C(O)qQu?N_IM9)Bh_xqaFwAW0d35&Iy?UmgXmKbZF z56;W}W%eP_{`klev%!JrnB%S>1;DoGMqY#)SAEzAp9k!kxmbY=KS4|P3Tmqd z8wUvfzN*R!&*FQEUBp{qm=&83d^hmrMM<7budPIs9!=@zbyIn3Sys~-%)bN=DF9G@8LqvrE(^!C9UCfp z>@%%k$y`z7?GG6D32R)~DwLHLGQU!NB_N}|hl}*A9Q8goMf*yb{O23+eJ1Dq=cf<0 zW^0tN6?;V(yq)&YZ4<1dz$A*dGSL_?_iad5I}IFQ3fo4pTyA;F&0Rk>43F$i{kgRo zG3cnZAPuBg;vu$O^2}I0OXSF|8CUuH6q*SayV7WG25lk}2mFpn!Ksm`+N^q5SF+M( zQ@K^`P*2GHA^sB)gNeI{#KJv!A^ia1DQZXsNzh~xCDpF`TRs;uU~QW2hng z6NYCM_T{l$2>*_B?V$0<<)3})h=gv=efnj6D)7mnde#r@KDR4-&05Ds!3h8kDc92$ zfXVvzuKHR7reG>RMP_VBIT7bX0$42x5%zJd4j8SEyRP-Wa@p;PWfM5Ue8uYQ_4+KA zn8N5YuA9K`aYl*L-^TrYvNc_PriV<)q&}$KW}G{4LoYeBy3Gah4{7yHKcHHA9Q69q zdU=zQ$W+7q9+uAJgtc7nUop@8w47(Po=hp#Wc)kW;oZiV{wy+fwWAdt95vFC``F82 z1*2m=P!m-wGtJcF7^>CgZ1&GW;ibm%E5inR{G{3GwY0C?DiXP>8ZD2H&3~F;2`U8@ z>E)>QYn9AYa%tZU-0R2N6h(2~nkU-c&)dwiG>D`lIn$Lbvs@??($MZC?3_+!KfwS< zDcm%f^ShIUdBY@6^3l)0qA+3IH>wVaPW>;7Tbb=uN zrhA%F(kwOc6(X{8Yz@5$vPm%FDY3SVf@-Z+BGQV&9$6T^`8f7470O#uWy78@X!#Jr zvtSkk&oPj`f^GB&8I&~)Tb^SwrVAq;S|JYs0n1-Q*IZ7gih=-6Du;#Evq=ITD+=cBIO4=e7YqI%9&lRur@G->A@3>8@ri!U7A(bN>BzWm?jcrkS=71OW(hr_AY+BI#i{N zHbdVUz-IKYWS6L_P@^$mz|*l;tz5MihLL3uc9*OqrhE`Pw5|?fdFOHdtngKzvx$`K zob_C~tDcLfIOkc*R}mNXxR}AS&q<6BQ!fpi=lwjm&Q2C-bK0D+ri6!XZ?=+F#jsB&V?|@gN&uwMm5Y_pqHSYb^jeX296_dn z0q#oW=%G%&MRxAcjge13UW=@hlDXj=jMHRtd9?bK!@j^9Eu!^`74v(qadeTP35h9& z=%{AX*#Paz)0;vZ@G)aB(8mnxFRNjo<6opoG*`04@Bp5gD3nG6h&fJp5XNhvz`bRu zqRxS|@Uvl0K6pnp7FpZzb-OVh^u`mGR1fS)GCBKp2BLm64~_bWBp_||3|S#8#M*PFSnhk zTOeBG!Ww3iP_=`fu>}jhnM`2oia0Rdz0&t2;7h{*7gk_i1<&BZ=F6dcu+_f^$C`Qxil?J30k>~T?9WU zaM#8Z{iI7?#Wr?VSwfaq*wiJj>Yq?x`RkDfPpnQ8>^nq#Ors6gYK`oa25xN$#R)1LF- zas7txmXxaUN7y#n`U2x<3x(nQ_(EJ^(jfA!{~44~IoBAD6gt;rZ}`-PwL(vJQ1yXu z!yV5X*+L6Hl#%B1W}$G7#+|hVisxQI6l9L=I;}ALJVpz-=WV*pI&ob}!b_n=s}rc} zz=_N$mrww(^d`;>F&AJe{Jp47ulnGwT0hD}Sv8V@_xVA^kgBOLdgGW8{G=(%g9Xx# zarb(q)=;?#U2vYL4_r2F24#B5Wb=m>{Nr19YPfK?DWGIIJK9(N`BjP6qv9@_ z@^zJYBq#Zms5sfrqra?bG82vaPuv>%sX}LudYE_NSy4s~F>&?$%{`JY#gGThP*{4< z8!jvDLz$e>j(AH)ZJJeaEBpRjzW$sIudD)XO7cdoDp&78;6F<2d1_*O|5kAI>4Ywrr$vK%RQzUwJZ z4N>2H;i~}?wfd@w{RnyuGW+k*T9N(0AQNWBZtupE+$-afH_H?;7`_SD?GibO9x{#NkmWC{kmG7{Ca`t7 zPUSm`k>CDiCBmlXidBP@9+_Ru)1G(4Fr*Y#JdcU7$C?2J39>1;Z+%=Vg%t zZLrOFuchDwVp4py^=udjbbxX|n*yW@m3Aq498z;tyEVWwfYBSmB|Xh#<3xA9GPfHe zuH&d{_nfV^&+ud~SkI%*zLxXshEt0bsra~NzZBP+!Q4SZafiBA;L3gYjH_^WxEz+}b zqu3rBF6q}_e$30Ld{6>AbT1&)G3HB8U-Qjr(U_Dwl({>T7-MP+lH#)LjAu`qmO;;X zblJHFN44b=SSHRqHH}fXmAo@aARgT%>1(B^V9|-0v zF@4rr5!<#qSZ)pwo}!j?;=eqz^x5qTw+!{8^OaQ@fF=tkpT>X9RZg!Ori#BPW7k^L zyQ4-Uri}^Tg2cLP$UKwi{;k~FRKc=})5+!v0S~nB!le;kv`PR@qKd)5Vjee2akwz( zN*-au1at%N!!Y_3S7G*c_4%ckI?=i%u*~pb@mlcb8;%Z^cgUjGp!rg7s(o8^M?Kbs zQ*Rc#1BFk=-ktFa-p{j|OqH+<@D+KvyP4ZOKS3=oQWEPiSawAcLGQ0W2KX6U3^@jt zx#mD~{HpLl1q<4rK%RM+pXG!>%jU8zkVh%prRyN%?%??XH9S}*9sJ58CK6t{; zHMN3f0+m})fvu$*<**E>ZB{I3|?sjONK5RVnBQ<5>eYz|*K=#Dn zj^I|UOnQipq*;!N-7nj-5f<1K6Ob$&4(fUh*)6SrfS!09xchP49GV@OyvgNh&kWnW z%wRAR{Sgy!st5kdvfXn?Tz98#9?D+54)BV>Rn;P|y=DirXmyzxw;4d6XR9+A!6KX} zm6MS~x7p;tba1CPdC9+9uqpI)1z}%xW>13TMlmq7g5R(72(5Kw6+Rq6&-*M(Uz*UZg22@6P8=9!4dU#cG>z6FY0(p?v;M2_c%2+p5no9 zu|K{q+s;3=FNw8=8EAvltI3%iLqw$8S7ZGhJ7rB6(h`ku2uG^;VSNMl>mrZ0F4`oM;YC@xzw%~FetpWqm2+Qg@e=0rJVP{n> zmB)_bbAWCq5R`I-5LDiMC%=s-wM2_I{-_4^afx_)exr09SY;o;h^-Ew*5~FIIE+`Y zegQ&XdkxIb39f%*HuW6rx2{ybshjZ=L;4sP-Z(PJx+nA1++I`IvTZ4`Z=UE6o~C+1 zk$DV+*4+fNk=xl&Xb1HLunL(C1z9h+eH&gqa9(U|XH?7{3tv>la@jScS3yAv^7q-R z>zCBAIv0zjs=cXrzmKz3=Hx*{lr_d*hPoqBqM8Sa!pvU8^0kO~&GA00^ekx#6;P{i z6NY{BcdR~e&VWXibB&^SxotKt=!gF&<6vC%)`yAfg0hs1R?+&{GXvDriO3ZD$2AS|bO@F5yw4>!@u58z4Q2@o>j@jwy!<3fhA&Ry6?#s;TEwJvGui%AY z!~R{Bbq8u8!=#u^zl{1t5whfRnqQ8ctM_uM$*H zE$y`507>rbC^dY%FM~pj#S|B=m5C^PTs7&JvGPM{NsZuasr76##D#rH*=i~jS|f?~ zPv|sDfcVKU2EsgL61N(?$#I1c8_;%#q=JZcr`i=o@3sp@Odof=$thxIs_Bz5l`T%S zZQ9a-!t>b7^%Z@rW_UFMu3Y3^I^S$h9?VMGdCa`UjO|^TKygtwZ)@<(QNg>i@xBD|HR4b-QGPO&8kQ7(5%{ z0sGoIT|V6PVgE>Cm;jZaW_<3*e{(b|?;G!Nglv7~J&JFq%t2++mDmX{N%cbx!%@*12BD^^d_bYOej z9$S~`(MY8lFF-QI?aSdVLsS^DCWi|&9Ez!(rb(YTTHfcWGJxTNK2U-D2e)LC*cfft`^0F6s)@BL~U(W3&&c2Z`~)#9Rg2O)&KOtG-y2z zrqV!$e*h0_a;U8OIECG2PrFv9xMlzTd3HAW>|fMx5Z))wz5c@}wPDho;Ek8Fl~&k@ zUp(^cL#&9*U&r=0Q@%o@4tYAW+QGeeV&rkhYh;IY?`{kf;2^ggTFMv?eL~d^*f_p; zBX!Q1JsbQg+jnL)^f5#59!XmV=e_1oXjeS^ zX%##TM=pIwD%Dtd@I2w;2A8Jp@1_#m$N;A9(u3XEns4NyU{SjhQkET18}O3kVr_*- zJ@dhK8T0dnuGdlzyEI}hPD4fgWUbs=-#~Lqo~`epy|%0TKYB;)IIkS7dt^s{LHMGg zD@F)s-_urExgfaTZBy!2 zB~ecu=;uXwwhesD8oW)319(B+3Qz4_B-Ocjl9#rxgzl)83{bTRn-)I~xIbS|+W$qJ z{zc%VGKI~8=UO=-z^U}~_1_Ajgq^xB=GdL}z$2d|0bDy3Mnk*tpd&t?)CQhjv#hrb zNW7{0*0G1Xl`-)IlR_WZcWtsKTo+LMb_i-p&=2TfT7snV5m8fdSJWUnO920|P9_DX z>MWZ~x8^F6v^BnP5#1xA7|=pho*O%HhY{9&f?3H~v}G-Y3$k;7*e&MVv2EBB6P-;? zIUg?iTc>=>K=}iShmRRa7e(n&%-)Imo(3@dk#;8XeKXB>PHA{DPQ-;u?rsZ0E?Xkr z4dv6esr&7H)x=S0LU8Hv5P}D4)5dkEyr>LDqHK+iWJtO?qyj@C@u66?Ehv^Sqi(+3 z^lWJ%lu?WMd(2IRhZ2QnvA`~L<+9(__+w21aF=QUraMSOzJ_x9@hxpnelN?Dt8)4l z%RyVAo&TtQ-1(E92CfLd^uEtY%}DbW7fmaaF0%H_3u&K-LlXJQtEnQ30~x?yOpv)6Gz$ee0sI>5)Q-9vgJ%2+0I1&E+~2*axFJ4 z6D8Q{wBoB6Ad|c9<8KfLdJ7mxS=Z%$*t>Q<&EWQs2hSR!MShQS0UPQq=et_@)_bzu zo}ES1rE8@oV~|!WP8}5$x8Cee9JI?JxvZ&!iVIr5;V@5b#{5=Y?S07gT-yeehM{b{ z@=i$oOefe7bX(nC>w_0{7vwf=TbFP$;aqYiOV@9It$gB~L9Y15ALBV}6K9>ir$`Cr z36;5(#Uwu#ppK{an$CNASl1ts_T_S6D`Nd#lD9&tnq|cwewJ?+6?{gVmHSn_#@-IsQvjFnK?0sgwrTY_dqaeH?iN`sI!^k9is$PpA=pY^X$H`xMD&R56 zFE&BDAmFKPrENdgzI^pRX1)pi))U;1dM|3MF-n0#Y=I9h|CHE|LnyfQEK}TGngRNc z8z_rJuN~6`#pT=XumeMu*k39K9Cs}~el|av+j3T0;Di8U9%1KHl5Xv@->oENt&@>G zX|^39!RQw0Wyef;SJYwkb_r!AZC=`_NZIh&M24xldc=1x6@wd=&x0dPD5n>!?Un7r zp8xJ_$7n#;#GK~Hp{ktV<;Tq13S)=ZVA}RJPmn~(Zm@v*>BuO>wpmYsl-8<~g96s} z&yF|?oPDR3sx{We1|V2UJ5L-gwDkWdD{xT?KP4WKzjGnFvWd~tS=}hobj!+3dDj9lIx5>B;gK%lek<8Ne z*5thXI3JcrGjSzio2ZjzLKT9{O8DPASkqrTk#dM z;Fn{y*$|W+s+JyLh|+St^>FIM@0REBljWi-{d1n;*UE6Qr&$NYVT^(614(}rr~KfF z<+1+q1J2=a3ELk6-)eDd(d!y?k&UqqrCC?+TG3aebhhsA$>FH1OH2z+b-Lusx!=n~ zD(f8w$%)sNbH)|F&UhHHFgmsS1~|No$F8(Nl>*ZM8|%K9axDqYhW*xqrVeYJwUS7v z3j`+m_nC<}U&+r7`ApRsuoK;CIoTO8FrRv_=Z`Y)9 zBZIt5vv_=^@`fc>>PGY~!4T-kW?uP8vnT5bRF5-t*JG~!-9Zx&Am~HeU(%qAOhBpc zLPqDlc?GQ(GG@4P?CG{ukS`P|jos1PSynL?vq8@v*33^X6_R)mgkm#2HJBvnki2<= zM+`PIBk^eO?+gc+78q2G9E7J*#)@OM8?e zPJJxsTJb522;CspH;a;Ocl4DGUC?I^@Q4wgr*NlLFx`r?h04_V-z}@KU5g(etdFHL z1iSR5I6HNlG-M=KkJ$o#+8?P4R>D=5v+h2sn%+Jk$R&mC)u|^wVn&gqENXm485{)h z`yC4hrpyV=-i<9(u||WhVW#Na<72RP%oi!pBnj?yxMDi9`JpvxGObq5_vugha_#t|Fr~Y3pfWKhGzwR{GAk34GGK~^VU#0^RXyZ~r z6mMx~lS>I9<>j9P@&-Dk$1ni1T7)iO`+D}?j*0{B^5I@rQfCUDA}3_0VCBDcxM{<` zKcm#36eWyk<-+TFZaGTcksB3wl2@SH5f<>c1}gbAc>$ z@?eMoKzP%sllwM&Lc+1Teij%ow0*vHthS{~rz%YNd&~B?Yo=#wMmK=R{fq)nCvBd%qqM)nm?;j*pn6UwOsQePT7s*sjxWZ-aW{n!#T``I zGonadoCLDcW5L0s79^MNx7%lhuupy$3g@<-98@Gz4dc$&3Q5qN))L$Ly=(kqM#r1UsO3 zsWj*)eb1H3QrN}Sms|wj#BW*Klr{f_AC{gAVM%?1&EEEc3wg_7sj1ANAM`9j*5`U; z2&Cn`OX!W%S@##g@iQKg33z3fO_YkCW>?ETXhkPvp8fh|AbvcoK? zZWMCv2Mxh@8(A6pfoCDO(Dk+=|N61+67%#X9iT$idZTGvrYnjpT${2qQB0mp`lBj5 zSq9-(QKeYQeJaRd=y4nF+k#EiP{Tn~lEU{DNPvQ8*-}WVs8VAfH7e_7^Y79Z&=0F+ zibT3JKHVfCeF8frSBkFEkH0q;8~Kd}Wak5A=B&k$THo;^mqklAsuq(JiL@JP5WO~y z6UrjZ%I?}!63m^E53?Us^u83D64LmV_Hgb;=-tMtpYJj5eE3ja@)2F!zn*phf|JI^V zL9Q$sKqj(oiQRJKut(+?u=|<;hPgM((Ruesz0YG|9KWQ{k8>3KJrerMIKKg@Xz*Pd*Q(FAw8feY z3p6kd-c&Hj%~NSzZkglBmAkUo{*^ox1}A`PwqV(rQlZiZ@xKeuUkT=>#T-U5gy_3qqiuglmA)5b+rwetIy-_1|j<}dh5{pqXxOLHD@}_Pb!44cDNaed23b%A(jz_&EMt zU!m4{o_&vj;@9(NnhC#TBVmnG8vo(Aseck5D6iiAJnhh(HGg=%ph9AFQ18~KW1G#& z6mxk4Lb8>BwwQG~lX><>4KqW-iE7nkPWmq6Z5oD<3fH8X+bs0e!HT)K^W%u_x=FQ~Wa*f5B$|Sr>mA6Y5;(Mlo?& zq8cR}k9rxYuNrT!)6Y*YsWf^Xhp^wn8dMjj{PiS{eM7g>J&@?Y)5@I#vC6P!9Z zn@2|2*B|gI*trT!vO?tNbgXK;-T_@&Dv84rQQS2jT)%X8zY}fca7!$HP)EziW?^QA z0F09_53HS+SzB9MEHWp+$HwH*qg*%yq6*-zHFdK_&^{7XWBL(TvQ?D`?Nzir;t{=WVvbR_58gS22fx)7+TpipHCzFc@UmXdWk4{G-ZkSfVZTpi-Ho)lNqUZH`{uijs2Qdw_nn zjQ78GMpVfqCFb|GJM-l+yPeS!GLZJSrE29aiy}=Vx3e6BaX~qBM_lxM@%0*NYcm{} z0HNH3ZsWmb6=s#X&g)%nR?;uULdN$ar0hHW!0|4_2;7~z!NCN;wgewvhr_ol_W{Rm z2<>D6%Zeq-hnvKqpFf2GD0$*;ou1OT3+C+~n_1jbsL=8C8DX!kEPGeX6HB}-{OwCu zQzN)`GR?Y6kc~|hR8d&?yMRe4i=@7pS&B2Kq>+(P<)hCrrStNCnfyPU9Q8=*9xwez z&DR{$>;(oPI%hv6%APs&Ts+w{y%ps)^H%1~B%ny!zNxXt{!Pc~@*GVPEOa#+PkwJq zdsEuI{|nlNy=d^F{F5~NBjx(gG({2`J5WtlO^5)-e|pX>gNr?cjNS)DKO1_k6G&gcN3PX?*>kg$;xd_k(^7O1IsAV%LbK( z8Pn<|#-QzYd2OMaqNbbvZY))&gY=-(2Bo2`d@x93FR!&k2xTeGf7M=kq}F~x;2ZXd z!ycAxOW#8;bf}@&u^OOnw@%z;bw%>~b5nWx(}Is+Ge?hgf;VL4#|NrnvL?(lg(>@XE6+EWik%wL8<4XUIC?;=cBUg4~I=IFNA2 zDqFK+9bauX?rG)NthY=oRoC+Ok8L?Hg9fiAgRc&ty@$s5xS@q`Pi(tQ=u%6mv-Ec3 zoqphc2Q2huvj%v%l_HC43T8hXdjZrOF~wH8Ld88kK3Qm;T{c`#9SVOOEBk2!+&|xP zjf~bQ@%JZXLpohpg?{VfoKiY?05Dt&@LR88NRGzCCleUJVVO{OQ@FH2cJL zP5^0aWb~k=m2u%xtIH6vbc?@y*^H&?O-_zO$ENHUf%)Ll#)$L>{8i6PN%tqXKz$Q_0B5LfY+Ol(34H9_ehvRu!2nm+O*X`{CcAlln-d*w^ zy>eIMz$~m`H2l-nUMvLPYlq&6*3oFWyJ5SQ^zoRyNdJkA!^CWMPVT-%$ zgcuVC_5*?9zni!}&Cts~Zt1dcyBV^)@Z@Lxsbz7A+aoN$H@E@?dNz5!(Y?RHZYtEv zaeSZl1OAJ<*OhNSq{Qu(xJ(1srGFXkgD4nWuxiPbhj}l7xB0GE@$VOcvMMci@ZxRb z*s{%Kfdv3J&1^8bEG|TO8;?zy4Ofxmbtg#^gb)T&G1`0z1=FHHumN`Q+r9 z-WLW;K*G`kjsli$Y2RNKN6%5kAtpTyX%T zjgfPx-0Wq&Pr4$+Y|xG9$&JI@a5=?&)FYrzN>^Uh@=C8PpJE(s-Sjt<>ulR#o`9?+++@HZc*GP*D=NI^-#-Ov`OMkK z7V?xuMMyX+dL{bygbPx7!Tc_PrW)IHP!vdT(ajV1J82H zl9ykwE$;5dv0%#ggW_WrliP++i=N5u+omysX|XL{4gu`JK)+&qAZhYViJroOk8xAd zH%W`F4%p$n9Bj54^N1+X+m0E5Lo`x$vFOm+@Panvasf}UeUd2 zP0k)2PJ?!6-lWRb>K5C7&rm>qfxKwWjhDN0iS?>+JpehrS~Gw0BR?NEYhcdNj(g!` zLalhS&$PDx(DZC2P8+O$e1~utBP?}KaqfMKJJrZ(v}~mU4{xzNcWqT;9+h@!{m8v> z*S5M;i?qo(CD_E;-x14ijD_pY2i{{IpjB>r{P7e2=Vi5kgMjU(#a7?J0Rj`Z3su|t zDpBU>$NBdB{Z29K%o1htYSbPZeWK(NwWD(lWoy2;DYURioz%XBw6=#-HQrrz-F(Jb zz2J@<^9mfKy<==#AX=P$j(B5y{YAL-&}QGRAS0zhfRvgOJZzprr_aoDyZ>Nc4*jax zVxWENQyciOs;S9?lI*lh}79Cc7luJJb1(z~jtpeI+x{(~s; z=RLDbZt}%tOK+{#+IP9H4bGc2Iq16f=C&4Ss`7NpYcNa(Qy4-je0S*CS0J%R|BV4ZFLV4F3(Q{?E|E&a%E(7Ol&hhtZg#UBqLpkUw z(s`~1c&Pt75-&{ti$7_h=<9ns} zM_Y1P6dH8*Md~z;NK=l5y}lBkknq!-bL^%M=Q*}W!F{F!A79^V+vgkW-ZCDwsBI_d zZymz|U0ll1XSw-YT+f}0q`MM8bvE{nj+q{tB?i{v0;~79JTSl6Z~OOJHRCO%F{jT- zNtrq99~huiBCe;|j9Zy5v-h5%n@CSjmkNA0Vs0!jl15vWl59NQ)UkFXl$)TEtD0ev ziE6|Y?7FzxbO~L%)<*a9WjTxf{fku$UbGYE*=I)2U#l2?gDZJBtKqCq8{uuwFNDf!q&bmMLl1Hg38QG4b-^bZbhk?kgH^xQ3zn-6OFu+pY`4 zpY1;#qTW7C_m1b`Q_UZ1^ICfDe_yVcd!k-L>yw>5*MHiP|8d;PJ`A_6nVDUFKJjw2 z^Lf1%|M%{xWQQ5yfbk^Z0Ky39h}Do=Pji~F&6LY#aPBb9HAnGU52itaCgvxt07|gh z-y`c$%P5+n3@FM3<**}(lZ|{_yY1u>`+7^T!es}1}mbHyp&dpUE$)iVT>)nSh#=F8TY{i5WWG@4P^3XZ^|{5H2W&m4YjkK=UBK+M{Z z-H1(dNF`9+Fr)ub;hnN=%gJY$TMQp1#Krf4EU!eZ8*E!r(qlxgp3!eF_$c-Jn?FW# z`rO0TkVar4H3K-gJ){%jd#PS)#5#5=Xjzk*li;BQHENzkC%3o~UXYTInE1HhaA!dB zYxQGl8=mM41i$>Pg(V(o|K>`|meElp!P$5ig7__`G3?ywtibNw8yTL)Fz4tqZB~Qg z3%$IbGcb2D(fy0a^gt<*nUk8SHyxZK2EX_C^JyCye^|PBFfNHDR{*AJb!sVf4&AkquHu!SrM^d7r(lAoS+To;iX1zwMMXud zGSI6xL${Z<&8kBcaLy$NS~KaDNqu)L-v8aHvj`EfN}-&cC%v+CFjVr7VGOz_6Ff?yHFx|t6v%y7vbj$>o$OL5~R@Ud-+CZn8V zRt>g!koE7ja(ihxDLJin1Xd;H!_N-Y6pync>!Qx&#oTZvdL>qnl7vK;B8ONnlkCp1 z3hK`5V0{4dE1wtA(49v6W#8KYE6fK?7CoLwJOVSBOQC`vuO>#^R)2opqWs{g+}skb z)Xn6|FJAqdMf(M;-=lXkv}^ddG~QIyz$>4D=u}9Uij)+0SRgVPpcMBE6`<8JL(|g2Mif$6IM~-fs*SZ^w>D zIl=VPX4`kK&-eSe{NAtE z=hN>W7yt0w^SQ_4@x0&fx9jbGuOn&_*9K*)!SIerJG*NqY0Gaq?YkioEUVU3Vq{<> ze^wsHF&d_*NlLSaU3A+DTcTdtLXnNM_AlL@Yq%SXRhPT ziMf8BWHjLR0b;XnBNG&?e&@iEV3m#=s_;`jgvs5GWe>kF1_&s1KV=aje@;~4!h#KK zqD-vVldSrh!JnenuP)uf*rWQ?8FzFO|0TMV`imL~g0ed@wNY0U7TXd!x$8ksJ1o{8 zra9Ukh`gb)Gpkmmy}6^K(vE$;*q$MdOH|V$*4=txsCP#1t8<-d%lnd_(lOB?&ESf_ zWmI$EvgxlK+TA5r99~RT54dTxE>&27iYdkPGMPG^k3Qzf{Pt5dbG`sWA4&jKBt8G@ z7?}btP16SLg6}m=N$%v)Y>Xr?jaJ(veCWCT;Qq~7D*v`} zOEj@uYe_jVmePN>(zFyl zwpdUitVyoUj7vK}Tf z`XBYU(l6peAwV!UsLOn{8L#9}F{OU9=`6JEkv{ub9;2%f=8%=neLsh-|8Y{PA$lFE zA31cdVd3oc@)6&O+9yHYuSn3wQzhjEaV2HFmE!NyD;k)3g-C6TV?Bhs$VT_|V=UKZumk0!`hoSmoeOroBP0I zctd>4paz=vqFD%!IDJ`Y*LdX&fw6{%BiDsfYkEZ4;uh#?WXQzyicS7)tnoG8EVv?Q z_O60n&<7H@3E&knSd&Lm+O!#3MJ1Y)BEma5jRg;dl@s>X0HHI&LVah{A~}`TUw~e^ zP`I1bGwtcsU-;ncr2uaGb6PQ&Bn`<$Rrk$w4hJe_R#}FNPu^FI3a9>eCq^@5#eRv>|`SCXz`<6Iy*j zAj19FCS4xonz#Y4gl{e?^Y0bDSpRy2OLc~^4&K&SwX63bMQ-!{&N1G2uVbzVf96#X z@JhNqGi)Oa&%h}VmT3r{3#Nt>7YQmgrwsGrcVR;Zng9fFNJnBM0gT{fbohJmo;Z@x8pd|`RN!A zUBz4JCLV&!Ml5%PSY(ZZ^g>>I>J?{Ip7CYDsz5=%!qDpGY6{FG0iLzWN#P8d8``P8 zEoB>W3})o_9#%5^pzn?z4q=(DQZXdF>f%&z>lgX`)v*l=qsMcTX#OpV^zPCgOXGp8+dZ&rDdV0`?-Ihb-?DzS9IkOz9(=+5gv{u+GL7Q~pWWEZ3}hbU z8y2zYhI){}AVk1aZ;#cBx@lEAUzyhz#{qU2nWG&ZoL!ZVPL&cQ=i`^C*Pjl{m(Pvq_|Tr zSDgFk_ZBMZma@XydPpDIG>8cJ(3*vKS$=%?2xancheiAScTPtyZ5Y0bfKgyScROMv zYQa21iWagr=mAs?0b7<=V@#yF4}SFhsaCoPcLbCr;3+RQsK{%sx7rKm!Klf5Z4TFf z3&kz<$-Qg`xuC~NQ@RpPR&=-sdu&$p@%nzzncpkwd@qI$lA*X0X& z8}#dfWlsM(KX7!oN#2=>n8J(aq7Dv=5%qFp&JdP112sPmuBi+G7dRg!4rm7jJX)Qv;YSL$_Y)oy&ten@ zT85~*1_$LVwetxq@PTEk(2pT_PK$70F#MhaX2fo_QQGu)Afiif9JZVoGGyj1UFL-* zG&CnF_=f1aODWzGjbEPn$*%W`j+NTCf%-HZO?K-{VlL3zc#6W1(`a>w8RZTi ziDI1>{ie57b|kBRJWn1ekJC6>JYLtFu_aB* zu=!bx3Ht_Vp#}3rr!V4V<{4EZ6&xD(XsG>Ds?c`IW!-|i??8lSrA!1O--fj)B3>HX z=Z$Z)wfnA3c78h7K8zS<*MmI`oxqEwjmgHFk2;N(5y6|g2tycobO`Y+V;kqlbHxp2 zU%!7rlrlI-@#$~}So|XJZs(1gEY~d`Q6x1aj4KFP5 zb37K}fBf(>I@V!F-h_xkUqSW+ePAX~2lY`YcDg$M_s7#Iyw&-UZ}T5;8udEZt*ySI z0X>wk_lk3ee%0B%&`u7s;>`r4nxS+zG$36&3h&+<|KMymzWP;|9koE-NSu;;-mD<5 z6?K#&3p`htKq*4nr65KZdu2K|;*jCCeqVCB%yV$aTbzLqO)#X`+WNukSY#Kl!~qgA z6q!u&8xqzuBRZ))PLpF8w4Tis%IUPkYX6)vFD~vPkQ?} z_Q29w({zT5yyK~4a+F#9=H~h`oV^$}odw}CNL5~U#vHams0|oXx}{YzU-i1ba%k`S zoCFDHxhFpOVBrAP%@-v{HN8!tLqQ@(J1!~BHO%*EER@V@OjLEp;Oo0$q}dWrBs2_K zfKAA|{7RdjyH9Z)3BFM;R!AVTh2El`ynHd~v5o>BFf@{Ynqu`qq>brOq7w5cO_G%J zevTbHnaz)!_{D_-%KD5wA%#f!x^ZL?toH3L@y!cAFHK7-bs_KCn<~T6)pJ@jO$Wf* z@gYs=puOMvN*vc`)g3N`Fyi_^z1EDAxRKgSFU@lUVzDkDImp?d*N%#&fNwx3$@wwG z$6P?{zM%k!Z_y{#Fku?bUNHD>Xt#-}vaanSrtNVeq{?Gdtq!4dsm;j$0Ws-gL!=a3 zqd_@&^DQ&0CUDv6=-gOBZRrdfjt(%)lV4XIUEmjn*OkJ%rZ4Axrsg9fq!eukzU1@# z&oZ40olX26NGDonFk`#5D<8v@R~`h5vc%Wpq!b=doI_lDZ;dG`@f?m7-glmN7^wRl z*|bkzTt5=%^oxr4St$wsSy014Bg>!cQ-hj`YwRL1iJsrK}0 z*3dQPBfx_!p$3z-ZkF-e5-m)sBKbs-Aiz90qII2LraBBDm+Xz~lsdHXkue@=mhBmw zIFjl-f$TS5-HOr^! zwnuk=1ZIG2V=kJk#gcC1%9Kihox3$2BwAJXL_l4;7}*cVPAK3>bkhvI zMlcUt2RKtkyK{3g++u~E>vnxfY-0eg3Y6<-RMCw5tL#iN9B#_$@Pl$b$7(z}xZNzg zI9R0KdQHyO8`<;~;YCFk*4{Pkk=vsalu}^=4gl@B`yj~jr(Pi7A;s?)5-BP7UR4q( z%18l%4izK6Lj0*1oGLPScf*1NNKG_mZZS6M2q1O}e81xUfg|&&&ZxfgCftulRW`XS z#O6b}Z>>(;63<}aXkKCZSFwR!sy2vSy&+eEjP2XCN3=7*yT>i=FCefLd&3$a7*952 zQ~`7BJ>on4Den0sn1I1H<)IrCtgcb*f{FM z!Lkkw*bne=m4fZcA0BR_sWKa$eK91OEEGdNObyDP!HaU*D<5}V15mVXV$@~}Sq{Iqo6>IRUAh44 zJ1nqWP~3A{a}HN3oQ9WqhABMjmFunXN)S@ym+ZaI7VB4j+qD=!Zp@p;QGFBy^0%dZ zqC1fe*)#odaYm-d$CUWEy~ct$3JMt%vr)f6zN@kd9d-5lXK$7MyW4N>doJ@VwFCbk zVOL|pnE>)LYIV}7({70%lPSqmd${hg_ER0s5Aiw}Z{d`9>EfZ2M>2}jJs4|aq}$x^ zh@j^w&P7PD<=M{sU=-3PQ7AlKK(n1CM}g8uE|*-RJKd`f;+6U(OggYoXDm_;$(6>BptBE%SeHq006@lX*#$&8rxr1 zsYf=m*UYmNN|rmS!2VeY5fWHYq;);0Ap7~a0tN%GX4R-}a%KjP&NaMrX-NuHE;y8? zS`)p+F}?WadZ_7x>6|zvZ};jsIGZo%0vH>QUhxN6>l(ZmjJr>K<3qhb304`}e}g6( zBRV*D4)cq}lfDg~k&~noHXjJK8d*z&6^`~N^;4xTz25*_oiDUA6d10ry_Lqxn5>hj z2~MuOjmn#4uIY$&C*`S;Z;{RSV*tMq0sQn2g0Vx0^4OUM!Fen_IO;p9|j8IxukQuaeU1!c}F ziusnW*Q+x&p1fnd!+5@Q^QbR;^<-fs0JRl+-THZvL{m&0(k|MQZioKsj{4Q0V}_t= zYd`~XhbD8HJvuoXc`au1)!nB>8(YL&skJpj*vg{V@e+OF=2-e0w1y`! zOju7F>8yPYTBv$x#y4{kw*8;eB&~S%h>9ubdQ2d@LfDwj;1;!=>rs72MnzHbWwA-= zR~R9nCz!pv`8=7J%{*7yw8phU&mht7!uVIh{+#l;L1=$#9J+SP_*hc{0}G_7K8mMv zDlHodk?`MNt>90PUvV_9(tdpD(S93!SM@2X#mQzp& ze_r)Fx2hyh_&gba^rWpyyI)rA6nwfuV-DljiL_cjq-8)M(h2@Gy7Nk%e2c!(;*^B3 z{0MDfxr|daoF5}NBN-%LK%TatNz7Sc`c8aNJh}d94u6N1;?lfHcPtC?CtW@56GZc} z>VeAlf0j6Zb~He=QXpK3uhb=Mnnq zf@$)iv}Wy^0nL2PTK+SN{G#DykCkDI?Jq+F!%SK`PvdIXatS;lYe7gewu#x*s-$08 z`5hIUW<`hq9NZLEY(K{t6?=?_-$RMtY459EZzdAEZR^7r)+fWfsm~RMW44ezb52lM z^7H&V65C^RYL7m!jz@dU)f7V%jON{8YdOk)QFA?j&i)xb5#NJ(Qa~<|FxQGYdtDx7 zyOzd821jSk;Tc2}E78bn${SpdG@3U*99Ec)i75%F&xswBFYdIyUqo7{rZb7JhNL zXX2Sv2^!TuG_O!2mP>Tkz!zrX{mlGA6|kT=AVzilI+QsGw!h+cV(lnm)>OGi@^{JT|TjuSGUf|a)bXUYMqu>9X3$7Mv zCun>}8eRs=TojS~PqAjky6r1zt*;k<5RXY~}f*)e0gET1IHU z*J2_cRj!^q&8J+1`_%Pio#_sVjWu#~&kAfF47(X=(>uLV65_llaWk;x%o@6vGcHVG zDttu!zd!0CvoBIOSfcI)J?PwTtFaQ;Xf3REOQFM-*<}Su9_Kfa%Ua2r_mD-9BdA=}d+9$g$JR=^vA6bI3Qg0O6raaJvqH zP5h1$aNy+W$=Y3ei$47T^m)Y{*3X5ruV2g2PEJqv0BGGUxls~?_q&glXtkMd%5yrq zFU)l%qxh47d@KgW%xBi%K*JXtE43(Set9XdB%M^uYC5m(ttcKKx7}!N5I9q?uoye| zI6`Y?IrObl=k)pErW4BuRg6)ol>xs}nxb8yssRW?m4ZfFGeT%nsX1S#Q&PdnKMKy? z0DNva5>m;sN45Egeyg`$;0Vj75%<4L%U_?-SGwVigXkPO@BQZth|V3kKTV8_BaXXPz7^=yBsCHLQ4+Ch{u8B3dEaFKF!Bjo>C% z%3w?MhFrg!I*{D4v@`PDzGAgtgfQ~>tvyN&rS88?xu9;9We;(&PijcA@~1x^rsYln zQv^Q_Rt#}fhsUs$r{}O`J%5)JwVY^oVluWQf}9ub(I&&e@~gZ(KMEWfcq8eJPokvQ zT~^coY9)JlcKiEk@SN%%iVEgv9S!fh4t~x}dAFbh-EwjDSrkP=ZM6M(#p$<%X4eL3 zCic4=1;DPkPLsWvf3+wF!o-G6d0E}x^#E3?#s3^!N2hb?;U^3`GICH-|}el_7yLN@^&x}xpbse zEg;B9xs0v*YqGI2yBcrh+Qu$VOirzBG0;|z)Q{D20@P?CFFXjX@s(7&oc&IuF)n$b z#JLxp@7m6AdJKOFVGLHiBX>19i$$))^K_O%Q1*GTbU8vge!(@l++owL=5WTuT~Xfm z*LhBwd;pQxbwgQ+1@rNJum8jHe`xqZ#&l<+OdBDr>2b|AkY%3#*^p{`` zWBwQ4_TTEU`i-Nf8y0>h;!Z4;4+H1h{s^LdojUzWR(pUjn16BDfD6qZNSp7FsV(N* zlp7UqSZ1_^&z_O~!`<}PZ5I+q1)o(GG$K@b8D!kT?Cn3|fc+8MU$nn$54U=oYjM+_ zCENbLfAGIAb@a@uS7)7eA%wUR;Ci0a%>!9~>W%#M{i$rbfwmVZ2bIh9>i))UZtsb$ zDoUqwqkKKYQh8?oR@MLePQJ^pb%68(#8TzN9)#wKECxt7Vqt?tD=R_zrR$#DHIjjX zUW&N?$lAYG?EeejzP>+^{ld?+aU1B`P@`*8+shP1WzWwOHFq-AddAqPLW1LeFj1La zZUcY#Z(BrK|H|%DnLCnuv@T4&;^j-pTeog4XPE7~qv*_hP@UcQG&DFEC*3TUG%XbJ z$E|;cMU)@*!u^Rv>L1%4mF)=lEly$=AQ!Ig7`^g0StTd>wAx25N-SCX-x~FQFW{X= zOT#2oWGUckVCbHA$`3nb_Q#6p%h)~@tEyif`s0H19uYBFQxx)OzA{H*l>?^l$^Wn0 z?=xGh3yYUIDrxme+v|^}#{TPn zTbp)elK=SJPFNl_^hdRUZJeDmNisvH!g}&pV#EXs^W)UJc}6>Cq3RN`9`1_tsSd8cJ5**6XD#HF+U zS(UH+GYo%q?)v$72)p+2sn98w8qOvhJ*{r3{oAn;Jbg*ruWS4guPui zqQAbpY_KCP?hgz9^2@)eKjAYd&U!Mxvysx71pFVb%l~q&T_%2Q5S3sgza~*X*;nvn z9Cf|#D+V(kLFf7gyq^m>y#npF4N_LABSPOf{=fd>fBbN3PgSonQQG}H_k)h>3t<;R zB`$HPTU8aJAp)hUUdq=NktyABjEd_|uDS-|5na z>j5M^(b4*yOihQSLk>JikLn($~2L zjYXD2dlwH@1teM60~T&yd-0|J7<M2#?7VavXJg88&09GIc`1I1**)T?5UyYjt z3Y7Dr;~t^kC{4fH4P!qyO?N>@&5Dk3r&t#8p@)Zu+0U%!@uZ{!uIJq+z{9%t zz(T#am|;nEBy2#V3;O86zZ#nIInBr7lk?c`JLqi4*gJwp4EQ!vom>`CCfgFB_@pd`H{AxBO8LQ61_#`a~?qyK_)o z;w$DyYo*EZH7e?S?1qPh8W|^s6B;Z(17G~W2PRGhuk>Zi(?wn8f3<=)rfqEQ&JW-U z>}7NjXM_Eh74MLu3EFgbnAemaK9U-(Zun*4)k=ZcID;9VwOx}oQzd@RD8d*4+Z|sd zPHCPLMh}qUS9fy0L&&Yqni5j`qt@4X(NWaDz?GRY1(qKl0NF(H;C%le6E^YwuhhG5 z7^J1KbVv$nHIkDjUwrq=ix>^fdv=p;DQC2`FV#xG1&p&?*R|_jIGm@kt}?}zfcED^ z>ke8nsIs-jO%@-FeV-E6NV*WgHjb*6Bx0j1wYUjq!6-d0ea7dtWA6&aNH8KuduZAq zoQ!_h#dPT0$iBvf`0;}N zH!SosLfK6r-sy}qh~EKz2he!!?W@GJ3(p5P9k8DJ6=D%fzktDA54b5RlA5#A7d?*; zZG(5tU7cx@Q!Al5h(;f((OAeR z^~Qbq^4XyW358FtE=uW9HE*37AgQ_49NoOA^laKbGx3vg>lE7A6HoT?fU4P5Y@5TC zm8nhjD=+g)&mRg&*gi`7k##@NPQ8qvP+2x7r%F976;kCZzv+U|NOQw#Ety8`C(qS9 zzu08w=012BXo6mx9`oSq*z?zylh5VP%?2wD_6Msw%JvuAl~iFDLQCx9I_S}z7S`vaw4mhIHS*fy-9=Flw5%CSU&R z=ogC&ttLz3)f4(F-y0dVjy{rBs*AigJ4Q!EEf44?oi&hr;2cWusu47vCDcB&P}O4N4xba%v!>H|C` zrX4HYVulU!N$HwoqYvjAGW#cQ--4RJ=&Dl1oitX_r@>CnVkXeBsZxBkJ!)5QCrN32 z`mUN?Y3~WoON*~1v@e7o34BhH5-bndVA2M6Jywyep+AjMLf)o0PmD{G&H_r?F6V^T zorBCt!p2h~b*5NlxJQe3h2Y@O4fBmvwKPOS^^Mgi)D^pib*azmo(ew)sULZWUN&17c(}#U6 z(xyluD-(~ac;HB*{zzWfJLxgGepDL1vm}*zWLS?&fqXaC_Oz`MLi^=05=&NJRr(Lj zB)#sd320I^W~>BSmZ}1lXdH0FQi=3WwOZsFe3dm-MSr9A69-TOQU#geO+G}Ps=yh+ zD%rX^&N1dfMqo5_@SuH*y>S&L?;79r?I}xkiA;Eg-!ot8($cbT7`0jG=s(w@8VpLYk!y!G=@Lv?FkGa*%>hCO>C+WWIy0AZ7dyd%0lFrMg~%y0r~JeX!r$w$Mt%h|hm1U7>}-{G z(zWEW)XYHYoWouQu|*?NfxT{{mIDNbwh*Cm9ooaK=KvB};ZtRN;JytfI&gcqk}hPV z-}2(R3Qfb5MWAEq8h_UOmZcIc_?V!P@DdUrm_^7-HmvM|Q((*S=KM}yjudcs*6{+_ zLE-yc>zgAeb0(q>GVZ@h)y&HqDbNcO`UWQ{n&U50wB}{ynREBz;zq>Es5!HUTDr;# zA|$&gvEwHmS2Y6sG_Lj~fmz`~Ce ztpq9O&NE6~)bs^oJtcR>cTCaFBdX~J)iSnO)tx^KrAK7f57YHaO2-xKOY!HKq zRONxFqfR~Kn{#9BKg(~s%L_GxN=5e!L^*G>$Cp{L5WP26Oz&T5bCZOV-|WfNS@UfW z8VEmV4u-qwLsixUi;M4^|*E1{tvM5mT`gF6-$gmYc~o|o~Cg^;+myT z5)|2YA@ng=olEIP*;ZbVe`#BIr|Ft{+I#`vX32dyC;HpdYE#@6tePFRs@BPy;K(T* z)b^tOGCfk3Ni%*#>r@yAXE~RYI|3uS3gth4{<6^$uJJKyVb;cdW5Mphs0JRoz}?Sx zpOHQuaWvoh_W$J%6)-haQ9srIi^QK5DKMUwlYif|oA->!34=#8Wj6$hQp7u%)$7M> z%yODv&Q`!b6hH|}2w(F)-5x{1%r-%SZJaZriIIQS!ZfG$$~oTb^yXa2;Ui5D0ZNnT z#Ti}g>|pfUfYhNe)oGjIy3~8AXna4Y%4p1@ z@(SoB;HP-Be$ZfRm0edjGi=5Uyp>l>ecwNRpw1FW;V}*`HoB6F(WchL0hjgbanD);Lcxs-KLWOmZ+$@#g(vU#E0& zK`pNR$s{y1tc+y_a!X;Fvuh}z zKnh5k+_pBIJ96jnqvT| z{oofWi&NYn$5`&nm=>9NE$n6eBg48|Wv*hsvyUHQ^+yd~-tE6h7ih zORNGtP_EgtIjn2ElGX7W>)DtWIg<*4XL5%$D`7+K4lHfBbOdum*_gMGkP+JU>rU2q zZjx`Q)9{Wg6A+Nc2JML6Yw+_cJy7ur5pP0uD}f5ZK@4OlV$65&jWP8C}2^RT4Cu$ z8b+=Fg>m-R$fb2|1bMVX<9mWGaWly8#CJf`!?*c~j6@(!HwHGkSSeww zoiqn6#JuU_MY1GUww8!kuuc2SC_+aLQ}vi}-y<5!A*D5Bc=h>g0Y|*jb}`}w>MGzK zY60Xz+J@&k0+4+uQp*3c6o@-;OKt+CuxASImE~gml1luH1#fy7-(7YG%kLsk;wHn!TeV6X z;w2vBI@APkY)Puo*xA<=|I)CQF=w@9yWDzxo!->O?IEZj5&f_ZCwv|SPl>JKgUIx$Ek z(|l^r4?q}6@UumyGvm>nJs9zs-|`1GL=zqM&o1XNYME2FcwV+iXXc0v@&8?eWc7<$QT|R|aMn z^Ox+icHN6x3p!z?kR1D(f7OkxpI_edoMZ6&{{O=jLytZG5*l71-AJy&hfUV*eRBr5 zmp@!gn$Jq9@b;e)_p~W%=vBmAoB|OkPrM&?UrrxANlM5xYFug*d%F0(z`C#ofBj3HY(z^^cE-z0AQaja zc@47v0za7i=8*V3tR$lfIiOvsglCmiV2asR@0Nr}!65E&VL2P~OOTdS@2=4g=qiC0 zsXrHDhperDC&AL#*#422UyHF0Z*FH^?C>EnosDY}yru(~F$McSXkXkLFy}9CW+4Ig z#z62;a=s^cY0b2A-RN7uuU08+?{mf4(%Dpv$I?F@YU&-(M?~*nAGazx87Fz6H`;;K zk|%xXxE3fR=r)J`8u8Gz4)x7$XCyQ$0si%(M}sX*Z>pIHyhgYt-5d4U9b-D*YVN#% z*f{ek;2NfIed@|>1*adIVJRa4R2WVdbA_9UYMBX@VdJ&QY!~Nf8`cQ=dnLU z2@7IliMeOoJJY5$+X^p(Oj{T)11&FL=eu@l^dh<9m$i=sl7_5dAAW0xqst_MqUU@K zcPIDH(cds(p$F?@9^48nZop9BzOzw&iP^5N&fL<%`P|dga?}p#b}vIsIWsN$<=FNK z^0D-*mme55VvD^rS#-^t#Yde;&O4Kn@bj`^g%aVOj}l^o6o?PfY+J$<7y>_zrrF-N;94!76Xvgg}fjik+aeNt8&+u*E_ zk$g0L%gX4KZ!1XK+2~@78`$l&;N(-IA`*+o`t4I1AYlt|k_{x1dgp(A&dq;g4 zGSJ2RJ$~kqeUjgr^WfwZnjwRs6shVTsKq?7m+KVLlD-7G1B}Jk`g+fettEUd%ALc;P)$w+J@vDGs^`*N{QC>;Dz142h$03CB%~?y$KO7&kj*9ivwxTQRG)23Ot%!TA46~G z7_Uqy8hx{gVYo;extDwb%uY@?o@Y z>&WAOM|uLU7vp5IYDN~sTS5ZlPm$wer9ot3RBOMrHHM7Y-y1n42|QhV-%gHiWw?<` zv$UWT5^+s&B0bqGtmT_$jtE&CVg1%Uj&B{<@3E&gXW-CsXZ81&;hJ95zn3am#sQ)c zmBZ`PyuM0BSRtIqNiJNSbsK2IWnL}{5BA=aM;i6@?n0*qR}n&__sc*ZdZl?Cl=acI z>4qnt`7vyt|is3*)yc+JeJsyI|j06_QsMtr1F$8C$ z04+XP?h)$g-SgT(Oa;z6EKSYGTkVurRrst-4PG<{g zzk{QmcGxr4g$zgeY8{O^h*S4o^oL>26GNVnr!iUMa|T`Fh;0mVANQlObAY_G!H*5Z zQn}%L)bH#DJMueMZDC=HvCRpvm;Nw~7IE&ITgL~NTN#uD=Gb%TVeU(5j9yKf8}@>= z_Ih27#HQ%JaY$o}QpA))V%?OcUg3+4eDo5KbzCZ=(z;zv5fRfOZVfFz34^56Zq%Ws z!{@C-gGs{EzQGO~*582z|AL)VJ1+ok8(mMW0q-bujT^LAvE8l83JjFUJh6+Uu}U2D zl2qD2NvsH#<;Yoh=g!)%E?wK4<=M7exsR=u+`JZ8ZGu_sHs1Xjy>out4(Y*9mzE>Q zB;*KIPa+K6>*S_VC#@Zo0F$W6gou&QN!}HC@ViMSx&?0poHH-E`3UjL zwu%6C0^l`16uErqZ0bFI{oEaQF{sIR8nGx&vBqu`=iP|BRc%7r96VpfOT3xw zB?V8e8ZN?;zLz;ntXvFIQhFu5?h{&i(dS>9;>}}wwvW}Gb?Y+ybhJ+P$;l7U3XCL> zn31*@p(}9IZn^eD!T0Y1Efom4zb%*v@d~IY_H;0_5~}P}0skvVv}gY9K@!zWUApwp zY|4De?rCa6v0Nna1sdrzURIXHUEhp8!FMmDC!$Kt3@fUTRcN`}p>LsE8>C0LCpO=) z4Q4I~?lG?Rx;U8BiX4nVxMv0xu{`i!*`(dg!j!l|f$Ka<%P}HEIZ38=<1?rp4Aw9@hrToh0s3)oRo%1mI(({pB8l zLT5kvq#voe^%FnRDI70yin3%nnmrF-z$qotTwV|C%JHE*d*$chwfv^q{uz%J(0l<3 zkf_?EZEH~?p_N}F&Q2JrUEz0)XIq6No33Kv(%-@8h-jHT4uV;CC6?y1qJ(_U@23m( z*jB~7X<=TmB(KbX_S^Fh0)8ZgkXy=$Y_wNH#Rj)tZ{;!v>zvq_bg`><^(uiDonxnN zk~Nd96f7nmL*jZy>p zM0w)vNFy)@eh<=t#kGn<7)s{@$M~&!KC1 zqJjdBe^;CnM3WiBl3}2lpNg>v!>gxxC1k$pvnsT-^e=g36q)r3riDQ~boE(HC%6{D=CaJQI9`xCB=UX*V? zGlD)5__bTbXsR6-l#(w$8?1F zs)3qT{Slj(uBI>OpSW}fr3aunGtM>8+qbS90lQ49^V5`CV<5A`&X zss1}Of@QhU>I_~;$kGhuM<>ZUnuU~gu%_`xe%dUll|_5F!WB9zOQ9;?0h3dvRwK;D zqR?eSPA`ft0$t3{fb(04R^|CTt<4Zvx2U_x$vmV9S>sAF0083>*0&a4c_`XjA0e&` zU>EKwffp#kT|@S4KCThB1sI~TX1!GUVa$}c<2m`Nw$Y`49=Y-&!{-~is5cdV6Pt4n z&AM_Q)UUPEHF~;z&Q8f6t9M5`#H9)$o~zy4eR->wjXd%6^H0L^ zIf%@)<1SPH2`-_$jl))H7s;MAYO--$u-6fQ-QTM9eNZx4+L3PK_-Y|${epV%Ozx+i zp(Y!$yWY(3Vg2MIUOePYpMF0tC7pIhz=Y@CyMsAkt(n}XdZ$dRod;c?O#_<{nW($K z=LhvyyhO2Qf898AkUr5k%xoxS=vA`iT@y19@FOTBA_>2i7w&-)#yPJ(b`0eV*IUxzNSEPVe?7M?Q;lfIW8DMzPW#I_|tYbFajY_4>E`zEO%C6in}- z_e~#YarBwzUKlZ0%v9?LmR+?^s2Uo}beynstEmWhjnfLJAz$dPZO#GD)o#Izw3vv( z{M&n1gpo@fWp4RWOU#Jg4&gByy1ab&pYYG+fn8_p>%Pf8_j(81Ga|^~3<+S<;ArG$ zM`x^qHXS;)crt^~wJv)-YgTKNg1gj#v@4mD5JFgjsu+#x54+MK2Qn*Ed&*NW<+2pa z#k{`S%(g{?0K1YhaZ~`LV9=t(=gXXq+oEK8ZFml#(0Sl)pL3}trrjT&VPofJ-;QLL z+`yK2_qCbn1P^$(qu%%w$>lZX{4i@*@5H{ijECTOgrl&&$S^hy8@?y zt3ET=R5pLb^r`}o6B}akinoP6$fw>yB|HylN^w_^ol$qF{uXDtsCxev{&vi$=M8Jy zqmFM}J@K~3Fl`Ij=0OX37BZFk2&4;Q1Q(Fq)l{;pH8;F$N$xI8hvuk7ATp5us}Af2GC=rhBZ^t?R%y= z+CTdxo~=7OyWJ=&^(|e^z{Uydz0ku6MRf)Wx0eZEwkS+@RmI`}v8*aZ*aq;4<)cEx z=6BjPyyq`%V)*(uo1}8dkQ}RP1G@@yop~ux|C{VV#j9(?d! z&yzTf1jL6IpZ}l4*V9L`{r4V(Zn_P&s=RvwSvnRPDHi`1<^7n0|1-TWvX?Yv_FlK! zn(^b((ue5wVvu<`bGms%1Mmd=J3Kyhx*wjL0heYiIf=FZFDQx*^l2ivudYa`}^Cy_q)C4yoZxFB%614XJ&V2o_Qu5N9Hehpg%q6>}xpU4TpH+_jH5` zTyaEZ9{H1)3V2^iD8r4aXvph+!v**GKe$l)T7#zv1I)D?vvwL`kAthS{8*_VF^x zzw4j?<$uAEe&xMS(3lqOJ^fSP|Aw7F$6{i_*6qqlOZz}xv|A&EvG4LbB-d;gyegxC zekJd^j80gxH(6OGMi&%Un{pwxjO$-H+9gW!0d zq|{XFb=}cgJkkdXno!ABR zzyYMsM$=yWGb|0>mMs%pouZM)KKyO-|9?+K@C>e!LN?R?SUmBMr>TgC14so4fZu=H zY55E|Kj>PCKH=}W(jiMghrJ>XE%|L-C^{#^-A@MEurxRu@_pv2z^{EP-``phN;$fx zPkYOS8dNrP;CiQfO??js`^X+WbGFj|EgZMw2mb3PW$lxbS|CdJd``o43r`xFkwG;y zIx4WVr#eI+>M;@l-`nH~L1n+P~G|p?BR?aBZ0*`;9!9MiS{I*j?@`R|h<9&b<>Yud&<9 zyOjjKPuEyx!T6L*9Qs{sP*?Lb2x>nv=v}Y(xAc$y6Fo^@jBi3B5|pZyTt-)ME+0zx zYn9Z=z~SmgHW>4HEC5{_BJJScX&eG_opOK?92r_Z1c$1l-8eBY4T@pUsJ;)YvDm{` zwMmqr(wY7naoGRjaG47IdClMTN|AxTl`6}_|7Mzr{ZZp=)I?4d8Ps>Qr0)?b;D=67yn9#?pDk^tF8|H4QN1~oJlBVGE2plU$vcO(E=RL8 zRIL4-=7pW6FMKt;E{N!J`&;zwe_g8SO&#Cj+i#5C&lT4mPUnAbmoUFp6M)lvP1J~8 z&0A@%ylQ_aO7AtUApGq>?>`09$f^Ut@ciq?z}G&IE|mS+;kQ2cW*)pW#C5-81(uKN zINcn2NFxeHV{9llUP~W>8-f3$f%KhuxE%O8hQ&?~ufX63=blrt}b7w}4}y+9mVVXy)m1qhPwjXvRQd!SP(H-0c-5 z`(uf5!t>4Q*93G3|8xalC3hehHDMZ)HW?-IbPsJ178&y^D{c|bfu!HgjWX#U^K_`U z2g?V?W(O{bye6Pw)(|Q@K62Vg?B8o`i?#Z~D8ccDT?qu!cHSP%ELJoSaQI?{)=F1v zW5V&$m@ILxVF{m>mJ10D8y~%{qUGjvaNnWD91qPkJu#7qQqcZ0A`0dXTl-22Qqf~s zX7k4RuHJLax|)@i*Y?NHzyj8mqhJPne0;wNw7bMcob(x5LEQ;Ot+@UqVF@pFzhX%LmmYIO0WIZ4}19ORiHJhyhs#Pd7V}&^G&M zNr$#n(p8L;O^g-F%Cui{Hf|7b6^;3k4#JZ@L*S(@DL+LGr-*+5lX^N2Y3phW??;rG zw5z+_*S{=lE!)-KhIr<*19`YFH)~RzHLEF^vc$6j&Cu|E^s7ZC$qyN%DQRhGaaREg zfq_m&qeX}`&&uC4zKNj{X8q%!FGMgaXL2ZrSj;n*gLOrI3WER1Zv&mVTzQL7SAaiq?;r**T2Ucs1!`Dkz6UfSQI!<^@_HKPse z8-=x92?z;C+P>}71cSMSiCp9_kKLnLX6=fxgDNZ*>JCq3$_w)pzp!m=>dIb^@WkC- z?ak?`f`QL9+cQ|;RV~g^vqsq9lSx2xWRcjjJ-6-lBxm8?6ASsB;D<#zZOfD&yyn+K zmY|D0?R+>~wP@T??iSt9@6}WNM&~d0um#>!ZnGTVuV%ShUdOyp!AF;%up%fmNA3P3 zFT?!g^NCGNj`f@DAZ?)`hND3UqspNuZ{yML= zZH$G-2X>F5(e$y%g`k_BiPNxw<(qi}e4cUOnWc+0eG z00~A$#s_1@F`Fd(ETl$GfRhRJMOsTi(3fA<;xl8VNpPo0qMbo~RxR0)?bzI<)7WJU zrKr^47dFOi*3j}BtLVndu9a}t7oBOU@={I=|hqVGFbbSI1xHz7r#4}R)U^U zF6y(yqal5CI)9=(bxY>;9Mf5sr0Wgl=His`ck`R(V;@s-3x7^HJmkp2=OrSOzC6mJRzA7_pUAf9G%yBf8O!RWG zq~=wB?8x4OQSH3;b3dvD5uFk(sUM)z?2VEAl>~yEg-3A@B`8vhMVR;Du5mr!va8Dn zk6|{!CtuV_+URB~hFa7<;dZe=8EJsSYc|pZ(3RnWPCUoDcVXBN+CYgtbo^h*GvBLm0*DaR zUV&&XtT)5fF}xCP2t6}Bg;u=3XSJ`_*V=Kw>)|AY(RwK#$8#*J;=MO9G|Y(dyTB=W zUD<-|RhMOO9hxlBwd_;B>fKnY@}2*zi|%Jpf=Jg^@8zCt!2OMDlTtYLp^&bPivgRp z?>@(Nno7hOAHx_C%1#(#7#@~oLI>0*mB*$Ezw1}>y%D2}H0mS9MpK5k@BAd+^XHrH zZ%}m)ORC)Kwr%e`Wa~(S60Vnr-oKG-23h4|B-HV8(5A`3<6g<6mtubQ5 zKlcQ$^Okt3J~!f412LJ**8M0w=` zIR$@8^XWJ>;FGxe@%)R&dp3BTJOSe+>f;up57yVjv!QSkI%J=LIbOJtW|P)`+|I>E zFSY7=)srBW=e)_LEYibiax$ZM2J4S!ta>}3aV8!XLGYry%xrl90kKzbG`s5t}D%T+z zO^cdhV#{!*Wv>Yct3@fEL>h?vqKAQ&8){unlL&Ar0PQxZ3f4CaWX7JUWMEEL1fpoF zf>xT{vs%QrIE3Bq8z#T0tI!-QDr|kzU8@&Sp`C7aS9A9u!+yK^hV$Wulur`zSbu+M zhsY;}MvOVj_cr7{dj@??p-AKD$mHF0eceYa0rWZEcZ12HGp;j67K1e3X;O!hAFP2X zS>AqepIXJer*)ZC&b9_;yc@I{-`0Fv7*VQg=+p@Oseh;XX(&opm>@^ZeINEZqAkV-;aCm*61@@8 zkNaFV+>l4a0x28+X#Zn9c~7S)0Wc5ru<4Ey8K+d%410r9U{uwF;2VV=C1^{=YNP%;sYEY)knB+dtI#U$43-Qs~J*Zs|1D{vy71X&@2uOH#` zj>82(sFe*Kj&M+%OeE-5e^q!<6QwCcg8eh-e9j*hDzfeww2f3oZ+lS;igFjX%;JBq zH->FU?-OmsLT3dQ!po+0EbpWcw0~cBiAM&y?(CzGHqSIHh~oLFRc~+Sa=qJz6h+ry z+KI=UsPrx~S)#=+n`2jeVu59 zcI6)CoW^^jMA+5u5{giUGT-WZ2ePclee5V4zPQpB^vDHWL;7xG`()3kT1#Y{h}UzK zA2Mf9#>j(bo>%}uG=A4o&%Z%Kux-?>s3yj^`IyCg7fC|nG{A?wJlv@zGf3DrQ)NZIP<0mvbIEQ}taPid20nso zUiX*#v?Bh=bW`Ur&)4l(&f88Qj0S~c_s8u9@4>Y@CJp70`|c5fTVrCPT1OLYp`EJO zIgne2#R`SJnR2#6(Zwy8UybWa&xB_x6XL(7IfB>8EX6O5CX|jJR+Ka8D%N2Sux8r) zp_f-fTuLCqbI%A!5>x$O7B&C{;*vLz-ed$!ud+XrIJa_7r~SQfvfgLPd^4gw?_y+T z*WqM55cocT5Z$gNtYe?}JaH;A1HY;7F_q<)@M*DLX16Wch$y{#?80+ApE@q>#u@El z`dER1LllQAX zV(ssrT=9EgOPrn45l@TrF>KR<*h!V8u4-yo1;pkLoz|T~XBVLbLC}zjubn?}|Az|z zU&7}xSD|gaJyRAct#2VgamN4ctq&_9zWx1YyUWO1tBq^3;Z6dV;NJkB1I1R{dGx6b>YU!h8UyX=k z0Ce%x(jqbSuZL{Q^+USlkX-{`hYCplL~;+_m9jN$k(=kqUE#k$c>^+NxR9)`#y^0a z+w%Aak+lM9%>awsgipAFUj%%`etAddMNf0@FEu95NnGqL0jo*1a~f-LbrH?HBV<;p7#GL!29T%7rE%_F30!tN%r&v z)W>6g1lh_~9&6jS-WcMDP9945?Gx1DfegArN^wQ3xGel2U^ejD8A9*;U! zc=af#0}#Pv*f0UXIClD=6{Ra*!Q@Yx&PH*aPkR*3N+Q(4Nw;5onEe*8y8xgz13ZJ9 zv8uNiXLC1J+c8F`mEw`y4KoK@lp`0H+JYN=jvauBZQ9+-xQ)rqX0?xW=0hYit#;*D zxBC^@To^Lb_#&El(jli?`pJS0G*|4wqaxg)j%4AvF$l!x32ka(^!qaoaS$)gg9@+C z+$D+quZ*`+Zx%ysz7=GOx~EA6N{4X1;JM1Qt35v39J4_!g_5suc;K4;L=T-)Y*3@` zSh&oqk4=v{$Fo$L)hDfM6$LW1KQFXqyu7mrSOaFu8xbn{^u zPaDzrN!|i48t$7dOO?|Fx-(J0+GIaFSJ6?0pshjs`vX+ZR#VZL-SFS_?9pEAp`S&0 z$YnKq6bRu2eXW2K#eX~!I6lb?@?P;uds!kUN@1O4S3E|54VuROhoXW#~k-)Dq z#gsNVGJq~iTmTk7zsy?d)|XLI=6tc+N%MzR%Jp&DF(%;Ha;FVoy3CidoTp;N@5W zzaS;H@y;`Nd8WW;GBde*7wON`XfEtqm0+(=5NUo?P};YQ+R_h_a2n3G)Pu&0A1`K1 zEao3Fy}CL9y2RjG52rNxX*DGSrDi6QNmyz1{T@zz2Db z8$avSwlYT61C3-;6l*ADts(pdL^R-WP4y} z$jC0OyL)=xXCC%51m#_6>9mx;AM25b-RL40ig87Q@i&PQ!#Q5rj$Tv$XhM?$W$TNL z5sR1w3BcUh&RTuQlqO|0am)uXGd19^m!!LF7Qmuk?R5@!V;*!CKR@hLbZQt`VRqQ- zj%gS+i;C0mA;}xzOZ7shB8>>cGs7(}_@GUa-SbBfI{Ef5~L(MO4F^)F^ ze0R+s8&?)3QiLt+X2Z8t+RtX~edkx@7q;nTW`5j(nIYdxvc8p;oR{w{x|V z3~=SCKsg2#<`>2;=*tL6h=|NDB6&D0L|#FC`fQ2F_QH>8QY1*+3L+GCuGfX*f`3YQ zpmv?Lg1zh+>tLNhXwfr*<}L<7y7jGG+kG ziZ{cdmm@gEpR~HrP9q*54KR;d=)~}NW#NdF&6OcdJX&j}SJn=B!U&VyKaJ+M4R7C?yj z!3+B$AUwp86EMRyBN9XT*fxOZJ2v`pwhVmIplfgE>K$~B zI2BJEMu&{i^~{V8g==y9%B@NC)gBUST~`Lpn0B$2Dt)LZ>BA7Xd+JK_?dSXK>vz3T zfSA}F?}}@o3+$QKq&s(jM^nHwA+%#$sw_rv%?PL<-6~cdL4AZ2H)X1x}kzj zpR(L&ku^O$Eq?qs(03UzMFeMpuV{f6C+xRtkKJ$PW6YuRFU`vqv0>{+@yZIVcS zwNke;CxR>zIg5TW6tU-Q`o~}(&dKxla+X8ZrwWy4z}qL~_GDfBLKYTQPMBC= zbG)t`y`XJ<8ee_3664_%%-G}!I^!AiP$C^?K(nJ0(pkH#_#u2pw3Do~i_sNQm*XXMvopo((_> zSeoxu5%^sCYsUm&5q}G%93#u`Z5bg+d9YNETOA=dV-~G)Wx_F&KKPE0?P&9L<>bV& z=CQNKtn_M-dmG!26I)HWu^JtwE*N^$2hZ8LNm}E9X~6u`R!cD_dg`)`iHNIdCu*Z#^Ndb7Z;rlmYlUrnXXQ@jJ+_!UTQCR* z8#TM_BT@+tJ?zo9nsv*jmieCG5cg)Dte0*BQRTSN5rYJ)Cg?Cx&D+K|rxB1#UYOzp zy#=65@Se+AHBrZKD|+BU{B91;VwWC}k(5S5%zdYxl{z0f1y@OCJt&|haqk4AlC%wz z8!VVCtuEn-e%y)MBC7U=`!1!=3EJ&2NhR*mL#6jI5(!R@r@5twEfRhq60evcPV6%$EdMe zkj%u{1K>vv7|E`ZAPUyDP^&orcacimj_** zDN?Ic0Ax07H`5MW-w}#i@wQ~pG&cvBW|_}&SfHA!0(9BcxFz1R*zQI&+U;Ld08*nIa!wS6D^E#;ePhnJc5oS z=?2BgLe+XXRw&(!xldWWkBG{-DH#{}PCTLHnmhQP5!{A2$~=0lH0}0rnzTrlq*+kH zwe;>st*-;@L3z59w72N=ysy{kEu6%H2mC8A0))g!sEO@9zuXD@hJ`?yEdnq{$yOM* zP(AtB{X<3equhaY@ScAM>9;ScBqZ^$P|C6wb~2)nS5YAoB|^xZI8G@GwKj}=mMTd+ z<+09T$te=Y%|yLY^3o-G;g#WNha;=IHIv;N1H>bG{IEq0zYPzI+OM%3X_w6iMr*5`cm0q` z)(see^x|Ygi^P$l~~-WI?YM-a+_7 z4V%S#__M#eP}lHki~hW&&5IAy+%pJ4fep_9a+=9+^pZGE{)AedmzTR5j7Asmpdvq*Y^B*Ye##z5~ z(0|;7e~Lxn!)HYrYi7I!h6oC;)h>J-^?8^Y!JKU{p>ynBryS!l3GG#Ma>(_WM5_J> zZB$5*eUAF{;3nYGDSg@APBg>Jepp`vi+pDc_b;rr5h78H<8rJQO2_6}h(i|R^ zPGP~eRGxaRw;DI?PQNy_J{O(J8omPS-PTm;r`>NnE|GILIv@!YknzX}Lg4Z_W;7OQ z13{^zW+{E}7?FwFl%QSnL}X+gPcWnZ8V{m#LD!R_!m^kr`$OWrIXEfCpcYQ?ypJpE z<1JNQwZ)f9?FuP9%rf#dmlaa*dYy5$IjMWe+KiJ$_RuWky;>5L>SficI zkLU*(Qaa`5A8J3$jZ*!HwPmlH)(H7{|23>(W_OwM$dO0@j~zX9_b9yeZa$@+|6{)` zA?FhYk+rTj`ySheKh6kq@UbP%*=&FY?EYabX6Pu0v0C9M;Q=-ye8ZhzZgXjPKuAUH zBS_|a1&@qiZkZQT9jj|=VbBeW_+@ELM<`sW)F=6a@QSbRZb52(PW@X(Vp z7Yhb{wM+7yoz9Wn`VX9F`T2|NKIJ?7&)IjmMoZbsw#hxF{Y1na&8~;mAZY>)IAQ-lHRXQbli;F-*t0 zE{tmV=zaXsMMOq2c>lz*;b_{0G$1XX=~ zds8+S&I}5V(=i>1IM}GcPnE<}*U+NRLO=m@=#V66-nBj#NzOo~?4?(}-$zk~c5)ep z=dD2c+18XoQbTv_rSr($(dgS9G!EKKb6j zWr*ZmgO#uciRi+rz+!ivC#X!;lmNvdu0)Xqi{vpxF6>|)NA?A2X#wW7Aj)1jSo z>h}AD1?Yw#`5P3$qTtztaaaVhwGl_X0h{!|{?!k~m>tLG@mv~BBdS{QKxOeJobhP) zNF{jM2XCqWn!|sv-o!D2tYu%n^FY*DF6wg9@S9g zhA*3-_bB-dZZ=4?gQULRtme8ecHtI;8sKB~K|HZea3eoTb7R)PgxvGJ_S^VRwcVHK zxaq6UtZ!8fCS+o4Gi>F7lvlRIi8 z(io!FliYR|qgv3KrAn1v(6`!75!rg2@Gxv1GRjR^mr*`>Mw)|NhBY5{UH@LNtQ*ek zZ{AZ~c!$f~RV#*Okk>2i-=I3ZI(5I?OiLj(cZBpvIn@ma#0w_(AX>1onlQc7i?u%@ z+gRl{ySLin-s6!Z{Yk~+lW3qPw?pg!eyQA78|sf!J%a`@bX;Dgf5?C2vy$0*c6ZL3 z!!vH)IYmp#DS!kBZ-xbO-jd5dIB{aP9JSs_5q8h$+M4PPcEA{9`B2$8H7mz;Js3PZ zXs4YcN!Zqojs@J$v4S%ym*FR{Iw2Bj-%o8h8O^P2VS?wQ)XiEl(fpD2UMh(#gfuJH z5fyw=z=<5SWEQ7XI(yMUu&_nU?sibt{&DJgiTLqCy@^HUvA{n1syDJ-Uu(cCCi>A^ zzDde$*;jJuog!m3&r~zw#G0=Z#c?bkhW?PjCHk(Cnh_DvkjWwGtnBMLp7HbxVVve^qi~90o02IUNdFk~u_yT$# z>p?W#l)VOt>i(&dyW<@b)@%gj8AP6f4Jgxf#&-&WjIy)YRIi8 z7KWq1J7kSVF;;-v?&1>mVhgX=#vGALXBJX9>|$TApPC|wteR?hYRrhHY2|#$l%FP2 z*3GjboqYD2R#6UyV1PchTY)_HV1GC;>3w(-ZQGrCO!1T3@uBWO4df#;k30*&6FzTl z==DJG*yc#RY9 zYc;k#A!l10anJW!?WHjc7kYwig~!qp>8JXno2sq+o(?5FKKDG^_;o$$y0rY*$qI;U zFb_rTBJb?Jg_9jA;9nGs3|?LuJSNyuOz-O8=$ex%^)aD~F{+}EC z1Gluwz!3Me2Rh%&#jqx2&pL-f&Qi_zk5IP%yxBm9@fIWN;8MGb$o}(vrRK)Rdz1m; zo#;S0p+5}fBTEu` zaH1$LA2&OzH|H$Bu~0hqb|k>@lTv^G=TyPph}D0|m!uB8|4){18btb*kK*w=JMTY* zhjYijl7>)&A?8Aoq9`ZV*B*>fFR9d&mHB7rgD$R1oJ;lb{<NwO2H8L&4!l${drFJe zj>GAr?WEs}<{#48#cbN^(2VoW<|SunSd|ld3|>+2{6Ep!pxJ5Onfhen+LtrfZ&%7RAPBhhK1-ELyc_L}&Q_I8<3gZU10*zGr1E(U{!MY{0!n07wahcvWAwQa z+#Z<6zPx|_#hLg;U;|G2=U=WPj$@AePv8g1+zv)6>Fa2e?{7agr%3$!+>yPvj9i@o&TDkIbox7_G%<>~t@&E@OAG{yMH7 zZn>fz!xVp!L9n9-aIsS zwT1|GBI=j)lmbXCHEC^4*s+6AK)49f@2(-_L;1IUzvV%+w*JvsX5ZiA2qF#xz?K3) z_fH`_c!*^D6~ipy-W?FRf13#?9ekm4gpbW=%#&GlYza{};J;Xtj#sOz0YO#fH(6Cy zGxpWF@|8In4|{1EUrhtBP}P$F-Q*OT2X};XnPQ?~h2QHSo%6vC54=>WEGE%Hz5R!f;e9_$ZPB2SPAqcdVhw};Y4R&jid+qx1Q4lR6ncmp5b)i4Ti#3gR`ofYk1slOnjZ)jky=f?k1&2{cm4K ze;Tmf7n8aMkEv82Ti{YvG>_hK^g_>I1YiRsf;GYocv0Qt#%j{MK^C|o!wl;*>g;d0 zrYg_Ot{g#;Z8`rXwt#83gcW-}AFm7aN@AKySkWi2XC}gQ53mW@MRP-pn`sXp! z50)$p6T$ieK3~Dptz^Z;LhM4M;Pfz^HOpwARzNd=p1^Ib{;gPRKwJ4#)^dg)?H!~O z^oId>KH3+?e8Z-2*z%4Eq=h@@v==wL*lg&QC2l_cVpvzoStf&A>;yQcdjsB;zPTx+ z1;v{r-P6NCiptstNh1sI`u&shbu#yOJ@i(z`VM2@OQz>#YCK{mNfMU{4f(_6Yj2@% z3we1=b2Vm>W#;K7r=Dq$x+O@(&jgDQKLbIs7!qhO{@{P>T zc2jlrcwX7#Ln>jHJVny}n8s=nc^_$?;yn1?DDX7My8ZFA@aHQx{qMy@ zY~WMT&gz!lTi|fAL^)VaA1J>!K2u`jx9K zQD!yH#;QGHzlj^s2D2s0xsw&n#;w~>*ysHHly=~WZ>U$j^$LTL=dBWz3p8$3wEoq1 z%8}L$uER#EGu_d-%ad9gfqu|}`vA_!sF>{6@@+|sLtR)~Gt~E?&jF;czmSF&+_!R} z!WMM6;MUJ2!Ie;Yn=xDpC;CnOS)o9UfrX{yv?u!jdV4Vl%9qNW*Z2m_e;#v!^jBjd zInCtQ83A!8{mvDArYryIK}Nb{c}d+%s`WAO)%i1WRP7bDnhY4D`#iMp3V{1cWT$PJHI z$6}_p5_>^@TO;s4OoW#5-fp_TJ2!awg5*u&eTC_#L=F>HrB4vDD1X3_8upcKnBZmS zyx{Too?j>%Aeraf8A0;#>h4k3N4;R6EN8##T~52g`|7M&#ol#xvqwN z;{}}W`Pkp*eA&vmTSetvvtP3CopD7O-sy?1s@H8p^^pxs|5+p{jDm<~amMAVRIZ3$ zuOoJ-&*jOaf3G8GW`_1S3v{buOmSI2#HFs)u30`wu5UBq|7K#JLNUv4Ni!qjmi^8* zgi6>)>&W-(%Lt46ziwE!kpetTd^x;7TZ9y9uVB!|yigd_EoO;M12^X?zu9~@d4i>b zos66um;Nnq>_=|%QwDZJ#Vz*t!1`mBHwJzZE&4FAHl+|Kt}MFRXD0nNQ1yYBtIQrr zF+4av@Klhbgnd8eIKKR{H13YeW`m>)w9>AAnE_;)ji;LW9MB;aPZU zQZr;xrCcV>iXS%c=~{0tmu92lOIV%Nq$}{d55yY#_-FAHo6-&DJB>~CN)2K1yE)1q z6QWa6eKFzv=$==(>U(Bh(nlYz!q?^Z>H(`jvF{p%1SemPHZYP&*^pOj*h_D7vC$zF z6NRdG#NG7Mz9O4OpR~qiCHJy)mNE__qoetxGs<1IMjkq;J}f{;KD9Aj3?uEw;~}{3 zC_VVTBZT^)KLYO6`#f-RksM4Nwrqpy*THvyx*u6`zHe?I9y!Z1I}UBbsbOcW_2;md zmH8vfBF*-KkKPsKwsVP>zL7AY{UHZs9>K+pJFHHV)KL-CG~8`E4))fED%(bl)7~h& zx2HX+r*qdu%yS}fgB9#nfTqN1XP zf7+0gIA^$C+0Hcj9Zlxv)|~UAgUW71#w61Bz4cz%p+f*|w8wW9jXM2=L6XNUJgWA# z%UZ=VE#pK%0U-E)N47z&uaAx<#W#XBM=wvs{$znVZWLBGvl-tWl?d~i?QO%e1$IBa zFkhH19pAld&f6?4-PpB4%iA|h7kW*|h)fe+3 zg?$ZCOH&JWyEccY+XpCXY2qA-=h-${SRZuMNE9v$z*7yeh6MOe+i7Q67-5;#qd4tM zie0|wk<3!zkHZ<4wa+V6nkOeZAPS$Xe^pQkR}WPrYp!TjQW(FTKKDdCX5;q%b~M7h zbgg*-epBt0NhSWV$HUdVVPA*P4`o-dY=xb+xSk4mmwDzz+&d8*3TZJvQ}HtCMPhB% zG+X>;pWdwRBi3Z?u&)_l{b$!E9{99 zeh)+FCx8R|$d;AWz7c+#^ig~Ku6I8s8@DXZl4jdl$)$SrO2GK3(?e{sXVl4Aa&?PMNj~)OQ)}BQYE42(Q(Xju~Z4p#EL^} zw4fLCz_bq%x0a*s8hP*OTT!W({zxlWsN4Dppsbi}5NaOUp5|>W^&QfpmkFGDd|YVL zT=qV*Ae=_NlC1~h_0K-~GD|$3H#R2U4=joT}pge!fNE7RZFBl2S0yO)_-?z^rHos#Bk(Am4 zfCh8|1@FACb{=?ckrGNC6J9Q5e}z|%=ib=gBJ|DHx@EecFH{8dzkF0jW zJ?xtigT$M#FpBB>QpP%uZy z)!InCp+id&#HrQkfuyKp>~d`oZw8CrOMD!bApsJft8t2~JE2|2dE8mg9C7J31TBSC zyn@|g>}|#tRxgkKu?Mn&_8o%;@+tPvuc9V=jJ$_+yljTUWM_Mozvs%u4#6u;ZnllO z_itw@C%4x)*e}KF1Q zS)&M8csiG}Nka^xG|^ni2=q43FxK@cUjuhslxUV0jBpwm_>7IE@{HZn`ul+_&TR4q zYRjCt{i8cY&tIIUPurz8-(6OZwY<0FV1Lsv=_B!#E1NcZY_<9AeV7TlWz_0jsrrPM zm&p=XvE_%8Ub5Vg>Iqfj$(D^Wi%@V#b=^)_K65#^l{N|H8=B_$T>HCjWmx0WKl_{l z{Lz8qXFRE*E_H^wh1tXRmo|;XUZ!Rax~10!^NW+x@I;lT{{aX8{Nat>V%(`z1fgbh zu}tVUoMS47aaSe9rO>@_`BFo>xsdy)_%=p~NgA~JYIA@d)N9MrR}~_skQgdH=jGFW zlkxaDgSQfvJD#h-H;^URUSq`nX8!&<;=aKlT3CWt*NtQ7X$~Z~BHZze{I2RV05=|W zjiX?Brr2PqX5_}|lPaMsyK)PivNwev0Cz{!us3Kxb|YBmwi5R9yvFsmyP>c9A*vs< z5~1rwa1$wND+AVxOLp6^AB~|N0afC#RTp>9aFE#6=G7v&d<=Nfa&r>vLRZ|*e~xn| zZRnz$^`E4K$6rkUNlKWup-pX(#U{_?F#@?-=3f{&AlnRer|aHES-ZU3sV+}*lg#VD zyEIAWOz5NWl3g!3;=iG9>0x7BiO(W|nWN&;-Aki~-C(L$%J)ibOqKMyGAC6k9Gsv9 zp;z>O`$i;(OC$6)_{pUA(3aiSENg309kuCuyDCf`fSQFlZn`zJL1&dV*HbTO_5NEO zTA)agRoKs%wkp~3dKIJq(wh!C!w$_k zik>X%gl#f)(rQvVM8;UNv1k$xXtpZA_BDw#k3VtC1)U;9Y`hINc*oozMi=`Wr0>_QsaoUV1Xg3N}b6;Si+B!aZI1hIQ3 z3O2OsAq4j6dLq?M51Hhu?JPu~)7%_)U`8ao(tQlU^EB07Lpe*Wd`~QB8)rTo0XO{7 zeR)EMdtz0%^~&9b3L^eM`vt5f&P2AFUW~*OKsGH+`rz21)!i$X!FZxf~Z>tzlesN|B6!~lRvF7V#QYdX@!k?$UVD$Y&d+dvtpIf zq1k@c?LJhXVlX1+W(tZLAi3&Mrw$g$t~QqiKBkdVQQbO&o|p49%i;POoomGI2_S`KUe)p;DQapPhU6F8draf_d}zSD6W)Z4Qok#oCca7(>~t;oBqh%UaFmZr|R?_&!6bP;QW4#XX%LXVps-=08A(fA|h3V~i(3nFJL))S|2R`R>@?{-nY2+)VR0-_x zkz7W5vE_m!XIna7YhH5xXK^D5qvgFy{wV4nkh z;*IWnhsVCWwo^(0j~dUTRiHzi>hZH}z9&dj?~!g7O%EA+IoVhljmh(jXLVC+ad3Ct z6JB8dJU@7d<5#DrdE{gNbwO=v8gT54U9k$>zIY|kB?2Hz*~|0wEc1(fC<__~*ZJ;) z0;N8bP_Oc1CyJu!sOw_eu6(ODWHr(Mg?;-K=dZ{Cd9HvzvhIgbt{>YI@$2cG3*V{I zQ*ATgy;(&bcgw;8mb0hwHhWpMYi=4xZ&GviR%POMC)3WeMq_F5x`x(Y?K-+ryU%tZ z?dMrT1}*V`{eAWZOfy7H(pA=eD7nrG-=(L9*^VNRQ>(8WZlDs$^`(rF><`ls=JiX< zykD${SYcatj4yya0%y6C1~0e9jCM$zkB=r*TZy<5)cVwT5R3U97H+$VP-FG~NNLdN z48mbxA$b|pHzV+XfH@{K3spy&yn`mfy|7KvfPsW(^^3X2h5!wS8FsU{Fcpf7{#eL& zI0^1* z#ub3RLiLdCEzuHI{|$=#Yrq;R6k=A{*{lWJU;bV#NmK*RW`Hzhmxj;8yb|H5K)@46 zCk5mf?B-ZP=a0TgzY9RTL2)uu$Ca_=?x|gDw21xW9b;2()XNuM1LZsx{C}*yWmsI> zvNcT5CRh`KyA#~qH9-?dkl^lajk~)$1QH+#?(Xgcg1b93+Bk3L+tQ}h`Sk4U>TUSZ1a(^m`*_G|*m1Sq z)f0hQ5#2>EaBsmf^c5@|Hqe;mhdPAe1AL?O9VepJviy|`VoYg_i!#Op9h94q!Au7Y zPQX~ZF{5OCU$}?~KD399U<>g!cX3lDJyuwht-NLKm%vdsu@-~#?GdIT&H@3%cRD_{ zfqSmJ)5p=O(%`o9<=X4r8CEldX9O+{?sS^AXSa<&l3P~P2jT#s^11zQ;U4?TNCK&2 zyto0FKFpf=It9)n!aJmkC}ZeHhA#TK%DOv`bzUL(n|{1R1=$POoZC-uu2*&@#>;)? zq_qJ;2u`P~%=FDVg)@Tm?DSFs<^5#Y2@=wRN9a2+%qSNmM|L|UG-aX($%%<(6Czxz zY+XosM=ivSh2IHX4yV%SSWyqjuQ(*%q4x8kjfo0k1-EdD_H^Bw6m*^$zk2!sH?(kK zWv$qy2YXx0b3Ub1E=q+sGO@mwMr)GPo`GM{qJIZzK%4?Euhg(;bzd&la99Jy;i|&Q zXEznxF}A0JB-XHFdNgOsgn|#{)fr7!0%mxdbI1!iJxIj)Z?i9hxoP)ES-ZUa5eO@M zIVV;>uJaL;R?UO)$HJhAv{V%!(u#)SN3X@?0H)@qEayJquKQOlJRk6StI*faY=}2~ zd81mxP6&th+aZ9~J65w0(`p{g(lI}@fdIWL(1zvy9%B`&o4cM8acuqkaI7e*PpV`4 z-P$oEElo6)I8c3C4)TTZ>0ftMn(AUxgfc4@rtL-*ef~uX4&iUZouj^A$S(7%^P4pU`P( zKVh5_O~?7h*yH#z_A3~$!4=)Z9YeEA5!vJ|m!zb+_$=n*J!?0HX6UPBRD%SL<`7eY zKxscQyQ)r1VajG-u6y2Eoxq_-_1wuqZGE*>YveBbP%W_H*ypVdi|JAeoU#DZ%o;=f zBO<8Ez}{*F0vi$R{sw*OO=b0d3~4tItr#!b{Gb(H{Jy+r-F6kBqGQsE?)hPe>31}d z09MrUDf;*AWWnSZ)r8B3tks`&0*XEii%hNzKYIu;zqzhJqsjjO26KkOjR|v1wAAZs zVTh!IiWPFy4$Q@NE&$X*S_PD)Av~sE{U&HCIA+%b99B2TbPo=cCebK$VUxXvHV%G!?Gb$$5|c< z6<7F%2dj&SAM4N#c+!9ps**vx;g+i-K=J&jV1tfTLY-@y_8J$0PRDk#{f|YtB+XYk zy}}@F&92M3hq>b_P2C5ox7pmJR%~xW)VAnLpE(>lI$XLw6yxd4o}z}3MRr77%tH6Q ziDF7^U*Q03+xU?ug>oqQk2=1uQ8CN}SHMfYZ#nRWcSBwdJAQ(P#ISVvCe@{`;VlM?M~Vq)d3YA`qOB;XlJ$9emKm%re@y!R zt;f|9sa=*Y8+#e&vvm3k>InQX@B1A86S+ci_}I15Cz1Q|b}lTWszp7QV5qzSqbth! zsN+rc-^zm%6u?k2MRGx=a6jU<)gHUhl^h! zD&3xQY1EvjON;{R(DTE{s3RLs7~5h!jh}1C5wkUlRe3yDTccS+OrQKLvS01R&-TQlA^obRIiiV`w`zT1zeNn?VaP)qhU#_P@sMJU1Dfb6ui%kTNsv3` zx;r`i^KPL~AmH48rZ1<>yi`TpVb}>EH0sisz+KquBCRP@Qm$jlkPafL$|>pNS(osb ze_Br06a4v(r1>0k9(E0`896po)0RoR>0xX?(LnG_rXM4ar=2WHMX;+P z^6;sjv@@}goVk>1(Q*3F_nB}Z_Ek@If`;bsEZBJTAe}?QOyJ7{HY_{p8n?yNw_}`S zudNMRpZeh1GZR3@Tiu?kLgx_}%Yzx4vqo>2XhU#kdYj{#Mw7IVU&)#Vy#DkgR0PAK z(u%em8>^sgiz;Cxw;bt@t+4E zaiJbCifRRsPq%UT%wR;wcY03sZO2XH=s-ZD9SZ~j)uA6wMp$=!LCdKPupRej4D@E| zUmIXgOaon!yJ3wF(^AT~#eMr+#{&mWc3tZZ9{o(4%X(6T&YisVjM!mxE--|x19{Ds_cIKoB`R(Kv(%BgH7y=jEY}cA_ zEbUN9xyHLmWkPWoEuvysr~mS@wd|Gv zYjC~!Ff?LmtsW_f+ix6b$EmmG$FYXzp4h14)vqn`nSO_nX3Md@dD10E&PYfAGV1W4fCbX((ewM3zI8oT01GgHe3a~cUm}Z1TG0M| z)`8pbD?kSWK^}K4pzztrj_AmqH6n{?H6{!1O*`%XXaRsQ)u!rKRlfw&LIi9hlWOZ$ zC?;D8Xv(|Ie80(cqu#@9T0eVy3IxSn$J|_=}pcEtH#R8mLmSc^!WRp;rod#Z1i{Clk9m< z9?5Gr_#{`;taoq_f#Z=fryo+5)0&Ik=$AN_@Os=qI}9yw$^dKFj6AmOQ%2v}h;r0a zbIh+A_880c=J?a9?XXovYNcFjX26`Av}Kl5yuKX==1%sGDjae(D02Ar^kvzcez5#W zA7UC1AMeYz8E8y|XUoVs5>-dNXyN17F<(+lwIim~1xXlEv4GRd+FUx$@y1Z9rx+^* z9tg|a^k~Ih1x3sf58v72`Lhc;ooaQiPfde`U?g5Mvi4ny^G4}?{3MuVOtA^*jM1}K z4qXkkw=qQ{&k~Uy^=xkGYiFL{pKHsSLETy7zbM??swkcwIKf#W$|D>MU%$&X^G7 zIPM0mu8u&2ZZTSEEvMcr*_bu}w<`2#Ox~|H5wlHKfGdqR1oH-gMf(x5xBVfg zA+5wDM=fcVO2=_4SJttOLG=Awd=Z0@MIQZo-Mj<%YtJ)a%fLHoKHr$VDRhsnRT|am z-h4R+LT&@55!e?a%J&I*TTW)iZyO^bbjqR>%@{v?mL%~pk49Z z8he4WZLC>}7nAV?fSlNX&>JXhr>5k}`#i(DJ9fsXJ(d=pVx6$8l|@|(b&W^%yLw2_ zL-CTmE8%$nJ}e9>`73bO3v%&T9@JdXWnzIrzz&$EZkvyqkgXmwhZCAR79``A2<W12WWgXsM0S0V zCdkVeoPL!1d^jw(YKJ574~;>MgAK;(s6By}I;ZS9y&3JyyIjbYc?A1)-A;*#*dNZP z;!b{*{i0G>)=3TRl|l- z@rtP1OJ-r5W!}XsZJ&SDU7RQgynBIOy<kp~>i4`HT33cwZa z{4ClnvuLn*k_zpSvxkX;oGVFYbv2D}!YG$1Aa_TkV>V`ag31xV8=4fE3b`Vn)GmxJ z^@~S$U9vE!Hq>>uN@v~-O)2!^J`$=L+N;?=iFo&vmQT=45GVa$nsD z;C6DKr3qg$oEH~s*9A8qX6w{|M)i{W+Y6;rg~L4U(O6+%G1N>2vhjba{3@Ng(U231 zCkukX+8|3tA!mtQEXLRfT=6fd<=8sR@}Fs(YsQ%3kF&H7kjW&a>qsC?63d_DQBl#S zQcrXh(+t;dHf|ys`4;v!V<5?GB~44W`?SvcQ>dYZBS@Nw0p*%wmi;J<*QgRAl_-F_ zCU146_EbX}--9Yko>JOrrO?pGpFKDkRHV=4rXXOi#fwHnlV@ugmv>wl!6>IDKs!uL zLbg#%W_W+DRzq%^S&L3LzOiTL7Z>(B4WN@^kBapmFZZL3JCrBFPg&f(0yti+$Gy_o z8jT_XWmO)9t$u2kJdUj?cl?4DE{47^Uhw2bi0FIPw_Umfg)60O6Z^F$1nA!*-y9qW z_jOzmYYvQel%+q0je%_333=g^P_J6gZ+SZm%BIU#oyTZNxk)i85XJ}u#;qrR+Nx`= z61TobqHhlJt=Oyods4d^N(Ni(1;^Jls4 zHm0$^{V4Y!o-|C65jP-yy%)&qW|AUqN3}wW4B}v}(v%Mk3tc#;9lER7<{ZM_O-5c7i5jIkSTr`!?(+%L{ z26=Oq@X;U~1sf_X*TiZNX)%~6P=?JL$L>Tz@_NH z%~9a7mb?Qd8*PPi^%gCkl~=qYpOzT+C1+YUGQ1T=&0CNoC?pWMA`e{%lJ8hy^i{8j ze6z!VinQ*cDZyx0C^lThrdA=sJx$#!aTrj?IM_7|_Y)aqQRDQyy1?;yjk@G0K`F6# z8PPav4-f*~xe2WV6E(a?LTN_knZ{bVFw{Q$kpay!YuYbY5t)i~S46w6nB1bn+m}~( zvVsGjp;@Yu)#V)FR52YsAdWeBv zCxPpFXT>p*b+qEUt2cUR;te${CZ2jL1+xYwgB}|?aU1Sqcc$tXitHJcR3<~KQas6ZO{?nD1>+Wkkd)o7w zN7Mja7Z}t%#C_L_Noacz#=eA#su+Ruc8^8d(MHWjUPSWQIh-#osy(*dR(+S{xe>9` zlbJhiBya&gY%#dtoUhS)gn~81Q~iS3k6I$Z{Gir_py;?5A(L|_9o>s1S7A>Lq$rHk z+qrRG?h?m{8k+WjK#EBZH1gHVEmV@}nY6_Rl9js0`EA91p1R>4gnYZoI*1NT zsCeOX_$6@fVs_V)d2L=T8=3x5I!ivW=qx4Cf6iejT|&TU;ak9qB!Q+tKFLyeZ1XO~ z?l(c1LHUxUa>g@Zcp9!wbyQoSiD0=rWg}8TNBMZCMmwX@!xwQ6k$`lHcd^1C!+i!z z`G=qxj%KF-{axWZXTL5h0)9B1jz`oynrJBu zw5G|+hHb0KbjG`J)fjujd5?)4o(|j><7pdgzVvQ>rpxIQEzNf9+UnlrwjhSHq_%c~4udWp>n+N)hHQK?S+nav zz{K=&z6XIJ7^z+GL;ROONk17v!QL(+l7ejHg!ex=_nba*PMFl@%b<=OHdfxRVzmbx zkf3S87R`K0KGkt{=R$1jMiJ?=ZPTGs;Yvj64CQPo%|)JbSgT8C8fJtI9eSGV%%?HJ zG7w+!Hnn`UuAa$TwV#B!Ww7Lb&}{0@-c^u^^uLo9|GLxpHZ}6uj45nO;Q2 zLrOq8r}%5>6ngAe{Q4JDrN0}Y;xF+Y{inPREwK_sdM1M4eLeGa?v5D$37{*7^ zFwFEzR*Ya1#FLCNj@9-b;88y#*q2>%*soV%$zV=oyCK5gb=E^?WCeI&$Anj8qW35g zI-U z)vt~DTLS3s30FLLC{TBq&ZkGOdMj@@;E;K6XjJ+)p_;$aP`)IHCaa5{KMoP3Td;&_ zuoS-7n3xNU;NTPNBoEYd!KFp0P5<|o^M!|{{1-=tao9GBY+rfyG9nb|j|gFZ^4Me( zqQhX5FxFQcHZRZrK;QY7NHwirZ0OqC>#nXYYJS0Z{kNGHzvaLFcKcAYI6EvL2@Oh3 zv8QY4;?P2sF@z@fgm#Q8)i?bAGtgXJpwfNxg$)!V?hgu%SgX%`dkq!!&9{EsKkF%7 zG3xO1*(LY3zjFMOO;#zWp&)3pu7vlLtxP^#pYbIG(366wzZo0p_mh+b2EvC$MJ1up zUJYKPVppzTqlYO(j`ipLCzcG(FP01#U|S>(3L*-k=I8%NK|vvJ6!#VsVp@-fKB_1t zC@j1T*Z}rnL4}x&`}Kr`BeX131O7C2%)+p$yTQ1;-L}oZH<@ykA2kQDwwbfqgg> z^$(GTe=i>Xt{Lu!k^v)W-+b7acIEi>BQZPMB6q*L-$KdK7}H;%WdB1?`Cp%F4^n7% z4_mON(c8KIfz8$Yp3wX!w}Nsu$P%C=lQAkVaQ2}lc#`ka}wIC)c^c%%Lh;9yMawYVp9us1q-YfY>VNm$jHbq zpknW{>j>|Nq*+eiYTG3LCPw|22nsz>F121uJGsA%q-3g^k_MwPTeR|}-&a7~AOcpt#-55Yf6i+sr)tb@ zi7j%H7i>f%tH4ekV);a#1e8!{bTv=4 z=X{C$N|S66u+D8ID=mQ>Muf&jwDzubY5zqhVbb8P3EO|5I}}U8jQ(J@=9S;JyohCX zQv`J8i=tu#8G~MN%Rh&=hnBHqcz=`WfR5H*e^=M3-pX;U`+Q&sHnIBu7&4*(gM0g{nwWo2{4=nW<9E?7$2A`bvbPx0>)MAjQ` z?_mss>aq}d)BN=VzaL}3YKWsg85YWvT(jxH>dmyuwhyGm{^?6AWAcJleeW-K*ymb? zsHbt5RJAOA7E>cU$B|FE@?468(rtpJ`RKibo#G1a`uMB3DGx8wF?t#AkJakQkM~8J zqgevlAJQMU7}NL+Yx#DJF7Y1f4n`x<|kXNwwkMY6ZCv5-}9z&bSdr8 z`>MD>xzL8`)yL}4l}2COYyE}$?Tq^Zv>qt^$Z)ezO76=R(+D*^J^f3m^jf4rz4@Q` z6Xox`Ua1!E@=YBT@vWGNlrZ;%iR|WVFGU^31>7$7za1zd0jrUb zN)EMN92=IsFa9?9|1}+%jbL$;@X+m>WR_U5J5_WhOt!j=93%U|Qg7l@H~FV)YHj@- z7uKjce#E$J`73^IZo3qj*MsS}&?(Y*6n$DQF;I0Jn{Lte2& z2>MV!mcX041zX1x$mRCGx+@sV$rVjAc~K(|K0@^?b1^$7Uv#xydxneNHj7CBl!pUJ2+ZR@q|_<~r{U zt_`8=N=^C9;uS&$Y!n%}Z};z%x!d|TF1w6|ClX<=aubjpE)wNF)d(3*9ZROnfbxc% zo6WLchlK+Y;1z1m?K0og9sx)BunhaMGm>h3o?2wNi*Ox2T=p~S=4Q@(7h|o<9vg+Y zI$5zm8@5pT+*d4mWuMo36mFnJ)6!A-7|8d?VjJDllFBYO`?(3;GH|52F=^jsm?Oh) z^Q@3;n0@GN@iM;Xf0+jHs>~66YdzB%b(j9-_3CxG>zK|!j&7l-QTa$Jmj(sJ#1&^y zs?U+L!qzl*346VCYn6z@b@`$4DQ0l1U!#n4@G<6Yj9-;atESx=@mzyp3T9`~3}AFP zzF4~dmxVn(Ofgh_bk-pK-F{?#KrB2ytI**r4i(`nPHovF=7iFTI_o70h>H~ilV(kv zcR=u87zACn&i`T%?BA^uAbgRol)0W5q1`&lFQgWvkbQv_JygEUm+#a;*Kj`pU8l~o z8&18_C|{yiPEgIUjM5y^;I885+L*&?aniMSjvRS6PcE{Hx)>47A}u zje?^sCcp97rg^3J65>F%P}9#MHO=X=6}lRWnZ(ie3jSG7#dak#`=w7fhC$-Oh7(uy{{K-Au;@ zXRv6}A{U`58tF%5baj?bu|I$EXZgvCY0|K>D=!n>2Rqhy-VUg1l%%z&s})U0u^XSc z(r7jgXh&^+D2Nw%A8UY@*cN@#1@D6FKs>d4nw(eD zH*cXWj_K-Ga>wiv1J_@fr2G?;@C&-=#)v=2!gU$o7bbKK!v zRgNp5tYt!X-Wl|oq9!I1@6qpel!r};Fq0Rm*nEN>BWT4)oc!3LweTX>MQ|>^C0WKJ z_96NaLD-taiiu~%Apx^UXrW59#M5pRa=1(h?Y6>x#i{UfdGowNH|=WaQ!>AHqp-0F zU+}Du{d(OQBtAc$#YgyAObCe$Wdsfr_YUUVCA2jWoAos~AZDv(#y!Jvw@8S0HCxD? zP%c~)bPW}WqRmESlpm|T+`Eg{2s%7`lh8UT!d`lCoMtK@S8EmLu>bQ_!WPO9Hn{n+ z7SY1T-n9}cW-fj;ty|8ahIp1L zxVK08Tw&PC3GvlxaFBh-I7qYfsk_ox7z=KLh{bO_C?T2ZWt`bHA&or1LDF(A_7DyW z3zk<%+%EK?P=&`icioOjxX=un@0P+XEjJb+OadNPG3*eZ^Ud8Kh7)g3JnBXC%O_`? z1CH!gMiOXa_6SqQo+)C7@QUq8^F*K)UEAblzmE{7y;M)w`((I?YqaT8FM6;;_iUzM!7q-qt@eq1bk z^-FeKv|&3$oIfoP0Nv0{H@z zya~6H2HqPG!u__-<0RUNLt=sT_4v)64ZW&uWe@W%mh&pY+Vdm-x2i4fAN+vmWotHL z7p$#aEZ^xgWP-YQbV#YpXaniWg9~pIc&6C=2?Om3O+SBD5YTsq`+-RWeIjyDu3{_a zqO4E%kw#&v?}+Z|RCp%oTh+OG=V{aa)FjYZg%XbngwKdcecmI?YYw~V*4YI7EYXx3 z{5o?}GoxdeOv-CF4j4v2c^O-`j87GnvG__N1T`Uv-pRuf<8~?V6e}MTwthCd5d%PE z*YV7)4-}?5Q zl~(b!CB{B#Vo002ApR#E!fflRA$^7btJ}itW3xb~3&-y9j;`vXr&O_TNDIPxw-luU zK5J!ddQETur`%quJq>I(HQ&{lz6?Q%LbgMS>zz#3to*_9V24p&-hQ4oTth`Xg z6ln@SUuwGKe3)ElsCHhj`e2!|gn_6-uU1NDC`@SD{Q*_yg7iB5aKbOO!?x8r&;ZcK z`zV6*_zI)8Wkn$kp$vAy;U6Z~19P>qld#6?}!0XD={@0%MRlZHAT-J^2j zov|l{dEuYEhos9N5q{oWCtmOYLAA9Cnh^L`FIOOD=yP2r8Ff`!xWQ*KgGg9)7vt z{VNUj6gkuw6&3JqY_SPxjZ^%vM-ds^i)12D*u~9OW_NE!*>=hMW8kZ;ABEm4%nldw z25A@9Xjr5adJUKa>AWXL8)O=e*EN$F*NXZ_E@W>y zLX~z4ylBTejWO9cnXMS|R5~tcQY;9Md?)|)_FSp$`KUeiNNk9)9P|@k0-w(Vzn)fi z47GTq7XO4c9y6alHQ-pC$dlrzLK(Z7J+v>*{G3Ne(^BnKJn{3n^l&%-#m*V_pe2q6 zH^&ci?-BIA8?h@8`O*_N@ym8TXY{vZpI6j8ioImW1n;K;&Nq#GYv+?qh7Iocl8%nEMiPwbg1SoHv@qGb z6DW-4=l6-V5+DM;(SWl_5VcJ`+ z-;LDEteU?>^>jlV{orr}I&LWS9}qa$g~6s-Tl_Y|RVv~$gLdVp^3XVO>#g?0U#bM- z#d*sCIkrd7@p#lYH$uB9=+Aee) zdqUStqdJLA*v(7eMB}VZmC4u{k)`A-_g;2Eh+~o@-r2tyBnt23ep4r>OcwRzn}sZi z6^d84G)?x#lE+4V*ucA9TXUq>8Tn%tzlgpSb1A*!hxoH=zrLf>-Fs&XHVXRSjUOPw z{$`=C3E1c!rh%B*M-HCZ{_2~6&|_eQXV7E;+%!VJLF;4izBFox=tkt`{&Nfr9)IrI zhNCM)=`ST8?xZP$1!&=eVuKlX=(0933_#x|@;MXcsMY5lN;HlJ3HodSsb;3$*gMDX zn($_8$nNfk_c(0iu$#0Ua#rLCOvAszMGWW!vxdF_Beu?s8Ce=^1z7V_eWOvf=8TWH z=It|65?_Nh9Hg2nwp;T*UI8w|5DD);9-W}j;MDj%gA0w$ekO-F`$L_@y*R|q{VhkY zHK=w6QD}_2(yWcCC~cdYT}sl7=KBGGP=2QF?qht~BDOehrAk7C=sXr6Ms)v8DbwiX z=$kk8=+VRvO0+ezw&Bea5H!;tMs1W$xN9YCWR#?SVz7k3c?=R>st^of_6XKc{m-&V zgEnlQh2iI^uB*o;kh!UCth7bmLco}#IgWcXvFvP&#({;<&YMO6Tqb!FgU87itVpc&cf$Zjh1 z*9V52sOdv&O4hf(c^QIWuHU2S9T{SM>}gDSCs*sv zv`oeRPbNkd$i80=4YUC$6|_jr=E9}E>c*^{AK%(#N#Mt5$1u1tfF_Ag>>D7IaI|BR z+egSK@_1uf68U)TW!O_KDYVgItR!*YV>#;~5w(QGz6u4pM_v=THv`lk)!*Sl9k~9% zzONm3u*+5ZMK+Ef*BJgKCQIy!v+aeMO?-}vF6d*ANly-+4VW^st zF^%NPI>Z9eJx1{@Klr{_oJu~M{6KLk#3?AwZafmViESX%i4~EbVGBwmnMMpP;786i zO_T-BmJ?x3%tbB)&|i3--hirx9SL>Y*uGH(+QBzR#d(GGon~nSx7I2s4Xx!zh`P`g zv)e89zkXZ`$r*M)g$K=3q+;V@bqT-qso9?zc15*n&%q^`5Q0k4gT-aYJ`*RL zchKa$%@wB+AZ{I0N5p^(;g^q!6|gtQ{BV)i0%SxJSQtCJvS%a@s5j^dhdsToLqJiO zc4+=cNiHtYHB2U7?Oq^2nLqFN>hjtmqbloKXZ!nvi5_x7+qyXds&D&=M}bb!W2df< zUlbawDlxE|d^4|%j12tQXDpR%;SePNXY^5Jqn9wU@QR>@(7zv5*Y>(WMoKE}2+pgE z3l?+n80i5vZTa$G=A6HMLxY9JpD8zOqrOg0PjBCKC4<7{Q+INRod9TDli%&wit=K^ z9C#?u`-8TeDH;JQqmzZ?VS>~ZRkTAIs_*)#Bl<{S>foN(u*(2)aKa%dI4qxfQ>BA3 z`&f^@ucrtPvi;nWYKAwrR-Vo0a+vA~^?gk(D}neXo8}{iSmVcAEwY8Gf0ReG!k|Q- z=rFdD5NP!~@d#N>-UReszW%=Y@eAkoOa+H;S4&0Sp^|+;Zvw-EAM>;??aWhl$MwQ{ zzfyLoaJ)!#_8~0na8BJs^T#a~iG8z+FBO5qmOTrkXM4AKs3G#4z3m7-0a(a7Ksepe zgm?NF`VJ~?<&B>Q3xvI-lz_Nh&IiW^I_N5;n$cKtB*P(VM5l7vci-6(`Yc2DtUvh^ z!mCG)8)tqZMc^D(tsKCO*fwA8Jv6q9cx>K%9$}aNuC5^^WTc9yj608Es|in2$nf=w z1egOfds?_{kr_Ka9>B&R=C_&_^Xx^zAtH??VeU5z%7=%S0+{Og4WMSFWgZC#8r8P^ ztka^Gr%2ulbO*O1;>r_*zIg;j5v-Uq>Vk<$pnLLn&0Ulj__-BIWi#cntor0M!cOyv z*B(EgZLRtdz0Fh`ZfL(Gwr(oB6f)_2C(j@5OUl;#x$fImF!Glw0g9hvFfZXHUWOzg zF?tm52#zA(!qLzKhVXe#v0;+Ya2)%A@Q3^xTv?vAFcJ`H%Zja z+{D^}QNnFrypy8boez6ypBeM5uewfjnr&XQ)l(zq0pUJ&E^2_D+{7>=y~f5V=X}}A zeb9(l)a`b_beO-qdldn8W^20SDf~!N9dYbvy(i*EFokXdHG}W56J-e=>%c$+09Yt% zU0%@mxdXvr%E-8r7-~AM2@qijQCq!;KXeZ8A3eZ0s+&7h-x=n1 zB`R1E4mq+~>303+T1u4S8AGThhJCRg@8|$K7QXgi9dLwQ;ll^qG`Idr0k^X+7wWMx z!CZYV-C^8YR~2&4o&V4efug~Kr1^Nvy8$m)N^_w z!)Wb0Jrkd$XE}(*Y0<~)lsk`mYYEdj>Pxn!amq&xv+nmbBUL5+ znafokW!XbuU)ncP#E-Wq+5EhpT95F6j#rpQVG@g@xd~c%EDAZ?lSa{Pw;x$ttJ<$c zh$ZQ@zQn549{Ue?b|j#t_Pw#boqf63>myR= zOc1NfJqOnFrQhc8-3hc5T*)aOab!Pqle%6ofQLTgC|;#k-Jxt^Ef@lsq!&=j94^!d zO|q6FR%2p3a@y)=U9OvK?vGd+oT1UtVZq_AQab-C8EOr{KTZ|~kcUz#%K>uO?K=@f zmeAy;?nulm=bc~KjvIM=rMaITk%^L`s|HXV=DTRUGvL+hmCA{mRguZ}^plV0uuNrK zaFZ6Ns4No2VW}`xlr>tW_((6QNV;Sx$ze-N#>uEvxhb0&%DDa0K8z2GS|3w`+R-Hi zX2-he=SLTEgKIEEn6)pccz+WX4j=fI!*uk}alLVKzx)7$R5%az!!`Ac;elLHPI(d^ z2>c222U#%3YqA!s_5`d9ZtLVbE{~_`@cz#iu6=z1nLKe;?jnkXc-hM_K}WQ|mu8`O zE>VD_RMxmrh4prg?TJoX#>?rO4yXc#wa#2s(;~{fP3-XC5OuIF8~3YZA9YC_?2?H7 zt15ekWBF#uP1%=aloVJ!L&a`n;Yc`X6kH3cx_jh}LObq}BC)jwoQPNCuP|Kr%L&=7q1ndP!yE} z=7T>L#9zF|f}aS)%f1PBW^TI>7IjG(jph1Z(9J(!I7XhZ=HjSj?>ILC6iXTAT*A_K*-&;NTAve4KEjMRaKUl~8B_?gK0u_$NM|6>aLuRr@mD!}CN>L`}kF(e{w^as!c zF9cL*tX@qr@6i6y!lJ#uw3P4ghd-k=;*ksoY_$HCQT+WS47@+j2U@p3IU#iP+l`^v zse5+69h*P+I`q{3zmQm9bw{QDBVhamlacZZ*EIy6-Si(&VkA(MScl<=(4TdRwP>Id zodpYY{2vf(ASi+jDTWL3M}!BJT^cmJw8^hF{0GI~RZIZJa;cUFzw2@4je$r&i_h_& zabl}yKmal_n?P+&hn3WLH56cLH-`3or4!FZ~dWpn&T^pU?@yNRC!JO&C1M;RDwcOI5Jd--kYI}kfhyOsJ| zHErQ9L=4kHbl82fP(-l+5j~szw`vvDKZ{Do766Q%V|~kH(IJ4&;CzP&Nl8gOF+p|w zb*Q_?{gP8Yhy&qQFe)FH;!$vghZ~{G>bR%#uUP$KC-m2ts}%=7+8ch~B4gAW?09x~ zS-APVIaeB3SNrN48a15*XSdz#=lkX*>cAO>wE2*p+dn(y#EWNCGELQMz?(Nq68zhO z)a7!1cqEapFI&_zUINz3rNyLq|A_Z|y*fx`7hutS@ZUf&z1Y{zY(z+YaGib~l$q0X z9lRxrNPn7^Uld@+Lvyh;*#y-~P{Z*^iUZcXf1ikdd1(ClQ#+yGCs43qUq3noccoOw zba+xfZ}fFGjI@#--{g1M*JnW*{=ZiX{(8y@JikrOjYJz8sHgd$g#cUaLfT$v?Yw~r zKx8)AfeI;iuKg3z{g)*PR7jbo8{7r~Rfmhf(ZINre23S{MEH}TR|R2Z&`FcAhf)Sx zTbL_q?c(y3OK#EPd30OP2McBpppVT$uE8Ex(#j@~%VPY$|MH?#^!tIHB1a2UbS_pa zCV{6-C+5_1RXeXMnOh!TV;O+lgZf|@*%O^ZS4g}L! zz4N-E5K%*7f&P)Y#m%a33y(|8zeg-J_p%d}aQOmwed4$uI+tb!^^7<^}^_}R6eF1~uKKLcm z$IZNh##l7yR*Z62#&{?x4qD4(PGV4Nj2#(cAjHKrEzLnA z|9mH@MGNH@LZ!*&I9}km`6xcU+@Xh_4=NrCt@a-@yFcEY)c-2~r!P*83raid_`%@# zuKl2tX_-D*T+|iF+?wcw=z2fqXnokGe^mrQ`RAth*Hdwe)=nyH4OrJSjrxvU@YQv& zvBc)eX9_sn%!F&lQfH;1kghu0^Wde&iDtwZ;P1|SF@a?;J$9S0U@X*AOla>O|q zS)ib;u?94U94*);6Z1KA+hSEbbOeM%iU#`+BtbMaU>L?S`4~db8>E>=v){GHR~lRw zr-`JhE-riznwf^4!^tl-i_22AVUnCt2T+xoipQgS2%SS;X1wydJ7Z&kLbNhV!=6)o z=IeFr#}TI{&gV@h@rBvmi>MBasZUKF3D4kkQ!J*+9G_Z$vY1}n&pEoWW8;HrtDxm| zhp7+wMnl^r?B!3u8;ef$F^XqTy?R{>wk@XJ-~-#Q%m~jnQ*?dOEY*c?)$iUDUsj4DwJ?7`)vdM4*EvCI37-O1h}?i87hM*ZaG8S3v?@MMmuQ~U zc<-FvT830$>@!h=o4Iz5zw14bPG} zEGA2cR8vY%cZ1|fJ;gM z=oFVDQq-@WpMuCL7wo)G9`8ADGto#$%V?$GE=5DHu={Vbxt)VGyzYpXIm}$GR?Md% zdw0q&^iRc8a}S5-R`NmI^+JlA8oZnLfRZ$h}rA1o{2p@U@UVz?9Ns^ZEn7qQ9&DJ_w(+< zdI|b&u&W8T74=xw&oSl_k0FDR(xl0mRAJaVkUsSU|M5e3iohpT5nkc{)2T)0saZko z2M&%$`;~@wwJ0`JwH>uzHw-ADlC>I7+X{qPB9luH1{#j$uZ_O%<@y0n=PQ$E9XX~S zR)?rg?P-lgKU*o&>(YEn6?qp}W9l%U&T%$W@P)!c9TccRjO#J)d1aZZFt|NN)i>5H zS4OIp*7{XIu&OAtgrUSsG2^zyd_oAacvNE!jn{Fm%OuQqTq;DJu0v2bO~2HvM)B0} z&$17^f!c7eIFyhj*O*21^I)pGpmJXGiMmSj4U+^f?IKmQxi8lBxzf&VXWydV46Y;1 zi>Q_01F6Gt6G()bGERWJg-RK@;#(AyS6zzKn%pUcBQiOWoW_DHdZxbhJIhTqR$q23 z&IS0z#w|0=g5FCO9hXi+my9AP80TSGu z;1FB}cXuZV1b26LCqZX`!QI_v_$GT_XP=z&?){#h-@m$gtf#xHy82mnt+g!ZY(?7g zEOhMBwm4yfpXy~tZrZ4j>ml!mT}zo?t;K@876e%FP-9m10KMj_dDuTq*x1w)F?ruq zbap$>{#0{(PS3M$h;YEC+GNOx#Yk=6U^r!+Zg648!m^xXa8MX#up#@KX)-4A7d%Zw zD0TYHwNR0^CS|*Fxbz=)JW}feMKq0Sv*9{j`ej^P*!Gh(;i~`)b3iLRhdCY$c=yxNp942!{zcZM}BBC8csuKiWi(-UKuL z%2UXgRoUDdKEg9JG&~#3J=}!r{wCwtVnZ`?ALEGhcV5LqBAjFV?Lp$d);LHF(ckc zOKedSfU>1zxynPo zPh)VlXsoICNu2qtVD%A%680_7eAonqq;rO(9prcai7N7g`UFMW=6GO$*wcsjsm;m5w}5yoxQ@DsK7#BRN! z1{20MesDyP4ZrSqo$O=rM`ibV?n6jPM+la;y1gAtW^Uc1{6keMK0?P|=Pf2r5VM*^ z>1)j7xxZgU3OH?oif0f7;fCM;V6NVW#s5Hd=0K#k?=uNjXH8`I0<03&qoHvs(jd1K zy@$EI2SqQ@#zcy_Q8a0f$iCmcYf5N6+a6_t7qZ6c3uPtoI6nKc`hc4!N?0&i=Zh%1 zadMv#vAyK6m48l_Xo#*iq9nZhrr!E?PV?o$F%E=EP$Dw`!l8%r?!Ha* zLu?RST)gT;kcxB*Hf0EbN_ho!9Upg{ej!3m|icTANSEWOx{m ztg%utDWwU|lzyHq4Qrq=TOhH)r(qE;;xH$qYke9)B0VjE6f#VdQ}U!B5(g(7mYG9DzZ{q;N!6};J ztHgj;?~@Cuq9B#%RW3p}QK#gG_Xocwo-QAZci!~@SvkFt@l!>?mrtEA7?3D;$CX^U zN#^-uDhs&YL0_p9-W zwGKh&yfLAJCV_Wl!#LY$0pbPtUYE+m-So(Y$fd4SdVDiJbL)NEUrx+xs_eR&A7M^e z6aU34h34%P@Pf&gFX0oCWUjq;hmqPZrumdS#Pix14aJcq4>;Q>V%wt6q)<9?w_lH& z^t*cIhm8B-(6F~(v`!bPsxE)`oNr%G%&75)u%n7Q3iy8eUN+#o8fS3F%5)zN>wAMYiz6vmtvc^J|4M)#`eT zD0cv1(1qyO|D4VKzPAs$A;Z(SS-F9k=F&EyV4S7|^a7k{8)Z~ldqnj}V!c1;rr-~( zSudU5_TacTn6iieyhEB6mztan*G>J!KMLuIPc=p!hJ0Qx%7eOL5oQ% z#H-!C7gY+=WpaN)K^5uk$9{?_M(@7>>28`FGBJDLcpr z_-iCI`t7Y-zvi?#Qdu>trvZZ&J>F%E zjy8^eoL{uBy)CPiOz|}j;=;ynwnK05vy9hG*MRLl!8CTqXJlyyFQ8qo_Sp@741lYT zI$ki>9(T@|W^koF)%Qcx;HEL&9Go9+qEEh57pwNsuC*|u%MN4!Dbts|;cZkUqu?ma z!5!gDV+~xZdo1PtG(OiAhSIQX&DEB5-&wUajpl$($h6}Vj}!n{(s!E;y=c_KxxYHZ zm;V|e7>65m@Yqtvx`T8=d`(zb;FMAzFTzfDkz*>C0Q=T24j67*YzZ0Ckj<(z>K4ZU zVR1I8ZqttmE&?gsYqJi-R$j<=IRmQ5(x3s$2lRt0*M#jLTn1Wg`l~3L7nUx9D2Ofx z^QP*z(AtHg9rMNpKTWx@wSEy~r6>HT`X?rm`}f*u^gJhl=qPdUwFtBjr5wxQdUa|F zuyC0kl%HrM<<*&2y*3dyPCRppE0z!GP|{y3R=fV4K>&E3Gdna<4_rKdA~Ehs3%`h@FtalG7j=cxm#-{@@h=NNwrNPC&~Ak=lg120!| zfS1B{PK}#Qx7)N}T&Zx9=o=yHJ8petmy-;;3%iWP#R`(c7QofN%~^a z$HqFaSAiNWKqJ^K0xscxl*3Tpa>J|>x7TuWOU5ST&S;7-4&cXN#sHW>KSshf)~f^y zW2d(e6F$M8TMUB)ChFJu0-fgtsWL#^j+?_tCb-DgWK7M1J+NSnPzfZ!(!6V0wP_@O zM0MqXeLobtmg_rP@^9SH#LrkfcT;36st!4s9qQR+Jt?n+Y^{j{zgWj33M=8ydoDu> zb3AYToSv)_9Wtwzmoo0(s;M8E#I{AqU%HGfBrC%SUk^Nl(}E|;eAT(&-MZ&*pR=m< zW*L$fr7eZdr)pfQKE#r4DaTGd(r>AC7FNp8)?UEUvcdO2Mm|T;-;dl&2T(8LP-A9JrMDaJ$MyKC~Q* zHC267E7IlICkzhi~&!5_n`BNDv2Jr{xr_73ePCJZlPNx>kEiOZYA zi*{K$0pU(>_dZW>yZvf+e=hkP9^Iz2cxP~fKy{tr{aNd}aA$lk3QPEzMN8QZI`%Fd z$Q;-F?VfaG6}yrDX+C7Tl`o?!&~Ux<)SxM8pItxyVAE#sp)J}{6IPU zF8hSY{rAX^kGJaj`$>5e_dWVTY=Lp?Z1XVTwWqAsV5wMbm{(PykiLj7Jb;pCFKkjb77p%%lz$fzJBfAMH(A`8>Ng-9b z_ESl6{h)h}x4!#=yhjK@<+1PN5wm~aL2=3Z3_7ZP&>eoyKJj;2xA{>ARfg5Wu`WAe zA(RKQ*9L`|0^a>iASgNh3rUO1NES`579tx+d(XV@?^2ZKLZ6QWq+3AD;UTcIg3J$1 zn^Uk(*aXvb= zzPyrKJQN;y?wDQmL(V5KAqInQO2f%qv^a@-3DF4rAw?g2qaE!@i6U-*{rU~`)M1=6 z%rc79j$`iV#pTMt3LeVS6%h&og3SuS^1qr!40is>ABn8~9e*VAPyR?<#4?O{CM7#P z?rSN%ZqgPIT5d!u-K0IUPuQJcF5c|R`aEU6rWuAemjqVi(gzc&uYa)X2wTT{FY=YUiTB_{i^Z7kzV^qY!W^beb z$*Bf7uyq$OsLn32@y0FfZt2?WT35(bopOy{ULaI%@Mu&p&d12@1e00qN=5~-fNumcy47u{V^8F%3UxGS@S zcZGgY$mgMfF#w6lN`2v*Km6PEcJG6WC=p+JWvre?h27he9a0=IKvCQ2Qs3ey6w&DI zz^cFO-X-FCa1MRvj%AI68fcpwNdoG4`Eg`MtXwL{b$p>n<3FNYmpvwISkI0sucL4P z)RcZ<0_sId^)^DuJRL2JqLDeTd%ZCvJI_#%SW+Wr`jKpoxC8Jlut7nc8^Lbhyi@b4 z;N{Kw#(O6S47%l+QD3%1GJKST;L}vIdf1B4c+h{CPY(~h`&U&-UopjTU{m3Na$ z^dtbI(e?u&1uRr1>NpW>YiH0d1ex5b_OPafP#%h^vtJz?)hlqC*5O=X?&Fhj>j5;* z6InK!ubRJjYjOCo(QWpf8a?hzB%=-M7kCqRRaNZGsVl6J!{H)C(?BRc(lw7zTxo|> zu#F@XBUDn}l>+@pVEkOjU}fK|p|QZ`Q%0d^n;v(^>Fi}%LDZXPN4j*G1Wm~&S$~lX zr~>feHjQ=iU8|C0+a_s@PHPp!oMd~wfiu*Oyjy|4*&_EFVM%BEx1bCxX*m<=!sip@ zN8=O&jx#=9$g>`3lo4%-t=17{FHKA#Sya3pyuFqReqj)UB*Qnuyf#`Th0Yb8`1@7| zda)-@oi$2)d9j!6q~8J7a1_hjKJR%@Rk6{ATDt7yg+bD}aJu7^(L0u04t4SA`uVx4 zS4TWwN=(+?i)F>`W@^q+e?XNMMV8hHwVLB+A&-rV@U;(NS=vBMu#&WQ=IZ7hhrm+( z!c2FY*!KgXHvKrH%yxYxDo7`+LVMK!!5U~{)}fVy&w`<--FKNi1t661r`3xS*$U~@ z(JtMtq@8vs(NR(F=8p{%;7cJ!x|o@mru3eY z^uQO-Vq~PYpN-aJMG}WYW?@_K)qXFI9RNG!)*H{ZS{Lf6(Lvh1eTL<}=SV)&042(c zbGbWxM2Q#m3~;65Lq_6bUaX=P+I_?i58*3f2aHR*a(ep9)H0sXGJ=5TJa zLt#dJ5zX#WEPW%~R97@D#7Hvjh~TB~X}h?%X;B>=-rj2!Mkd*wJl|W7wFGfh+_>hO^gSy4}O7o#h=vr$L5CKhQko2 z07eyA{F62EjvX-+B4%7JQc_a2hXWsD$2_tDVS=#;VzT=1C#!A3Zx1sf@O6=qto^ay zkjBs7x!4j}n>Ds$73~1x@E28>KCmO}-ZUEfE1Iv6NxZHzs&!iG^BU-UCcH&`qUA7M zlWh!74fR6}t!y85vt8f6rajEzfLaf`;=hz5gz&y=ukRomxrR$bBgy5H2-z`D{h7I- zB=68-l~pJn{+&At>^A}xOAc;Gn`>|Lgc8iWET_4fcC|bo&K7adq%*qU|I#fdvLTrT9#t_8`M(Elz* zxl#16gK`w!Xo&JZ zk=tICVp;ketY(}wuw$}k^H-ME{rg=H_-b3KY(jFzwwMb&N|GPf+=Ewttq{hF@8Q7- z>XYfdVGuVB!n}X0h;=2In2$~wPqkUE)ac0oGVlg`+&Ck@=vJi0OhQ43{U0nnxQO9I z1<66+nY~Jm_;{v(Dv*9rsu=f#N5Z7|FqYuo{PaK2pFA|gZTo;V{Xj-a{IX$gGrziz_m*5bWfA|KYtn?2hM;ff!`w~N#%Br71~@@e4{tP z^VetoKev#Ch&!e`yr;BGuYC7zH^9jI&lmqqd*vn0b562nKnsgPlV`b@;rOEerrP`8 zm_uih%oDYR$?$I^sN6!rGkx}Ckpw-=+Yx;2E9#)3&C|cG68P`WK0#{)u*@{kG8d^? z_njD^trf8aubC zD?HZeMNt#?#~5|EqlL7r>`1*ajhNj8%|2GhdwHGDM`_Og=_2{(-sKS>R?7H!0s>D| zn%=;zwqCNZjPgey)BfWh`no(@tE#G!Ar9fEpro`LmtxvKwcOOHA^$f=^Iy1-eIIV6 zBwuwWwa50L!RbLADVHT?P=NF~+2SYRl{#sM~bIag7GFABc=EbuHNq3HDVGE;1 z5#o%ewqpMx6#qP`f3{7m5!D{xu#Pb3Ys6}oF#plt1WEq`-Vmp9Q~keV>HmMxN?(a7 zgl8A=|E`Ptf5W`|PF4k6E<$J5xaxaaS;VSj}&{@lWb?FW~&YmDKXn;NnLM#zng zKAg-uI_?tI_&?4P9K!N_60HvJqPMjj0W;}eLs!+1V2N}eJYL;QOn?N<@8K^H%=AX2+cF9-^4GBUpFQz^x5Ys( zpF|O#zoYSc0dlCMVK8#5#0tLu&(O?25du48|22|ma{7W3VAVagX5rQ@hqC!gr6;-c zZkb=Ds+xz0%+US<8~g#{yfsFS{}rj)+yuXkKzYa{Zz@9O?wGrsDUdlV4APWwu6=uQ z{_ZTVNKh$+t_}jsb;uAZbBTQ%4jKvmb8m#BvT;4KCY#lp)xcxXny(+?x>YXk_TV(t zGDZLc{EKR3nr>jyE<_X2+JAGW2$jwXjZaf8cqLKgbnikN$=6a41djt38EU?`qlJIV zE&&Bt;qOlNZq^g7Zd}umloZnaX)Z|0!)=Q7LhX=A#9YgdcFp8vh~%$(gv$Tp@+ML; zfGoF}83R_~m-UwEn^U;Sla5!PrL|o6s^vjorNG}EHyn9H`rPlMM^u5}N=OWas5Q}# zdnHg_-o2`uB`OXPDZLbGRATW=MRH3z#KAJj!72>ioj)H z`27H-+_{Z>7>XI3WBxenr6OAW0Z%4F@i67EV1jZ85Qk%_vZ7TK-xj*@Y@*mSda#vt z-}Jl)>0SLm%$+;a+bEqMuaxrNE_iRH%8OOXUuB8d4$q9~D$?}e$tnr+UDW|69J?N% z;Dk%M-PSIZ)9dF0MQKVBJckzlVa(#^0oG6=W8oGfWsM-xZo(8tNl{_C{tq5segIq7-a6WJ@jf$NBy>% zVrNmG=zSR8n;$wsC2UqgjZXigyEXnqL%s&qNaQWm!-GW_m7314+?d$3%qVnT=^yA> z%q9)S%Lc2p>N^ITJEXLUnlm;Pd~m+qJP~wv1uL1kTf7a8EdHq&ZsktOXVJ9vv}8E2 z4LB4i60z}vE9#YSZv(@{z|}t?8pMoW7FJI{2)FA5(gwd>oS$gia{;NZq#4 z{P`1`r#8Dek`L6_E^~*ugTCX#)V~XPwc_e82Yy|_&s7TZi6n|+wR?sZf3{8rt_1^T-GMQ zOYnPrX!m(5@MM{oQ8z#Eb=$q7fY(lFBq{fhm(sBjfA-dVllPpv5&u!#gz$`9_AadP zW2uhfY}pb+$He`9eBa5%G`$bZFuPQ{RUWp~UPIqlxtzAS1aO_SoQB^}FsaBrGaqLxi8)FH=3=E1 z?MY>ZN4c;>UvV6cl$valbVSSg-t z7Lu|Tu6+FZ;5vHwQvOUfoAdq3VwQjt?;C77k;|}0m=Lw+^#R|2zT&YRWc&s5LbYr@ zLyS06kI!i@=9o#`{ zVyQf=M+&tizB@H$GlBx8gn-bxN9*Sp41qhH67Jr$d_X@hGk0t2QVepApC-?OnIhDu z@sR!Z9(`a6vA1AY)%}kdq_NF|1&g$snU4vYp#uxJKW1zD?7M<=eG&%vCo~L(L}9*u!#-RnAaviPNtrE`3T< zpQ|8xWXR)G>&33q_%)GHeb}qPZV;n>fH;k{1cMj^E_Vb{2;0By z2a3K?+dZYOA!M<&bE|o6<4{nQ+k`_1XF=i?AdfT=FpbZ9i0pu?OVa6NdciuUWA0dX zxEEs5_c!Pn=hh>%PC~BPi+H&Kn7C=QCKE^ATx#6Se5DYh1J$Fpj-o}45oauYpY{$> z5U}Rq?w4J^UcuQ6!A9CPWeQy1e^-0u$3CE#+>ufm`;hML^JyV(@c=fyb4zvt;wUOp zBj}iT966}agQovnJ>`vmC1;kviH2)C7cJsn&E9ra`f$;slU7`h$PXl*cE1>^H#0DY zf)vgUdr4F_LGSbBTd|^fItt=mTTM+KU>%6f%Ku6lnN2me`g!1D30+yjgWKB%ip|qM z@E$7-C9pTF+*vGM@YXE7KLm0YKrQa*xq=MMpDec{dOP%1Dvj>x4FhHcDnYkr=R;hN zKp-)1-QF@)jPU8#jj4f~tT-}k43*K;yWA!U#Vlb~o=Xo&$_6#j5t!bq1Uh*>GUiWg z6=akg+nC*3LUukDT<7$PsrhsB_>OneBxwRr^UIb;+34*GEhbqw&P^A~VmQ>izjT0BSss)N8De0Scb2%fCn5qFnwm%Cn z(o>ytykG0Z@HJR3RgbwH(1M8qI2tL0k>*+}>Td;Qgm*d4mXyYX0d9Ajy0L9RobD4+5hJYI4 zGi)6$5rA`{WlP#Q@S;VoIi$a?P5ho}uXU^Wy`$Z}8C~w{Cgbyg5%nw!(xZE0(R3>9 zi+0VDaZLYxQpuZZ)$LRSI4(!b2G>k63BB({*x`b3ob%l^LfTp*Y6`Yymk-jeW|$1U*6jW8N+fqCc7D)-}1hAaN`S z2mJ!VB+83Cbj73eY8;bl8*9Q688lTs{HB>m__!CjK19(9YZIbPWWRANx-p_<6ICoW zS9-4JBU+``P0KBp8cv$#C^H_r^$ z!Zwa|Tol$6z9hO)!?QH2IcpAQZ`jbBj76^_AoCcD{BXQ5Dl9Ci0TozC1LtYIBqMv; zaZjr25bQMFoM>|K5iQjSZ0t*WAa{d=oX%KBVvB&%d2B?I3Y-h-m)7^LDY)P6TKV7l z=%1u$c03%<1V44VIO6PNacXCMu&EM234MoiE`mgH5Yw=43qs)}L?Fb$4K7{@03<%` zO)(6R-32xSlNa|`J%5eWVX%`0Bm@<`_T>v5=g;x>#NwJ2fbVf4qTtC?J+zID^Gyzp zTl)wz)63xZF?zar_0JlS^vKP|NsB|?p6(V_MBV_pFlf#*(;m4!gI5Ig+XktN#c!aN zS2`7};WI2g_V@6V>Ojj-m6N>w6$GP-aQ}7D42FR^$l^ktA1>`Fv3FK8&qmfYgu}Z@ zDaLP0SmsSuVqZjZ$w}2u4V~9k3m*(>o&ddsSx?gMnvYeLZ-Vr)qAX&3L^m@?%)}}~ zelJhJ@Mx)cZ8!B+CW_b!*=PE8ZzSNy+p2Ce-I=|)L9m?<)xbyOD*}GLF>PM15=v!d zu@;YWxPZzh1Gl)R`P{*$AHTFGfr&85jU*;OaYES76B1^Z11w6wJzqY!&R!rwF}=HV zmn+`+D$P#LFq}=RqPKHb>^_Wu^s&)al;rZqbjOe6jlm>UlwB9%FMDFODTy-@>^h{` zO2mtf+T`4m_HEBnoa;K~Z)}co`Xvi@mt0f#m$3kCB0ET$Wr+LuS>Si1b;R9Y=bsah z<2G&?C@bF;#|Mi&G;>a_k>EhQ>c);avjvQg1Ry}Z0l+<`ZH4)f6!A+53?JjbrglyS z^{?VS*E~l}kM)OIX>SzLm&*h6e~h{6)C@#Zq_9nh(3uV=#y4E>b5;h^zkSf9t+%!F z?yj-xsecN^K{BntJ}5#mUF|PMbI!bUvWB_cmf(NqK5|yyUzv(^)#rgeg;F@ltZ0Qy zdIQ0PTg{z3;q765B7mZ&&$hV1;-meSPdJzLNm)_v(b&!11Zh9;-EHM`JZ7DEmYq6I zB=9wmyp|9mPQV- z9+9$)bU#h)GO>1sfOCPfwuuY7&$wAn?q6tr4X`3EJ^XfEThQXdajgT`1OVq~j+Po@ z7E6();PIz`sCL(5=-9@1e?#j2#R|fl4l``;)HySIvF8FJ-Cl0_Yd(-~;yjZKyQOR! zWBFj=#v?x1KL)zp<7Jyi=*fVcT;7<}H2|h};qLG?9JG+vV~jnB z(AGbxWOHM@>n?UOCo^&HEZ|1j_kII^O=3vfDJIIa-wJO`PX)LfvpxmaCqi-Z5CcTW9O<`Acu~b_T zJP$8=PS1^98n5KaYHyu&)gj3tQ`>sXCSlfq89H!-&B5hF{fxGU2p{n~2CT*!NmI_a zA3LQzJuCPTS*p`_UVSq?oUW%-@|C)oO5L71X@w@sL<)P%!o#x(0miM5hCcp;ieDpY){1T(aJkhnr`T`NI3 z679HIWC;wI!6}P<2XG)WV*H*2E9eMv6rUx81%w-;&Cnx1BVeP#QJyOl4|wb~T6r?y zS2c`UjcBL|fr zzR)88AR#JanJKGTgb4z?&VA16vWm~&jov0b^qnb`oiA;6B~l_VgraF8w5j9O1*(bX z`Xa3=!C_x#ps9bUNye+F$h#~dmyD}#?9??lqQ63=P-7a~_Qnun_(D}?u@LSB(;0^`w)8i<8FIb$IQ*U{veoVPx{Ej8hJGin1a{dhPz{%R!;$YD2M`mm-)ctN|tA zQ#P%+3uEEj(^Ol~N_B#V%stnhtjy!|P>q5M;~juYL7#G3HK@5nfwc36NJOj*ylCS? zi+h}*iODoPkJk1jru;^hI8|O>VCuI59sk3eum~u;n!8|eUFvO@}YJH!sGu;_#Qc1 zMx6hiw_2pD9CSH1$SES$$V;EPYxnHJqegN8-qJxv2~FruHKspH`->; z=u`jNy&S=f1#Ow!J%t{+prbTy9%GKmBie& zK)&j>-(_qfU}fLe%n=J-5am;?Z?an18Z=M7F4=Gwp?^X=?*gqa)F=Ml1r zJY6}J$PB;kuFhu3SBZ~@o%{Gos+$9GSHhNO0Dhi{A#za{!Pk zc{+hKZBOa6N7G+LXkLCy!E#J73#K{A;U%RD_&~2K70g%Nk>$rCBOMwZDS#hH2bfRY z7-qba-pvK!&?>WoA|Ky&l&7@;Oju@%o!W*JU!z}O!t!v!gs5$amE2$-lpuMg4#22CtcXojl6KKv-~@aw?lgF#X`Xc@R3Fk8*dU%^m6T@ufQ`7tJPqu2?sy4H7aA4M7#r!d= zAvlUtNHkxT?)X68%^cq+%I4P-^kl7ZWxp3UyPAf{a^d2rnuBfuai&E5%U)^>fG>XaLV}P|{hDOZ8Ms2`Ly&Kq-2_6- zs9wPlVl;c@i|V*fWzRdOx#5;L5Kb-5R)enIdwyh(T1+(#K*ny=@s4^3UbX%ySYO;8 z3%(})P3bGlY%R2n7pLkK@c9HGKf!vu>6!X=dmC!hA^uSxKxa*Znv zw9WO}r*60jwAgW2ZRGJNRH#PJ+gdkOUX$aZ(lhosCFp(uo!yleDg4oP!SJBcV?VEU zD5%0Ie$hokzwqsvH*?Ds?xNWDFHZ=eQ*)P_&wCP>BfM|+_6Ij)WE!KMCE-1~pmaA3 z*Ohl^T4R%D*=W1&{SvWD-#c-=KP{kidBm*s9y%JibuU|9$93`7_p~v2Ph8jqSvS!6 zHO}kqgB=*M4Tp;QE}HB7U^d79m<+hPA_tye&C((o?zU3i zSmka*7Y}^x{w&7t#*0(=|F$L*s9S|E9|M&=UAtD-*3kr)L|dou47kc^m;_cy-Cj>q zH?g_C3j<*C!B(}EnxwhN#uVH~G6K4#Qy;ShSsDlmS%Mx3~N} z;d>ajw1r_3N_&!H#B9CFCk+o_$myRuM-<&9BE2H6^o}N5Sel`dBey=GbP9!yp9~;w z#(oPfb@UXCTZlNA!`$Z^c{`?p{hA6U47$=>H@D%g?!rog4L&Sau$)r5XT~@rh~kA!2W5+ zX>aH0I{m281!^rR0=003hp)LD&Pkkum3MQ49oRB3LgSF=gwjXbMxDyhP<%(EZjP}+ zNzf#E&_sAkpY-PYkW0USXJOKHyOGPz=Gf7_@3UZ+pISE;G+ypSp6iBBQ|Mc2UCmSB zZa#?oC?>H*dfHE6+1|~~+Etc4Y6(-Ac6Nc6LntM9uOfzsAd#;%kI=W+@NOW+22TT! zhk_c;ZOhP>p$5KYpIT7J4IswXQG#*qIXX9186C#Gy`BI&n~2a*j(r1XcpaSLoLuRP zfiGk1i5h)9`tOd^QDe706bt-va?&#qyq7=`G^R@2SRv4(E%b2e{|LVgI&Wxe)XW+D8p?Lha1!M zWD;va-mxX?oI4iEKO)rO3Fft$EzJ+D*dJ-~rZV-ubjFxmfqh0we#eWry8oit-o{Tt z)MRa4FO1&FVArq1Gj#-E^;l~(56s_l^+pJnfA;Bm`ju-3T42aIv2e$|AS!(RJT4gD z;@aaRI(nL@OQ`UCzKxj^;fa#y3WOQsiMeB$g1=-tiM-3 zG-rF1I|rw(R2-yRqgw@myDV7G3P9ecINbJ0iT5KX0&0oyHKN_N*I#zyP_)R1%S#6@ zhOH|j#0M);H*Md(_aR@zoM6Zw?Y?@tS8wzBo@l zz3w-=!M?ryoni%60P{~WUg&0JJb?JGO=V;AY zbD4Uzb?p;0)d>+7Ru3f4hKfzp17)`=O_pDJkDo|nISWRmLwnybSwa5 zbNU>F(h3Lh(8o{u+gAHO_m?B@(_6{#jKAtdo(`bLFo@xnhPF^6)3lH)AsJNC&}rph zDboR_V+84y>~GgRJ*+Y$qxqQYsZ}YQ{RUXdD4`@sEQ3#iw_BVHx~iJo)^W=Na8fzW z=O~E^Q|^2S9&=yUc=~(r0xhBuX7MYRyYO)=nD6%FmOia0@uQeSi34|+e^lU38ZaxD zC_2}Gu!sdlzEiIAXDZq9&w-CC6}qa%Ghw+$=ZRJHeoA~t)I@kNkPG#|&Z7Bd$w)>H zd`z?LRX3sS0WRpTq)rVy^abhAj*G6ADZvY;X`#v$Xc+v4C+IXhy~5_5+b9{reKhKR6{T90?tGoHQVMzd#B! z2!phjM~L5l+xAIVw;jpFgq5>vW91O3%r=p?&y_m2o!<6CK4qHgu=BDd9$$Mv9yWR$ z%s8--G+%xBfB`&K-P3AK^vGOWy-W)BQ;0@kzX=lCtlvRy1k6)f5-tS(1b zeQs8iwpy3eL;=ZYi~SFG+@E}K3qfNe`F(15w=tct44%LUsgtk>yzW`F_vl~|ZPffs z$Usj}Q&i~Ytu8=HNxRUkBgs+y{SJCguEJnCd$jqpXpuagTQ~yl-umsqmmndG-q2DCWhowaHdgC7Dg0rfR&><>*4sJ*WT0VU7u%XtbT zj02LBP|jZUUS@VoUL$s|FVq*^38d{SIF^Q<{*{Of)mAn#w=GgK?}mQ9G8y`Anvb!p zH`BaKavNNQ)Mes>*yDqY@C`MsvtC!j1x(II5~CuI0#F;b zm@3S=wPZC}0Y=HrIA9zeA6s8pmJx7>e^6o-qv8VcdbBQ#Q=*%&_dG+Xa28xJjneuw zv)8-18oAy;QZasYa}TRltmc0>jM+@1*LNY3qT_pL8e4gTJ0`8pOeSAy)|N%e+w{q{ zEjPQxE1unhHL$QUQgl+GhTwh|xkNX|eg;$D!3})(%+002@-|QtQyD{Vo;P#B?j&Qm z*|`Nw_{5Y$Gmk(L23dCRlvQ?Ce#l3pzIfqbM<|UO_uYs0LwYDx_;UhfMnY+XF8^mt zG*|xL{llMy|74keQx)OM&=$BXr9gGrvTd`>wQyj|OTEBiU*55ll?AWUjy;Z9Z?p<+ zuioc$uqIzaraU!}@D=pDG)aHuuVOD6qTsI}M07fbyGVHLI;moF7iC?#g)q2G^_7j~ z`K3Oub-ng8qnux&vLdKQrUdf#>9{USd+HPzxPhZScby;AIHriYbErX2P?x=O;Nb=` zVqDWKo}TA@>-E&ojZ@Bvp|9G(8B0{Nkt%X%nNlH&DQMPo2i{?bl|aAwr~?P-eA?h4 z;eKCSx(~IQ7pa}o;9nGjD}6jT->%S7^p`XTH{?g2hJp1-mOiG6! zrI&T8d-ztLIUTwlF!jeL@V{>~1J>^e*%UcGL1A z=aL+#ilcDpBfD~pfk@NTV6PYA@QDfKA`xNlw126_$CLb~r9=37sECCh)LGp&%gMOg zTt~X-!tXNvH1TqP2&^1Zqc`g5*I{~fjTTTb_#AsfPuhFATQ7lMk?N8kye_Pybku%i zIl;?+f9nEmJ9ln#KiYN|wsx(@gxFBt+?PoUm~U(~-0P5+9)9O_N)07s*T`FG?|!65 z>HT>smIwpw+r}qh0$#(+_{x+y=)Nl7N>|TK8(2PiTv{iY4eRHNQzDbIJZv z*%&f)+L+r=ka_uT?qFDZ4fHnU#rFF^#ePd#-BShq$C}of?HCrL;Q@2ZL>j?`E$kNc z!J8Z71wEIJhlh*)X?18%u<%B@@KF%wk-Vv$=_(2A0-rs@TFW|Q5=A+08&jRM3Q4n` zHdr*>MjhbE-VVzX8X?%E$_5QlPz&-2-D7a$62Lg`HfpfbBKf!T5^xeZ1ZcT<53A@E z+(9O0LVY5h_5e+sg##1IbDm!=py`;!Zr(rw4XVzZ8OGUlGKdFMM6+?lRXb@2B+{Ya zlyy>-i%U?2kXBb=A=6B7IcW>%OVz=DTzS)}hM3Ul9+82fV+T`?v>E*1quQ2s==)^W zFrF>3Yo#~7<8h1&p@5yxjjY9UJEO`o*>`3B5EOmnV!DvpL-g>RO4X<0*$j?k8D+H- zpGMq@gO_l<4XaEh*qfEs=}Uo6ISC&gqZV`3XNHLquqUVGv5w)5g^@98e zbvk6d2Syv~LNR+DF;+V$Va9{vN~N=&efs?KWU{#1%FPb8iLcs>C;FuTl1%_PM)ayX zY}`>pCRmv3tJUrkylP$Vr=X`LxK_IBC1kT;AK8wZ8u1f#)cRX1ZQJoFcQTC6UF9f< z)or$!;H(jTa~;%iKf-O7ZO5bO4*CbY+ZArw<42} zIB}nrEazOk2f7v4{&QE+1{Nfn-18fqKaDW?tI!T04f<4}w9noHa&EgYpbbULO zb_hY|W!){5e)VHy*R)u!>H8~_+jiFK(?Pv3CT00zeon-ZD&7L6S*>8ZP8H}%j5p|HVuBRu6%)0(&+;-Wr7QN*zB-XW&Xc_ZZ47=Xg3 zH?m|DF(EhZ!hQ#K8bTTs>#fs4z#S>^z)I99EJTGQCZ;EXcZ0JLKmh5K+8a3&!&~&w zrBpI>e>$%r@2A6t@lrR6p0M26Innmnb;^qySyAMZ%s_s9XK`_f4C=xzI;4e%?C!D( zZz%^6H#iNZ*p@ypBV9o3qeDQjHhw#li=z|}^5-%97iJ7lMoeY5QT1vCZP`u>L`9Do zZFjz zE^jsB?8DextmCC3s1W@d+Gq1AV|l{%UX{mSg(N9=EfU&$$)pUmcNjZ;W5ps#7vYBJ z8&j2Ug>>$v{n$EAZLg6tZlyo}q1Jjyr$$aUVUAo$D3)ZmmW)5U`I!}bey6Ol-MRiJcJeP#Adn3~ z*n9Ej;@2qi7wxnTRw2v2lU9Rgzrwn^y;_hOOE6TGXIB%~^d_Fd4Q*TT8Q`&yrw`+v zm;+aV!1Kzufao%Qgp{*{0W`NeYpy?Z)IU5%H-!9*x8`p3c$ohz`~B-^|98W0k_sN_ z`})qW?7z4DQ7HV^Mh0=XLnJy5TgksYd;fYH027g#I+zG^E{u`HokrZny}Li_Pwe=A zwG}ykzrBG?_3?R6Hn2d><#r&R=WnbkGh2~qq*kZz)ND-Ct2hkn$lfWT1Bna&H0-L|84*L&reYL19Is9$qeV9l863B$@tGd z2JWtM)U(p+TmZp}X6oYRAGywjPaDEXudCvtq-Xb!Y^ndb$L&Q@&m1K==ygZvLl9*! z810zfE+YpE0-=44$>ub~R(CqBE9b^n%J0#@?iWLL&}ZvI*l^pVEDt`z=uTY^(IfC;U^Rr_=3sOjxmp?h&-3+Yn;1+eM}MvcI?n-Ix?&`u-f>P4XNAY2{Pa)*mkU zzkC_NVgTor)PL9D&)4z)$HAU|`1?@)A9Kh5JKsja;uh*aNKH=OMK?dDPn^^D=ckS8 zEeyce+G)RHO)W!8|5s}GSAskTSK9T; zXEDEcR(~>{bBpt0gUK?fQIkJ|VMU^aNo)>R9lEBl3x8pn>xhyN!gwN;idL4 zRaj2M!T({9pX8!sz_=?c2yu#D?cJWI#D9w;sFaXP!kIqLfIOsB;&n$nvxV-1-S!i`@-=T0eG}a~+ zj`hBpZEwv#u-bj-wfXqhRsivAyx>yoEhR4>&u%#@U8c$Yj)J21c+Xaym=Qh7M=YIF zP>aaN_~&YMaIHP`BJFH!(xlp`Pu|=?bJJ>MzLHc|*ZtP{)^)+x!Mnn8Wa#x^e}7!9 z<(w3d^LnY$TaGrk8$j&yrLI6eVfxctR1@d&k{UyOWi={*bQ(z#Es;;u`u!vzmrizq=eUBmoIvg9?I=be>m-&U0#c z#>holb)e}|jjonaFbo34si*z&CI43J>eH9We3=1!vusG&=j_>sdi~rn9zo~{cEd>? zNGVt3mTzX{AJ&~HG40gvLqwN|mdIp-PfCRtD+nkKu*G^A`r|H8=RnU#ykF=NT$>?* zR2jFMhJA3ljvvWts!Ky(q}gI{2xfB+{VWg-z0z2`oRmq3LSnu;WuE-Ghpz$3ve0XN z+$V6q&7Z4A?pomziW3zRmvjef|2nvW$i*@4`qKAA;3Ujik6tfk?`C^Tsa9x`tCXn5 zJC5;F%A`dTa1Q9WF44-Rvwbn(+=+sgN!0ltyU7=3DJdymbdP`k z{;uZb5Jz5K-uFEXvq4ThU&`|xABxNU8E%H>*IoF;L(|m9(mG`YJ`Wz%hCR}^cD=IZ zQ=p_Jey6Zk)Jok%JcT*uwa3X1=M_t=hw_d~bt_%~G^a>K-|*(_Nwpt_km&^uGoNt9 z>YSdZGDGW4rgpZXb|jBz<$s>YgYurYOqc7>>h;+9I*U~1+%IaeI83*=@6$s5 zt4?;1o%EPFn%se`pA*^)5>%u)r0SoGWy_u?Vs#;M2c1MCQ&)SKRZmyo(X$VVNm20MKCP`uB^nusezTMS9A|w9bz)}L^J<=%J6tb1mQ7a5zo-iILK(Yq zE16C9Ka#5#0O^nQ)EwgG_?oLaH|-Q#8RpTyAbop!AE{u5?TwACLx*OfOYD-BWuQ;$#L%ck1e|)I6w$%6i94FkBtRGK6V4K&D(Q0L}o8c1N zLak>VrY z1B{9O6Z28l^~|Di7N&0{n#;2Xo5QT_2@j;c94|ibPxqnZvYq(rbFEr74pi4={`u)p zI&I_kWE|!a8=b*zsQ`wKHbe4qBxSfxP1i?`HB4t0i5JIqVFcrF1rW4%2z5i2Jy9cN z7+TbMoLeW6lp75EQ3u@51pqSj?Z`B|E|7}KF-r<-)3UI@Ruk2Fvt^`~Q`fdpZ3X66 zb2Am}Li8Tk@RhBCMMSID=>DASUQ;vXa2!JiO`_F#t63+o8rC=U!&@ITSE8mCvSi<+ zLFBT!yx1nXrE^3Z2Cn4n0QL8m8ae6nJVjQp4LL?meQ!V1^0>hqwZF9*Z?b`I*B&TTEu<5ne@Yj{f@D^Lg$t*TZLKNzS` zh*ufRv#2c&cpmneKAnKs^Zv#R?s^&V*q17c*EQ}I!0@8;aZOtD2M#-FC)A@^-xv2~ z^IK*gg6mdq?qlEZFIj0;ZxN^EZUe)Oc9+fe2P&fU=>{{6hiU4W8`iTxJo-^ywPSyR zX3dAS#_r3=I~0wll)9$rte4rvbd+t6$?i&UbKNgnDzGbs=pH?bhSScA8uUn^Ixe^G zZ4T1!LY8e?n^>IcJ{Xl<%a42oP&4e8Nz}@{>JYk_E75KuzAcxc{I-{2ZI4i`AG8Ea zR?~&K1JBBTpnAi5LG2kg>vlU3iDm@c+Am&K_n+Q<{3hW%Wj3p7Hr*e(1yft4e)13- zB9l_WV_NhE7OfH+s_Yv%n8auXEt`AEUz6C85=!JXTngI+wmG&%7#%0B#DIP}9&ugu zq#cVI4~f0d4CKeyZz{H=tPM3ci`k8Bs%!1`+jZmt%G&XYB8volX12!}{@!w3O2h1_ zR*kFOipJ3Uusiebx1nruAQ zILe%4e`P~m8^!t(m8y7py<)O+sOH*gRiJX7}l{i0E za=4<7^2zdSR#`QMW|fq;AE}pckcAIq0g^22=0Ed{1qm~!Q2+5}zQA~C{*=XepUssU zsRUzpws;M-YHvN1h&7L*o2nZX=L^8;C2O7X6zTV;eZi)8WH`UtVJa}Mqd5*TZD068dop21 zVRfgn{jIh$pvxAE2&c$^lOTg+k_5)Y|4Q(3nza|}d!olW$Qr!(6Ll@}Vwm{xfbj6I1((yq*0S%cWIP3SQVw zSK}a=``#%yfUYZ6YalAw574rWy5%m=%X#%M<8P;hC+&^*MTYq#`Bz)`)5f)}joL8+ zpRYChJRrC#$I$R<$JWcAvUOyMhU_iD7&siC*`JB zxFC%mlCd7@#j4>6l)Zu!X6%+Un>m?uv!t|TZ%@vw4IKX&Aeo*(+gWoqj5D#7$ZA zos#n&_JhOIw?R5Q|GLrzY)I!UFKwoX@z`qG)`17!!Omwxw?~KyLH9(9f4j^zKfAi4 zC4Pz>Qc781f_lHk*$c*|I8gDbSpBM-?1|UrdxGEB;^%o8J)k^7+Wy?}-C9usGfx~& zQJW}Zi5wn`FQuN>y2?}vIv<-7+QHXJFqE?^q=Ki4P$6#37UqWK@BPPiTz{qFvEhuT z7R*{w8_}&%>P!)?D{;c8c4T9@4pgr;>~iC1B?e|bIdT6jrj&8lYzsLxPDOaFPvabn z@rrt8ABqpC9UNoWWkRLoVlDRz+;H?_GwqP7+3*qyNXsF!?<4aOx{Zv3JF#H$h^xgm zKOF|Vgs%o!lfb$Tf-7FI(2*t!{i;_KPo9#uHLRv4T_PRjDJiO=_p4MX&!o+OG+N%J zHUg`^A|*Ll1j6c=vYUV?i?B4nTvSfX=Qi#(E}|WX6!~j<55g^x8#7*@T%s(;bP!^LjDg&RJ47x2{q$PEK?q%QTkN=it+3c%tsJSHAAH+bU zwKQM%)fe{{l=f!@hU*e{|AP}8OEGDw@F$&CuYB{n;IxFmMe?t}S(ZxNOPY3G>u3@l z!)oE~jkc831H_?zp5r_iDvAuTB`-%keCGS-rtY|%(Q}kkTJ9P&y@=A zJXHCM9tI+ZU}6Z!B+-~BpI*(^Gw<*sh@TB2wRz&HhmWu78e4FVuTCrRFq@>JwL*(o zMejzm^e7HxFw`SRPPF#kI*B|Pt)29wWMgKe`O00F!1kKOu>(smmJVy?7L4zZ_IpPnQqO1 zt#M_nzNp&%!dq|@Y*Q3~({c*&WW2fTv)EciiV0tIOUJ1(YE z9XXBPBZLZZtwZ|5tc>9f(MK&8I+5O(2Dg%0dILaQ3DlqX5EpPfxfBKMBnbYpLT={<_G;3*oSpT{vC~uoEW+lfK824*=9;A}daNIylaV?tY7&ZWs^;oP@WCOPz zcwE(Hj1=HZo#rbV&G}&_Gw*FakQ1?qT>fs{009%uT37~6ZS5q{FOiP#u&y#>Ewz=2 zy-sr77mjYaAC6yj8PEoWl_)KEZM=jP3&R;w!&e*|eU5>>`R0SHoYai}53*h~MC|en z_5QP~sKF$iz<27gpd)9b@KG1sDGxnmM z?L2OqCC6cV@&n{vcDke96GCKD4!fl0vhvIfUvH?Mvy{n)FG`=_{IGj>M2N_zfazxX zDVg9hvzyY@*W3KqddvmvZ82NCz7pgQkJN=HCVI0^dyN(Ye{%(H_alblfHeCYqruS3 z9E@USsx9l*FlXr9G52YW@>|Ao4%I6U+C11FdR~u7ceW8VOWaUTWL|RZO*aF(n-GDy zri^vEgHmkx(q}fi8g|Fs!13;1`s%>z6A=;&;Y^WwTswDl=fSY`Q)Fl?baDT4o`{Hw z+#1+HXsfA~y4vT?hr7;{*<7XX{aM%=@lzjM(=Bbn_5pj5#<;|5dYk=$>N7Yf%#(>& zpS)UQkContYs6&hpuRUdx(uS$m5TTM+XX)iymj{%xwx+}IJ&^eH8~jB7#ORiC)UJ) zp?b{S`{-;3^QM%d7u~enw}qI+@wq6xD8`ARnKfzA$8s4q=mr9$qa1;Zp}!!O zFak~JgWDXXg+UMcclxjz`xv7n!*BVV#>+W!9@W1-Lm)9N{AhcJ4Z}c(LQ>pzzwfBA zFLK4#sbf-7S>w`z$`kpqiZyE|A(l zK+6F>x?-iFk)|nnW{D^qXOaWv+Kifto?|Q}vKv{4dK#U*IyZ*%x{B|KwnS)qf3?`n z97k{c&3FK72by*D_==vuMzu>C;V6(0nQZu!*RfH+-p_MO5~s867RrmycB86H5yxqp zkiW4a+}^hcl_k$0)Y$HSbU`R3$GB6u|4GJuD7$ZBy5dnnyzM(9YdLQorRWQ(VFSOJ zvl}Dwt{fn2y%K+}2aZ$m@hKUkxLZ*DX)$AwaQm_;Stu{)Os~PgIXTagt)|HuJPUD_ ze}p78K0AmD$IhN|e_1{?43*#EbO;mB!1_G@C~@1&B0Ujh*GwV6yMk&(vPsOL@bV%Y zP(Xwmb&Osa!2eKs&MY91;^fsOpvKs}^H;9Czq zw*30~`p710tP{U=^tr;jhr46nvwcg(R4WHQ_MkRWBt9HFlfJ7-wH&+l%k%KZ7=ens zo9}Zvj|Z*$-K}^|BA;dpsPX=O1c`z4-1oP$K?n&!wKsnfPW38?;WCusiz!9JK&7gI zl3s1CH??9leM1mi+0LNNl}o^S`~;RtsaP^Dk+Ps7EFkzL6>|EuR&9CW!hnH|QR1v5 z0M~6I%Ysl6AOyQX8D!V(Jf~+_{l-9WZI}i1CqrUjD$>(zNd<%q=sm@mWqV3936pOO z-L9r8xN&_e3-b(FHtDvrg|RzYto<33I;>#%GT=(Pn! zP>WsJQx%}GBIs5&Rv{ zr-tcjgm10yH{=5|PvoC-{jx=>!|=c)9<_6C! z=9&*fc|IHD*a<}UA!SzK{Dw;jY!bvMkGF>IPKi>VC==I&tj+0u!S-rWE zQ2bK&o7}1$sKVsAh{XB$u&e=}l%JmoQuRv5YYvo?-a32LnwluCD1Q`GHhm>cxALwp zomxVly0BqWPcZXX?kifv-Q0rAwd4aa@r*ly()9MaeN$hZ_+8;N@2xi+Q?HSF;=(dc z75ExMUkUV`6fhJda6n(UsgW~H0EDPLo)5uxXc(zD4PJqaypRDC=sV>79&eXp{%eA# zBcYN2eryC;rc=@zM!nX~d1B$U5o-Nh|IhQ4xj@ewe~8MsxS*sbxnjP_wSqTtQG=o) zYFtv`*TYLdZ4Dn#pv0v9CGR?;jpyMq|H$8oCoYulohy^)d0F7t_Id)3)^M<7&8!TH=3 zdDn&#zt=gc4Ho_2T@mjL3z7mwmWziG>qO0iFR~>2!t3>pm^SCDy&^04`u|FPY{sYz z;G*dLNd&}IwD|GFs1(SW-sC2JxOM0;FrTZhPgFYcGzK$C$w$V0#-V-?KQ@h@Y|@y; zqTWw;IM810{4hJ2dwE2O^FIE0nB?qGiCS?*;m3(u>mA7)JQL-Fw<$ZRCYyzSwtU?HSVQFE?_ zCa+5x?urs)m?@rO@g=d1r@#qZ9S6!bOU=xBWX!M{m|+_607l)i`ErLBMs@U;TfR(T z)oT{-i&KyJCZSS@vyPynY{ok31l;Vqp=mYz0IE*n7hypdAtwRtD4t2Mh}vmkT)qX- zeTTyxu{Lws+oKmwpeY7&(H7e_U5ci+FvWFLn;0MX>4oH#_1u^|&rS=L6JoNxU06p# zV%S999qRi$Y;!t#laLd`5&+X?{v8vSUNgytKw-kn`r9?mmj-B$a~K?1SML*av*?@; zW;w9w7p1Qb?d4=64$6UM>e0ToHyQ2Y!fp7-R4%aP ziaE}2fY^Rnltcnwb~x09On=Vd^gS+ewTT=_?Gh%g_H?g$I!OY7?)3`H>sP664r|t~ zgIk<>r>bJR=gu=Pa(1d>`KjwxJwmAwnVo+^19)?S+7W)bj<12?uy+kvKcLpFGySHEtD^yoh8ZsHMX-$`n`Eui>O*s{> zTfcQLnMq*8Bn98R0ikNJ*<&`q)0laI06GzA&uNyBo5M!udcC+w512M~>Sy|2qk$^cP>gAgBF#vHdoQNUK}*_eYJj~r{)YhPp-tHs~N zSuVW>T?&i!eI?aJ($1-@3h)kZkvI~#I+uOmWd}C(hid10e2Ar$b6P-qG0pO+c$8n3 z8#vIFK=ePoez|gp_wmiw;g3XZT6O4e^&eYvD}fFBOG7Uk1gVv^lze28D{;b8C!-vKf}PHf=&Jl2!|gHx;4!aRtL_RR)V z{0CjPI^1G3U5C65&xaQ{CKwgX_vx5eBe=13z}by*Wa_9XJshFzk|`owC04@JDzqng&&tV>Zd2nhI&oO2tyep+39ZGGxnR7JU*OJ9$d1#Q+->5B>5kpONK!=9+%pIfC2v;TUF?0L4tVu z6;-Guy?72%k!G1R2kBCSAO%@aE!^l7O8n?iYjS*9tdF>-T{GOsm+SXR(BdwzhcURT zG2cNu-*0qAi?ILWO3^j9PYQ|g4n^GisB`KJ?q@+)UvrL*18Nww3z+vst!pZ>z?GS5 z62B}_v)dODE_u83vRa{7yMf$zYX`^pl|=rOKGa7to}<|&Fb`;u!ZF8o_Hj$T@y#2W z(C0$}oEItfJou~#KO9i*eNjeRsx_GmNN}zDls_e72U%Bk2}zOoQ-(pWAhtUyCz(#{ zaSs|Hf0r`|>9<*6VRU1{zYp?dd*Hvu?%xr5-Yjg?w6k~sOSRXA#I|5ij4bFZ9PpUY zQa_>M_0Y8FE7t(_kPcrLN=Wt+Q^Lmn?h{N!twAU9+M0*VV4@0&zU(B$@1Slt*qKf$ z`vnV>=6mnag-?V@EiR+tWGgMjM?x)$CMD*?OpOK5zS>)q1sJw~Be{3_HN zhhmCxQpz7y!_XAvP?_QPF_EBZ9CCagqz?D|rC~fL=8VSd97}g4kcE`yfwuqc5$7j^ z0w9+TwQ@5)0!>-X3FUd#=~CQz&t{^xRErb-dJz_U(mQH-2WuWl5BuV^Y`=zLu%@ly zclzt)I>G*_{p+O~GuNzNqb6pCK&Txhf<^1y4&r6B;5&sFU%JGMK~TIXg1-ey=58+L!{urIf$l5eG8654#m$yta;JknK4HOaGwo zEp7pFb?q=M2PjA;@tE)zEE^Akri`gYAjG;KsIkTHsE+LA14@+l1)s2u)dC(iH zXHe)BI$fS28|^1ON|7b^xv$#n*c-(K?dc^bEH8MidC#f^uR4BxthFc~cmi5wYjtnJ zW-vxzk;59~eJ%9vzH0kqBJKl7ayLSS`SU=oW7-64@6dv04%jNiy=k(Ko*`%{yb!$4 zr9ynyl6qUXFUA^My!-qlw3%10-ysO1gd`eirsz)HodR?3K6=%W8vT%2GLpcpvZQ^c-tLoxd;g*P@?^9Z?iy8R)Kna-t68 zxtW(-3{&OBa49gaXnz~fSdf=oJbgD)URY+>wmqzr;1O9=o?LWhF65+}X07h?HJo&U za%aWbj(+d2+Q1F*aPm&Sf5j=B)RUGC-7Bl*nd1L$|GdLUhm+R;l*vtjWn})HlzzxC zt7%t1s)Ppyb{kK$4lJG@TCdro6+C5H*^A_lSp9F|;X-!EA5qa6Y>A@bCA#H9ay0oJmF>aS&A=C|aJPWWG+CX%NCq|OW#?qWOT!*Rg`X{4hsa{m4}|Mgfv{yhv3v@2D+?p};3 zN*iK|dc>uUnVhQ_hbVgKmJet@0kLNHG4?G>Yhmd;HUqUeyu0!2pGCYFG;sRSlZ9T5 z+PdUY7RhJ{zkYoV2fW;)WF7rzzXjP}vjW@zNhxM8X-q*uL52^NKEJ>xX!_CL+CKmB z)?Ycs52rC69_^HX(Kr}U842+Mkd$-9;dGxOV=nphar{vl8P`4__biV~SH8lIHW6`i zIFKR)oKU5)b~RPL``?Ki|05;`UH~6#k5~n?v}OcO%zUYoeMbMs0CfPXTiWIbcI@D|LVO_nz2)UY!3Yz6}GvS+pT0F+Sel^`Tj1 z?AYd?uk(^3H8P?-; zOaY31c6ODMa9lBaxSH<3nPon-e>Rc)yF$RF_>I7XA`xL?8tBHy$M0a%SAkzNs=zaZ z@CKi3Z5eAM)GG@}sguc|*~V{OOa}-E{vYgf3>dh)F7kZyQ;W&k;WQ=2A9;Yt8BqlT zUAbz}WJ5=Y=_5aI@Dx_PF zC}?#%*FsG?aDGq%qYgw3zdQ>whT4ZLgc{0^!qQ0^q^DUfC7gr%f61Lm0l711@uV~; zpXKLgcYhs}?h}16f8w6S??M=%?7%nS|9NB=Q25UlV={6#FT7LKdf>u)hoX(zCskjj zG)pf42LPh;VgbfOMIRGzO`c<;JV*nuJSAC<%<@ID)B%4 zdyMMehL_#%CkhMIAARK2$pK*dmMj2lFB_ar@KMp))lPl?*Z=nS*CgHlGQhYx0)^zx z@@AS|aH!5R`Hr^J`Zu%vnW+I(Z?(9L4|GQRry=xvvH;}CFR)d4!QI>17|(bk)*GJn z=KbN9FCZN**Qh1aLA6}0z&0S22moz7WyF6?m39dYw2dD$& zb~kNX(C1cewp`b`x<^ZIezuf%VzpD@0$cy?&lA|0ygr~!G&Mt7pO~2Vjc;%9yxwWq zzyK#0x`jC-W&BaT-|^1wW}MWK3u&F>(4jM%-(?2*Nh%`|yY)rvG2Eoml<|E%ZoYZV z)YhPU3Xz`4QpW#253U7~j~6`2QEF$LPp&1XqE={>W=_WzB?b@dcN1deBznIim+jn& zKr#D}FwaGeqaizVm~?zy9>?D3n{)Q;*;_v;G2u_)yfufhjMhVJaXa}IQfY;F3l7FP z!z&FRK*ap|gS9j|3Wj7c(b;a0!-Jdb?SR{dK4PCFJ={m?_tLM>cML>#)P3)vb@vZ* zf?qN->CCd7(_7O&?6KsZwRi&=M{$8Xm>sue8M~g#rA$xAUVNL%<}+8?MQ_!ZpJ>r1 z-l#F?kpsc@2{H_0Er|2Y3wqM$ug&12=Ui9m68C3HKC~dziY8KI{9|Rrh=7yVWWI;i zCr|#6ZLu20p88_4Ql8J^JS0+6<}ty-(rSQCjju7Sb9MNIJ>N3l^z1Tw`)oLbaRho& z$>g3dWn1)P+dA{xHjj!G&9U{J+zJ?4f?6@9OV&kpxMP(gNLd9RPTI}JxBYOO_sXj`LKbsn z_DdT!y5NM8Ue8yf;qj98*K^xXWnXeypXe0~Zw?sSjMU*RPxK!PRtMp6vlN(@mOZuq zV`lyLL6rXzcIwmKZZ-K7Enh*)YWDpPYNa}1Z*y;92=<;sv7kCwL3ixrr&AY`PEXAY zB50s7koVnt{uyJN>%t}Nn)VVTILPVCCIV7EQI@TW<%6yBkw}v<#T3cW{Ova~p(t{4 z{N5!Xmlr@9wl3{C2mO~oYN>Q(hDlloUy=6r@AnCnQnSR1=DLOG@u}&Dy4Pc^a;7?k z%?~xk4OZ%HQlp<2)%Ryhwre#oX3tn1wDdGJHGPOybZ{3>2PAU_F1^c>kr|`n!J!uk zW_m=lGnT4J+&NzB41K}lED0b+{$y*8tA=o zyKWT1twaZeChpNf!FFa3mTF}#KK$#+3X8*bU~I`rCF7HgI`~DWy^}namZL0F<#R0d ze0f=ToX*I!Jp4O zMcbX*oAhl%=erlmiu~Gdx<4gjK8<2XxrU!OE-sOaljUv2@>`lLMJ$-Eo{%YG5#jLk zn~mKs{VW-pQA<_0b9uKH*37ysdV6}O7hO58RK{OCSNI#EO#^tBjW5Wr`*F`(Dz{u!(mq@On4&q+_yg>7@y~=cBVrx3S=9 z>r3bRcvZKsy94$}bEqsL!j@_AmUYcGGLu&1lJMP^l4n|Dy5ySSS>V~G_hG|>p`#D zREt$3r-+N_&o)>SqQ`iu573GD=x}wGq&-%rMLV71;E%jEXUwZ@tedCH^kfj_n5;JV z^>bOaFRx9GSG;+uXGPG=bt*IctrTXv+$mqs6qTfJ_3 zuD`f(NooGHGEM!;_?qEHCz;_^VKABgea`5uW_p%m$fpvbf~BTZi9?xr##nwXMd+%J zD2VO`F+|X&+6LF_DKfLf*`={K>xKu7=FSQ-$KpEc6&@4uMJDM9ef6HMo`OWZ?P8dK z#io-gTon`+DjA(p(&WnK!WJGbbvBy8x>cw58b2dDlEIo>&AF-b1PpQx{rcoP%>nSz zHSMB7$97dw_z#IiyEC{2{eJ%5!|rJ~&=AYdr+FsTKFboqdM-_%1d==AKq8>o1zjq9 zIN(01aqP2k_t`l3ksdEfoPuLrd6lj|7)r-2$p8 zMTLQv4~-d?Q>)(V$McU+lSw|XD%rUTda9!2 zje7;I#>H?BTb<12JC*Odu6O$v*$LoWh(pl{Un3da#Nc*=A7~gv!@6CO+BsCq)M*24 z@eRBN5@IQ2stm=WsUG`(A)mEC9~adx`mkKRG2(N)4&ljzjP7jP?%xx9TI(+0-yx>S zJXSuPC&E6kFBSUsY43{!Qcz2mvXNfmZH~8@*r%{w7cxaRD$y;=ynyw7#N)(yEM|wX zHAEFR`NVIMF-V}X+nW~+Qa$^T4CeH#X2S_MPh}i(ZX5Bt(SE>g1i%HJO$Dqrb6C%f z^0Vx(R4J(h?>B|KjPpiD+yYnlupLg|av^jJ5MXS(7P&>H=94jY+y8X^bpnq5e0P&- zHo{>nY1F>4OZ*g<-O~7m)NAL$-H`Q&RLFMar%IWSMf%w&=f!E{SSzW@rwiM~L#MXB z+uOcXlgUVBXOgeA7fstdx6nvp**3zNR(WYY!{jMe#4x)koTu{HgC2G_|Bh(iWQG(i zgsbH2mWt%PV;?f&qMNAHi^)$?($Y$#-ano8C*^hBAL$pW-7~aTn=%%B#6xAi$Mj91 z;_7;}Vm6M=3Rum6f-#*8lpTCZcFrzd#NrXeDt7fYR#+;>wOYL{_iJl1Exq}gU4aeu!a2_(;st1nI(arZw8>eHJPShc-6*?Qcu zJWlicUIE&>U?sqhhmNhj&ZvGYm-CGpe?pJ(nsnRhLi^RMy4!!Q&_>~@Vl1&Hg{d7F z2x*i8^rX}Z=EMeHmW3A46uf+|gWHUZPI}e!*5~Ubne1k+e^{FAUg`YE`q2U1Ip(Q0 zxsm#Z=$_mUUq9_BvD_UnMisuaCm?f`Uw_qE#kj8zCr{!unFwp^ST6l~>hLfnW9h5z z$~ul#Nwt*bTC42ARaR+0#}83eff!8mWE6w4~KKb1O_ z+Wm0PXOm8F=S>;h8hPO`Q{Kq{z<@E-%tP$!O&h>|B%!4$-IwV&9=ABS16DX*Wpy`Q zn~;R+?ZN&NL`WRK_FXVloGaGRfwUfTxSQUWfN0KdzeE&=l{D%UX$yN356f+q*>~ULCON|EC_F0m z%hW>G+2OIDS%CQsk$6agGuXk+^YdYg1f6c7$mP&ji@CIso&IFDzV#zMdEla!-$68NIcrA9Dtqs)JqcbH(dMGAT3WSu#H;xDYrg%m zLAryJpY3`)&jJpR4>8oPe|_%0fbjDU<-&%(XMeBK>CChjA)oRgP{KJM5W*;l2OGmd ziFUV9jSo|(@M&>q)#B=Dl0I#DFB>mzBExk1X?kzd;vC@a1r^n|T9H^1dPjaP)II%$ zC`OE<%Q$sF>@S=WO~8?1;=5#TJAYz@j-Uu3U>Ae`RUkT!&h%&;*~ZY ztUSlU#3W5hBuB6Z*{)KXI4Lb;F51xad!4)J387v)Tip|_7a`etmZ!ptMvXysaUXN zYN@>}$ermZOm_9k6XYRc4_{p`I9FS! zng=*hlC-4I?OCLbRO8GeZVtxYS3C5)sfj|hhXWmt(GOxnBL9s^VEyWUZeTE9!F3Y(ROgxIuH1WxbD5b zg>IZ_s@8UW`fign`pUH>&o((d&e^)>BmyT^hq5wGN)gj>Z^Kv4-E4=Y`*JxQ9)e#9 zYd(9A>I!CdH%hW#=$NaaYbBS#^*^*Y2@HOrJ1X=DEhJ2PqLzQ zj>~nZdTA>vWXDs!&tC@>PMbibShYG*S8&3#`>KqAHa+y1__%gSh%nC0rt@9HIp}Vr z*6q~9I$9@cu2VoD1c6yZnDH7{#L+|;0^7E!XM3+P>Ye7H~TBGw^Uthf=s9s&w* zbwA3ywreMeFjI$ke!7wPRH8Ve^L@bl<+33cUk5O%AcW0yhw~7u7G`z*Zy9JJB&u|J z)(8Y7LFD>%fgdJ>!-W=YP|RB0n;7p2!FqZI8ycwf^TH^oZflDYTTX-~9HGSj&KT1 zXE}#bksQ4@h#KlejEc1L6Y1o_Qe2;)8j}fA-%T1C&(<`Sqp4OvP7l};sG*#Ud7>#p z-wPvFM#Gdk#zVlnnyhsX;tmc!>dQcQqm_A_vq@B#wDMKWBKc?gW;&XKzlK-=hn#7U z-Pq;LO6z8i1nQI1s9NvAn>HY+u&vzTQ4vMJnQTPLlv(%paU7+)QcbQ}So zdGH^gB3*KD6xAZ9;?VA3(X5Zyuy(~3OLD>~-N-QNfuzaa;y=wlO}gMBSsBo%6@3;n zFowTF0dH~>B6_3nViN!c_L|7W(DI(2!vj`jA7X1~Bev2~h^A0?EPm(*EUvsOTthv@ zfyKd|UWD6Vt*%~|)YGf!S|1|%{MGtu0R9lR3ExXl4iO#(+15J)8{PNP@EM{4^IABZ zwk1Zlr*jki5JPfPiz<`4p}6BgN-W{MPKE}}#f{RpWErtuzZe`y+sC^UkF-Gfl~H^Z zxJj4Y+~%3he=RaWLRuUZPHM5MIH(9f1c4-ku>L>$)BheAl+XUHc)LGnH+saoRWtiA z15inOA>uo=EOf$oL%nZE-$vj2jG?${AWPZ@te0*?fp_LYFPT1 zpC4s2FFu&WV7af6*r*+xW6im_#JZKNOYAVb>y1JB~2 z!JaQ#&-P@s)&Y zT>g~L4*%ul?0KG+!AT3(l@1R>C-xaBzmiAzifIvn(fKC->26G#d|ZUBJ1F^t=?XVm z!7+T8btT98zQ_O(<__Q1KHapg>*?0E7V=Cq?4E;!Dvw^!>n^g$ti!dfUc4QtHl{r& zt#V`@WDu-K-C z)zSnw&h4F!(I+y`@?OXZD4QC&}CoP6>>bp(IAjooDTS=n##Y zs+18gny%U7+q4XjC&aStju+I{+L?e|&uRbEVPSejsW(p(-4b1K(61Br;vWizUBF+Y zWao9~^3Y*BmHPL5i+ASbvWui`$C~8yG}8$oJ+womHHbqK7q6&^)~la{RBuv!1!pyw z#08em>Xvkmm4Hsvrxi;cp{pNnIkqD{t8#BFsL+gWmD2Cn9&hJz+!&G5zQPtruCN>4 z=u(9j)K$k4YWKby!QU~dbN3M$zqehGCNJ+qe0xc@CUXx-M2An1ylQDENc)P8Eob

MGWMZnaXNv8ME$y6y;VO6unU)ZT=PDRLbFIR?AeCC+)oW>$^ye` zNc3{t7we^RX+SUel5dxrdo(Br;E7+1uV_&54%4x#B(BFwkOkH%5%^ zx;`9CccgfH#<=56+tP&2rUlly6?7c^yxNx|$NT`<5)c9NC{w;7bUXJp)tahNm^-k^ zT9{j{4TovP%YF2BU7?B<0G4dWgb(%yUt1w`_HI8tBROzE7vL+aJGTa4db&%`&homN zY2YdJ_k_#i*R2E;UuPb?w@oqCwc)h?(1%)sBhiPqKOl{v5PiFAoOV0qZjjEL#^1u? zyU$Tea)6nP2pD*iR}NO=>&6aq%B}=tD9yB7n`tGct$SSm^J zC6Ck)N}m|tD}nT6G6jYV@-_-v7BbUDQ->9-If=$;E;TYI7I}ib2wNyLXFH4E*BFym zP{8u*tBC;)-}x4i-o7T5sw08hhrQ^c3L{j4vp7qKKHOeB`x&iPY6*jECpE4&92h7jBhm z@E`2eSxd^wf|n6<>b)n>@jez7i!?HlE1S^`h36z(6lt-&zF*BmRFo^0I*A zrY+m4;`M#SoYSJL*Z4h{nY7k^4!e)9@6@%kZt~g93o}1q(jH$Dy0v!WzpG{ODO$;M z%@d_>CxkNh7lhybHmm26mQ}~Y_3{B?bCR!K0^YHE{BUu6di4h0$a4Y<3w0OD#l+wH zCB|%5CgeBgw9MI#f9}^Uf1kU*^<}@{y5F(4(r2g$E0rz~%((sc>hsq(EWe7L6Ad%p zG2iC)oA*|qu8ZYiS!>W3pt_;{eqEzNpN0mo6V7fN!X0>5O+3)XENs`;Hxs*U_lUPD z-1RqoxIO#(v`0s~SEmU-4SoJ`U-`Z3^R^^aL;Xz6x5G!5O(PzdOffQ}`k;k;w*I7jKwYX5xBCD0oTS$F3mVZF_ROeI|smZ})cV z>1uH8tISpr+Bb87?Bll^(aNKK(W}PqHvtXl!)pXh zwcX}Yn567%tbSV9$ARPGwkAKTmzrrecQO6!xxXRuUBE$!na9`fw>5NUw3uM!yzzL8~FGg!}+#Ids`McuTEu@~iZ@u}dL4U_`iFs=U!dYAxn8ktjx{9ax zs=qvNK;>`Uf;N1oZ3Qq7msdAZ+CO}aP9i7 zFU*_N0{0#`d7*1%FWMKU;h>JII(_l z)QJEN`>E>~=3$%X6VYL`k-ZN(&v11@zlhb=`M|UhC(yn!KJUgxmxB4BEiaF>@humb z|J1LI_w+O!bGZlKV$9auPuUdu@W0HPh>!PQ>Et%@p1idGzwOH-AAsYqQ{QJuo2=62 zx2f{qcl~vw;u`MsR3103KlGpMKzi;P%rEj+dJ9*zB%NJrN)Oh6%#mGCNtxm31#4%^M(aD@2D@ePB_@;lS{*!Pc6OHcoqQ6 zNdhU)k6rdpv(5Iu-}E=1`c<>>96@JM$=TA9UICny<$2i3x%#O`)v1%-*7E0hBp(VK z(%oINW>zR3+a?_FbXUkos)&e~5nmxts`=)Z>yci3J}$TD`*lAvK z$1>i8Y=9q=qu}9%AKKg7y-O-oas%dk+3AB%%W?_Eh*Z#m9hoz4KKy6?#AyA3b&)~| P0}yz+`njwFE`0|8_v;<` literal 0 HcmV?d00001 From 1452fdd7410131a235799deaa2045326ef27d4cf Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Tue, 18 Jan 2022 17:50:04 -0500 Subject: [PATCH 4/6] Add support for config blocks parsing Signed-off-by: Jim Zhang --- internal/fabric/client/ledger.go | 2 - internal/fabric/utils/block.go | 23 +- internal/fabric/utils/blockdecoder.go | 86 +- internal/fabric/utils/blockdecoder_test.go | 24 +- internal/fabric/utils/rawblock.go | 47 +- .../{block-1.bin => chaincode-deploy.block} | Bin .../{block-1.json => chaincode-deploy.json} | 0 test/resources/config-0.block | Bin 0 -> 12902 bytes test/resources/config-0.json | 682 ++++++++++ test/resources/config-1.block | Bin 0 -> 31861 bytes test/resources/config-1.json | 1203 +++++++++++++++++ test/resources/genesis.block | Bin 9282 -> 0 bytes .../resources/{block-2.bin => tx-event.block} | Bin .../resources/{block-2.json => tx-event.json} | 0 14 files changed, 2016 insertions(+), 51 deletions(-) rename test/resources/{block-1.bin => chaincode-deploy.block} (100%) rename test/resources/{block-1.json => chaincode-deploy.json} (100%) create mode 100644 test/resources/config-0.block create mode 100644 test/resources/config-0.json create mode 100644 test/resources/config-1.block create mode 100644 test/resources/config-1.json delete mode 100644 test/resources/genesis.block rename test/resources/{block-2.bin => tx-event.block} (100%) rename test/resources/{block-2.json => tx-event.json} (100%) diff --git a/internal/fabric/client/ledger.go b/internal/fabric/client/ledger.go index 598b464..72e9331 100644 --- a/internal/fabric/client/ledger.go +++ b/internal/fabric/client/ledger.go @@ -17,8 +17,6 @@ package client import ( - //nolint - "github.com/hyperledger/fabric-sdk-go/pkg/client/ledger" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/core" diff --git a/internal/fabric/utils/block.go b/internal/fabric/utils/block.go index ad47262..391b854 100644 --- a/internal/fabric/utils/block.go +++ b/internal/fabric/utils/block.go @@ -16,14 +16,20 @@ package utils -import "github.com/hyperledger/fabric-protos-go/peer" +import ( + "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/peer" +) type Block struct { Number uint64 `json:"block_number"` - DataHash string `json:"data_hash"` // hex string - PreviousHash string `json:"previous_hash"` // hex string + DataHash string `json:"data_hash"` + PreviousHash string `json:"previous_hash"` - Transactions []*Transaction `json:"transactions"` + // only for endorser transaction blocks + Transactions []*Transaction `json:"transactions,omitempty"` + // only for Config blocks + Config *ConfigRecord `json:"config,omitempty"` } type Creator struct { @@ -51,3 +57,12 @@ type TransactionAction struct { ProposalHash string `json:"proposal_hash"` // hex string Event *ChaincodeEvent `json:"event"` } + +type ConfigRecord struct { + Type string `json:"type"` + Signature string `json:"signature"` + Timestamp int64 `json:"timestamp"` // unix nano + Nonce string `json:"nonce"` // hex string + Creator *Creator `json:"creator"` + Config *common.Config `json:"config"` +} diff --git a/internal/fabric/utils/blockdecoder.go b/internal/fabric/utils/blockdecoder.go index 64803eb..57deb4b 100644 --- a/internal/fabric/utils/blockdecoder.go +++ b/internal/fabric/utils/blockdecoder.go @@ -69,11 +69,10 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) { blockdata.Data = dataEnvs bloc := &Block{} - transactions := make([]*Transaction, len(block.Data.Data)) - bloc.Transactions = transactions // this array in the block header's metadata contains each transaction's status code txFilter := []byte(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER]) + var transactions []*Transaction for i, data := range block.Data.Data { env, err := getEnvelopeFromBlock(data) if err != nil { @@ -83,13 +82,24 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) { return nil, nil, errors.New("nil envelope") } - data, tx, err := rawblock.DecodeBlockDataEnvelope(env) + data, payload, err := rawblock.DecodeBlockDataEnvelope(env) if err != nil { return nil, nil, errors.Wrap(err, "error decoding block data envelope") } dataEnvs[i] = data - transactions[i] = tx - tx.Status = peer.TxValidationCode(txFilter[i]).String() + tx, ok := payload.(*Transaction) + if ok { + if transactions == nil { + transactions = make([]*Transaction, len(block.Data.Data)) + bloc.Transactions = transactions + } + transactions[i] = tx + tx.Status = peer.TxValidationCode(txFilter[i]).String() + } else { + // there should only be one config record in the block data + config := payload.(*ConfigRecord) + bloc.Config = config + } } bloc.Number = rawblock.Header.Number @@ -99,15 +109,11 @@ func DecodeBlock(block *common.Block) (*RawBlock, *Block, error) { return rawblock, bloc, nil } -func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockDataEnvelope, *Transaction, error) { +func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockDataEnvelope, interface{}, error) { // used for the raw block dataEnv := &BlockDataEnvelope{} dataEnv.Signature = base64.StdEncoding.EncodeToString(env.Signature) - // used in the user-friendly block - transaction := &Transaction{} - transaction.Signature = dataEnv.Signature - _payload := &Payload{} dataEnv.Payload = _payload @@ -116,47 +122,72 @@ func (block *RawBlock) DecodeBlockDataEnvelope(env *common.Envelope) (*BlockData return nil, nil, errors.Wrap(err, "error decoding Payload from envelope") } - err = block.decodePayload(payload, _payload, transaction) + result, err := block.decodePayload(payload, _payload) if err != nil { return nil, nil, err } - return dataEnv, transaction, nil + tx, ok := result.(*Transaction) + if ok { + tx.Signature = dataEnv.Signature + return dataEnv, tx, nil + } else { + config := result.(*ConfigRecord) + config.Signature = dataEnv.Signature + return dataEnv, config, nil + } } -func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload, _transaction *Transaction) error { +// based on the channel header type, returns a *Transaction (type=1) or a *ConfigRecord (type=3) +func (block *RawBlock) decodePayload(payload *common.Payload, _payload *Payload) (interface{}, error) { _payloadData := &PayloadData{} _payload.Data = _payloadData _payloadHeader := &PayloadHeader{} _payload.Header = _payloadHeader if err := block.decodePayloadHeader(payload.Header, _payloadHeader); err != nil { - return err + return nil, err } timestamp := _payloadHeader.ChannelHeader.Timestamp - _transaction.Type = _payloadHeader.ChannelHeader.Type - _transaction.Timestamp = timestamp creator := _payloadHeader.SignatureHeader.Creator - _transaction.Creator = &Creator{ + _creator := &Creator{ MspID: creator.Mspid, Cert: string(creator.IdBytes), } - _transaction.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce)) - _transaction.TxId = _payloadHeader.ChannelHeader.TxId - // TODO: support other block types (1=ConfigEvelope, 2=ConfigUpdateEnvelope) if _payloadHeader.ChannelHeader.Type == common.HeaderType_name[3] { + // used in the user-friendly block + _transaction := &Transaction{} + _transaction.Type = _payloadHeader.ChannelHeader.Type + _transaction.Timestamp = timestamp + _transaction.Creator = _creator + _transaction.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce)) + _transaction.TxId = _payloadHeader.ChannelHeader.TxId + if err := block.decodeTxPayloadData(payload.Data, _payloadData, _transaction); err != nil { - return err + return nil, err } for _, action := range _payloadData.Actions { if action.Payload.Action.ProposalResponsePayload.Extension.Events != nil { action.Payload.Action.ProposalResponsePayload.Extension.Events.Timestamp = strconv.FormatInt(timestamp, 10) } } + return _transaction, nil + } else if _payloadHeader.ChannelHeader.Type == common.HeaderType_name[1] { + _configRec := &ConfigRecord{} + _configRec.Type = _payloadHeader.ChannelHeader.Type + _configRec.Timestamp = timestamp + _configRec.Creator = _creator + _configRec.Nonce = hex.EncodeToString([]byte(_payloadHeader.SignatureHeader.Nonce)) + + if err := block.decodeConfigPayloadData(payload.Data, _payloadData, _configRec); err != nil { + return nil, err + } + + return _configRec, nil } - return nil + return nil, nil } func (block *RawBlock) decodePayloadHeader(header *common.Header, _header *PayloadHeader) error { @@ -357,6 +388,17 @@ func (block *RawBlock) decodeProposalResponsePayloadExtension(_extension *Extens return nil } +func (block *RawBlock) decodeConfigPayloadData(payloadData []byte, _payloadData *PayloadData, _configRec *ConfigRecord) error { + configEnv := &common.ConfigEnvelope{} + if err := proto.Unmarshal(payloadData, configEnv); err != nil { + return errors.Wrap(err, "error decoding config envelope") + } + _configRec.Config = configEnv.Config + _payloadData.Config = configEnv.Config + + return nil +} + func getEnvelopeFromBlock(data []byte) (*common.Envelope, error) { // Block always begins with an envelope var err error diff --git a/internal/fabric/utils/blockdecoder_test.go b/internal/fabric/utils/blockdecoder_test.go index 50eb67f..98fd9f6 100644 --- a/internal/fabric/utils/blockdecoder_test.go +++ b/internal/fabric/utils/blockdecoder_test.go @@ -27,7 +27,7 @@ import ( func TestDecodeEndorserBlockWithEvents(t *testing.T) { assert := assert.New(t) - content, _ := os.ReadFile("../../../test/resources/block-2.bin") + content, _ := os.ReadFile("../../../test/resources/tx-event.block") testblock := &common.Block{} _ = proto.Unmarshal(content, testblock) decoded, _, err := DecodeBlock(testblock) @@ -62,7 +62,7 @@ func TestDecodeEndorserBlockWithEvents(t *testing.T) { func TestDecodeEndorserBlockLifecycleTxs(t *testing.T) { assert := assert.New(t) - content, _ := os.ReadFile("../../../test/resources/block-1.bin") + content, _ := os.ReadFile("../../../test/resources/chaincode-deploy.block") testblock := &common.Block{} _ = proto.Unmarshal(content, testblock) decoded, _, err := DecodeBlock(testblock) @@ -86,9 +86,27 @@ func TestDecodeEndorserBlockLifecycleTxs(t *testing.T) { assert.Equal("ApproveChaincodeDefinitionForMyOrg", cpp.Input.ChaincodeSpec.Input.Args[0]) } +func TestDecodeConfigBlock(t *testing.T) { + assert := assert.New(t) + + content, _ := os.ReadFile("../../../test/resources/config-0.block") + testblock := &common.Block{} + _ = proto.Unmarshal(content, testblock) + decoded, _, err := DecodeBlock(testblock) + assert.NoError(err) + assert.Equal(1, len(decoded.Data.Data)) + + content, _ = os.ReadFile("../../../test/resources/config-1.block") + testblock = &common.Block{} + _ = proto.Unmarshal(content, testblock) + decoded, _, err = DecodeBlock(testblock) + assert.NoError(err) + assert.Equal(1, len(decoded.Data.Data)) +} + func TestGetEvents(t *testing.T) { assert := assert.New(t) - content, _ := os.ReadFile("../../../test/resources/block-2.bin") + content, _ := os.ReadFile("../../../test/resources/tx-event.block") testblock := &common.Block{} _ = proto.Unmarshal(content, testblock) events := GetEvents(testblock) diff --git a/internal/fabric/utils/rawblock.go b/internal/fabric/utils/rawblock.go index 8ba5fd6..7b11696 100644 --- a/internal/fabric/utils/rawblock.go +++ b/internal/fabric/utils/rawblock.go @@ -42,10 +42,36 @@ type Payload struct { Header *PayloadHeader `json:"header"` } +type PayloadHeader struct { + ChannelHeader *ChannelHeader `json:"channel_header"` + SignatureHeader *SignatureHeader `json:"signature_header"` +} + +type ChannelHeader struct { + ChannelId string `json:"channel_id"` + Epoch string `json:"epoch"` + Timestamp int64 `json:"timestamp"` + TxId string `json:"tx_id"` + Type string `json:"type"` + Version int `json:"version"` +} + +type SignatureHeader struct { + Creator *msp.SerializedIdentity `json:"creator"` + Nonce string `json:"nonce"` +} + type PayloadData struct { - Actions []*Action `json:"actions"` + // Actions only exists in endorsement transaction blocks + Actions []*Action `json:"actions,omitempty"` + // Config only exists in config and config update blocks + Config *common.Config `json:"config,omitempty"` } +// +// Types used only in endorsement transaction blocks +// + type Action struct { Header *SignatureHeader `json:"header"` Payload *ActionPayload `json:"payload"` @@ -100,22 +126,3 @@ type ChaincodeSpecInput struct { Args []string `json:"args"` IsInit bool `json:"is_init"` } - -type PayloadHeader struct { - ChannelHeader *ChannelHeader `json:"channel_header"` - SignatureHeader *SignatureHeader `json:"signature_header"` -} - -type ChannelHeader struct { - ChannelId string `json:"channel_id"` - Epoch string `json:"epoch"` - Timestamp int64 `json:"timestamp"` - TxId string `json:"tx_id"` - Type string `json:"type"` - Version int `json:"version"` -} - -type SignatureHeader struct { - Creator *msp.SerializedIdentity `json:"creator"` - Nonce string `json:"nonce"` -} diff --git a/test/resources/block-1.bin b/test/resources/chaincode-deploy.block similarity index 100% rename from test/resources/block-1.bin rename to test/resources/chaincode-deploy.block diff --git a/test/resources/block-1.json b/test/resources/chaincode-deploy.json similarity index 100% rename from test/resources/block-1.json rename to test/resources/chaincode-deploy.json diff --git a/test/resources/config-0.block b/test/resources/config-0.block new file mode 100644 index 0000000000000000000000000000000000000000..47c78a210a0cef612427c4f5e7f83bd044e2efe1 GIT binary patch literal 12902 zcmeI2-*4ks7027@Oeb@vY0KR~l)$p1s584enNA$1X;QQcT>psUq)ubU`N_k|u^q=v zVkfp!H<4BGqbRrS@FQ& zM0RBRo|AKb9Np{B_kw8T=3l?}cKImwe);$B{o+RE?)%@YzWmFF?d9KI`&(%xI->7t z@aG!%nFc=4z}w)h(Bk@HWI6PQU;XC(a&%48+sdftZK)l_vh?1aZxOAGrYr6Cj;bb- z+wDX`Z?{t^t-VX?O1hm&Ch;SECz&Lbq|(+iT8d1mX;GS=zOhLM{(QfCX3xcaeROZlwk}z-Glv-C^G1P;snw4!+;hWOjQ&)*3VZf z6IK+^Hir@wmh$~vrJr@1wVtIgXZLBt&y~rO``y-rm2nI>xTyv5{emV_Nwwdx)w-C_ zCdlMaW-Kr~K3KoVunB=tC;L^q!Ay8xm>x1Bum`!~7bilhb{f^Q#)NC}L{$GiCaNh= z0!NL9Tv^!OMFz{!^pFdKlPjOiA%aI#VbCnsGlMv{mnS(rE>wwZO;&|H%1mozd)TJ) zeWfqm!jBcXMy=3tPj z`H50()J@4p{Psbv7zRy4pm~vw=|w)a>of{xTav6)lN%M5Acej^-b%}{U9BJbO_P` zUbP>gsK_#!=7k$!z%|y=2DWK=E=*_@l(e@$t=>`^YO` z<)n;k_H}#Y!8LdVERnd8W=>uKFVTW2&BMTP_UE3v7ZFS{W!UvuApy?!p_> zYJkgC@+|@`%uD@v6@+PQs9WyHmB#}ezPk#)vkF4Gr)rMU_Tb;c;E!Q&>q1?z{@}w) zk>7nbD=bYgE7Z$ptZ>I73H6Xlb3-kac1r17JWVkFBdoA6C77R^71jl+GUJ!~m|vPr z&NyoIr9-uxAX+)WugOTJ4E2dt@S>wv~OV(;deY zE^lw|DpWmNQ3xNi!hQdQRZ0{i`hv~pTiotJqjqGsl10KWs|kV2cNHc(YI3_rdsfBQ zl6hOnWr<>49VwV0V^&z}bz9@qs9Beitz_O2kbS~qr!)w9)ZRq2_UGM!}MmtZ)w1h0Xsrwf4DWEzd0EhFY-PS+LZ%gojd^D
Wt4_^&*zCh;-biS$37wCL}_#KGf7ZSe% zo$pz8rv$pji>+$}I^VPH)(Z0B#mVlV^(gh#a374LDerfKU zn=?T81|+ewdl0LIXU?B6Tzuks?r28uA{P?vrkwG=!4RW&A8+KB;Y*lvu03{heI9c( z32y9}ZCxF!J^g&&do^onX_MDOGjT{|!_HyW-__H=Ie$h6b$;cacxGZ7u^z`~%3tXTw7wM~c Rh1bAka0x8n@8a~d@GsOe#mdj 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 literal 0 HcmV?d00001 diff --git a/test/resources/config-1.json b/test/resources/config-1.json new file mode 100644 index 0000000..9a6d027 --- /dev/null +++ b/test/resources/config-1.json @@ -0,0 +1,1203 @@ +{ + "data": { + "data": [ + { + "payload": { + "data": { + "config": { + "channel_group": { + "groups": { + "Application": { + "groups": { + "sys--mon": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Endorsement": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "PEER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + }, + { + "principal": { + "msp_identifier": "sys--mon", + "role": "PEER" + }, + "principal_classification": "ROLE" + }, + { + "principal": { + "msp_identifier": "sys--mon", + "role": "CLIENT" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + }, + { + "signed_by": 1 + }, + { + "signed_by": 2 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + }, + { + "principal": { + "msp_identifier": "sys--mon", + "role": "CLIENT" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + }, + { + "signed_by": 1 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + } + }, + "values": { + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA2" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "admin" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "client" + }, + "enable": true, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "orderer" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "peer" + } + }, + "intermediate_certs": [], + "name": "sys--mon", + "organizational_unit_identifiers": [], + "revocation_list": [], + "root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + ], + "signing_identity": null, + "tls_intermediate_certs": [], + "tls_root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Endorsement": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Endorsement" + } + }, + "version": "0" + }, + "LifecycleEndorsement": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Endorsement" + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + } + }, + "values": { + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V2_0": {} + } + }, + "version": "0" + } + }, + "version": "0" + }, + "Orderer": { + "groups": { + "sys--mon": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + } + }, + "values": { + "Endpoints": { + "mod_policy": "Admins", + "value": { + "addresses": [ + "u0bdh1ndoq.u0wrhhqt5w.kaleido.network:40020" + ] + }, + "version": "0" + }, + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA2" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "admin" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "client" + }, + "enable": true, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "orderer" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "organizational_unit_identifier": "peer" + } + }, + "intermediate_certs": [], + "name": "sys--mon", + "organizational_unit_identifiers": [], + "revocation_list": [], + "root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + ], + "signing_identity": null, + "tls_intermediate_certs": [], + "tls_root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDQVRDQ0FhZWdBd0lCQWdJUWYyeEsrQ0pXbFhBL29yazNOa3BOeWpBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVUTUJFR0ExVUVBd3dLZFRCM2NtaG9jWFExZHpCWk1CTUdCeXFHU000OUFnRUdDQ3FHDQpTTTQ5QXdFSEEwSUFCS3BaU1g4cC9HTEoyR2UvTVYwRldUY01MQmk3ZFNvcWZDSm1hV3puSkZwN2hzZlBtOTNKDQoxOExtbzBGQ3Urb2ZsS2U4VmZGZFpOSW83Z2MxWC9zVEVyK2pjekJ4TUJJR0ExVWRFd0VCL3dRSU1BWUJBZjhDDQpBUU13RGdZRFZSMFBBUUgvQkFRREFnR21NQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ3ZKL1BwRHJFcS9qRkZCTVF2NTlncmRqMllLZ3FTbks2bzNZNWlRRnluOWN3DQpDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdSbVJydW5uVFF5cUZYeHZUWTFkaG5acHA1V3cxUFd1azVUeEk0akhODQpaZ01DSVFDK2VOSSs5cllLaWZSUm41WkdxM0Z5SFN0bkF0blBSVnRXN0pBTVh2REJLdz09DQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + }, + "u0o4mkkzs6": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + } + }, + "values": { + "Endpoints": { + "mod_policy": "Admins", + "value": { + "addresses": [ + "u0u52tiobg.u0wrhhqt5w.kaleido.network:40040" + ] + }, + "version": "0" + }, + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA2" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "admin" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "client" + }, + "enable": true, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "orderer" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "peer" + } + }, + "intermediate_certs": [], + "name": "u0o4mkkzs6", + "organizational_unit_identifiers": [], + "revocation_list": [], + "root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + ], + "signing_identity": null, + "tls_intermediate_certs": [], + "tls_root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "sys--mon", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "BlockValidation": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + } + }, + "values": { + "BatchSize": { + "mod_policy": "Admins", + "value": { + "absolute_max_bytes": 103809024, + "max_message_count": 10, + "preferred_max_bytes": 524288 + }, + "version": "0" + }, + "BatchTimeout": { + "mod_policy": "Admins", + "value": { + "timeout": "2ns" + }, + "version": "0" + }, + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V2_0": {} + } + }, + "version": "0" + }, + "ChannelRestrictions": { + "mod_policy": "Admins", + "value": null, + "version": "0" + }, + "ConsensusType": { + "mod_policy": "Admins", + "value": { + "metadata": { + "consenters": [ + { + "client_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDUHpDQ0FlU2dBd0lCQWdJUWY2bjIwY3ZCN0dxZDU3clA3SC83MERBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQktNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVYTUJVR0ExVUVBd3dPZFRCaVpHZ3hibVJ2Y1MxMGJITXdXVEFUQmdjcWhrak9QUUlCDQpCZ2dxaGtqT1BRTUJCd05DQUFUUTJOTUlOeEpUcDhLeVJ6QlpZbkpUYUF3a1NZbFROYlZRdlpSelZzNVQrTEJLDQptUkNvU2ljcys2eTZXbVcwYlZJelpWZFJ6Z3VTRU5xTWhpOG9OQUl3bzRHck1JR29NQXdHQTFVZEV3RUIvd1FDDQpNQUF3RGdZRFZSMFBBUUgvQkFRREFnV2dNQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ25GRi9LVkkxcjhwMjdxaEhMMjEvN29hT0Joa3krYUdKbzQ5YUJYRlVhMHd3DQpPd1lEVlIwUkJEUXdNb0lKYkc5allXeG9iM04wZ2lWMU1HSmthREZ1Wkc5eExuVXdkM0pvYUhGME5YY3VhMkZzDQpaV2xrYnk1dVpYUjNiM0pyTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEdTBsa0VqUlZ4cXJlMGRhd1BJVEUzDQpYekNJVVVXTGx0bmhNYkQwb3JFMmhBSWhBSkRMMElXNmlZQ3RWRWlzNjFnQlYvUnRMY1FSMnFIOGh3OHdYc3RzDQpFelVuDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "host": "u0bdh1ndoq.u0wrhhqt5w.kaleido.network", + "port": 40021, + "server_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDUHpDQ0FlU2dBd0lCQWdJUWY2bjIwY3ZCN0dxZDU3clA3SC83MERBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQktNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVYTUJVR0ExVUVBd3dPZFRCaVpHZ3hibVJ2Y1MxMGJITXdXVEFUQmdjcWhrak9QUUlCDQpCZ2dxaGtqT1BRTUJCd05DQUFUUTJOTUlOeEpUcDhLeVJ6QlpZbkpUYUF3a1NZbFROYlZRdlpSelZzNVQrTEJLDQptUkNvU2ljcys2eTZXbVcwYlZJelpWZFJ6Z3VTRU5xTWhpOG9OQUl3bzRHck1JR29NQXdHQTFVZEV3RUIvd1FDDQpNQUF3RGdZRFZSMFBBUUgvQkFRREFnV2dNQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ25GRi9LVkkxcjhwMjdxaEhMMjEvN29hT0Joa3krYUdKbzQ5YUJYRlVhMHd3DQpPd1lEVlIwUkJEUXdNb0lKYkc5allXeG9iM04wZ2lWMU1HSmthREZ1Wkc5eExuVXdkM0pvYUhGME5YY3VhMkZzDQpaV2xrYnk1dVpYUjNiM0pyTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEdTBsa0VqUlZ4cXJlMGRhd1BJVEUzDQpYekNJVVVXTGx0bmhNYkQwb3JFMmhBSWhBSkRMMElXNmlZQ3RWRWlzNjFnQlYvUnRMY1FSMnFIOGh3OHdYc3RzDQpFelVuDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + }, + { + "client_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQXF5Z0F3SUJBZ0lVWmkyRGI2UnJJcjAxQXVTaW5hWHZRQXZEU3Uwd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpNakF4TURJeE5URXhNREJhTUZzeEN6QUpCZ05WQkFZVEFsVlRNUkF3RGdZRFZRUUhFd2RTWVd4bGFXZG9NUkF3CkRnWURWUVFLRXdkTFlXeGxhV1J2TVE4d0RRWURWUVFMRXdaamJHbGxiblF4RnpBVkJnTlZCQU1URG5Vd2RUVXkKZEdsdlltY3RkR3h6TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWVJlRi9OeVUxOUdIcHZocwp4dHlac0VtdTN0bklMVTl6MG94djhiZlBldDA4TUxCNk41enZrdUtwUmpFUlhJK2FtQ1d6ZTJ0NDVUemlMMGNHCnFtS3RncU9DQVl3d2dnR0lNQTRHQTFVZER3RUIvd1FFQXdJRHFEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhRNEVGZ1FVbmc4aVFZQUhtUmZBMGNWRApvMmRjSFZkYjRtMHdId1lEVlIwakJCZ3dGb0FVMlpKV0l2RFZFOEhHcDNiMkMzbUpXbmhicWU0d2dhVUdBMVVkCkVRU0JuVENCbW9JSmJHOWpZV3hvYjNOMGdpVjFNSFUxTW5ScGIySm5MblV3ZDNKb2FIRjBOWGN1YTJGc1pXbGsKYnk1dVpYUjNiM0pyZ2pOMU1IZHlhR2h4ZERWM0xYVXdkVFV5ZEdsdlltY3RiM0prWlhKbGNpNTFjekF0WVhkegpMWGR6TG10aGJHVnBaRzh1YVcrQ01YVXdkM0pvYUhGME5YY3RkVEIxTlRKMGFXOWlaeTFoWkcxcGJpNTFjekF0CllYZHpMWGR6TG10aGJHVnBaRzh1YVc4d1lRWUlLZ01FQlFZSENBRUVWWHNpWVhSMGNuTWlPbnNpYUdZdVFXWm0KYVd4cFlYUnBiMjRpT2lJaUxDSm9aaTVGYm5KdmJHeHRaVzUwU1VRaU9pSjFNSFUxTW5ScGIySm5MWFJzY3lJcwpJbWhtTGxSNWNHVWlPaUpqYkdsbGJuUWlmWDB3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9MRXZwVnpuK0NXCk5MQjJBWkRNM2Qxbk05b1hGSVJxbDUrdmtCOGk0SGRxQWlBeWpJMEw2N0V1R09VcWlpRkxlZ1JubEcxMkJoelMKc3RxdWNGcmpUQzRLL1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "host": "u0u52tiobg.u0wrhhqt5w.kaleido.network", + "port": 40041, + "server_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQXF5Z0F3SUJBZ0lVWmkyRGI2UnJJcjAxQXVTaW5hWHZRQXZEU3Uwd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpNakF4TURJeE5URXhNREJhTUZzeEN6QUpCZ05WQkFZVEFsVlRNUkF3RGdZRFZRUUhFd2RTWVd4bGFXZG9NUkF3CkRnWURWUVFLRXdkTFlXeGxhV1J2TVE4d0RRWURWUVFMRXdaamJHbGxiblF4RnpBVkJnTlZCQU1URG5Vd2RUVXkKZEdsdlltY3RkR3h6TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWVJlRi9OeVUxOUdIcHZocwp4dHlac0VtdTN0bklMVTl6MG94djhiZlBldDA4TUxCNk41enZrdUtwUmpFUlhJK2FtQ1d6ZTJ0NDVUemlMMGNHCnFtS3RncU9DQVl3d2dnR0lNQTRHQTFVZER3RUIvd1FFQXdJRHFEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhRNEVGZ1FVbmc4aVFZQUhtUmZBMGNWRApvMmRjSFZkYjRtMHdId1lEVlIwakJCZ3dGb0FVMlpKV0l2RFZFOEhHcDNiMkMzbUpXbmhicWU0d2dhVUdBMVVkCkVRU0JuVENCbW9JSmJHOWpZV3hvYjNOMGdpVjFNSFUxTW5ScGIySm5MblV3ZDNKb2FIRjBOWGN1YTJGc1pXbGsKYnk1dVpYUjNiM0pyZ2pOMU1IZHlhR2h4ZERWM0xYVXdkVFV5ZEdsdlltY3RiM0prWlhKbGNpNTFjekF0WVhkegpMWGR6TG10aGJHVnBaRzh1YVcrQ01YVXdkM0pvYUhGME5YY3RkVEIxTlRKMGFXOWlaeTFoWkcxcGJpNTFjekF0CllYZHpMWGR6TG10aGJHVnBaRzh1YVc4d1lRWUlLZ01FQlFZSENBRUVWWHNpWVhSMGNuTWlPbnNpYUdZdVFXWm0KYVd4cFlYUnBiMjRpT2lJaUxDSm9aaTVGYm5KdmJHeHRaVzUwU1VRaU9pSjFNSFUxTW5ScGIySm5MWFJzY3lJcwpJbWhtTGxSNWNHVWlPaUpqYkdsbGJuUWlmWDB3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9MRXZwVnpuK0NXCk5MQjJBWkRNM2Qxbk05b1hGSVJxbDUrdmtCOGk0SGRxQWlBeWpJMEw2N0V1R09VcWlpRkxlZ1JubEcxMkJoelMKc3RxdWNGcmpUQzRLL1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" + } + ], + "options": { + "election_tick": 10, + "heartbeat_tick": 1, + "max_inflight_blocks": 5, + "snapshot_interval_size": 16777216, + "tick_interval": "500ms" + } + }, + "state": "STATE_NORMAL", + "type": "etcdraft" + }, + "version": "1" + } + }, + "version": "1" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "MAJORITY", + "sub_policy": "Admins" + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Readers" + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 3, + "value": { + "rule": "ANY", + "sub_policy": "Writers" + } + }, + "version": "0" + } + }, + "values": { + "BlockDataHashingStructure": { + "mod_policy": "Admins", + "value": { + "width": 4294967295 + }, + "version": "0" + }, + "Capabilities": { + "mod_policy": "Admins", + "value": { + "capabilities": { + "V2_0": {} + } + }, + "version": "0" + }, + "HashingAlgorithm": { + "mod_policy": "Admins", + "value": { + "name": "SHA256" + }, + "version": "0" + } + }, + "version": "0" + }, + "sequence": "1" + }, + "last_update": { + "payload": { + "data": { + "config_update": { + "channel_id": "default-channel", + "isolated_data": {}, + "read_set": { + "groups": { + "Orderer": { + "groups": { + "sys--mon": { + "groups": {}, + "mod_policy": "", + "policies": {}, + "values": {}, + "version": "0" + } + }, + "mod_policy": "", + "policies": { + "Admins": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "BlockValidation": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "Readers": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "Writers": { + "mod_policy": "", + "policy": null, + "version": "0" + } + }, + "values": { + "BatchSize": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "BatchTimeout": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "Capabilities": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "ChannelRestrictions": { + "mod_policy": "", + "value": null, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "", + "policies": {}, + "values": {}, + "version": "0" + }, + "write_set": { + "groups": { + "Orderer": { + "groups": { + "sys--mon": { + "groups": {}, + "mod_policy": "", + "policies": {}, + "values": {}, + "version": "0" + }, + "u0o4mkkzs6": { + "groups": {}, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "ADMIN" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Readers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + }, + "Writers": { + "mod_policy": "Admins", + "policy": { + "type": 1, + "value": { + "identities": [ + { + "principal": { + "msp_identifier": "u0o4mkkzs6", + "role": "MEMBER" + }, + "principal_classification": "ROLE" + } + ], + "rule": { + "n_out_of": { + "n": 1, + "rules": [ + { + "signed_by": 0 + } + ] + } + }, + "version": 0 + } + }, + "version": "0" + } + }, + "values": { + "Endpoints": { + "mod_policy": "Admins", + "value": { + "addresses": [ + "u0u52tiobg.u0wrhhqt5w.kaleido.network:40040" + ] + }, + "version": "0" + }, + "MSP": { + "mod_policy": "Admins", + "value": { + "config": { + "admins": [], + "crypto_config": { + "identity_identifier_hash_function": "SHA256", + "signature_hash_family": "SHA2" + }, + "fabric_node_ous": { + "admin_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "admin" + }, + "client_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "client" + }, + "enable": true, + "orderer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "orderer" + }, + "peer_ou_identifier": { + "certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "organizational_unit_identifier": "peer" + } + }, + "intermediate_certs": [], + "name": "u0o4mkkzs6", + "organizational_unit_identifiers": [], + "revocation_list": [], + "root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + ], + "signing_identity": null, + "tls_intermediate_certs": [], + "tls_root_certs": [ + "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lVQThINTU5YVg4Q2VJVWdsSFJKTkV4dGVkS2xZd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpOakV5TXpFeE5UQTBNREJhTUJzeEdUQVhCZ05WQkFNVEVHWmhZbkpwWXkxallTMXpaWEoyWlhJd1dUQVRCZ2NxCmhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUmFTTWVOWlJhL0grbjZtMGZUZnhDS2VLdkFsRkJPQUtBWm5sQ0kKUlM5OWJZKzRLeGJYYW5kb1pTVE9DckljeldjTXN6dWlTd21wWVBvaGduZ0d1UXdqbzBVd1F6QU9CZ05WSFE4QgpBZjhFQkFNQ0FRWXdFZ1lEVlIwVEFRSC9CQWd3QmdFQi93SUJBREFkQmdOVkhRNEVGZ1FVMlpKV0l2RFZFOEhHCnAzYjJDM21KV25oYnFlNHdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTkFNQUJBNjIrdFhZZ3FCVEVuTUNkdEsKaFVVN2xoWUFDMGpSR05aVkNjTUxBaUF4UjYwWHJyWm5hdUt6ZHBaY05uZ05EaXJwWE5KbWw0Qk41Q3VOZS9KZQo2dz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + ] + }, + "type": 0 + }, + "version": "0" + } + }, + "version": "0" + } + }, + "mod_policy": "Admins", + "policies": { + "Admins": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "BlockValidation": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "Readers": { + "mod_policy": "", + "policy": null, + "version": "0" + }, + "Writers": { + "mod_policy": "", + "policy": null, + "version": "0" + } + }, + "values": { + "BatchSize": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "BatchTimeout": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "Capabilities": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "ChannelRestrictions": { + "mod_policy": "", + "value": null, + "version": "0" + }, + "ConsensusType": { + "mod_policy": "Admins", + "value": { + "metadata": { + "consenters": [ + { + "client_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDUHpDQ0FlU2dBd0lCQWdJUWY2bjIwY3ZCN0dxZDU3clA3SC83MERBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQktNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVYTUJVR0ExVUVBd3dPZFRCaVpHZ3hibVJ2Y1MxMGJITXdXVEFUQmdjcWhrak9QUUlCDQpCZ2dxaGtqT1BRTUJCd05DQUFUUTJOTUlOeEpUcDhLeVJ6QlpZbkpUYUF3a1NZbFROYlZRdlpSelZzNVQrTEJLDQptUkNvU2ljcys2eTZXbVcwYlZJelpWZFJ6Z3VTRU5xTWhpOG9OQUl3bzRHck1JR29NQXdHQTFVZEV3RUIvd1FDDQpNQUF3RGdZRFZSMFBBUUgvQkFRREFnV2dNQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ25GRi9LVkkxcjhwMjdxaEhMMjEvN29hT0Joa3krYUdKbzQ5YUJYRlVhMHd3DQpPd1lEVlIwUkJEUXdNb0lKYkc5allXeG9iM04wZ2lWMU1HSmthREZ1Wkc5eExuVXdkM0pvYUhGME5YY3VhMkZzDQpaV2xrYnk1dVpYUjNiM0pyTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEdTBsa0VqUlZ4cXJlMGRhd1BJVEUzDQpYekNJVVVXTGx0bmhNYkQwb3JFMmhBSWhBSkRMMElXNmlZQ3RWRWlzNjFnQlYvUnRMY1FSMnFIOGh3OHdYc3RzDQpFelVuDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=", + "host": "u0bdh1ndoq.u0wrhhqt5w.kaleido.network", + "port": 40021, + "server_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDUHpDQ0FlU2dBd0lCQWdJUWY2bjIwY3ZCN0dxZDU3clA3SC83MERBS0JnZ3Foa2pPUFFRREFqQkdNUXN3DQpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUDQpNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwDQpNVFV3TnpNNVdqQktNUXN3Q1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFDQpDZ3dIUzJGc1pXbGtiekVYTUJVR0ExVUVBd3dPZFRCaVpHZ3hibVJ2Y1MxMGJITXdXVEFUQmdjcWhrak9QUUlCDQpCZ2dxaGtqT1BRTUJCd05DQUFUUTJOTUlOeEpUcDhLeVJ6QlpZbkpUYUF3a1NZbFROYlZRdlpSelZzNVQrTEJLDQptUkNvU2ljcys2eTZXbVcwYlZJelpWZFJ6Z3VTRU5xTWhpOG9OQUl3bzRHck1JR29NQXdHQTFVZEV3RUIvd1FDDQpNQUF3RGdZRFZSMFBBUUgvQkFRREFnV2dNQ0FHQTFVZEpRRUIvd1FXTUJRR0NDc0dBUVVGQndNQ0JnZ3JCZ0VGDQpCUWNEQVRBcEJnTlZIUTRFSWdRZ25GRi9LVkkxcjhwMjdxaEhMMjEvN29hT0Joa3krYUdKbzQ5YUJYRlVhMHd3DQpPd1lEVlIwUkJEUXdNb0lKYkc5allXeG9iM04wZ2lWMU1HSmthREZ1Wkc5eExuVXdkM0pvYUhGME5YY3VhMkZzDQpaV2xrYnk1dVpYUjNiM0pyTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDSVFEdTBsa0VqUlZ4cXJlMGRhd1BJVEUzDQpYekNJVVVXTGx0bmhNYkQwb3JFMmhBSWhBSkRMMElXNmlZQ3RWRWlzNjFnQlYvUnRMY1FSMnFIOGh3OHdYc3RzDQpFelVuDQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tDQo=" + }, + { + "client_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQXF5Z0F3SUJBZ0lVWmkyRGI2UnJJcjAxQXVTaW5hWHZRQXZEU3Uwd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpNakF4TURJeE5URXhNREJhTUZzeEN6QUpCZ05WQkFZVEFsVlRNUkF3RGdZRFZRUUhFd2RTWVd4bGFXZG9NUkF3CkRnWURWUVFLRXdkTFlXeGxhV1J2TVE4d0RRWURWUVFMRXdaamJHbGxiblF4RnpBVkJnTlZCQU1URG5Vd2RUVXkKZEdsdlltY3RkR3h6TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWVJlRi9OeVUxOUdIcHZocwp4dHlac0VtdTN0bklMVTl6MG94djhiZlBldDA4TUxCNk41enZrdUtwUmpFUlhJK2FtQ1d6ZTJ0NDVUemlMMGNHCnFtS3RncU9DQVl3d2dnR0lNQTRHQTFVZER3RUIvd1FFQXdJRHFEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhRNEVGZ1FVbmc4aVFZQUhtUmZBMGNWRApvMmRjSFZkYjRtMHdId1lEVlIwakJCZ3dGb0FVMlpKV0l2RFZFOEhHcDNiMkMzbUpXbmhicWU0d2dhVUdBMVVkCkVRU0JuVENCbW9JSmJHOWpZV3hvYjNOMGdpVjFNSFUxTW5ScGIySm5MblV3ZDNKb2FIRjBOWGN1YTJGc1pXbGsKYnk1dVpYUjNiM0pyZ2pOMU1IZHlhR2h4ZERWM0xYVXdkVFV5ZEdsdlltY3RiM0prWlhKbGNpNTFjekF0WVhkegpMWGR6TG10aGJHVnBaRzh1YVcrQ01YVXdkM0pvYUhGME5YY3RkVEIxTlRKMGFXOWlaeTFoWkcxcGJpNTFjekF0CllYZHpMWGR6TG10aGJHVnBaRzh1YVc4d1lRWUlLZ01FQlFZSENBRUVWWHNpWVhSMGNuTWlPbnNpYUdZdVFXWm0KYVd4cFlYUnBiMjRpT2lJaUxDSm9aaTVGYm5KdmJHeHRaVzUwU1VRaU9pSjFNSFUxTW5ScGIySm5MWFJzY3lJcwpJbWhtTGxSNWNHVWlPaUpqYkdsbGJuUWlmWDB3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9MRXZwVnpuK0NXCk5MQjJBWkRNM2Qxbk05b1hGSVJxbDUrdmtCOGk0SGRxQWlBeWpJMEw2N0V1R09VcWlpRkxlZ1JubEcxMkJoelMKc3RxdWNGcmpUQzRLL1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "host": "u0u52tiobg.u0wrhhqt5w.kaleido.network", + "port": 40041, + "server_tls_cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCakNDQXF5Z0F3SUJBZ0lVWmkyRGI2UnJJcjAxQXVTaW5hWHZRQXZEU3Uwd0NnWUlLb1pJemowRUF3SXcKR3pFWk1CY0dBMVVFQXhNUVptRmljbWxqTFdOaExYTmxjblpsY2pBZUZ3MHlNakF4TURReE5UQTBNREJhRncwegpNakF4TURJeE5URXhNREJhTUZzeEN6QUpCZ05WQkFZVEFsVlRNUkF3RGdZRFZRUUhFd2RTWVd4bGFXZG9NUkF3CkRnWURWUVFLRXdkTFlXeGxhV1J2TVE4d0RRWURWUVFMRXdaamJHbGxiblF4RnpBVkJnTlZCQU1URG5Vd2RUVXkKZEdsdlltY3RkR3h6TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFWVJlRi9OeVUxOUdIcHZocwp4dHlac0VtdTN0bklMVTl6MG94djhiZlBldDA4TUxCNk41enZrdUtwUmpFUlhJK2FtQ1d6ZTJ0NDVUemlMMGNHCnFtS3RncU9DQVl3d2dnR0lNQTRHQTFVZER3RUIvd1FFQXdJRHFEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0RBWURWUjBUQVFIL0JBSXdBREFkQmdOVkhRNEVGZ1FVbmc4aVFZQUhtUmZBMGNWRApvMmRjSFZkYjRtMHdId1lEVlIwakJCZ3dGb0FVMlpKV0l2RFZFOEhHcDNiMkMzbUpXbmhicWU0d2dhVUdBMVVkCkVRU0JuVENCbW9JSmJHOWpZV3hvYjNOMGdpVjFNSFUxTW5ScGIySm5MblV3ZDNKb2FIRjBOWGN1YTJGc1pXbGsKYnk1dVpYUjNiM0pyZ2pOMU1IZHlhR2h4ZERWM0xYVXdkVFV5ZEdsdlltY3RiM0prWlhKbGNpNTFjekF0WVhkegpMWGR6TG10aGJHVnBaRzh1YVcrQ01YVXdkM0pvYUhGME5YY3RkVEIxTlRKMGFXOWlaeTFoWkcxcGJpNTFjekF0CllYZHpMWGR6TG10aGJHVnBaRzh1YVc4d1lRWUlLZ01FQlFZSENBRUVWWHNpWVhSMGNuTWlPbnNpYUdZdVFXWm0KYVd4cFlYUnBiMjRpT2lJaUxDSm9aaTVGYm5KdmJHeHRaVzUwU1VRaU9pSjFNSFUxTW5ScGIySm5MWFJzY3lJcwpJbWhtTGxSNWNHVWlPaUpqYkdsbGJuUWlmWDB3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQU9MRXZwVnpuK0NXCk5MQjJBWkRNM2Qxbk05b1hGSVJxbDUrdmtCOGk0SGRxQWlBeWpJMEw2N0V1R09VcWlpRkxlZ1JubEcxMkJoelMKc3RxdWNGcmpUQzRLL1E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" + } + ], + "options": { + "election_tick": 10, + "heartbeat_tick": 1, + "max_inflight_blocks": 5, + "snapshot_interval_size": 16777216, + "tick_interval": "500ms" + } + }, + "state": "STATE_NORMAL", + "type": "etcdraft" + }, + "version": "1" + } + }, + "version": "1" + } + }, + "mod_policy": "", + "policies": {}, + "values": {}, + "version": "0" + } + }, + "signatures": [ + { + "signature": "MEUCIQCWfK5N19SE1TWHCexRAUaVzA0nKw76dPoOCQMwNYd5/gIgSxgvvhOhQWoubUgq81df3il7QAFK+ZaVD2+vyM3c2GE=", + "signature_header": { + "creator": { + "id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNFVENDQWJlZ0F3SUJBZ0lRZiszdDJXVC9rdTdxQU9WTUQ4L0lTREFLQmdncWhrak9QUVFEQWpCR01Rc3cKQ1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFQ2d3SFMyRnNaV2xrYnpFVApNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EYzBNRm9ZRHpJd016SXdNVEEwCk1UVXdOelF3V2pCY01Rc3dDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUUKQ2d3SFMyRnNaV2xrYnpFT01Bd0dBMVVFQ3d3RllXUnRhVzR4R1RBWEJnTlZCQU1NRUhVd1ltUm9NVzVrYjNFdApZV1J0YVc0d1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUy9HZzYxdkw5cnhkdUNKd2dkTVI0ClZPbUJTMzVDSDhDeTRPdXFXZk82VU9ueWwyVmU5a3gvKzBkQWpVV0l6dkFSTmRaUzhJNGJ3S2tSZ0ZZbm4rYzkKbzIwd2F6QU1CZ05WSFJNQkFmOEVBakFBTUE0R0ExVWREd0VCL3dRRUF3SUZvREFnQmdOVkhTVUJBZjhFRmpBVQpCZ2dyQmdFRkJRY0RBZ1lJS3dZQkJRVUhBd0V3S1FZRFZSME9CQ0lFSU1MNVZEMzZRWk5maVF3SjlrVml0YWR6CjZHRzI2cTVUT2drbG5QQXFaQ2JUTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFEVEhpK0lXbnQyN3FONE5vWDMKa05QYjdlRkF4Mnk4REl3SjllT1BEcDNTaFFJZ1h2SWFoZ2xrVG50QnNYL3dleHA3ek5VeHI1KzU3RmQ4S2wvYwpZZDFvWmljPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "mspid": "sys--mon" + }, + "nonce": "c1EsuNQD1jha99UUn18TivaGynSJzR5g" + } + }, + { + "signature": "MEQCIFp47Absp+RBqd/kre4LhvmZ+MCSp9bFQZF1ohEpmQtyAiBpwUoDKXcUjOyk5UgwWtWbtETMWr3VdXMZLZkjK04A3A==", + "signature_header": { + "creator": { + "id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNFVENDQWJlZ0F3SUJBZ0lRZiszdDJXVC9rdTdxQU9WTUQ4L0lTREFLQmdncWhrak9QUVFEQWpCR01Rc3cKQ1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFQ2d3SFMyRnNaV2xrYnpFVApNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EYzBNRm9ZRHpJd016SXdNVEEwCk1UVXdOelF3V2pCY01Rc3dDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUUKQ2d3SFMyRnNaV2xrYnpFT01Bd0dBMVVFQ3d3RllXUnRhVzR4R1RBWEJnTlZCQU1NRUhVd1ltUm9NVzVrYjNFdApZV1J0YVc0d1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUy9HZzYxdkw5cnhkdUNKd2dkTVI0ClZPbUJTMzVDSDhDeTRPdXFXZk82VU9ueWwyVmU5a3gvKzBkQWpVV0l6dkFSTmRaUzhJNGJ3S2tSZ0ZZbm4rYzkKbzIwd2F6QU1CZ05WSFJNQkFmOEVBakFBTUE0R0ExVWREd0VCL3dRRUF3SUZvREFnQmdOVkhTVUJBZjhFRmpBVQpCZ2dyQmdFRkJRY0RBZ1lJS3dZQkJRVUhBd0V3S1FZRFZSME9CQ0lFSU1MNVZEMzZRWk5maVF3SjlrVml0YWR6CjZHRzI2cTVUT2drbG5QQXFaQ2JUTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFEVEhpK0lXbnQyN3FONE5vWDMKa05QYjdlRkF4Mnk4REl3SjllT1BEcDNTaFFJZ1h2SWFoZ2xrVG50QnNYL3dleHA3ek5VeHI1KzU3RmQ4S2wvYwpZZDFvWmljPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "mspid": "sys--mon" + }, + "nonce": "29DLTJCtS4Y9WlJTABkzKK2PfTZchb13" + } + } + ] + }, + "header": { + "channel_header": { + "channel_id": "default-channel", + "epoch": "0", + "extension": null, + "timestamp": "2022-01-04T15:11:14Z", + "tls_cert_hash": null, + "tx_id": "", + "type": 2, + "version": 0 + }, + "signature_header": { + "creator": { + "id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNFVENDQWJlZ0F3SUJBZ0lRZiszdDJXVC9rdTdxQU9WTUQ4L0lTREFLQmdncWhrak9QUVFEQWpCR01Rc3cKQ1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFQ2d3SFMyRnNaV2xrYnpFVApNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EYzBNRm9ZRHpJd016SXdNVEEwCk1UVXdOelF3V2pCY01Rc3dDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUUKQ2d3SFMyRnNaV2xrYnpFT01Bd0dBMVVFQ3d3RllXUnRhVzR4R1RBWEJnTlZCQU1NRUhVd1ltUm9NVzVrYjNFdApZV1J0YVc0d1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRUy9HZzYxdkw5cnhkdUNKd2dkTVI0ClZPbUJTMzVDSDhDeTRPdXFXZk82VU9ueWwyVmU5a3gvKzBkQWpVV0l6dkFSTmRaUzhJNGJ3S2tSZ0ZZbm4rYzkKbzIwd2F6QU1CZ05WSFJNQkFmOEVBakFBTUE0R0ExVWREd0VCL3dRRUF3SUZvREFnQmdOVkhTVUJBZjhFRmpBVQpCZ2dyQmdFRkJRY0RBZ1lJS3dZQkJRVUhBd0V3S1FZRFZSME9CQ0lFSU1MNVZEMzZRWk5maVF3SjlrVml0YWR6CjZHRzI2cTVUT2drbG5QQXFaQ2JUTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFEVEhpK0lXbnQyN3FONE5vWDMKa05QYjdlRkF4Mnk4REl3SjllT1BEcDNTaFFJZ1h2SWFoZ2xrVG50QnNYL3dleHA3ek5VeHI1KzU3RmQ4S2wvYwpZZDFvWmljPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==", + "mspid": "sys--mon" + }, + "nonce": "LSOcSaQjYSQ8CumQz6WUouWWUil9GVLV" + } + } + }, + "signature": "MEQCIHivmB1S4v4wHazD4/HYIt6jGY4NK9v2b6gTKBoTCM7QAiBJX56eg5cNFt0cD687lKyjUKyxgLGz60K3omXXyH3XSw==" + } + }, + "header": { + "channel_header": { + "channel_id": "default-channel", + "epoch": "0", + "extension": null, + "timestamp": "2022-01-04T15:11:14Z", + "tls_cert_hash": null, + "tx_id": "", + "type": 1, + "version": 0 + }, + "signature_header": { + "creator": { + "id_bytes": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNEVENDQWJPZ0F3SUJBZ0lRZm1xeTJIMVpLRkxUZXpKQWZqVUFnakFLQmdncWhrak9QUVFEQWpCR01Rc3cKQ1FZRFZRUUdFd0pWVXpFUU1BNEdBMVVFQnd3SFVtRnNaV2xuYURFUU1BNEdBMVVFQ2d3SFMyRnNaV2xrYnpFVApNQkVHQTFVRUF3d0tkVEIzY21ob2NYUTFkekFpR0E4eU1ESXlNREV3TkRFMU1EY3pPVm9ZRHpJd016SXdNVEEwCk1UVXdOek01V2pCWU1Rc3dDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUUKQ2d3SFMyRnNaV2xrYnpFUU1BNEdBMVVFQ3d3SGIzSmtaWEpsY2pFVE1CRUdBMVVFQXd3S2RUQmlaR2d4Ym1SdgpjVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCTk9GWHIwZXFYekFZM3BBc3Y0ZzZQNTExYTlnCkJtYzhRTkpaTmhzM3B5QlZ1VWZRbk15bVNjaE5PVmhzUkFCREtvOTlCM2c2aU5TUXZQY0VYSGZ2ZWJTamJUQnIKTUF3R0ExVWRFd0VCL3dRQ01BQXdEZ1lEVlIwUEFRSC9CQVFEQWdXZ01DQUdBMVVkSlFFQi93UVdNQlFHQ0NzRwpBUVVGQndNQ0JnZ3JCZ0VGQlFjREFUQXBCZ05WSFE0RUlnUWdENmQ1VkpjUzl3c2FGYWRDOFo3aXR5WnIyZk83CmNnM05sRW1wbzNuQm1jVXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTTdHSlVYeTdKbkg2djU3NC96aTFhbTgKWjMzRzFGUFFwUEs2eHUxR3hRN3VBaUJVTHFSNmkySml4N1JvMmJnT1UwK2NDVmxobnRwYVFFbkhQMTh4endtaQp6QT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K", + "mspid": "sys--mon" + }, + "nonce": "RJGs6S4bQnpDdjkbeof06PrZJaOMahr3" + } + } + }, + "signature": "MEUCIQCif9/luetvRL6qc7wTuVR4ofqkFqudT8duvvU33s7SDgIgPT7sWzmJ7Ch2YbrG4/K1ZXdyrWN3615pOq3v7Jcqz5A=" + } + ] + }, + "header": { + "data_hash": "qGyMqEK/tlGXtUHMLVR+aEDiaVscybAuWZZAg8Z4ipM=", + "number": "1", + "previous_hash": "apUUii+Y4Op8oDXllZeG6jXoQjfqFzUJeahrUauqT1E=" + }, + "metadata": { + "metadata": [ + "Cg4KAggBEggKBgoCAQIQAxL7BgqwBgqTBgoIc3lzLS1tb24ShgYtLS0tLUJFR0lOIENFUlRJRklDQVRFLS0tLS0KTUlJQ0RUQ0NBYk9nQXdJQkFnSVFmbXF5MkgxWktGTFRlekpBZmpVQWdqQUtCZ2dxaGtqT1BRUURBakJHTVFzdwpDUVlEVlFRR0V3SlZVekVRTUE0R0ExVUVCd3dIVW1Gc1pXbG5hREVRTUE0R0ExVUVDZ3dIUzJGc1pXbGtiekVUCk1CRUdBMVVFQXd3S2RUQjNjbWhvY1hRMWR6QWlHQTh5TURJeU1ERXdOREUxTURjek9Wb1lEekl3TXpJd01UQTAKTVRVd056TTVXakJZTVFzd0NRWURWUVFHRXdKVlV6RVFNQTRHQTFVRUJ3d0hVbUZzWldsbmFERVFNQTRHQTFVRQpDZ3dIUzJGc1pXbGtiekVRTUE0R0ExVUVDd3dIYjNKa1pYSmxjakVUTUJFR0ExVUVBd3dLZFRCaVpHZ3hibVJ2CmNUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJOT0ZYcjBlcVh6QVkzcEFzdjRnNlA1MTFhOWcKQm1jOFFOSlpOaHMzcHlCVnVVZlFuTXltU2NoTk9WaHNSQUJES285OUIzZzZpTlNRdlBjRVhIZnZlYlNqYlRCcgpNQXdHQTFVZEV3RUIvd1FDTUFBd0RnWURWUjBQQVFIL0JBUURBZ1dnTUNBR0ExVWRKUUVCL3dRV01CUUdDQ3NHCkFRVUZCd01DQmdnckJnRUZCUWNEQVRBcEJnTlZIUTRFSWdRZ0Q2ZDVWSmNTOXdzYUZhZEM4WjdpdHlacjJmTzcKY2czTmxFbXBvM25CbWNVd0NnWUlLb1pJemowRUF3SURTQUF3UlFJaEFNN0dKVVh5N0puSDZ2NTc0L3ppMWFtOApaMzNHMUZQUXBQSzZ4dTFHeFE3dUFpQlVMcVI2aTJKaXg3Um8yYmdPVTArY0NWbGhudHBhUUVuSFAxOHh6d21pCnpBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoSGPQGxnh+2sHppcnJtSzltxSCXnNniJqAzxJGMEQCIGLP71sv96yGs/IeUlPaUFsFxq28Y6P//b9KQA/AMVzjAiB1a8S7F7LIRQHU3GtOp9PuS1Sw0gbM5ADZwOWHl9wXNQ==", + "CgIIAQ==", + "AA==", + "", + "CiBH3FQMlM63BKI4dcESc+FrsLioeu2E3pEfITNWgRXyVA==" + ] + } +} diff --git a/test/resources/genesis.block b/test/resources/genesis.block deleted file mode 100644 index 02de111662fd9eccf203b1aeac9a2567e5e1b390..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9282 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 diff --git a/test/resources/block-2.bin b/test/resources/tx-event.block similarity index 100% rename from test/resources/block-2.bin rename to test/resources/tx-event.block diff --git a/test/resources/block-2.json b/test/resources/tx-event.json similarity index 100% rename from test/resources/block-2.json rename to test/resources/tx-event.json From 9e4af3764dee7526bcb3915f1ff8125bc39064a9 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 20 Jan 2022 17:23:08 -0500 Subject: [PATCH 5/6] Updated openapi spec for config block schema Signed-off-by: Jim Zhang --- openapi/spec.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/openapi/spec.yaml b/openapi/spec.yaml index a9c37e9..6a2af98 100644 --- a/openapi/spec.yaml +++ b/openapi/spec.yaml @@ -705,6 +705,22 @@ components: - type: string - type: object - type: array + config: + type: object + properties: + type: + type: string + signature: + type: string + timestamp: + type: integer + nonce: + type: string + creator: + $ref: "#/components/schemas/creator" + config: + type: object + description: the object defined by the Fabric protobuf type `common.Config` get_block_output: type: object properties: @@ -724,6 +740,8 @@ components: type: array items: $ref: "#/components/schemas/transaction" + config: + $ref: "#/components/schemas/config" raw: type: object description: the raw data structure defined by Fabric protobuf type `common.Block` From 4d675ce3cd0d9f1515aee1fe581b1e1d91d75ba7 Mon Sep 17 00:00:00 2001 From: Jim Zhang Date: Thu, 20 Jan 2022 17:26:50 -0500 Subject: [PATCH 6/6] Fix payload decoder to cover more payload types Signed-off-by: Jim Zhang --- internal/events/submanager_test.go | 2 +- internal/events/subscription.go | 6 +-- internal/utils/payload_decoder.go | 16 ++---- internal/utils/payload_decoder_test.go | 67 ++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 15 deletions(-) create mode 100644 internal/utils/payload_decoder_test.go diff --git a/internal/events/submanager_test.go b/internal/events/submanager_test.go index 0a00e82..0346b90 100644 --- a/internal/events/submanager_test.go +++ b/internal/events/submanager_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 Kaleido +// Copyright 2021 Kaleido // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/internal/events/subscription.go b/internal/events/subscription.go index 6496b0a..0e5c567 100644 --- a/internal/events/subscription.go +++ b/internal/events/subscription.go @@ -155,7 +155,7 @@ func (s *subscription) getEventTimestamp(evt *eventsapi.EventEntry) { blockNumber := strconv.FormatUint(evt.BlockNumber, 10) if ts, ok := s.ep.stream.blockTimestampCache.Get(blockNumber); ok { // we found the timestamp for the block in our local cache, assert it's type and return, no need to query the chain - timestamps := ts.(map[int]int64) + timestamps := ts.([]int64) evt.Timestamp = timestamps[evt.TransactionIndex] return } @@ -167,8 +167,8 @@ func (s *subscription) getEventTimestamp(evt *eventsapi.EventEntry) { return } // blocks in Fabric does not have a timestamp. instead only transactions have their own timestamps - // so each entry in the cache is a map of (tx index, tx timestamp) - timestamps := make(map[int]int64) + // so each entry in the cache is a slice of (tx timestamp) + timestamps := make([]int64, len(block.Transactions)) for idx, tx := range block.Transactions { timestamps[idx] = tx.Timestamp } diff --git a/internal/utils/payload_decoder.go b/internal/utils/payload_decoder.go index 916ad5d..a18a973 100644 --- a/internal/utils/payload_decoder.go +++ b/internal/utils/payload_decoder.go @@ -20,17 +20,11 @@ import "encoding/json" func DecodePayload(payload []byte) interface{} { // first attempt to parse for JSON, if not successful then just decode to string - var structuredArray []map[string]interface{} - err2 := json.Unmarshal(payload, &structuredArray) - if err2 != nil { - structuredMap := make(map[string]interface{}) - err2 = json.Unmarshal(payload, &structuredMap) - if err2 != nil { - return string(payload) - } else { - return structuredMap - } + var structured interface{} + err := json.Unmarshal(payload, &structured) + if err != nil { + return string(payload) } else { - return structuredArray + return structured } } diff --git a/internal/utils/payload_decoder_test.go b/internal/utils/payload_decoder_test.go new file mode 100644 index 0000000..5fc9bb0 --- /dev/null +++ b/internal/utils/payload_decoder_test.go @@ -0,0 +1,67 @@ +// Copyright 2022 Kaleido + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package utils + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDecodingArrayOfJson(t *testing.T) { + assert := assert.New(t) + + testBytes := []byte(`[{"a":1, "b":2}, {"a":10, "b":20}]`) + result := DecodePayload(testBytes) + m, ok := result.([]interface{}) + assert.Equal(true, ok) + n, ok := m[0].(map[string]interface{}) + assert.Equal(true, ok) + assert.Equal(float64(1), n["a"]) +} + +func TestDecodingJson(t *testing.T) { + assert := assert.New(t) + + testBytes := []byte(`{"a":1, "b":2}`) + result := DecodePayload(testBytes) + m, ok := result.(map[string]interface{}) + assert.Equal(true, ok) + assert.Equal(float64(1), m["a"]) +} + +func TestDecodingArrayOfStrings(t *testing.T) { + assert := assert.New(t) + + testBytes := []byte(`["string1", "string2"]`) + result := DecodePayload(testBytes) + m, ok := result.([]interface{}) + assert.Equal(true, ok) + n, ok := m[0].(string) + assert.Equal(true, ok) + assert.Equal("string1", n) +} + +func TestDecodingArrayOfNumbers(t *testing.T) { + assert := assert.New(t) + + testBytes := []byte(`[1, 2, 3]`) + result := DecodePayload(testBytes) + m, ok := result.([]interface{}) + assert.Equal(true, ok) + n, ok := m[0].(float64) + assert.Equal(true, ok) + assert.Equal(float64(1), n) +}