package mock import ( "context" "errors" "fmt" "strings" "sync" "time" "github.com/tendermint/tendermint/light/provider" "github.com/tendermint/tendermint/types" ) type Mock struct { chainID string mtx sync.Mutex headers map[int64]*types.SignedHeader vals map[int64]*types.ValidatorSet evidenceToReport map[string]types.Evidence // hash => evidence latestHeight int64 } var _ provider.Provider = (*Mock)(nil) // New creates a mock provider with the given set of headers and validator // sets. func New(chainID string, headers map[int64]*types.SignedHeader, vals map[int64]*types.ValidatorSet) *Mock { height := int64(0) for h := range headers { if h > height { height = h } } return &Mock{ chainID: chainID, headers: headers, vals: vals, evidenceToReport: make(map[string]types.Evidence), latestHeight: height, } } // ChainID returns the blockchain ID. func (p *Mock) ChainID() string { return p.chainID } func (p *Mock) String() string { var headers strings.Builder for _, h := range p.headers { fmt.Fprintf(&headers, " %d:%X", h.Height, h.Hash()) } var vals strings.Builder for _, v := range p.vals { fmt.Fprintf(&vals, " %X", v.Hash()) } return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String()) } func (p *Mock) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) { p.mtx.Lock() defer p.mtx.Unlock() // allocate a window of time for contexts to be canceled select { case <-ctx.Done(): return nil, ctx.Err() case <-time.After(10 * time.Millisecond): } var lb *types.LightBlock if height > p.latestHeight { return nil, provider.ErrHeightTooHigh } if height == 0 && len(p.headers) > 0 { height = p.latestHeight } if _, ok := p.headers[height]; ok { sh := p.headers[height] vals := p.vals[height] lb = &types.LightBlock{ SignedHeader: sh, ValidatorSet: vals, } } if lb == nil { return nil, provider.ErrLightBlockNotFound } if lb.SignedHeader == nil || lb.ValidatorSet == nil { return nil, provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")} } if err := lb.ValidateBasic(lb.ChainID); err != nil { return nil, provider.ErrBadLightBlock{Reason: err} } return lb, nil } func (p *Mock) ReportEvidence(_ context.Context, ev types.Evidence) error { p.evidenceToReport[string(ev.Hash())] = ev return nil } func (p *Mock) HasEvidence(ev types.Evidence) bool { _, ok := p.evidenceToReport[string(ev.Hash())] return ok } func (p *Mock) AddLightBlock(lb *types.LightBlock) { p.mtx.Lock() defer p.mtx.Unlock() if err := lb.ValidateBasic(lb.ChainID); err != nil { panic(fmt.Sprintf("unable to add light block, err: %v", err)) } p.headers[lb.Height] = lb.SignedHeader p.vals[lb.Height] = lb.ValidatorSet if lb.Height > p.latestHeight { p.latestHeight = lb.Height } } func (p *Mock) Copy(id string) *Mock { return New(id, p.headers, p.vals) }