Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/beekeeper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ env:
SETUP_CONTRACT_IMAGE: "ethersphere/bee-localchain"
SETUP_CONTRACT_IMAGE_TAG: "0.9.2"
BEELOCAL_BRANCH: "main"
BEEKEEPER_BRANCH: "master"
BEEKEEPER_BRANCH: "feat/feed-wrapping"
BEEKEEPER_METRICS_ENABLED: false
REACHABILITY_OVERRIDE_PUBLIC: true
BATCHFACTOR_OVERRIDE_PUBLIC: 2
Expand Down
6 changes: 6 additions & 0 deletions openapi/Swarm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,12 @@ paths:
$ref: "SwarmCommon.yaml#/components/schemas/FeedType"
required: false
description: "Feed indexing scheme (default: sequence)"
- in: header
name: legacy-feed-resolution
schema:
type: boolean
required: false
description: "Resolves feed payloads in legacy structure (timestamp, content address)."
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmOnlyRootChunkParameter"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmCache"
- $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyStrategyParameter"
Expand Down
1 change: 1 addition & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ const (
SwarmSocSignatureHeader = "Swarm-Soc-Signature"
SwarmFeedIndexHeader = "Swarm-Feed-Index"
SwarmFeedIndexNextHeader = "Swarm-Feed-Index-Next"
SwarmLegacyFeedResolve = "Swarm-Feed-Legacy-Resolve"
SwarmOnlyRootChunk = "Swarm-Only-Root-Chunk"
SwarmCollectionHeader = "Swarm-Collection"
SwarmPostageBatchIdHeader = "Swarm-Postage-Batch-Id"
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/bzz.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ FETCH:
jsonhttp.NotFound(w, "no update found")
return
}
wc, err := feeds.GetWrappedChunk(ctx, s.storer.Download(cache), ch)
wc, err := feeds.GetWrappedChunk(ctx, s.storer.Download(cache), ch, false)
if err != nil {
logger.Debug("bzz download: mapStructure feed update failed", "error", err)
logger.Error(nil, "bzz download: mapStructure feed update failed")
Expand Down
16 changes: 11 additions & 5 deletions pkg/api/bzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/ethersphere/bee/v2/pkg/manifest"
mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock"
mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock"
testingsoc "github.com/ethersphere/bee/v2/pkg/soc/testing"
"github.com/ethersphere/bee/v2/pkg/storage/inmemchunkstore"
mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock"
"github.com/ethersphere/bee/v2/pkg/swarm"
Expand Down Expand Up @@ -794,16 +795,22 @@ func TestFeedIndirection(t *testing.T) {
t.Fatalf("expected file reference, did not got any")
}

// now use the "content" to mock the feed lookup
// get root chunk of data
// and wrap it in a feed
rootCh, err := storer.ChunkStore().Get(context.Background(), resp.Reference)
if err != nil {
t.Fatal(err)
}
socRootCh := testingsoc.GenerateMockSOC(t, rootCh.Data()[swarm.SpanSize:]).Chunk()

// now use the "content" root chunk to mock the feed lookup
// also, use the mocked mantaray chunks that unmarshal
// into a real manifest with the mocked feed values when
// called from the bzz endpoint. then call the bzz endpoint with
// the pregenerated feed root manifest hash

feedUpdate := toChunk(t, 121212, resp.Reference.Bytes())

var (
look = newMockLookup(-1, 0, feedUpdate, nil, &id{}, nil)
look = newMockLookup(-1, 0, socRootCh, nil, &id{}, nil)
factory = newMockFactory(look)
bzzDownloadResource = func(addr, path string) string { return "/bzz/" + addr + "/" + path }
ctx = context.Background()
Expand All @@ -813,7 +820,6 @@ func TestFeedIndirection(t *testing.T) {
Logger: logger,
Feeds: factory,
})
err := storer.Cache().Put(ctx, feedUpdate)
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/api/feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ func (s *Service) feedGetHandler(w http.ResponseWriter, r *http.Request) {
}

headers := struct {
OnlyRootChunk bool `map:"Swarm-Only-Root-Chunk"`
OnlyRootChunk bool `map:"Swarm-Only-Root-Chunk"`
LegacyFeedResolve bool `map:"Swarm-Feed-Legacy-Resolve"`
}{}
if response := s.mapStructure(r.Header, &headers); response != nil {
response("invalid header params", logger, w)
Expand Down Expand Up @@ -102,7 +103,7 @@ func (s *Service) feedGetHandler(w http.ResponseWriter, r *http.Request) {
return
}

wc, err := feeds.GetWrappedChunk(r.Context(), s.storer.Download(false), ch)
wc, err := feeds.GetWrappedChunk(r.Context(), s.storer.Download(false), ch, headers.LegacyFeedResolve)
if err != nil {
logger.Error(nil, "wrapped chunk cannot be retrieved")
jsonhttp.NotFound(w, "wrapped chunk cannot be retrieved")
Expand Down
8 changes: 6 additions & 2 deletions pkg/api/feed_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func TestFeed_Get(t *testing.T) {

jsonhttptest.Request(t, client, http.MethodGet, feedResource(ownerString, "aabbcc", "12"), http.StatusOK,
jsonhttptest.WithExpectedResponse(mockWrappedCh.Data()[swarm.SpanSize:]),
jsonhttptest.WithRequestHeader(api.SwarmLegacyFeedResolve, "true"),
jsonhttptest.WithExpectedResponseHeader(api.SwarmFeedIndexHeader, hex.EncodeToString(idBytes)),
jsonhttptest.WithExpectedResponseHeader(api.AccessControlExposeHeaders, api.SwarmFeedIndexHeader),
jsonhttptest.WithExpectedResponseHeader(api.AccessControlExposeHeaders, api.SwarmFeedIndexNextHeader),
Expand All @@ -86,7 +87,7 @@ func TestFeed_Get(t *testing.T) {
)
})

t.Run("latest", func(t *testing.T) {
t.Run("latest with legacy payload", func(t *testing.T) {
t.Parallel()

var (
Expand All @@ -103,6 +104,7 @@ func TestFeed_Get(t *testing.T) {
)

jsonhttptest.Request(t, client, http.MethodGet, feedResource(ownerString, "aabbcc", ""), http.StatusOK,
jsonhttptest.WithRequestHeader(api.SwarmLegacyFeedResolve, "true"),
jsonhttptest.WithExpectedResponse(mockWrappedCh.Data()[swarm.SpanSize:]),
jsonhttptest.WithExpectedContentLength(len(mockWrappedCh.Data()[swarm.SpanSize:])),
jsonhttptest.WithExpectedResponseHeader(api.SwarmFeedIndexHeader, hex.EncodeToString(idBytes)),
Expand Down Expand Up @@ -161,7 +163,9 @@ func TestFeed_Get(t *testing.T) {
})
)

jsonhttptest.Request(t, client, http.MethodGet, feedResource(ownerString, "aabbcc", ""), http.StatusNotFound)
jsonhttptest.Request(t, client, http.MethodGet, feedResource(ownerString, "aabbcc", ""), http.StatusNotFound,
jsonhttptest.WithRequestHeader(api.SwarmLegacyFeedResolve, "true"),
)
})

t.Run("bigger payload than one chunk", func(t *testing.T) {
Expand Down
19 changes: 9 additions & 10 deletions pkg/feeds/getter.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (f *Getter) Get(ctx context.Context, i Index) (swarm.Chunk, error) {
return f.getter.Get(ctx, addr)
}

func GetWrappedChunk(ctx context.Context, getter storage.Getter, ch swarm.Chunk) (swarm.Chunk, error) {
func GetWrappedChunk(ctx context.Context, getter storage.Getter, ch swarm.Chunk, legacyResolve bool) (swarm.Chunk, error) {
wc, err := FromChunk(ch)
if err != nil {
return nil, err
Expand All @@ -59,16 +59,15 @@ func GetWrappedChunk(ctx context.Context, getter storage.Getter, ch swarm.Chunk)
// possible values right now:
// unencrypted ref: span+timestamp+ref => 8+8+32=48
// encrypted ref: span+timestamp+ref+decryptKey => 8+8+64=80
ref, err := legacyPayload(wc)
if err != nil {
if errors.Is(err, errNotLegacyPayload) {
return wc, nil
if legacyResolve {
ref, err := legacyPayload(wc)
if err != nil {
return nil, err
}
wc, err = getter.Get(ctx, ref)
if err != nil {
return nil, err
}
return nil, err
}
wc, err = getter.Get(ctx, ref)
if err != nil {
return nil, err
}

return wc, nil
Expand Down
37 changes: 27 additions & 10 deletions pkg/feeds/getter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ import (
func TestGetWrappedChunk(t *testing.T) {
storer := mockstorer.New()

data := []byte("data")
// new format (wraps chunk)
ch := soctesting.GenerateMockSOC(t, []byte("data")).Chunk()
wch, err := GetWrappedChunk(context.Background(), storer.ChunkStore(), ch)
ch := soctesting.GenerateMockSOC(t, data).Chunk()
wch, err := GetWrappedChunk(context.Background(), storer.ChunkStore(), ch, false)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(wch.Data()[8:], []byte("data")) {
if !bytes.Equal(wch.Data()[8:], data) {
t.Fatal("data mismatch")
}

// old format
err = storer.Put(context.Background(), wch)
if err != nil {
t.Fatal(err)
}

tt := []struct {
name string
addr []byte
Expand All @@ -49,19 +55,30 @@ func TestGetWrappedChunk(t *testing.T) {
binary.BigEndian.PutUint64(timestamp, 1)
ch = soctesting.GenerateMockSOC(t, append(timestamp, tc.addr...)).Chunk()

err = storer.Put(context.Background(), wch)
wch, err = GetWrappedChunk(context.Background(), storer.ChunkStore(), ch, true)
if err != nil {
t.Fatal(err)
}

wch, err = GetWrappedChunk(context.Background(), storer.ChunkStore(), ch)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(wch.Data()[8:], []byte("data")) {
if !bytes.Equal(wch.Data()[8:], data) {
t.Fatal("data mismatch")
}
})
}

t.Run("returns feed legacy payload", func(t *testing.T) {
timestamp := make([]byte, 8)
binary.BigEndian.PutUint64(timestamp, 1)
feedChData := append(timestamp, wch.Address().Bytes()...)
ch = soctesting.GenerateMockSOC(t, feedChData).Chunk()

wch, err = GetWrappedChunk(context.Background(), storer.ChunkStore(), ch, false)
if err != nil {
t.Fatal(err)
}

if !bytes.Equal(wch.Data()[8:], feedChData) {
t.Fatal("data should be similar as old legacy feed payload format.")
}
})
}
2 changes: 1 addition & 1 deletion pkg/node/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ func getLatestSnapshot(
return nil, err
}

return feeds.GetWrappedChunk(ctx, st, u)
return feeds.GetWrappedChunk(ctx, st, u, false)
}

func batchStoreExists(s storage.StateStorer) (bool, error) {
Expand Down
Loading