github.com/ethersphere/bee/v2@v2.2.0/pkg/feeds/feed.go (about)

     1  // Copyright 2021 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package feeds implements generic interfaces and methods for time-based feeds
     6  // indexing schemes are implemented in subpackages
     7  // - epochs
     8  // - sequence
     9  package feeds
    10  
    11  import (
    12  	"encoding"
    13  	"errors"
    14  	"fmt"
    15  	"strings"
    16  
    17  	"github.com/ethereum/go-ethereum/common"
    18  	"github.com/ethersphere/bee/v2/pkg/crypto"
    19  	"github.com/ethersphere/bee/v2/pkg/soc"
    20  	"github.com/ethersphere/bee/v2/pkg/storage"
    21  	"github.com/ethersphere/bee/v2/pkg/swarm"
    22  )
    23  
    24  var ErrFeedTypeNotFound = errors.New("no such feed type")
    25  
    26  // Factory creates feed lookups for different types of feeds.
    27  type Factory interface {
    28  	NewLookup(Type, *Feed) (Lookup, error)
    29  }
    30  
    31  // Type enumerates the time-based feed types
    32  type Type int
    33  
    34  const (
    35  	Sequence Type = iota
    36  	Epoch
    37  )
    38  
    39  func (t Type) String() string {
    40  	switch t {
    41  	case Sequence:
    42  		return "Sequence"
    43  	case Epoch:
    44  		return "Epoch"
    45  	default:
    46  		return ""
    47  	}
    48  }
    49  
    50  // FromString constructs the type from a string
    51  func (t *Type) FromString(s string) error {
    52  	switch s = strings.ToLower(s); s {
    53  	case "sequence":
    54  		*t = Sequence
    55  	case "epoch":
    56  		*t = Epoch
    57  	default:
    58  		return ErrFeedTypeNotFound
    59  	}
    60  	return nil
    61  }
    62  
    63  type id struct {
    64  	topic []byte
    65  	index []byte
    66  }
    67  
    68  var _ encoding.BinaryMarshaler = (*id)(nil)
    69  
    70  func (i *id) MarshalBinary() ([]byte, error) {
    71  	return crypto.LegacyKeccak256(append(append([]byte{}, i.topic...), i.index...))
    72  }
    73  
    74  // Feed is representing an epoch based feed
    75  type Feed struct {
    76  	Topic []byte
    77  	Owner common.Address
    78  }
    79  
    80  // New constructs an epoch based feed from a keccak256 digest of a plaintext
    81  // topic and an ether address.
    82  func New(topic []byte, owner common.Address) *Feed {
    83  	return &Feed{topic, owner}
    84  }
    85  
    86  // Index is the interface for feed implementations.
    87  type Index interface {
    88  	encoding.BinaryMarshaler
    89  	Next(last int64, at uint64) Index
    90  	fmt.Stringer
    91  }
    92  
    93  // Update represents an update instance of a feed, i.e., pairing of a Feed with an Epoch
    94  type Update struct {
    95  	*Feed
    96  	index Index
    97  }
    98  
    99  // Update called on a feed with an index and returns an Update
   100  func (f *Feed) Update(index Index) *Update {
   101  	return &Update{f, index}
   102  }
   103  
   104  // NewUpdate creates an update from an index, timestamp, payload and signature
   105  func NewUpdate(f *Feed, idx Index, timestamp int64, payload, sig []byte) (swarm.Chunk, error) {
   106  	id, err := f.Update(idx).Id()
   107  	if err != nil {
   108  		return nil, fmt.Errorf("update: %w", err)
   109  	}
   110  	cac, err := toChunk(uint64(timestamp), payload)
   111  	if err != nil {
   112  		return nil, fmt.Errorf("toChunk: %w", err)
   113  	}
   114  
   115  	ss, err := soc.NewSigned(id, cac, f.Owner.Bytes(), sig)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("new signed soc: %w", err)
   118  	}
   119  
   120  	ch, err := ss.Chunk()
   121  	if err != nil {
   122  		return nil, fmt.Errorf("new chunk: %w", err)
   123  	}
   124  
   125  	if !soc.Valid(ch) {
   126  		return nil, storage.ErrInvalidChunk
   127  	}
   128  	return ch, nil
   129  }
   130  
   131  // Id calculates the identifier if a  feed update to be used in single owner chunks
   132  func (u *Update) Id() ([]byte, error) {
   133  	return Id(u.Topic, u.index)
   134  }
   135  
   136  // Id calculates the feed id from a topic and an index
   137  func Id(topic []byte, index Index) ([]byte, error) {
   138  	indexBytes, err := index.MarshalBinary()
   139  	if err != nil {
   140  		return nil, err
   141  	}
   142  	i := &id{topic, indexBytes}
   143  	return i.MarshalBinary()
   144  }
   145  
   146  // Address calculates the soc address of a feed update
   147  func (u *Update) Address() (swarm.Address, error) {
   148  	var addr swarm.Address
   149  	i, err := u.Id()
   150  	if err != nil {
   151  		return addr, err
   152  	}
   153  	return soc.CreateAddress(i, u.Owner[:])
   154  }