feed.go 3.05 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// Copyright 2021 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package feeds implements generic interfaces and methods for time-based feeds
// indexing schemes are implemented in subpackages
// - epochs
// - sequence
package feeds

import (
	"encoding"
13 14 15
	"errors"
	"fmt"
	"strings"
16 17 18 19

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethersphere/bee/pkg/crypto"
	"github.com/ethersphere/bee/pkg/soc"
20
	"github.com/ethersphere/bee/pkg/storage"
21 22 23
	"github.com/ethersphere/bee/pkg/swarm"
)

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
var ErrFeedTypeNotFound = errors.New("no such feed type")

// Factory creates feed lookups for different types of feeds.
type Factory interface {
	NewLookup(Type, *Feed) (Lookup, error)
}

type Type int

const (
	Sequence Type = iota
	Epoch
)

func (t Type) String() string {
	switch t {
	case Sequence:
		return "Sequence"
	case Epoch:
		return "Epoch"
	default:
		return ""
	}
}

func (t *Type) FromString(s string) error {
	switch s = strings.ToLower(s); s {
	case "sequence":
		*t = Sequence
	case "epoch":
		*t = Epoch
	default:
		return ErrFeedTypeNotFound
	}
	return nil
}

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
type id struct {
	topic []byte
	index []byte
}

var _ encoding.BinaryMarshaler = (*id)(nil)

func (i *id) MarshalBinary() ([]byte, error) {
	return crypto.LegacyKeccak256(append(i.topic, i.index...))
}

// Feed is representing an epoch based feed
type Feed struct {
	Topic []byte
	Owner common.Address
}

78 79 80 81
// New constructs an epoch based feed from a keccak256 digest of a plaintext
// topic and an ether address.
func New(topic []byte, owner common.Address) *Feed {
	return &Feed{topic, owner}
82 83
}

84
// Index is the interface for feed implementations.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
type Index interface {
	encoding.BinaryMarshaler
}

// Update represents an update instance of a feed, i.e., pairing of a Feed with an Epoch
type Update struct {
	*Feed
	index Index
}

// Update called on a feed with an index and returns an Update
func (f *Feed) Update(index Index) *Update {
	return &Update{f, index}
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
func NewUpdate(f *Feed, idx Index, timestamp int64, payload []byte, sig []byte) (swarm.Chunk, error) {
	id, err := f.Update(idx).Id()
	if err != nil {
		return nil, fmt.Errorf("update: %w", err)
	}
	cac, err := toChunk(uint64(timestamp), payload)
	if err != nil {
		return nil, fmt.Errorf("toChunk: %w", err)
	}

	ch, err := soc.NewSignedChunk(id, cac, f.Owner.Bytes(), sig)
	if err != nil {
		return nil, fmt.Errorf("new chunk: %w", err)
	}
	if !soc.Valid(ch) {
		return nil, storage.ErrInvalidChunk
	}
	return ch, nil
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
// Id calculates the identifier if a  feed update to be used in single owner chunks
func (u *Update) Id() ([]byte, error) {
	index, err := u.index.MarshalBinary()
	if err != nil {
		return nil, err
	}
	i := &id{u.Topic, index}
	return i.MarshalBinary()
}

// Address calculates the soc address of a feed update
func (u *Update) Address() (swarm.Address, error) {
	var addr swarm.Address
	i, err := u.Id()
	if err != nil {
		return addr, err
	}
	owner, err := soc.NewOwner(u.Owner[:])
	if err != nil {
		return addr, err
	}
	return soc.CreateAddress(i, owner)
}