From fc030442957e29402c7175b526366a456d4a87d9 Mon Sep 17 00:00:00 2001 From: charliec Date: Thu, 9 Nov 2023 15:38:50 -0600 Subject: [PATCH] clean pending-forever cctxs and associated trackers --- cmd/zetaclientd/start.go | 2 +- docs/openapi/openapi.swagger.yaml | 39 + proto/crosschain/query.proto | 15 + x/crosschain/keeper/cctx_utils.go | 8 + x/crosschain/keeper/keeper_cross_chain_tx.go | 28 + x/crosschain/types/query.pb.go | 861 ++++++++++++++----- x/crosschain/types/query.pb.gw.go | 83 ++ zetaclient/cctx_scanner.go | 264 ++++++ zetaclient/cctx_scanner_test.go | 293 +++++++ zetaclient/evm_client.go | 47 +- zetaclient/query.go | 14 + zetaclient/types/sql_observer.go | 33 + zetaclient/zetacore_observer.go | 34 +- 13 files changed, 1503 insertions(+), 218 deletions(-) create mode 100644 zetaclient/cctx_scanner.go create mode 100644 zetaclient/cctx_scanner_test.go create mode 100644 zetaclient/types/sql_observer.go diff --git a/cmd/zetaclientd/start.go b/cmd/zetaclientd/start.go index 0e770b951f..b2af42ae83 100644 --- a/cmd/zetaclientd/start.go +++ b/cmd/zetaclientd/start.go @@ -237,7 +237,7 @@ func start(_ *cobra.Command, _ []string) error { } // CreateCoreObserver : Core observer wraps the zetacore bridge and adds the client and signer maps to it . This is the high level object used for CCTX interactions - mo1 := mc.NewCoreObserver(zetaBridge, signerMap, chainClientMap, metrics, tss, masterLogger, cfg, telemetryServer) + mo1 := mc.NewCoreObserver(zetaBridge, signerMap, chainClientMap, dbpath, metrics, tss, masterLogger, cfg, telemetryServer) mo1.MonitorCore() startLogger.Info().Msgf("awaiting the os.Interrupt, syscall.SIGTERM signals...") diff --git a/docs/openapi/openapi.swagger.yaml b/docs/openapi/openapi.swagger.yaml index ae13471986..c99139f2c9 100644 --- a/docs/openapi/openapi.swagger.yaml +++ b/docs/openapi/openapi.swagger.yaml @@ -26646,6 +26646,37 @@ paths: type: boolean tags: - Query + /zeta-chain/crosschain/cctxPendingInNonceRange: + get: + summary: Queries a list of pending send items in nonce range. + operationId: Query_CctxAllPendingInNonceRange + responses: + "200": + description: A successful response. + schema: + $ref: '#/definitions/crosschainQueryAllCctxPendingInNonceRangeResponse' + default: + description: An unexpected error response. + schema: + $ref: '#/definitions/googlerpcStatus' + parameters: + - name: chain_id + in: query + required: false + type: string + format: int64 + - name: nonce_low + in: query + required: false + type: string + format: uint64 + - name: nonce_high + in: query + required: false + type: string + format: uint64 + tags: + - Query /zeta-chain/crosschain/chainNonces: get: summary: Queries a list of chainNonces items. @@ -50615,6 +50646,14 @@ definitions: tss: type: string title: store key is tss+chainid + crosschainQueryAllCctxPendingInNonceRangeResponse: + type: object + properties: + cross_chain_tx: + type: array + items: + type: object + $ref: '#/definitions/crosschainCrossChainTx' crosschainQueryAllCctxPendingResponse: type: object properties: diff --git a/proto/crosschain/query.proto b/proto/crosschain/query.proto index 9f2c035a4c..4b24a35413 100644 --- a/proto/crosschain/query.proto +++ b/proto/crosschain/query.proto @@ -127,6 +127,11 @@ service Query { option (google.api.http).get = "/zeta-chain/crosschain/cctxPending"; } + // Queries a list of pending send items in nonce range. + rpc CctxAllPendingInNonceRange(QueryAllCctxPendingInNonceRangeRequest) returns (QueryAllCctxPendingInNonceRangeResponse) { + option (google.api.http).get = "/zeta-chain/crosschain/cctxPendingInNonceRange"; + } + // Queries a list of lastMetaHeight items. rpc LastZetaHeight(QueryLastZetaHeightRequest) returns (QueryLastZetaHeightResponse) { option (google.api.http).get = "/zeta-chain/crosschain/lastZetaHeight"; @@ -316,6 +321,16 @@ message QueryAllCctxPendingResponse { cosmos.base.query.v1beta1.PageResponse pagination = 2; } +message QueryAllCctxPendingInNonceRangeRequest { + int64 chain_id = 1; + uint64 nonce_low = 2; + uint64 nonce_high = 3; +} + +message QueryAllCctxPendingInNonceRangeResponse { + repeated CrossChainTx cross_chain_tx = 1; +} + message QueryLastZetaHeightRequest {} message QueryLastZetaHeightResponse { diff --git a/x/crosschain/keeper/cctx_utils.go b/x/crosschain/keeper/cctx_utils.go index d77d928017..e7c844b3be 100644 --- a/x/crosschain/keeper/cctx_utils.go +++ b/x/crosschain/keeper/cctx_utils.go @@ -86,3 +86,11 @@ func (k Keeper) RefundAmountOnZetaChain(ctx sdk.Context, cctx types.CrossChainTx return nil } + +func IsPending(cctx types.CrossChainTx) bool { + // pending inbound is not considered a "pending" state because it has not reached consensus yet + if cctx.CctxStatus.Status == types.CctxStatus_PendingOutbound || cctx.CctxStatus.Status == types.CctxStatus_PendingRevert { + return true + } + return false +} diff --git a/x/crosschain/keeper/keeper_cross_chain_tx.go b/x/crosschain/keeper/keeper_cross_chain_tx.go index a53c1159c4..6247c8ef49 100644 --- a/x/crosschain/keeper/keeper_cross_chain_tx.go +++ b/x/crosschain/keeper/keeper_cross_chain_tx.go @@ -215,6 +215,34 @@ func (k Keeper) CctxAllPending(c context.Context, req *types.QueryAllCctxPending return &types.QueryAllCctxPendingResponse{CrossChainTx: sends}, nil } +func (k Keeper) CctxAllPendingInNonceRange(c context.Context, req *types.QueryAllCctxPendingInNonceRangeRequest) (*types.QueryAllCctxPendingInNonceRangeResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + ctx := sdk.UnwrapSDKContext(c) + tss, found := k.GetTSS(ctx) + if !found { + return nil, status.Error(codes.Internal, "tss not found") + } + cctxs := make([]*types.CrossChainTx, 0) + + for i := req.NonceLow; i < req.NonceHigh; i++ { + res, found := k.GetNonceToCctx(ctx, tss.TssPubkey, req.ChainId, int64(i)) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("nonceToCctx not found: nonce %d, chainid %d", i, req.ChainId)) + } + cctx, found := k.GetCrossChainTx(ctx, res.CctxIndex) + if !found { + return nil, status.Error(codes.Internal, fmt.Sprintf("cctx not found: index %s", res.CctxIndex)) + } + if IsPending(cctx) { + cctxs = append(cctxs, &cctx) + } + } + + return &types.QueryAllCctxPendingInNonceRangeResponse{CrossChainTx: cctxs}, nil +} + func (k Keeper) CreateNewCCTX(ctx sdk.Context, msg *types.MsgVoteOnObservedInboundTx, index string, tssPubkey string, s types.CctxStatus, senderChain, receiverChain *common.Chain) types.CrossChainTx { if msg.TxOrigin == "" { msg.TxOrigin = msg.Sender diff --git a/x/crosschain/types/query.pb.go b/x/crosschain/types/query.pb.go index dfeacc4365..7f2e9b470a 100644 --- a/x/crosschain/types/query.pb.go +++ b/x/crosschain/types/query.pb.go @@ -1998,6 +1998,114 @@ func (m *QueryAllCctxPendingResponse) GetPagination() *query.PageResponse { return nil } +type QueryAllCctxPendingInNonceRangeRequest struct { + ChainId int64 `protobuf:"varint,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + NonceLow uint64 `protobuf:"varint,2,opt,name=nonce_low,json=nonceLow,proto3" json:"nonce_low,omitempty"` + NonceHigh uint64 `protobuf:"varint,3,opt,name=nonce_high,json=nonceHigh,proto3" json:"nonce_high,omitempty"` +} + +func (m *QueryAllCctxPendingInNonceRangeRequest) Reset() { + *m = QueryAllCctxPendingInNonceRangeRequest{} +} +func (m *QueryAllCctxPendingInNonceRangeRequest) String() string { return proto.CompactTextString(m) } +func (*QueryAllCctxPendingInNonceRangeRequest) ProtoMessage() {} +func (*QueryAllCctxPendingInNonceRangeRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{43} +} +func (m *QueryAllCctxPendingInNonceRangeRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllCctxPendingInNonceRangeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllCctxPendingInNonceRangeRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllCctxPendingInNonceRangeRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllCctxPendingInNonceRangeRequest.Merge(m, src) +} +func (m *QueryAllCctxPendingInNonceRangeRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryAllCctxPendingInNonceRangeRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllCctxPendingInNonceRangeRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllCctxPendingInNonceRangeRequest proto.InternalMessageInfo + +func (m *QueryAllCctxPendingInNonceRangeRequest) GetChainId() int64 { + if m != nil { + return m.ChainId + } + return 0 +} + +func (m *QueryAllCctxPendingInNonceRangeRequest) GetNonceLow() uint64 { + if m != nil { + return m.NonceLow + } + return 0 +} + +func (m *QueryAllCctxPendingInNonceRangeRequest) GetNonceHigh() uint64 { + if m != nil { + return m.NonceHigh + } + return 0 +} + +type QueryAllCctxPendingInNonceRangeResponse struct { + CrossChainTx []*CrossChainTx `protobuf:"bytes,1,rep,name=cross_chain_tx,json=crossChainTx,proto3" json:"cross_chain_tx,omitempty"` +} + +func (m *QueryAllCctxPendingInNonceRangeResponse) Reset() { + *m = QueryAllCctxPendingInNonceRangeResponse{} +} +func (m *QueryAllCctxPendingInNonceRangeResponse) String() string { return proto.CompactTextString(m) } +func (*QueryAllCctxPendingInNonceRangeResponse) ProtoMessage() {} +func (*QueryAllCctxPendingInNonceRangeResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_65a992045e92a606, []int{44} +} +func (m *QueryAllCctxPendingInNonceRangeResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryAllCctxPendingInNonceRangeResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryAllCctxPendingInNonceRangeResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryAllCctxPendingInNonceRangeResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryAllCctxPendingInNonceRangeResponse.Merge(m, src) +} +func (m *QueryAllCctxPendingInNonceRangeResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryAllCctxPendingInNonceRangeResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryAllCctxPendingInNonceRangeResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryAllCctxPendingInNonceRangeResponse proto.InternalMessageInfo + +func (m *QueryAllCctxPendingInNonceRangeResponse) GetCrossChainTx() []*CrossChainTx { + if m != nil { + return m.CrossChainTx + } + return nil +} + type QueryLastZetaHeightRequest struct { } @@ -2005,7 +2113,7 @@ func (m *QueryLastZetaHeightRequest) Reset() { *m = QueryLastZetaHeightR func (m *QueryLastZetaHeightRequest) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightRequest) ProtoMessage() {} func (*QueryLastZetaHeightRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{43} + return fileDescriptor_65a992045e92a606, []int{45} } func (m *QueryLastZetaHeightRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2042,7 +2150,7 @@ func (m *QueryLastZetaHeightResponse) Reset() { *m = QueryLastZetaHeight func (m *QueryLastZetaHeightResponse) String() string { return proto.CompactTextString(m) } func (*QueryLastZetaHeightResponse) ProtoMessage() {} func (*QueryLastZetaHeightResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{44} + return fileDescriptor_65a992045e92a606, []int{46} } func (m *QueryLastZetaHeightResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2087,7 +2195,7 @@ func (m *QueryConvertGasToZetaRequest) Reset() { *m = QueryConvertGasToZ func (m *QueryConvertGasToZetaRequest) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaRequest) ProtoMessage() {} func (*QueryConvertGasToZetaRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{45} + return fileDescriptor_65a992045e92a606, []int{47} } func (m *QueryConvertGasToZetaRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2140,7 +2248,7 @@ func (m *QueryConvertGasToZetaResponse) Reset() { *m = QueryConvertGasTo func (m *QueryConvertGasToZetaResponse) String() string { return proto.CompactTextString(m) } func (*QueryConvertGasToZetaResponse) ProtoMessage() {} func (*QueryConvertGasToZetaResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{46} + return fileDescriptor_65a992045e92a606, []int{48} } func (m *QueryConvertGasToZetaResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2197,7 +2305,7 @@ func (m *QueryMessagePassingProtocolFeeRequest) Reset() { *m = QueryMess func (m *QueryMessagePassingProtocolFeeRequest) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeRequest) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{47} + return fileDescriptor_65a992045e92a606, []int{49} } func (m *QueryMessagePassingProtocolFeeRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2236,7 +2344,7 @@ func (m *QueryMessagePassingProtocolFeeResponse) Reset() { func (m *QueryMessagePassingProtocolFeeResponse) String() string { return proto.CompactTextString(m) } func (*QueryMessagePassingProtocolFeeResponse) ProtoMessage() {} func (*QueryMessagePassingProtocolFeeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{48} + return fileDescriptor_65a992045e92a606, []int{50} } func (m *QueryMessagePassingProtocolFeeResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2280,7 +2388,7 @@ func (m *QueryZEVMGetTransactionReceiptRequest) Reset() { *m = QueryZEVM func (m *QueryZEVMGetTransactionReceiptRequest) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetTransactionReceiptRequest) ProtoMessage() {} func (*QueryZEVMGetTransactionReceiptRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{49} + return fileDescriptor_65a992045e92a606, []int{51} } func (m *QueryZEVMGetTransactionReceiptRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2337,7 +2445,7 @@ func (m *QueryZEVMGetTransactionReceiptResponse) Reset() { func (m *QueryZEVMGetTransactionReceiptResponse) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetTransactionReceiptResponse) ProtoMessage() {} func (*QueryZEVMGetTransactionReceiptResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{50} + return fileDescriptor_65a992045e92a606, []int{52} } func (m *QueryZEVMGetTransactionReceiptResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2467,7 +2575,7 @@ func (m *Log) Reset() { *m = Log{} } func (m *Log) String() string { return proto.CompactTextString(m) } func (*Log) ProtoMessage() {} func (*Log) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{51} + return fileDescriptor_65a992045e92a606, []int{53} } func (m *Log) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2567,7 +2675,7 @@ func (m *QueryZEVMGetTransactionRequest) Reset() { *m = QueryZEVMGetTran func (m *QueryZEVMGetTransactionRequest) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetTransactionRequest) ProtoMessage() {} func (*QueryZEVMGetTransactionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{52} + return fileDescriptor_65a992045e92a606, []int{54} } func (m *QueryZEVMGetTransactionRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2627,7 +2735,7 @@ func (m *QueryZEVMGetTransactionResponse) Reset() { *m = QueryZEVMGetTra func (m *QueryZEVMGetTransactionResponse) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetTransactionResponse) ProtoMessage() {} func (*QueryZEVMGetTransactionResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{53} + return fileDescriptor_65a992045e92a606, []int{55} } func (m *QueryZEVMGetTransactionResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2783,7 +2891,7 @@ func (m *QueryZEVMGetBlockByNumberRequest) Reset() { *m = QueryZEVMGetBl func (m *QueryZEVMGetBlockByNumberRequest) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetBlockByNumberRequest) ProtoMessage() {} func (*QueryZEVMGetBlockByNumberRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{54} + return fileDescriptor_65a992045e92a606, []int{56} } func (m *QueryZEVMGetBlockByNumberRequest) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2847,7 +2955,7 @@ func (m *QueryZEVMGetBlockByNumberResponse) Reset() { *m = QueryZEVMGetB func (m *QueryZEVMGetBlockByNumberResponse) String() string { return proto.CompactTextString(m) } func (*QueryZEVMGetBlockByNumberResponse) ProtoMessage() {} func (*QueryZEVMGetBlockByNumberResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_65a992045e92a606, []int{55} + return fileDescriptor_65a992045e92a606, []int{57} } func (m *QueryZEVMGetBlockByNumberResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3067,6 +3175,8 @@ func init() { proto.RegisterType((*QueryAllCctxResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxResponse") proto.RegisterType((*QueryAllCctxPendingRequest)(nil), "zetachain.zetacore.crosschain.QueryAllCctxPendingRequest") proto.RegisterType((*QueryAllCctxPendingResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxPendingResponse") + proto.RegisterType((*QueryAllCctxPendingInNonceRangeRequest)(nil), "zetachain.zetacore.crosschain.QueryAllCctxPendingInNonceRangeRequest") + proto.RegisterType((*QueryAllCctxPendingInNonceRangeResponse)(nil), "zetachain.zetacore.crosschain.QueryAllCctxPendingInNonceRangeResponse") proto.RegisterType((*QueryLastZetaHeightRequest)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightRequest") proto.RegisterType((*QueryLastZetaHeightResponse)(nil), "zetachain.zetacore.crosschain.QueryLastZetaHeightResponse") proto.RegisterType((*QueryConvertGasToZetaRequest)(nil), "zetachain.zetacore.crosschain.QueryConvertGasToZetaRequest") @@ -3085,197 +3195,203 @@ func init() { func init() { proto.RegisterFile("crosschain/query.proto", fileDescriptor_65a992045e92a606) } var fileDescriptor_65a992045e92a606 = []byte{ - // 3038 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5a, 0xdd, 0x6f, 0x1b, 0xc7, - 0x11, 0xf7, 0x89, 0xfa, 0x5c, 0x7d, 0x7a, 0xad, 0x38, 0x0c, 0x63, 0x8b, 0xce, 0x39, 0xfe, 0x88, - 0x3f, 0xc8, 0x58, 0xb1, 0x9d, 0xc4, 0x76, 0xd2, 0x48, 0x76, 0xac, 0x18, 0x51, 0x12, 0xf5, 0xa4, - 0xf4, 0xc3, 0x45, 0x4b, 0x9c, 0x8e, 0x6b, 0xea, 0xe0, 0x23, 0x8f, 0xb9, 0x5d, 0x0a, 0x52, 0x0c, - 0xb5, 0x40, 0xfe, 0x82, 0x00, 0x05, 0xda, 0x97, 0xbe, 0xf6, 0xe3, 0xa1, 0x0f, 0x01, 0x1a, 0x34, - 0x05, 0x0a, 0xa4, 0x0f, 0x6d, 0xd3, 0x3c, 0x06, 0x2d, 0x50, 0xb4, 0x28, 0x40, 0x14, 0x71, 0x9f, - 0xf8, 0x1f, 0x14, 0xe8, 0x43, 0xb1, 0x73, 0x73, 0xbc, 0x3d, 0xf2, 0x4e, 0x3c, 0x51, 0x6c, 0xd1, - 0xbe, 0x88, 0xbb, 0xb3, 0x3b, 0xb3, 0xbf, 0x99, 0x9d, 0xd9, 0x9d, 0xbd, 0x11, 0x39, 0x6e, 0x79, - 0x2e, 0xe7, 0xd6, 0x96, 0x69, 0xd7, 0x8a, 0xef, 0x35, 0x98, 0xb7, 0x5b, 0xa8, 0x7b, 0xae, 0x70, - 0xe9, 0xc9, 0xf7, 0x99, 0x30, 0x81, 0x5c, 0x80, 0x96, 0xeb, 0xb1, 0x42, 0x38, 0x35, 0x77, 0xc1, - 0x72, 0x79, 0xd5, 0xe5, 0xc5, 0x4d, 0x93, 0x33, 0x9f, 0xaf, 0xb8, 0x7d, 0x65, 0x93, 0x09, 0xf3, - 0x4a, 0xb1, 0x6e, 0x56, 0xec, 0x9a, 0x29, 0x6c, 0xb7, 0xe6, 0x8b, 0xca, 0x9d, 0x54, 0x96, 0x80, - 0xbf, 0xa5, 0x9a, 0x5b, 0xb3, 0x18, 0xc7, 0xe1, 0xbc, 0x3a, 0x2c, 0x9b, 0x25, 0x7f, 0x92, 0xd8, - 0xc1, 0x09, 0x39, 0x65, 0x42, 0xc5, 0xe4, 0xa5, 0xba, 0x67, 0x5b, 0x0c, 0xc7, 0x4e, 0x2b, 0x63, - 0xc0, 0x53, 0xda, 0x32, 0xf9, 0x56, 0x49, 0xb8, 0x25, 0xcb, 0x6a, 0x0b, 0xd0, 0x95, 0x49, 0x8e, - 0xc9, 0x45, 0x69, 0xd3, 0x71, 0xad, 0x87, 0xa5, 0x2d, 0x66, 0x57, 0xb6, 0x04, 0xce, 0x59, 0x50, - 0xe6, 0x00, 0xbc, 0x0e, 0x19, 0x2a, 0x4a, 0xb7, 0x21, 0xe4, 0x4a, 0xc2, 0x33, 0xad, 0x87, 0xcc, - 0xc3, 0x09, 0x4f, 0x2a, 0x13, 0xea, 0xa6, 0x67, 0x56, 0x03, 0xfd, 0xe6, 0x95, 0x01, 0xc1, 0xdb, - 0xd4, 0x8a, 0x5b, 0x71, 0xa1, 0x59, 0x94, 0x2d, 0xa4, 0x9e, 0xa8, 0xb8, 0x6e, 0xc5, 0x61, 0x45, - 0xb3, 0x6e, 0x17, 0xcd, 0x5a, 0xcd, 0x15, 0x60, 0x47, 0xe4, 0xd1, 0xb3, 0xe4, 0xf8, 0x57, 0xa5, - 0xa9, 0x37, 0x38, 0x7f, 0xc3, 0xe6, 0xc2, 0xf5, 0x76, 0x0d, 0xf6, 0x5e, 0x83, 0x71, 0xa1, 0x7f, - 0x87, 0x3c, 0xd9, 0x35, 0xc2, 0xeb, 0x6e, 0x8d, 0x33, 0x7a, 0x9b, 0x8c, 0x0b, 0xce, 0x4b, 0x8e, - 0xcd, 0x45, 0x56, 0x3b, 0x95, 0x39, 0x3f, 0xb9, 0xa8, 0x17, 0xf6, 0xdd, 0xdb, 0xc2, 0xc6, 0xfa, - 0xfa, 0xf2, 0xf0, 0xe7, 0xcd, 0xfc, 0x11, 0x63, 0x4c, 0x70, 0xbe, 0x6a, 0x73, 0xa1, 0xcf, 0x13, - 0x0a, 0xf2, 0xd7, 0x40, 0xb1, 0x60, 0xd5, 0xfb, 0xe4, 0x58, 0x84, 0xda, 0x5e, 0x71, 0xd4, 0x37, - 0x40, 0x56, 0x3b, 0xa5, 0x9d, 0x9f, 0x5c, 0x3c, 0xd3, 0x63, 0x3d, 0x9f, 0x1d, 0x97, 0x44, 0x56, - 0xfd, 0x2d, 0xf2, 0x34, 0xc8, 0x5e, 0x61, 0xe2, 0x9d, 0x86, 0xd8, 0xd8, 0xd9, 0xf0, 0x8d, 0x8d, - 0x4b, 0xd3, 0x2c, 0x19, 0x03, 0xe6, 0x7b, 0x77, 0x60, 0x91, 0x8c, 0x11, 0x74, 0xe9, 0x3c, 0x19, - 0x81, 0xfd, 0xcb, 0x0e, 0x9d, 0xd2, 0xce, 0x0f, 0x1b, 0x7e, 0x47, 0x6f, 0x90, 0x13, 0xf1, 0xe2, - 0x10, 0xf3, 0xbb, 0x64, 0xca, 0x55, 0xe8, 0x88, 0xfc, 0x62, 0x0f, 0xe4, 0xaa, 0x28, 0xc4, 0x1f, - 0x11, 0xa3, 0x33, 0xd4, 0x62, 0xc9, 0x71, 0xe2, 0xb4, 0xb8, 0x4b, 0x48, 0x18, 0x2d, 0xb8, 0xe6, - 0xd9, 0x82, 0x1f, 0x5a, 0x05, 0x19, 0x5a, 0x05, 0x3f, 0x24, 0x31, 0xb4, 0x0a, 0x6b, 0x66, 0x85, - 0x21, 0xaf, 0xa1, 0x70, 0xea, 0x9f, 0x6a, 0xa8, 0x5e, 0xd7, 0x3a, 0x89, 0xea, 0x65, 0x06, 0xa0, - 0x1e, 0x5d, 0x89, 0xe0, 0x1f, 0x02, 0xfc, 0xe7, 0x7a, 0xe2, 0xf7, 0x31, 0x45, 0x14, 0xf8, 0x40, - 0x23, 0x7a, 0x9c, 0x02, 0xcb, 0xbb, 0xb7, 0x25, 0x92, 0xc0, 0x5e, 0xf3, 0x64, 0x04, 0x90, 0xe1, - 0x9e, 0xfb, 0x9d, 0x0e, 0x2b, 0x0e, 0xf5, 0x6d, 0xc5, 0xdf, 0x6b, 0xe4, 0xf4, 0xbe, 0x20, 0xfe, - 0x4f, 0x8c, 0x79, 0x93, 0x9c, 0x0c, 0x7c, 0xfd, 0x5e, 0x6d, 0x63, 0xe7, 0x0d, 0x93, 0x6f, 0x6d, - 0xb8, 0xb7, 0x2d, 0xb1, 0x13, 0x98, 0x31, 0x47, 0xc6, 0x6d, 0x1c, 0x00, 0x4b, 0x4e, 0x18, 0xed, - 0xbe, 0xbe, 0x47, 0x16, 0x92, 0x98, 0x51, 0xfd, 0x6f, 0x91, 0x19, 0x3b, 0x32, 0x82, 0x8e, 0x7b, - 0xb9, 0x87, 0x01, 0xa2, 0xe2, 0xd0, 0x04, 0x1d, 0xa2, 0xf4, 0x5b, 0xb8, 0x7c, 0x74, 0xf2, 0x1d, - 0x53, 0x98, 0x69, 0xc0, 0xbf, 0x4f, 0xf2, 0x89, 0xdc, 0x88, 0xfe, 0xeb, 0x64, 0xfa, 0xb6, 0xc4, - 0x04, 0x5b, 0xba, 0xb1, 0xc3, 0x53, 0xee, 0x9e, 0xca, 0x83, 0xd0, 0xa3, 0x72, 0xf4, 0x0a, 0x5a, - 0x7d, 0xc9, 0x71, 0xe2, 0xad, 0x3e, 0xa8, 0x60, 0xff, 0x4c, 0x43, 0x1b, 0xc5, 0xac, 0xb4, 0xcf, - 0x16, 0x65, 0x06, 0xb4, 0x45, 0x83, 0xf4, 0xd3, 0xa7, 0x02, 0x57, 0xdb, 0xe0, 0x7c, 0xa9, 0x5c, - 0xf6, 0x18, 0x0f, 0xee, 0x16, 0xba, 0x40, 0x26, 0xe5, 0xb5, 0x55, 0x6f, 0x6c, 0x96, 0x1e, 0xb2, - 0x5d, 0xdc, 0xe9, 0x09, 0xc1, 0xf9, 0x5a, 0x63, 0xf3, 0x4d, 0xb6, 0xab, 0xbf, 0x46, 0x72, 0x71, - 0xcc, 0x68, 0x80, 0x39, 0x92, 0x61, 0x22, 0xf0, 0x0f, 0xd9, 0x94, 0x94, 0x4d, 0x61, 0x01, 0xdc, - 0x09, 0x43, 0x36, 0xdb, 0x77, 0x9a, 0x94, 0xb0, 0xbe, 0x1e, 0xdc, 0x69, 0x6f, 0xe2, 0x9d, 0x16, - 0x50, 0x51, 0xe0, 0x55, 0x92, 0xd9, 0x58, 0x5f, 0xc7, 0x5d, 0x4b, 0x71, 0x81, 0x1a, 0x72, 0xba, - 0x5e, 0xc4, 0x6b, 0x79, 0x85, 0x89, 0x15, 0x93, 0xaf, 0xc9, 0xbc, 0x45, 0x39, 0xca, 0xec, 0x5a, - 0x99, 0xed, 0x20, 0x46, 0xbf, 0xa3, 0x97, 0x48, 0xb6, 0x9b, 0x21, 0xbc, 0xc8, 0x03, 0x1a, 0xe2, - 0x38, 0xd7, 0x03, 0x47, 0x5b, 0x44, 0x9b, 0x51, 0x37, 0x11, 0xd1, 0x92, 0xe3, 0x74, 0x22, 0x1a, - 0x94, 0x7f, 0xfe, 0x4c, 0x43, 0x25, 0x22, 0x6b, 0xc4, 0x2a, 0x91, 0xe9, 0x4b, 0x89, 0xc1, 0x79, - 0xe0, 0x62, 0xe8, 0x44, 0x10, 0xc7, 0x6f, 0x43, 0x5e, 0xba, 0xff, 0x16, 0x3d, 0x0c, 0x13, 0x93, - 0x08, 0x0f, 0x2a, 0xb8, 0x4a, 0x26, 0x15, 0x32, 0x9a, 0xf1, 0x42, 0xaf, 0xd3, 0x45, 0x11, 0xa4, - 0xb2, 0xeb, 0x65, 0x04, 0xb8, 0xe4, 0x38, 0x31, 0x00, 0x07, 0xb5, 0x63, 0x1f, 0x6b, 0x61, 0x9a, - 0x92, 0x4a, 0xa7, 0xcc, 0x21, 0x74, 0x1a, 0xdc, 0xee, 0x2d, 0x84, 0x49, 0xcf, 0x1a, 0xab, 0x95, - 0xed, 0x5a, 0x25, 0x62, 0x1e, 0x5d, 0x84, 0x27, 0x72, 0xc7, 0x38, 0xea, 0xb5, 0x4e, 0x66, 0xea, - 0xfe, 0x00, 0xbe, 0x48, 0x50, 0xb5, 0x4b, 0xbd, 0x12, 0xd6, 0x88, 0xb4, 0xe9, 0xba, 0xda, 0xd5, - 0x5f, 0x21, 0xa7, 0xfc, 0xa4, 0x58, 0xa5, 0x76, 0xe4, 0x31, 0x4f, 0x91, 0x71, 0xff, 0x8d, 0x63, - 0x97, 0xa3, 0xe9, 0x6b, 0x59, 0xff, 0x2e, 0x79, 0x66, 0x1f, 0x76, 0x04, 0xfe, 0xcd, 0x18, 0xe0, - 0xda, 0x41, 0x81, 0x07, 0xd7, 0x58, 0x14, 0xfe, 0xf5, 0xf0, 0xfe, 0x5f, 0x35, 0xb9, 0x58, 0x96, - 0x2f, 0xa5, 0x37, 0xe0, 0xa1, 0xb4, 0x7f, 0x58, 0x3c, 0xc2, 0xab, 0x37, 0x8e, 0x0f, 0x51, 0x7f, - 0x83, 0xcc, 0x76, 0x0c, 0x21, 0xec, 0x42, 0x0f, 0xd8, 0x9d, 0x02, 0x3b, 0xc5, 0xe8, 0x5b, 0xe1, - 0x8d, 0x98, 0x00, 0x7a, 0x50, 0xa1, 0xf2, 0x3b, 0x0d, 0xf5, 0x8c, 0x5b, 0x6a, 0x3f, 0x3d, 0x33, - 0x03, 0xd0, 0x73, 0x70, 0xa1, 0x73, 0x31, 0xbc, 0xe5, 0xd4, 0x14, 0x25, 0x7e, 0x6b, 0x57, 0x95, - 0x53, 0x52, 0xa6, 0x05, 0xbb, 0xe0, 0x2a, 0xfd, 0xbe, 0xc4, 0x2a, 0x64, 0x3e, 0xba, 0x34, 0x5a, - 0xed, 0x1d, 0x32, 0xa5, 0x26, 0x54, 0x29, 0x5f, 0x60, 0x2a, 0x8b, 0x11, 0x11, 0xa0, 0x7f, 0x1b, - 0x75, 0x94, 0x87, 0xda, 0x7f, 0x20, 0x0d, 0xfb, 0x48, 0x43, 0x45, 0xda, 0xf2, 0x13, 0x15, 0xc9, - 0x1c, 0x4a, 0x91, 0xc1, 0xed, 0xfa, 0xf7, 0x94, 0xdb, 0xc4, 0x12, 0x3b, 0x78, 0x1a, 0xf4, 0x3e, - 0x94, 0x06, 0xf6, 0xc2, 0xfa, 0x44, 0xbd, 0x68, 0x54, 0x04, 0xff, 0xf3, 0xa6, 0x3b, 0x81, 0xa6, - 0x93, 0x11, 0x79, 0x9f, 0x09, 0x33, 0x72, 0xba, 0xe8, 0xd7, 0x50, 0xad, 0xce, 0x51, 0x54, 0xeb, - 0x38, 0x19, 0x55, 0xce, 0xbb, 0x8c, 0x81, 0x3d, 0x7d, 0x03, 0x2f, 0xb0, 0xdb, 0x6e, 0x6d, 0x9b, - 0x79, 0x32, 0xe3, 0xdb, 0x70, 0x25, 0x7b, 0x57, 0x68, 0x75, 0x6d, 0x48, 0x8e, 0x8c, 0x57, 0x4c, - 0xbe, 0x6a, 0x57, 0x6d, 0x81, 0x29, 0x6d, 0xbb, 0xaf, 0xff, 0x58, 0xc3, 0x7b, 0xaf, 0x5b, 0x2c, - 0xe2, 0xb9, 0x44, 0x8e, 0xba, 0x0d, 0xb1, 0xe9, 0x36, 0x6a, 0xe5, 0x15, 0x93, 0xdf, 0xab, 0xc9, - 0x41, 0x0c, 0xf9, 0xee, 0x01, 0x39, 0x1b, 0x3e, 0x3f, 0x59, 0xae, 0x73, 0x97, 0x31, 0x9c, 0xed, - 0x2f, 0xda, 0x3d, 0x40, 0xcf, 0x93, 0x59, 0xf9, 0xab, 0x1e, 0x7e, 0x19, 0x08, 0xff, 0x4e, 0xb2, - 0x7e, 0x8e, 0x9c, 0x01, 0x98, 0x6f, 0x31, 0xce, 0xcd, 0x0a, 0x5b, 0x33, 0x39, 0xb7, 0x6b, 0x95, - 0xb5, 0x50, 0x62, 0x60, 0xdd, 0xbb, 0xe4, 0x6c, 0xaf, 0x89, 0xa8, 0xd8, 0x09, 0x32, 0xf1, 0xa0, - 0x0d, 0x11, 0x9f, 0x0c, 0x6d, 0x82, 0x7e, 0x13, 0x17, 0xbc, 0xff, 0xfa, 0xd7, 0xde, 0x92, 0xe9, - 0xbd, 0x67, 0xd6, 0xb8, 0x69, 0xc9, 0xed, 0x35, 0x98, 0xc5, 0xec, 0x7a, 0xfb, 0xb2, 0xa0, 0x64, - 0x78, 0x2b, 0x7c, 0x5e, 0x42, 0x5b, 0xff, 0xd7, 0x30, 0xa2, 0xd8, 0x87, 0xbb, 0x6d, 0x5e, 0x82, - 0x1f, 0x18, 0xdb, 0x42, 0x96, 0xa7, 0x5b, 0xcd, 0xfc, 0x04, 0x50, 0xe5, 0x4b, 0xca, 0x08, 0x9b, - 0x74, 0x91, 0x4c, 0xf9, 0xb3, 0x6b, 0x8d, 0xea, 0x26, 0xf3, 0x7c, 0xcb, 0x2e, 0xcf, 0xb6, 0x9a, - 0xf9, 0x49, 0xa0, 0xbf, 0x0d, 0x64, 0x43, 0xed, 0xd0, 0x57, 0xc9, 0x9c, 0xe5, 0xd6, 0x84, 0x67, - 0x5a, 0xa2, 0x64, 0xfa, 0x4f, 0x1f, 0xb0, 0xf2, 0xc4, 0xf2, 0xb1, 0x56, 0x33, 0x3f, 0x1b, 0x8c, - 0x05, 0xaf, 0xa2, 0x4e, 0x02, 0x7d, 0x9d, 0x1c, 0xb3, 0x1a, 0xd5, 0x86, 0x63, 0x0a, 0x7b, 0x9b, - 0x95, 0x2a, 0x26, 0x2f, 0x35, 0x38, 0x2b, 0x67, 0x87, 0x41, 0xc4, 0x13, 0xad, 0x66, 0xfe, 0x68, - 0x38, 0xbc, 0x62, 0xf2, 0x77, 0x39, 0x2b, 0x1b, 0xdd, 0x24, 0x7a, 0x82, 0x0c, 0x3f, 0xf0, 0xdc, - 0x6a, 0x76, 0x04, 0xf8, 0xc6, 0x5b, 0xcd, 0x3c, 0xf4, 0x0d, 0xf8, 0x4b, 0xcf, 0x82, 0x8f, 0xfa, - 0x92, 0x47, 0x61, 0xc6, 0x64, 0xab, 0x99, 0x1f, 0xab, 0xa0, 0xbc, 0xa0, 0x21, 0xcd, 0xe5, 0xb8, - 0x15, 0x5e, 0xda, 0x74, 0x5c, 0xb7, 0x9a, 0x1d, 0x0b, 0xcd, 0x25, 0xa9, 0xcb, 0x92, 0x68, 0x84, - 0x4d, 0xaa, 0x93, 0x51, 0x2e, 0x4c, 0xd1, 0xe0, 0xd9, 0x71, 0x98, 0x49, 0x5a, 0xcd, 0x3c, 0x52, - 0x0c, 0xfc, 0xa5, 0xc7, 0xc9, 0x90, 0x70, 0xb3, 0x13, 0x30, 0x3e, 0xda, 0x6a, 0xe6, 0x87, 0x84, - 0x6b, 0x0c, 0x09, 0x57, 0x9a, 0x4d, 0x84, 0xdb, 0xe6, 0x6f, 0x0f, 0x09, 0xcd, 0xa6, 0x8c, 0xc1, - 0x26, 0x75, 0x12, 0xe8, 0x12, 0x39, 0xaa, 0xf2, 0xfb, 0x57, 0xe5, 0x24, 0x08, 0x98, 0x6f, 0x35, - 0xf3, 0xaa, 0xf0, 0x7b, 0x72, 0xcc, 0xe8, 0xa2, 0xd0, 0xeb, 0x64, 0x58, 0xea, 0x92, 0x9d, 0x4a, - 0xf5, 0x25, 0x76, 0xd5, 0xad, 0x18, 0x30, 0x5f, 0xff, 0x20, 0x43, 0x32, 0xab, 0x6e, 0x45, 0x1e, - 0x09, 0xc1, 0x86, 0xfb, 0xde, 0x19, 0x74, 0xe5, 0x21, 0x23, 0xdc, 0xba, 0x6d, 0xf1, 0xec, 0xd0, - 0xa9, 0xcc, 0xf9, 0x09, 0x03, 0x7b, 0xd2, 0x99, 0xcb, 0xa6, 0x30, 0x7d, 0xff, 0x30, 0xa0, 0xdd, - 0xe5, 0x73, 0x72, 0xe3, 0x87, 0x7b, 0xfb, 0x5c, 0x97, 0xf1, 0x46, 0x0e, 0x6b, 0xbc, 0x51, 0x58, - 0x38, 0xad, 0xf1, 0xa2, 0x81, 0x35, 0xd6, 0x23, 0xb0, 0x9e, 0x23, 0xd2, 0x6d, 0x70, 0xa1, 0x71, - 0x58, 0x68, 0xaa, 0xd5, 0xcc, 0x8f, 0x3b, 0x6e, 0xc5, 0x5f, 0xa0, 0xdd, 0xa2, 0x67, 0xc8, 0x98, - 0xc7, 0xaa, 0xee, 0x36, 0x2b, 0x83, 0xd7, 0x8c, 0xfb, 0x9e, 0x8a, 0x24, 0x23, 0x68, 0xe8, 0x57, - 0x31, 0xcd, 0x8c, 0x3b, 0x02, 0x92, 0x4f, 0x8e, 0x7f, 0x0e, 0x63, 0xca, 0x18, 0xc7, 0xf6, 0x5f, - 0x3b, 0x32, 0x82, 0x58, 0xcd, 0xc4, 0xc6, 0xea, 0x53, 0x24, 0x53, 0x31, 0x39, 0x1e, 0x00, 0x63, - 0xad, 0x66, 0x5e, 0x76, 0x0d, 0xf9, 0x47, 0x9a, 0xb1, 0x5d, 0x74, 0xc1, 0x0d, 0x07, 0x33, 0x56, - 0xda, 0xef, 0xf2, 0xa0, 0x25, 0xd7, 0x00, 0xfc, 0xa3, 0xe1, 0x1a, 0xb2, 0xef, 0xdb, 0x81, 0xe6, - 0x65, 0x72, 0x59, 0x6f, 0x08, 0xdc, 0xb8, 0x89, 0x56, 0x33, 0xef, 0x13, 0x0c, 0xff, 0x47, 0x4e, - 0xf0, 0xf3, 0xc5, 0xf1, 0x70, 0x02, 0x10, 0x30, 0x75, 0x4c, 0x8c, 0xeb, 0x58, 0xd7, 0x22, 0x07, - 0x8a, 0xcb, 0x3c, 0x19, 0xd9, 0x36, 0x9d, 0x06, 0xc3, 0x70, 0x86, 0xb5, 0x81, 0x60, 0xf8, 0x3f, - 0x52, 0x37, 0xb1, 0x5b, 0x67, 0xd9, 0xa9, 0x50, 0x37, 0xd9, 0x37, 0xe0, 0x2f, 0x2d, 0x92, 0x49, - 0xd3, 0xb2, 0x58, 0x50, 0x67, 0x99, 0x96, 0x11, 0xb8, 0x3c, 0xd3, 0x6a, 0xe6, 0x89, 0x4f, 0x5e, - 0xb5, 0x65, 0x26, 0x14, 0xb6, 0xe5, 0xe1, 0xd8, 0x4e, 0xb6, 0x66, 0xc2, 0xc3, 0x11, 0xef, 0xf7, - 0xf0, 0xa2, 0x3f, 0x46, 0xb4, 0xed, 0xec, 0x2c, 0x4c, 0x18, 0x69, 0x35, 0xf3, 0xda, 0xb6, 0xa1, - 0x6d, 0x4b, 0xa2, 0x97, 0x9d, 0x0b, 0x89, 0x9e, 0xa1, 0x79, 0x92, 0xc8, 0xb3, 0x47, 0x43, 0x22, - 0x37, 0x34, 0xae, 0xdf, 0xc0, 0xc7, 0x28, 0xba, 0x1e, 0x5c, 0xbf, 0xcb, 0xbb, 0xe8, 0x1f, 0xe8, - 0xb3, 0xc7, 0xc9, 0xe8, 0x56, 0x98, 0x9d, 0x0c, 0x1b, 0xd8, 0xd3, 0xff, 0x3a, 0x86, 0x4f, 0xd1, - 0x78, 0x66, 0xf4, 0x5c, 0x9d, 0x8c, 0xa2, 0x17, 0x6a, 0xe1, 0x79, 0xec, 0x53, 0x0c, 0xfc, 0x6d, - 0xfb, 0xc5, 0x50, 0xac, 0x5f, 0x14, 0xc9, 0x64, 0xdd, 0xf4, 0x58, 0x4d, 0xf8, 0xce, 0xef, 0x3b, - 0x28, 0xd8, 0xce, 0x27, 0x83, 0xf7, 0x2b, 0xed, 0xd0, 0x4f, 0x86, 0x13, 0xfc, 0xa4, 0x48, 0x26, - 0xf9, 0x96, 0xf9, 0x42, 0xa9, 0x51, 0xb3, 0x1c, 0xc6, 0xd1, 0x69, 0x41, 0xa2, 0x24, 0xbf, 0x0b, - 0x54, 0x43, 0x69, 0x77, 0x5c, 0x41, 0xa3, 0x3d, 0xae, 0xa0, 0xa8, 0xbb, 0xf1, 0x92, 0xe7, 0xba, - 0x81, 0x53, 0x77, 0xba, 0x1b, 0x37, 0x5c, 0x57, 0x18, 0x5d, 0x14, 0xb9, 0xa0, 0xbc, 0xab, 0x98, - 0xcf, 0x3b, 0x1e, 0x2e, 0x08, 0x54, 0x60, 0x0a, 0x9b, 0xf4, 0x1a, 0x99, 0xf6, 0xfc, 0x1c, 0x03, - 0x17, 0xf3, 0x43, 0x60, 0xae, 0xd5, 0xcc, 0x4f, 0x05, 0x03, 0xc0, 0x13, 0xe9, 0x49, 0x3b, 0x55, - 0xed, 0x1a, 0xf3, 0x30, 0x14, 0xc0, 0x4e, 0x40, 0x30, 0xfc, 0x1f, 0x5a, 0x20, 0xa4, 0x6c, 0x3f, - 0x78, 0x60, 0x5b, 0x0d, 0x47, 0xec, 0xa2, 0xe7, 0x83, 0x99, 0x42, 0xaa, 0xa1, 0xb4, 0xe1, 0x0a, - 0x70, 0x85, 0xe9, 0x94, 0x14, 0xae, 0x29, 0xe5, 0x0a, 0x90, 0x63, 0x77, 0x42, 0xd6, 0x4e, 0x82, - 0xd4, 0x9a, 0xed, 0x08, 0xcf, 0x2c, 0xc1, 0x85, 0x34, 0x1d, 0x6a, 0x0d, 0x54, 0xf8, 0x4c, 0x1f, - 0x36, 0xa5, 0xd7, 0x70, 0xfb, 0x7d, 0x86, 0xe1, 0x01, 0x5e, 0x23, 0xfb, 0x06, 0xfc, 0x0d, 0x8e, - 0x25, 0x07, 0x52, 0xe0, 0xd9, 0xc8, 0xb1, 0x04, 0x69, 0x70, 0x98, 0x10, 0x47, 0x12, 0x91, 0xb9, - 0x7d, 0x12, 0x91, 0x8b, 0x64, 0x42, 0xd8, 0x55, 0xc6, 0x85, 0x59, 0xad, 0x63, 0x24, 0x01, 0xba, - 0x36, 0xd1, 0x08, 0x9b, 0xf4, 0x2a, 0x99, 0x52, 0x77, 0x35, 0x4b, 0x21, 0xe4, 0x61, 0x4b, 0x22, - 0xbb, 0x1d, 0xe9, 0xc9, 0x68, 0x41, 0xa7, 0x3c, 0x06, 0xf3, 0x21, 0x5a, 0x7c, 0x8a, 0x81, 0xbf, - 0xf4, 0x06, 0x99, 0x93, 0x2f, 0x93, 0xd2, 0x03, 0xc6, 0x4a, 0x75, 0xe6, 0xc9, 0xf4, 0x2c, 0x3b, - 0x0f, 0x68, 0x8e, 0xb6, 0x9a, 0xf9, 0x69, 0x39, 0x76, 0x97, 0xb1, 0x35, 0xe6, 0xad, 0x98, 0xdc, - 0x88, 0x76, 0xa5, 0xaa, 0x55, 0xdb, 0xaf, 0x81, 0x67, 0x9f, 0x08, 0x55, 0xad, 0xda, 0xf0, 0x01, - 0xdf, 0x08, 0x1a, 0x8b, 0x1f, 0x3d, 0x4b, 0x46, 0x20, 0xb6, 0xe9, 0x0f, 0x34, 0x32, 0xea, 0x17, - 0x60, 0xe9, 0x95, 0x1e, 0xd9, 0x48, 0x77, 0x05, 0x38, 0xb7, 0x78, 0x10, 0x16, 0xff, 0xc4, 0xd0, - 0xcf, 0x7c, 0xf0, 0xa7, 0x7f, 0x7c, 0x7f, 0x28, 0x4f, 0x4f, 0x16, 0x25, 0xc7, 0x65, 0xa5, 0xf0, - 0xaf, 0x16, 0xcf, 0xe9, 0x67, 0x1a, 0x99, 0x52, 0x6b, 0x66, 0xf4, 0x46, 0x9a, 0xb5, 0xe2, 0xcb, - 0xc5, 0xb9, 0x9b, 0x7d, 0xf1, 0x22, 0xe0, 0x57, 0x00, 0xf0, 0x8b, 0xf4, 0x5a, 0x02, 0x60, 0xb5, - 0x8a, 0x57, 0x7c, 0x84, 0x5f, 0x3f, 0xf6, 0x8a, 0x8f, 0xe0, 0x30, 0xda, 0xa3, 0x9f, 0x68, 0x64, - 0x56, 0x95, 0xbb, 0xe4, 0x38, 0xe9, 0x74, 0x89, 0x2f, 0x1a, 0xa7, 0xd3, 0x25, 0xa1, 0x10, 0xac, - 0x5f, 0x04, 0x5d, 0xce, 0xd0, 0xd3, 0x29, 0x74, 0xa1, 0x7f, 0xd3, 0xc8, 0xf1, 0x0e, 0xe4, 0xf8, - 0x25, 0x92, 0x2e, 0xf5, 0x01, 0x22, 0xfa, 0x11, 0x34, 0xb7, 0x7c, 0x18, 0x11, 0xa8, 0xce, 0x0d, - 0x50, 0xe7, 0x2a, 0x5d, 0x4c, 0xa1, 0x0e, 0xf2, 0xe2, 0x0e, 0xed, 0xd1, 0x3f, 0x68, 0x64, 0x26, - 0x5a, 0xf0, 0xa2, 0xb7, 0x52, 0xba, 0x49, 0x6c, 0x81, 0x2f, 0xf7, 0x4a, 0x9f, 0xdc, 0xa8, 0xcb, - 0x4b, 0xa0, 0xcb, 0x22, 0x7d, 0x3e, 0x41, 0x97, 0x68, 0x19, 0xae, 0xf8, 0x28, 0xe8, 0xef, 0xd1, - 0x3f, 0x6b, 0x84, 0x76, 0x97, 0x3c, 0x69, 0x2a, 0x3c, 0x89, 0x85, 0xd6, 0xdc, 0xab, 0xfd, 0xb2, - 0xa3, 0x3e, 0x4b, 0xa0, 0xcf, 0x4d, 0xfa, 0x72, 0xa2, 0x3e, 0x9d, 0xff, 0xae, 0x03, 0xf7, 0x82, - 0xaa, 0xd8, 0x6f, 0x34, 0x72, 0x34, 0xba, 0x82, 0x0c, 0x9e, 0x5b, 0x29, 0x1d, 0xe7, 0x10, 0xbb, - 0x94, 0x58, 0x5a, 0xd5, 0x2f, 0x83, 0x56, 0xe7, 0xe8, 0x99, 0x54, 0xbb, 0x44, 0x3f, 0xd6, 0xc8, - 0x74, 0xa4, 0x44, 0x49, 0x5f, 0x4a, 0xe9, 0x25, 0x5d, 0x25, 0xd1, 0xdc, 0xcb, 0x7d, 0x70, 0x22, - 0xea, 0x02, 0xa0, 0x3e, 0x4f, 0xcf, 0x26, 0xa0, 0xae, 0x30, 0x51, 0x12, 0x9c, 0x07, 0x1f, 0x13, - 0xe8, 0x87, 0x1a, 0xd4, 0x3b, 0xd3, 0x5d, 0x09, 0x91, 0x02, 0x6a, 0xba, 0x2b, 0x21, 0x5a, 0x5d, - 0xd5, 0x75, 0x80, 0x77, 0x82, 0xe6, 0x12, 0xe0, 0x49, 0x28, 0x3f, 0xd7, 0xc2, 0xd2, 0x21, 0xbd, - 0x9e, 0x72, 0x91, 0x8e, 0x1a, 0x67, 0xee, 0xc5, 0x03, 0xf3, 0x21, 0xc2, 0x22, 0x20, 0x7c, 0x8e, - 0x9e, 0x4b, 0x32, 0x20, 0x32, 0x48, 0xef, 0x2d, 0xb3, 0x9d, 0x3d, 0xfa, 0x53, 0x8d, 0x4c, 0x06, - 0x52, 0xa4, 0xd3, 0x5e, 0x4f, 0xe9, 0x76, 0x7d, 0x21, 0x8e, 0xa9, 0xb4, 0xea, 0xe7, 0x00, 0xf1, - 0x33, 0x34, 0xdf, 0x03, 0x31, 0xfd, 0x54, 0x23, 0x73, 0x9d, 0x9f, 0x0a, 0x69, 0xaa, 0x4b, 0x26, - 0xe1, 0xbb, 0x65, 0xee, 0x56, 0x7f, 0xcc, 0x29, 0x4d, 0x6d, 0x75, 0x62, 0xfd, 0x4c, 0x23, 0x93, - 0xca, 0xd7, 0x40, 0x7a, 0x27, 0xcd, 0xf2, 0xbd, 0xbe, 0x3a, 0xe6, 0x5e, 0x3f, 0xa4, 0x14, 0xd4, - 0xe6, 0x02, 0x68, 0xf3, 0x2c, 0xd5, 0x93, 0xb2, 0x1d, 0x05, 0xf8, 0xaf, 0xb4, 0x48, 0xa1, 0x95, - 0xa6, 0x0d, 0xf8, 0xee, 0xd2, 0x70, 0xee, 0x46, 0x3f, 0xac, 0x08, 0x79, 0x11, 0x20, 0x5f, 0xa2, - 0x17, 0x92, 0x36, 0x20, 0xe4, 0x69, 0xbb, 0xfb, 0x2f, 0x34, 0x32, 0xa3, 0xc8, 0x92, 0x1e, 0xff, - 0x72, 0x4a, 0xcf, 0xed, 0x17, 0x7d, 0x7c, 0xb1, 0xba, 0xa7, 0xc1, 0x15, 0xf4, 0xf4, 0xd7, 0x1a, - 0x99, 0x8b, 0xd4, 0x44, 0x25, 0xee, 0xb4, 0xf9, 0x55, 0x5c, 0xcd, 0x39, 0x77, 0xab, 0x3f, 0x66, - 0xc4, 0x7e, 0x09, 0xb0, 0x9f, 0xa5, 0xcf, 0x26, 0x39, 0x8b, 0xca, 0x45, 0xff, 0xa8, 0x91, 0xf9, - 0xb8, 0x32, 0x31, 0xfd, 0x4a, 0xaa, 0xac, 0x3c, 0xb9, 0x3e, 0x9d, 0x7b, 0xad, 0x7f, 0x01, 0xa8, - 0xc9, 0x8b, 0xa0, 0xc9, 0x15, 0x5a, 0x4c, 0xa3, 0x09, 0xa6, 0x64, 0x25, 0xbb, 0xbc, 0x47, 0x3f, - 0xd7, 0xba, 0xaa, 0xa7, 0x34, 0x6d, 0x62, 0x15, 0x5f, 0xfb, 0x4d, 0x97, 0xc8, 0x24, 0xd7, 0xad, - 0xf5, 0xeb, 0xa0, 0xcb, 0xf3, 0xb4, 0x90, 0xa0, 0x8b, 0x13, 0xe5, 0x6b, 0xc7, 0xc4, 0x6f, 0x35, - 0x42, 0x3b, 0x64, 0x4a, 0xff, 0x4a, 0x9b, 0x80, 0x1c, 0x46, 0x9b, 0xe4, 0xea, 0x74, 0xcf, 0x54, - 0xa0, 0x43, 0x1b, 0xfa, 0x23, 0x8d, 0x0c, 0x43, 0x2a, 0x93, 0xf6, 0x62, 0x57, 0x93, 0xad, 0x17, - 0x0e, 0xc4, 0x93, 0xf2, 0x8d, 0x62, 0x61, 0xfa, 0x0b, 0x46, 0xfe, 0x58, 0x9e, 0x99, 0x61, 0x55, - 0x3a, 0xfd, 0x99, 0xd9, 0x55, 0xc9, 0xee, 0x0f, 0xec, 0x35, 0x00, 0x5b, 0xa4, 0x97, 0xf7, 0x05, - 0xdb, 0xf5, 0x28, 0xfc, 0xa1, 0x46, 0xc6, 0x82, 0x7c, 0x76, 0x31, 0xed, 0x69, 0x77, 0x50, 0xc3, - 0x76, 0x54, 0xa6, 0xf5, 0xd3, 0x80, 0xf5, 0x24, 0x7d, 0x7a, 0x1f, 0xac, 0xfe, 0x49, 0xee, 0x23, - 0xc3, 0x08, 0x4f, 0x7f, 0x92, 0x77, 0x15, 0x95, 0xd3, 0x9f, 0xe4, 0xdd, 0xd5, 0xe0, 0xde, 0x27, - 0x79, 0xc8, 0x43, 0x7f, 0xa9, 0x91, 0x99, 0x68, 0xf5, 0x35, 0x1d, 0xea, 0xd8, 0x7a, 0x6e, 0x3a, - 0xd4, 0xf1, 0xc5, 0xde, 0x9e, 0x0f, 0x04, 0x27, 0x8a, 0xf2, 0x27, 0x1a, 0x21, 0xe1, 0x7f, 0xed, - 0xd3, 0x6b, 0x69, 0x56, 0xee, 0xfa, 0xff, 0xff, 0xdc, 0xf5, 0x83, 0xb2, 0x21, 0xd8, 0xe7, 0x00, - 0xec, 0x69, 0xfa, 0x4c, 0x02, 0x58, 0xd1, 0x66, 0x59, 0x7e, 0xf3, 0xf3, 0x2f, 0x17, 0xb4, 0x2f, - 0xbe, 0x5c, 0xd0, 0xfe, 0xfe, 0xe5, 0x82, 0xf6, 0xe1, 0xe3, 0x85, 0x23, 0x5f, 0x3c, 0x5e, 0x38, - 0xf2, 0x97, 0xc7, 0x0b, 0x47, 0xee, 0x5f, 0xa9, 0xd8, 0x62, 0xab, 0xb1, 0x59, 0xb0, 0xdc, 0xaa, - 0x2a, 0x26, 0xc0, 0x51, 0xdc, 0x89, 0x48, 0xdc, 0xad, 0x33, 0xbe, 0x39, 0x0a, 0x69, 0xcf, 0x0b, - 0xff, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xe5, 0x26, 0xeb, 0xed, 0x7e, 0x32, 0x00, 0x00, + // 3136 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5b, 0xdd, 0x6f, 0x1b, 0xc7, + 0x11, 0xf7, 0x89, 0xb2, 0x3e, 0x56, 0x9f, 0x5e, 0x2b, 0x0e, 0xc3, 0xd8, 0xa2, 0x73, 0x8e, 0x2d, + 0xc7, 0x1f, 0x64, 0xac, 0xd8, 0x4e, 0x62, 0x3b, 0x69, 0x24, 0x7f, 0xc8, 0x46, 0x94, 0x44, 0x39, + 0x29, 0xfd, 0x70, 0xd1, 0x12, 0xa7, 0xe3, 0x9a, 0x3c, 0xf8, 0xc8, 0x63, 0xb8, 0x4b, 0x45, 0x8a, + 0xab, 0x16, 0xc8, 0x5f, 0x10, 0xa0, 0x40, 0xfa, 0xd2, 0xd7, 0x7e, 0x3c, 0xf4, 0xa1, 0x40, 0x83, + 0xa6, 0x40, 0x81, 0xf4, 0xa1, 0x6d, 0x9a, 0xc7, 0xa0, 0x05, 0x8a, 0x16, 0x05, 0x88, 0x22, 0xee, + 0x13, 0xff, 0x83, 0x02, 0x7d, 0x28, 0x76, 0x6e, 0x8e, 0xb7, 0x47, 0xde, 0x89, 0x27, 0x8a, 0x2d, + 0x90, 0x17, 0x71, 0x77, 0x76, 0x67, 0xf6, 0x37, 0xb3, 0xb3, 0xb3, 0xb3, 0x37, 0x36, 0x39, 0x66, + 0xd5, 0x5d, 0xce, 0xad, 0xb2, 0x69, 0x57, 0xf3, 0xef, 0x36, 0x58, 0x7d, 0x27, 0x57, 0xab, 0xbb, + 0xc2, 0xa5, 0x27, 0xde, 0x67, 0xc2, 0x04, 0x72, 0x0e, 0x5a, 0x6e, 0x9d, 0xe5, 0x82, 0xa9, 0x99, + 0x73, 0x96, 0xcb, 0x2b, 0x2e, 0xcf, 0x6f, 0x9a, 0x9c, 0x79, 0x7c, 0xf9, 0xad, 0x4b, 0x9b, 0x4c, + 0x98, 0x97, 0xf2, 0x35, 0xb3, 0x64, 0x57, 0x4d, 0x61, 0xbb, 0x55, 0x4f, 0x54, 0xe6, 0x84, 0xb2, + 0x04, 0xfc, 0x2d, 0x54, 0xdd, 0xaa, 0xc5, 0x38, 0x0e, 0x67, 0xd5, 0x61, 0xd9, 0x2c, 0x78, 0x93, + 0xc4, 0x36, 0x4e, 0xc8, 0x28, 0x13, 0x4a, 0x26, 0x2f, 0xd4, 0xea, 0xb6, 0xc5, 0x70, 0xec, 0x94, + 0x32, 0x06, 0x3c, 0x85, 0xb2, 0xc9, 0xcb, 0x05, 0xe1, 0x16, 0x2c, 0xab, 0x2d, 0x40, 0x57, 0x26, + 0x39, 0x26, 0x17, 0x85, 0x4d, 0xc7, 0xb5, 0x1e, 0x16, 0xca, 0xcc, 0x2e, 0x95, 0x05, 0xce, 0x99, + 0x57, 0xe6, 0x00, 0xbc, 0x0e, 0x19, 0x2a, 0x4a, 0xb7, 0x21, 0xe4, 0x4a, 0xa2, 0x6e, 0x5a, 0x0f, + 0x59, 0x1d, 0x27, 0x3c, 0xa9, 0x4c, 0xa8, 0x99, 0x75, 0xb3, 0xe2, 0xeb, 0x37, 0xa7, 0x0c, 0x08, + 0xde, 0xa6, 0x96, 0xdc, 0x92, 0x0b, 0xcd, 0xbc, 0x6c, 0x21, 0xf5, 0x78, 0xc9, 0x75, 0x4b, 0x0e, + 0xcb, 0x9b, 0x35, 0x3b, 0x6f, 0x56, 0xab, 0xae, 0x00, 0x3b, 0x22, 0x8f, 0x9e, 0x26, 0xc7, 0xde, + 0x96, 0xa6, 0xde, 0xe0, 0xfc, 0xae, 0xcd, 0x85, 0x5b, 0xdf, 0x31, 0xd8, 0xbb, 0x0d, 0xc6, 0x85, + 0xfe, 0x5d, 0xf2, 0x64, 0xd7, 0x08, 0xaf, 0xb9, 0x55, 0xce, 0xe8, 0x4d, 0x32, 0x26, 0x38, 0x2f, + 0x38, 0x36, 0x17, 0x69, 0xed, 0x64, 0xea, 0xec, 0xc4, 0xa2, 0x9e, 0xdb, 0x73, 0x6f, 0x73, 0x1b, + 0xeb, 0xeb, 0xcb, 0xc3, 0x9f, 0x37, 0xb3, 0x87, 0x8c, 0x51, 0xc1, 0xf9, 0xaa, 0xcd, 0x85, 0x3e, + 0x47, 0x28, 0xc8, 0x5f, 0x03, 0xc5, 0xfc, 0x55, 0xef, 0x93, 0xa3, 0x21, 0x6a, 0x7b, 0xc5, 0x11, + 0xcf, 0x00, 0x69, 0xed, 0xa4, 0x76, 0x76, 0x62, 0xf1, 0x74, 0x8f, 0xf5, 0x3c, 0x76, 0x5c, 0x12, + 0x59, 0xf5, 0x37, 0xc8, 0xd3, 0x20, 0x7b, 0x85, 0x89, 0xb7, 0x1a, 0x62, 0x63, 0x7b, 0xc3, 0x33, + 0x36, 0x2e, 0x4d, 0xd3, 0x64, 0x14, 0x98, 0xef, 0xdd, 0x82, 0x45, 0x52, 0x86, 0xdf, 0xa5, 0x73, + 0xe4, 0x30, 0xec, 0x5f, 0x7a, 0xe8, 0xa4, 0x76, 0x76, 0xd8, 0xf0, 0x3a, 0x7a, 0x83, 0x1c, 0x8f, + 0x16, 0x87, 0x98, 0xdf, 0x21, 0x93, 0xae, 0x42, 0x47, 0xe4, 0xe7, 0x7b, 0x20, 0x57, 0x45, 0x21, + 0xfe, 0x90, 0x18, 0x9d, 0xa1, 0x16, 0x4b, 0x8e, 0x13, 0xa5, 0xc5, 0x1d, 0x42, 0x82, 0xd3, 0x82, + 0x6b, 0x9e, 0xc9, 0x79, 0x47, 0x2b, 0x27, 0x8f, 0x56, 0xce, 0x3b, 0x92, 0x78, 0xb4, 0x72, 0x6b, + 0x66, 0x89, 0x21, 0xaf, 0xa1, 0x70, 0xea, 0x9f, 0x6a, 0xa8, 0x5e, 0xd7, 0x3a, 0xb1, 0xea, 0xa5, + 0x06, 0xa0, 0x1e, 0x5d, 0x09, 0xe1, 0x1f, 0x02, 0xfc, 0x0b, 0x3d, 0xf1, 0x7b, 0x98, 0x42, 0x0a, + 0x7c, 0xa0, 0x11, 0x3d, 0x4a, 0x81, 0xe5, 0x9d, 0x9b, 0x12, 0x89, 0x6f, 0xaf, 0x39, 0x72, 0x18, + 0x90, 0xe1, 0x9e, 0x7b, 0x9d, 0x0e, 0x2b, 0x0e, 0xf5, 0x6d, 0xc5, 0x3f, 0x6a, 0xe4, 0xd4, 0x9e, + 0x20, 0xbe, 0x22, 0xc6, 0xbc, 0x4e, 0x4e, 0xf8, 0xbe, 0x7e, 0xaf, 0xba, 0xb1, 0x7d, 0xd7, 0xe4, + 0xe5, 0x0d, 0xf7, 0xa6, 0x25, 0xb6, 0x7d, 0x33, 0x66, 0xc8, 0x98, 0x8d, 0x03, 0x60, 0xc9, 0x71, + 0xa3, 0xdd, 0xd7, 0x77, 0xc9, 0x7c, 0x1c, 0x33, 0xaa, 0xff, 0x6d, 0x32, 0x6d, 0x87, 0x46, 0xd0, + 0x71, 0x2f, 0xf6, 0x30, 0x40, 0x58, 0x1c, 0x9a, 0xa0, 0x43, 0x94, 0x7e, 0x03, 0x97, 0x0f, 0x4f, + 0xbe, 0x65, 0x0a, 0x33, 0x09, 0xf8, 0xf7, 0x49, 0x36, 0x96, 0x1b, 0xd1, 0x7f, 0x83, 0x4c, 0xdd, + 0x94, 0x98, 0x60, 0x4b, 0x37, 0xb6, 0x79, 0xc2, 0xdd, 0x53, 0x79, 0x10, 0x7a, 0x58, 0x8e, 0x5e, + 0x42, 0xab, 0x2f, 0x39, 0x4e, 0xb4, 0xd5, 0x07, 0x75, 0xd8, 0x3f, 0xd3, 0xd0, 0x46, 0x11, 0x2b, + 0xed, 0xb1, 0x45, 0xa9, 0x01, 0x6d, 0xd1, 0x20, 0xfd, 0xf4, 0x29, 0xdf, 0xd5, 0x36, 0x38, 0x5f, + 0x2a, 0x16, 0xeb, 0x8c, 0xfb, 0x77, 0x0b, 0x9d, 0x27, 0x13, 0xf2, 0xda, 0xaa, 0x35, 0x36, 0x0b, + 0x0f, 0xd9, 0x0e, 0xee, 0xf4, 0xb8, 0xe0, 0x7c, 0xad, 0xb1, 0xf9, 0x3a, 0xdb, 0xd1, 0x5f, 0x23, + 0x99, 0x28, 0x66, 0x34, 0xc0, 0x2c, 0x49, 0x31, 0xe1, 0xfb, 0x87, 0x6c, 0x4a, 0xca, 0xa6, 0xb0, + 0x00, 0xee, 0xb8, 0x21, 0x9b, 0xed, 0x3b, 0x4d, 0x4a, 0x58, 0x5f, 0xf7, 0xef, 0xb4, 0xd7, 0xf1, + 0x4e, 0xf3, 0xa9, 0x28, 0xf0, 0x32, 0x49, 0x6d, 0xac, 0xaf, 0xe3, 0xae, 0x25, 0xb8, 0x40, 0x0d, + 0x39, 0x5d, 0xcf, 0xe3, 0xb5, 0xbc, 0xc2, 0xc4, 0x8a, 0xc9, 0xd7, 0x64, 0xde, 0xa2, 0x84, 0x32, + 0xbb, 0x5a, 0x64, 0xdb, 0x88, 0xd1, 0xeb, 0xe8, 0x05, 0x92, 0xee, 0x66, 0x08, 0x2e, 0x72, 0x9f, + 0x86, 0x38, 0x16, 0x7a, 0xe0, 0x68, 0x8b, 0x68, 0x33, 0xea, 0x26, 0x22, 0x5a, 0x72, 0x9c, 0x4e, + 0x44, 0x83, 0xf2, 0xcf, 0x9f, 0x6b, 0xa8, 0x44, 0x68, 0x8d, 0x48, 0x25, 0x52, 0x7d, 0x29, 0x31, + 0x38, 0x0f, 0x5c, 0x0c, 0x9c, 0x08, 0xce, 0xf1, 0x9b, 0x90, 0x97, 0xee, 0xbd, 0x45, 0x0f, 0x83, + 0xc4, 0x24, 0xc4, 0x83, 0x0a, 0xae, 0x92, 0x09, 0x85, 0x8c, 0x66, 0x3c, 0xd7, 0x2b, 0xba, 0x28, + 0x82, 0x54, 0x76, 0xbd, 0x88, 0x00, 0x97, 0x1c, 0x27, 0x02, 0xe0, 0xa0, 0x76, 0xec, 0x63, 0x2d, + 0x48, 0x53, 0x12, 0xe9, 0x94, 0x3a, 0x80, 0x4e, 0x83, 0xdb, 0xbd, 0xf9, 0x20, 0xe9, 0x59, 0x63, + 0xd5, 0xa2, 0x5d, 0x2d, 0x85, 0xcc, 0xa3, 0x8b, 0x20, 0x22, 0x77, 0x8c, 0xa3, 0x5e, 0xeb, 0x64, + 0xba, 0xe6, 0x0d, 0xe0, 0x8b, 0x04, 0x55, 0xbb, 0xd0, 0x2b, 0x61, 0x0d, 0x49, 0x9b, 0xaa, 0xa9, + 0x5d, 0xfd, 0x15, 0x72, 0xd2, 0x4b, 0x8a, 0x55, 0x6a, 0x47, 0x1e, 0xf3, 0x14, 0x19, 0xf3, 0xde, + 0x38, 0x76, 0x31, 0x9c, 0xbe, 0x16, 0xf5, 0xef, 0x93, 0x67, 0xf6, 0x60, 0x47, 0xe0, 0xdf, 0x8a, + 0x00, 0xae, 0xed, 0x17, 0xb8, 0x7f, 0x8d, 0x85, 0xe1, 0x5f, 0x0d, 0xee, 0xff, 0x55, 0x93, 0x8b, + 0x65, 0xf9, 0x52, 0xba, 0x0b, 0x0f, 0xa5, 0xbd, 0x8f, 0xc5, 0x23, 0xbc, 0x7a, 0xa3, 0xf8, 0x10, + 0xf5, 0x37, 0xc9, 0x4c, 0xc7, 0x10, 0xc2, 0xce, 0xf5, 0x80, 0xdd, 0x29, 0xb0, 0x53, 0x8c, 0x5e, + 0x0e, 0x6e, 0xc4, 0x18, 0xd0, 0x83, 0x3a, 0x2a, 0x7f, 0xd0, 0x50, 0xcf, 0xa8, 0xa5, 0xf6, 0xd2, + 0x33, 0x35, 0x00, 0x3d, 0x07, 0x77, 0x74, 0xce, 0x07, 0xb7, 0x9c, 0x9a, 0xa2, 0x44, 0x6f, 0xed, + 0xaa, 0x12, 0x25, 0x65, 0x5a, 0xb0, 0x03, 0xae, 0xd2, 0xef, 0x4b, 0xac, 0x44, 0xe6, 0xc2, 0x4b, + 0xa3, 0xd5, 0xde, 0x22, 0x93, 0x6a, 0x42, 0x95, 0xf0, 0x05, 0xa6, 0xb2, 0x18, 0x21, 0x01, 0xfa, + 0x77, 0x50, 0x47, 0x19, 0xd4, 0xfe, 0x07, 0x69, 0xd8, 0x2f, 0x35, 0x54, 0xa4, 0x2d, 0x3f, 0x56, + 0x91, 0xd4, 0x81, 0x14, 0x19, 0xdc, 0xae, 0xff, 0x40, 0xb9, 0x4d, 0x2c, 0xb1, 0x8d, 0xd1, 0xa0, + 0x77, 0x50, 0x1a, 0xd8, 0x0b, 0xeb, 0x13, 0xf5, 0xa2, 0x51, 0x11, 0x7c, 0x05, 0x4c, 0x77, 0x26, + 0x02, 0xf8, 0x3d, 0xef, 0x4e, 0x33, 0xcc, 0x6a, 0x5b, 0xdf, 0xbd, 0xcc, 0xf8, 0x34, 0x19, 0xf7, + 0x3e, 0x2d, 0x39, 0xee, 0x7b, 0x78, 0x28, 0xc6, 0x80, 0xb0, 0xea, 0xbe, 0x47, 0x4f, 0x10, 0xe2, + 0x0d, 0x96, 0xed, 0x52, 0x39, 0x9d, 0x82, 0x51, 0x6f, 0xfa, 0x5d, 0xbb, 0x54, 0xd6, 0xbf, 0x47, + 0x16, 0x7a, 0x02, 0x40, 0x2b, 0xbe, 0x4d, 0xa6, 0xc3, 0xdf, 0xd1, 0xfa, 0xb2, 0xa3, 0xa5, 0x9e, + 0xa5, 0xe3, 0xe8, 0x39, 0x32, 0x20, 0xdd, 0x67, 0xc2, 0x0c, 0x05, 0x57, 0xfd, 0x0a, 0xee, 0x6a, + 0xe7, 0x28, 0xe2, 0x39, 0x46, 0x46, 0x94, 0x70, 0x9f, 0x32, 0xb0, 0xa7, 0x6f, 0xe0, 0xfd, 0x7d, + 0xd3, 0xad, 0x6e, 0xb1, 0xba, 0x4c, 0x78, 0x37, 0x5c, 0xc9, 0xde, 0x15, 0x59, 0xba, 0x0c, 0x99, + 0x21, 0x63, 0x25, 0x93, 0xaf, 0xda, 0x15, 0x5b, 0x60, 0x46, 0xdf, 0xee, 0xeb, 0x3f, 0xd1, 0xf0, + 0xda, 0xef, 0x16, 0x8b, 0x78, 0x2e, 0x90, 0x23, 0x6e, 0x43, 0x6c, 0xba, 0x8d, 0x6a, 0x71, 0xc5, + 0xe4, 0xf7, 0xaa, 0x72, 0x10, 0x23, 0x5e, 0xf7, 0x80, 0x9c, 0x0d, 0x5f, 0xdf, 0x2c, 0xd7, 0xb9, + 0xc3, 0x18, 0xce, 0xf6, 0x16, 0xed, 0x1e, 0xa0, 0x67, 0xc9, 0x8c, 0xfc, 0x55, 0x63, 0xbf, 0xb7, + 0x95, 0x9d, 0x64, 0x7d, 0x81, 0x9c, 0x06, 0x98, 0x6f, 0x30, 0xce, 0xcd, 0x12, 0x5b, 0x33, 0x39, + 0xb7, 0xab, 0xa5, 0xb5, 0x40, 0xa2, 0x6f, 0xdd, 0x3b, 0xe8, 0x7a, 0x7b, 0x4c, 0x44, 0xc5, 0x8e, + 0x93, 0xf1, 0x07, 0x6d, 0x88, 0xf8, 0x62, 0x6a, 0x13, 0xf4, 0xeb, 0xb8, 0xe0, 0xfd, 0xdb, 0x5f, + 0x7f, 0x43, 0xbe, 0x6e, 0xea, 0x66, 0x95, 0x9b, 0x96, 0xf4, 0x6e, 0x83, 0x59, 0xcc, 0xae, 0xb5, + 0xef, 0x4a, 0x4a, 0x86, 0xcb, 0xc1, 0xeb, 0x1a, 0xda, 0xfa, 0x7f, 0x86, 0x11, 0xc5, 0x1e, 0xdc, + 0x6d, 0xf3, 0x12, 0xfc, 0xbe, 0xda, 0x16, 0xb2, 0x3c, 0xd5, 0x6a, 0x66, 0xc7, 0x81, 0x2a, 0x1f, + 0x92, 0x46, 0xd0, 0xa4, 0x8b, 0x64, 0xd2, 0x9b, 0x5d, 0x6d, 0x54, 0x36, 0x59, 0xdd, 0xb3, 0xec, + 0xf2, 0x4c, 0xab, 0x99, 0x9d, 0x00, 0xfa, 0x9b, 0x40, 0x36, 0xd4, 0x0e, 0x7d, 0x95, 0xcc, 0x5a, + 0x6e, 0x55, 0xd4, 0x4d, 0x4b, 0x14, 0x4c, 0xef, 0xe5, 0x07, 0x56, 0x1e, 0x5f, 0x3e, 0xda, 0x6a, + 0x66, 0x67, 0xfc, 0x31, 0xff, 0x51, 0xd8, 0x49, 0xa0, 0xb7, 0xc9, 0x51, 0xab, 0x51, 0x69, 0x38, + 0xa6, 0xb0, 0xb7, 0x58, 0xa1, 0x64, 0xf2, 0x42, 0x83, 0xb3, 0x62, 0x7a, 0x18, 0x44, 0x3c, 0xd1, + 0x6a, 0x66, 0x8f, 0x04, 0xc3, 0x2b, 0x26, 0x7f, 0x87, 0xb3, 0xa2, 0xd1, 0x4d, 0xa2, 0xc7, 0xc9, + 0xf0, 0x83, 0xba, 0x5b, 0x49, 0x1f, 0x06, 0xbe, 0xb1, 0x56, 0x33, 0x0b, 0x7d, 0x03, 0xfe, 0xd2, + 0x33, 0xe0, 0xa3, 0x9e, 0xe4, 0x11, 0x98, 0x31, 0xd1, 0x6a, 0x66, 0x47, 0x4b, 0x28, 0xcf, 0x6f, + 0x48, 0x73, 0x39, 0x6e, 0x89, 0x17, 0x36, 0x1d, 0xd7, 0xad, 0xa4, 0x47, 0x03, 0x73, 0x49, 0xea, + 0xb2, 0x24, 0x1a, 0x41, 0x93, 0xea, 0x64, 0x84, 0x0b, 0x53, 0x34, 0x78, 0x7a, 0x0c, 0x66, 0x92, + 0x56, 0x33, 0x8b, 0x14, 0x03, 0x7f, 0xe9, 0x31, 0x32, 0x24, 0xdc, 0xf4, 0x38, 0x8c, 0x8f, 0xb4, + 0x9a, 0xd9, 0x21, 0xe1, 0x1a, 0x43, 0xc2, 0x95, 0x66, 0x13, 0xc1, 0xb6, 0x79, 0xdb, 0x43, 0x02, + 0xb3, 0x29, 0x63, 0xb0, 0x49, 0x9d, 0x04, 0xba, 0x44, 0x8e, 0xa8, 0xfc, 0x5e, 0xa6, 0x30, 0x01, + 0x02, 0xe6, 0x5a, 0xcd, 0xac, 0x2a, 0xfc, 0x9e, 0x1c, 0x33, 0xba, 0x28, 0xf4, 0x2a, 0x19, 0x96, + 0xba, 0xa4, 0x27, 0x13, 0x7d, 0x88, 0x5e, 0x75, 0x4b, 0x06, 0xcc, 0xd7, 0x3f, 0x48, 0x91, 0xd4, + 0xaa, 0x5b, 0x92, 0x21, 0xc1, 0xdf, 0x70, 0xcf, 0x3b, 0xfd, 0xae, 0x0c, 0x32, 0xc2, 0xad, 0xd9, + 0x16, 0x4f, 0x0f, 0x9d, 0x4c, 0x9d, 0x1d, 0x37, 0xb0, 0x27, 0x9d, 0xb9, 0x68, 0x0a, 0xd3, 0xf3, + 0x0f, 0x03, 0xda, 0x5d, 0x3e, 0x27, 0x37, 0x7e, 0xb8, 0xb7, 0xcf, 0x75, 0x19, 0xef, 0xf0, 0x41, + 0x8d, 0x37, 0x02, 0x0b, 0x27, 0x35, 0x5e, 0xf8, 0x60, 0x8d, 0xf6, 0x38, 0x58, 0xcf, 0x11, 0xe9, + 0x36, 0xb8, 0xd0, 0x18, 0x2c, 0x34, 0xd9, 0x6a, 0x66, 0xc7, 0x1c, 0xb7, 0xe4, 0x2d, 0xd0, 0x6e, + 0xd1, 0xd3, 0x64, 0xb4, 0xce, 0x2a, 0xee, 0x16, 0x2b, 0x82, 0xd7, 0x8c, 0x79, 0x9e, 0x8a, 0x24, + 0xc3, 0x6f, 0xe8, 0x97, 0x31, 0xcb, 0x8e, 0x0a, 0x01, 0xf1, 0x91, 0xe3, 0xdf, 0xc3, 0x98, 0x31, + 0x47, 0xb1, 0xfd, 0xdf, 0x42, 0x86, 0x7f, 0x56, 0x53, 0x91, 0x67, 0xf5, 0x29, 0x92, 0x2a, 0x99, + 0x1c, 0x03, 0xc0, 0x68, 0xab, 0x99, 0x95, 0x5d, 0x43, 0xfe, 0x91, 0x66, 0x6c, 0xd7, 0x9c, 0x70, + 0xc3, 0xc1, 0x8c, 0xa5, 0xf6, 0x67, 0x09, 0xbf, 0x25, 0xd7, 0x00, 0xfc, 0x23, 0xc1, 0x1a, 0xb2, + 0xef, 0xd9, 0x81, 0x66, 0x65, 0x6e, 0x5d, 0x6b, 0x08, 0xdc, 0xb8, 0xf1, 0x56, 0x33, 0xeb, 0x11, + 0x0c, 0xef, 0x47, 0x4e, 0xf0, 0xd2, 0xe5, 0xb1, 0x60, 0x02, 0x10, 0x30, 0x73, 0x8e, 0x3d, 0xd7, + 0x91, 0xae, 0x45, 0xf6, 0x75, 0x2e, 0xb3, 0xe4, 0xf0, 0x96, 0xe9, 0x34, 0x18, 0x1e, 0x67, 0x58, + 0x1b, 0x08, 0x86, 0xf7, 0x23, 0x75, 0x13, 0x3b, 0x35, 0x96, 0x9e, 0x0c, 0x74, 0x93, 0x7d, 0x03, + 0xfe, 0xd2, 0x3c, 0x99, 0x30, 0x2d, 0x8b, 0xf9, 0x65, 0xa6, 0x29, 0x79, 0x02, 0x97, 0xa7, 0x5b, + 0xcd, 0x2c, 0xf1, 0xc8, 0xab, 0xb6, 0x4c, 0x04, 0x83, 0xb6, 0x0c, 0x8e, 0xed, 0x24, 0x69, 0x3a, + 0x08, 0x8e, 0x78, 0xbf, 0x07, 0x17, 0xfd, 0x51, 0xa2, 0x6d, 0xa5, 0x67, 0x60, 0xc2, 0xe1, 0x56, + 0x33, 0xab, 0x6d, 0x19, 0xda, 0x96, 0x24, 0xd6, 0xd3, 0xb3, 0x01, 0xb1, 0x6e, 0x68, 0x75, 0x49, + 0xe4, 0xe9, 0x23, 0x01, 0x91, 0x1b, 0x1a, 0xd7, 0xaf, 0xe1, 0x5b, 0x1c, 0x5d, 0x0f, 0xae, 0xdf, + 0xe5, 0x1d, 0xf4, 0x0f, 0xf4, 0xd9, 0x63, 0x64, 0xa4, 0x1c, 0x64, 0x27, 0xc3, 0x06, 0xf6, 0xf4, + 0xbf, 0x8f, 0xe2, 0x4b, 0x3c, 0x9a, 0x19, 0x3d, 0x57, 0x27, 0x23, 0xe8, 0x85, 0x5a, 0x10, 0x8f, + 0x3d, 0x8a, 0x81, 0xbf, 0x6d, 0xbf, 0x18, 0x8a, 0xf4, 0x8b, 0x3c, 0x99, 0xa8, 0x99, 0x75, 0x56, + 0x15, 0x9e, 0xf3, 0x7b, 0x0e, 0x0a, 0xb6, 0xf3, 0xc8, 0xe0, 0xfd, 0x4a, 0x3b, 0xf0, 0x93, 0xe1, + 0x18, 0x3f, 0xc9, 0x93, 0x09, 0x5e, 0x36, 0x5f, 0x28, 0x34, 0xaa, 0x96, 0xc3, 0x38, 0x3a, 0x2d, + 0x48, 0x94, 0xe4, 0x77, 0x80, 0x6a, 0x28, 0xed, 0x8e, 0x2b, 0x68, 0xa4, 0xc7, 0x15, 0x14, 0x76, + 0x37, 0x5e, 0xa8, 0xbb, 0xae, 0xef, 0xd4, 0x9d, 0xee, 0xc6, 0x0d, 0xd7, 0x15, 0x46, 0x17, 0x45, + 0x2e, 0x28, 0xef, 0x2a, 0xe6, 0xf1, 0x8e, 0x05, 0x0b, 0x02, 0x15, 0x98, 0x82, 0x26, 0xbd, 0x42, + 0xa6, 0xea, 0x5e, 0x8e, 0x81, 0x8b, 0x79, 0x47, 0x60, 0xb6, 0xd5, 0xcc, 0x4e, 0xfa, 0x03, 0xc0, + 0x13, 0xea, 0x49, 0x3b, 0x55, 0xec, 0x2a, 0xab, 0xe3, 0x51, 0x00, 0x3b, 0x01, 0xc1, 0xf0, 0x7e, + 0x68, 0x8e, 0x90, 0xa2, 0xfd, 0xe0, 0x81, 0x6d, 0x35, 0x1c, 0xb1, 0x83, 0x9e, 0x0f, 0x66, 0x0a, + 0xa8, 0x86, 0xd2, 0x86, 0x2b, 0xc0, 0x15, 0xa6, 0x53, 0x50, 0xb8, 0x26, 0x95, 0x2b, 0x40, 0x8e, + 0xdd, 0x0a, 0x58, 0x3b, 0x09, 0x52, 0x6b, 0xb6, 0x2d, 0xea, 0x66, 0x01, 0x2e, 0xa4, 0xa9, 0x40, + 0x6b, 0xa0, 0x42, 0x95, 0x22, 0x68, 0x4a, 0xaf, 0xe1, 0xf6, 0xfb, 0x0c, 0x8f, 0x07, 0x78, 0x8d, + 0xec, 0x1b, 0xf0, 0xd7, 0x0f, 0x4b, 0x0e, 0xa4, 0xc0, 0x33, 0xa1, 0xb0, 0x04, 0x69, 0x70, 0x90, + 0x10, 0x87, 0x12, 0x91, 0xd9, 0x3d, 0x12, 0x91, 0xf3, 0x64, 0x5c, 0xd8, 0x15, 0xc6, 0x85, 0x59, + 0xa9, 0xe1, 0x49, 0x02, 0x74, 0x6d, 0xa2, 0x11, 0x34, 0xe9, 0x65, 0x32, 0xa9, 0xee, 0x6a, 0x9a, + 0xc2, 0x91, 0x87, 0x2d, 0x09, 0xed, 0x76, 0xa8, 0x27, 0x4f, 0x0b, 0x3a, 0xe5, 0x51, 0x98, 0x0f, + 0xa7, 0xc5, 0xa3, 0x18, 0xf8, 0x4b, 0xaf, 0x91, 0x59, 0xf9, 0x30, 0x2b, 0x3c, 0x60, 0xac, 0x50, + 0x63, 0x75, 0x99, 0x9e, 0xa5, 0xe7, 0x00, 0xcd, 0x91, 0x56, 0x33, 0x3b, 0x25, 0xc7, 0xee, 0x30, + 0xb6, 0xc6, 0xea, 0x2b, 0x26, 0x37, 0xc2, 0x5d, 0xa9, 0x6a, 0xc5, 0xf6, 0xfe, 0x09, 0x40, 0xfa, + 0x89, 0x40, 0xd5, 0x8a, 0x0d, 0xf5, 0x0b, 0xc3, 0x6f, 0x2c, 0x7e, 0x74, 0x86, 0x1c, 0x86, 0xb3, + 0x4d, 0x3f, 0xd2, 0xc8, 0x88, 0x57, 0x7f, 0xa6, 0x97, 0x7a, 0x64, 0x23, 0xdd, 0x05, 0xf0, 0xcc, + 0xe2, 0x7e, 0x58, 0xbc, 0x88, 0xa1, 0x9f, 0xfe, 0xe0, 0x2f, 0xff, 0xfa, 0xe1, 0x50, 0x96, 0x9e, + 0xc8, 0x4b, 0x8e, 0x8b, 0xca, 0xbf, 0x7b, 0x50, 0xff, 0xed, 0x00, 0xfd, 0x4c, 0x23, 0x93, 0x6a, + 0xc9, 0x90, 0x5e, 0x4b, 0xb2, 0x56, 0x74, 0xb5, 0x3c, 0x73, 0xbd, 0x2f, 0x5e, 0x04, 0xfc, 0x0a, + 0x00, 0x7e, 0x91, 0x5e, 0x89, 0x01, 0xac, 0x16, 0x31, 0xf3, 0x8f, 0xf0, 0xe3, 0xcf, 0x6e, 0xfe, + 0x11, 0x04, 0xa3, 0x5d, 0xfa, 0x89, 0x46, 0x66, 0x54, 0xb9, 0x4b, 0x8e, 0x93, 0x4c, 0x97, 0xe8, + 0x9a, 0x79, 0x32, 0x5d, 0x62, 0xea, 0xe0, 0xfa, 0x79, 0xd0, 0xe5, 0x34, 0x3d, 0x95, 0x40, 0x17, + 0xfa, 0x0f, 0x8d, 0x1c, 0xeb, 0x40, 0x8e, 0x1f, 0x62, 0xe9, 0x52, 0x1f, 0x20, 0xc2, 0xdf, 0x80, + 0x33, 0xcb, 0x07, 0x11, 0x81, 0xea, 0x5c, 0x03, 0x75, 0x2e, 0xd3, 0xc5, 0x04, 0xea, 0x20, 0x2f, + 0xee, 0xd0, 0x2e, 0xfd, 0x93, 0x46, 0xa6, 0xc3, 0xf5, 0x3e, 0x7a, 0x23, 0xa1, 0x9b, 0x44, 0xd6, + 0x37, 0x33, 0xaf, 0xf4, 0xc9, 0x8d, 0xba, 0xbc, 0x04, 0xba, 0x2c, 0xd2, 0xe7, 0x63, 0x74, 0x09, + 0x57, 0x21, 0xf3, 0x8f, 0xfc, 0xfe, 0x2e, 0xfd, 0xab, 0x46, 0x68, 0x77, 0xc5, 0x97, 0x26, 0xc2, + 0x13, 0x5b, 0x67, 0xce, 0xbc, 0xda, 0x2f, 0x3b, 0xea, 0xb3, 0x04, 0xfa, 0x5c, 0xa7, 0x2f, 0xc7, + 0xea, 0xd3, 0xf9, 0xaf, 0x95, 0xe0, 0x5e, 0x50, 0x15, 0xfb, 0x9d, 0x46, 0x8e, 0x84, 0x57, 0x90, + 0x87, 0xe7, 0x46, 0x42, 0xc7, 0x39, 0xc0, 0x2e, 0xc5, 0x56, 0x96, 0xf5, 0x8b, 0xa0, 0xd5, 0x02, + 0x3d, 0x9d, 0x68, 0x97, 0xe8, 0xc7, 0x1a, 0x99, 0x0a, 0x55, 0x68, 0xe9, 0x4b, 0x09, 0xbd, 0xa4, + 0xab, 0x22, 0x9c, 0x79, 0xb9, 0x0f, 0x4e, 0x44, 0x9d, 0x03, 0xd4, 0x67, 0xe9, 0x99, 0x18, 0xd4, + 0x25, 0x26, 0x0a, 0x82, 0x73, 0xff, 0x63, 0x02, 0xfd, 0x50, 0x83, 0x72, 0x6f, 0xb2, 0x2b, 0x21, + 0x54, 0x3f, 0x4e, 0x76, 0x25, 0x84, 0x8b, 0xcb, 0xba, 0x0e, 0xf0, 0x8e, 0xd3, 0x4c, 0x0c, 0x3c, + 0x09, 0xe5, 0x17, 0x5a, 0x50, 0x39, 0xa5, 0x57, 0x13, 0x2e, 0xd2, 0x51, 0xe2, 0xcd, 0xbc, 0xb8, + 0x6f, 0x3e, 0x44, 0x98, 0x07, 0x84, 0xcf, 0xd1, 0x85, 0x38, 0x03, 0x22, 0x83, 0xf4, 0xde, 0x22, + 0xdb, 0xde, 0xa5, 0x3f, 0xd3, 0xc8, 0x84, 0x2f, 0x45, 0x3a, 0xed, 0xd5, 0x84, 0x6e, 0xd7, 0x17, + 0xe2, 0x88, 0x42, 0xb3, 0xbe, 0x00, 0x88, 0x9f, 0xa1, 0xd9, 0x1e, 0x88, 0xe9, 0xa7, 0x1a, 0x99, + 0xed, 0xfc, 0x54, 0x48, 0x13, 0x5d, 0x32, 0x31, 0xdf, 0x2d, 0x33, 0x37, 0xfa, 0x63, 0x4e, 0x68, + 0x6a, 0xab, 0x13, 0xeb, 0x67, 0x1a, 0x99, 0x50, 0xbe, 0x06, 0xd2, 0x5b, 0x49, 0x96, 0xef, 0xf5, + 0xd5, 0x31, 0x73, 0xfb, 0x80, 0x52, 0x50, 0x9b, 0x73, 0xa0, 0xcd, 0xb3, 0x54, 0x8f, 0xcb, 0x76, + 0x14, 0xe0, 0xbf, 0xd1, 0x42, 0x75, 0x66, 0x9a, 0xf4, 0xc0, 0x77, 0x57, 0xc6, 0x33, 0xd7, 0xfa, + 0x61, 0x45, 0xc8, 0x8b, 0x00, 0xf9, 0x02, 0x3d, 0x17, 0xb7, 0x01, 0x01, 0x4f, 0xdb, 0xdd, 0x7f, + 0xa5, 0x91, 0x69, 0x45, 0x96, 0xf4, 0xf8, 0x97, 0x13, 0x7a, 0x6e, 0xbf, 0xe8, 0xa3, 0x6b, 0xf5, + 0x3d, 0x0d, 0xae, 0xa0, 0xa7, 0xbf, 0xd5, 0xc8, 0x6c, 0xa8, 0x24, 0x2c, 0x71, 0x27, 0xcd, 0xaf, + 0xa2, 0x4a, 0xee, 0x99, 0x1b, 0xfd, 0x31, 0x23, 0xf6, 0x0b, 0x80, 0xfd, 0x0c, 0x7d, 0x36, 0xce, + 0x59, 0x54, 0x2e, 0xfa, 0x67, 0x8d, 0xcc, 0x45, 0x55, 0xc9, 0xe9, 0xd7, 0x12, 0x65, 0xe5, 0xf1, + 0xe5, 0xf9, 0xcc, 0x6b, 0xfd, 0x0b, 0x40, 0x4d, 0x5e, 0x04, 0x4d, 0x2e, 0xd1, 0x7c, 0x12, 0x4d, + 0x30, 0x25, 0x2b, 0xd8, 0xc5, 0x5d, 0xfa, 0xb9, 0xd6, 0x55, 0x3c, 0xa6, 0x49, 0x13, 0xab, 0xe8, + 0xd2, 0x77, 0xb2, 0x44, 0x26, 0xbe, 0x6c, 0xaf, 0x5f, 0x05, 0x5d, 0x9e, 0xa7, 0xb9, 0x18, 0x5d, + 0x9c, 0x30, 0x5f, 0xfb, 0x4c, 0xfc, 0x5e, 0x23, 0xb4, 0x43, 0xa6, 0xf4, 0xaf, 0xa4, 0x09, 0xc8, + 0x41, 0xb4, 0x89, 0x2f, 0xce, 0xf7, 0x4c, 0x05, 0x3a, 0xb4, 0xa1, 0x3f, 0xd6, 0xc8, 0x30, 0xa4, + 0x32, 0x49, 0x2f, 0x76, 0x35, 0xd9, 0x7a, 0x61, 0x5f, 0x3c, 0x09, 0xdf, 0x28, 0x16, 0xa6, 0xbf, + 0x60, 0xe4, 0x8f, 0x65, 0xcc, 0x0c, 0x8a, 0xf2, 0xc9, 0x63, 0x66, 0x57, 0x21, 0xbf, 0x3f, 0xb0, + 0x57, 0x00, 0x6c, 0x9e, 0x5e, 0xdc, 0x13, 0x6c, 0xd7, 0xa3, 0xf0, 0x47, 0x1a, 0x19, 0xf5, 0xf3, + 0xd9, 0xc5, 0xa4, 0xd1, 0x6e, 0xbf, 0x86, 0xed, 0x28, 0xcc, 0xeb, 0xa7, 0x00, 0xeb, 0x09, 0xfa, + 0xf4, 0x1e, 0x58, 0xbd, 0x48, 0xee, 0x21, 0xc3, 0x13, 0x9e, 0x3c, 0x92, 0x77, 0xd5, 0xd4, 0x93, + 0x47, 0xf2, 0xee, 0x62, 0x78, 0xef, 0x48, 0x1e, 0xf0, 0xd0, 0xc7, 0x1a, 0xc9, 0x84, 0x51, 0xab, + 0x95, 0x61, 0x7a, 0x7b, 0xff, 0x30, 0x22, 0x4a, 0xdb, 0x99, 0x3b, 0x07, 0x15, 0x93, 0x30, 0xa2, + 0x58, 0xd1, 0xfc, 0xf4, 0xd7, 0x1a, 0x99, 0x0e, 0xd7, 0x98, 0x93, 0xed, 0x4d, 0x64, 0xd5, 0x3a, + 0xd9, 0xde, 0x44, 0x97, 0xb4, 0x7b, 0x3e, 0x83, 0x9c, 0x30, 0xca, 0x9f, 0x6a, 0x84, 0x04, 0xff, + 0x35, 0x83, 0x5e, 0x49, 0xb2, 0x72, 0xd7, 0x7f, 0xf2, 0xc8, 0x5c, 0xdd, 0x2f, 0x1b, 0x82, 0x7d, + 0x0e, 0xc0, 0x9e, 0xa2, 0xcf, 0xc4, 0x80, 0x15, 0x6d, 0x96, 0xe5, 0xd7, 0x3f, 0xff, 0x72, 0x5e, + 0xfb, 0xe2, 0xcb, 0x79, 0xed, 0x9f, 0x5f, 0xce, 0x6b, 0x1f, 0x3e, 0x9e, 0x3f, 0xf4, 0xc5, 0xe3, + 0xf9, 0x43, 0x7f, 0x7b, 0x3c, 0x7f, 0xe8, 0xfe, 0xa5, 0x92, 0x2d, 0xca, 0x8d, 0xcd, 0x9c, 0xe5, + 0x56, 0x54, 0x31, 0x3e, 0x8e, 0xfc, 0x76, 0x48, 0xe2, 0x4e, 0x8d, 0xf1, 0xcd, 0x11, 0x48, 0xee, + 0x5e, 0xf8, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1a, 0xbf, 0x5a, 0xcc, 0x63, 0x34, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -3331,6 +3447,8 @@ type QueryClient interface { CctxAll(ctx context.Context, in *QueryAllCctxRequest, opts ...grpc.CallOption) (*QueryAllCctxResponse, error) // Queries a list of send items. CctxAllPending(ctx context.Context, in *QueryAllCctxPendingRequest, opts ...grpc.CallOption) (*QueryAllCctxPendingResponse, error) + // Queries a list of pending send items in nonce range. + CctxAllPendingInNonceRange(ctx context.Context, in *QueryAllCctxPendingInNonceRangeRequest, opts ...grpc.CallOption) (*QueryAllCctxPendingInNonceRangeResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(ctx context.Context, in *QueryLastZetaHeightRequest, opts ...grpc.CallOption) (*QueryLastZetaHeightResponse, error) TssHistory(ctx context.Context, in *QueryTssHistoryRequest, opts ...grpc.CallOption) (*QueryTssHistoryResponse, error) @@ -3551,6 +3669,15 @@ func (c *queryClient) CctxAllPending(ctx context.Context, in *QueryAllCctxPendin return out, nil } +func (c *queryClient) CctxAllPendingInNonceRange(ctx context.Context, in *QueryAllCctxPendingInNonceRangeRequest, opts ...grpc.CallOption) (*QueryAllCctxPendingInNonceRangeResponse, error) { + out := new(QueryAllCctxPendingInNonceRangeResponse) + err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/CctxAllPendingInNonceRange", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *queryClient) LastZetaHeight(ctx context.Context, in *QueryLastZetaHeightRequest, opts ...grpc.CallOption) (*QueryLastZetaHeightResponse, error) { out := new(QueryLastZetaHeightResponse) err := c.cc.Invoke(ctx, "/zetachain.zetacore.crosschain.Query/LastZetaHeight", in, out, opts...) @@ -3612,6 +3739,8 @@ type QueryServer interface { CctxAll(context.Context, *QueryAllCctxRequest) (*QueryAllCctxResponse, error) // Queries a list of send items. CctxAllPending(context.Context, *QueryAllCctxPendingRequest) (*QueryAllCctxPendingResponse, error) + // Queries a list of pending send items in nonce range. + CctxAllPendingInNonceRange(context.Context, *QueryAllCctxPendingInNonceRangeRequest) (*QueryAllCctxPendingInNonceRangeResponse, error) // Queries a list of lastMetaHeight items. LastZetaHeight(context.Context, *QueryLastZetaHeightRequest) (*QueryLastZetaHeightResponse, error) TssHistory(context.Context, *QueryTssHistoryRequest) (*QueryTssHistoryResponse, error) @@ -3690,6 +3819,9 @@ func (*UnimplementedQueryServer) CctxAll(ctx context.Context, req *QueryAllCctxR func (*UnimplementedQueryServer) CctxAllPending(ctx context.Context, req *QueryAllCctxPendingRequest) (*QueryAllCctxPendingResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method CctxAllPending not implemented") } +func (*UnimplementedQueryServer) CctxAllPendingInNonceRange(ctx context.Context, req *QueryAllCctxPendingInNonceRangeRequest) (*QueryAllCctxPendingInNonceRangeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CctxAllPendingInNonceRange not implemented") +} func (*UnimplementedQueryServer) LastZetaHeight(ctx context.Context, req *QueryLastZetaHeightRequest) (*QueryLastZetaHeightResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method LastZetaHeight not implemented") } @@ -4115,6 +4247,24 @@ func _Query_CctxAllPending_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Query_CctxAllPendingInNonceRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllCctxPendingInNonceRangeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).CctxAllPendingInNonceRange(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/zetachain.zetacore.crosschain.Query/CctxAllPendingInNonceRange", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).CctxAllPendingInNonceRange(ctx, req.(*QueryAllCctxPendingInNonceRangeRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Query_LastZetaHeight_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(QueryLastZetaHeightRequest) if err := dec(in); err != nil { @@ -4247,6 +4397,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "CctxAllPending", Handler: _Query_CctxAllPending_Handler, }, + { + MethodName: "CctxAllPendingInNonceRange", + Handler: _Query_CctxAllPendingInNonceRange_Handler, + }, { MethodName: "LastZetaHeight", Handler: _Query_LastZetaHeight_Handler, @@ -5793,6 +5947,81 @@ func (m *QueryAllCctxPendingResponse) MarshalToSizedBuffer(dAtA []byte) (int, er return len(dAtA) - i, nil } +func (m *QueryAllCctxPendingInNonceRangeRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllCctxPendingInNonceRangeRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllCctxPendingInNonceRangeRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.NonceHigh != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.NonceHigh)) + i-- + dAtA[i] = 0x18 + } + if m.NonceLow != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.NonceLow)) + i-- + dAtA[i] = 0x10 + } + if m.ChainId != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.ChainId)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryAllCctxPendingInNonceRangeResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryAllCctxPendingInNonceRangeResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryAllCctxPendingInNonceRangeResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.CrossChainTx) > 0 { + for iNdEx := len(m.CrossChainTx) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.CrossChainTx[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + func (m *QueryLastZetaHeightRequest) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7208,6 +7437,39 @@ func (m *QueryAllCctxPendingResponse) Size() (n int) { return n } +func (m *QueryAllCctxPendingInNonceRangeRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ChainId != 0 { + n += 1 + sovQuery(uint64(m.ChainId)) + } + if m.NonceLow != 0 { + n += 1 + sovQuery(uint64(m.NonceLow)) + } + if m.NonceHigh != 0 { + n += 1 + sovQuery(uint64(m.NonceHigh)) + } + return n +} + +func (m *QueryAllCctxPendingInNonceRangeResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.CrossChainTx) > 0 { + for _, e := range m.CrossChainTx { + l = e.Size() + n += 1 + l + sovQuery(uint64(l)) + } + } + return n +} + func (m *QueryLastZetaHeightRequest) Size() (n int) { if m == nil { return 0 @@ -11442,6 +11704,197 @@ func (m *QueryAllCctxPendingResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryAllCctxPendingInNonceRangeRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllCctxPendingInNonceRangeRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllCctxPendingInNonceRangeRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ChainId", wireType) + } + m.ChainId = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ChainId |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NonceLow", wireType) + } + m.NonceLow = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NonceLow |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field NonceHigh", wireType) + } + m.NonceHigh = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.NonceHigh |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryAllCctxPendingInNonceRangeResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryAllCctxPendingInNonceRangeResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryAllCctxPendingInNonceRangeResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CrossChainTx", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CrossChainTx = append(m.CrossChainTx, &CrossChainTx{}) + if err := m.CrossChainTx[len(m.CrossChainTx)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *QueryLastZetaHeightRequest) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/x/crosschain/types/query.pb.gw.go b/x/crosschain/types/query.pb.gw.go index 7832f5170c..cf9e4217b3 100644 --- a/x/crosschain/types/query.pb.gw.go +++ b/x/crosschain/types/query.pb.gw.go @@ -1031,6 +1031,42 @@ func local_request_Query_CctxAllPending_0(ctx context.Context, marshaler runtime } +var ( + filter_Query_CctxAllPendingInNonceRange_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Query_CctxAllPendingInNonceRange_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllCctxPendingInNonceRangeRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxAllPendingInNonceRange_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.CctxAllPendingInNonceRange(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_CctxAllPendingInNonceRange_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryAllCctxPendingInNonceRangeRequest + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Query_CctxAllPendingInNonceRange_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.CctxAllPendingInNonceRange(ctx, &protoReq) + return msg, metadata, err + +} + func request_Query_LastZetaHeight_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq QueryLastZetaHeightRequest var metadata runtime.ServerMetadata @@ -1602,6 +1638,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_CctxAllPendingInNonceRange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_CctxAllPendingInNonceRange_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CctxAllPendingInNonceRange_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_LastZetaHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2149,6 +2208,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_CctxAllPendingInNonceRange_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_CctxAllPendingInNonceRange_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_CctxAllPendingInNonceRange_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_Query_LastZetaHeight_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -2239,6 +2318,8 @@ var ( pattern_Query_CctxAllPending_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPending"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_CctxAllPendingInNonceRange_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "cctxPendingInNonceRange"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_LastZetaHeight_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "lastZetaHeight"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_TssHistory_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"zeta-chain", "crosschain", "tssHistory"}, "", runtime.AssumeColonVerbOpt(false))) @@ -2291,6 +2372,8 @@ var ( forward_Query_CctxAllPending_0 = runtime.ForwardResponseMessage + forward_Query_CctxAllPendingInNonceRange_0 = runtime.ForwardResponseMessage + forward_Query_LastZetaHeight_0 = runtime.ForwardResponseMessage forward_Query_TssHistory_0 = runtime.ForwardResponseMessage diff --git a/zetaclient/cctx_scanner.go b/zetaclient/cctx_scanner.go new file mode 100644 index 0000000000..e9d85f8419 --- /dev/null +++ b/zetaclient/cctx_scanner.go @@ -0,0 +1,264 @@ +package zetaclient + +import ( + "math" + "os" + "path/filepath" + "sort" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + + "github.com/rs/zerolog" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" +) + +const ( + RescanBatch uint64 = 10 +) + +// CctxScanner scans missed pending cctx and updates their status +type CctxScanner struct { + tssPubkey string + db *gorm.DB + logger *zerolog.Logger + bridge *ZetaCoreBridge + nextNonceToScan map[int64]uint64 // chainID -> next nonce to scan from + missedPendingCctx map[int64]map[uint64]*crosschaintypes.CrossChainTx // chainID -> nonce -> missed pending cctx +} + +func NewCctxScanner(bridge *ZetaCoreBridge, dbpath string, memDB bool, tssPubkey string, logger *zerolog.Logger) (*CctxScanner, error) { + sc := &CctxScanner{ + logger: logger, + bridge: bridge, + nextNonceToScan: make(map[int64]uint64), + missedPendingCctx: make(map[int64]map[uint64]*crosschaintypes.CrossChainTx), + } + err := sc.LoadDB(dbpath, memDB) + if err != nil { + return nil, err + } + + // on bootstrap or tss migration + if tssPubkey != sc.tssPubkey { + err = sc.Reset(tssPubkey) + if err != nil { + return nil, err + } + } + return sc, nil +} + +// ScanMissedPendingCctx scans a new batch of missed pending cctx +func (sc *CctxScanner) ScanMissedPendingCctx(bn int64, chainID int64, maxNonce uint64) { + // calculate nonce range to scan + nonceFrom, found := sc.nextNonceToScan[chainID] + if !found { + sc.nextNonceToScan[chainID] = 0 // start from scratch if not specified in db + sc.logger.Info().Msgf("scanner: scan pending cctx for chain %d from nonce 0", chainID) + } + nonceTo := nonceFrom + RescanBatch + if nonceTo > maxNonce { + nonceTo = maxNonce + } + + // scans [fromNonce, toNonce) for missed pending cctx + if nonceFrom < nonceTo { + missedList := make([]*crosschaintypes.CrossChainTx, 0) + for nonce := nonceFrom; nonce < nonceTo; nonce++ { + cctx, err := sc.bridge.GetCctxByNonce(chainID, nonce) + if err != nil { + sc.logger.Error().Err(err).Msgf("scanner: failed to get pending cctx for chain %d nonce %d", chainID, nonce) + nonceTo = nonce // stop scanning + break + } + if crosschainkeeper.IsPending(*cctx) { + missedList = append(missedList, cctx) + } + } + sc.addMissedPendingCctx(chainID, nonceFrom, nonceTo, missedList) + } +} + +func (sc *CctxScanner) EarliestPendingCctxByChain(chainID int64) *crosschaintypes.CrossChainTx { + oldPendingCctxs := sc.AllMissedPendingCctxByChain(chainID) + + // try removing finalized cctx + for _, cctx := range oldPendingCctxs { + nonce := cctx.GetCurrentOutTxParam().OutboundTxTssNonce + coreCctx, err := sc.bridge.GetCctxByNonce(chainID, nonce) + if err != nil { + sc.logger.Error().Err(err).Msgf("scanner: failed to get cctx for chain %d nonce %d", chainID, nonce) + return nil + } + nonceChanged := coreCctx.GetCurrentOutTxParam().OutboundTxTssNonce != nonce + if nonceChanged { + sc.logger.Info().Msgf("scanner: cctx nonce updated from %d to %d", nonce, coreCctx.GetCurrentOutTxParam().OutboundTxTssNonce) + } + if !crosschainkeeper.IsPending(*coreCctx) || nonceChanged { + sc.logger.Info().Msgf("scanner: removed finalized cctx for chain %d nonce %d", chainID, nonce) + sc.removeMissedPendingCctx(chainID, nonce) + } else { + break + } + } + + newPendingCctxs := sc.AllMissedPendingCctxByChain(chainID) + if len(newPendingCctxs) == 0 { + return nil + } + return newPendingCctxs[0] +} + +// Note: deep clone is unnecessary as the cctx list is used in a single thread +func (sc *CctxScanner) AllMissedPendingCctxByChain(chainID int64) []*crosschaintypes.CrossChainTx { + missed := make([]*crosschaintypes.CrossChainTx, 0) + for _, send := range sc.missedPendingCctx[chainID] { + missed = append(missed, send) + } + sort.Slice(missed, func(i, j int) bool { + return missed[i].GetCurrentOutTxParam().OutboundTxTssNonce < missed[j].GetCurrentOutTxParam().OutboundTxTssNonce + }) + return missed +} + +func (sc *CctxScanner) IsMissedPendingCctx(chainID int64, nonce uint64) bool { + _, found := sc.missedPendingCctx[chainID][nonce] + return found +} + +// Re-check and update missed cctx's status +func (sc *CctxScanner) UpdateMissedPendingCctxStatus(chainID int64, nonce uint64) { + send, err := sc.bridge.GetCctxByNonce(chainID, nonce) + if err != nil { + sc.logger.Error().Err(err).Msgf("scanner: error GetCctxByNonce for chain %d nonce %d", chainID, nonce) + return + } + // A missed cctx will pend forever if: + // 1. No tracker. For some reason(e.g., RPC failure), no observer had reported outtx hash to zetacore. + // 2. No true hash. Track exists but none of the hashes is true (can't be verified) + if !crosschainkeeper.IsPending(*send) { // no longer pending + sc.removeMissedPendingCctx(chainID, nonce) + sc.logger.Info().Msgf("scanner: removed missed pending cctx for chain %d nonce %d", chainID, nonce) + } +} + +func (sc *CctxScanner) addMissedPendingCctx(chainID int64, nonceFrom uint64, nonceTo uint64, missedList []*crosschaintypes.CrossChainTx) { + // initialize missed cctx map if not done yet + if _, found := sc.missedPendingCctx[chainID]; !found { + sc.missedPendingCctx[chainID] = make(map[uint64]*crosschaintypes.CrossChainTx) + } + + nonces := make([]uint64, 0) // for logging only + for _, send := range missedList { + nonce := send.GetCurrentOutTxParam().OutboundTxTssNonce + nonces = append(nonces, nonce) + sc.missedPendingCctx[chainID][nonce] = send + } + sc.nextNonceToScan[chainID] = nonceTo + if len(missedList) > 0 { + sc.saveFirstNonceToScan(chainID) + sc.logger.Info().Msgf("scanner: found missed pending cctx for chain %d with nonces %v", chainID, nonces) + } +} + +func (sc *CctxScanner) removeMissedPendingCctx(chainID int64, nonce uint64) { + delete(sc.missedPendingCctx[chainID], nonce) + sc.saveFirstNonceToScan(chainID) +} + +func (sc *CctxScanner) saveFirstNonceToScan(chainID int64) { + firstNonceToScan := uint64(math.MaxUint64) + if len(sc.missedPendingCctx[chainID]) == 0 { + // either no missed pending cctx found so far OR last missed pending cctx removed + firstNonceToScan = sc.nextNonceToScan[chainID] + } else { // save the lowest nonce for future restart if there ARE missed pending cctx + for nonceMissed := range sc.missedPendingCctx[chainID] { + if nonceMissed < firstNonceToScan { + firstNonceToScan = nonceMissed + } + } + } + if firstNonceToScan < uint64(math.MaxUint64) { + if err := sc.db.Save(clienttypes.ToFirstNonceToScanSQLType(chainID, firstNonceToScan)).Error; err != nil { + sc.logger.Error().Err(err).Msgf("scanner: error writing firstNonceToScan for chain %d nonce %d", chainID, firstNonceToScan) + } + } +} + +// LoadDB open sql database and load data into scanner +func (sc *CctxScanner) LoadDB(dbpath string, memDB bool) error { + if _, err := os.Stat(dbpath); os.IsNotExist(err) { + err := os.MkdirAll(dbpath, os.ModePerm) + if err != nil { + return err + } + } else if err != nil { + return err + } + path := dbpath + if !memDB { // memDB is used for uint test only + path = filepath.Join(dbpath, "scanner") + } + db, err := gorm.Open(sqlite.Open(path), &gorm.Config{}) + if err != nil { + return err + } + sc.db = db + + err = db.AutoMigrate(&clienttypes.CurrentTssSQLType{}, + &clienttypes.FirstNonceToScanSQLType{}) + if err != nil { + return err + } + + // Load current tss pubkey + sc.loadCurrentTssPubkey() + + // Load first nonce for each chain to start scanning from + return sc.buildFirstNonceToScanMap() +} + +func (sc *CctxScanner) Reset(tssPubkey string) error { + sc.tssPubkey = tssPubkey + sc.nextNonceToScan = make(map[int64]uint64) + sc.missedPendingCctx = make(map[int64]map[uint64]*crosschaintypes.CrossChainTx) + + // save current tss pubkey + if err := sc.db.Save(clienttypes.ToCurrentTssSQLType(tssPubkey)).Error; err != nil { + sc.logger.Error().Err(err).Msgf("scanner: error writing current tss pubkey %s", tssPubkey) + return err + } + + // clean db, GORM uses pluralizes struct name to snake_cases as table name + if err := sc.db.Exec("DELETE FROM first_nonce_to_scan_sql_types").Error; err != nil { + sc.logger.Error().Err(err).Msg("scanner: error cleaning FirstNonceToScan db") + return err + } + sc.logger.Info().Msgf("scanner: reset db successfully for tss pubkey %s", tssPubkey) + + return nil +} + +func (sc *CctxScanner) loadCurrentTssPubkey() { + var tss clienttypes.CurrentTssSQLType + if err := sc.db.First(&tss, clienttypes.CurrentTssID).Error; err != nil { + sc.logger.Info().Msg("scanner: use empty tss pubkey as db is empty") + } + sc.tssPubkey = tss.TssPubkey +} + +func (sc *CctxScanner) buildFirstNonceToScanMap() error { + var firstNonces []clienttypes.FirstNonceToScanSQLType + if err := sc.db.Find(&firstNonces).Error; err != nil { + sc.logger.Error().Err(err).Msg("scanner: error iterating over FirstNonceToScan db") + return err + } + for _, nonce := range firstNonces { + sc.nextNonceToScan[nonce.ID] = nonce.FirstNonce + sc.logger.Info().Msgf("scanner: the next nonce to scan for chain %d is %d", nonce.ID, nonce.FirstNonce) + } + return nil +} diff --git a/zetaclient/cctx_scanner_test.go b/zetaclient/cctx_scanner_test.go new file mode 100644 index 0000000000..254067a33e --- /dev/null +++ b/zetaclient/cctx_scanner_test.go @@ -0,0 +1,293 @@ +package zetaclient + +import ( + "os" + "testing" + + "github.com/rs/zerolog" + "github.com/stretchr/testify/require" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" + clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" +) + +const ( + tssPubkey = "zetapub1addwnpepqde0ztz2agdt0ss47dhdj2867ad63ju82f87a7h97memasegvnr3xehkryd" + tssPubkeyNew = "zetapub1addwnpepqfapt52wqw6k2kv0kvkuf8u0e8l37q57ntau7qu5ppz9sh690cs9cg0yxzs" +) + +// type alias for testing +type CCTX = crosschaintypes.CrossChainTx +type OutTxParam = crosschaintypes.OutboundTxParams + +func SetupTest(t *testing.T) *CctxScanner { + logger := zerolog.New(os.Stdout) + sc, err := NewCctxScanner(nil, TempSQLiteDbPath, true, tssPubkey, &logger) + require.NoError(t, err) + return sc +} + +func SaveNLoadNonces(t *testing.T, sc *CctxScanner, goerliNonce uint64, bsctestNonce uint64, mumbaiNonce uint64, btctestNonce uint64) { + goerli := clienttypes.ToFirstNonceToScanSQLType(5, goerliNonce) + bsctest := clienttypes.ToFirstNonceToScanSQLType(97, bsctestNonce) + mumbai := clienttypes.ToFirstNonceToScanSQLType(80001, mumbaiNonce) + btctest := clienttypes.ToFirstNonceToScanSQLType(18332, btctestNonce) + firstNonces := []*clienttypes.FirstNonceToScanSQLType{goerli, bsctest, mumbai, btctest} + for _, firstNonce := range firstNonces { + dbc := sc.db.Save(firstNonce) + require.NoError(t, dbc.Error) + } + err := sc.LoadDB(TempSQLiteDbPath, true) + require.NoError(t, err) +} + +// Restart the scanner by reloading the DB +func Restart(t *testing.T, tssPubkey string) *CctxScanner { + logger := zerolog.New(os.Stdout) + sc, err := NewCctxScanner(nil, TempSQLiteDbPath, true, tssPubkey, &logger) + require.NoError(t, err) + return sc +} + +func AddMissedCctxBatch1(sc *CctxScanner) (map[int64]map[uint64]*CCTX, map[int64]uint64, map[int64]uint64) { + // expected nonce map + expNextNonceToScanRestart := make(map[int64]uint64) + expNextNonceToScan := make(map[int64]uint64) + missedCctxMap := make(map[int64]map[uint64]*CCTX) + + // range [0, 1000] + cctx0 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 361}}} + goerliMissed := []*CCTX{cctx0} + expNextNonceToScanRestart[5] = 361 + expNextNonceToScan[5] = 1000 + missedCctxMap[5] = make(map[uint64]*CCTX) + missedCctxMap[5][361] = cctx0 + + // range [12000, 13000] + cctx1 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 12359}}} + cctx2 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 12007}}} + bscMissed := []*CCTX{cctx1, cctx2} + expNextNonceToScanRestart[97] = 12007 + expNextNonceToScan[97] = 13000 + missedCctxMap[97] = make(map[uint64]*CCTX) + missedCctxMap[97][12359] = cctx1 + missedCctxMap[97][12007] = cctx2 + + // range [4000, 5000] + cctx3 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 4081}}} + cctx4 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 4602}}} + cctx5 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 4600}}} + mumbaiMissed1 := []*CCTX{cctx3, cctx4, cctx5} + expNextNonceToScanRestart[80001] = 4081 + expNextNonceToScan[80001] = 5000 + missedCctxMap[80001] = make(map[uint64]*CCTX) + missedCctxMap[80001][4081] = cctx3 + missedCctxMap[80001][4602] = cctx4 + missedCctxMap[80001][4600] = cctx5 + + // range [11000, 12000] + cctx6 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 11475}}} + cctx7 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 11292}}} + cctx8 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 11528}}} + mumbaiMissed2 := []*CCTX{cctx6, cctx7, cctx8} + expNextNonceToScan[80001] = 12000 + missedCctxMap[80001][11475] = cctx6 + missedCctxMap[80001][11292] = cctx7 + missedCctxMap[80001][11528] = cctx8 + + sc.addMissedPendingCctx(5, 0, 1000, goerliMissed) + sc.addMissedPendingCctx(97, 12000, 13000, bscMissed) + sc.addMissedPendingCctx(80001, 4000, 5000, mumbaiMissed1) + sc.addMissedPendingCctx(80001, 11000, 12000, mumbaiMissed2) + + return missedCctxMap, expNextNonceToScanRestart, expNextNonceToScan +} + +func AddMissedCctxBatch2(sc *CctxScanner, missedCctxMap map[int64]map[uint64]*CCTX) map[int64]uint64 { + // expected nonce map + expNextNonceToScan := make(map[int64]uint64) + + // range [60000, 61000] + cctx0 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 60953}}} + cctx1 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 60437}}} + goerliMissed := []*CCTX{cctx0, cctx1} + expNextNonceToScan[5] = 61000 + missedCctxMap[5][60953] = cctx0 + missedCctxMap[5][60437] = cctx1 + + // range [14000, 15000] + cctx2 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 14271}}} + bscMissed := []*CCTX{cctx2} + expNextNonceToScan[97] = 15000 + missedCctxMap[97][14271] = cctx2 + + // range [23000, 24000] + cctx3 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 23651}}} + cctx4 := &CCTX{OutboundTxParams: []*OutTxParam{{OutboundTxTssNonce: 23494}}} + mumbaiMissed := []*CCTX{cctx3, cctx4} + expNextNonceToScan[80001] = 24000 + missedCctxMap[80001][23651] = cctx3 + missedCctxMap[80001][23494] = cctx4 + + sc.addMissedPendingCctx(5, 60000, 61000, goerliMissed) + sc.addMissedPendingCctx(97, 14000, 15000, bscMissed) + sc.addMissedPendingCctx(80001, 23000, 24000, mumbaiMissed) + + return expNextNonceToScan +} + +func CheckEmptyNonces(t *testing.T, sc *CctxScanner) { + require.Zero(t, sc.nextNonceToScan[5]) + require.Zero(t, sc.nextNonceToScan[97]) + require.Zero(t, sc.nextNonceToScan[80001]) +} + +func TestScannerDB(t *testing.T) { + sc := SetupTest(t) + + // Make sure all maps are empty + CheckEmptyNonces(t, sc) + + // Create some entries in the DB + SaveNLoadNonces(t, sc, 1, 41806, 17490, 138) + + // Check the DB nonces + var firstNonces1 []clienttypes.FirstNonceToScanSQLType + err := sc.db.Find(&firstNonces1).Error + require.NoError(t, err) + for _, firstNonce := range firstNonces1 { + want := sc.nextNonceToScan[firstNonce.ID] + have := firstNonce.FirstNonce + require.Equal(t, want, have) + } + + // Update entries in the DB + SaveNLoadNonces(t, sc, 2349, 51570, 21086, 259) + + // Check the DB nonces again + var firstNonces2 []clienttypes.FirstNonceToScanSQLType + err = sc.db.Find(&firstNonces2).Error + require.NoError(t, err) + for _, firstNonce := range firstNonces2 { + want := sc.nextNonceToScan[firstNonce.ID] + have := firstNonce.FirstNonce + require.Equal(t, want, have) + } + + // Tear down + sc.Reset("") +} + +func TestScannerDBReset(t *testing.T) { + sc := SetupTest(t) + + // Create some entries in the DB + SaveNLoadNonces(t, sc, 1, 41806, 17490, 138) + + // Restart scanner with different tss pubkey + sc = Restart(t, tssPubkeyNew) + + // Make sure all maps are empty again + CheckEmptyNonces(t, sc) + + // Tear down + sc.Reset("") +} + +func TestCctxNonces(t *testing.T) { + sc := SetupTest(t) + + // Add some missed pending cctx + allMissedMap, expFirstNonceMapRestart, expNextNonceMap := AddMissedCctxBatch1(sc) + + // Check the next nonce to scan + for chainID, want := range expNextNonceMap { + have := sc.nextNonceToScan[chainID] + require.Equal(t, want, have) + } + + // Add some more missed pending cctx + expNextNonceMap = AddMissedCctxBatch2(sc, allMissedMap) + + // Check the next nonce to scan + for chainID, want := range expNextNonceMap { + have := sc.nextNonceToScan[chainID] + require.Equal(t, want, have) // next nonce should change + } + + // Restart the scanner + sc = Restart(t, tssPubkey) + + // Check the next nonce to scan again + for chainID, want := range expFirstNonceMapRestart { + have := sc.nextNonceToScan[chainID] + require.Equal(t, want, have) // next nonce should fall back to first nonce after restart + } + + // Tear down + sc.Reset("") +} + +func CheckMissedCctxByChain(t *testing.T, sc *CctxScanner, allMissedMap map[int64]map[uint64]*crosschaintypes.CrossChainTx, chainID int64) { + chainMissed := sc.AllMissedPendingCctxByChain(chainID) + require.Equal(t, len(allMissedMap[chainID]), len(chainMissed)) + for _, have := range chainMissed { + want := allMissedMap[chainID][have.OutboundTxParams[0].OutboundTxTssNonce] + require.Equal(t, *want, *have) + } +} + +func TestGetMissedPendingCctxByChain(t *testing.T) { + sc := SetupTest(t) + + // Add some missed pending cctx + allMissedMap, _, _ := AddMissedCctxBatch1(sc) + + // Check missed cctx list for goerli, bsc, mumbai + CheckMissedCctxByChain(t, sc, allMissedMap, 5) + CheckMissedCctxByChain(t, sc, allMissedMap, 97) + CheckMissedCctxByChain(t, sc, allMissedMap, 80001) + + // Add some more missed pending cctx + _ = AddMissedCctxBatch2(sc, allMissedMap) + + // Check missed cctx list for goerli, bsc, mumbai again + CheckMissedCctxByChain(t, sc, allMissedMap, 5) + CheckMissedCctxByChain(t, sc, allMissedMap, 97) + CheckMissedCctxByChain(t, sc, allMissedMap, 80001) + + // Tear down + sc.Reset("") +} + +func TestRemoveMissedPendingCctx(t *testing.T) { + sc := SetupTest(t) + + // Add some missed pending cctx + _, expNextNonceMapRestart, expNextNonceMap := AddMissedCctxBatch1(sc) + + // Remove a goerli missed cctx, edge case: delete the only cctx + sc.removeMissedPendingCctx(5, 361) + require.Nil(t, sc.missedPendingCctx[5][361]) + require.Equal(t, expNextNonceMap[5], sc.nextNonceToScan[5]) // won't affect next nonce + + // Remove some bsc missed cctx + sc.removeMissedPendingCctx(97, 12359) + require.Nil(t, sc.missedPendingCctx[97][12359]) + require.Equal(t, expNextNonceMap[97], sc.nextNonceToScan[97]) // won't affect next nonce + + // Remove some mumbai missed cctx + sc.removeMissedPendingCctx(80001, 4600) + sc.removeMissedPendingCctx(80001, 11528) + require.Nil(t, sc.missedPendingCctx[80001][4600]) + require.Nil(t, sc.missedPendingCctx[80001][11528]) + require.Equal(t, expNextNonceMap[80001], sc.nextNonceToScan[80001]) // won't affect next nonce + + // Restart the scanner anc check nonces + sc = Restart(t, tssPubkey) + require.Equal(t, expNextNonceMap[5], sc.nextNonceToScan[5]) // next nonce fall back to first nonce for goerli, , EDGE CASE: next nonce should be 1000 + require.Equal(t, expNextNonceMapRestart[97], sc.nextNonceToScan[97]) // next nonce fall back to first nonce for bsc + require.Equal(t, expNextNonceMapRestart[80001], sc.nextNonceToScan[80001]) // next nonce fall back to first nonce for mumbai + + // Tear down + sc.Reset("") // clean db after each test +} diff --git a/zetaclient/evm_client.go b/zetaclient/evm_client.go index 911c0ce9c4..1a0a69f9be 100644 --- a/zetaclient/evm_client.go +++ b/zetaclient/evm_client.go @@ -38,6 +38,7 @@ import ( "github.com/zeta-chain/zetacore/common" "github.com/zeta-chain/zetacore/zetaclient/config" + crosschainkeeper "github.com/zeta-chain/zetacore/x/crosschain/keeper" "github.com/zeta-chain/zetacore/x/crosschain/types" observertypes "github.com/zeta-chain/zetacore/x/observer/types" clienttypes "github.com/zeta-chain/zetacore/zetaclient/types" @@ -467,6 +468,9 @@ var lowestOutTxNonceToObserve = map[int64]uint64{ 80001: 154500, // Mumbai } +// the number of garbage trackers to look back +const garbageTrackersLookback = 2 + // FIXME: there's a chance that a txhash in OutTxChan may not deliver when Stop() is called // observeOutTx periodically checks all the txhash in potential outbound txs func (ob *EVMChainClient) observeOutTx() { @@ -494,28 +498,41 @@ func (ob *EVMChainClient) observeOutTx() { return trackers[i].Nonce < trackers[j].Nonce }) outTimeout := time.After(time.Duration(timeoutNonce) * time.Second) + garbageTrackerChecked := 0 TRACKERLOOP: - // Skip old gabbage trackers as we spent too much time on querying them for _, tracker := range trackers { nonceInt := tracker.Nonce if nonceInt < lowestOutTxNonceToObserve[ob.chain.ChainId] { + // Look into only a few old gabbage trackers for those old pending-forever cctxs + if garbageTrackerChecked >= garbageTrackersLookback { + continue + } + // Skip those problematic trackers whose associated cctxs are no longer pending (Reverted/Outboundminted/Aborted) + cctx, err := ob.zetaClient.GetCctxByNonce(ob.chain.ChainId, nonceInt) + if err != nil || cctx == nil { + ob.logger.ObserveOutTx.Error().Err(err).Msgf("garbage tracker, error GetCctxByNonce for chain %s nonce %d", ob.chain.String(), nonceInt) + continue + } + if !crosschainkeeper.IsPending(*cctx) { + ob.logger.ObserveOutTx.Info().Msgf("garbage tracker chain %s nonce %d is not pending", ob.chain.String(), nonceInt) + continue + } + } + + // Go to next tracker if this one is already confirmed + ob.mu.Lock() + _, found := ob.outTXConfirmedReceipts[ob.GetTxID(nonceInt)] + ob.mu.Unlock() + if found { continue } - TXHASHLOOP: + trueHash := "" for _, txHash := range tracker.HashList { - //inTimeout := time.After(3000 * time.Millisecond) select { case <-outTimeout: ob.logger.ObserveOutTx.Warn().Msgf("observeOutTx timeout on chain %d nonce %d", ob.chain.ChainId, nonceInt) break TRACKERLOOP default: - ob.mu.Lock() - _, found := ob.outTXConfirmedReceipts[ob.GetTxID(nonceInt)] - ob.mu.Unlock() - if found { - continue - } - receipt, transaction, err := ob.queryTxByHash(txHash.TxHash, nonceInt) time.Sleep(time.Duration(rpcRestTime) * time.Millisecond) if err == nil && receipt != nil { // confirmed @@ -523,15 +540,21 @@ func (ob *EVMChainClient) observeOutTx() { ob.outTXConfirmedReceipts[ob.GetTxID(nonceInt)] = receipt ob.outTXConfirmedTransaction[ob.GetTxID(nonceInt)] = transaction ob.mu.Unlock() + trueHash = txHash.TxHash + ob.logger.ObserveOutTx.Info().Msgf("observeOutTx confirmed outTx %s for chain %d nonce %d", txHash.TxHash, ob.chain.ChainId, nonceInt) - break TXHASHLOOP + break } if err != nil { ob.logger.ObserveOutTx.Debug().Err(err).Msgf("error queryTxByHash: chain %s hash %s", ob.chain.String(), txHash.TxHash) } - //<-inTimeout } } + // Count the number of garbage trackers checked + if nonceInt < lowestOutTxNonceToObserve[ob.chain.ChainId] { + garbageTrackerChecked++ + ob.logger.ObserveOutTx.Info().Msgf("garbage tracker checked for chain %d nonce %d txHash %s", ob.chain.ChainId, nonceInt, trueHash) + } } ticker.UpdateInterval(ob.GetCoreParams().OutTxTicker, ob.logger.ObserveOutTx) case <-ob.stop: diff --git a/zetaclient/query.go b/zetaclient/query.go index 909e2bdc56..eb5338e34e 100644 --- a/zetaclient/query.go +++ b/zetaclient/query.go @@ -145,6 +145,20 @@ func (b *ZetaCoreBridge) GetAllPendingCctx(chainID int64) ([]*types.CrossChainTx return resp.CrossChainTx, nil } +func (b *ZetaCoreBridge) GetAllPendingCctxInNonceRange(chainID int64, nonceLow uint64, nonceHigh uint64) ([]*types.CrossChainTx, error) { + client := types.NewQueryClient(b.grpcConn) + maxSizeOption := grpc.MaxCallRecvMsgSize(32 * 1024 * 1024) + resp, err := client.CctxAllPendingInNonceRange(context.Background(), &types.QueryAllCctxPendingInNonceRangeRequest{ + ChainId: chainID, + NonceLow: nonceLow, + NonceHigh: nonceHigh, + }, maxSizeOption) + if err != nil { + return nil, err + } + return resp.CrossChainTx, nil +} + func (b *ZetaCoreBridge) GetLastBlockHeight() ([]*types.LastBlockHeight, error) { client := types.NewQueryClient(b.grpcConn) resp, err := client.LastBlockHeightAll(context.Background(), &types.QueryAllLastBlockHeightRequest{}) diff --git a/zetaclient/types/sql_observer.go b/zetaclient/types/sql_observer.go new file mode 100644 index 0000000000..5cbde313b4 --- /dev/null +++ b/zetaclient/types/sql_observer.go @@ -0,0 +1,33 @@ +package types + +import ( + "gorm.io/gorm" +) + +const CurrentTssID = 0xACEE + +type CurrentTssSQLType struct { + gorm.Model + ID int64 + TssPubkey string +} + +func ToCurrentTssSQLType(tssPubkey string) *CurrentTssSQLType { + return &CurrentTssSQLType{ + ID: CurrentTssID, + TssPubkey: tssPubkey, + } +} + +type FirstNonceToScanSQLType struct { + gorm.Model + ID int64 + FirstNonce uint64 +} + +func ToFirstNonceToScanSQLType(chainID int64, firstNonceToScan uint64) *FirstNonceToScanSQLType { + return &FirstNonceToScanSQLType{ + ID: chainID, + FirstNonce: firstNonceToScan, + } +} diff --git a/zetaclient/zetacore_observer.go b/zetaclient/zetacore_observer.go index 1b6ecaec26..a11cd1e4e8 100644 --- a/zetaclient/zetacore_observer.go +++ b/zetaclient/zetacore_observer.go @@ -28,6 +28,7 @@ type CoreObserver struct { bridge *ZetaCoreBridge signerMap map[common.Chain]ChainSigner clientMap map[common.Chain]ChainClient + scanner *CctxScanner metrics *metrics.Metrics tss *TSS logger ZetaCoreLog @@ -36,7 +37,7 @@ type CoreObserver struct { stop chan struct{} } -func NewCoreObserver(bridge *ZetaCoreBridge, signerMap map[common.Chain]ChainSigner, clientMap map[common.Chain]ChainClient, metrics *metrics.Metrics, tss *TSS, logger zerolog.Logger, cfg *config.Config, ts *TelemetryServer) *CoreObserver { +func NewCoreObserver(bridge *ZetaCoreBridge, signerMap map[common.Chain]ChainSigner, clientMap map[common.Chain]ChainClient, dbpath string, metrics *metrics.Metrics, tss *TSS, logger zerolog.Logger, cfg *config.Config, ts *TelemetryServer) *CoreObserver { co := CoreObserver{ ts: ts, stop: make(chan struct{}), @@ -62,6 +63,12 @@ func NewCoreObserver(bridge *ZetaCoreBridge, signerMap map[common.Chain]ChainSig co.logger.ChainLogger.Error().Err(err).Msg("error registering counter") } + scanner, err := NewCctxScanner(bridge, dbpath, false, tss.CurrentPubkey, &co.logger.ZetaChainWatcher) + if err == nil { + co.scanner = scanner + } else { + co.logger.ChainLogger.Error().Err(err).Msg("error creating cctx scanner") + } return &co } @@ -133,6 +140,31 @@ func (co *CoreObserver) startSendScheduler() { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("failed to GetAllPendingCctx for chain %s", c.ChainName.String()) continue } + + // Hotfix for EVM pending forever cctx + if common.IsEVMChain(c.ChainId) { + pendingNonces, err := co.bridge.GetPendingNoncesByChain(c.ChainId) + if err != nil { + co.logger.ZetaChainWatcher.Error().Err(err).Msgf("failed to GetPendingNoncesByChain for chain %s", c.ChainName.String()) + continue + } + if len(cctxList) > 0 { // adjust nonceLow to avoid scanning overlapped nonce range of GetAllPendingCctx + pendingNonces.NonceLow = int64(cctxList[0].GetCurrentOutTxParam().OutboundTxTssNonce) + } + + // for smoketest + // cctxList = []*types.CrossChainTx{} + // pendingNonces.NonceLow = pendingNonces.NonceHigh + + // Scan missed pending (forever) cctx in history and get the earliest + // #nosec G701 non negative value + co.scanner.ScanMissedPendingCctx(bn, c.ChainId, uint64(pendingNonces.NonceLow)) + earliestCctx := co.scanner.EarliestPendingCctxByChain(c.ChainId) + if earliestCctx != nil { // append earliest missed pending cctx to head + cctxList = append([]*types.CrossChainTx{earliestCctx}, cctxList...) + } + } + ob, err := co.getUpdatedChainOb(c.ChainId) if err != nil { co.logger.ZetaChainWatcher.Error().Err(err).Msgf("getTargetChainOb fail, Chain ID: %s", c.ChainName)