diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index 0ffed9b39..d8e57bd45 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -255,6 +255,10 @@ func (cli *grpcClient) ApplySnapshotChunk(ctx context.Context, params *types.Req return cli.client.ApplySnapshotChunk(ctx, types.ToRequestApplySnapshotChunk(params).GetApplySnapshotChunk(), grpc.WaitForReady(true)) } +func (cli *grpcClient) FinalizeSnapshot(ctx context.Context, params *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error) { + return cli.client.FinalizeSnapshot(ctx, types.ToRequestFinalizeSnapshot(params).GetFinalizeSnapshot(), grpc.WaitForReady(true)) +} + func (cli *grpcClient) PrepareProposal(ctx context.Context, params *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { return cli.client.PrepareProposal(ctx, types.ToRequestPrepareProposal(params).GetPrepareProposal(), grpc.WaitForReady(true)) } diff --git a/abci/client/mocks/client.go b/abci/client/mocks/client.go index 7c2d06285..9994b266d 100644 --- a/abci/client/mocks/client.go +++ b/abci/client/mocks/client.go @@ -362,6 +362,65 @@ func (_c *Client_FinalizeBlock_Call) RunAndReturn(run func(context.Context, *typ return _c } +// FinalizeSnapshot provides a mock function with given fields: _a0, _a1 +func (_m *Client) FinalizeSnapshot(_a0 context.Context, _a1 *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for FinalizeSnapshot") + } + + var r0 *types.ResponseFinalizeSnapshot + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeSnapshot) *types.ResponseFinalizeSnapshot); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseFinalizeSnapshot) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeSnapshot) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_FinalizeSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FinalizeSnapshot' +type Client_FinalizeSnapshot_Call struct { + *mock.Call +} + +// FinalizeSnapshot is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *types.RequestFinalizeSnapshot +func (_e *Client_Expecter) FinalizeSnapshot(_a0 interface{}, _a1 interface{}) *Client_FinalizeSnapshot_Call { + return &Client_FinalizeSnapshot_Call{Call: _e.mock.On("FinalizeSnapshot", _a0, _a1)} +} + +func (_c *Client_FinalizeSnapshot_Call) Run(run func(_a0 context.Context, _a1 *types.RequestFinalizeSnapshot)) *Client_FinalizeSnapshot_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.RequestFinalizeSnapshot)) + }) + return _c +} + +func (_c *Client_FinalizeSnapshot_Call) Return(_a0 *types.ResponseFinalizeSnapshot, _a1 error) *Client_FinalizeSnapshot_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_FinalizeSnapshot_Call) RunAndReturn(run func(context.Context, *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error)) *Client_FinalizeSnapshot_Call { + _c.Call.Return(run) + return _c +} + // Flush provides a mock function with given fields: _a0 func (_m *Client) Flush(_a0 context.Context) error { ret := _m.Called(_a0) diff --git a/abci/client/routed_client.go b/abci/client/routed_client.go index fa1afc1bc..2bc4a8aac 100644 --- a/abci/client/routed_client.go +++ b/abci/client/routed_client.go @@ -351,6 +351,11 @@ func (cli *routedClient) ApplySnapshotChunk(ctx context.Context, req *types.Requ return result.(*types.ResponseApplySnapshotChunk), err } +func (cli *routedClient) FinalizeSnapshot(ctx context.Context, req *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error) { + result, err := cli.delegate(ctx, req) + return result.(*types.ResponseFinalizeSnapshot), err +} + func (cli *routedClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { result, err := cli.delegate(ctx, req) return result.(*types.ResponsePrepareProposal), err diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index fb046187d..4ca9e0739 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -357,6 +357,14 @@ func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.Requ return res.GetApplySnapshotChunk(), nil } +func (cli *socketClient) FinalizeSnapshot(ctx context.Context, req *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error) { + res, err := cli.doRequest(ctx, types.ToRequestFinalizeSnapshot(req)) + if err != nil { + return nil, err + } + return res.GetFinalizeSnapshot(), nil +} + func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) { res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req)) if err != nil { @@ -429,6 +437,8 @@ func resMatchesReq(req *types.Request, res *types.Response) (ok bool) { _, ok = res.Value.(*types.Response_ListSnapshots) case *types.Request_OfferSnapshot: _, ok = res.Value.(*types.Response_OfferSnapshot) + case *types.Request_FinalizeSnapshot: + _, ok = res.Value.(*types.Response_FinalizeSnapshot) case *types.Request_FinalizeBlock: _, ok = res.Value.(*types.Response_FinalizeBlock) } diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index bf952d5ab..6b8c91939 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -579,6 +579,31 @@ func (app *Application) ApplySnapshotChunk(_ context.Context, req *abci.RequestA app.logger.Debug("ApplySnapshotChunk", "resp", resp) return resp, nil } + +func (app *Application) FinalizeSnapshot(_ctx context.Context, req *abci.RequestFinalizeSnapshot) (*abci.ResponseFinalizeSnapshot, error) { + app.mu.Lock() + defer app.mu.Unlock() + + // we only run some verifications here + + if app.LastCommittedState.GetHeight() != req.SnapshotBlock.SignedHeader.Header.Height { + return &abci.ResponseFinalizeSnapshot{}, fmt.Errorf("snapshot height mismatch during state sync finalization: expected %d, got %d", + app.LastCommittedState.GetHeight(), req.SnapshotBlock.SignedHeader.Header.Height) + } + + if !app.LastCommittedState.GetAppHash().Equal(req.SnapshotBlock.SignedHeader.Header.AppHash) { + return &abci.ResponseFinalizeSnapshot{}, fmt.Errorf("snapshot apphash mismatch during state sync finalization: expected %x, got %x", + app.LastCommittedState.GetAppHash(), req.SnapshotBlock.SignedHeader.Header.AppHash) + } + + if app.initialHeight != req.GenesisBlock.SignedHeader.Header.Height { + return &abci.ResponseFinalizeSnapshot{}, fmt.Errorf("genesis height mismatch during state sync finalization: expected %d, got %d", + app.initialHeight, req.GenesisBlock.SignedHeader.Header.Height) + } + + app.logger.Debug("FinalizeSnapshot finished successfully", "req", req) + return &abci.ResponseFinalizeSnapshot{}, nil +} func (app *Application) appVersionForHeight(height int64) uint64 { if app.appVersion == 0 { return uint64(height) diff --git a/abci/types/application.go b/abci/types/application.go index fe7b60bca..3a19116bd 100644 --- a/abci/types/application.go +++ b/abci/types/application.go @@ -16,6 +16,8 @@ type StateSyncer interface { LoadSnapshotChunk(context.Context, *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) // ApplySnapshotChunk applies a chunk of snapshot ApplySnapshotChunk(context.Context, *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) + // FinalizeSnapshot sends light block to ABCI app after successful state sync + FinalizeSnapshot(context.Context, *RequestFinalizeSnapshot) (*ResponseFinalizeSnapshot, error) } // Application is an interface that enables any finite, deterministic state machine @@ -97,6 +99,11 @@ func (BaseApplication) ApplySnapshotChunk(_ context.Context, _req *RequestApplyS return &ResponseApplySnapshotChunk{}, nil } +// FinalizeSnapshot provides a no-op implementation for the StateSyncer interface +func (BaseApplication) FinalizeSnapshot(_ context.Context, _req *RequestFinalizeSnapshot) (*ResponseFinalizeSnapshot, error) { + return &ResponseFinalizeSnapshot{}, nil +} + func (BaseApplication) PrepareProposal(_ context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) { trs := make([]*TxRecord, 0, len(req.Txs)) var totalBytes int64 diff --git a/abci/types/messages.go b/abci/types/messages.go index 4a749fc83..5c058a791 100644 --- a/abci/types/messages.go +++ b/abci/types/messages.go @@ -87,6 +87,12 @@ func ToRequestApplySnapshotChunk(req *RequestApplySnapshotChunk) *Request { } } +func ToRequestFinalizeSnapshot(req *RequestFinalizeSnapshot) *Request { + return &Request{ + Value: &Request_FinalizeSnapshot{req}, + } +} + func ToRequestExtendVote(req *RequestExtendVote) *Request { return &Request{ Value: &Request_ExtendVote{req}, diff --git a/abci/types/mocks/application.go b/abci/types/mocks/application.go index 1a706370d..5e0404ee5 100644 --- a/abci/types/mocks/application.go +++ b/abci/types/mocks/application.go @@ -258,6 +258,65 @@ func (_c *Application_FinalizeBlock_Call) RunAndReturn(run func(context.Context, return _c } +// FinalizeSnapshot provides a mock function with given fields: _a0, _a1 +func (_m *Application) FinalizeSnapshot(_a0 context.Context, _a1 *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for FinalizeSnapshot") + } + + var r0 *types.ResponseFinalizeSnapshot + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeSnapshot) *types.ResponseFinalizeSnapshot); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.ResponseFinalizeSnapshot) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeSnapshot) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Application_FinalizeSnapshot_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'FinalizeSnapshot' +type Application_FinalizeSnapshot_Call struct { + *mock.Call +} + +// FinalizeSnapshot is a helper method to define mock.On call +// - _a0 context.Context +// - _a1 *types.RequestFinalizeSnapshot +func (_e *Application_Expecter) FinalizeSnapshot(_a0 interface{}, _a1 interface{}) *Application_FinalizeSnapshot_Call { + return &Application_FinalizeSnapshot_Call{Call: _e.mock.On("FinalizeSnapshot", _a0, _a1)} +} + +func (_c *Application_FinalizeSnapshot_Call) Run(run func(_a0 context.Context, _a1 *types.RequestFinalizeSnapshot)) *Application_FinalizeSnapshot_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(*types.RequestFinalizeSnapshot)) + }) + return _c +} + +func (_c *Application_FinalizeSnapshot_Call) Return(_a0 *types.ResponseFinalizeSnapshot, _a1 error) *Application_FinalizeSnapshot_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Application_FinalizeSnapshot_Call) RunAndReturn(run func(context.Context, *types.RequestFinalizeSnapshot) (*types.ResponseFinalizeSnapshot, error)) *Application_FinalizeSnapshot_Call { + _c.Call.Return(run) + return _c +} + // Info provides a mock function with given fields: _a0, _a1 func (_m *Application) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) { ret := _m.Called(_a0, _a1) diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index f60329ec9..4d650aed8 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -124,7 +124,7 @@ func (x ResponseOfferSnapshot_Result) String() string { } func (ResponseOfferSnapshot_Result) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{25, 0} + return fileDescriptor_252557cfdd89a31a, []int{26, 0} } type ResponseApplySnapshotChunk_Result int32 @@ -164,7 +164,7 @@ func (x ResponseApplySnapshotChunk_Result) String() string { } func (ResponseApplySnapshotChunk_Result) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{27, 0} + return fileDescriptor_252557cfdd89a31a, []int{28, 0} } type ResponseProcessProposal_ProposalStatus int32 @@ -192,7 +192,7 @@ func (x ResponseProcessProposal_ProposalStatus) String() string { } func (ResponseProcessProposal_ProposalStatus) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{29, 0} + return fileDescriptor_252557cfdd89a31a, []int{31, 0} } type ResponseVerifyVoteExtension_VerifyStatus int32 @@ -220,7 +220,7 @@ func (x ResponseVerifyVoteExtension_VerifyStatus) String() string { } func (ResponseVerifyVoteExtension_VerifyStatus) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{32, 0} + return fileDescriptor_252557cfdd89a31a, []int{34, 0} } // TxAction contains App-provided information on what to do with a transaction that is part of a raw proposal @@ -255,7 +255,7 @@ func (x TxRecord_TxAction) String() string { } func (TxRecord_TxAction) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{39, 0} + return fileDescriptor_252557cfdd89a31a, []int{41, 0} } // Request types @@ -271,6 +271,7 @@ type Request struct { // *Request_OfferSnapshot // *Request_LoadSnapshotChunk // *Request_ApplySnapshotChunk + // *Request_FinalizeSnapshot // *Request_PrepareProposal // *Request_ProcessProposal // *Request_ExtendVote @@ -348,6 +349,9 @@ type Request_LoadSnapshotChunk struct { type Request_ApplySnapshotChunk struct { ApplySnapshotChunk *RequestApplySnapshotChunk `protobuf:"bytes,14,opt,name=apply_snapshot_chunk,json=applySnapshotChunk,proto3,oneof" json:"apply_snapshot_chunk,omitempty"` } +type Request_FinalizeSnapshot struct { + FinalizeSnapshot *RequestFinalizeSnapshot `protobuf:"bytes,20,opt,name=finalize_snapshot,json=finalizeSnapshot,proto3,oneof" json:"finalize_snapshot,omitempty"` +} type Request_PrepareProposal struct { PrepareProposal *RequestPrepareProposal `protobuf:"bytes,15,opt,name=prepare_proposal,json=prepareProposal,proto3,oneof" json:"prepare_proposal,omitempty"` } @@ -374,6 +378,7 @@ func (*Request_ListSnapshots) isRequest_Value() {} func (*Request_OfferSnapshot) isRequest_Value() {} func (*Request_LoadSnapshotChunk) isRequest_Value() {} func (*Request_ApplySnapshotChunk) isRequest_Value() {} +func (*Request_FinalizeSnapshot) isRequest_Value() {} func (*Request_PrepareProposal) isRequest_Value() {} func (*Request_ProcessProposal) isRequest_Value() {} func (*Request_ExtendVote) isRequest_Value() {} @@ -457,6 +462,13 @@ func (m *Request) GetApplySnapshotChunk() *RequestApplySnapshotChunk { return nil } +func (m *Request) GetFinalizeSnapshot() *RequestFinalizeSnapshot { + if x, ok := m.GetValue().(*Request_FinalizeSnapshot); ok { + return x.FinalizeSnapshot + } + return nil +} + func (m *Request) GetPrepareProposal() *RequestPrepareProposal { if x, ok := m.GetValue().(*Request_PrepareProposal); ok { return x.PrepareProposal @@ -505,6 +517,7 @@ func (*Request) XXX_OneofWrappers() []interface{} { (*Request_OfferSnapshot)(nil), (*Request_LoadSnapshotChunk)(nil), (*Request_ApplySnapshotChunk)(nil), + (*Request_FinalizeSnapshot)(nil), (*Request_PrepareProposal)(nil), (*Request_ProcessProposal)(nil), (*Request_ExtendVote)(nil), @@ -1161,6 +1174,67 @@ func (m *RequestApplySnapshotChunk) GetSender() string { return "" } +// RequestFinalizeSnapshot is called by Tenderdash after successfully applying all snapshot chunks, e.g. +// when the ABCI application returned `ResponseApplySnapshotChunk` with `Result = ACCEPT`. +// It includes the light block committed at the synced height, which Tenderdash uses to reconstruct its own state. +// The application should validate the light block against its restored state. +// +// If the application fails to validate the light block, it should return error in the response. +// This is considered fatal, non-recoverable consensus failure and will cause Tenderdash to restart. +type RequestFinalizeSnapshot struct { + // Snapshot block is a block at which the snapshot was taken. + SnapshotBlock *types1.LightBlock `protobuf:"bytes,1,opt,name=snapshot_block,json=snapshotBlock,proto3" json:"snapshot_block,omitempty"` + // Genesis block is the first block of the chain + GenesisBlock *types1.LightBlock `protobuf:"bytes,2,opt,name=genesis_block,json=genesisBlock,proto3" json:"genesis_block,omitempty"` +} + +func (m *RequestFinalizeSnapshot) Reset() { *m = RequestFinalizeSnapshot{} } +func (m *RequestFinalizeSnapshot) String() string { return proto.CompactTextString(m) } +func (*RequestFinalizeSnapshot) ProtoMessage() {} +func (*RequestFinalizeSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{11} +} +func (m *RequestFinalizeSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *RequestFinalizeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_RequestFinalizeSnapshot.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 *RequestFinalizeSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_RequestFinalizeSnapshot.Merge(m, src) +} +func (m *RequestFinalizeSnapshot) XXX_Size() int { + return m.Size() +} +func (m *RequestFinalizeSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_RequestFinalizeSnapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_RequestFinalizeSnapshot proto.InternalMessageInfo + +func (m *RequestFinalizeSnapshot) GetSnapshotBlock() *types1.LightBlock { + if m != nil { + return m.SnapshotBlock + } + return nil +} + +func (m *RequestFinalizeSnapshot) GetGenesisBlock() *types1.LightBlock { + if m != nil { + return m.GenesisBlock + } + return nil +} + // Prepare new block proposal, potentially altering list of transactions. // // #### Usage @@ -1280,7 +1354,7 @@ func (m *RequestPrepareProposal) Reset() { *m = RequestPrepareProposal{} func (m *RequestPrepareProposal) String() string { return proto.CompactTextString(m) } func (*RequestPrepareProposal) ProtoMessage() {} func (*RequestPrepareProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{11} + return fileDescriptor_252557cfdd89a31a, []int{12} } func (m *RequestPrepareProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1480,7 +1554,7 @@ func (m *RequestProcessProposal) Reset() { *m = RequestProcessProposal{} func (m *RequestProcessProposal) String() string { return proto.CompactTextString(m) } func (*RequestProcessProposal) ProtoMessage() {} func (*RequestProcessProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{12} + return fileDescriptor_252557cfdd89a31a, []int{13} } func (m *RequestProcessProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1652,7 +1726,7 @@ func (m *RequestExtendVote) Reset() { *m = RequestExtendVote{} } func (m *RequestExtendVote) String() string { return proto.CompactTextString(m) } func (*RequestExtendVote) ProtoMessage() {} func (*RequestExtendVote) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{13} + return fileDescriptor_252557cfdd89a31a, []int{14} } func (m *RequestExtendVote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1747,7 +1821,7 @@ func (m *RequestVerifyVoteExtension) Reset() { *m = RequestVerifyVoteExt func (m *RequestVerifyVoteExtension) String() string { return proto.CompactTextString(m) } func (*RequestVerifyVoteExtension) ProtoMessage() {} func (*RequestVerifyVoteExtension) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{14} + return fileDescriptor_252557cfdd89a31a, []int{15} } func (m *RequestVerifyVoteExtension) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1858,7 +1932,7 @@ func (m *RequestFinalizeBlock) Reset() { *m = RequestFinalizeBlock{} } func (m *RequestFinalizeBlock) String() string { return proto.CompactTextString(m) } func (*RequestFinalizeBlock) ProtoMessage() {} func (*RequestFinalizeBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{15} + return fileDescriptor_252557cfdd89a31a, []int{16} } func (m *RequestFinalizeBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1950,6 +2024,7 @@ type Response struct { // *Response_OfferSnapshot // *Response_LoadSnapshotChunk // *Response_ApplySnapshotChunk + // *Response_FinalizeSnapshot // *Response_PrepareProposal // *Response_ProcessProposal // *Response_ExtendVote @@ -1962,7 +2037,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{16} + return fileDescriptor_252557cfdd89a31a, []int{17} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2030,6 +2105,9 @@ type Response_LoadSnapshotChunk struct { type Response_ApplySnapshotChunk struct { ApplySnapshotChunk *ResponseApplySnapshotChunk `protobuf:"bytes,11,opt,name=apply_snapshot_chunk,json=applySnapshotChunk,proto3,oneof" json:"apply_snapshot_chunk,omitempty"` } +type Response_FinalizeSnapshot struct { + FinalizeSnapshot *ResponseFinalizeSnapshot `protobuf:"bytes,17,opt,name=finalize_snapshot,json=finalizeSnapshot,proto3,oneof" json:"finalize_snapshot,omitempty"` +} type Response_PrepareProposal struct { PrepareProposal *ResponsePrepareProposal `protobuf:"bytes,12,opt,name=prepare_proposal,json=prepareProposal,proto3,oneof" json:"prepare_proposal,omitempty"` } @@ -2057,6 +2135,7 @@ func (*Response_ListSnapshots) isResponse_Value() {} func (*Response_OfferSnapshot) isResponse_Value() {} func (*Response_LoadSnapshotChunk) isResponse_Value() {} func (*Response_ApplySnapshotChunk) isResponse_Value() {} +func (*Response_FinalizeSnapshot) isResponse_Value() {} func (*Response_PrepareProposal) isResponse_Value() {} func (*Response_ProcessProposal) isResponse_Value() {} func (*Response_ExtendVote) isResponse_Value() {} @@ -2147,6 +2226,13 @@ func (m *Response) GetApplySnapshotChunk() *ResponseApplySnapshotChunk { return nil } +func (m *Response) GetFinalizeSnapshot() *ResponseFinalizeSnapshot { + if x, ok := m.GetValue().(*Response_FinalizeSnapshot); ok { + return x.FinalizeSnapshot + } + return nil +} + func (m *Response) GetPrepareProposal() *ResponsePrepareProposal { if x, ok := m.GetValue().(*Response_PrepareProposal); ok { return x.PrepareProposal @@ -2196,6 +2282,7 @@ func (*Response) XXX_OneofWrappers() []interface{} { (*Response_OfferSnapshot)(nil), (*Response_LoadSnapshotChunk)(nil), (*Response_ApplySnapshotChunk)(nil), + (*Response_FinalizeSnapshot)(nil), (*Response_PrepareProposal)(nil), (*Response_ProcessProposal)(nil), (*Response_ExtendVote)(nil), @@ -2213,7 +2300,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{17} + return fileDescriptor_252557cfdd89a31a, []int{18} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2257,7 +2344,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{18} + return fileDescriptor_252557cfdd89a31a, []int{19} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2300,7 +2387,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{19} + return fileDescriptor_252557cfdd89a31a, []int{20} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2341,7 +2428,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{20} + return fileDescriptor_252557cfdd89a31a, []int{21} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2426,7 +2513,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{21} + return fileDescriptor_252557cfdd89a31a, []int{22} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2543,7 +2630,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{22} + return fileDescriptor_252557cfdd89a31a, []int{23} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2649,7 +2736,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{23} + return fileDescriptor_252557cfdd89a31a, []int{24} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2735,7 +2822,7 @@ func (m *ResponseListSnapshots) Reset() { *m = ResponseListSnapshots{} } func (m *ResponseListSnapshots) String() string { return proto.CompactTextString(m) } func (*ResponseListSnapshots) ProtoMessage() {} func (*ResponseListSnapshots) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{24} + return fileDescriptor_252557cfdd89a31a, []int{25} } func (m *ResponseListSnapshots) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2779,7 +2866,7 @@ func (m *ResponseOfferSnapshot) Reset() { *m = ResponseOfferSnapshot{} } func (m *ResponseOfferSnapshot) String() string { return proto.CompactTextString(m) } func (*ResponseOfferSnapshot) ProtoMessage() {} func (*ResponseOfferSnapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{25} + return fileDescriptor_252557cfdd89a31a, []int{26} } func (m *ResponseOfferSnapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2825,7 +2912,7 @@ func (m *ResponseLoadSnapshotChunk) Reset() { *m = ResponseLoadSnapshotC func (m *ResponseLoadSnapshotChunk) String() string { return proto.CompactTextString(m) } func (*ResponseLoadSnapshotChunk) ProtoMessage() {} func (*ResponseLoadSnapshotChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{26} + return fileDescriptor_252557cfdd89a31a, []int{27} } func (m *ResponseLoadSnapshotChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2878,7 +2965,7 @@ func (m *ResponseApplySnapshotChunk) Reset() { *m = ResponseApplySnapsho func (m *ResponseApplySnapshotChunk) String() string { return proto.CompactTextString(m) } func (*ResponseApplySnapshotChunk) ProtoMessage() {} func (*ResponseApplySnapshotChunk) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{27} + return fileDescriptor_252557cfdd89a31a, []int{28} } func (m *ResponseApplySnapshotChunk) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2935,6 +3022,43 @@ func (m *ResponseApplySnapshotChunk) GetNextChunks() [][]byte { return nil } +// The response to a `RequestPrepareSnapshot` message. +type ResponseFinalizeSnapshot struct { +} + +func (m *ResponseFinalizeSnapshot) Reset() { *m = ResponseFinalizeSnapshot{} } +func (m *ResponseFinalizeSnapshot) String() string { return proto.CompactTextString(m) } +func (*ResponseFinalizeSnapshot) ProtoMessage() {} +func (*ResponseFinalizeSnapshot) Descriptor() ([]byte, []int) { + return fileDescriptor_252557cfdd89a31a, []int{29} +} +func (m *ResponseFinalizeSnapshot) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ResponseFinalizeSnapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ResponseFinalizeSnapshot.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 *ResponseFinalizeSnapshot) XXX_Merge(src proto.Message) { + xxx_messageInfo_ResponseFinalizeSnapshot.Merge(m, src) +} +func (m *ResponseFinalizeSnapshot) XXX_Size() int { + return m.Size() +} +func (m *ResponseFinalizeSnapshot) XXX_DiscardUnknown() { + xxx_messageInfo_ResponseFinalizeSnapshot.DiscardUnknown(m) +} + +var xxx_messageInfo_ResponseFinalizeSnapshot proto.InternalMessageInfo + type ResponsePrepareProposal struct { // Possibly modified list of transactions that have been picked as part of the proposed block. TxRecords []*TxRecord `protobuf:"bytes,1,rep,name=tx_records,json=txRecords,proto3" json:"tx_records,omitempty"` @@ -2956,7 +3080,7 @@ func (m *ResponsePrepareProposal) Reset() { *m = ResponsePrepareProposal func (m *ResponsePrepareProposal) String() string { return proto.CompactTextString(m) } func (*ResponsePrepareProposal) ProtoMessage() {} func (*ResponsePrepareProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{28} + return fileDescriptor_252557cfdd89a31a, []int{30} } func (m *ResponsePrepareProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3053,7 +3177,7 @@ func (m *ResponseProcessProposal) Reset() { *m = ResponseProcessProposal func (m *ResponseProcessProposal) String() string { return proto.CompactTextString(m) } func (*ResponseProcessProposal) ProtoMessage() {} func (*ResponseProcessProposal) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{29} + return fileDescriptor_252557cfdd89a31a, []int{31} } func (m *ResponseProcessProposal) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3143,7 +3267,7 @@ func (m *ExtendVoteExtension) Reset() { *m = ExtendVoteExtension{} } func (m *ExtendVoteExtension) String() string { return proto.CompactTextString(m) } func (*ExtendVoteExtension) ProtoMessage() {} func (*ExtendVoteExtension) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{30} + return fileDescriptor_252557cfdd89a31a, []int{32} } func (m *ExtendVoteExtension) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3227,7 +3351,7 @@ func (m *ResponseExtendVote) Reset() { *m = ResponseExtendVote{} } func (m *ResponseExtendVote) String() string { return proto.CompactTextString(m) } func (*ResponseExtendVote) ProtoMessage() {} func (*ResponseExtendVote) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{31} + return fileDescriptor_252557cfdd89a31a, []int{33} } func (m *ResponseExtendVote) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3271,7 +3395,7 @@ func (m *ResponseVerifyVoteExtension) Reset() { *m = ResponseVerifyVoteE func (m *ResponseVerifyVoteExtension) String() string { return proto.CompactTextString(m) } func (*ResponseVerifyVoteExtension) ProtoMessage() {} func (*ResponseVerifyVoteExtension) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{32} + return fileDescriptor_252557cfdd89a31a, []int{34} } func (m *ResponseVerifyVoteExtension) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3316,7 +3440,7 @@ func (m *ResponseFinalizeBlock) Reset() { *m = ResponseFinalizeBlock{} } func (m *ResponseFinalizeBlock) String() string { return proto.CompactTextString(m) } func (*ResponseFinalizeBlock) ProtoMessage() {} func (*ResponseFinalizeBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{33} + return fileDescriptor_252557cfdd89a31a, []int{35} } func (m *ResponseFinalizeBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3363,7 +3487,7 @@ func (m *CommitInfo) Reset() { *m = CommitInfo{} } func (m *CommitInfo) String() string { return proto.CompactTextString(m) } func (*CommitInfo) ProtoMessage() {} func (*CommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{34} + return fileDescriptor_252557cfdd89a31a, []int{36} } func (m *CommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3434,7 +3558,7 @@ func (m *Event) Reset() { *m = Event{} } func (m *Event) String() string { return proto.CompactTextString(m) } func (*Event) ProtoMessage() {} func (*Event) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{35} + return fileDescriptor_252557cfdd89a31a, []int{37} } func (m *Event) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3488,7 +3612,7 @@ func (m *EventAttribute) Reset() { *m = EventAttribute{} } func (m *EventAttribute) String() string { return proto.CompactTextString(m) } func (*EventAttribute) ProtoMessage() {} func (*EventAttribute) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{36} + return fileDescriptor_252557cfdd89a31a, []int{38} } func (m *EventAttribute) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3554,7 +3678,7 @@ func (m *ExecTxResult) Reset() { *m = ExecTxResult{} } func (m *ExecTxResult) String() string { return proto.CompactTextString(m) } func (*ExecTxResult) ProtoMessage() {} func (*ExecTxResult) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{37} + return fileDescriptor_252557cfdd89a31a, []int{39} } func (m *ExecTxResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3646,7 +3770,7 @@ func (m *TxResult) Reset() { *m = TxResult{} } func (m *TxResult) String() string { return proto.CompactTextString(m) } func (*TxResult) ProtoMessage() {} func (*TxResult) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{38} + return fileDescriptor_252557cfdd89a31a, []int{40} } func (m *TxResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3712,7 +3836,7 @@ func (m *TxRecord) Reset() { *m = TxRecord{} } func (m *TxRecord) String() string { return proto.CompactTextString(m) } func (*TxRecord) ProtoMessage() {} func (*TxRecord) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{39} + return fileDescriptor_252557cfdd89a31a, []int{41} } func (m *TxRecord) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3765,7 +3889,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{40} + return fileDescriptor_252557cfdd89a31a, []int{42} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3823,7 +3947,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{41} + return fileDescriptor_252557cfdd89a31a, []int{43} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3905,7 +4029,7 @@ func (m *ValidatorSetUpdate) Reset() { *m = ValidatorSetUpdate{} } func (m *ValidatorSetUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorSetUpdate) ProtoMessage() {} func (*ValidatorSetUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{42} + return fileDescriptor_252557cfdd89a31a, []int{44} } func (m *ValidatorSetUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3963,7 +4087,7 @@ func (m *ThresholdPublicKeyUpdate) Reset() { *m = ThresholdPublicKeyUpda func (m *ThresholdPublicKeyUpdate) String() string { return proto.CompactTextString(m) } func (*ThresholdPublicKeyUpdate) ProtoMessage() {} func (*ThresholdPublicKeyUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{43} + return fileDescriptor_252557cfdd89a31a, []int{45} } func (m *ThresholdPublicKeyUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4007,7 +4131,7 @@ func (m *QuorumHashUpdate) Reset() { *m = QuorumHashUpdate{} } func (m *QuorumHashUpdate) String() string { return proto.CompactTextString(m) } func (*QuorumHashUpdate) ProtoMessage() {} func (*QuorumHashUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{44} + return fileDescriptor_252557cfdd89a31a, []int{46} } func (m *QuorumHashUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4053,7 +4177,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{45} + return fileDescriptor_252557cfdd89a31a, []int{47} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4110,7 +4234,7 @@ func (m *ExtendedVoteInfo) Reset() { *m = ExtendedVoteInfo{} } func (m *ExtendedVoteInfo) String() string { return proto.CompactTextString(m) } func (*ExtendedVoteInfo) ProtoMessage() {} func (*ExtendedVoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{46} + return fileDescriptor_252557cfdd89a31a, []int{48} } func (m *ExtendedVoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4178,7 +4302,7 @@ func (m *Misbehavior) Reset() { *m = Misbehavior{} } func (m *Misbehavior) String() string { return proto.CompactTextString(m) } func (*Misbehavior) ProtoMessage() {} func (*Misbehavior) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{47} + return fileDescriptor_252557cfdd89a31a, []int{49} } func (m *Misbehavior) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4253,7 +4377,7 @@ func (m *Snapshot) Reset() { *m = Snapshot{} } func (m *Snapshot) String() string { return proto.CompactTextString(m) } func (*Snapshot) ProtoMessage() {} func (*Snapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_252557cfdd89a31a, []int{48} + return fileDescriptor_252557cfdd89a31a, []int{50} } func (m *Snapshot) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4329,6 +4453,7 @@ func init() { proto.RegisterType((*RequestOfferSnapshot)(nil), "tendermint.abci.RequestOfferSnapshot") proto.RegisterType((*RequestLoadSnapshotChunk)(nil), "tendermint.abci.RequestLoadSnapshotChunk") proto.RegisterType((*RequestApplySnapshotChunk)(nil), "tendermint.abci.RequestApplySnapshotChunk") + proto.RegisterType((*RequestFinalizeSnapshot)(nil), "tendermint.abci.RequestFinalizeSnapshot") proto.RegisterType((*RequestPrepareProposal)(nil), "tendermint.abci.RequestPrepareProposal") proto.RegisterType((*RequestProcessProposal)(nil), "tendermint.abci.RequestProcessProposal") proto.RegisterType((*RequestExtendVote)(nil), "tendermint.abci.RequestExtendVote") @@ -4346,6 +4471,7 @@ func init() { proto.RegisterType((*ResponseOfferSnapshot)(nil), "tendermint.abci.ResponseOfferSnapshot") proto.RegisterType((*ResponseLoadSnapshotChunk)(nil), "tendermint.abci.ResponseLoadSnapshotChunk") proto.RegisterType((*ResponseApplySnapshotChunk)(nil), "tendermint.abci.ResponseApplySnapshotChunk") + proto.RegisterType((*ResponseFinalizeSnapshot)(nil), "tendermint.abci.ResponseFinalizeSnapshot") proto.RegisterType((*ResponsePrepareProposal)(nil), "tendermint.abci.ResponsePrepareProposal") proto.RegisterType((*ResponseProcessProposal)(nil), "tendermint.abci.ResponseProcessProposal") proto.RegisterType((*ExtendVoteExtension)(nil), "tendermint.abci.ExtendVoteExtension") @@ -4372,239 +4498,246 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 3712 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5b, 0xcb, 0x73, 0x1b, 0x47, - 0x73, 0xc7, 0xe2, 0x8d, 0xc6, 0x6b, 0x39, 0xa4, 0x24, 0x08, 0x92, 0x48, 0x7a, 0x15, 0x5b, 0xb2, - 0x6c, 0x93, 0xb6, 0x14, 0x5b, 0x76, 0xec, 0xa4, 0x0a, 0x04, 0xa1, 0x80, 0x14, 0x45, 0xd2, 0x4b, - 0x90, 0x2e, 0xc7, 0xb1, 0xb7, 0x96, 0xc0, 0x90, 0x58, 0x0b, 0xc0, 0xae, 0x77, 0x17, 0x14, 0xe8, - 0x6b, 0xe2, 0xaa, 0x94, 0x4f, 0xfe, 0x07, 0x7c, 0x4b, 0x8e, 0xb9, 0xe7, 0x94, 0x54, 0x4e, 0x71, - 0x2a, 0x17, 0x1f, 0x53, 0x95, 0x8a, 0xe2, 0x92, 0x6f, 0xb9, 0xf9, 0xf4, 0x5d, 0xbe, 0xc3, 0x57, - 0xf3, 0xd8, 0x27, 0xb0, 0x78, 0x58, 0xae, 0xfa, 0xea, 0xbb, 0x61, 0x7a, 0xba, 0x7b, 0xe7, 0xd1, - 0xd3, 0xdd, 0xf3, 0xeb, 0x01, 0xdc, 0xb0, 0xf1, 0xa0, 0x83, 0xcd, 0xbe, 0x36, 0xb0, 0x37, 0xd5, - 0xd3, 0xb6, 0xb6, 0x69, 0x5f, 0x1a, 0xd8, 0xda, 0x30, 0x4c, 0xdd, 0xd6, 0x51, 0xd9, 0xeb, 0xdc, - 0x20, 0x9d, 0xd5, 0x5b, 0x3e, 0xee, 0xb6, 0x79, 0x69, 0xd8, 0xfa, 0xa6, 0x61, 0xea, 0xfa, 0x19, - 0xe3, 0xaf, 0xfa, 0x95, 0x51, 0x3d, 0x9b, 0x1d, 0xd5, 0xea, 0xf2, 0xce, 0x9b, 0x63, 0x9d, 0xa7, - 0x3d, 0xbd, 0xfd, 0x34, 0xb2, 0xd7, 0x37, 0x90, 0x40, 0x2f, 0xff, 0xee, 0x53, 0x7c, 0xe9, 0xf4, - 0xde, 0x1a, 0x93, 0x35, 0x54, 0x53, 0xed, 0x3b, 0xdd, 0xab, 0xbe, 0xee, 0x0b, 0x6c, 0x5a, 0x9a, - 0x3e, 0x08, 0x28, 0x5f, 0x3b, 0xd7, 0xf5, 0xf3, 0x1e, 0xde, 0xa4, 0xad, 0xd3, 0xe1, 0xd9, 0xa6, - 0xad, 0xf5, 0xb1, 0x65, 0xab, 0x7d, 0x83, 0x33, 0xac, 0x9c, 0xeb, 0xe7, 0x3a, 0xfd, 0xb9, 0x49, - 0x7e, 0x31, 0xaa, 0xf4, 0x4d, 0x0e, 0x32, 0x32, 0xfe, 0x6a, 0x88, 0x2d, 0x1b, 0xdd, 0x87, 0x24, - 0x6e, 0x77, 0xf5, 0x8a, 0xb0, 0x2e, 0xdc, 0xcd, 0xdf, 0xbf, 0xb9, 0x11, 0x5a, 0xb7, 0x0d, 0xce, - 0xd7, 0x68, 0x77, 0xf5, 0x66, 0x4c, 0xa6, 0xbc, 0xe8, 0x5d, 0x48, 0x9d, 0xf5, 0x86, 0x56, 0xb7, - 0x12, 0xa7, 0x42, 0xb7, 0xa2, 0x84, 0x1e, 0x11, 0xa6, 0x66, 0x4c, 0x66, 0xdc, 0xe4, 0x53, 0xda, - 0xe0, 0x4c, 0xaf, 0x24, 0xa6, 0x7f, 0x6a, 0x67, 0x70, 0x46, 0x3f, 0x45, 0x78, 0xd1, 0x16, 0x80, - 0x36, 0xd0, 0x6c, 0xa5, 0xdd, 0x55, 0xb5, 0x41, 0x25, 0x49, 0x25, 0x5f, 0x89, 0x96, 0xd4, 0xec, - 0x3a, 0x61, 0x6c, 0xc6, 0xe4, 0x9c, 0xe6, 0x34, 0xc8, 0x70, 0xbf, 0x1a, 0x62, 0xf3, 0xb2, 0x92, - 0x9a, 0x3e, 0xdc, 0x8f, 0x09, 0x13, 0x19, 0x2e, 0xe5, 0x46, 0x1f, 0x41, 0xb6, 0xdd, 0xc5, 0xed, - 0xa7, 0x8a, 0x3d, 0xaa, 0x64, 0xa8, 0xe4, 0x5a, 0x94, 0x64, 0x9d, 0xf0, 0xb5, 0x46, 0xcd, 0x98, - 0x9c, 0x69, 0xb3, 0x9f, 0x68, 0x1f, 0x4a, 0x3d, 0xcd, 0xb2, 0x15, 0x6b, 0xa0, 0x1a, 0x56, 0x57, - 0xb7, 0xad, 0x4a, 0x9e, 0xea, 0x78, 0x35, 0x4a, 0xc7, 0x9e, 0x66, 0xd9, 0x47, 0x0e, 0x73, 0x33, - 0x26, 0x17, 0x7b, 0x7e, 0x02, 0xd1, 0xa7, 0x9f, 0x9d, 0x61, 0xd3, 0x55, 0x58, 0x29, 0x4c, 0xd7, - 0x77, 0x40, 0xb8, 0x1d, 0x79, 0xa2, 0x4f, 0xf7, 0x13, 0xd0, 0x67, 0xb0, 0xdc, 0xd3, 0xd5, 0x8e, - 0xab, 0x4e, 0x69, 0x77, 0x87, 0x83, 0xa7, 0x95, 0x22, 0x55, 0xfa, 0x7a, 0xe4, 0x20, 0x75, 0xb5, - 0xe3, 0xa8, 0xa8, 0x13, 0x81, 0x66, 0x4c, 0x5e, 0xea, 0x85, 0x89, 0xe8, 0x0b, 0x58, 0x51, 0x0d, - 0xa3, 0x77, 0x19, 0xd6, 0x5e, 0xa2, 0xda, 0xef, 0x45, 0x69, 0xaf, 0x11, 0x99, 0xb0, 0x7a, 0xa4, - 0x8e, 0x51, 0x51, 0x0b, 0x44, 0xc3, 0xc4, 0x86, 0x6a, 0x62, 0xc5, 0x30, 0x75, 0x43, 0xb7, 0xd4, - 0x5e, 0xa5, 0x4c, 0x75, 0xdf, 0x89, 0xd2, 0x7d, 0xc8, 0xf8, 0x0f, 0x39, 0x7b, 0x33, 0x26, 0x97, - 0x8d, 0x20, 0x89, 0x69, 0xd5, 0xdb, 0xd8, 0xb2, 0x3c, 0xad, 0xe2, 0x2c, 0xad, 0x94, 0x3f, 0xa8, - 0x35, 0x40, 0x42, 0x0d, 0xc8, 0xe3, 0x11, 0x11, 0x57, 0x2e, 0x74, 0x1b, 0x57, 0x96, 0xa8, 0x42, - 0x29, 0xf2, 0x9c, 0x51, 0xd6, 0x13, 0xdd, 0xc6, 0xcd, 0x98, 0x0c, 0xd8, 0x6d, 0x21, 0x15, 0xae, - 0x5c, 0x60, 0x53, 0x3b, 0xbb, 0xa4, 0x6a, 0x14, 0xda, 0x43, 0xfc, 0x41, 0x05, 0x51, 0x85, 0x6f, - 0x44, 0x29, 0x3c, 0xa1, 0x42, 0x44, 0x45, 0xc3, 0x11, 0x69, 0xc6, 0xe4, 0xe5, 0x8b, 0x71, 0x32, - 0x31, 0xb1, 0x33, 0x6d, 0xa0, 0xf6, 0xb4, 0xaf, 0xb1, 0x42, 0x1d, 0x5c, 0x65, 0x79, 0xba, 0x89, - 0x3d, 0xe2, 0xdc, 0x5b, 0x84, 0x99, 0x98, 0xd8, 0x99, 0x9f, 0xb0, 0x95, 0x81, 0xd4, 0x85, 0xda, - 0x1b, 0xe2, 0xdd, 0x64, 0x36, 0x2d, 0x66, 0x76, 0x93, 0xd9, 0xac, 0x98, 0xdb, 0x4d, 0x66, 0x73, - 0x22, 0xec, 0x26, 0xb3, 0x20, 0xe6, 0xa5, 0x3b, 0x90, 0xf7, 0xb9, 0x17, 0x54, 0x81, 0x4c, 0x1f, - 0x5b, 0x96, 0x7a, 0x8e, 0xa9, 0x37, 0xca, 0xc9, 0x4e, 0x53, 0x2a, 0x41, 0xc1, 0xef, 0x52, 0xa4, - 0xef, 0x04, 0x57, 0x92, 0x78, 0x0b, 0x22, 0xc9, 0xdd, 0xa3, 0x23, 0xc9, 0x9b, 0xe8, 0x36, 0x14, - 0xe9, 0x54, 0x14, 0xa7, 0x9f, 0xb8, 0xac, 0xa4, 0x5c, 0xa0, 0xc4, 0x13, 0xce, 0xb4, 0x06, 0x79, - 0xe3, 0xbe, 0xe1, 0xb2, 0x24, 0x28, 0x0b, 0x18, 0xf7, 0x0d, 0x87, 0xe1, 0x15, 0x28, 0x90, 0x79, - 0xbb, 0x1c, 0x49, 0xfa, 0x91, 0x3c, 0xa1, 0x71, 0x16, 0xe9, 0xef, 0x13, 0x20, 0x86, 0xdd, 0x10, - 0x7a, 0x1f, 0x92, 0xc4, 0x23, 0x73, 0xe7, 0x5a, 0xdd, 0x60, 0xee, 0x7a, 0xc3, 0x71, 0xd7, 0x1b, - 0x2d, 0xc7, 0x5d, 0x6f, 0x65, 0x7f, 0x78, 0xbe, 0x16, 0xfb, 0xee, 0xff, 0xd6, 0x04, 0x99, 0x4a, - 0xa0, 0xeb, 0xc4, 0xf9, 0xa8, 0xda, 0x40, 0xd1, 0x3a, 0x74, 0xc8, 0x39, 0xe2, 0x59, 0x54, 0x6d, - 0xb0, 0xd3, 0x41, 0x7b, 0x20, 0xb6, 0xf5, 0x81, 0x85, 0x07, 0xd6, 0xd0, 0x52, 0x58, 0xb8, 0xe0, - 0x2e, 0x35, 0xe0, 0x18, 0x59, 0x9c, 0xa8, 0x3b, 0x9c, 0x87, 0x94, 0x51, 0x2e, 0xb7, 0x83, 0x04, - 0xb4, 0x0f, 0xc5, 0x0b, 0xb5, 0xa7, 0x75, 0x54, 0x5b, 0x37, 0x15, 0x0b, 0xdb, 0xdc, 0xc7, 0xde, - 0x1e, 0xdb, 0xf3, 0x13, 0x87, 0xeb, 0x08, 0xdb, 0xc7, 0x46, 0x47, 0xb5, 0xf1, 0x56, 0xf2, 0x87, - 0xe7, 0x6b, 0x82, 0x5c, 0xb8, 0xf0, 0xf5, 0xa0, 0xd7, 0xa0, 0xac, 0x1a, 0x86, 0x62, 0xd9, 0xaa, - 0x8d, 0x95, 0xd3, 0x4b, 0x1b, 0x5b, 0xd4, 0xed, 0x16, 0xe4, 0xa2, 0x6a, 0x18, 0x47, 0x84, 0xba, - 0x45, 0x88, 0xe8, 0x55, 0x28, 0x11, 0x0f, 0xad, 0xa9, 0x3d, 0xa5, 0x8b, 0xb5, 0xf3, 0xae, 0x5d, - 0x49, 0xaf, 0x0b, 0x77, 0x13, 0x72, 0x91, 0x53, 0x9b, 0x94, 0x88, 0x36, 0x60, 0xd9, 0x61, 0x6b, - 0xeb, 0x26, 0x76, 0x78, 0x89, 0x3f, 0x2e, 0xca, 0x4b, 0xbc, 0xab, 0xae, 0x9b, 0x98, 0xf1, 0x4b, - 0x1d, 0xd7, 0x52, 0xa8, 0x37, 0x47, 0x08, 0x92, 0x1d, 0xd5, 0x56, 0xe9, 0x0e, 0x14, 0x64, 0xfa, - 0x9b, 0xd0, 0x0c, 0xd5, 0xee, 0xf2, 0x75, 0xa5, 0xbf, 0xd1, 0x55, 0x48, 0x73, 0xd5, 0x09, 0x3a, - 0x0c, 0xde, 0x42, 0x2b, 0x90, 0x32, 0x4c, 0xfd, 0x02, 0xd3, 0x65, 0xc9, 0xca, 0xac, 0x21, 0xc9, - 0x50, 0x0a, 0x7a, 0x7e, 0x54, 0x82, 0xb8, 0x3d, 0xe2, 0x5f, 0x89, 0xdb, 0x23, 0xf4, 0x36, 0x24, - 0xc9, 0x06, 0xd0, 0x6f, 0x94, 0x26, 0xc4, 0x3a, 0x2e, 0xd7, 0xba, 0x34, 0xb0, 0x4c, 0x39, 0xa5, - 0xab, 0xb0, 0x32, 0x29, 0x12, 0x48, 0x5d, 0x97, 0x1e, 0xf0, 0xe8, 0xe8, 0x5d, 0xc8, 0xba, 0xa1, - 0x80, 0xd9, 0xd7, 0xf5, 0xb1, 0xaf, 0x38, 0xcc, 0xb2, 0xcb, 0x4a, 0x0c, 0x8b, 0xec, 0x4f, 0x57, - 0xe5, 0xe1, 0xbb, 0x20, 0x67, 0x54, 0xc3, 0x68, 0xaa, 0x56, 0x57, 0x3a, 0x87, 0x4a, 0x94, 0x9b, - 0xf7, 0xad, 0x8f, 0x40, 0x4f, 0x87, 0xb3, 0x3e, 0xbe, 0x93, 0x17, 0xa7, 0x7b, 0xe2, 0x9e, 0x3c, - 0x6a, 0xc1, 0xc3, 0xc1, 0x53, 0x62, 0xc1, 0x09, 0xf6, 0x21, 0xda, 0xde, 0xe9, 0x48, 0x1d, 0xb8, - 0x1e, 0xe9, 0xf1, 0x03, 0x72, 0x42, 0x40, 0x8e, 0x6c, 0x06, 0x8b, 0x23, 0x6c, 0xe0, 0xac, 0x41, - 0x86, 0x66, 0xd1, 0x79, 0xd3, 0xcf, 0xe4, 0x64, 0xde, 0x92, 0x7e, 0x49, 0xc2, 0xd5, 0xc9, 0xce, - 0x1f, 0xad, 0x43, 0xa1, 0xaf, 0x8e, 0x14, 0x7b, 0xc4, 0x2d, 0x54, 0xa0, 0x7b, 0x0e, 0x7d, 0x75, - 0xd4, 0x1a, 0x31, 0xf3, 0x14, 0x21, 0x61, 0x8f, 0xac, 0x4a, 0x7c, 0x3d, 0x71, 0xb7, 0x20, 0x93, - 0x9f, 0xe8, 0x09, 0x2c, 0xf5, 0xf4, 0xb6, 0xda, 0x53, 0x7a, 0xaa, 0x65, 0x2b, 0x6d, 0xbd, 0xdf, - 0xd7, 0x6c, 0x7e, 0xee, 0x6e, 0x8c, 0x6f, 0x2f, 0xed, 0x26, 0xbe, 0x89, 0x1e, 0x92, 0x98, 0x5c, - 0xa6, 0xb2, 0x7b, 0xaa, 0x65, 0xb3, 0x2e, 0xb4, 0x0d, 0xf9, 0xbe, 0x66, 0x9d, 0xe2, 0xae, 0x7a, - 0xa1, 0xe9, 0x66, 0x25, 0xb9, 0x9e, 0x98, 0x98, 0x13, 0x3d, 0xf1, 0x78, 0xb8, 0x26, 0xbf, 0x98, - 0x6f, 0x5b, 0x52, 0x01, 0xb3, 0x75, 0x1c, 0x4f, 0x7a, 0x61, 0xc7, 0xf3, 0x36, 0xac, 0x0c, 0xf0, - 0xc8, 0x56, 0xdc, 0x43, 0x6d, 0x31, 0x5b, 0xc9, 0xd0, 0x25, 0x47, 0xa4, 0xcf, 0xf5, 0x04, 0x16, - 0x31, 0x1b, 0xb2, 0x2b, 0xa6, 0x3e, 0x1c, 0x74, 0x2a, 0xd9, 0x75, 0xe1, 0x6e, 0x4a, 0x66, 0x0d, - 0xf4, 0x10, 0x2a, 0xf4, 0xc0, 0x32, 0x2f, 0x46, 0xbc, 0x2d, 0xee, 0x38, 0xa7, 0x37, 0x47, 0x2d, - 0xe5, 0x0a, 0xe9, 0xa7, 0x7e, 0x72, 0x8f, 0xf6, 0xf2, 0x13, 0xbf, 0x09, 0x2b, 0x2c, 0xfa, 0x62, - 0x93, 0x84, 0x61, 0xb2, 0x49, 0x74, 0x00, 0x40, 0x07, 0xb0, 0xe4, 0xf4, 0x1d, 0x9a, 0x7a, 0x6b, - 0x44, 0xbf, 0xff, 0xb6, 0x2b, 0xd0, 0x51, 0x88, 0x69, 0x3b, 0xf6, 0x98, 0xa7, 0x86, 0x8a, 0x9c, - 0xbe, 0x9a, 0xe1, 0xba, 0xf3, 0x87, 0x9e, 0xd1, 0x16, 0xc6, 0x53, 0x42, 0xde, 0xe5, 0xb9, 0x4e, - 0xcf, 0xa6, 0xd7, 0x20, 0xff, 0xd5, 0x50, 0x37, 0x87, 0x7d, 0x36, 0xa4, 0x22, 0x1d, 0x12, 0x30, - 0x12, 0x3d, 0x42, 0xff, 0x96, 0xf2, 0xd9, 0x5c, 0x30, 0x0f, 0xe0, 0x16, 0x25, 0x78, 0x16, 0x75, - 0xe4, 0x1b, 0xb8, 0xdf, 0xa8, 0xe2, 0xf3, 0x1a, 0x95, 0x3b, 0xb7, 0x68, 0xbb, 0x4a, 0xfc, 0x3a, - 0xbb, 0x42, 0x90, 0xa4, 0x33, 0x4c, 0x32, 0xb7, 0x49, 0x7e, 0x47, 0xda, 0x9a, 0xbb, 0xff, 0x69, - 0xff, 0xfe, 0x3b, 0x16, 0x98, 0xf9, 0xcd, 0x2c, 0x30, 0x1b, 0x69, 0x81, 0xbf, 0xda, 0xd6, 0x5a, - 0x70, 0x35, 0x24, 0xa8, 0x0c, 0x69, 0x68, 0xa3, 0xd6, 0x16, 0x4a, 0xf8, 0x9d, 0x80, 0xea, 0x53, - 0x24, 0x2f, 0x07, 0xf4, 0xb2, 0xb0, 0x18, 0x69, 0xc1, 0xf9, 0x45, 0x2d, 0xb8, 0x30, 0x8f, 0x05, - 0x17, 0x5f, 0xc6, 0x82, 0x4b, 0x63, 0x16, 0x7c, 0x0c, 0x4b, 0x63, 0xa9, 0xa8, 0x6b, 0x0e, 0xc2, - 0x44, 0x73, 0x88, 0x4f, 0x36, 0x87, 0x84, 0xcf, 0x1c, 0xa4, 0x9f, 0x04, 0xa8, 0x46, 0x67, 0xa4, - 0x13, 0x3f, 0xf0, 0x0e, 0x5c, 0xf1, 0x32, 0x13, 0xff, 0x3a, 0x32, 0xef, 0x8f, 0xdc, 0x4e, 0x6f, - 0x21, 0xa7, 0x44, 0x71, 0x36, 0xa6, 0xa4, 0xdf, 0x44, 0x9f, 0x40, 0x39, 0x98, 0x4b, 0x93, 0x54, - 0x85, 0x1c, 0x97, 0x3f, 0x1b, 0x3b, 0x2e, 0xde, 0x5a, 0xb8, 0x63, 0x96, 0x4b, 0x17, 0xfe, 0xa6, - 0x25, 0xfd, 0x57, 0xdc, 0x8d, 0xd4, 0x81, 0xc4, 0x18, 0x7d, 0x00, 0x69, 0x7e, 0xb2, 0x85, 0x79, - 0x4f, 0x36, 0x17, 0x08, 0x9f, 0xe6, 0xf8, 0xcb, 0x9d, 0xe6, 0xc4, 0xc4, 0xed, 0x4b, 0x4e, 0x5e, - 0xaa, 0x94, 0x7f, 0xa9, 0xde, 0x82, 0x14, 0xbb, 0x11, 0xb0, 0x80, 0x72, 0x6d, 0xfc, 0x5c, 0xd0, - 0xa9, 0xca, 0x8c, 0x0b, 0xd5, 0x20, 0xcb, 0xb2, 0x6e, 0xad, 0xc3, 0x1d, 0xc0, 0xf5, 0x08, 0x89, - 0x9d, 0xed, 0xad, 0xfc, 0x8b, 0xe7, 0x6b, 0x19, 0xde, 0x90, 0x33, 0x54, 0x6e, 0xa7, 0x23, 0xfd, - 0x7b, 0x0e, 0xb2, 0x32, 0xb6, 0x0c, 0x62, 0xc2, 0x68, 0x0b, 0x72, 0x78, 0xd4, 0xc6, 0x86, 0xed, - 0x64, 0xf8, 0x93, 0x6f, 0x50, 0x8c, 0xbb, 0xe1, 0x70, 0x36, 0x63, 0xb2, 0x27, 0x86, 0x1e, 0x70, - 0xa0, 0x23, 0x1a, 0xb3, 0xe0, 0xe2, 0x7e, 0xa4, 0xe3, 0x3d, 0x07, 0xe9, 0x60, 0x81, 0x7e, 0x35, - 0x52, 0x2a, 0x04, 0x75, 0x3c, 0xe0, 0x50, 0x47, 0x72, 0xc6, 0xc7, 0x02, 0x58, 0x47, 0x3d, 0x80, - 0x75, 0xa4, 0x66, 0x4c, 0x33, 0x02, 0xec, 0x78, 0xcf, 0x01, 0x3b, 0xd2, 0x33, 0x46, 0x1c, 0x42, - 0x3b, 0xfe, 0x72, 0x0c, 0xed, 0x58, 0x8f, 0x14, 0x9d, 0x00, 0x77, 0x1c, 0x8c, 0xc1, 0x1d, 0x59, - 0xaa, 0xe4, 0xb5, 0x48, 0x25, 0x33, 0xf0, 0x8e, 0x83, 0x31, 0xbc, 0x23, 0x37, 0x43, 0xe1, 0x0c, - 0xc0, 0xe3, 0x6f, 0x27, 0x03, 0x1e, 0x10, 0x09, 0x49, 0xf0, 0x61, 0xce, 0x87, 0x78, 0x28, 0x11, - 0x88, 0x47, 0x3e, 0xf2, 0x76, 0xce, 0xd4, 0xcf, 0x0d, 0x79, 0x1c, 0x4f, 0x80, 0x3c, 0x58, 0xf2, - 0x72, 0x37, 0x52, 0xf9, 0x1c, 0x98, 0xc7, 0xf1, 0x04, 0xcc, 0xa3, 0x38, 0x53, 0xed, 0x4c, 0xd0, - 0xe3, 0x51, 0x10, 0xf4, 0x28, 0x45, 0xdc, 0x29, 0xbd, 0x23, 0x1b, 0x81, 0x7a, 0x9c, 0x46, 0xa1, - 0x1e, 0x0c, 0xed, 0x79, 0x33, 0x52, 0xe3, 0x02, 0xb0, 0xc7, 0xc1, 0x18, 0xec, 0x21, 0xce, 0xb0, - 0xb4, 0x39, 0x71, 0x0f, 0xe9, 0x75, 0x12, 0x4b, 0x43, 0x4e, 0x89, 0x38, 0x58, 0x6c, 0x9a, 0xba, - 0xc9, 0x91, 0x0a, 0xd6, 0x90, 0xee, 0x92, 0x7b, 0xab, 0xe7, 0x80, 0xa6, 0x60, 0x21, 0x65, 0x28, - 0x06, 0x9c, 0x8e, 0xf4, 0x2f, 0x82, 0x27, 0x4b, 0xd1, 0x10, 0xff, 0x9d, 0x37, 0xc7, 0xef, 0xbc, - 0xa1, 0x7b, 0x5a, 0x2e, 0x90, 0x11, 0xf8, 0x73, 0x0e, 0x0e, 0x7e, 0xa8, 0x5e, 0xae, 0x71, 0x0f, - 0x96, 0x68, 0x76, 0xca, 0x3c, 0x7a, 0x20, 0x68, 0x94, 0x49, 0x07, 0x5b, 0x05, 0x16, 0x3d, 0xde, - 0x82, 0x65, 0x1f, 0xaf, 0x7b, 0xd1, 0x64, 0x08, 0x80, 0xe8, 0x72, 0xd7, 0xf8, 0x8d, 0xf3, 0x3f, - 0x12, 0xde, 0x0a, 0x79, 0xa8, 0xc9, 0x24, 0x80, 0x43, 0xf8, 0xd5, 0x00, 0x47, 0xf4, 0x85, 0x17, - 0x7d, 0x06, 0x2b, 0x01, 0xec, 0xc3, 0x49, 0xfe, 0x12, 0x8b, 0x41, 0x20, 0x31, 0x5f, 0x2e, 0xe2, - 0xf6, 0xa0, 0xcf, 0xe1, 0x06, 0x4d, 0x63, 0x23, 0x12, 0xcc, 0xe4, 0x7c, 0x09, 0xe6, 0x35, 0xa2, - 0xa3, 0x3e, 0x21, 0xc9, 0x8c, 0x00, 0x46, 0x52, 0x11, 0xc0, 0x08, 0xda, 0x83, 0xc2, 0x39, 0x1e, - 0x60, 0x4b, 0xb3, 0x94, 0x05, 0x6e, 0x86, 0x02, 0xc9, 0xcb, 0x9b, 0x31, 0x39, 0xcf, 0x65, 0x49, - 0xef, 0x3f, 0x08, 0xc2, 0x56, 0x19, 0x8a, 0x8a, 0x5f, 0x9d, 0xf4, 0x3b, 0xc1, 0x33, 0x4b, 0x17, - 0x79, 0x69, 0xeb, 0x1d, 0x66, 0xbe, 0x45, 0x99, 0xfe, 0x26, 0x77, 0xa0, 0x9e, 0x7e, 0xce, 0x2d, - 0x90, 0xfc, 0x24, 0x5c, 0x6e, 0x4d, 0x20, 0xc7, 0xe3, 0xe0, 0x0a, 0xa4, 0xb4, 0x41, 0x07, 0x8f, - 0xb8, 0x91, 0xb1, 0x06, 0x91, 0x7d, 0x8a, 0x2f, 0xb9, 0x29, 0x91, 0x9f, 0x84, 0x8f, 0x9e, 0x33, - 0x3a, 0x97, 0x82, 0xcc, 0x1a, 0xe8, 0x7d, 0xc8, 0xd1, 0xc2, 0x8e, 0xa2, 0x1b, 0x16, 0x8f, 0x64, - 0x81, 0x84, 0x8b, 0x15, 0x61, 0x36, 0x0e, 0x09, 0xcf, 0x81, 0x61, 0xc9, 0x59, 0x83, 0xff, 0xf2, - 0xa5, 0x44, 0xd9, 0x40, 0x4a, 0x74, 0x13, 0x72, 0x64, 0xf4, 0x96, 0xa1, 0xb6, 0x31, 0x8d, 0x42, - 0x39, 0xd9, 0x23, 0x48, 0xff, 0x2a, 0x40, 0x39, 0x14, 0x18, 0x27, 0xce, 0xdd, 0x39, 0x95, 0xf1, - 0x20, 0x12, 0x35, 0x36, 0xfb, 0x5b, 0x00, 0xe7, 0xaa, 0xa5, 0x3c, 0x53, 0x07, 0x36, 0xee, 0xf0, - 0x25, 0xc8, 0x9d, 0xab, 0xd6, 0x27, 0x94, 0x10, 0x1c, 0x4c, 0x2a, 0x34, 0x18, 0x1f, 0x16, 0x92, - 0xf6, 0x63, 0x21, 0xa8, 0x0a, 0x59, 0xc3, 0xd4, 0x74, 0x53, 0xb3, 0x2f, 0xe9, 0x9a, 0x24, 0x64, - 0xb7, 0x2d, 0x1d, 0xc2, 0x95, 0x89, 0x31, 0x19, 0x3d, 0x84, 0x9c, 0x17, 0xce, 0x05, 0x9a, 0x7a, - 0x4e, 0x81, 0x98, 0x3c, 0x5e, 0xb2, 0x24, 0x57, 0x26, 0x46, 0x65, 0xd4, 0x80, 0xb4, 0x89, 0xad, - 0x61, 0x8f, 0xa5, 0xc2, 0xa5, 0xfb, 0x6f, 0xcd, 0x17, 0xcd, 0x09, 0x75, 0xd8, 0xb3, 0x65, 0x2e, - 0x2c, 0x7d, 0x01, 0x69, 0x46, 0x41, 0x79, 0xc8, 0x1c, 0xef, 0x3f, 0xde, 0x3f, 0xf8, 0x64, 0x5f, - 0x8c, 0x21, 0x80, 0x74, 0xad, 0x5e, 0x6f, 0x1c, 0xb6, 0x44, 0x01, 0xe5, 0x20, 0x55, 0xdb, 0x3a, - 0x90, 0x5b, 0x62, 0x9c, 0x90, 0xe5, 0xc6, 0x6e, 0xa3, 0xde, 0x12, 0x13, 0x68, 0x09, 0x8a, 0xec, - 0xb7, 0xf2, 0xe8, 0x40, 0x7e, 0x52, 0x6b, 0x89, 0x49, 0x1f, 0xe9, 0xa8, 0xb1, 0xbf, 0xdd, 0x90, - 0xc5, 0x94, 0xf4, 0x0e, 0x5c, 0x8f, 0x8c, 0xff, 0x1e, 0x0a, 0x25, 0xf8, 0x50, 0x28, 0xe9, 0xc7, - 0x38, 0xb9, 0xe0, 0x44, 0x05, 0x75, 0xb4, 0x1b, 0x9a, 0xf8, 0xfd, 0x05, 0x32, 0x82, 0xd0, 0xec, - 0xd1, 0xab, 0x50, 0x32, 0xf1, 0x19, 0xb6, 0xdb, 0x5d, 0x96, 0x64, 0x38, 0x30, 0x55, 0x91, 0x53, - 0xa9, 0x90, 0xc5, 0xd8, 0xbe, 0xc4, 0x6d, 0x5b, 0x61, 0x46, 0x60, 0x51, 0x30, 0x20, 0x47, 0xd8, - 0x08, 0xf5, 0x88, 0x11, 0x89, 0xff, 0x67, 0x7e, 0x8a, 0xa9, 0x4a, 0x52, 0x55, 0x40, 0xdd, 0x0e, - 0xa5, 0x48, 0xcf, 0x16, 0x5a, 0xec, 0x1c, 0xa4, 0xe4, 0x46, 0x4b, 0xfe, 0x54, 0x4c, 0x20, 0x04, - 0x25, 0xfa, 0x53, 0x39, 0xda, 0xaf, 0x1d, 0x1e, 0x35, 0x0f, 0xc8, 0x62, 0x2f, 0x43, 0xd9, 0x59, - 0x6c, 0x87, 0x98, 0x42, 0x57, 0x60, 0xa9, 0x7e, 0xf0, 0xe4, 0x70, 0xaf, 0xd1, 0x6a, 0x78, 0xe4, - 0xb4, 0xf4, 0x3f, 0x09, 0xb8, 0x16, 0x91, 0xca, 0xa0, 0xf7, 0x01, 0xec, 0x91, 0x62, 0xe2, 0xb6, - 0x6e, 0x76, 0xa2, 0x8d, 0xb3, 0x35, 0x92, 0x29, 0x87, 0x9c, 0xb3, 0xf9, 0xaf, 0xa9, 0xf1, 0xe0, - 0x23, 0xae, 0x94, 0x4c, 0xd6, 0xe2, 0xd0, 0xc9, 0xad, 0x09, 0x77, 0x41, 0xdc, 0x26, 0x8a, 0xe9, - 0x9e, 0x50, 0xc5, 0x94, 0x1f, 0x7d, 0x0a, 0xd7, 0x42, 0x61, 0x8b, 0xfb, 0x7a, 0x6b, 0x52, 0xdd, - 0x72, 0x72, 0xf4, 0xba, 0x12, 0x8c, 0x5e, 0xcc, 0xd7, 0x5b, 0x53, 0x70, 0x8a, 0xd4, 0x4b, 0xe0, - 0x14, 0x51, 0xe1, 0x2f, 0xbd, 0x68, 0x05, 0x60, 0x52, 0xf8, 0x0b, 0xa5, 0x15, 0x99, 0x70, 0x5a, - 0x21, 0xfd, 0x3e, 0xb0, 0xbb, 0xc1, 0xf4, 0xf1, 0x00, 0xd2, 0x96, 0xad, 0xda, 0x43, 0x8b, 0x9f, - 0x96, 0x87, 0xf3, 0xe6, 0xa2, 0x1b, 0xce, 0x8f, 0x23, 0x2a, 0x2e, 0x73, 0x35, 0x7f, 0x92, 0x9b, - 0x1e, 0xb5, 0x3d, 0xa9, 0xdf, 0x62, 0x7b, 0x9a, 0x90, 0xc6, 0x17, 0x78, 0x60, 0x5b, 0x95, 0x34, - 0x9d, 0xf1, 0xd5, 0xf1, 0x19, 0x93, 0xee, 0xad, 0x0a, 0xc9, 0x6f, 0xfe, 0xff, 0xf9, 0x9a, 0xc8, - 0xb8, 0xdf, 0xd4, 0xfb, 0x9a, 0x8d, 0xfb, 0x86, 0x7d, 0x29, 0x73, 0x79, 0xe9, 0x5d, 0x28, 0x05, - 0x17, 0x3d, 0xda, 0x4d, 0x78, 0x8e, 0x38, 0x2e, 0xfd, 0xb3, 0x00, 0xcb, 0x13, 0x50, 0x15, 0xf4, - 0x90, 0x17, 0x4e, 0xd8, 0xc6, 0xdf, 0x1e, 0x5f, 0xbd, 0x00, 0xbb, 0x57, 0x3f, 0x21, 0x81, 0xd1, - 0xbb, 0x1e, 0xb0, 0x3d, 0xf6, 0x08, 0xe8, 0x0d, 0x28, 0x5b, 0xda, 0xf9, 0x40, 0x31, 0x19, 0x40, - 0xe3, 0x16, 0x25, 0x48, 0xf6, 0x4e, 0x3a, 0x9c, 0xd2, 0x5d, 0x87, 0x64, 0x37, 0x08, 0x44, 0x25, - 0xc4, 0x2d, 0xb5, 0x01, 0x8d, 0xdf, 0x56, 0x26, 0x41, 0x48, 0xc2, 0x4b, 0x40, 0x48, 0xff, 0x24, - 0xc0, 0x8d, 0x29, 0x37, 0x18, 0xf4, 0x71, 0xe8, 0x5c, 0x7c, 0xb0, 0xc8, 0xfd, 0x67, 0x83, 0xd1, - 0x82, 0x27, 0x43, 0x7a, 0x00, 0x05, 0x3f, 0x7d, 0xbe, 0xcd, 0xdb, 0xf5, 0xe2, 0x7b, 0x10, 0xea, - 0xba, 0x0d, 0x45, 0x13, 0xdb, 0xc4, 0x49, 0x05, 0xb0, 0xc1, 0x02, 0x23, 0xb2, 0x54, 0x74, 0x37, - 0x99, 0x15, 0xc4, 0xb8, 0x6b, 0x3f, 0xff, 0x29, 0x00, 0x78, 0xf8, 0x97, 0x87, 0x3f, 0x09, 0x7e, - 0xfc, 0x29, 0x04, 0x5b, 0xc6, 0xc3, 0xb0, 0x25, 0xba, 0x03, 0x65, 0x76, 0xe7, 0x20, 0xfb, 0xa6, - 0xda, 0x43, 0x13, 0x73, 0xb4, 0xab, 0x44, 0xc9, 0x47, 0x0e, 0x15, 0x7d, 0x06, 0xd7, 0xed, 0xae, - 0x89, 0xad, 0xae, 0xde, 0xeb, 0x28, 0xe1, 0xbd, 0x63, 0x55, 0x98, 0xb5, 0x19, 0x46, 0x27, 0x5f, - 0x73, 0x35, 0x9c, 0x04, 0xf7, 0xef, 0x6b, 0x48, 0xd1, 0x63, 0x43, 0x12, 0x3b, 0xd7, 0x8a, 0x73, - 0xdc, 0x40, 0x3f, 0x07, 0x50, 0x6d, 0xdb, 0xd4, 0x4e, 0x87, 0xc4, 0x3b, 0xc4, 0xc7, 0x3f, 0xe5, - 0x1d, 0xbb, 0x9a, 0xc3, 0xb7, 0x75, 0x93, 0x9f, 0xbf, 0x15, 0x4f, 0xd4, 0x77, 0x06, 0x7d, 0x0a, - 0xa5, 0x7d, 0x28, 0x05, 0x65, 0x9d, 0x8c, 0x99, 0x8d, 0x21, 0x98, 0x31, 0xb3, 0x0c, 0x9c, 0x67, - 0xcc, 0x6e, 0xbe, 0x9d, 0x60, 0x35, 0x4e, 0xda, 0x90, 0x7e, 0x11, 0xa0, 0xe0, 0xf7, 0x7a, 0x73, - 0x27, 0xb5, 0x3c, 0xc9, 0x4f, 0x8c, 0x27, 0xf9, 0x49, 0x5f, 0x9a, 0x7b, 0x1d, 0xb2, 0x24, 0xcd, - 0x1d, 0x5a, 0xb8, 0xc3, 0x2b, 0xbf, 0x99, 0x73, 0xd5, 0x3a, 0xb6, 0x70, 0xc7, 0xe7, 0x9b, 0x32, - 0x2f, 0xe7, 0x9b, 0x82, 0xc9, 0x72, 0x36, 0x94, 0x2c, 0xef, 0x26, 0xb3, 0x29, 0x31, 0x2d, 0xfb, - 0xb2, 0x6d, 0xe9, 0x1b, 0x01, 0xb2, 0xee, 0x7c, 0x83, 0x25, 0xcf, 0x00, 0x42, 0xca, 0x96, 0x8b, - 0x15, 0x3c, 0xf9, 0xf5, 0x84, 0x15, 0x80, 0x13, 0x6e, 0x01, 0xf8, 0x43, 0x37, 0xe1, 0x8b, 0xc2, - 0x00, 0xfd, 0x8b, 0xeb, 0xc0, 0xbe, 0x3c, 0xbf, 0xfd, 0x47, 0x3e, 0x0e, 0x92, 0xb1, 0xa0, 0xbf, - 0x80, 0xb4, 0xda, 0x76, 0x91, 0xcf, 0xd2, 0x04, 0x48, 0xd0, 0x61, 0xdd, 0x68, 0x8d, 0x6a, 0x94, - 0x53, 0xe6, 0x12, 0x7c, 0x54, 0x71, 0x67, 0x54, 0xd2, 0x1e, 0xd1, 0xcb, 0x78, 0x82, 0x27, 0xbd, - 0x04, 0x70, 0xbc, 0xff, 0xe4, 0x60, 0x7b, 0xe7, 0xd1, 0x4e, 0x63, 0x9b, 0x67, 0x74, 0xdb, 0xdb, - 0x8d, 0x6d, 0x31, 0x4e, 0xf8, 0xe4, 0xc6, 0x93, 0x83, 0x93, 0xc6, 0xb6, 0x98, 0x20, 0x8d, 0xed, - 0xc6, 0x5e, 0xed, 0xd3, 0xc6, 0xb6, 0x98, 0x94, 0x6a, 0x90, 0x73, 0x83, 0x0e, 0xad, 0x94, 0xeb, - 0xcf, 0xb0, 0xc9, 0x57, 0x8b, 0x35, 0xd0, 0x2a, 0xe4, 0xc7, 0xa1, 0x7b, 0x72, 0x41, 0x63, 0x88, - 0x3d, 0x09, 0x03, 0x65, 0x57, 0x07, 0x8f, 0x4d, 0x1f, 0x42, 0xc6, 0x18, 0x9e, 0x2a, 0x8e, 0xed, - 0x86, 0x00, 0x6f, 0xe7, 0xfe, 0x36, 0x3c, 0xed, 0x69, 0xed, 0xc7, 0xf8, 0x92, 0x07, 0xb9, 0xb4, - 0x31, 0x3c, 0x7d, 0xcc, 0x4c, 0x9c, 0x0d, 0x23, 0x3e, 0x65, 0x18, 0x89, 0xd0, 0x30, 0xd0, 0x1d, - 0x28, 0x0c, 0xf4, 0x0e, 0x56, 0xd4, 0x4e, 0xc7, 0xc4, 0x16, 0x8b, 0xdd, 0x39, 0xae, 0x39, 0x4f, - 0x7a, 0x6a, 0xac, 0x43, 0xfa, 0x49, 0x00, 0x34, 0x1e, 0x68, 0xd1, 0x11, 0x2c, 0x79, 0xb1, 0xda, - 0x49, 0x00, 0x58, 0x24, 0x58, 0x8f, 0x0e, 0xd4, 0x01, 0x0c, 0x41, 0xbc, 0x08, 0x92, 0x49, 0xd6, - 0xb7, 0xe2, 0xb9, 0x2a, 0x83, 0xce, 0x97, 0x2e, 0x4a, 0x7c, 0xce, 0x45, 0x89, 0xc9, 0xc8, 0x95, - 0x77, 0x7b, 0xc2, 0xae, 0x34, 0x31, 0x56, 0x01, 0x32, 0xa0, 0xd2, 0x1a, 0x13, 0xe3, 0xf3, 0x8c, - 0x1a, 0x92, 0xf0, 0x32, 0x43, 0x92, 0x1e, 0x80, 0xf8, 0xb1, 0xfb, 0x7d, 0x2f, 0x7f, 0xf4, 0x0f, - 0x53, 0x18, 0x1b, 0xe6, 0x05, 0x64, 0x89, 0xf7, 0xa5, 0x41, 0xe3, 0xaf, 0x20, 0xe7, 0xae, 0x9e, - 0xfb, 0xd8, 0x26, 0x72, 0xd9, 0xf9, 0x48, 0x3c, 0x11, 0x74, 0x0f, 0x96, 0x48, 0xdc, 0x70, 0xea, - 0xb0, 0x0c, 0x05, 0x8c, 0x53, 0x6f, 0x58, 0x66, 0x1d, 0x7b, 0x0e, 0x74, 0x45, 0x62, 0xb4, 0xc8, - 0x62, 0x39, 0xee, 0xfc, 0x31, 0x06, 0x40, 0xee, 0x75, 0x21, 0x30, 0x94, 0xed, 0x61, 0x31, 0x90, - 0x4c, 0x48, 0x7f, 0x17, 0x87, 0xbc, 0xaf, 0x2e, 0x84, 0xfe, 0x3c, 0x90, 0x58, 0xad, 0x4f, 0xab, - 0x21, 0xf9, 0xb2, 0xaa, 0xc0, 0xc4, 0xe2, 0x8b, 0x4f, 0x2c, 0xaa, 0x22, 0xe7, 0x94, 0x87, 0x93, - 0x0b, 0x97, 0x87, 0xdf, 0x04, 0x64, 0xeb, 0xb6, 0xda, 0x23, 0xc1, 0x5b, 0x1b, 0x9c, 0x2b, 0xec, - 0xb4, 0xb3, 0x92, 0xb4, 0x48, 0x7b, 0x4e, 0x68, 0xc7, 0x21, 0xa1, 0x4b, 0x3d, 0xc8, 0xba, 0xe0, - 0xc3, 0xe2, 0x6f, 0x58, 0x26, 0x95, 0xc1, 0xab, 0x90, 0xed, 0x63, 0x5b, 0xa5, 0x61, 0x8f, 0x81, - 0x51, 0x6e, 0xfb, 0xde, 0x07, 0x90, 0xf7, 0x3d, 0xec, 0x21, 0x91, 0x70, 0xbf, 0xf1, 0x89, 0x18, - 0xab, 0x66, 0xbe, 0xfd, 0x7e, 0x3d, 0xb1, 0x8f, 0x9f, 0x91, 0x4f, 0xc9, 0x8d, 0x7a, 0xb3, 0x51, - 0x7f, 0x2c, 0x0a, 0xd5, 0xfc, 0xb7, 0xdf, 0xaf, 0x67, 0x64, 0x4c, 0x4b, 0x28, 0xf7, 0x1e, 0x43, - 0x39, 0xb4, 0x03, 0x41, 0x07, 0x8d, 0xa0, 0xb4, 0x7d, 0x7c, 0xb8, 0xb7, 0x53, 0xaf, 0xb5, 0x1a, - 0xca, 0xc9, 0x41, 0xab, 0x21, 0x0a, 0xe8, 0x1a, 0x2c, 0xef, 0xed, 0xfc, 0x75, 0xb3, 0xa5, 0xd4, - 0xf7, 0x76, 0x1a, 0xfb, 0x2d, 0xa5, 0xd6, 0x6a, 0xd5, 0xea, 0x8f, 0xc5, 0xf8, 0xfd, 0xff, 0x05, - 0x28, 0xd7, 0xb6, 0xea, 0x3b, 0x35, 0xc3, 0xe8, 0x69, 0x6d, 0x95, 0xba, 0xfb, 0x3a, 0x24, 0x29, - 0xb2, 0x3c, 0xf5, 0x89, 0x6f, 0x75, 0x7a, 0x5d, 0x0c, 0x3d, 0x82, 0x14, 0x05, 0x9d, 0xd1, 0xf4, - 0x37, 0xbf, 0xd5, 0x19, 0x85, 0x32, 0x32, 0x18, 0x7a, 0x6e, 0xa6, 0x3e, 0x02, 0xae, 0x4e, 0xaf, - 0x9b, 0xa1, 0x3d, 0xc8, 0x38, 0x80, 0xdb, 0xac, 0x97, 0xb9, 0xd5, 0x99, 0xc5, 0x2c, 0x32, 0x35, - 0x06, 0x5c, 0x4e, 0x7f, 0x1f, 0x5c, 0x9d, 0x51, 0x51, 0x43, 0x32, 0xe4, 0x3c, 0x28, 0x7b, 0xf6, - 0x53, 0xe5, 0xea, 0x1c, 0x15, 0x3e, 0xf4, 0x05, 0x14, 0x83, 0xd0, 0xdc, 0x7c, 0xaf, 0x88, 0xab, - 0x73, 0x56, 0xdf, 0x88, 0xfe, 0x20, 0x4e, 0x37, 0xdf, 0xab, 0xe2, 0xea, 0x9c, 0xc5, 0x38, 0xf4, - 0x25, 0x2c, 0x8d, 0xe3, 0x68, 0xf3, 0x3f, 0x32, 0xae, 0x2e, 0x50, 0x9e, 0x43, 0x7d, 0x40, 0x13, - 0xf0, 0xb7, 0x05, 0xde, 0x1c, 0x57, 0x17, 0xa9, 0xd6, 0xa1, 0x0e, 0x94, 0xc3, 0xd8, 0xd4, 0xbc, - 0x6f, 0x90, 0xab, 0x73, 0x57, 0xee, 0xd8, 0x57, 0x82, 0x18, 0xc9, 0xbc, 0x6f, 0x92, 0xab, 0x73, - 0x17, 0xf2, 0xd0, 0x31, 0x80, 0xef, 0x6e, 0x3b, 0xc7, 0x1b, 0xe5, 0xea, 0x3c, 0x25, 0x3d, 0x64, - 0xc0, 0xf2, 0xa4, 0xcb, 0xec, 0x22, 0x4f, 0x96, 0xab, 0x0b, 0x55, 0xfa, 0x88, 0x3d, 0x07, 0xef, - 0xa5, 0xf3, 0x3d, 0x61, 0xae, 0xce, 0x59, 0xf2, 0xdb, 0xda, 0xfa, 0xe1, 0xc5, 0xaa, 0xf0, 0xe3, - 0x8b, 0x55, 0xe1, 0xa7, 0x17, 0xab, 0xc2, 0x77, 0x3f, 0xaf, 0xc6, 0x7e, 0xfc, 0x79, 0x35, 0xf6, - 0xdf, 0x3f, 0xaf, 0xc6, 0xfe, 0xe6, 0xee, 0xb9, 0x66, 0x77, 0x87, 0xa7, 0x1b, 0x6d, 0xbd, 0x4f, - 0xff, 0x41, 0x62, 0xa8, 0x97, 0x9b, 0x4c, 0x27, 0x69, 0xf9, 0xfe, 0xa7, 0x72, 0x9a, 0xa6, 0xb1, - 0xee, 0xc1, 0x1f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x71, 0x2a, 0xdd, 0xc7, 0x32, 0x00, 0x00, + // 3811 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x5b, 0x4d, 0x6c, 0x1b, 0xd7, + 0x76, 0xe6, 0xf0, 0x9f, 0x87, 0x7f, 0xa3, 0x2b, 0xd9, 0xa6, 0x69, 0x5b, 0xd2, 0x1b, 0x37, 0xcf, + 0x7e, 0x7e, 0x89, 0x94, 0x67, 0x37, 0xcf, 0x49, 0x5f, 0x5a, 0x80, 0xa2, 0xe8, 0x52, 0xb2, 0x2c, + 0x29, 0x23, 0x4a, 0x6e, 0x9a, 0x26, 0x83, 0x11, 0x79, 0x45, 0x4e, 0x4c, 0x72, 0x26, 0x33, 0x43, + 0x99, 0xca, 0xb6, 0x2d, 0x50, 0x64, 0x95, 0x7d, 0x91, 0x45, 0x81, 0x76, 0xd9, 0x7d, 0x57, 0xed, + 0xae, 0x4d, 0xd1, 0x4d, 0x96, 0x05, 0x0a, 0xb8, 0x81, 0xb3, 0xeb, 0x2e, 0xab, 0x6e, 0xba, 0x28, + 0xee, 0xcf, 0xfc, 0x92, 0xc3, 0x9f, 0x38, 0x40, 0xd1, 0x1d, 0xef, 0xb9, 0xe7, 0x9c, 0xb9, 0x3f, + 0xe7, 0x9e, 0x73, 0xee, 0x77, 0x2e, 0xe1, 0x96, 0x8d, 0x87, 0x1d, 0x6c, 0x0e, 0xb4, 0xa1, 0xbd, + 0xad, 0x9e, 0xb7, 0xb5, 0x6d, 0xfb, 0xca, 0xc0, 0xd6, 0x96, 0x61, 0xea, 0xb6, 0x8e, 0xca, 0x5e, + 0xe7, 0x16, 0xe9, 0xac, 0xde, 0xf1, 0x71, 0xb7, 0xcd, 0x2b, 0xc3, 0xd6, 0xb7, 0x0d, 0x53, 0xd7, + 0x2f, 0x18, 0x7f, 0xd5, 0xaf, 0x8c, 0xea, 0xd9, 0xee, 0xa8, 0x56, 0x8f, 0x77, 0xde, 0x9e, 0xe8, + 0x3c, 0xef, 0xeb, 0xed, 0x17, 0x91, 0xbd, 0xbe, 0x81, 0x04, 0x7a, 0xf9, 0x77, 0x5f, 0xe0, 0x2b, + 0xa7, 0xf7, 0xce, 0x84, 0xac, 0xa1, 0x9a, 0xea, 0xc0, 0xe9, 0x5e, 0xf7, 0x75, 0x5f, 0x62, 0xd3, + 0xd2, 0xf4, 0x61, 0x40, 0xf9, 0x46, 0x57, 0xd7, 0xbb, 0x7d, 0xbc, 0x4d, 0x5b, 0xe7, 0xa3, 0x8b, + 0x6d, 0x5b, 0x1b, 0x60, 0xcb, 0x56, 0x07, 0x06, 0x67, 0x58, 0xeb, 0xea, 0x5d, 0x9d, 0xfe, 0xdc, + 0x26, 0xbf, 0x18, 0x55, 0x7a, 0x95, 0x83, 0x8c, 0x8c, 0xbf, 0x18, 0x61, 0xcb, 0x46, 0x0f, 0x21, + 0x89, 0xdb, 0x3d, 0xbd, 0x22, 0x6c, 0x0a, 0xf7, 0xf3, 0x0f, 0x6f, 0x6f, 0x85, 0xd6, 0x6d, 0x8b, + 0xf3, 0x35, 0xda, 0x3d, 0xbd, 0x19, 0x93, 0x29, 0x2f, 0x7a, 0x0f, 0x52, 0x17, 0xfd, 0x91, 0xd5, + 0xab, 0xc4, 0xa9, 0xd0, 0x9d, 0x28, 0xa1, 0x27, 0x84, 0xa9, 0x19, 0x93, 0x19, 0x37, 0xf9, 0x94, + 0x36, 0xbc, 0xd0, 0x2b, 0x89, 0xd9, 0x9f, 0xda, 0x1b, 0x5e, 0xd0, 0x4f, 0x11, 0x5e, 0xb4, 0x03, + 0xa0, 0x0d, 0x35, 0x5b, 0x69, 0xf7, 0x54, 0x6d, 0x58, 0x49, 0x52, 0xc9, 0x5f, 0x44, 0x4b, 0x6a, + 0x76, 0x9d, 0x30, 0x36, 0x63, 0x72, 0x4e, 0x73, 0x1a, 0x64, 0xb8, 0x5f, 0x8c, 0xb0, 0x79, 0x55, + 0x49, 0xcd, 0x1e, 0xee, 0x47, 0x84, 0x89, 0x0c, 0x97, 0x72, 0xa3, 0x0f, 0x21, 0xdb, 0xee, 0xe1, + 0xf6, 0x0b, 0xc5, 0x1e, 0x57, 0x32, 0x54, 0x72, 0x23, 0x4a, 0xb2, 0x4e, 0xf8, 0x5a, 0xe3, 0x66, + 0x4c, 0xce, 0xb4, 0xd9, 0x4f, 0x74, 0x08, 0xa5, 0xbe, 0x66, 0xd9, 0x8a, 0x35, 0x54, 0x0d, 0xab, + 0xa7, 0xdb, 0x56, 0x25, 0x4f, 0x75, 0xbc, 0x15, 0xa5, 0xe3, 0x40, 0xb3, 0xec, 0x13, 0x87, 0xb9, + 0x19, 0x93, 0x8b, 0x7d, 0x3f, 0x81, 0xe8, 0xd3, 0x2f, 0x2e, 0xb0, 0xe9, 0x2a, 0xac, 0x14, 0x66, + 0xeb, 0x3b, 0x22, 0xdc, 0x8e, 0x3c, 0xd1, 0xa7, 0xfb, 0x09, 0xe8, 0x13, 0x58, 0xed, 0xeb, 0x6a, + 0xc7, 0x55, 0xa7, 0xb4, 0x7b, 0xa3, 0xe1, 0x8b, 0x4a, 0x91, 0x2a, 0xfd, 0x55, 0xe4, 0x20, 0x75, + 0xb5, 0xe3, 0xa8, 0xa8, 0x13, 0x81, 0x66, 0x4c, 0x5e, 0xe9, 0x87, 0x89, 0xe8, 0x33, 0x58, 0x53, + 0x0d, 0xa3, 0x7f, 0x15, 0xd6, 0x5e, 0xa2, 0xda, 0x1f, 0x44, 0x69, 0xaf, 0x11, 0x99, 0xb0, 0x7a, + 0xa4, 0x4e, 0x50, 0xd1, 0x73, 0x58, 0xb9, 0xd0, 0x86, 0x6a, 0x5f, 0xfb, 0x12, 0x7b, 0xeb, 0xb1, + 0x46, 0x95, 0xdf, 0x8f, 0x34, 0x46, 0x2e, 0xe0, 0x5b, 0x12, 0xf1, 0x22, 0x44, 0x43, 0x2d, 0x10, + 0x0d, 0x13, 0x1b, 0xaa, 0x89, 0x15, 0xc3, 0xd4, 0x0d, 0xdd, 0x52, 0xfb, 0x95, 0x32, 0xd5, 0x7b, + 0x2f, 0x4a, 0xef, 0x31, 0xe3, 0x3f, 0xe6, 0xec, 0xcd, 0x98, 0x5c, 0x36, 0x82, 0x24, 0xa6, 0x55, + 0x6f, 0x63, 0xcb, 0xf2, 0xb4, 0x8a, 0xf3, 0xb4, 0x52, 0xfe, 0xa0, 0xd6, 0x00, 0x09, 0x35, 0x20, + 0x8f, 0xc7, 0x44, 0x5c, 0xb9, 0xd4, 0x6d, 0x5c, 0x59, 0xa1, 0x0a, 0xa5, 0xc8, 0x03, 0x4c, 0x59, + 0xcf, 0x74, 0x1b, 0x37, 0x63, 0x32, 0x60, 0xb7, 0x85, 0x54, 0xb8, 0x76, 0x89, 0x4d, 0xed, 0xe2, + 0x8a, 0xaa, 0x51, 0x68, 0x0f, 0x71, 0x34, 0x15, 0x44, 0x15, 0xfe, 0x3a, 0x4a, 0xe1, 0x19, 0x15, + 0x22, 0x2a, 0x1a, 0x8e, 0x48, 0x33, 0x26, 0xaf, 0x5e, 0x4e, 0x92, 0x89, 0xed, 0xba, 0xdb, 0x45, + 0x3d, 0x67, 0x65, 0x75, 0xb6, 0xed, 0x3a, 0x7b, 0xb5, 0x43, 0x98, 0x89, 0xed, 0x5e, 0xf8, 0x09, + 0x3b, 0x19, 0x48, 0x5d, 0xaa, 0xfd, 0x11, 0xde, 0x4f, 0x66, 0xd3, 0x62, 0x66, 0x3f, 0x99, 0xcd, + 0x8a, 0xb9, 0xfd, 0x64, 0x36, 0x27, 0xc2, 0x7e, 0x32, 0x0b, 0x62, 0x5e, 0xba, 0x07, 0x79, 0x9f, + 0xdf, 0x42, 0x15, 0xc8, 0x0c, 0xb0, 0x65, 0xa9, 0x5d, 0x4c, 0xdd, 0x5c, 0x4e, 0x76, 0x9a, 0x52, + 0x09, 0x0a, 0x7e, 0x5f, 0x25, 0x7d, 0x2d, 0xb8, 0x92, 0xc4, 0x0d, 0x11, 0x49, 0xee, 0x77, 0x1d, + 0x49, 0xde, 0x44, 0x77, 0xa1, 0x48, 0xa7, 0xa2, 0x38, 0xfd, 0xc4, 0x17, 0x26, 0xe5, 0x02, 0x25, + 0x9e, 0x71, 0xa6, 0x0d, 0xc8, 0x1b, 0x0f, 0x0d, 0x97, 0x25, 0x41, 0x59, 0xc0, 0x78, 0x68, 0x38, + 0x0c, 0xbf, 0x80, 0x02, 0x99, 0xb7, 0xcb, 0x91, 0xa4, 0x1f, 0xc9, 0x13, 0x1a, 0x67, 0x91, 0xfe, + 0x22, 0x01, 0x62, 0xd8, 0xbf, 0xa1, 0xf7, 0x21, 0x49, 0x5c, 0x3d, 0xf7, 0xda, 0xd5, 0x2d, 0x16, + 0x07, 0xb6, 0x9c, 0x38, 0xb0, 0xd5, 0x72, 0xe2, 0xc0, 0x4e, 0xf6, 0xdb, 0x57, 0x1b, 0xb1, 0xaf, + 0xff, 0x73, 0x43, 0x90, 0xa9, 0x04, 0xba, 0x49, 0xbc, 0x9a, 0xaa, 0x0d, 0x15, 0xad, 0x43, 0x87, + 0x9c, 0x23, 0x2e, 0x4b, 0xd5, 0x86, 0x7b, 0x1d, 0x74, 0x00, 0x62, 0x5b, 0x1f, 0x5a, 0x78, 0x68, + 0x8d, 0x2c, 0x85, 0xc5, 0x21, 0xee, 0xab, 0x03, 0x1e, 0x97, 0x05, 0xa0, 0xba, 0xc3, 0x79, 0x4c, + 0x19, 0xe5, 0x72, 0x3b, 0x48, 0x40, 0x87, 0x50, 0xbc, 0x54, 0xfb, 0x5a, 0x47, 0xb5, 0x75, 0x53, + 0xb1, 0xb0, 0xcd, 0x9d, 0xf7, 0xdd, 0x89, 0x3d, 0x3f, 0x73, 0xb8, 0x4e, 0xb0, 0x7d, 0x6a, 0x74, + 0x54, 0x1b, 0xef, 0x24, 0xbf, 0x7d, 0xb5, 0x21, 0xc8, 0x85, 0x4b, 0x5f, 0x0f, 0xfa, 0x25, 0x94, + 0x55, 0xc3, 0x50, 0x2c, 0x5b, 0xb5, 0xb1, 0x72, 0x7e, 0x65, 0x63, 0x8b, 0xfa, 0xf3, 0x82, 0x5c, + 0x54, 0x0d, 0xe3, 0x84, 0x50, 0x77, 0x08, 0x11, 0xbd, 0x05, 0x25, 0xe2, 0xfa, 0x35, 0xb5, 0xaf, + 0xf4, 0xb0, 0xd6, 0xed, 0xd9, 0x95, 0xf4, 0xa6, 0x70, 0x3f, 0x21, 0x17, 0x39, 0xb5, 0x49, 0x89, + 0x68, 0x0b, 0x56, 0x1d, 0xb6, 0xb6, 0x6e, 0x62, 0x87, 0x97, 0x38, 0xfa, 0xa2, 0xbc, 0xc2, 0xbb, + 0xea, 0xba, 0x89, 0x19, 0xbf, 0xd4, 0x71, 0x2d, 0x85, 0x86, 0x09, 0x84, 0x20, 0xd9, 0x51, 0x6d, + 0x95, 0xee, 0x40, 0x41, 0xa6, 0xbf, 0x09, 0xcd, 0x50, 0xed, 0x1e, 0x5f, 0x57, 0xfa, 0x1b, 0x5d, + 0x87, 0x34, 0x57, 0x9d, 0xa0, 0xc3, 0xe0, 0x2d, 0xb4, 0x06, 0x29, 0xc3, 0xd4, 0x2f, 0x31, 0x5d, + 0x96, 0xac, 0xcc, 0x1a, 0x92, 0x0c, 0xa5, 0x60, 0x48, 0x41, 0x25, 0x88, 0xdb, 0x63, 0xfe, 0x95, + 0xb8, 0x3d, 0x46, 0xef, 0x42, 0x92, 0x6c, 0x00, 0xfd, 0x46, 0x69, 0x4a, 0x10, 0xe5, 0x72, 0xad, + 0x2b, 0x03, 0xcb, 0x94, 0x53, 0xba, 0x0e, 0x6b, 0xd3, 0x42, 0x8c, 0xd4, 0x73, 0xe9, 0x81, 0x50, + 0x81, 0xde, 0x83, 0xac, 0xeb, 0x53, 0x99, 0x7d, 0xdd, 0x9c, 0xf8, 0x8a, 0xc3, 0x2c, 0xbb, 0xac, + 0xc4, 0xb0, 0xc8, 0xfe, 0xf4, 0x54, 0x9e, 0x17, 0x14, 0xe4, 0x8c, 0x6a, 0x18, 0x4d, 0xd5, 0xea, + 0x49, 0x5d, 0xa8, 0x44, 0xc5, 0x0f, 0xdf, 0xfa, 0x08, 0xf4, 0x74, 0x38, 0xeb, 0xe3, 0x3b, 0x79, + 0x71, 0xba, 0x27, 0xee, 0xc9, 0xa3, 0x16, 0x3c, 0x1a, 0xbe, 0x20, 0x16, 0x9c, 0x60, 0x1f, 0xa2, + 0xed, 0xbd, 0x8e, 0xd4, 0x81, 0x9b, 0x91, 0xa1, 0x24, 0x20, 0x27, 0x04, 0xe4, 0xc8, 0x66, 0xb0, + 0x00, 0xc5, 0x06, 0xce, 0x1a, 0x64, 0x68, 0x16, 0x9d, 0x37, 0xfd, 0x4c, 0x4e, 0xe6, 0x2d, 0xe9, + 0x6f, 0x04, 0xb8, 0x11, 0x11, 0x54, 0x50, 0x1d, 0x4a, 0x6e, 0xcc, 0x63, 0xae, 0x6e, 0x4a, 0x62, + 0xc5, 0x4e, 0xd0, 0x01, 0x99, 0x27, 0x75, 0x68, 0x72, 0xd1, 0x91, 0xa1, 0x4d, 0x54, 0x83, 0x62, + 0x17, 0x0f, 0xb1, 0xa5, 0x59, 0x5c, 0x47, 0x7c, 0x01, 0x1d, 0x05, 0x2e, 0x42, 0x5b, 0xd2, 0x8f, + 0x49, 0xb8, 0x3e, 0x3d, 0x40, 0xa1, 0x4d, 0x28, 0x0c, 0xd4, 0xb1, 0x62, 0x8f, 0xf9, 0x29, 0x12, + 0xa8, 0x5d, 0xc2, 0x40, 0x1d, 0xb7, 0xc6, 0xec, 0x08, 0x89, 0x90, 0xb0, 0xc7, 0x56, 0x25, 0xbe, + 0x99, 0xb8, 0x5f, 0x90, 0xc9, 0x4f, 0xf4, 0x0c, 0x56, 0xfa, 0x7a, 0x5b, 0xed, 0x2b, 0x7d, 0xd5, + 0xb2, 0x95, 0xb6, 0x3e, 0x18, 0x68, 0x36, 0xf7, 0x0d, 0xb7, 0x26, 0x4d, 0x90, 0x76, 0x13, 0xff, + 0x49, 0x0f, 0x72, 0x4c, 0x2e, 0x53, 0xd9, 0x03, 0xd5, 0xb2, 0x59, 0x17, 0xda, 0x85, 0xfc, 0x40, + 0xb3, 0xce, 0x71, 0x4f, 0xbd, 0xd4, 0x74, 0xb3, 0x92, 0xdc, 0x4c, 0x4c, 0x4d, 0x08, 0x9f, 0x79, + 0x3c, 0x5c, 0x93, 0x5f, 0xcc, 0x67, 0x3a, 0xa9, 0xc0, 0xd1, 0x72, 0x9c, 0x63, 0x7a, 0x69, 0xe7, + 0xf8, 0x2e, 0xac, 0x0d, 0xf1, 0xd8, 0x56, 0x5c, 0xc7, 0x63, 0x31, 0x7b, 0xce, 0x50, 0xb3, 0x40, + 0xa4, 0xcf, 0xf5, 0x56, 0x16, 0x31, 0x6d, 0x62, 0x39, 0xa6, 0x3e, 0x1a, 0x76, 0x2a, 0xd9, 0x4d, + 0xe1, 0x7e, 0x4a, 0x66, 0x0d, 0xf4, 0x18, 0x2a, 0xd4, 0xa9, 0x30, 0x4f, 0x4b, 0x36, 0x04, 0x77, + 0x1c, 0x0f, 0x93, 0xa3, 0xd6, 0x7c, 0x8d, 0xf4, 0x53, 0x5f, 0x7e, 0x40, 0x7b, 0xb9, 0x57, 0xda, + 0x86, 0x35, 0x96, 0x21, 0x60, 0x93, 0xa4, 0x0a, 0x64, 0x93, 0xe8, 0x00, 0x80, 0x0e, 0x60, 0xc5, + 0xe9, 0x3b, 0x36, 0xf5, 0xd6, 0x98, 0x7e, 0xff, 0x5d, 0x57, 0xa0, 0xa3, 0x90, 0xe3, 0xe7, 0x9c, + 0x99, 0x3c, 0x3d, 0x4c, 0xc8, 0xe9, 0xab, 0x19, 0x6e, 0xc8, 0x79, 0xec, 0x1d, 0xac, 0xc2, 0x64, + 0x3e, 0xcc, 0xbb, 0x3c, 0xf7, 0xee, 0x9d, 0xbb, 0x0d, 0xc8, 0x7f, 0x31, 0xd2, 0xcd, 0xd1, 0x80, + 0x0d, 0xa9, 0x48, 0x87, 0x04, 0x8c, 0x44, 0x8f, 0xf9, 0x3f, 0xa5, 0x7c, 0x36, 0x17, 0xcc, 0x55, + 0xb8, 0x45, 0x09, 0x9e, 0x45, 0x9d, 0xf8, 0x06, 0xee, 0x37, 0xaa, 0xf8, 0xa2, 0x46, 0xe5, 0xce, + 0x2d, 0xda, 0xae, 0x12, 0x3f, 0xcd, 0xae, 0x10, 0x24, 0xe9, 0x0c, 0x93, 0xcc, 0xb5, 0x93, 0xdf, + 0x91, 0xb6, 0xe6, 0xee, 0x7f, 0xda, 0xbf, 0xff, 0x8e, 0x05, 0x66, 0x7e, 0x36, 0x0b, 0xcc, 0x46, + 0x5a, 0xe0, 0x4f, 0xb6, 0xb5, 0x16, 0x5c, 0x0f, 0x09, 0x2a, 0x23, 0x1a, 0x7e, 0xa9, 0xb5, 0x85, + 0x6e, 0x3b, 0x4e, 0xd0, 0xf7, 0x29, 0x92, 0x57, 0x03, 0x7a, 0x59, 0xe8, 0x8e, 0xb4, 0xe0, 0xfc, + 0xb2, 0x16, 0x5c, 0x58, 0xc4, 0x82, 0x8b, 0x6f, 0x62, 0xc1, 0xa5, 0x09, 0x0b, 0x3e, 0x85, 0x95, + 0x89, 0x74, 0xd9, 0x35, 0x07, 0x61, 0xaa, 0x39, 0xc4, 0xa7, 0x9b, 0x43, 0xc2, 0x67, 0x0e, 0xd2, + 0xf7, 0x02, 0x54, 0xa3, 0xb3, 0xe6, 0xa9, 0x1f, 0xf8, 0x0d, 0x5c, 0xf3, 0xb2, 0x27, 0xff, 0x3a, + 0xb2, 0x08, 0x85, 0xdc, 0x4e, 0x6f, 0x21, 0x67, 0x64, 0x1a, 0x6c, 0x4c, 0x49, 0xbf, 0x89, 0x3e, + 0x83, 0x72, 0x30, 0xdf, 0x27, 0xe9, 0x14, 0x39, 0x2e, 0xbf, 0x37, 0x71, 0x5c, 0xbc, 0xb5, 0x70, + 0xc7, 0x2c, 0x97, 0x2e, 0xfd, 0x4d, 0x4b, 0xfa, 0xb7, 0xb8, 0x9b, 0x4d, 0x04, 0x92, 0x77, 0xf4, + 0x01, 0xa4, 0xf9, 0xc9, 0x16, 0x16, 0x3d, 0xd9, 0x5c, 0x20, 0x7c, 0x9a, 0xe3, 0x6f, 0x76, 0x9a, + 0x13, 0x53, 0xb7, 0x2f, 0x39, 0x7d, 0xa9, 0x52, 0xfe, 0xa5, 0x7a, 0x07, 0x52, 0x2c, 0x0c, 0xb3, + 0x80, 0x72, 0x63, 0xf2, 0x5c, 0xb0, 0x08, 0xcc, 0xb8, 0x50, 0x0d, 0xb2, 0xec, 0x66, 0xa0, 0x75, + 0xb8, 0x03, 0xb8, 0x19, 0x21, 0xb1, 0xb7, 0xbb, 0x93, 0x7f, 0xfd, 0x6a, 0x23, 0xc3, 0x1b, 0x72, + 0x86, 0xca, 0xed, 0x75, 0xa4, 0xbf, 0x06, 0xc8, 0xca, 0xd8, 0x32, 0x88, 0x09, 0xa3, 0x1d, 0xc8, + 0xe1, 0x71, 0x1b, 0x1b, 0xb6, 0x73, 0x0b, 0x99, 0x7e, 0xcb, 0x63, 0xdc, 0x0d, 0x87, 0xb3, 0x19, + 0x93, 0x3d, 0x31, 0xf4, 0x88, 0xa3, 0x3c, 0xd1, 0x80, 0x0d, 0x17, 0xf7, 0xc3, 0x3c, 0xbf, 0x75, + 0x60, 0x1e, 0x16, 0xe8, 0xd7, 0x23, 0xa5, 0x42, 0x38, 0xcf, 0x23, 0x8e, 0xf3, 0x24, 0xe7, 0x7c, + 0x2c, 0x00, 0xf4, 0xd4, 0x03, 0x40, 0x4f, 0x6a, 0xce, 0x34, 0x23, 0x90, 0x9e, 0xdf, 0x3a, 0x48, + 0x4f, 0x7a, 0xce, 0x88, 0x43, 0x50, 0xcf, 0x1f, 0x4e, 0x40, 0x3d, 0x9b, 0x91, 0xa2, 0x53, 0xb0, + 0x9e, 0xa3, 0x09, 0xac, 0x27, 0x4b, 0x95, 0xfc, 0x32, 0x52, 0xc9, 0x1c, 0xb0, 0xe7, 0x68, 0x02, + 0xec, 0xc9, 0xcd, 0x51, 0x38, 0x07, 0xed, 0xf9, 0xb3, 0xe9, 0x68, 0x0f, 0x44, 0xe2, 0x31, 0x7c, + 0x98, 0x8b, 0xc1, 0x3d, 0x4a, 0x04, 0xdc, 0x93, 0x8f, 0x44, 0x10, 0x98, 0xfa, 0x85, 0xf1, 0x9e, + 0x3f, 0x99, 0x86, 0xf7, 0xac, 0x44, 0x42, 0x55, 0xdc, 0x2a, 0x17, 0x01, 0x7c, 0x4e, 0xa7, 0x00, + 0x3e, 0x85, 0x48, 0x20, 0x89, 0x29, 0x5e, 0x00, 0xf1, 0x39, 0x9d, 0x82, 0xf8, 0x14, 0xe7, 0xaa, + 0x9d, 0x0b, 0xf9, 0x3c, 0x09, 0x42, 0x3e, 0xa5, 0x88, 0x1b, 0xb5, 0xe7, 0x0c, 0x22, 0x30, 0x9f, + 0xf3, 0x28, 0xcc, 0x87, 0x61, 0x5d, 0x6f, 0x47, 0x6a, 0x5c, 0x02, 0xf4, 0x39, 0x9a, 0x00, 0x7d, + 0xc4, 0x39, 0x36, 0xbc, 0x20, 0xea, 0x23, 0xfd, 0x8a, 0x44, 0xe9, 0x90, 0xbb, 0x23, 0xae, 0x1b, + 0x9b, 0xa6, 0x6e, 0x72, 0x9c, 0x86, 0x35, 0xa4, 0xfb, 0xe4, 0xd6, 0xee, 0xb9, 0xb6, 0x19, 0x48, + 0x50, 0x19, 0x8a, 0x01, 0x77, 0x26, 0xfd, 0x83, 0xe0, 0xc9, 0x52, 0x2c, 0xc8, 0x7f, 0xe3, 0xcf, + 0xf1, 0x1b, 0x7f, 0xe8, 0x96, 0x9a, 0x0b, 0xe4, 0x1a, 0xfe, 0x6c, 0x86, 0x43, 0x3f, 0xaa, 0x97, + 0xc5, 0x3c, 0x80, 0x15, 0x9a, 0xf7, 0xb2, 0x58, 0x11, 0x08, 0x47, 0x65, 0xd2, 0xc1, 0x56, 0x81, + 0xc5, 0xa5, 0x77, 0x60, 0xd5, 0xc7, 0xeb, 0x5e, 0xb3, 0x19, 0xfe, 0x21, 0xba, 0xdc, 0x35, 0x7e, + 0xdf, 0xfe, 0x97, 0x84, 0xb7, 0x42, 0x1e, 0x66, 0x34, 0x0d, 0xde, 0x11, 0x7e, 0x32, 0xbc, 0x13, + 0x7d, 0xdd, 0x47, 0x9f, 0xc0, 0x5a, 0x00, 0xf9, 0x71, 0xd2, 0xca, 0xc4, 0x72, 0x00, 0x50, 0xcc, + 0x97, 0xe5, 0xb8, 0x3d, 0xe8, 0x53, 0xb8, 0x45, 0x13, 0xe4, 0x88, 0xd4, 0x35, 0xb9, 0x58, 0xea, + 0x7a, 0x83, 0xe8, 0xa8, 0x4f, 0x49, 0x5f, 0x23, 0x60, 0xa1, 0x54, 0x04, 0x2c, 0x84, 0x0e, 0xc0, + 0xb9, 0x77, 0x2b, 0x4b, 0xdc, 0x39, 0x05, 0x92, 0xf1, 0x37, 0x63, 0x72, 0x9e, 0xcb, 0x92, 0xde, + 0xbf, 0x12, 0x84, 0x9d, 0x32, 0x14, 0x15, 0xbf, 0x3a, 0xe9, 0xbf, 0x05, 0xcf, 0x2c, 0x5d, 0xdc, + 0xa9, 0xad, 0x77, 0x98, 0xf9, 0x16, 0x65, 0xfa, 0x9b, 0xdc, 0xae, 0xfa, 0x7a, 0x97, 0x5b, 0x20, + 0xf9, 0x49, 0xb8, 0xdc, 0x52, 0x4b, 0x8e, 0x47, 0xd8, 0x35, 0x48, 0x69, 0xc3, 0x0e, 0x1e, 0x73, + 0x23, 0x63, 0x0d, 0x22, 0xfb, 0x02, 0x5f, 0x71, 0x53, 0x22, 0x3f, 0x09, 0x1f, 0x3d, 0x67, 0x74, + 0x2e, 0x05, 0x99, 0x35, 0xd0, 0xfb, 0x90, 0xa3, 0xf5, 0x32, 0x45, 0x37, 0x2c, 0x1e, 0x23, 0x03, + 0xa9, 0x1c, 0xab, 0x6d, 0x6d, 0x1d, 0x13, 0x9e, 0x23, 0xc3, 0x92, 0xb3, 0x06, 0xff, 0xe5, 0x4b, + 0xb6, 0xb2, 0x81, 0x64, 0xeb, 0x36, 0xe4, 0xc8, 0xe8, 0x2d, 0x43, 0x6d, 0x63, 0x1a, 0xdf, 0x72, + 0xb2, 0x47, 0x90, 0xfe, 0x51, 0x80, 0x72, 0x28, 0xe4, 0x4e, 0x9d, 0xbb, 0x73, 0x2a, 0xe3, 0x41, + 0x1c, 0x6e, 0x62, 0xf6, 0x77, 0x00, 0xba, 0xaa, 0xa5, 0xbc, 0x54, 0x87, 0x36, 0xee, 0xf0, 0x25, + 0xc8, 0x75, 0x55, 0xeb, 0x39, 0x25, 0x04, 0x07, 0x93, 0x0a, 0x0d, 0xc6, 0x87, 0x04, 0xa5, 0xfd, + 0x48, 0x10, 0xaa, 0x42, 0xd6, 0x30, 0x35, 0xdd, 0xd4, 0xec, 0x2b, 0xba, 0x26, 0x09, 0xd9, 0x6d, + 0x4b, 0xc7, 0x70, 0x6d, 0x6a, 0xb4, 0x47, 0x8f, 0x21, 0xe7, 0x25, 0x0a, 0x02, 0x4d, 0x6a, 0x67, + 0x00, 0x6c, 0x1e, 0x2f, 0x59, 0x92, 0x6b, 0x53, 0xe3, 0x3d, 0x6a, 0x40, 0xda, 0xc4, 0xd6, 0xa8, + 0xcf, 0x92, 0xec, 0xd2, 0xc3, 0x77, 0x16, 0xcb, 0x13, 0x08, 0x75, 0xd4, 0xb7, 0x65, 0x2e, 0x2c, + 0x7d, 0x06, 0x69, 0x46, 0x41, 0x79, 0xc8, 0x9c, 0x1e, 0x3e, 0x3d, 0x3c, 0x7a, 0x7e, 0x28, 0xc6, + 0x10, 0x40, 0xba, 0x56, 0xaf, 0x37, 0x8e, 0x5b, 0xa2, 0x80, 0x72, 0x90, 0xaa, 0xed, 0x1c, 0xc9, + 0x2d, 0x31, 0x4e, 0xc8, 0x72, 0x63, 0xbf, 0x51, 0x6f, 0x89, 0x09, 0xb4, 0x02, 0x45, 0xf6, 0x5b, + 0x79, 0x72, 0x24, 0x3f, 0xab, 0xb5, 0xc4, 0xa4, 0x8f, 0x74, 0xd2, 0x38, 0xdc, 0x6d, 0xc8, 0x62, + 0x4a, 0xfa, 0x0d, 0xdc, 0x8c, 0xcc, 0x2c, 0x3c, 0x0c, 0x4e, 0xf0, 0x61, 0x70, 0xd2, 0x77, 0x71, + 0x72, 0x75, 0x8a, 0x4a, 0x17, 0xd0, 0x7e, 0x68, 0xe2, 0x0f, 0x97, 0xc8, 0x35, 0x42, 0xb3, 0x47, + 0x6f, 0x41, 0xc9, 0xc4, 0x17, 0xd8, 0x6e, 0xf7, 0x58, 0xfa, 0xe2, 0x00, 0x60, 0x45, 0x4e, 0xa5, + 0x42, 0x16, 0x63, 0xfb, 0x1c, 0xb7, 0x6d, 0x85, 0x19, 0x81, 0x45, 0x61, 0x86, 0x1c, 0x61, 0x23, + 0xd4, 0x13, 0x46, 0x24, 0xfe, 0x9f, 0xf9, 0x29, 0xa6, 0x2a, 0x49, 0x55, 0x01, 0x75, 0x3b, 0x94, + 0x22, 0xbd, 0x5c, 0x6a, 0xb1, 0x73, 0x90, 0x92, 0x1b, 0x2d, 0xf9, 0x63, 0x31, 0x81, 0x10, 0x94, + 0xe8, 0x4f, 0xe5, 0xe4, 0xb0, 0x76, 0x7c, 0xd2, 0x3c, 0x22, 0x8b, 0xbd, 0x0a, 0x65, 0x67, 0xb1, + 0x1d, 0x62, 0x0a, 0x5d, 0x83, 0x95, 0xfa, 0xd1, 0xb3, 0xe3, 0x83, 0x46, 0xab, 0xe1, 0x91, 0xd3, + 0x52, 0x15, 0x2a, 0x51, 0x29, 0x92, 0xf4, 0x1f, 0x09, 0xb8, 0x11, 0x91, 0xe6, 0xa0, 0xf7, 0x01, + 0xec, 0xb1, 0x62, 0xe2, 0xb6, 0x6e, 0x76, 0xa2, 0x0d, 0xb7, 0x35, 0x96, 0x29, 0x87, 0x9c, 0xb3, + 0xf9, 0xaf, 0x99, 0xb1, 0xe2, 0x43, 0xae, 0x94, 0x2c, 0x84, 0xc5, 0x01, 0x9b, 0x3b, 0x53, 0x6e, + 0xa0, 0xb8, 0x4d, 0x14, 0xd3, 0xfd, 0xa2, 0x8a, 0x29, 0x3f, 0xfa, 0x18, 0x6e, 0x84, 0x42, 0x1a, + 0x8f, 0x03, 0xd6, 0xb4, 0x52, 0xf1, 0xf4, 0xc8, 0x76, 0x2d, 0x18, 0xd9, 0x58, 0x1c, 0xb0, 0x66, + 0xa0, 0x23, 0xa9, 0x37, 0x40, 0x47, 0xa2, 0x42, 0x63, 0x7a, 0xd9, 0xda, 0xc8, 0xb4, 0xd0, 0x18, + 0x4a, 0x39, 0x32, 0xe1, 0x94, 0x43, 0xfa, 0x9f, 0xc0, 0xee, 0x06, 0x53, 0xcb, 0x23, 0x48, 0x5b, + 0xb6, 0x6a, 0x8f, 0x2c, 0x7e, 0x92, 0x1e, 0x2f, 0x9a, 0xa7, 0x6e, 0x39, 0x3f, 0x4e, 0xa8, 0xb8, + 0xcc, 0xd5, 0xfc, 0xbf, 0xdc, 0xf4, 0xa8, 0xed, 0x49, 0xfd, 0x1c, 0xdb, 0xd3, 0x84, 0x34, 0xbe, + 0xc4, 0x43, 0xdb, 0xaa, 0xa4, 0xe9, 0x8c, 0xaf, 0x4f, 0xce, 0x98, 0x74, 0xef, 0x54, 0x48, 0xee, + 0xf3, 0x5f, 0xaf, 0x36, 0x44, 0xc6, 0xfd, 0xb6, 0x3e, 0xd0, 0x6c, 0x3c, 0x30, 0xec, 0x2b, 0x99, + 0xcb, 0x4b, 0xef, 0x41, 0x29, 0xb8, 0xe8, 0xd1, 0x2e, 0xc4, 0x73, 0xd2, 0x71, 0xe9, 0xef, 0x05, + 0x58, 0x9d, 0x82, 0xe5, 0xa0, 0xc7, 0xbc, 0xa4, 0xc4, 0x36, 0xfe, 0xee, 0xe4, 0xea, 0x05, 0xd8, + 0xbd, 0xca, 0x12, 0x09, 0x9a, 0xde, 0xd5, 0x81, 0xed, 0xb1, 0x47, 0x40, 0xbf, 0x86, 0xb2, 0xa5, + 0x75, 0x87, 0x8a, 0xc9, 0x60, 0x21, 0xb7, 0x5c, 0x43, 0x32, 0x7b, 0xd2, 0xe1, 0x14, 0x35, 0x3b, + 0x24, 0xf3, 0x41, 0x20, 0x2a, 0x21, 0x6e, 0xa9, 0x0d, 0x68, 0xf2, 0x26, 0x33, 0x0d, 0xb8, 0x12, + 0xde, 0x00, 0xb8, 0xfa, 0x3b, 0x01, 0x6e, 0xcd, 0xb8, 0xdd, 0xa0, 0x8f, 0x42, 0xe7, 0xe2, 0x83, + 0x65, 0xee, 0x46, 0x5b, 0x8c, 0x16, 0x3c, 0x19, 0xd2, 0x23, 0x28, 0xf8, 0xe9, 0x8b, 0x6d, 0xde, + 0xbe, 0x17, 0xfb, 0x83, 0x00, 0xdb, 0x5d, 0x28, 0x9a, 0xd8, 0x26, 0x4e, 0x2a, 0x80, 0x48, 0x16, + 0x18, 0x91, 0xa5, 0xa9, 0xfb, 0xc9, 0xac, 0x20, 0xc6, 0x5d, 0xfb, 0xf9, 0x57, 0x01, 0xc0, 0x43, + 0xdd, 0x3c, 0xd4, 0x4b, 0xf0, 0xa3, 0x5e, 0x21, 0xb0, 0x34, 0x1e, 0x06, 0x4b, 0xd1, 0x3d, 0x28, + 0xb3, 0xfb, 0x08, 0xd9, 0x37, 0xd5, 0x1e, 0x99, 0x98, 0x63, 0x6c, 0x25, 0x4a, 0x3e, 0x71, 0xa8, + 0xe8, 0x13, 0xb8, 0x69, 0xf7, 0x4c, 0x6c, 0xf5, 0xf4, 0x7e, 0x47, 0x09, 0xef, 0x1d, 0xab, 0xfd, + 0x6c, 0xcc, 0x31, 0x3a, 0xf9, 0x86, 0xab, 0xe1, 0x2c, 0xb8, 0x7f, 0x5f, 0x42, 0x8a, 0x1e, 0x1b, + 0x92, 0xf4, 0xb9, 0x56, 0x9c, 0xe3, 0x06, 0xfa, 0x29, 0x80, 0x6a, 0xdb, 0xa6, 0x76, 0x3e, 0x22, + 0xde, 0x21, 0x3e, 0xf9, 0x29, 0xef, 0xd8, 0xd5, 0x1c, 0xbe, 0x9d, 0xdb, 0xfc, 0xfc, 0xad, 0x79, + 0xa2, 0xbe, 0x33, 0xe8, 0x53, 0x28, 0x1d, 0x42, 0x29, 0x28, 0xeb, 0x64, 0xd3, 0x6c, 0x0c, 0xc1, + 0x6c, 0x9a, 0x65, 0xe7, 0x3c, 0x9b, 0x76, 0x73, 0xf1, 0x04, 0xab, 0xfe, 0xd2, 0x86, 0xf4, 0xa3, + 0x00, 0x05, 0xbf, 0xd7, 0x5b, 0x38, 0xe1, 0xe5, 0x17, 0x80, 0xc4, 0xe4, 0x05, 0x20, 0xe9, 0x4b, + 0x81, 0x6f, 0x42, 0x96, 0xa4, 0xc0, 0x23, 0x0b, 0x77, 0x78, 0x4d, 0x3c, 0xd3, 0x55, 0xad, 0x53, + 0x0b, 0x77, 0x7c, 0xbe, 0x29, 0xf3, 0x66, 0xbe, 0x29, 0x98, 0x48, 0x67, 0x43, 0x89, 0xf4, 0x7e, + 0x32, 0x9b, 0x12, 0xd3, 0xb2, 0x2f, 0x13, 0x97, 0xfe, 0x52, 0x80, 0xac, 0x3b, 0xdf, 0x60, 0x31, + 0x38, 0x80, 0xcb, 0xb2, 0xe5, 0x62, 0xa5, 0x60, 0x7e, 0x75, 0x61, 0xa5, 0xf1, 0x84, 0x5b, 0x1a, + 0xff, 0x9d, 0x9b, 0x0c, 0x46, 0x21, 0x8f, 0xfe, 0xc5, 0x75, 0xc0, 0x66, 0x9e, 0xfb, 0xfe, 0x2d, + 0x1f, 0x07, 0xc9, 0x58, 0xd0, 0x1f, 0x40, 0x5a, 0x6d, 0xbb, 0x78, 0x6b, 0x69, 0x0a, 0x10, 0xe9, + 0xb0, 0x6e, 0xb5, 0xc6, 0x35, 0xca, 0x29, 0x73, 0x09, 0x3e, 0xaa, 0xb8, 0x33, 0x2a, 0xe9, 0x80, + 0xe8, 0x65, 0x3c, 0xc1, 0x93, 0x5e, 0x02, 0x38, 0x3d, 0x7c, 0x76, 0xb4, 0xbb, 0xf7, 0x64, 0xaf, + 0xb1, 0xcb, 0xb3, 0xbd, 0xdd, 0xdd, 0xc6, 0xae, 0x18, 0x27, 0x7c, 0x72, 0xe3, 0xd9, 0xd1, 0x59, + 0x63, 0x57, 0x4c, 0x90, 0xc6, 0x6e, 0xe3, 0xa0, 0xf6, 0x71, 0x63, 0x57, 0x4c, 0x4a, 0x35, 0xc8, + 0xb9, 0x41, 0x87, 0xbe, 0x21, 0xd0, 0x5f, 0x62, 0x93, 0xaf, 0x16, 0x6b, 0xa0, 0x75, 0xc8, 0x4f, + 0x16, 0x0c, 0xc8, 0xe5, 0x8d, 0xd5, 0x09, 0x48, 0x18, 0x28, 0xbb, 0x3a, 0x78, 0x6c, 0xfa, 0x1d, + 0x64, 0x8c, 0xd1, 0xb9, 0xe2, 0xd8, 0x6e, 0x08, 0x66, 0x77, 0xee, 0x76, 0xa3, 0xf3, 0xbe, 0xd6, + 0x7e, 0x8a, 0xaf, 0x78, 0x90, 0x4b, 0x1b, 0xa3, 0xf3, 0xa7, 0xcc, 0xc4, 0xd9, 0x30, 0xe2, 0x33, + 0x86, 0x91, 0x08, 0x0d, 0x03, 0xdd, 0x83, 0xc2, 0x50, 0xef, 0x60, 0x45, 0xed, 0x74, 0x4c, 0x6c, + 0xb1, 0xd8, 0x9d, 0xe3, 0x9a, 0xf3, 0xa4, 0xa7, 0xc6, 0x3a, 0xa4, 0xef, 0x05, 0x40, 0x93, 0x81, + 0x16, 0x9d, 0xc0, 0x8a, 0x17, 0xab, 0x9d, 0x04, 0x80, 0x45, 0x82, 0xcd, 0xe8, 0x40, 0x1d, 0xc0, + 0x17, 0xc4, 0xcb, 0x20, 0x99, 0x64, 0x7d, 0x6b, 0x9e, 0xab, 0x32, 0xe8, 0x7c, 0xe9, 0xa2, 0xc4, + 0x17, 0x5c, 0x94, 0x98, 0x8c, 0x5c, 0x79, 0xb7, 0x27, 0xec, 0x4a, 0x13, 0x13, 0x75, 0x27, 0x03, + 0x2a, 0xad, 0x09, 0x31, 0x3e, 0xcf, 0xa8, 0x21, 0x09, 0x6f, 0x32, 0x24, 0xe9, 0x11, 0x88, 0x1f, + 0xb9, 0xdf, 0xf7, 0xf2, 0x47, 0xff, 0x30, 0x85, 0x89, 0x61, 0x5e, 0x42, 0x96, 0x78, 0x5f, 0x1a, + 0x34, 0xfe, 0x08, 0x72, 0xee, 0xea, 0xb9, 0xcf, 0x90, 0x22, 0x97, 0x9d, 0x8f, 0xc4, 0x13, 0x41, + 0x0f, 0x60, 0x85, 0xc4, 0x0d, 0xa7, 0xfa, 0xeb, 0xbd, 0x73, 0xc8, 0xca, 0x65, 0xd6, 0x71, 0xe0, + 0xc0, 0x5a, 0x24, 0x46, 0x8b, 0x2c, 0x96, 0xe3, 0xce, 0xff, 0xc5, 0x00, 0xc8, 0x9d, 0x2f, 0x04, + 0x94, 0xb2, 0x3d, 0x2c, 0x06, 0x92, 0x09, 0xe9, 0xcf, 0xe3, 0x90, 0xf7, 0x55, 0xa3, 0xd0, 0xef, + 0x07, 0x12, 0xab, 0xcd, 0x59, 0x95, 0x2b, 0x5f, 0x56, 0x15, 0x98, 0x58, 0x7c, 0xf9, 0x89, 0x45, + 0xd5, 0x01, 0x9d, 0xa2, 0x74, 0x72, 0xe9, 0xa2, 0xf4, 0xdb, 0x80, 0x6c, 0xdd, 0x56, 0xfb, 0x24, + 0x78, 0x6b, 0xc3, 0xae, 0xc2, 0x4e, 0x3b, 0x2b, 0x84, 0x8b, 0xb4, 0xe7, 0x8c, 0x76, 0x1c, 0x13, + 0xba, 0xd4, 0x87, 0xac, 0x0b, 0x4c, 0x2c, 0xff, 0xba, 0x67, 0x5a, 0xf1, 0xbd, 0x0a, 0xd9, 0x01, + 0xb6, 0x55, 0x1a, 0xf6, 0x18, 0x50, 0xe5, 0xb6, 0x1f, 0x7c, 0x00, 0x79, 0xdf, 0x93, 0x27, 0x12, + 0x09, 0x0f, 0x1b, 0xcf, 0xc5, 0x58, 0x35, 0xf3, 0xd5, 0x37, 0x9b, 0x89, 0x43, 0xfc, 0x92, 0x7c, + 0x4a, 0x6e, 0xd4, 0x9b, 0x8d, 0xfa, 0x53, 0x51, 0xa8, 0xe6, 0xbf, 0xfa, 0x66, 0x33, 0x23, 0x63, + 0x5a, 0xb8, 0x79, 0xf0, 0x14, 0xca, 0xa1, 0x1d, 0x08, 0x3a, 0x68, 0x04, 0xa5, 0xdd, 0xd3, 0xe3, + 0x83, 0xbd, 0x7a, 0xad, 0xd5, 0x50, 0xce, 0x8e, 0x5a, 0x0d, 0x51, 0x40, 0x37, 0x60, 0xf5, 0x60, + 0xef, 0x8f, 0x9b, 0x2d, 0xa5, 0x7e, 0xb0, 0xd7, 0x38, 0x6c, 0x29, 0xb5, 0x56, 0xab, 0x56, 0x7f, + 0x2a, 0xc6, 0x1f, 0xfe, 0x73, 0x1e, 0xca, 0xb5, 0x9d, 0xfa, 0x5e, 0xcd, 0x30, 0xfa, 0x5a, 0x5b, + 0xa5, 0xee, 0xbe, 0x0e, 0x49, 0x8a, 0x3a, 0xcf, 0x7c, 0x55, 0x5d, 0x9d, 0x5d, 0x8d, 0x43, 0x4f, + 0x20, 0x45, 0x01, 0x69, 0x34, 0xfb, 0x99, 0x75, 0x75, 0x4e, 0x79, 0x8e, 0x0c, 0x86, 0x9e, 0x9b, + 0x99, 0xef, 0xae, 0xab, 0xb3, 0xab, 0x75, 0xe8, 0x00, 0x32, 0x0e, 0x18, 0x37, 0xef, 0x31, 0x74, + 0x75, 0x6e, 0x09, 0x8d, 0x4c, 0x8d, 0x81, 0x9a, 0xb3, 0x9f, 0x64, 0x57, 0xe7, 0xd4, 0xf1, 0x90, + 0x0c, 0x39, 0x0f, 0xe6, 0x9e, 0xff, 0x3a, 0xbc, 0xba, 0x40, 0x5d, 0x11, 0x7d, 0x06, 0xc5, 0x20, + 0x6c, 0xb7, 0xd8, 0xc3, 0xed, 0xea, 0x82, 0x35, 0x3f, 0xa2, 0x3f, 0x88, 0xe1, 0x2d, 0xf6, 0x90, + 0xbb, 0xba, 0x60, 0x09, 0x10, 0x7d, 0x0e, 0x2b, 0x93, 0x18, 0xdb, 0xe2, 0xef, 0xba, 0xab, 0x4b, + 0x14, 0x05, 0xd1, 0x00, 0xd0, 0x14, 0x6c, 0x6e, 0x89, 0x67, 0xde, 0xd5, 0x65, 0x6a, 0x84, 0xa8, + 0x0b, 0xe2, 0xc4, 0xbb, 0xbb, 0x85, 0x9f, 0x7d, 0x57, 0x17, 0x2f, 0x18, 0xa2, 0x0e, 0x94, 0xc3, + 0x20, 0xd8, 0xa2, 0xcf, 0xc0, 0xab, 0x0b, 0x97, 0x0f, 0xd9, 0x57, 0x82, 0x60, 0xcc, 0xa2, 0xcf, + 0xc2, 0xab, 0x0b, 0x57, 0x13, 0xd1, 0x29, 0x80, 0xef, 0x12, 0xbd, 0xc0, 0x33, 0xf1, 0xea, 0x22, + 0x75, 0x45, 0x64, 0xc0, 0xea, 0xb4, 0x5b, 0xf3, 0x32, 0xaf, 0xc6, 0xab, 0x4b, 0x95, 0x1b, 0xc9, + 0xc1, 0x09, 0x5e, 0x80, 0x17, 0x7b, 0x45, 0x5e, 0x5d, 0xb0, 0xee, 0xb8, 0xb3, 0xf3, 0xed, 0xeb, + 0x75, 0xe1, 0xbb, 0xd7, 0xeb, 0xc2, 0xf7, 0xaf, 0xd7, 0x85, 0xaf, 0x7f, 0x58, 0x8f, 0x7d, 0xf7, + 0xc3, 0x7a, 0xec, 0xdf, 0x7f, 0x58, 0x8f, 0xfd, 0xe9, 0xfd, 0xae, 0x66, 0xf7, 0x46, 0xe7, 0x5b, + 0x6d, 0x7d, 0x40, 0xff, 0x1d, 0x64, 0xa8, 0x57, 0xdb, 0x4c, 0x27, 0x69, 0xf9, 0xfe, 0x83, 0x74, + 0x9e, 0xa6, 0x41, 0xf5, 0xd1, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x18, 0x19, 0x24, 0x60, 0xa3, + 0x34, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4630,6 +4763,7 @@ type ABCIApplicationClient interface { OfferSnapshot(ctx context.Context, in *RequestOfferSnapshot, opts ...grpc.CallOption) (*ResponseOfferSnapshot, error) LoadSnapshotChunk(ctx context.Context, in *RequestLoadSnapshotChunk, opts ...grpc.CallOption) (*ResponseLoadSnapshotChunk, error) ApplySnapshotChunk(ctx context.Context, in *RequestApplySnapshotChunk, opts ...grpc.CallOption) (*ResponseApplySnapshotChunk, error) + FinalizeSnapshot(ctx context.Context, in *RequestFinalizeSnapshot, opts ...grpc.CallOption) (*ResponseFinalizeSnapshot, error) PrepareProposal(ctx context.Context, in *RequestPrepareProposal, opts ...grpc.CallOption) (*ResponsePrepareProposal, error) ProcessProposal(ctx context.Context, in *RequestProcessProposal, opts ...grpc.CallOption) (*ResponseProcessProposal, error) ExtendVote(ctx context.Context, in *RequestExtendVote, opts ...grpc.CallOption) (*ResponseExtendVote, error) @@ -4735,6 +4869,15 @@ func (c *aBCIApplicationClient) ApplySnapshotChunk(ctx context.Context, in *Requ return out, nil } +func (c *aBCIApplicationClient) FinalizeSnapshot(ctx context.Context, in *RequestFinalizeSnapshot, opts ...grpc.CallOption) (*ResponseFinalizeSnapshot, error) { + out := new(ResponseFinalizeSnapshot) + err := c.cc.Invoke(ctx, "/tendermint.abci.ABCIApplication/FinalizeSnapshot", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *aBCIApplicationClient) PrepareProposal(ctx context.Context, in *RequestPrepareProposal, opts ...grpc.CallOption) (*ResponsePrepareProposal, error) { out := new(ResponsePrepareProposal) err := c.cc.Invoke(ctx, "/tendermint.abci.ABCIApplication/PrepareProposal", in, out, opts...) @@ -4793,6 +4936,7 @@ type ABCIApplicationServer interface { OfferSnapshot(context.Context, *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) LoadSnapshotChunk(context.Context, *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) ApplySnapshotChunk(context.Context, *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) + FinalizeSnapshot(context.Context, *RequestFinalizeSnapshot) (*ResponseFinalizeSnapshot, error) PrepareProposal(context.Context, *RequestPrepareProposal) (*ResponsePrepareProposal, error) ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error) ExtendVote(context.Context, *RequestExtendVote) (*ResponseExtendVote, error) @@ -4834,6 +4978,9 @@ func (*UnimplementedABCIApplicationServer) LoadSnapshotChunk(ctx context.Context func (*UnimplementedABCIApplicationServer) ApplySnapshotChunk(ctx context.Context, req *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) { return nil, status.Errorf(codes.Unimplemented, "method ApplySnapshotChunk not implemented") } +func (*UnimplementedABCIApplicationServer) FinalizeSnapshot(ctx context.Context, req *RequestFinalizeSnapshot) (*ResponseFinalizeSnapshot, error) { + return nil, status.Errorf(codes.Unimplemented, "method FinalizeSnapshot not implemented") +} func (*UnimplementedABCIApplicationServer) PrepareProposal(ctx context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) { return nil, status.Errorf(codes.Unimplemented, "method PrepareProposal not implemented") } @@ -5034,6 +5181,24 @@ func _ABCIApplication_ApplySnapshotChunk_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _ABCIApplication_FinalizeSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RequestFinalizeSnapshot) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABCIApplicationServer).FinalizeSnapshot(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/tendermint.abci.ABCIApplication/FinalizeSnapshot", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABCIApplicationServer).FinalizeSnapshot(ctx, req.(*RequestFinalizeSnapshot)) + } + return interceptor(ctx, in, info, handler) +} + func _ABCIApplication_PrepareProposal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RequestPrepareProposal) if err := dec(in); err != nil { @@ -5168,6 +5333,10 @@ var _ABCIApplication_serviceDesc = grpc.ServiceDesc{ MethodName: "ApplySnapshotChunk", Handler: _ABCIApplication_ApplySnapshotChunk_Handler, }, + { + MethodName: "FinalizeSnapshot", + Handler: _ABCIApplication_FinalizeSnapshot_Handler, + }, { MethodName: "PrepareProposal", Handler: _ABCIApplication_PrepareProposal_Handler, @@ -5548,6 +5717,29 @@ func (m *Request_FinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { } return len(dAtA) - i, nil } +func (m *Request_FinalizeSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Request_FinalizeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.FinalizeSnapshot != nil { + { + size, err := m.FinalizeSnapshot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0xa2 + } + return len(dAtA) - i, nil +} func (m *RequestEcho) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5716,12 +5908,12 @@ func (m *RequestInitChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - n18, err18 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err18 != nil { - return 0, err18 + n19, err19 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err19 != nil { + return 0, err19 } - i -= n18 - i = encodeVarintTypes(dAtA, i, uint64(n18)) + i -= n19 + i = encodeVarintTypes(dAtA, i, uint64(n19)) i-- dAtA[i] = 0xa return len(dAtA) - i, nil @@ -5963,6 +6155,53 @@ func (m *RequestApplySnapshotChunk) MarshalToSizedBuffer(dAtA []byte) (int, erro return len(dAtA) - i, nil } +func (m *RequestFinalizeSnapshot) 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 *RequestFinalizeSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *RequestFinalizeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.GenesisBlock != nil { + { + size, err := m.GenesisBlock.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.SnapshotBlock != nil { + { + size, err := m.SnapshotBlock.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *RequestPrepareProposal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -6031,12 +6270,12 @@ func (m *RequestPrepareProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x3a } - n21, err21 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err21 != nil { - return 0, err21 + n24, err24 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err24 != nil { + return 0, err24 } - i -= n21 - i = encodeVarintTypes(dAtA, i, uint64(n21)) + i -= n24 + i = encodeVarintTypes(dAtA, i, uint64(n24)) i-- dAtA[i] = 0x32 if m.Height != 0 { @@ -6160,12 +6399,12 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x42 } - n25, err25 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err25 != nil { - return 0, err25 + n28, err28 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err28 != nil { + return 0, err28 } - i -= n25 - i = encodeVarintTypes(dAtA, i, uint64(n25)) + i -= n28 + i = encodeVarintTypes(dAtA, i, uint64(n28)) i-- dAtA[i] = 0x3a if m.Round != 0 { @@ -6780,6 +7019,29 @@ func (m *Response_FinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) } return len(dAtA) - i, nil } +func (m *Response_FinalizeSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Response_FinalizeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + if m.FinalizeSnapshot != nil { + { + size, err := m.FinalizeSnapshot.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTypes(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x8a + } + return len(dAtA) - i, nil +} func (m *ResponseException) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7003,12 +7265,12 @@ func (m *ResponseInitChain_GenesisTime) MarshalTo(dAtA []byte) (int, error) { func (m *ResponseInitChain_GenesisTime) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) if m.GenesisTime != nil { - n49, err49 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.GenesisTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.GenesisTime):]) - if err49 != nil { - return 0, err49 + n53, err53 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.GenesisTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.GenesisTime):]) + if err53 != nil { + return 0, err53 } - i -= n49 - i = encodeVarintTypes(dAtA, i, uint64(n49)) + i -= n53 + i = encodeVarintTypes(dAtA, i, uint64(n53)) i-- dAtA[i] = 0x32 } @@ -7315,6 +7577,29 @@ func (m *ResponseApplySnapshotChunk) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *ResponseFinalizeSnapshot) 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 *ResponseFinalizeSnapshot) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ResponseFinalizeSnapshot) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *ResponsePrepareProposal) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -8285,12 +8570,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n62, err62 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) - if err62 != nil { - return 0, err62 + n66, err66 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Time):]) + if err66 != nil { + return 0, err66 } - i -= n62 - i = encodeVarintTypes(dAtA, i, uint64(n62)) + i -= n66 + i = encodeVarintTypes(dAtA, i, uint64(n66)) i-- dAtA[i] = 0x22 if m.Height != 0 { @@ -8566,6 +8851,18 @@ func (m *Request_FinalizeBlock) Size() (n int) { } return n } +func (m *Request_FinalizeSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FinalizeSnapshot != nil { + l = m.FinalizeSnapshot.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} func (m *RequestEcho) Size() (n int) { if m == nil { return 0 @@ -8749,6 +9046,23 @@ func (m *RequestApplySnapshotChunk) Size() (n int) { return n } +func (m *RequestFinalizeSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.SnapshotBlock != nil { + l = m.SnapshotBlock.Size() + n += 1 + l + sovTypes(uint64(l)) + } + if m.GenesisBlock != nil { + l = m.GenesisBlock.Size() + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func (m *RequestPrepareProposal) Size() (n int) { if m == nil { return 0 @@ -9153,6 +9467,18 @@ func (m *Response_FinalizeBlock) Size() (n int) { } return n } +func (m *Response_FinalizeSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.FinalizeSnapshot != nil { + l = m.FinalizeSnapshot.Size() + n += 2 + l + sovTypes(uint64(l)) + } + return n +} func (m *ResponseException) Size() (n int) { if m == nil { return 0 @@ -9402,6 +9728,15 @@ func (m *ResponseApplySnapshotChunk) Size() (n int) { return n } +func (m *ResponseFinalizeSnapshot) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *ResponsePrepareProposal) Size() (n int) { if m == nil { return 0 @@ -10409,6 +10744,41 @@ func (m *Request) Unmarshal(dAtA []byte) error { } m.Value = &Request_FinalizeBlock{v} iNdEx = postIndex + case 20: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalizeSnapshot", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &RequestFinalizeSnapshot{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Request_FinalizeSnapshot{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -11673,6 +12043,128 @@ func (m *RequestApplySnapshotChunk) Unmarshal(dAtA []byte) error { } return nil } +func (m *RequestFinalizeSnapshot) 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 ErrIntOverflowTypes + } + 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: RequestFinalizeSnapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RequestFinalizeSnapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SnapshotBlock", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SnapshotBlock == nil { + m.SnapshotBlock = &types1.LightBlock{} + } + if err := m.SnapshotBlock.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field GenesisBlock", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.GenesisBlock == nil { + m.GenesisBlock = &types1.LightBlock{} + } + if err := m.GenesisBlock.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *RequestPrepareProposal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -13716,6 +14208,41 @@ func (m *Response) Unmarshal(dAtA []byte) error { } m.Value = &Response_FinalizeBlock{v} iNdEx = postIndex + case 17: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FinalizeSnapshot", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + v := &ResponseFinalizeSnapshot{} + if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + m.Value = &Response_FinalizeSnapshot{v} + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15326,6 +15853,56 @@ func (m *ResponseApplySnapshotChunk) Unmarshal(dAtA []byte) error { } return nil } +func (m *ResponseFinalizeSnapshot) 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 ErrIntOverflowTypes + } + 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: ResponseFinalizeSnapshot: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ResponseFinalizeSnapshot: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ResponsePrepareProposal) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/config/config.go b/config/config.go index ac43d0441..a6e4903fa 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,6 @@ package config import ( - "encoding/hex" "encoding/json" "errors" "fmt" @@ -1001,18 +1000,16 @@ type StateSyncConfig struct { // with net.Dial, for example: "host.example.com:2125". RPCServers []string `mapstructure:"rpc-servers"` - // The hash and height of a trusted block. Must be within the trust-period. - TrustHeight int64 `mapstructure:"trust-height"` - TrustHash string `mapstructure:"trust-hash"` - - // The trust period should be set so that Tendermint can detect and gossip - // misbehavior before it is considered expired. For chains based on the Cosmos SDK, - // one day less than the unbonding period should suffice. - TrustPeriod time.Duration `mapstructure:"trust-period"` - // Time to spend discovering snapshots before initiating a restore. DiscoveryTime time.Duration `mapstructure:"discovery-time"` + // Number of times to retry state sync. When retries are exhausted, the node will + // fall back to the regular block sync. Set to 0 to disable retries. Default is 3. + // + // Note that in pessimistic case, it will take at least `discovery-time * retries` before + // falling back to block sync. + Retries int `mapstructure:"retries"` + // Temporary directory for state sync snapshot chunks, defaults to os.TempDir(). // The synchronizer will create a new, randomly named directory within this directory // and remove it when the sync is complete. @@ -1026,22 +1023,13 @@ type StateSyncConfig struct { Fetchers int `mapstructure:"fetchers"` } -func (cfg *StateSyncConfig) TrustHashBytes() []byte { - // validated in ValidateBasic, so we can safely panic here - bytes, err := hex.DecodeString(cfg.TrustHash) - if err != nil { - panic(err) - } - return bytes -} - // DefaultStateSyncConfig returns a default configuration for the state sync service func DefaultStateSyncConfig() *StateSyncConfig { return &StateSyncConfig{ - TrustPeriod: 168 * time.Hour, DiscoveryTime: 15 * time.Second, ChunkRequestTimeout: 15 * time.Second, Fetchers: 4, + Retries: 3, } } @@ -1074,21 +1062,8 @@ func (cfg *StateSyncConfig) ValidateBasic() error { return errors.New("discovery time must be 0s or greater than five seconds") } - if cfg.TrustPeriod <= 0 { - return errors.New("trusted-period is required") - } - - if cfg.TrustHeight <= 0 { - return errors.New("trusted-height is required") - } - - if len(cfg.TrustHash) == 0 { - return errors.New("trusted-hash is required") - } - - _, err := hex.DecodeString(cfg.TrustHash) - if err != nil { - return fmt.Errorf("invalid trusted-hash: %w", err) + if cfg.Retries < 0 { + return errors.New("retries must be greater than or equal to zero") } if cfg.ChunkRequestTimeout < 5*time.Second { diff --git a/config/toml.go b/config/toml.go index b6be127b2..dddf0ac43 100644 --- a/config/toml.go +++ b/config/toml.go @@ -495,18 +495,15 @@ use-p2p = {{ .StateSync.UseP2P }} # for example: "host.example.com:2125" rpc-servers = "{{ StringsJoin .StateSync.RPCServers "," }}" -# The hash and height of a trusted block. Must be within the trust-period. -trust-height = {{ .StateSync.TrustHeight }} -trust-hash = "{{ .StateSync.TrustHash }}" - -# The trust period should be set so that Tendermint can detect and gossip misbehavior before -# it is considered expired. For chains based on the Cosmos SDK, one day less than the unbonding -# period should suffice. -trust-period = "{{ .StateSync.TrustPeriod }}" - # Time to spend discovering snapshots before initiating a restore. discovery-time = "{{ .StateSync.DiscoveryTime }}" +# Number of times to retry state sync. When retries are exhausted, the node will +# fall back to the regular block sync. Set to 0 to disable retries. Default is 3. +# Note that in pessimistic case, it will take at least (discovery-time * retries) before +# falling back to block sync. +retries = {{ .StateSync.Retries }} + # Temporary directory for state sync snapshot chunks, defaults to os.TempDir(). # The synchronizer will create a new, randomly named directory within this directory # and remove it when the sync is complete. diff --git a/go.mod b/go.mod index 96e5d7e88..86661cada 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/containerd/continuity v0.4.5 // indirect github.com/dashpay/bls-signatures/go-bindings v0.0.0-20230207105415-06df92693ac8 - github.com/dashpay/dashd-go v0.25.0 - github.com/dashpay/dashd-go/btcec/v2 v2.1.0 // indirect + github.com/dashpay/dashd-go v0.26.1 + github.com/dashpay/dashd-go/btcec/v2 v2.2.0 // indirect github.com/fortytw2/leaktest v1.3.0 github.com/fxamacker/cbor/v2 v2.4.0 github.com/go-kit/kit v0.13.0 @@ -81,7 +81,7 @@ require ( github.com/alingse/nilnesserr v0.1.1 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bombsimon/wsl/v4 v4.5.0 // indirect - github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/btcsuite/btclog v1.0.0 // indirect github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect @@ -109,7 +109,7 @@ require ( github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect - github.com/dashpay/dashd-go/btcutil v1.2.0 // indirect + github.com/dashpay/dashd-go/btcutil v1.3.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/badger/v4 v4.3.1 // indirect github.com/didip/tollbooth/v8 v8.0.1 // indirect diff --git a/go.sum b/go.sum index d5a88fcf3..541e41cf1 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,9 @@ github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btclog v1.0.0 h1:sEkpKJMmfGiyZjADwEIgB1NSwMyfdD1FB8v6+w1T0Ns= +github.com/btcsuite/btclog v1.0.0/go.mod h1:w7xnGOhwT3lmrS4H3b/D1XAXxvh+tbhUm8xeHN2y3TQ= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= @@ -221,12 +222,12 @@ github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/dashpay/bls-signatures/go-bindings v0.0.0-20230207105415-06df92693ac8 h1:v4K3CiDoFY1gjcWL/scRcwzyjBwh8TVG3ek8cWolK1g= github.com/dashpay/bls-signatures/go-bindings v0.0.0-20230207105415-06df92693ac8/go.mod h1:auvGS60NBZ+a21aCCQh366PdsjDvHinsCvl28VrYPu4= -github.com/dashpay/dashd-go v0.25.0 h1:tswVRmM2fLHC/JhpuAZ5Oa0TpOO6L+tqiE+QLTCvIQc= -github.com/dashpay/dashd-go v0.25.0/go.mod h1:4yuk/laGME2RnQRTdqTbw87PhT+42hE1anLCnpkgls8= -github.com/dashpay/dashd-go/btcec/v2 v2.1.0 h1:fXwlLf5H+TtgHxjGMU74NesKzk6NisjKMPF04pBcygk= -github.com/dashpay/dashd-go/btcec/v2 v2.1.0/go.mod h1:1i8XtxdOmvK6mYEUCneVXTzFbrCUw3wq1u91j8gvsns= -github.com/dashpay/dashd-go/btcutil v1.2.0 h1:YMq7L0V0au5bbphIhpsBBc+nfOZqU+gJ4pkgRZB7Eiw= -github.com/dashpay/dashd-go/btcutil v1.2.0/go.mod h1:7UHoqUh3LY3OI4mEcogx0CnL3rtzDQyoqvsOCZZtvzE= +github.com/dashpay/dashd-go v0.26.1 h1:/ZFgtPw1fPHpvoJgKfXo/v63ZXddjJm8KrHRpxcSpy0= +github.com/dashpay/dashd-go v0.26.1/go.mod h1:7KKS2jSPkC1pTz9WLXpiXZ96wT5bUqKTRuk35AyRQ74= +github.com/dashpay/dashd-go/btcec/v2 v2.2.0 h1:tk54BC++OvOUu0vcPoG8+45dGoJXKsmupYAawBO/1Vk= +github.com/dashpay/dashd-go/btcec/v2 v2.2.0/go.mod h1:uOmCM/hVoJ1x6w+3SX+zQv+2LdrK3aO59RV41jNvTF4= +github.com/dashpay/dashd-go/btcutil v1.3.0 h1:yDX8tz7C/KhFHbGlRXBpNN+zlkmAgwkICD9DlAv/Vsc= +github.com/dashpay/dashd-go/btcutil v1.3.0/go.mod h1:sMWZ0iR8a/wmIA6b5+ccjOGUfq+iZvi5t6ECaLCW+kw= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/internal/consensus/state_data.go b/internal/consensus/state_data.go index 0686b0f32..66b040571 100644 --- a/internal/consensus/state_data.go +++ b/internal/consensus/state_data.go @@ -119,7 +119,7 @@ func (s *StateDataStore) Subscribe(evsw *eventemitter.EventEmitter) { }) } -// StateData is a copy of the current RoundState nad state.State stored in the store +// StateData is a copy of the current RoundState and state.State stored in the store // Along with data, StateData provides some methods to check or update data inside type StateData struct { config *config.ConsensusConfig diff --git a/internal/proxy/client.go b/internal/proxy/client.go index 9421d6b8e..a4fa986cf 100644 --- a/internal/proxy/client.go +++ b/internal/proxy/client.go @@ -212,6 +212,11 @@ func (app *proxyClient) ApplySnapshotChunk(ctx context.Context, req *types.Reque return app.client.ApplySnapshotChunk(ctx, req) } +func (app *proxyClient) FinalizeSnapshot(ctx context.Context, req *types.RequestFinalizeSnapshot) (r *types.ResponseFinalizeSnapshot, err error) { + defer addTimeSample(app.metrics.MethodTiming.With("method", "finalize_snapshot", "type", "sync"))(&err) + return app.client.FinalizeSnapshot(ctx, req) +} + // addTimeSample returns a function that, when called, adds an observation to m. // The observation added to m is the number of seconds ellapsed since addTimeSample // was initially called. addTimeSample is meant to be called in a defer to calculate diff --git a/internal/statesync/chunks.go b/internal/statesync/chunks.go index bd9d23e88..57edbf5ee 100644 --- a/internal/statesync/chunks.go +++ b/internal/statesync/chunks.go @@ -133,9 +133,16 @@ func (q *chunkQueue) dequeue() (bytes.HexBytes, error) { // Add adds a chunk to the queue. It ignores chunks that already exist, returning false. func (q *chunkQueue) Add(chunk *chunk) (bool, error) { - if chunk == nil || chunk.Chunk == nil { + if chunk == nil { return false, errChunkNil } + + // empty chunk content is allowed, but we ensure it's not nil + data := chunk.Chunk + if data == nil { + data = []byte{} + } + q.mtx.Lock() defer q.mtx.Unlock() if q.snapshot == nil { @@ -154,7 +161,7 @@ func (q *chunkQueue) Add(chunk *chunk) (bool, error) { return false, err } item.file = filepath.Join(q.dir, chunkIDKey) - err = item.write(chunk.Chunk) + err = item.write(data) if err != nil { return false, err } diff --git a/internal/statesync/mocks/stateprovider.go b/internal/statesync/mocks/stateprovider.go index 8ac2b1544..a4189eb08 100644 --- a/internal/statesync/mocks/stateprovider.go +++ b/internal/statesync/mocks/stateprovider.go @@ -145,6 +145,65 @@ func (_c *StateProvider_Commit_Call) RunAndReturn(run func(context.Context, uint return _c } +// LightBlock provides a mock function with given fields: ctx, height +func (_m *StateProvider) LightBlock(ctx context.Context, height uint64) (*types.LightBlock, error) { + ret := _m.Called(ctx, height) + + if len(ret) == 0 { + panic("no return value specified for LightBlock") + } + + var r0 *types.LightBlock + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, uint64) (*types.LightBlock, error)); ok { + return rf(ctx, height) + } + if rf, ok := ret.Get(0).(func(context.Context, uint64) *types.LightBlock); ok { + r0 = rf(ctx, height) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.LightBlock) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, uint64) error); ok { + r1 = rf(ctx, height) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// StateProvider_LightBlock_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'LightBlock' +type StateProvider_LightBlock_Call struct { + *mock.Call +} + +// LightBlock is a helper method to define mock.On call +// - ctx context.Context +// - height uint64 +func (_e *StateProvider_Expecter) LightBlock(ctx interface{}, height interface{}) *StateProvider_LightBlock_Call { + return &StateProvider_LightBlock_Call{Call: _e.mock.On("LightBlock", ctx, height)} +} + +func (_c *StateProvider_LightBlock_Call) Run(run func(ctx context.Context, height uint64)) *StateProvider_LightBlock_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(uint64)) + }) + return _c +} + +func (_c *StateProvider_LightBlock_Call) Return(_a0 *types.LightBlock, _a1 error) *StateProvider_LightBlock_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *StateProvider_LightBlock_Call) RunAndReturn(run func(context.Context, uint64) (*types.LightBlock, error)) *StateProvider_LightBlock_Call { + _c.Call.Return(run) + return _c +} + // State provides a mock function with given fields: ctx, height func (_m *StateProvider) State(ctx context.Context, height uint64) (state.State, error) { ret := _m.Called(ctx, height) diff --git a/internal/statesync/reactor.go b/internal/statesync/reactor.go index e31088ff4..e69c18d5d 100644 --- a/internal/statesync/reactor.go +++ b/internal/statesync/reactor.go @@ -22,6 +22,7 @@ import ( sm "github.com/dashpay/tenderdash/internal/state" "github.com/dashpay/tenderdash/internal/store" "github.com/dashpay/tenderdash/libs/log" + tmmath "github.com/dashpay/tenderdash/libs/math" "github.com/dashpay/tenderdash/libs/service" "github.com/dashpay/tenderdash/light/provider" ssproto "github.com/dashpay/tenderdash/proto/tendermint/statesync" @@ -236,8 +237,7 @@ func (r *Reactor) OnStart(ctx context.Context) error { r.initStateProvider = func(ctx context.Context, chainID string, initialHeight int64) error { spLogger := r.logger.With("module", "stateprovider") - spLogger.Info("initializing state provider", - "trustHeight", r.cfg.TrustHeight, "useP2P", r.cfg.UseP2P) + spLogger.Info("initializing state provider", "useP2P", r.cfg.UseP2P) if r.cfg.UseP2P { if err := r.waitForEnoughPeers(ctx, 2); err != nil { @@ -250,8 +250,8 @@ func (r *Reactor) OnStart(ctx context.Context) error { providers[idx] = NewBlockProvider(p, chainID, r.dispatcher) } - stateProvider, err := NewP2PStateProvider(ctx, chainID, initialHeight, r.cfg.TrustHeight, providers, - paramsCh, r.logger.With("module", "stateprovider"), r.dashCoreClient) + stateProvider, err := NewP2PStateProvider(ctx, chainID, initialHeight, + providers, paramsCh, r.logger.With("module", "stateprovider"), r.dashCoreClient) if err != nil { return fmt.Errorf("failed to initialize P2P state provider: %w", err) } @@ -259,8 +259,7 @@ func (r *Reactor) OnStart(ctx context.Context) error { return nil } - stateProvider, err := NewRPCStateProvider(ctx, chainID, initialHeight, r.cfg.RPCServers, r.cfg.TrustHeight, - spLogger, r.dashCoreClient) + stateProvider, err := NewRPCStateProvider(ctx, chainID, initialHeight, r.cfg.RPCServers, spLogger, r.dashCoreClient) if err != nil { return fmt.Errorf("failed to initialize RPC state provider: %w", err) } @@ -279,8 +278,21 @@ func (r *Reactor) OnStart(ctx context.Context) error { if r.needsStateSync { r.logger.Info("starting state sync") if _, err := r.Sync(ctx); err != nil { - r.logger.Error("state sync failed; shutting down this node", "error", err) - return err + if errors.Is(err, errNoSnapshots) && r.postSyncHook != nil { + r.logger.Warn("no snapshots available; falling back to block sync", "err", err) + + state, err := r.stateStore.Load() + if err != nil { + return fmt.Errorf("failed to load state: %w", err) + } + + if err := r.postSyncHook(ctx, state); err != nil { + return fmt.Errorf("post sync failed: %w", err) + } + } else { + r.logger.Error("state sync failed; shutting down this node", "err", err) + return err + } } } @@ -329,7 +341,7 @@ func (r *Reactor) Sync(ctx context.Context) (sm.State, error) { } r.getSyncer().SetStateProvider(r.stateProvider) - state, commit, err := r.syncer.SyncAny(ctx, r.cfg.DiscoveryTime, r.requestSnaphot) + state, commit, err := r.syncer.SyncAny(ctx, r.cfg.DiscoveryTime, r.cfg.Retries, r.requestSnaphot) if err != nil { return sm.State{}, fmt.Errorf("sync any: %w", err) } @@ -1063,6 +1075,18 @@ func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, e break } + if r.csState == nil { + continue + } + currentHeight := r.csState.GetRoundState().Height + // we only accept snapshots where next block is already finalized, that is we are voting + // for `height + 2` or higher, because we need to be able to fetch light block containing + // commit for `height` from block store (which is stored in block `height+1`) + if tmmath.MustConvertInt64(s.Height) >= currentHeight-2 { + r.logger.Debug("snapshot too new, skipping", "height", s.Height, "state_height", currentHeight) + continue + } + snapshots = append(snapshots, &snapshot{ Height: s.Height, Version: s.Version, @@ -1077,7 +1101,7 @@ func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, e // fetchLightBlock works out whether the node has a light block at a particular // height and if so returns it so it can be gossiped to peers func (r *Reactor) fetchLightBlock(height uint64) (*types.LightBlock, error) { - h := int64(height) + h := tmmath.MustConvertInt64(height) blockMeta := r.blockStore.LoadBlockMeta(h) if blockMeta == nil { diff --git a/internal/statesync/reactor_test.go b/internal/statesync/reactor_test.go index eff91961c..799f017b9 100644 --- a/internal/statesync/reactor_test.go +++ b/internal/statesync/reactor_test.go @@ -236,6 +236,8 @@ func TestReactor_Sync(t *testing.T) { Once(). Return(&abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_COMPLETE_SNAPSHOT}, nil) + rts.conn.On("FinalizeSnapshot", ctx, mock.Anything).Return(&abci.ResponseFinalizeSnapshot{}, nil).Once() + // app query returns valid state app hash rts.conn. On("Info", mock.Anything, &proxy.RequestInfo). @@ -277,8 +279,6 @@ func TestReactor_Sync(t *testing.T) { // update the config to use the p2p provider rts.reactor.cfg.UseP2P = true - rts.reactor.cfg.TrustHeight = 1 - rts.reactor.cfg.TrustHash = fmt.Sprintf("%X", chain[1].Hash()) rts.reactor.cfg.DiscoveryTime = 1 * time.Second // Run state sync @@ -612,8 +612,6 @@ func TestReactor_StateProviderP2P(t *testing.T) { go handleConsensusParamsRequest(ctx, t, rts.paramsOutCh, rts.paramsInCh, closeCh) rts.reactor.cfg.UseP2P = true - rts.reactor.cfg.TrustHeight = 1 - rts.reactor.cfg.TrustHash = fmt.Sprintf("%X", chain[1].Hash()) for _, p := range []types.NodeID{peerA, peerB} { if !rts.reactor.peers.Contains(p) { diff --git a/internal/statesync/stateprovider.go b/internal/statesync/stateprovider.go index b10324acc..742d3be93 100644 --- a/internal/statesync/stateprovider.go +++ b/internal/statesync/stateprovider.go @@ -40,6 +40,8 @@ type StateProvider interface { Commit(ctx context.Context, height uint64) (*types.Commit, error) // State returns a state object at the given height. State(ctx context.Context, height uint64) (sm.State, error) + // LightBlock returns light block at the given height. + LightBlock(ctx context.Context, height uint64) (*types.LightBlock, error) } type stateProviderRPC struct { @@ -56,7 +58,6 @@ func NewRPCStateProvider( chainID string, initialHeight int64, servers []string, - trustHeight int64, logger log.Logger, dashCoreClient dashcore.Client, ) (StateProvider, error) { @@ -77,8 +78,7 @@ func NewRPCStateProvider( // provider used by the light client and use it to fetch consensus parameters. providerRemotes[provider] = server } - - lc, err := light.NewClientAtHeight(ctx, trustHeight, chainID, providers[0], providers[1:], + lc, err := light.NewClient(ctx, chainID, providers[0], providers[1:], lightdb.New(dbm.NewMemDB()), dashCoreClient, light.Logger(logger)) if err != nil { return nil, err @@ -123,6 +123,13 @@ func (s *stateProviderRPC) Commit(ctx context.Context, height uint64) (*types.Co return header.Commit, nil } +// LightBlock implements StateProvider. +func (s *stateProviderRPC) LightBlock(ctx context.Context, height uint64) (*types.LightBlock, error) { + s.Lock() + defer s.Unlock() + return s.verifyLightBlockAtHeight(ctx, height, time.Now()) +} + // State implements StateProvider. func (s *stateProviderRPC) State(ctx context.Context, height uint64) (sm.State, error) { s.Lock() @@ -208,7 +215,6 @@ func NewP2PStateProvider( ctx context.Context, chainID string, initialHeight int64, - trustHeight int64, providers []lightprovider.Provider, paramsSendCh p2p.Channel, logger log.Logger, @@ -218,7 +224,7 @@ func NewP2PStateProvider( return nil, fmt.Errorf("at least 2 peers are required, got %d", len(providers)) } - lc, err := light.NewClientAtHeight(ctx, trustHeight, chainID, providers[0], providers[1:], + lc, err := light.NewClient(ctx, chainID, providers[0], providers[1:], lightdb.New(dbm.NewMemDB()), dashCoreClient, light.Logger(logger)) if err != nil { return nil, err @@ -262,6 +268,13 @@ func (s *stateProviderP2P) Commit(ctx context.Context, height uint64) (*types.Co return header.Commit, nil } +// LightBlock implements StateProvider. +func (s *stateProviderP2P) LightBlock(ctx context.Context, height uint64) (*types.LightBlock, error) { + s.Lock() + defer s.Unlock() + return s.verifyLightBlockAtHeight(ctx, height, time.Now()) +} + // State implements StateProvider. func (s *stateProviderP2P) State(ctx context.Context, height uint64) (sm.State, error) { s.Lock() diff --git a/internal/statesync/syncer.go b/internal/statesync/syncer.go index 24ce2eb0a..ec0c4185b 100644 --- a/internal/statesync/syncer.go +++ b/internal/statesync/syncer.go @@ -16,6 +16,7 @@ import ( sm "github.com/dashpay/tenderdash/internal/state" tmbytes "github.com/dashpay/tenderdash/libs/bytes" "github.com/dashpay/tenderdash/libs/log" + tmmath "github.com/dashpay/tenderdash/libs/math" "github.com/dashpay/tenderdash/light" ssproto "github.com/dashpay/tenderdash/proto/tendermint/statesync" "github.com/dashpay/tenderdash/types" @@ -120,6 +121,8 @@ func (s *syncer) AddSnapshot(peerID types.NodeID, snapshot *snapshot) (bool, err "height", snapshot.Height, "version", snapshot.Version, "hash", snapshot.Hash.ShortString()) + } else { + s.logger.Debug("snapshot not added", "height", snapshot.Height, "hash", snapshot.Hash) } return added, nil } @@ -147,6 +150,7 @@ func (s *syncer) RemovePeer(peerID types.NodeID) { func (s *syncer) SyncAny( ctx context.Context, discoveryTime time.Duration, + retries int, requestSnapshots func() error, ) (sm.State, *types.Commit, error) { if discoveryTime != 0 && discoveryTime < minimumDiscoveryTime { @@ -156,19 +160,6 @@ func (s *syncer) SyncAny( timer := time.NewTimer(discoveryTime) defer timer.Stop() - if discoveryTime > 0 { - if err := requestSnapshots(); err != nil { - return sm.State{}, nil, err - } - s.logger.Info("discovering snapshots", - "interval", discoveryTime) - select { - case <-ctx.Done(): - return sm.State{}, nil, ctx.Err() - case <-timer.C: - } - } - // The app may ask us to retry a snapshot restoration, in which case we need to reuse // the snapshot and chunk queue from the previous loop iteration. var ( @@ -179,6 +170,10 @@ func (s *syncer) SyncAny( ) for { + if retries > 0 && snapshot == nil && iters > retries { + return sm.State{}, nil, errNoSnapshots + } + iters++ // If not nil, we're going to retry restoration of the same snapshot. if snapshot == nil { @@ -189,6 +184,10 @@ func (s *syncer) SyncAny( if discoveryTime == 0 { return sm.State{}, nil, errNoSnapshots } + // we re-request snapshots + if err := requestSnapshots(); err != nil { + return sm.State{}, nil, err + } s.logger.Info("discovering snapshots", "iterations", iters, "interval", discoveryTime) @@ -215,7 +214,7 @@ func (s *syncer) SyncAny( switch { case err == nil: s.metrics.SnapshotHeight.Set(float64(snapshot.Height)) - s.lastSyncedSnapshotHeight = int64(snapshot.Height) + s.lastSyncedSnapshotHeight = tmmath.MustConvertInt64(snapshot.Height) return newState, commit, nil case errors.Is(err, errAbort): @@ -342,7 +341,7 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, queue *chunkQueue if ctx.Err() != nil { return sm.State{}, nil, ctx.Err() } - if err == light.ErrNoWitnesses { + if errors.Is(err, light.ErrNoWitnesses) { return sm.State{}, nil, fmt.Errorf("failed to get tendermint state at height %d. No witnesses remaining", snapshot.Height) } @@ -350,7 +349,8 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, queue *chunkQueue "err", err, "height", snapshot.Height) return sm.State{}, nil, errRejectSnapshot } - commit, err := s.getStateProvider().Commit(pctx, snapshot.Height) + block, err := s.getStateProvider().LightBlock(pctx, snapshot.Height) + if err != nil { // check if the provider context exceeded the 10 second deadline if ctx.Err() != nil { @@ -358,9 +358,9 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, queue *chunkQueue } if err == light.ErrNoWitnesses { return sm.State{}, nil, - fmt.Errorf("failed to get commit at height %d. No witnesses remaining", snapshot.Height) + fmt.Errorf("failed to get light block at height %d. No witnesses remaining", snapshot.Height) } - s.logger.Info("failed to get and verify commit. Dropping snapshot and trying again", + s.logger.Error("failed to get and verify light block. Dropping snapshot and trying again", "err", err, "height", snapshot.Height) return sm.State{}, nil, errRejectSnapshot } @@ -371,6 +371,21 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, queue *chunkQueue return sm.State{}, nil, err } + if state.InitialHeight < 1 { + return sm.State{}, nil, fmt.Errorf("initial genesis height %d is invalid", state.InitialHeight) + } + + genesisHeight := tmmath.MustConvertUint64(state.InitialHeight) + genesisBlock, err := s.getStateProvider().LightBlock(ctx, genesisHeight) + if err != nil { + return sm.State{}, nil, fmt.Errorf("failed to get genesis block at height %d: %w", genesisHeight, err) + } + + // Finalize + if err := s.finalizeSnapshot(ctx, snapshot, genesisBlock, block); err != nil { + return sm.State{}, nil, fmt.Errorf("failed to finalize snapshot: %w", err) + } + // Verify app and app version if err := s.verifyApp(ctx, snapshot, state.Version.Consensus.App); err != nil { return sm.State{}, nil, err @@ -382,7 +397,7 @@ func (s *syncer) Sync(ctx context.Context, snapshot *snapshot, queue *chunkQueue "version", snapshot.Version, "hash", snapshot.Hash) - return state, commit, nil + return state, block.Commit, nil } // offerSnapshot offers a snapshot to the app. It returns various errors depending on the app's @@ -572,6 +587,35 @@ func (s *syncer) requestChunk(ctx context.Context, snapshot *snapshot, chunkID t return s.chunkCh.Send(ctx, msg) } +// / finalizeSnapshot sends light block to ABCI app after state sync is done +func (s *syncer) finalizeSnapshot(ctx context.Context, snapshot *snapshot, genesisBlock *types.LightBlock, snapshotBlock *types.LightBlock) error { + s.logger.Info("Finalizing snapshot restoration", + "snapshot", snapshot.Hash.String(), + "height", snapshot.Height, + "version", snapshot.Version, + "app_hash", snapshot.trustedAppHash, + ) + if genesisBlock == nil || snapshotBlock == nil { + return fmt.Errorf("nil block provided: genesis=%v snapshot=%v", genesisBlock == nil, snapshotBlock == nil) + } + + snapshotBlockProto, err := snapshotBlock.ToProto() + if err != nil { + return fmt.Errorf("failed to convert snapshot block %s to proto: %w", snapshotBlock.String(), err) + } + genesisBlockProto, err := genesisBlock.ToProto() + if err != nil { + return fmt.Errorf("failed to convert genesis block %s to proto: %w", genesisBlock.String(), err) + } + + _, err = s.conn.FinalizeSnapshot(ctx, &abci.RequestFinalizeSnapshot{ + SnapshotBlock: snapshotBlockProto, + GenesisBlock: genesisBlockProto, + }) + + return err +} + // verifyApp verifies the sync, checking the app hash, last block height and app version func (s *syncer) verifyApp(ctx context.Context, snapshot *snapshot, appVersion uint64) error { resp, err := s.conn.Info(ctx, &proxy.RequestInfo) diff --git a/internal/statesync/syncer_test.go b/internal/statesync/syncer_test.go index 00da83c9e..bd7de2caa 100644 --- a/internal/statesync/syncer_test.go +++ b/internal/statesync/syncer_test.go @@ -83,7 +83,7 @@ func (suite *SyncerTestSuite) TestSyncAny() { }, Software: version.TMCoreSemVer, }, - + InitialHeight: 1, LastBlockHeight: 1, LastBlockID: types.BlockID{Hash: []byte("blockhash")}, LastBlockTime: time.Now(), @@ -100,7 +100,19 @@ func (suite *SyncerTestSuite) TestSyncAny() { ConsensusParams: *types.DefaultConsensusParams(), LastHeightConsensusParamsChanged: 1, } - commit := &types.Commit{BlockID: types.BlockID{Hash: []byte("blockhash")}} + commit := &types.Commit{ + Height: 1, + BlockID: types.BlockID{Hash: []byte("blockhash")}, + } + + lightBlock := types.LightBlock{ + SignedHeader: &types.SignedHeader{ + Commit: commit, + Header: &types.Header{ + Height: 1, + }, + }, + } s := &snapshot{Height: 1, Version: 1, Hash: []byte{0}} chunks := []*chunk{ @@ -116,9 +128,8 @@ func (suite *SyncerTestSuite) TestSyncAny() { suite.stateProvider. On("AppHash", mock.Anything, uint64(2)). Return(tmbytes.HexBytes("app_hash_2"), nil) - suite.stateProvider. - On("Commit", mock.Anything, uint64(1)). - Return(commit, nil) + suite.stateProvider.On("LightBlock", mock.Anything, uint64(commit.Height)). + Return(&lightBlock, nil) suite.stateProvider. On("State", mock.Anything, uint64(1)). Return(state, nil) @@ -254,6 +265,9 @@ func (suite *SyncerTestSuite) TestSyncAny() { Once(). Return(asc.resp, nil) } + suite.conn.On("FinalizeSnapshot", mock.Anything, mock.Anything). + Once(). + Return(&abci.ResponseFinalizeSnapshot{}, nil) suite.conn. On("Info", mock.Anything, &proxy.RequestInfo). Once(). @@ -263,7 +277,7 @@ func (suite *SyncerTestSuite) TestSyncAny() { LastBlockAppHash: []byte("app_hash"), }, nil) - newState, lastCommit, err := suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + newState, lastCommit, err := suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().NoError(err) suite.Require().Equal([]int{0: 2, 1: 1, 2: 1, 3: 1}, chunkRequests) @@ -280,7 +294,7 @@ func (suite *SyncerTestSuite) TestSyncAnyNoSnapshots() { ctx, cancel := context.WithCancel(suite.ctx) defer cancel() - _, _, err := suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err := suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().Equal(errNoSnapshots, err) } @@ -306,7 +320,7 @@ func (suite *SyncerTestSuite) TestSyncAnyAbort() { Once(). Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil) - _, _, err = suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err = suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().Equal(errAbort, err) } @@ -356,7 +370,7 @@ func (suite *SyncerTestSuite) TestSyncAnyReject() { Once(). Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) - _, _, err = suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err = suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().Equal(errNoSnapshots, err) } @@ -399,7 +413,7 @@ func (suite *SyncerTestSuite) TestSyncAnyRejectFormat() { Once(). Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ABORT}, nil) - _, _, err = suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err = suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().Equal(errAbort, err) } @@ -453,7 +467,7 @@ func (suite *SyncerTestSuite) TestSyncAnyRejectSender() { Once(). Return(&abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_REJECT}, nil) - _, _, err := suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err := suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().Equal(errNoSnapshots, err) } @@ -481,7 +495,7 @@ func (suite *SyncerTestSuite) TestSyncAnyAbciError() { Once(). Return(nil, errBoom) - _, _, err = suite.syncer.SyncAny(ctx, 0, func() error { return nil }) + _, _, err = suite.syncer.SyncAny(ctx, 0, 0, func() error { return nil }) suite.Require().True(errors.Is(err, errBoom)) } @@ -616,8 +630,8 @@ func (suite *SyncerTestSuite) TestApplyChunksResults() { fetchStartTime := time.Now() - c := &chunk{Height: 1, Version: 1, ID: []byte{0}, Chunk: body} - chunks.Enqueue(c.ID) + chunkID := []byte{0} + chunks.Enqueue(chunkID) for _, resp := range tc.resps { suite.conn. diff --git a/libs/math/safemath.go b/libs/math/safemath.go index 9afb409b2..d9cd31df4 100644 --- a/libs/math/safemath.go +++ b/libs/math/safemath.go @@ -8,9 +8,11 @@ import ( var ErrOverflowInt64 = errors.New("int64 overflow") var ErrOverflowInt32 = errors.New("int32 overflow") +var ErrOverflowUint64 = errors.New("uint64 overflow") var ErrOverflowUint32 = errors.New("uint32 overflow") var ErrOverflowUint8 = errors.New("uint8 overflow") var ErrOverflowInt8 = errors.New("int8 overflow") +var ErrOverflow = errors.New("integer overflow") // SafeAddClipInt64 adds two int64 integers and clips the result to the int64 range. func SafeAddClipInt64(a, b int64) int64 { @@ -94,10 +96,107 @@ func SafeConvertUint32[T Integer](a T) (uint32, error) { return uint32(a), nil } +// SafeConvertUint64 takes a int and checks if it overflows. +func SafeConvertUint64[T Integer](a T) (uint64, error) { + return SafeConvert[T, uint64](a) +} + +// SafeConvertInt64 takes a int and checks if it overflows. +func SafeConvertInt64[T Integer](a T) (int64, error) { + return SafeConvert[T, int64](a) +} + +// SafeConvertInt16 takes a int and checks if it overflows. +func SafeConvertInt16[T Integer](a T) (int16, error) { + return SafeConvert[T, int16](a) +} + +// SafeConvertUint16 takes a int and checks if it overflows. +func SafeConvertUint16[T Integer](a T) (uint16, error) { + return SafeConvert[T, uint16](a) +} + type Integer interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 } +// SafeConvert converts a value of type T to a value of type U. +// It returns an error if the conversion would cause an overflow. +func SafeConvert[T Integer, U Integer](from T) (U, error) { + const uintIsSmall = math.MaxUint < math.MaxUint64 + const intIsSmall = math.MaxInt < math.MaxInt64 && math.MinInt > math.MinInt64 + + // special case for int64 and uint64 inputs; all other types are safe to convert to int64 + switch any(from).(type) { + case int64: + // conversion from int64 to uint64 - we need to check for negative values + if _, ok := any(U(0)).(uint64); ok && from < 0 { + return 0, ErrOverflow + } + return U(from), nil + case uint64: + // conversion from uint64 to int64 - we need to check for overflow + if _, ok := any(U(0)).(int64); ok && uint64(from) > math.MaxInt64 { + return 0, ErrOverflow + } + return U(from), nil + case int: + if !intIsSmall { + return SafeConvert[int64, U](int64(from)) + } + // no return here - it's safe to use normal logic + case uint: + if !uintIsSmall { + return SafeConvert[uint64, U](uint64(from)) + } + // no return here - it's safe to use normal logic + } + if uint64(from) > Max[U]() { + return 0, ErrOverflow + } + if int64(from) < Min[U]() { + return 0, ErrOverflow + } + return U(from), nil +} + +func MustConvert[FROM Integer, TO Integer](a FROM) TO { + i, err := SafeConvert[FROM, TO](a) + if err != nil { + var zero TO + panic(fmt.Errorf("cannot convert %d to %T: %w", a, zero, err)) + } + return i +} + +func MustConvertUint64[T Integer](a T) uint64 { + return MustConvert[T, uint64](a) +} + +func MustConvertInt64[T Integer](a T) int64 { + return MustConvert[T, int64](a) +} + +func MustConvertUint16[T Integer](a T) uint16 { + return MustConvert[T, uint16](a) +} + +func MustConvertInt16[T Integer](a T) int16 { + return MustConvert[T, int16](a) +} + +func MustConvertUint8[T Integer](a T) uint8 { + return MustConvert[T, uint8](a) +} + +func MustConvertUint[T Integer](a T) uint { + return MustConvert[T, uint](a) +} + +func MustConvertInt[T Integer](a T) int { + return MustConvert[T, int](a) +} + // MustConvertInt32 takes an Integer and converts it to int32. // Panics if the conversion overflows. func MustConvertInt32[T Integer](a T) int32 { @@ -159,3 +258,52 @@ func SafeMulInt64(a, b int64) (int64, bool) { return a * b, false } + +// Max returns the maximum value for a type T. +func Max[T Integer]() uint64 { + var max T + switch any(max).(type) { + case int: + return uint64(math.MaxInt) + case int8: + return uint64(math.MaxInt8) + case int16: + return uint64(math.MaxInt16) + case int32: + return uint64(math.MaxInt32) + case int64: + return uint64(math.MaxInt64) + case uint: + return uint64(math.MaxUint) + case uint8: + return uint64(math.MaxUint8) + case uint16: + return uint64(math.MaxUint16) + case uint32: + return uint64(math.MaxUint32) + case uint64: + return uint64(math.MaxUint64) + default: + panic("unsupported type") + } +} + +// Min returns the minimum value for a type T. +func Min[T Integer]() int64 { + switch any(T(0)).(type) { + case int: + return int64(math.MinInt) + case int8: + return int64(math.MinInt8) + case int16: + return int64(math.MinInt16) + case int32: + return int64(math.MinInt32) + case int64: + return math.MinInt64 + case uint, uint8, uint16, uint32, uint64: + return 0 + default: + panic("unsupported type") + } +} diff --git a/libs/math/safemath_test.go b/libs/math/safemath_test.go index 92a8f3211..98a57e116 100644 --- a/libs/math/safemath_test.go +++ b/libs/math/safemath_test.go @@ -84,3 +84,108 @@ func TestSafeMul(t *testing.T) { assert.Equal(t, tc.overflow, overflow, "#%d", i) } } + +func TestSafeConvert(t *testing.T) { + testCases := []struct { + from interface{} + want interface{} + err bool + }{ + {int(0), int64(0), false}, + {int(math.MaxInt), int64(math.MaxInt), false}, + {int(math.MinInt), int64(math.MinInt), false}, + {uint(0), uint64(0), false}, + {uint(math.MaxUint), uint64(math.MaxUint), false}, + {int64(0), uint64(0), false}, + {int64(math.MaxInt64), uint64(math.MaxInt64), false}, + {int64(math.MinInt64), uint64(0), true}, + {uint64(math.MaxUint64), int64(0), true}, + {uint64(math.MaxInt64), int64(math.MaxInt64), false}, + {int32(-1), uint32(0), true}, + {int32(0), uint32(0), false}, + {int32(math.MaxInt32), uint32(math.MaxInt32), false}, + {int32(math.MaxInt32), int16(0), true}, + {int32(math.MinInt32), int16(0), true}, + {int32(0), int16(0), false}, + {uint32(math.MaxUint32), int32(0), true}, + {uint32(math.MaxInt32), int32(math.MaxInt32), false}, + {uint32(0), int32(0), false}, + {int16(0), uint32(0), false}, + {int16(-1), uint32(0), true}, + {int16(math.MaxInt16), uint32(math.MaxInt16), false}, + } + + for i, tc := range testCases { + var result interface{} + var err error + + switch from := tc.from.(type) { + case int: + switch tc.want.(type) { + case int64: + result, err = SafeConvert[int, int64](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case uint: + switch tc.want.(type) { + case uint64: + result, err = SafeConvert[uint, uint64](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case int64: + switch tc.want.(type) { + case uint64: + result, err = SafeConvert[int64, uint64](from) + case int64: + result, err = SafeConvert[int64, int64](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case uint64: + switch tc.want.(type) { + case int64: + result, err = SafeConvert[uint64, int64](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case int32: + switch tc.want.(type) { + case int16: + result, err = SafeConvert[int32, int16](from) + case uint32: + result, err = SafeConvert[int32, uint32](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case uint32: + switch tc.want.(type) { + case int16: + result, err = SafeConvert[uint32, int16](from) + case int32: + result, err = SafeConvert[uint32, int32](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + case int16: + switch tc.want.(type) { + case int32: + result, err = SafeConvert[int16, int32](from) + case uint32: + result, err = SafeConvert[int16, uint32](from) + default: + t.Fatalf("test case %d: unsupported target type %T", i, tc.want) + } + default: + t.Fatalf("test case %d: unsupported source type %T", i, tc.from) + } + + if (err != nil) != tc.err { + t.Errorf("test case %d: expected error %v, got %v", i, tc.err, err) + } + if err == nil && result != tc.want { + t.Errorf("test case %d: expected result %v, got %v", i, tc.want, result) + } + } +} diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 3453c1ce4..6963cd792 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -32,6 +32,7 @@ message Request { RequestOfferSnapshot offer_snapshot = 12; RequestLoadSnapshotChunk load_snapshot_chunk = 13; RequestApplySnapshotChunk apply_snapshot_chunk = 14; + RequestFinalizeSnapshot finalize_snapshot = 20; RequestPrepareProposal prepare_proposal = 15; RequestProcessProposal process_proposal = 16; RequestExtendVote extend_vote = 17; @@ -182,6 +183,19 @@ message RequestApplySnapshotChunk { bytes chunk = 2; // The binary chunk contents, as returned by LoadSnapshotChunk. string sender = 3; // The P2P ID of the node who sent this chunk. } +// RequestFinalizeSnapshot is called by Tenderdash after successfully applying all snapshot chunks, e.g. +// when the ABCI application returned `ResponseApplySnapshotChunk` with `Result = ACCEPT`. +// It includes the light block committed at the synced height, which Tenderdash uses to reconstruct its own state. +// The application should validate the light block against its restored state. +// +// If the application fails to validate the light block, it should return error in the response. +// This is considered fatal, non-recoverable consensus failure and will cause Tenderdash to restart. +message RequestFinalizeSnapshot { + // Snapshot block is a block at which the snapshot was taken. + tendermint.types.LightBlock snapshot_block = 1; + // Genesis block is the first block of the chain + tendermint.types.LightBlock genesis_block = 2; +} // Prepare new block proposal, potentially altering list of transactions. // @@ -514,6 +528,7 @@ message Response { ResponseOfferSnapshot offer_snapshot = 9; ResponseLoadSnapshotChunk load_snapshot_chunk = 10; ResponseApplySnapshotChunk apply_snapshot_chunk = 11; + ResponseFinalizeSnapshot finalize_snapshot = 17; ResponsePrepareProposal prepare_proposal = 12; ResponseProcessProposal process_proposal = 13; ResponseExtendVote extend_vote = 14; @@ -629,6 +644,10 @@ message ResponseApplySnapshotChunk { } } +// The response to a `RequestPrepareSnapshot` message. +message ResponseFinalizeSnapshot { +} + message ResponsePrepareProposal { // Possibly modified list of transactions that have been picked as part of the proposed block. repeated TxRecord tx_records = 1; @@ -891,6 +910,7 @@ service ABCIApplication { rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot); rpc LoadSnapshotChunk(RequestLoadSnapshotChunk) returns (ResponseLoadSnapshotChunk); rpc ApplySnapshotChunk(RequestApplySnapshotChunk) returns (ResponseApplySnapshotChunk); + rpc FinalizeSnapshot(RequestFinalizeSnapshot) returns (ResponseFinalizeSnapshot); rpc PrepareProposal(RequestPrepareProposal) returns (ResponsePrepareProposal); rpc ProcessProposal(RequestProcessProposal) returns (ResponseProcessProposal); rpc ExtendVote(RequestExtendVote) returns (ResponseExtendVote); diff --git a/spec/abci++/api.md b/spec/abci++/api.md index 6162aaf3b..aff911a8b 100644 --- a/spec/abci++/api.md +++ b/spec/abci++/api.md @@ -18,6 +18,7 @@ - [RequestEcho](#tendermint-abci-RequestEcho) - [RequestExtendVote](#tendermint-abci-RequestExtendVote) - [RequestFinalizeBlock](#tendermint-abci-RequestFinalizeBlock) + - [RequestFinalizeSnapshot](#tendermint-abci-RequestFinalizeSnapshot) - [RequestFlush](#tendermint-abci-RequestFlush) - [RequestInfo](#tendermint-abci-RequestInfo) - [RequestInitChain](#tendermint-abci-RequestInitChain) @@ -35,6 +36,7 @@ - [ResponseException](#tendermint-abci-ResponseException) - [ResponseExtendVote](#tendermint-abci-ResponseExtendVote) - [ResponseFinalizeBlock](#tendermint-abci-ResponseFinalizeBlock) + - [ResponseFinalizeSnapshot](#tendermint-abci-ResponseFinalizeSnapshot) - [ResponseFlush](#tendermint-abci-ResponseFlush) - [ResponseInfo](#tendermint-abci-ResponseInfo) - [ResponseInitChain](#tendermint-abci-ResponseInitChain) @@ -247,6 +249,7 @@ Request types | offer_snapshot | [RequestOfferSnapshot](#tendermint-abci-RequestOfferSnapshot) | | | | load_snapshot_chunk | [RequestLoadSnapshotChunk](#tendermint-abci-RequestLoadSnapshotChunk) | | | | apply_snapshot_chunk | [RequestApplySnapshotChunk](#tendermint-abci-RequestApplySnapshotChunk) | | | +| finalize_snapshot | [RequestFinalizeSnapshot](#tendermint-abci-RequestFinalizeSnapshot) | | | | prepare_proposal | [RequestPrepareProposal](#tendermint-abci-RequestPrepareProposal) | | | | process_proposal | [RequestProcessProposal](#tendermint-abci-RequestProcessProposal) | | | | extend_vote | [RequestExtendVote](#tendermint-abci-RequestExtendVote) | | | @@ -423,6 +426,28 @@ Finalize newly decided block. + + +### RequestFinalizeSnapshot +RequestFinalizeSnapshot is called by Tenderdash after successfully applying all snapshot chunks, e.g. +when the ABCI application returned `ResponseApplySnapshotChunk` with `Result = ACCEPT`. +It includes the light block committed at the synced height, which Tenderdash uses to reconstruct its own state. +The application should validate the light block against its restored state. + +If the application fails to validate the light block, it should return error in the response. +This is considered fatal, non-recoverable consensus failure and will cause Tenderdash to restart. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| snapshot_block | [tendermint.types.LightBlock](#tendermint-types-LightBlock) | | Snapshot block is a block at which the snapshot was taken. | +| genesis_block | [tendermint.types.LightBlock](#tendermint-types-LightBlock) | | Genesis block is the first block of the chain | + + + + + + ### RequestFlush @@ -809,6 +834,7 @@ from this condition, but not sure), and _p_ receives a Precommit message for rou | offer_snapshot | [ResponseOfferSnapshot](#tendermint-abci-ResponseOfferSnapshot) | | | | load_snapshot_chunk | [ResponseLoadSnapshotChunk](#tendermint-abci-ResponseLoadSnapshotChunk) | | | | apply_snapshot_chunk | [ResponseApplySnapshotChunk](#tendermint-abci-ResponseApplySnapshotChunk) | | | +| finalize_snapshot | [ResponseFinalizeSnapshot](#tendermint-abci-ResponseFinalizeSnapshot) | | | | prepare_proposal | [ResponsePrepareProposal](#tendermint-abci-ResponsePrepareProposal) | | | | process_proposal | [ResponseProcessProposal](#tendermint-abci-ResponseProcessProposal) | | | | extend_vote | [ResponseExtendVote](#tendermint-abci-ResponseExtendVote) | | | @@ -919,6 +945,16 @@ nondeterministic + + +### ResponseFinalizeSnapshot +The response to a `RequestPrepareSnapshot` message. + + + + + + ### ResponseFlush @@ -1364,6 +1400,7 @@ TxAction contains App-provided information on what to do with a transaction that | OfferSnapshot | [RequestOfferSnapshot](#tendermint-abci-RequestOfferSnapshot) | [ResponseOfferSnapshot](#tendermint-abci-ResponseOfferSnapshot) | | | LoadSnapshotChunk | [RequestLoadSnapshotChunk](#tendermint-abci-RequestLoadSnapshotChunk) | [ResponseLoadSnapshotChunk](#tendermint-abci-ResponseLoadSnapshotChunk) | | | ApplySnapshotChunk | [RequestApplySnapshotChunk](#tendermint-abci-RequestApplySnapshotChunk) | [ResponseApplySnapshotChunk](#tendermint-abci-ResponseApplySnapshotChunk) | | +| FinalizeSnapshot | [RequestFinalizeSnapshot](#tendermint-abci-RequestFinalizeSnapshot) | [ResponseFinalizeSnapshot](#tendermint-abci-ResponseFinalizeSnapshot) | | | PrepareProposal | [RequestPrepareProposal](#tendermint-abci-RequestPrepareProposal) | [ResponsePrepareProposal](#tendermint-abci-ResponsePrepareProposal) | | | ProcessProposal | [RequestProcessProposal](#tendermint-abci-RequestProcessProposal) | [ResponseProcessProposal](#tendermint-abci-ResponseProcessProposal) | | | ExtendVote | [RequestExtendVote](#tendermint-abci-RequestExtendVote) | [ResponseExtendVote](#tendermint-abci-ResponseExtendVote) | | diff --git a/test/e2e/networks/rotate.toml b/test/e2e/networks/rotate.toml index 2e725ec19..30f694309 100644 --- a/test/e2e/networks/rotate.toml +++ b/test/e2e/networks/rotate.toml @@ -152,7 +152,7 @@ start_at = 1005 # Becomes part of the validator set at 1030 to ensure ther seeds = ["seed01"] snapshot_interval = 5 block_sync = "v0" -#state_sync = "p2p" +state_sync = "p2p" #persistent_peers = ["validator01", "validator02", "validator03", "validator04", "validator05", "validator07", "validator08"] perturb = ["pause", "disconnect", "restart"] privval_protocol = "dashcore" @@ -192,7 +192,7 @@ privval_protocol = "dashcore" start_at = 1030 mode = "full" block_sync = "v0" -#state_sync = "rpc" +state_sync = "p2p" persistent_peers = [ "validator01", "validator02", diff --git a/test/e2e/pkg/mockcoreserver/core_server.go b/test/e2e/pkg/mockcoreserver/core_server.go index 4b6025e12..5aa2b7b0c 100644 --- a/test/e2e/pkg/mockcoreserver/core_server.go +++ b/test/e2e/pkg/mockcoreserver/core_server.go @@ -146,9 +146,8 @@ func (c *MockCoreServer) QuorumVerify(ctx context.Context, cmd btcjson.QuorumCmd signatureVerified := thresholdPublicKey.VerifySignatureDigest(signID, signature) - res := btcjson.QuorumVerifyResult{ - Result: signatureVerified, - } + res := btcjson.QuorumVerifyResult{Result: signatureVerified} + return res } diff --git a/test/e2e/pkg/mockcoreserver/methods.go b/test/e2e/pkg/mockcoreserver/methods.go index 5894dfa86..b38f17982 100644 --- a/test/e2e/pkg/mockcoreserver/methods.go +++ b/test/e2e/pkg/mockcoreserver/methods.go @@ -64,8 +64,8 @@ func WithQuorumVerifyMethod(cs CoreServer, times int) MethodFunc { &cmd.LLMQType, &cmd.RequestID, &cmd.MessageHash, - &cmd.QuorumHash, &cmd.Signature, + &cmd.QuorumHash, ) if err != nil { return nil, err diff --git a/test/e2e/runner/setup.go b/test/e2e/runner/setup.go index b15302408..0ca57c228 100644 --- a/test/e2e/runner/setup.go +++ b/test/e2e/runner/setup.go @@ -9,7 +9,6 @@ import ( "fmt" "os" "path/filepath" - "regexp" "sort" "strconv" "strings" @@ -454,23 +453,6 @@ func MakeAppConfig(node *e2e.Node) ([]byte, error) { return buf.Bytes(), nil } -// UpdateConfigStateSync updates the state sync config for a node. -func UpdateConfigStateSync(node *e2e.Node, height int64, hash []byte) error { - cfgPath := filepath.Join(node.Testnet.Dir, node.Name, "config", "config.toml") - - // FIXME Apparently there's no function to simply load a config file without - // involving the entire Viper apparatus, so we'll just resort to regexps. - bz, err := os.ReadFile(cfgPath) - if err != nil { - return err - } - bz = regexp.MustCompile(`(?m)^trust-height =.*`).ReplaceAll(bz, []byte(fmt.Sprintf(`trust-height = %v`, height-1))) - bz = regexp.MustCompile(`(?m)^trust-hash =.*`).ReplaceAll(bz, []byte(fmt.Sprintf(`trust-hash = "%X"`, hash))) - //nolint: gosec - // G306: Expect WriteFile permissions to be 0600 or less - return os.WriteFile(cfgPath, bz, 0644) -} - func newDefaultFilePV(node *e2e.Node, nodeDir string) (*privval.FilePV, error) { return privval.NewFilePVWithOptions( privval.WithPrivateKeysMap(node.PrivvalKeys), diff --git a/test/e2e/runner/start.go b/test/e2e/runner/start.go index 3b45597fb..64c5b0ce8 100644 --- a/test/e2e/runner/start.go +++ b/test/e2e/runner/start.go @@ -70,11 +70,6 @@ func Start(ctx context.Context, logger log.Logger, testnet *e2e.Testnet, ti infr "nodes", len(testnet.Nodes)-len(nodeQueue), "pending", len(nodeQueue)) - block, blockID, err := waitForHeight(ctx, testnet, networkHeight) - if err != nil { - return err - } - for _, node := range nodeQueue { if node.StartAt > networkHeight { // if we're starting a node that's ahead of @@ -93,16 +88,7 @@ func Start(ctx context.Context, logger log.Logger, testnet *e2e.Testnet, ti infr networkHeight = node.StartAt - block, blockID, err = waitForHeight(ctx, testnet, networkHeight) - if err != nil { - return err - } - } - - // Update any state sync nodes with a trusted height and hash - if node.StateSync != e2e.StateSyncDisabled || node.Mode == e2e.ModeLight { - err = UpdateConfigStateSync(node, block.Height, blockID.Hash.Bytes()) - if err != nil { + if _, _, err := waitForHeight(ctx, testnet, networkHeight); err != nil { return err } } diff --git a/types/quorum_sign_data.go b/types/quorum_sign_data.go index 1bd51c622..ee73d4b46 100644 --- a/types/quorum_sign_data.go +++ b/types/quorum_sign_data.go @@ -161,7 +161,7 @@ func (i *SignItem) Validate() error { if len(i.MsgHash) != crypto.DefaultHashSize { return fmt.Errorf("invalid hash size %d: %X", len(i.MsgHash), i.MsgHash) } - if len(i.QuorumHash) != crypto.DefaultHashSize { + if len(i.QuorumHash) != crypto.QuorumHashSize { return fmt.Errorf("invalid quorum hash size %d: %X", len(i.QuorumHash), i.QuorumHash) } // Msg is optional diff --git a/types/validator_test.go b/types/validator_test.go index 72d905110..dffadeb12 100644 --- a/types/validator_test.go +++ b/types/validator_test.go @@ -2,12 +2,16 @@ package types import ( "context" + "encoding/base64" + "encoding/hex" + "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/dashpay/tenderdash/crypto" + "github.com/dashpay/tenderdash/crypto/bls12381" ) func TestValidatorProtoBuf(t *testing.T) { @@ -111,3 +115,23 @@ func TestValidatorValidateBasic(t *testing.T) { }) } } + +func TestValdiatorSetHash(t *testing.T) { + thresholdPublicKey, err := base64.RawStdEncoding.DecodeString("gw5F5F5kFNnWFUc8woFOaxccUI+cd+ixaSS3RZT2HJlWpvoWM16YRn6sjYvbdtGH") + require.NoError(t, err) + fmt.Printf("thresholdPublicKey: %s\n", hex.EncodeToString(thresholdPublicKey)) + + quorumHash, err := hex.DecodeString("703ee5bfc78765cc9e151d8dd84e30e196ababa83ac6cbdee31a88a46bba81b9") + require.NoError(t, err) + + expected := "81742F95E99EAE96ABC727FE792CECB4996205DE6BFC88AFEE1F60B96BC648B2" + // require.NoError(t, err) + + valset := ValidatorSet{ + ThresholdPublicKey: bls12381.PubKey(thresholdPublicKey), + QuorumHash: quorumHash, + } + + hash := valset.Hash() + assert.Equal(t, expected, hash.String()) +} diff --git a/version/version.go b/version/version.go index e2db66979..df7d4822e 100644 --- a/version/version.go +++ b/version/version.go @@ -11,7 +11,7 @@ const ( // when not using git describe. It is formatted with semantic versioning. TMVersionDefault = "1.4.0" // ABCISemVer is the semantic version of the ABCI library - ABCISemVer = "1.2.0" + ABCISemVer = "1.3.0" ABCIVersion = ABCISemVer )