move validation from protocol to API

This commit is contained in:
Lucas Molas 2020-09-22 20:19:31 -03:00
parent 5663b2d697
commit dcf2735836
2 changed files with 38 additions and 22 deletions

View File

@ -65,7 +65,15 @@ func NewClient(lc fx.Lifecycle, host host.Host, pmgr peermgr.MaybePeerMgr) Clien
// request options without disrupting external calls. In the future the // request options without disrupting external calls. In the future the
// consumers should be forced to use a more standardized service and // consumers should be forced to use a more standardized service and
// adhere to a single API derived from this function. // adhere to a single API derived from this function.
func (c *client) doRequest(ctx context.Context, req *Request, singlePeer *peer.ID) (*validatedResponse, error) { func (c *client) doRequest(
ctx context.Context,
req *Request,
singlePeer *peer.ID,
// In the `GetChainMessages` case, we won't request the headers but we still
// need them to check the integrity of the `CompactedMessages` in the response
// so the tipset blocks need to be provided by the caller.
tipsets []*types.TipSet,
) (*validatedResponse, error) {
// Validate request. // Validate request.
if req.Length == 0 { if req.Length == 0 {
return nil, xerrors.Errorf("invalid request of length 0") return nil, xerrors.Errorf("invalid request of length 0")
@ -116,7 +124,7 @@ func (c *client) doRequest(ctx context.Context, req *Request, singlePeer *peer.I
} }
// Process and validate response. // Process and validate response.
validRes, err := c.processResponse(req, res) validRes, err := c.processResponse(req, res, tipsets)
if err != nil { if err != nil {
log.Warnf("processing peer %s response failed: %s", log.Warnf("processing peer %s response failed: %s",
peer.String(), err) peer.String(), err)
@ -144,7 +152,7 @@ func (c *client) doRequest(ctx context.Context, req *Request, singlePeer *peer.I
// errors. Peer penalization should happen here then, before returning, so // errors. Peer penalization should happen here then, before returning, so
// we can apply the correct penalties depending on the cause of the error. // we can apply the correct penalties depending on the cause of the error.
// FIXME: Add the `peer` as argument once we implement penalties. // FIXME: Add the `peer` as argument once we implement penalties.
func (c *client) processResponse(req *Request, res *Response) (*validatedResponse, error) { func (c *client) processResponse(req *Request, res *Response, tipsets []*types.TipSet) (*validatedResponse, error) {
err := res.statusToError() err := res.statusToError()
if err != nil { if err != nil {
return nil, xerrors.Errorf("status error: %s", err) return nil, xerrors.Errorf("status error: %s", err)
@ -176,6 +184,16 @@ func (c *client) processResponse(req *Request, res *Response) (*validatedRespons
// Check for valid block sets and extract them into `TipSet`s. // Check for valid block sets and extract them into `TipSet`s.
validRes.tipsets = make([]*types.TipSet, resLength) validRes.tipsets = make([]*types.TipSet, resLength)
for i := 0; i < resLength; i++ { for i := 0; i < resLength; i++ {
if res.Chain[i] == nil {
return nil, xerrors.Errorf("response with nil tipset in pos %d", i)
}
for blockIdx, block := range res.Chain[i].Blocks {
if block == nil {
return nil, xerrors.Errorf("tipset with nil block in pos %d", blockIdx)
// FIXME: Maybe we should move this check to `NewTipSet`.
}
}
validRes.tipsets[i], err = types.NewTipSet(res.Chain[i].Blocks) validRes.tipsets[i], err = types.NewTipSet(res.Chain[i].Blocks)
if err != nil { if err != nil {
return nil, xerrors.Errorf("invalid tipset blocks at height (head - %d): %w", i, err) return nil, xerrors.Errorf("invalid tipset blocks at height (head - %d): %w", i, err)
@ -214,14 +232,16 @@ func (c *client) processResponse(req *Request, res *Response) (*validatedRespons
if err != nil { if err != nil {
return nil, err return nil, err
} }
} else {
// If we didn't request the headers they should have been provided
// by the caller.
if len(tipsets) < len(res.Chain) {
return nil, xerrors.Errorf("not enought tipsets provided for message response validation, needed %d, have %d", len(res.Chain), len(tipsets))
} }
if options.ValidateMessages {
// if the request includes target tipsets, validate against them
chain := make([]*BSTipSet, 0, resLength) chain := make([]*BSTipSet, 0, resLength)
for i, resChain := range res.Chain { for i, resChain := range res.Chain {
next := &BSTipSet{ next := &BSTipSet{
Blocks: req.TipSets[i].Blocks(), Blocks: tipsets[i].Blocks(),
Messages: resChain.Messages, Messages: resChain.Messages,
} }
chain = append(chain, next) chain = append(chain, next)
@ -290,7 +310,7 @@ func (c *client) GetBlocks(ctx context.Context, tsk types.TipSetKey, count int)
Options: Headers, Options: Headers,
} }
validRes, err := c.doRequest(ctx, req, nil) validRes, err := c.doRequest(ctx, req, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -308,7 +328,7 @@ func (c *client) GetFullTipSet(ctx context.Context, peer peer.ID, tsk types.TipS
Options: Headers | Messages, Options: Headers | Messages,
} }
validRes, err := c.doRequest(ctx, req, &peer) validRes, err := c.doRequest(ctx, req, &peer, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -335,11 +355,10 @@ func (c *client) GetChainMessages(ctx context.Context, tipsets []*types.TipSet)
req := &Request{ req := &Request{
Head: head.Cids(), Head: head.Cids(),
Length: length, Length: length,
Options: Messages | Validate, Options: Messages,
TipSets: tipsets,
} }
validRes, err := c.doRequest(ctx, req, nil) validRes, err := c.doRequest(ctx, req, nil, tipsets)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -57,8 +57,6 @@ type Request struct {
// Request options, see `Options` type for more details. Compressed // Request options, see `Options` type for more details. Compressed
// in a single `uint64` to save space. // in a single `uint64` to save space.
Options uint64 Options uint64
// Request tipsets for validation
TipSets []*types.TipSet
} }
// `Request` processed and validated to query the tipsets needed. // `Request` processed and validated to query the tipsets needed.
@ -73,7 +71,6 @@ type validatedRequest struct {
const ( const (
Headers = 1 << iota Headers = 1 << iota
Messages Messages
Validate
) )
// Decompressed options into separate struct members for easy access // Decompressed options into separate struct members for easy access
@ -81,7 +78,6 @@ const (
type parsedOptions struct { type parsedOptions struct {
IncludeHeaders bool IncludeHeaders bool
IncludeMessages bool IncludeMessages bool
ValidateMessages bool
} }
func (options *parsedOptions) noOptionsSet() bool { func (options *parsedOptions) noOptionsSet() bool {
@ -93,7 +89,6 @@ func parseOptions(optfield uint64) *parsedOptions {
return &parsedOptions{ return &parsedOptions{
IncludeHeaders: optfield&(uint64(Headers)) != 0, IncludeHeaders: optfield&(uint64(Headers)) != 0,
IncludeMessages: optfield&(uint64(Messages)) != 0, IncludeMessages: optfield&(uint64(Messages)) != 0,
ValidateMessages: optfield&(uint64(Validate)) != 0,
} }
} }
@ -144,6 +139,8 @@ func (res *Response) statusToError() error {
// FIXME: Rename. // FIXME: Rename.
type BSTipSet struct { type BSTipSet struct {
// List of blocks belonging to a single tipset to which the
// `CompactedMessages` are linked.
Blocks []*types.BlockHeader Blocks []*types.BlockHeader
Messages *CompactedMessages Messages *CompactedMessages
} }