diff --git a/.mockery.yaml b/.mockery.yaml index d44c09dfc8..ceed741a1c 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -53,6 +53,7 @@ packages: github.com/dashpay/tenderdash/internal/statesync: interfaces: StateProvider: + ConsensusStateProvider: github.com/dashpay/tenderdash/libs/store: interfaces: Store: diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index 0ffed9b399..d8e57bd45e 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 7c2d062858..9994b266df 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 fa1afc1bc3..2bc4a8aac1 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 fb046187d8..4ca9e0739b 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 6c322ade1f..6f93d2fadf 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 fe7b60bca4..3a19116bdc 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 fed82db198..3922c4fe0e 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 1a706370d6..5e0404ee5a 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 178d8b1c57..b3db8b02c0 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -125,7 +125,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 @@ -165,7 +165,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 @@ -193,7 +193,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 @@ -221,7 +221,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 @@ -256,7 +256,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 @@ -272,6 +272,7 @@ type Request struct { // *Request_OfferSnapshot // *Request_LoadSnapshotChunk // *Request_ApplySnapshotChunk + // *Request_FinalizeSnapshot // *Request_PrepareProposal // *Request_ProcessProposal // *Request_ExtendVote @@ -349,6 +350,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"` } @@ -375,6 +379,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() {} @@ -458,6 +463,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 @@ -506,6 +518,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), @@ -1162,6 +1175,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 @@ -1281,7 +1355,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) @@ -1481,7 +1555,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) @@ -1653,7 +1727,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) @@ -1748,7 +1822,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) @@ -1859,7 +1933,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) @@ -1951,6 +2025,7 @@ type Response struct { // *Response_OfferSnapshot // *Response_LoadSnapshotChunk // *Response_ApplySnapshotChunk + // *Response_FinalizeSnapshot // *Response_PrepareProposal // *Response_ProcessProposal // *Response_ExtendVote @@ -1963,7 +2038,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) @@ -2031,6 +2106,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"` } @@ -2058,6 +2136,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() {} @@ -2148,6 +2227,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 @@ -2197,6 +2283,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), @@ -2214,7 +2301,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) @@ -2258,7 +2345,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) @@ -2301,7 +2388,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) @@ -2342,7 +2429,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) @@ -2427,7 +2514,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) @@ -2544,7 +2631,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) @@ -2650,7 +2737,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) @@ -2736,7 +2823,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) @@ -2780,7 +2867,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) @@ -2826,7 +2913,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) @@ -2879,7 +2966,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) @@ -2936,6 +3023,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"` @@ -2957,7 +3081,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) @@ -3054,7 +3178,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) @@ -3144,7 +3268,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) @@ -3228,7 +3352,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) @@ -3272,7 +3396,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) @@ -3317,7 +3441,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) @@ -3364,7 +3488,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) @@ -3435,7 +3559,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) @@ -3489,7 +3613,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) @@ -3555,7 +3679,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) @@ -3647,7 +3771,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) @@ -3713,7 +3837,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) @@ -3766,7 +3890,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) @@ -3824,7 +3948,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) @@ -3906,7 +4030,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) @@ -3964,7 +4088,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) @@ -4008,7 +4132,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) @@ -4054,7 +4178,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) @@ -4111,7 +4235,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) @@ -4179,7 +4303,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) @@ -4254,7 +4378,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) @@ -4330,6 +4454,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") @@ -4347,6 +4472,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") @@ -4373,239 +4499,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. @@ -4631,6 +4764,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) @@ -4736,6 +4870,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...) @@ -4794,6 +4937,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) @@ -4835,6 +4979,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") } @@ -5035,6 +5182,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 { @@ -5170,6 +5335,10 @@ var _ABCIApplication_serviceDesc = grpc.ServiceDesc{ MethodName: "ApplySnapshotChunk", Handler: _ABCIApplication_ApplySnapshotChunk_Handler, }, + { + MethodName: "FinalizeSnapshot", + Handler: _ABCIApplication_FinalizeSnapshot_Handler, + }, { MethodName: "PrepareProposal", Handler: _ABCIApplication_PrepareProposal_Handler, @@ -5550,6 +5719,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) @@ -5718,12 +5910,12 @@ func (m *RequestInitChain) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - n18, err18 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err18 != nil { - return 0, err18 + n19, err19 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_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 @@ -5965,6 +6157,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) @@ -6033,12 +6272,12 @@ func (m *RequestPrepareProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x3a } - n21, err21 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err21 != nil { - return 0, err21 + n24, err24 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_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 { @@ -6162,12 +6401,12 @@ func (m *RequestProcessProposal) MarshalToSizedBuffer(dAtA []byte) (int, error) i-- dAtA[i] = 0x42 } - n25, err25 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err25 != nil { - return 0, err25 + n28, err28 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_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 { @@ -6782,6 +7021,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) @@ -7005,12 +7267,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_cosmos_gogoproto_types.StdTimeMarshalTo(*m.GenesisTime, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(*m.GenesisTime):]) - if err49 != nil { - return 0, err49 + n53, err53 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(*m.GenesisTime, dAtA[i-github_com_cosmos_gogoproto_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 } @@ -7317,6 +7579,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) @@ -8287,12 +8572,12 @@ func (m *Misbehavior) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x28 } - n62, err62 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdTime(m.Time):]) - if err62 != nil { - return 0, err62 + n66, err66 := github_com_cosmos_gogoproto_types.StdTimeMarshalTo(m.Time, dAtA[i-github_com_cosmos_gogoproto_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 { @@ -8568,6 +8853,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 @@ -8751,6 +9048,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 @@ -9155,6 +9469,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 @@ -9404,6 +9730,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 @@ -10411,6 +10746,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:]) @@ -11675,6 +12045,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 @@ -13718,6 +14210,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:]) @@ -15328,6 +15855,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 ac43d04413..a6e4903fa9 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 b6be127b28..dddf0ac43f 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 cc488c6b9f..e7258e6f59 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.2 // 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.5.1 // indirect github.com/dgraph-io/ristretto/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 6ceb54e829..d33615cece 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.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/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= @@ -222,12 +223,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/blocksync/reactor.go b/internal/blocksync/reactor.go index 822550f165..b182229133 100644 --- a/internal/blocksync/reactor.go +++ b/internal/blocksync/reactor.go @@ -159,7 +159,7 @@ func (r *Reactor) OnStop() { // processPeerUpdate processes a PeerUpdate. func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate, client *client.Client) { - r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + r.logger.Trace("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) // XXX: Pool#RedoRequest can sometimes give us an empty peer. if len(peerUpdate.NodeID) == 0 { diff --git a/internal/consensus/state.go b/internal/consensus/state.go index 02180c3305..69a6dcc6c4 100644 --- a/internal/consensus/state.go +++ b/internal/consensus/state.go @@ -307,6 +307,10 @@ func (cs *State) SetProposedAppVersion(ver uint64) { cs.emitter.Emit(setProposedAppVersionEventName, ver) } +func (cs *State) GetCurrentHeight() int64 { + return cs.stateDataStore.Get().Height +} + func (cs *State) updateStateFromStore() error { state, err := cs.stateStore.Load() if err != nil { diff --git a/internal/consensus/state_data.go b/internal/consensus/state_data.go index 0686b0f32d..66b0405711 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/evidence/reactor.go b/internal/evidence/reactor.go index d0ba641310..75689553e5 100644 --- a/internal/evidence/reactor.go +++ b/internal/evidence/reactor.go @@ -201,7 +201,7 @@ func (r *Reactor) processEvidenceCh(ctx context.Context) { // // REF: https://github.com/tendermint/tendermint/issues/4727 func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate) { - r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + r.logger.Trace("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) r.mtx.Lock() defer r.mtx.Unlock() diff --git a/internal/libs/sync/mutexguard.go b/internal/libs/sync/mutexguard.go new file mode 100644 index 0000000000..5c1c1be18e --- /dev/null +++ b/internal/libs/sync/mutexguard.go @@ -0,0 +1,54 @@ +package sync + +// UnlockFn is a function that unlocks a mutex. +// It returns true if the mutex was unlocked, false if it was already unlocked. +type UnlockFn func() bool + +// Mtx is a mutex interface. +// Implemented by sync.Mutex and deadlock.Mutex. +type Mtx interface { + Lock() + Unlock() +} + +// RMtx is a mutex that can be locked for read. +// +// Implemented by sync.RwMutex and deadlock.RwMutex. +type RMtx interface { + RLock() + RUnlock() +} + +// LockGuard locks the mutex and returns a function that unlocks it. +// The returned function must be called to release the lock. +// The returned function may be called multiple times - only the first call will unlock the mutex, others will be no-ops. +func LockGuard(mtx Mtx) UnlockFn { + mtx.Lock() + locked := true + + return func() bool { + if locked { + locked = false + mtx.Unlock() + return true + } + return false + } +} + +// RLockGuard locks the read-write mutex for reading and returns a function that unlocks it. +// The returned function must be called to release the lock. +// The returned function may be called multiple times - only the first call will unlock the mutex, others will be no-ops. +func RLockGuard(mtx RMtx) UnlockFn { + mtx.RLock() + locked := true + + return func() bool { + if locked { + locked = false + mtx.RUnlock() + return true + } + return false + } +} diff --git a/internal/libs/sync/mutexguard_test.go b/internal/libs/sync/mutexguard_test.go new file mode 100644 index 0000000000..a205a67f0d --- /dev/null +++ b/internal/libs/sync/mutexguard_test.go @@ -0,0 +1,141 @@ +package sync_test + +import ( + "sync" + "testing" + "time" + + deadlock "github.com/sasha-s/go-deadlock" + "github.com/stretchr/testify/assert" + + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" +) + +// TestLockGuardMultipleUnlocks checks that the LockGuard function correctly handles multiple unlock calls. +func TestLockGuardMultipleUnlocks(t *testing.T) { + // Disable deadlock detection logic for this test + deadlockDisabled := deadlock.Opts.Disable + deadlock.Opts.Disable = true + defer func() { + deadlock.Opts.Disable = deadlockDisabled + }() + var mtx deadlock.Mutex + + unlock := tmsync.LockGuard(&mtx) + // deferred unlock() will do nothing because we unlock inside the test, but we still want to check this + defer func() { assert.False(t, unlock()) }() + // locking should not be possible + assert.False(t, mtx.TryLock()) + + assert.True(t, unlock()) + // here we can lock + mtx.Lock() + // unlock should do nothing + assert.False(t, unlock()) + // locking again should not be possible + assert.False(t, mtx.TryLock()) + // unlock should do nothing + assert.False(t, unlock()) + // but this unlock should work + mtx.Unlock() + assert.True(t, mtx.TryLock()) +} + +// TestLockGuard checks that the LockGuard function correctly increments a counter using multiple goroutines. +func TestLockGuard(t *testing.T) { + var mtx deadlock.Mutex + var counter int + var wg sync.WaitGroup + + increment := func() { + defer wg.Done() + unlock := tmsync.LockGuard(&mtx) + defer unlock() + counter++ + } + + // Start multiple goroutines to increment the counter + for i := 0; i < 100; i++ { + wg.Add(1) + go increment() + } + + wg.Wait() + assert.Equal(t, 100, counter, "Counter should be incremented to 100") +} + +// TestRLockGuard checks that the RLockGuard function allows multiple read locks +// and correctly increments a counter using write locks. +func TestRLockGuard(t *testing.T) { + var mtx deadlock.RWMutex + var counter int + var wg sync.WaitGroup + + read := func() { + defer wg.Done() + unlock := tmsync.RLockGuard(&mtx) + defer unlock() + _ = counter // Just read the counter + } + + write := func() { + defer wg.Done() + unlock := tmsync.LockGuard(&mtx) + defer unlock() + counter++ + } + + // Start multiple goroutines to read the counter + for i := 0; i < 100; i++ { + wg.Add(1) + go read() + } + + // Start multiple goroutines to write to the counter + for i := 0; i < 10; i++ { + wg.Add(1) + go write() + } + + wg.Wait() + assert.Equal(t, 10, counter, "Counter should be incremented to 10") +} + +// TestMixedLocks checks the behavior of mixed read and write locks, +// ensuring that the counter is correctly incremented while allowing concurrent reads. +func TestMixedLocks(t *testing.T) { + var mtx deadlock.RWMutex + var counter int + var wg sync.WaitGroup + + read := func() { + defer wg.Done() + unlock := tmsync.RLockGuard(&mtx) + defer unlock() + time.Sleep(10 * time.Millisecond) // Simulate read delay + _ = counter // Just read the counter + } + + write := func() { + defer wg.Done() + unlock := tmsync.LockGuard(&mtx) + defer unlock() + counter++ + time.Sleep(10 * time.Millisecond) // Simulate write delay + } + + // Start multiple goroutines to read the counter + for i := 0; i < 50; i++ { + wg.Add(1) + go read() + } + + // Start multiple goroutines to write to the counter + for i := 0; i < 5; i++ { + wg.Add(1) + go write() + } + + wg.Wait() + assert.Equal(t, 5, counter, "Counter should be incremented to 5") +} diff --git a/internal/mempool/reactor.go b/internal/mempool/reactor.go index 415fef8bec..c00c364359 100644 --- a/internal/mempool/reactor.go +++ b/internal/mempool/reactor.go @@ -94,7 +94,7 @@ func (r *Reactor) OnStop() {} // removed peers, we remove the peer from the mempool peer ID set and signal to // stop the tx broadcasting goroutine. func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate) { - r.logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + r.logger.Trace("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) r.mtx.Lock() defer r.mtx.Unlock() diff --git a/internal/p2p/transport_mconn.go b/internal/p2p/transport_mconn.go index 3757f8f7d4..920382b134 100644 --- a/internal/p2p/transport_mconn.go +++ b/internal/p2p/transport_mconn.go @@ -184,7 +184,7 @@ func (m *MConnTransport) Dial(ctx context.Context, endpoint *Endpoint) (Connecti return nil, err } if endpoint.Port == 0 { - endpoint.Port = 26657 + endpoint.Port = 26656 } dialer := net.Dialer{} diff --git a/internal/proxy/client.go b/internal/proxy/client.go index 9421d6b8ea..a4fa986cf4 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 bd9d23e880..8a11275a39 100644 --- a/internal/statesync/chunks.go +++ b/internal/statesync/chunks.go @@ -1,6 +1,8 @@ package statesync import ( + "crypto/sha256" + "encoding/hex" "errors" "fmt" "os" @@ -9,6 +11,8 @@ import ( sync "github.com/sasha-s/go-deadlock" + tmsync "github.com/dashpay/tenderdash/internal/libs/sync" + "github.com/dashpay/tenderdash/libs/bytes" "github.com/dashpay/tenderdash/types" ) @@ -63,6 +67,22 @@ type ( } ) +// Filename updates `chunkItem.file` with an absolute path to file containing the the chunk and returns it. +// If the filename is already set, it isn't changed. +// +// Returns error if the filename cannot be created. +// +// Caller must ensure only one goroutine calls this method at a time, eg. by holding the mutex lock. +func (c *chunkItem) Filename(parentDir string) (string, error) { + var err error + if c.file == "" { + hash := sha256.Sum256(c.chunkID) + filename := hex.EncodeToString(hash[:]) + c.file, err = filepath.Abs(filepath.Join(parentDir, filename)) + } + return c.file, err +} + // newChunkQueue creates a new chunk requestQueue for a snapshot, using a temp dir for storage. // Callers must call Close() when done. func newChunkQueue(snapshot *snapshot, tempDir string, bufLen int) (*chunkQueue, error) { @@ -133,36 +153,56 @@ 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 } - q.mtx.Lock() - defer q.mtx.Unlock() - if q.snapshot == nil { - return false, errNilSnapshot + + // empty chunk content is allowed, but we ensure it's not nil + data := chunk.Chunk + if data == nil { + data = []byte{} } - chunkIDKey := chunk.ID.String() - item, ok := q.items[chunkIDKey] - if !ok { - return false, fmt.Errorf("failed to add the chunk %x, it was never requested", chunk.ID) + + unlockFn := tmsync.LockGuard(&q.mtx) + defer unlockFn() + + item, err := q.getItem(chunk.ID) + if err != nil { + return false, fmt.Errorf("get chunk %x: %w", chunk.ID, err) } + if item.status != inProgressStatus && item.status != discardedStatus { + // chunk either already exists, or we didn't request it yet, so we ignore it return false, nil } - err := q.validateChunk(chunk) + + err = q.validateChunk(chunk) if err != nil { - return false, err + return false, fmt.Errorf("validate chunk %x: %w", chunk.ID, err) + } + + // ensure filename is set on the item + _, err = item.Filename(q.dir) + if err != nil { + return false, fmt.Errorf("failed to get filename for chunk %x: %w", chunk.ID, err) } - item.file = filepath.Join(q.dir, chunkIDKey) - err = item.write(chunk.Chunk) + + err = item.write(data) if err != nil { return false, err } item.sender = chunk.Sender item.status = receivedStatus + + // unlock before sending to applyCh to avoid blocking/deadlock on the applyCh + unlockFn() + q.applyCh <- chunk.ID // Signal any waiters that the chunk has arrived. + q.mtx.Lock() item.closeWaitChs(true) + q.mtx.Unlock() + return true, nil } @@ -284,8 +324,25 @@ func (q *chunkQueue) Next() (*chunk, error) { q.doneCount++ return loadedChunk, nil case <-time.After(chunkTimeout): - return nil, errTimeout + // Locking is done inside q.Pending + pendingChunks := len(q.Pending()) + return nil, fmt.Errorf("timed out waiting for %d chunks: %w", pendingChunks, errTimeout) + } +} + +// Pending returns a list of all chunks that have been requested but not yet received. +func (q *chunkQueue) Pending() []bytes.HexBytes { + q.mtx.Lock() + defer q.mtx.Unlock() + + // get all keys from the map that don't have a status of received + waiting := make([]bytes.HexBytes, 0, len(q.items)) + for _, item := range q.items { + if item.status == initStatus || item.status == inProgressStatus { + waiting = append(waiting, item.chunkID) + } } + return waiting } // Retry schedules a chunk to be retried, without refetching it. @@ -296,12 +353,13 @@ func (q *chunkQueue) Retry(chunkID bytes.HexBytes) { } func (q *chunkQueue) retry(chunkID bytes.HexBytes) { - item, ok := q.items[chunkID.String()] + chunkKey := chunkID.String() + item, ok := q.items[chunkKey] if !ok || (item.status != receivedStatus && item.status != doneStatus) { return } q.requestQueue = append(q.requestQueue, chunkID) - q.items[chunkID.String()].status = initStatus + q.items[chunkKey].status = initStatus } // RetryAll schedules all chunks to be retried, without refetching them. @@ -345,6 +403,23 @@ func (q *chunkQueue) DoneChunksCount() int { return q.doneCount } +// getItem fetches chunk item from the items map. If the item is not found, it returns an error. +// The caller must hold the mutex lock. +func (q *chunkQueue) getItem(chunkID bytes.HexBytes) (*chunkItem, error) { + if q.snapshot == nil { + return nil, errNilSnapshot + } + chunkIDKey := chunkID.String() + item, ok := q.items[chunkIDKey] + if !ok { + return nil, fmt.Errorf("chunk %x not found", chunkID) + } + + return item, nil +} + +// validateChunk checks if the chunk is expected and valid for the current snapshot +// The caller must hold the mutex lock. func (q *chunkQueue) validateChunk(chunk *chunk) error { if chunk.Height != q.snapshot.Height { return fmt.Errorf("invalid chunk height %v, expected %v", diff --git a/internal/statesync/chunks_test.go b/internal/statesync/chunks_test.go index 0ecb01946e..e3be49683a 100644 --- a/internal/statesync/chunks_test.go +++ b/internal/statesync/chunks_test.go @@ -1,6 +1,7 @@ package statesync import ( + "errors" "os" "testing" @@ -100,10 +101,10 @@ func (suite *ChunkQueueTestSuite) TestChunkQueue() { {chunk: suite.chunks[1], want: true}, } require := suite.Require() - for _, tc := range testCases { + for i, tc := range testCases { added, err := suite.queue.Add(tc.chunk) - require.NoError(err) - require.Equal(tc.want, added) + require.NoError(err, "test case %d", i) + require.Equal(tc.want, added, "test case %d", i) } // At this point, we should be able to retrieve them all via Next @@ -244,7 +245,7 @@ func (suite *ChunkQueueTestSuite) TestNext() { go func() { for { c, err := suite.queue.Next() - if err == errDone { + if errors.Is(err, errDone) { close(chNext) break } @@ -284,7 +285,7 @@ func (suite *ChunkQueueTestSuite) TestNextClosed() { require.NoError(err) _, err = suite.queue.Next() - require.Equal(errDone, err) + require.ErrorIs(err, errDone) } func (suite *ChunkQueueTestSuite) TestRetry() { diff --git a/internal/statesync/mocks/consensusstateprovider.go b/internal/statesync/mocks/consensusstateprovider.go new file mode 100644 index 0000000000..4f12038ac5 --- /dev/null +++ b/internal/statesync/mocks/consensusstateprovider.go @@ -0,0 +1,127 @@ +// Code generated by mockery. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + types "github.com/dashpay/tenderdash/types" +) + +// ConsensusStateProvider is an autogenerated mock type for the ConsensusStateProvider type +type ConsensusStateProvider struct { + mock.Mock +} + +type ConsensusStateProvider_Expecter struct { + mock *mock.Mock +} + +func (_m *ConsensusStateProvider) EXPECT() *ConsensusStateProvider_Expecter { + return &ConsensusStateProvider_Expecter{mock: &_m.Mock} +} + +// GetCurrentHeight provides a mock function with no fields +func (_m *ConsensusStateProvider) GetCurrentHeight() int64 { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetCurrentHeight") + } + + var r0 int64 + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + return r0 +} + +// ConsensusStateProvider_GetCurrentHeight_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentHeight' +type ConsensusStateProvider_GetCurrentHeight_Call struct { + *mock.Call +} + +// GetCurrentHeight is a helper method to define mock.On call +func (_e *ConsensusStateProvider_Expecter) GetCurrentHeight() *ConsensusStateProvider_GetCurrentHeight_Call { + return &ConsensusStateProvider_GetCurrentHeight_Call{Call: _e.mock.On("GetCurrentHeight")} +} + +func (_c *ConsensusStateProvider_GetCurrentHeight_Call) Run(run func()) *ConsensusStateProvider_GetCurrentHeight_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *ConsensusStateProvider_GetCurrentHeight_Call) Return(_a0 int64) *ConsensusStateProvider_GetCurrentHeight_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConsensusStateProvider_GetCurrentHeight_Call) RunAndReturn(run func() int64) *ConsensusStateProvider_GetCurrentHeight_Call { + _c.Call.Return(run) + return _c +} + +// PublishCommitEvent provides a mock function with given fields: commit +func (_m *ConsensusStateProvider) PublishCommitEvent(commit *types.Commit) error { + ret := _m.Called(commit) + + if len(ret) == 0 { + panic("no return value specified for PublishCommitEvent") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*types.Commit) error); ok { + r0 = rf(commit) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ConsensusStateProvider_PublishCommitEvent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PublishCommitEvent' +type ConsensusStateProvider_PublishCommitEvent_Call struct { + *mock.Call +} + +// PublishCommitEvent is a helper method to define mock.On call +// - commit *types.Commit +func (_e *ConsensusStateProvider_Expecter) PublishCommitEvent(commit interface{}) *ConsensusStateProvider_PublishCommitEvent_Call { + return &ConsensusStateProvider_PublishCommitEvent_Call{Call: _e.mock.On("PublishCommitEvent", commit)} +} + +func (_c *ConsensusStateProvider_PublishCommitEvent_Call) Run(run func(commit *types.Commit)) *ConsensusStateProvider_PublishCommitEvent_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(*types.Commit)) + }) + return _c +} + +func (_c *ConsensusStateProvider_PublishCommitEvent_Call) Return(_a0 error) *ConsensusStateProvider_PublishCommitEvent_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *ConsensusStateProvider_PublishCommitEvent_Call) RunAndReturn(run func(*types.Commit) error) *ConsensusStateProvider_PublishCommitEvent_Call { + _c.Call.Return(run) + return _c +} + +// NewConsensusStateProvider creates a new instance of ConsensusStateProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConsensusStateProvider(t interface { + mock.TestingT + Cleanup(func()) +}) *ConsensusStateProvider { + mock := &ConsensusStateProvider{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/statesync/mocks/stateprovider.go b/internal/statesync/mocks/stateprovider.go index 8ac2b1544c..a4189eb08e 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/peer.go b/internal/statesync/peer.go index 589acecacc..3e1e49d3d7 100644 --- a/internal/statesync/peer.go +++ b/internal/statesync/peer.go @@ -96,7 +96,7 @@ func (p *PeerSubscriber) Stop(ctx context.Context) { // processPeerUpdate processes a PeerUpdate, returning an error upon failing to // handle the PeerUpdate or if a panic is recovered. func (p *PeerSubscriber) execute(ctx context.Context, peerUpdate p2p.PeerUpdate) error { - p.logger.Info("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + p.logger.Trace("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) handler, ok := p.handles[peerUpdate.Status] if !ok { // TODO: return error or write a log @@ -106,7 +106,7 @@ func (p *PeerSubscriber) execute(ctx context.Context, peerUpdate p2p.PeerUpdate) if err != nil { return err } - p.logger.Info("processed peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + p.logger.Trace("processed peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) return nil } diff --git a/internal/statesync/reactor.go b/internal/statesync/reactor.go index e31088ff4e..d10b78c5ed 100644 --- a/internal/statesync/reactor.go +++ b/internal/statesync/reactor.go @@ -16,12 +16,12 @@ import ( abci "github.com/dashpay/tenderdash/abci/types" "github.com/dashpay/tenderdash/config" dashcore "github.com/dashpay/tenderdash/dash/core" - "github.com/dashpay/tenderdash/internal/consensus" "github.com/dashpay/tenderdash/internal/eventbus" "github.com/dashpay/tenderdash/internal/p2p" 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" @@ -68,6 +68,9 @@ const ( // backfillSleepTime uses to sleep if no connected peers to fetch light blocks backfillSleepTime = 1 * time.Second + + // minPeers is the minimum number of peers required to start a state sync + minPeers = 2 ) func getChannelDescriptors() map[p2p.ChannelID]*p2p.ChannelDescriptor { @@ -132,7 +135,16 @@ type Reactor struct { dashCoreClient dashcore.Client - csState *consensus.State + csState ConsensusStateProvider +} + +// ConsensusStateProvider is an interface that allows the state sync reactor to +// ineract with the consensus state. It is defined to improve testability. +// +// Implemented by consensus.State +type ConsensusStateProvider interface { + PublishCommitEvent(commit *types.Commit) error + GetCurrentHeight() int64 } // NewReactor returns a reference to a new state sync reactor, which implements @@ -155,7 +167,7 @@ func NewReactor( postSyncHook func(context.Context, sm.State) error, needsStateSync bool, client dashcore.Client, - csState *consensus.State, + csState ConsensusStateProvider, ) *Reactor { r := &Reactor{ logger: logger, @@ -236,11 +248,10 @@ 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.Debug("initializing state sync state provider", "useP2P", r.cfg.UseP2P) if r.cfg.UseP2P { - if err := r.waitForEnoughPeers(ctx, 2); err != nil { + if err := r.waitForEnoughPeers(ctx, minPeers); err != nil { return err } @@ -250,8 +261,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 +270,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 +289,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 + } } } @@ -311,7 +334,7 @@ func (r *Reactor) Sync(ctx context.Context) (sm.State, error) { // We need at least two peers (for cross-referencing of light blocks) before we can // begin state sync - if err := r.waitForEnoughPeers(ctx, 2); err != nil { + if err := r.waitForEnoughPeers(ctx, minPeers); err != nil { return sm.State{}, fmt.Errorf("wait for peers: %w", err) } @@ -329,7 +352,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) } @@ -420,9 +443,12 @@ func (r *Reactor) publishCommitEvent(commit *types.Commit) error { // and time that is less or equal to the stopHeight and stopTime. The // trustedBlockID should be of the header at startHeight. func (r *Reactor) Backfill(ctx context.Context, state sm.State) error { - params := state.ConsensusParams.Evidence - stopHeight := state.LastBlockHeight - params.MaxAgeNumBlocks - stopTime := state.LastBlockTime.Add(-params.MaxAgeDuration) + // TODO: revert to using consensus params for backfill criteria after testing + // params := state.ConsensusParams.Evidence + // stopHeight := state.LastBlockHeight - params.MaxAgeNumBlocks + // stopTime := state.LastBlockTime.Add(-params.MaxAgeDuration) + stopHeight := state.LastBlockHeight - 500 + stopTime := state.LastBlockTime.Add(-24 * time.Hour) // ensure that stop height doesn't go below the initial height if stopHeight < state.InitialHeight { stopHeight = state.InitialHeight @@ -696,7 +722,7 @@ func (r *Reactor) handleSnapshotMessage(ctx context.Context, envelope *p2p.Envel "version", msg.Version) default: - return fmt.Errorf("received unknown message: %T", msg) + return fmt.Errorf("handleSnapshotMessage received unknown message: %T", msg) } return nil @@ -777,7 +803,7 @@ func (r *Reactor) handleChunkMessage(ctx context.Context, envelope *p2p.Envelope } default: - return fmt.Errorf("received unknown message: %T", msg) + return fmt.Errorf("handleChunkMessage received unknown message: %T", msg) } return nil @@ -838,7 +864,7 @@ func (r *Reactor) handleLightBlockMessage(ctx context.Context, envelope *p2p.Env } default: - return fmt.Errorf("received unknown message: %T", msg) + return fmt.Errorf("handleLightBlockMessage received unknown message: %T", msg) } return nil @@ -848,7 +874,7 @@ func (r *Reactor) handleParamsMessage(ctx context.Context, envelope *p2p.Envelop switch msg := envelope.Message.(type) { case *ssproto.ParamsRequest: r.logger.Debug("received consensus params request", "height", msg.Height) - cp, err := r.stateStore.LoadConsensusParams(int64(msg.Height)) + cp, err := r.stateStore.LoadConsensusParams(tmmath.MustConvertInt64(msg.Height)) if err != nil { r.logger.Error("failed to fetch requested consensus params", "height", msg.Height, @@ -886,7 +912,7 @@ func (r *Reactor) handleParamsMessage(ctx context.Context, envelope *p2p.Envelop } default: - return fmt.Errorf("received unknown message: %T", msg) + return fmt.Errorf("handleParamsMessage received unknown message: %T", msg) } return nil @@ -971,7 +997,7 @@ func (r *Reactor) processChannels(ctx context.Context, chanTable map[p2p.Channel // processPeerUpdate processes a PeerUpdate, returning an error upon failing to // handle the PeerUpdate or if a panic is recovered. func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpdate) { - r.logger.Info("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + r.logger.Trace("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) switch peerUpdate.Status { case p2p.PeerStatusUp: @@ -980,7 +1006,7 @@ func (r *Reactor) processPeerUpdate(ctx context.Context, peerUpdate p2p.PeerUpda r.processPeerDown(ctx, peerUpdate) } - r.logger.Info("processed peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) + r.logger.Trace("processed peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status) } func (r *Reactor) processPeerUp(ctx context.Context, peerUpdate p2p.PeerUpdate) { @@ -1038,6 +1064,12 @@ func (r *Reactor) processPeerUpdates(ctx context.Context, peerUpdates *p2p.PeerU // recentSnapshots fetches the n most recent snapshots from the app func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, error) { + // if we don't have current state, we don't return any snapshots + if r.csState == nil { + return []*snapshot{}, nil + } + currentHeight := r.csState.GetCurrentHeight() + resp, err := r.conn.ListSnapshots(ctx, &abci.RequestListSnapshots{}) if err != nil { return nil, err @@ -1063,6 +1095,14 @@ func (r *Reactor) recentSnapshots(ctx context.Context, n uint32) ([]*snapshot, e break } + // 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 +1117,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 eff91961c4..0e13fde975 100644 --- a/internal/statesync/reactor_test.go +++ b/internal/statesync/reactor_test.go @@ -21,6 +21,7 @@ import ( "github.com/dashpay/tenderdash/config" "github.com/dashpay/tenderdash/crypto" dashcore "github.com/dashpay/tenderdash/dash/core" + "github.com/dashpay/tenderdash/internal/p2p" "github.com/dashpay/tenderdash/internal/proxy" smmocks "github.com/dashpay/tenderdash/internal/state/mocks" @@ -86,6 +87,7 @@ func setup( t *testing.T, conn *clientmocks.Client, stateProvider *mocks.StateProvider, + csState ConsensusStateProvider, chBuf uint, ) *reactorTestSuite { t.Helper() @@ -187,7 +189,7 @@ func setup( nil, // post-sync-hook false, // run Sync during Start() rts.dashcoreClient, - nil, + csState, ) rts.syncer = &syncer{ @@ -224,7 +226,7 @@ func TestReactor_Sync(t *testing.T) { defer cancel() const snapshotHeight = 7 - rts := setup(ctx, t, nil, nil, 100) + rts := setup(ctx, t, nil, nil, nil, 100) chain := buildLightBlockChain(ctx, t, 1, 10, time.Now(), rts.privVal) // app accepts any snapshot rts.conn. @@ -236,6 +238,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 +281,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 @@ -290,7 +292,7 @@ func TestReactor_ChunkRequest_InvalidRequest(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - rts := setup(ctx, t, nil, nil, 2) + rts := setup(ctx, t, nil, nil, nil, 2) rts.chunkInCh <- p2p.Envelope{ From: types.NodeID("aa"), @@ -350,7 +352,7 @@ func TestReactor_ChunkRequest(t *testing.T) { ChunkId: tc.request.ChunkId, }).Return(&abci.ResponseLoadSnapshotChunk{Chunk: tc.chunk}, nil) - rts := setup(ctx, t, conn, nil, 2) + rts := setup(ctx, t, conn, nil, nil, 2) rts.chunkInCh <- p2p.Envelope{ From: types.NodeID("aa"), @@ -371,7 +373,7 @@ func TestReactor_SnapshotsRequest_InvalidRequest(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - rts := setup(ctx, t, nil, nil, 2) + rts := setup(ctx, t, nil, nil, nil, 2) rts.snapshotInCh <- p2p.Envelope{ From: types.NodeID("aa"), @@ -390,8 +392,9 @@ func TestReactor_SnapshotsRequest(t *testing.T) { testcases := map[string]struct { snapshots []*abci.Snapshot expectResponses []*ssproto.SnapshotsResponse + currentHeight int64 }{ - "no snapshots": {nil, []*ssproto.SnapshotsResponse{}}, + "no snapshots": {nil, []*ssproto.SnapshotsResponse{}, 1}, ">10 unordered snapshots": { []*abci.Snapshot{ {Height: 1, Version: 2, Hash: []byte{1, 2}, Metadata: []byte{1}}, @@ -419,25 +422,27 @@ func TestReactor_SnapshotsRequest(t *testing.T) { {Height: 1, Version: 4, Hash: []byte{1, 4}, Metadata: []byte{7}}, {Height: 1, Version: 3, Hash: []byte{1, 3}, Metadata: []byte{10}}, }, + 6, }, } ctx, cancel := context.WithCancel(context.Background()) defer cancel() for name, tc := range testcases { - tc := tc - t.Run(name, func(t *testing.T) { ctx, cancel := context.WithCancel(ctx) defer cancel() // mock ABCI connection to return local snapshots - conn := &clientmocks.Client{} + conn := clientmocks.NewClient(t) conn.On("ListSnapshots", mock.Anything, &abci.RequestListSnapshots{}).Return(&abci.ResponseListSnapshots{ Snapshots: tc.snapshots, - }, nil) + }, nil).Maybe() - rts := setup(ctx, t, conn, nil, 100) + consensusStateProvider := mocks.NewConsensusStateProvider(t) + consensusStateProvider.On("GetCurrentHeight").Return(tc.currentHeight).Maybe() + + rts := setup(ctx, t, conn, nil, consensusStateProvider, 100) rts.snapshotInCh <- p2p.Envelope{ From: types.NodeID("aa"), @@ -446,7 +451,10 @@ func TestReactor_SnapshotsRequest(t *testing.T) { } if len(tc.expectResponses) > 0 { - retryUntil(ctx, t, func() bool { return len(rts.snapshotOutCh) == len(tc.expectResponses) }, time.Second) + retryUntil(ctx, t, + func() bool { return len(rts.snapshotOutCh) == len(tc.expectResponses) }, + time.Second, + ) } responses := make([]*ssproto.SnapshotsResponse, len(tc.expectResponses)) @@ -465,7 +473,7 @@ func TestReactor_LightBlockResponse(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - rts := setup(ctx, t, nil, nil, 2) + rts := setup(ctx, t, nil, nil, nil, 2) var height int64 = 10 // generates a random header @@ -523,7 +531,7 @@ func TestReactor_BlockProviders(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - rts := setup(ctx, t, nil, nil, 2) + rts := setup(ctx, t, nil, nil, nil, 2) rts.peerUpdateCh <- p2p.PeerUpdate{ NodeID: "aa", Status: p2p.PeerStatusUp, @@ -590,7 +598,7 @@ func TestReactor_StateProviderP2P(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - rts := setup(ctx, t, nil, nil, 2) + rts := setup(ctx, t, nil, nil, nil, 2) // make syncer non nil else test won't think we are state syncing rts.reactor.syncer = rts.syncer peerA := types.NodeID(strings.Repeat("a", 2*types.NodeIDByteLength)) @@ -612,8 +620,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) { @@ -693,7 +699,7 @@ func TestReactor_Backfill(t *testing.T) { defer cancel() t.Cleanup(leaktest.CheckTimeout(t, 1*time.Minute)) - rts := setup(ctx, t, nil, nil, 21) + rts := setup(ctx, t, nil, nil, nil, 21) peers := genPeerIDs(tc.numPeers) for _, peer := range peers { @@ -764,6 +770,8 @@ func TestReactor_Backfill(t *testing.T) { // retryUntil will continue to evaluate fn and will return successfully when true // or fail when the timeout is reached. func retryUntil(ctx context.Context, t *testing.T, fn func() bool, timeout time.Duration) { + t.Helper() + ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() diff --git a/internal/statesync/stateprovider.go b/internal/statesync/stateprovider.go index b10324acc5..1a0adece92 100644 --- a/internal/statesync/stateprovider.go +++ b/internal/statesync/stateprovider.go @@ -40,8 +40,12 @@ 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) } +// stateProviderRPC is a state provider using RPC to communicate with light clients. +// Deprecated, will be removed in future. type stateProviderRPC struct { sync.Mutex // light.Client is not concurrency-safe lc *light.Client @@ -51,12 +55,12 @@ type stateProviderRPC struct { } // NewRPCStateProvider creates a new StateProvider using a light client and RPC clients. +// Deprecated, will be removed in future. func NewRPCStateProvider( ctx context.Context, chainID string, initialHeight int64, servers []string, - trustHeight int64, logger log.Logger, dashCoreClient dashcore.Client, ) (StateProvider, error) { @@ -77,8 +81,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 +126,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,17 +218,16 @@ func NewP2PStateProvider( ctx context.Context, chainID string, initialHeight int64, - trustHeight int64, providers []lightprovider.Provider, paramsSendCh p2p.Channel, logger log.Logger, dashCoreClient dashcore.Client, ) (StateProvider, error) { - if len(providers) < 2 { - return nil, fmt.Errorf("at least 2 peers are required, got %d", len(providers)) + if len(providers) < minPeers { + return nil, fmt.Errorf("at least %d peers are required, got %d", minPeers, 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 +271,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 24ce2eb0a6..3f6654e544 100644 --- a/internal/statesync/syncer.go +++ b/internal/statesync/syncer.go @@ -16,18 +16,22 @@ 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" ) const ( + // chunkTimeout is the timeout while waiting for the next chunk from the chunk queue. chunkTimeout = 2 * time.Minute // minimumDiscoveryTime is the lowest allowable time for a // SyncAny discovery time. minimumDiscoveryTime = 5 * time.Second + // chunkRequestSendTimeout is the timeout sending chunk requests to peers. + chunkRequestSendTimeout = 5 * time.Second dequeueChunkIDTimeoutDefault = 2 * time.Second ) @@ -89,7 +93,7 @@ func (s *syncer) AddChunk(chunk *chunk) (bool, error) { keyVals := []any{ "height", chunk.Height, "version", chunk.Version, - "chunk", chunk.ID, + "chunkID", chunk.ID, } added, err := s.chunkQueue.Add(chunk) if err != nil { @@ -120,6 +124,9 @@ 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, possibly duplicate or invalid", + "height", snapshot.Height, "hash", snapshot.Hash) } return added, nil } @@ -147,6 +154,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 +164,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 +174,11 @@ func (s *syncer) SyncAny( ) for { + // we loop one more time than `retries` to check if snapshots requested in previous iterations are available + 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 +189,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 +219,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 +346,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 +354,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 +363,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 +376,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 +402,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 @@ -509,31 +529,41 @@ func (s *syncer) fetchChunks(ctx context.Context, snapshot *snapshot, queue *chu } for { if queue.IsRequestQueueEmpty() { + s.logger.Debug("fetchChunks queue empty, waiting for chunk", "timeout", dequeueChunkIDTimeout) select { case <-ctx.Done(): + s.logger.Debug("fetchChunks context done on empty queue") return case <-time.After(dequeueChunkIDTimeout): + s.logger.Debug("fetchChunks queue empty, timed out", "timeout", dequeueChunkIDTimeout) continue } } ID, err := queue.Dequeue() if errors.Is(err, errQueueEmpty) { + s.logger.Debug("fetchChunks queue empty, waiting for chunk", "timeout", dequeueChunkIDTimeout, "err", err) continue } s.logger.Info("Fetching snapshot chunk", "height", snapshot.Height, "version", snapshot.Version, - "chunk", ID) + "chunkID", ID) ticker.Reset(s.retryTimeout) if err := s.requestChunk(ctx, snapshot, ID); err != nil { + s.logger.Error("failed to request snapshot chunk", "err", err, "chunkID", ID) + // retry the chunk + s.chunkQueue.Enqueue(ID) return } select { case <-queue.WaitFor(ID): // do nothing case <-ticker.C: + s.logger.Debug("chunk not received on time, retrying", + "chunkID", ID, "timeout", s.retryTimeout) s.chunkQueue.Enqueue(ID) case <-ctx.Done(): + s.logger.Debug("fetchChunks context done while waiting for chunk") return } } @@ -568,8 +598,39 @@ func (s *syncer) requestChunk(ctx context.Context, snapshot *snapshot, chunkID t ChunkId: chunkID, }, } + sCtx, cancel := context.WithTimeout(ctx, chunkRequestSendTimeout) + defer cancel() + + return s.chunkCh.Send(sCtx, 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 s.chunkCh.Send(ctx, msg) + return err } // verifyApp verifies the sync, checking the app hash, last block height and app version @@ -595,7 +656,7 @@ func (s *syncer) verifyApp(ctx context.Context, snapshot *snapshot, appVersion u return errVerifyFailed } - if uint64(resp.LastBlockHeight) != snapshot.Height { + if tmmath.MustConvertUint64(resp.LastBlockHeight) != snapshot.Height { s.logger.Error( "ABCI app reported unexpected last block height", "expected", snapshot.Height, diff --git a/internal/statesync/syncer_test.go b/internal/statesync/syncer_test.go index 00da83c9ed..bd7de2caa3 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 9afb409b21..56aa71f9d0 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,54 @@ func SafeMulInt64(a, b int64) (int64, bool) { return a * b, false } + +// Max returns the maximum value for a type T. +// +// The function panics if the type is not supported. +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 92a8f32110..98a57e1163 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 c9f6eeabc3..3329ca3ac0 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 6162aaf3b2..aff911a8b5 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 2e725ec190..30f6943097 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 4b6025e125..5aa2b7b0c2 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 5894dfa867..b38f17982b 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 d149e042a1..2273359241 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 3b45597fb9..64c5b0ce8f 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 1bd51c6224..ee73d4b46b 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 72d9051100..c96369d1af 100644 --- a/types/validator_test.go +++ b/types/validator_test.go @@ -2,12 +2,15 @@ package types import ( "context" + "encoding/base64" + "encoding/hex" "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 +114,22 @@ func TestValidatorValidateBasic(t *testing.T) { }) } } + +// TestValdiatorSetHashVectors checks if provided validator threshold pubkey and quorum hash returns expected hash +func TestValdiatorSetHashVectors(t *testing.T) { + thresholdPublicKey, err := base64.RawStdEncoding.DecodeString("gw5F5F5kFNnWFUc8woFOaxccUI+cd+ixaSS3RZT2HJlWpvoWM16YRn6sjYvbdtGH") + require.NoError(t, err) + + quorumHash, err := hex.DecodeString("703ee5bfc78765cc9e151d8dd84e30e196ababa83ac6cbdee31a88a46bba81b9") + require.NoError(t, err) + + expected := "81742F95E99EAE96ABC727FE792CECB4996205DE6BFC88AFEE1F60B96BC648B2" + + 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 e2db669794..df7d4822e8 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 )