github.com/celestiaorg/celestia-node@v0.15.0-beta.1/share/getters/ipld.go (about) 1 package getters 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 "sync/atomic" 9 10 "github.com/ipfs/boxo/blockservice" 11 "go.opentelemetry.io/otel/attribute" 12 "go.opentelemetry.io/otel/trace" 13 14 "github.com/celestiaorg/rsmt2d" 15 16 "github.com/celestiaorg/celestia-node/header" 17 "github.com/celestiaorg/celestia-node/libs/utils" 18 "github.com/celestiaorg/celestia-node/share" 19 "github.com/celestiaorg/celestia-node/share/eds" 20 "github.com/celestiaorg/celestia-node/share/eds/byzantine" 21 "github.com/celestiaorg/celestia-node/share/ipld" 22 ) 23 24 var _ share.Getter = (*IPLDGetter)(nil) 25 26 // IPLDGetter is a share.Getter that retrieves shares from the bitswap network. Result caching is 27 // handled by the provided blockservice. A blockservice session will be created for retrieval if the 28 // passed context is wrapped with WithSession. 29 type IPLDGetter struct { 30 rtrv *eds.Retriever 31 bServ blockservice.BlockService 32 } 33 34 // NewIPLDGetter creates a new share.Getter that retrieves shares from the bitswap network. 35 func NewIPLDGetter(bServ blockservice.BlockService) *IPLDGetter { 36 return &IPLDGetter{ 37 rtrv: eds.NewRetriever(bServ), 38 bServ: bServ, 39 } 40 } 41 42 // GetShare gets a single share at the given EDS coordinates from the bitswap network. 43 func (ig *IPLDGetter) GetShare(ctx context.Context, header *header.ExtendedHeader, row, col int) (share.Share, error) { 44 var err error 45 ctx, span := tracer.Start(ctx, "ipld/get-share", trace.WithAttributes( 46 attribute.Int("row", row), 47 attribute.Int("col", col), 48 )) 49 defer func() { 50 utils.SetStatusAndEnd(span, err) 51 }() 52 53 dah := header.DAH 54 upperBound := len(dah.RowRoots) 55 if row >= upperBound || col >= upperBound { 56 err := share.ErrOutOfBounds 57 span.RecordError(err) 58 return nil, err 59 } 60 root, leaf := ipld.Translate(dah, row, col) 61 62 // wrap the blockservice in a session if it has been signaled in the context. 63 blockGetter := getGetter(ctx, ig.bServ) 64 s, err := ipld.GetShare(ctx, blockGetter, root, leaf, len(dah.RowRoots)) 65 if errors.Is(err, ipld.ErrNodeNotFound) { 66 // convert error to satisfy getter interface contract 67 err = share.ErrNotFound 68 } 69 if err != nil { 70 return nil, fmt.Errorf("getter/ipld: failed to retrieve share: %w", err) 71 } 72 73 return s, nil 74 } 75 76 func (ig *IPLDGetter) GetEDS( 77 ctx context.Context, 78 header *header.ExtendedHeader, 79 ) (eds *rsmt2d.ExtendedDataSquare, err error) { 80 ctx, span := tracer.Start(ctx, "ipld/get-eds") 81 defer func() { 82 utils.SetStatusAndEnd(span, err) 83 }() 84 85 // rtrv.Retrieve calls shares.GetShares until enough shares are retrieved to reconstruct the EDS 86 eds, err = ig.rtrv.Retrieve(ctx, header.DAH) 87 if errors.Is(err, ipld.ErrNodeNotFound) { 88 // convert error to satisfy getter interface contract 89 err = share.ErrNotFound 90 } 91 var errByz *byzantine.ErrByzantine 92 if errors.As(err, &errByz) { 93 return nil, err 94 } 95 if err != nil { 96 return nil, fmt.Errorf("getter/ipld: failed to retrieve eds: %w", err) 97 } 98 return eds, nil 99 } 100 101 func (ig *IPLDGetter) GetSharesByNamespace( 102 ctx context.Context, 103 header *header.ExtendedHeader, 104 namespace share.Namespace, 105 ) (shares share.NamespacedShares, err error) { 106 ctx, span := tracer.Start(ctx, "ipld/get-shares-by-namespace", trace.WithAttributes( 107 attribute.String("namespace", namespace.String()), 108 )) 109 defer func() { 110 utils.SetStatusAndEnd(span, err) 111 }() 112 113 if err = namespace.ValidateForData(); err != nil { 114 return nil, err 115 } 116 117 // wrap the blockservice in a session if it has been signaled in the context. 118 blockGetter := getGetter(ctx, ig.bServ) 119 shares, err = eds.CollectSharesByNamespace(ctx, blockGetter, header.DAH, namespace) 120 if errors.Is(err, ipld.ErrNodeNotFound) { 121 // convert error to satisfy getter interface contract 122 err = share.ErrNotFound 123 } 124 if err != nil { 125 return nil, fmt.Errorf("getter/ipld: failed to retrieve shares by namespace: %w", err) 126 } 127 return shares, nil 128 } 129 130 var sessionKey = &session{} 131 132 // session is a struct that can optionally be passed by context to the share.Getter methods using 133 // WithSession to indicate that a blockservice session should be created. 134 type session struct { 135 sync.Mutex 136 atomic.Pointer[blockservice.Session] 137 ctx context.Context 138 } 139 140 // WithSession stores an empty session in the context, indicating that a blockservice session should 141 // be created. 142 func WithSession(ctx context.Context) context.Context { 143 return context.WithValue(ctx, sessionKey, &session{ctx: ctx}) 144 } 145 146 func getGetter(ctx context.Context, service blockservice.BlockService) blockservice.BlockGetter { 147 s, ok := ctx.Value(sessionKey).(*session) 148 if !ok { 149 return service 150 } 151 152 val := s.Load() 153 if val != nil { 154 return val 155 } 156 157 s.Lock() 158 defer s.Unlock() 159 val = s.Load() 160 if val == nil { 161 val = blockservice.NewSession(s.ctx, service) 162 s.Store(val) 163 } 164 return val 165 }