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 }