github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/light/provider/mock/mock.go (about)

     1  package mock
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/line/ostracon/light/provider"
    12  	"github.com/line/ostracon/types"
    13  )
    14  
    15  type Mock struct {
    16  	chainID string
    17  
    18  	mtx              sync.Mutex
    19  	headers          map[int64]*types.SignedHeader
    20  	vals             map[int64]*types.ValidatorSet
    21  	evidenceToReport map[string]types.Evidence // hash => evidence
    22  	latestHeight     int64
    23  }
    24  
    25  var _ provider.Provider = (*Mock)(nil)
    26  
    27  // New creates a mock provider with the given set of headers and validator
    28  // sets.
    29  func New(
    30  	chainID string,
    31  	headers map[int64]*types.SignedHeader,
    32  	vals map[int64]*types.ValidatorSet,
    33  ) *Mock {
    34  	height := int64(0)
    35  	for h := range headers {
    36  		if h > height {
    37  			height = h
    38  		}
    39  	}
    40  	return &Mock{
    41  		chainID:          chainID,
    42  		headers:          headers,
    43  		vals:             vals,
    44  		evidenceToReport: make(map[string]types.Evidence),
    45  		latestHeight:     height,
    46  	}
    47  }
    48  
    49  // ChainID returns the blockchain ID.
    50  func (p *Mock) ChainID() string {
    51  	return p.chainID
    52  }
    53  
    54  func (p *Mock) String() string {
    55  	var headers strings.Builder
    56  	for _, h := range p.headers {
    57  		fmt.Fprintf(&headers, " %d:%X", h.Height, h.Hash())
    58  	}
    59  
    60  	var vals strings.Builder
    61  	for _, v := range p.vals {
    62  		fmt.Fprintf(&vals, " %X", v.Hash())
    63  	}
    64  
    65  	return fmt.Sprintf("Mock{headers: %s, vals: %v}", headers.String(), vals.String())
    66  }
    67  
    68  func (p *Mock) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) {
    69  	p.mtx.Lock()
    70  	defer p.mtx.Unlock()
    71  
    72  	// allocate a window of time for contexts to be canceled
    73  	select {
    74  	case <-ctx.Done():
    75  		return nil, ctx.Err()
    76  	case <-time.After(10 * time.Millisecond):
    77  	}
    78  
    79  	var lb *types.LightBlock
    80  
    81  	if height > p.latestHeight {
    82  		return nil, provider.ErrHeightTooHigh
    83  	}
    84  
    85  	if height == 0 && len(p.headers) > 0 {
    86  		height = p.latestHeight
    87  	}
    88  
    89  	if _, ok := p.headers[height]; ok {
    90  		sh := p.headers[height]
    91  		vals := p.vals[height]
    92  		lb = &types.LightBlock{
    93  			SignedHeader: sh,
    94  			ValidatorSet: vals,
    95  		}
    96  	}
    97  	if lb == nil {
    98  		return nil, provider.ErrLightBlockNotFound
    99  	}
   100  	if lb.SignedHeader == nil || lb.ValidatorSet == nil {
   101  		return nil, provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")}
   102  	}
   103  	if err := lb.ValidateBasic(lb.ChainID); err != nil {
   104  		return nil, provider.ErrBadLightBlock{Reason: err}
   105  	}
   106  	return lb, nil
   107  }
   108  
   109  func (p *Mock) ReportEvidence(_ context.Context, ev types.Evidence) error {
   110  	p.evidenceToReport[string(ev.Hash())] = ev
   111  	return nil
   112  }
   113  
   114  func (p *Mock) HasEvidence(ev types.Evidence) bool {
   115  	_, ok := p.evidenceToReport[string(ev.Hash())]
   116  	return ok
   117  }
   118  
   119  func (p *Mock) AddLightBlock(lb *types.LightBlock) {
   120  	p.mtx.Lock()
   121  	defer p.mtx.Unlock()
   122  
   123  	if err := lb.ValidateBasic(lb.ChainID); err != nil {
   124  		panic(fmt.Sprintf("unable to add light block, err: %v", err))
   125  	}
   126  	p.headers[lb.Height] = lb.SignedHeader
   127  	p.vals[lb.Height] = lb.ValidatorSet
   128  	if lb.Height > p.latestHeight {
   129  		p.latestHeight = lb.Height
   130  	}
   131  }
   132  
   133  func (p *Mock) Copy(id string) *Mock {
   134  	return New(id, p.headers, p.vals)
   135  }