Remove multihash from Swarm bzz:// for Feeds (#18175)
This commit is contained in:
parent
0699287440
commit
2714e8f091
|
@ -16,7 +16,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/multihash"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||||
colorable "github.com/mattn/go-colorable"
|
colorable "github.com/mattn/go-colorable"
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
|
@ -36,7 +35,7 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
||||||
|
|
||||||
generateEndpoints(scheme, cluster, from, to)
|
generateEndpoints(scheme, cluster, from, to)
|
||||||
|
|
||||||
log.Info("generating and uploading MRUs to " + endpoints[0] + " and syncing")
|
log.Info("generating and uploading feeds to " + endpoints[0] + " and syncing")
|
||||||
|
|
||||||
// create a random private key to sign updates with and derive the address
|
// create a random private key to sign updates with and derive the address
|
||||||
pkFile, err := ioutil.TempFile("", "swarm-feed-smoke-test")
|
pkFile, err := ioutil.TempFile("", "swarm-feed-smoke-test")
|
||||||
|
@ -218,8 +217,7 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
multihashHex := hexutil.Encode(multihash.ToMultihash(hashBytes))
|
multihashHex := hexutil.Encode(hashBytes)
|
||||||
|
|
||||||
fileHash, err := digest(f)
|
fileHash, err := digest(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -7,7 +7,6 @@ swarm
|
||||||
├── fuse ────────────────── @jmozah, @holisticode
|
├── fuse ────────────────── @jmozah, @holisticode
|
||||||
├── grafana_dashboards ──── @nonsense
|
├── grafana_dashboards ──── @nonsense
|
||||||
├── metrics ─────────────── @nonsense, @holisticode
|
├── metrics ─────────────── @nonsense, @holisticode
|
||||||
├── multihash ───────────── @nolash
|
|
||||||
├── network ─────────────── ethersphere
|
├── network ─────────────── ethersphere
|
||||||
│ ├── bitvector ───────── @zelig, @janos, @gbalint
|
│ ├── bitvector ───────── @zelig, @janos, @gbalint
|
||||||
│ ├── priorityqueue ───── @zelig, @janos, @gbalint
|
│ ├── priorityqueue ───── @zelig, @janos, @gbalint
|
||||||
|
|
|
@ -42,7 +42,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/swarm/log"
|
"github.com/ethereum/go-ethereum/swarm/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/multihash"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
"github.com/ethereum/go-ethereum/swarm/spancontext"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||||
|
@ -417,7 +416,7 @@ func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage
|
||||||
return reader, mimeType, status, nil, err
|
return reader, mimeType, status, nil, err
|
||||||
}
|
}
|
||||||
// get the data of the update
|
// get the data of the update
|
||||||
_, rsrcData, err := a.feed.GetContent(entry.Feed)
|
_, contentAddr, err := a.feed.GetContent(entry.Feed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiGetNotFound.Inc(1)
|
apiGetNotFound.Inc(1)
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
|
@ -425,23 +424,23 @@ func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage
|
||||||
return reader, mimeType, status, nil, err
|
return reader, mimeType, status, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract multihash
|
// extract content hash
|
||||||
decodedMultihash, err := multihash.FromMultihash(rsrcData)
|
if len(contentAddr) != storage.AddressLength {
|
||||||
if err != nil {
|
|
||||||
apiGetInvalid.Inc(1)
|
apiGetInvalid.Inc(1)
|
||||||
status = http.StatusUnprocessableEntity
|
status = http.StatusUnprocessableEntity
|
||||||
log.Warn("invalid multihash in feed update", "err", err)
|
errorMessage := fmt.Sprintf("invalid swarm hash in feed update. Expected %d bytes. Got %d", storage.AddressLength, len(contentAddr))
|
||||||
return reader, mimeType, status, nil, err
|
log.Warn(errorMessage)
|
||||||
|
return reader, mimeType, status, nil, errors.New(errorMessage)
|
||||||
}
|
}
|
||||||
manifestAddr = storage.Address(decodedMultihash)
|
manifestAddr = storage.Address(contentAddr)
|
||||||
log.Trace("feed update contains multihash", "key", manifestAddr)
|
log.Trace("feed update contains swarm hash", "key", manifestAddr)
|
||||||
|
|
||||||
// get the manifest the multihash digest points to
|
// get the manifest the swarm hash points to
|
||||||
trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, NOOPDecrypt)
|
trie, err := loadManifest(ctx, a.fileStore, manifestAddr, nil, NOOPDecrypt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiGetNotFound.Inc(1)
|
apiGetNotFound.Inc(1)
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
log.Warn(fmt.Sprintf("loadManifestTrie (feed update multihash) error: %v", err))
|
log.Warn(fmt.Sprintf("loadManifestTrie (feed update) error: %v", err))
|
||||||
return reader, mimeType, status, nil, err
|
return reader, mimeType, status, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,8 +450,8 @@ func (a *API) Get(ctx context.Context, decrypt DecryptFunc, manifestAddr storage
|
||||||
if entry == nil {
|
if entry == nil {
|
||||||
status = http.StatusNotFound
|
status = http.StatusNotFound
|
||||||
apiGetNotFound.Inc(1)
|
apiGetNotFound.Inc(1)
|
||||||
err = fmt.Errorf("manifest (feed update multihash) entry for '%s' not found", path)
|
err = fmt.Errorf("manifest (feed update) entry for '%s' not found", path)
|
||||||
log.Trace("manifest (feed update multihash) entry not found", "key", manifestAddr, "path", path)
|
log.Trace("manifest (feed update) entry not found", "key", manifestAddr, "path", path)
|
||||||
return reader, mimeType, status, nil, err
|
return reader, mimeType, status, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,13 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed/lookup"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http"
|
swarmhttp "github.com/ethereum/go-ethereum/swarm/api/http"
|
||||||
"github.com/ethereum/go-ethereum/swarm/multihash"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -368,58 +368,99 @@ func newTestSigner() (*feed.GenericSigner, error) {
|
||||||
return feed.NewGenericSigner(privKey), nil
|
return feed.NewGenericSigner(privKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// test the transparent resolving of multihash feed updates with bzz:// scheme
|
// Test the transparent resolving of feed updates with bzz:// scheme
|
||||||
//
|
//
|
||||||
// first upload data, and store the multihash to the resulting manifest in a feed update
|
// First upload data to bzz:, and store the Swarm hash to the resulting manifest in a feed update.
|
||||||
// retrieving the update with the multihash should return the manifest pointing directly to the data
|
// This effectively uses a feed to store a pointer to content rather than the content itself
|
||||||
|
// Retrieving the update with the Swarm hash should return the manifest pointing directly to the data
|
||||||
// and raw retrieve of that hash should return the data
|
// and raw retrieve of that hash should return the data
|
||||||
func TestClientCreateFeedMultihash(t *testing.T) {
|
func TestClientBzzWithFeed(t *testing.T) {
|
||||||
|
|
||||||
signer, _ := newTestSigner()
|
signer, _ := newTestSigner()
|
||||||
|
|
||||||
|
// Initialize a Swarm test server
|
||||||
srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
|
srv := swarmhttp.NewTestSwarmServer(t, serverFunc, nil)
|
||||||
client := NewClient(srv.URL)
|
swarmClient := NewClient(srv.URL)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
// add the data our multihash aliased manifest will point to
|
// put together some data for our test:
|
||||||
databytes := []byte("bar")
|
dataBytes := []byte(`
|
||||||
|
//
|
||||||
|
// Create some data our manifest will point to. Data that could be very big and wouldn't fit in a feed update.
|
||||||
|
// So what we are going to do is upload it to Swarm bzz:// and obtain a **manifest hash** pointing to it:
|
||||||
|
//
|
||||||
|
// MANIFEST HASH --> DATA
|
||||||
|
//
|
||||||
|
// Then, we store that **manifest hash** into a Swarm Feed update. Once we have done this,
|
||||||
|
// we can use the **feed manifest hash** in bzz:// instead, this way: bzz://feed-manifest-hash.
|
||||||
|
//
|
||||||
|
// FEED MANIFEST HASH --> MANIFEST HASH --> DATA
|
||||||
|
//
|
||||||
|
// Given that we can update the feed at any time with a new **manifest hash** but the **feed manifest hash**
|
||||||
|
// stays constant, we have effectively created a fixed address to changing content. (Applause)
|
||||||
|
//
|
||||||
|
// FEED MANIFEST HASH (the same) --> MANIFEST HASH(2) --> DATA(2)
|
||||||
|
//
|
||||||
|
`)
|
||||||
|
|
||||||
swarmHash, err := client.UploadRaw(bytes.NewReader(databytes), int64(len(databytes)), false)
|
// Create a virtual File out of memory containing the above data
|
||||||
if err != nil {
|
f := &File{
|
||||||
t.Fatalf("Error uploading raw test data: %s", err)
|
ReadCloser: ioutil.NopCloser(bytes.NewReader(dataBytes)),
|
||||||
|
ManifestEntry: api.ManifestEntry{
|
||||||
|
ContentType: "text/plain",
|
||||||
|
Mode: 0660,
|
||||||
|
Size: int64(len(dataBytes)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
s := common.FromHex(swarmHash)
|
// upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded.
|
||||||
mh := multihash.ToMultihash(s)
|
manifestAddressHex, err := swarmClient.Upload(f, "", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating manifest: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// our feed topic
|
// convert the hex-encoded manifest hash to a 32-byte slice
|
||||||
topic, _ := feed.NewTopic("foo.eth", nil)
|
manifestAddress := common.FromHex(manifestAddressHex)
|
||||||
|
|
||||||
createRequest := feed.NewFirstRequest(topic)
|
if len(manifestAddress) != storage.AddressLength {
|
||||||
|
t.Fatalf("Something went wrong. Got a hash of an unexpected length. Expected %d bytes. Got %d", storage.AddressLength, len(manifestAddress))
|
||||||
|
}
|
||||||
|
|
||||||
createRequest.SetData(mh)
|
// Now create a **feed manifest**. For that, we need a topic:
|
||||||
if err := createRequest.Sign(signer); err != nil {
|
topic, _ := feed.NewTopic("interesting topic indeed", nil)
|
||||||
|
|
||||||
|
// Build a feed request to update data
|
||||||
|
request := feed.NewFirstRequest(topic)
|
||||||
|
|
||||||
|
// Put the 32-byte address of the manifest into the feed update
|
||||||
|
request.SetData(manifestAddress)
|
||||||
|
|
||||||
|
// Sign the update
|
||||||
|
if err := request.Sign(signer); err != nil {
|
||||||
t.Fatalf("Error signing update: %s", err)
|
t.Fatalf("Error signing update: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
feedManifestHash, err := client.CreateFeedWithManifest(createRequest)
|
// Publish the update and at the same time request a **feed manifest** to be created
|
||||||
|
feedManifestAddressHex, err := swarmClient.CreateFeedWithManifest(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error creating feed manifest: %s", err)
|
t.Fatalf("Error creating feed manifest: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctManifestAddrHex := "bb056a5264c295c2b0f613c8409b9c87ce9d71576ace02458160df4cc894210b"
|
// Check we have received the exact **feed manifest** to be expected
|
||||||
if feedManifestHash != correctManifestAddrHex {
|
// given the topic and user signing the updates:
|
||||||
t.Fatalf("Response feed manifest mismatch, expected '%s', got '%s'", correctManifestAddrHex, feedManifestHash)
|
correctFeedManifestAddrHex := "747c402e5b9dc715a25a4393147512167bab018a007fad7cdcd9adc7fce1ced2"
|
||||||
|
if feedManifestAddressHex != correctFeedManifestAddrHex {
|
||||||
|
t.Fatalf("Response feed manifest mismatch, expected '%s', got '%s'", correctFeedManifestAddrHex, feedManifestAddressHex)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check we get a not found error when trying to get feed updates with a made-up manifest
|
// Check we get a not found error when trying to get feed updates with a made-up manifest
|
||||||
_, err = client.QueryFeed(nil, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
|
_, err = swarmClient.QueryFeed(nil, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
|
||||||
if err != ErrNoFeedUpdatesFound {
|
if err != ErrNoFeedUpdatesFound {
|
||||||
t.Fatalf("Expected to receive ErrNoFeedUpdatesFound error. Got: %s", err)
|
t.Fatalf("Expected to receive ErrNoFeedUpdatesFound error. Got: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reader, err := client.QueryFeed(nil, correctManifestAddrHex)
|
// If we query the feed directly we should get **manifest hash** back:
|
||||||
|
reader, err := swarmClient.QueryFeed(nil, correctFeedManifestAddrHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error retrieving feed updates: %s", err)
|
t.Fatalf("Error retrieving feed updates: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -428,10 +469,27 @@ func TestClientCreateFeedMultihash(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(mh, gotData) {
|
|
||||||
t.Fatalf("Expected: %v, got %v", mh, gotData)
|
//Check that indeed the **manifest hash** is retrieved
|
||||||
|
if !bytes.Equal(manifestAddress, gotData) {
|
||||||
|
t.Fatalf("Expected: %v, got %v", manifestAddress, gotData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now the final test we were looking for: Use bzz://<feed-manifest> and that should resolve all manifests
|
||||||
|
// and return the original data directly:
|
||||||
|
f, err = swarmClient.Download(feedManifestAddressHex, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
gotData, err = ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we get back the original data:
|
||||||
|
if !bytes.Equal(dataBytes, gotData) {
|
||||||
|
t.Fatalf("Expected: %v, got %v", manifestAddress, gotData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestClientCreateUpdateFeed will check that feeds can be created and updated via the HTTP client.
|
// TestClientCreateUpdateFeed will check that feeds can be created and updated via the HTTP client.
|
||||||
|
|
|
@ -45,7 +45,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/swarm/api"
|
"github.com/ethereum/go-ethereum/swarm/api"
|
||||||
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
swarm "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||||
"github.com/ethereum/go-ethereum/swarm/multihash"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||||
|
@ -69,60 +68,91 @@ func newTestSigner() (*feed.GenericSigner, error) {
|
||||||
return feed.NewGenericSigner(privKey), nil
|
return feed.NewGenericSigner(privKey), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// test the transparent resolving of multihash-containing feed updates with bzz:// scheme
|
// Test the transparent resolving of feed updates with bzz:// scheme
|
||||||
//
|
//
|
||||||
// first upload data, and store the multihash to the resulting manifest in a feed update
|
// First upload data to bzz:, and store the Swarm hash to the resulting manifest in a feed update.
|
||||||
// retrieving the update with the multihash should return the manifest pointing directly to the data
|
// This effectively uses a feed to store a pointer to content rather than the content itself
|
||||||
|
// Retrieving the update with the Swarm hash should return the manifest pointing directly to the data
|
||||||
// and raw retrieve of that hash should return the data
|
// and raw retrieve of that hash should return the data
|
||||||
func TestBzzFeedMultihash(t *testing.T) {
|
func TestBzzWithFeed(t *testing.T) {
|
||||||
|
|
||||||
signer, _ := newTestSigner()
|
signer, _ := newTestSigner()
|
||||||
|
|
||||||
|
// Initialize Swarm test server
|
||||||
srv := NewTestSwarmServer(t, serverFunc, nil)
|
srv := NewTestSwarmServer(t, serverFunc, nil)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
// add the data our multihash aliased manifest will point to
|
// put together some data for our test:
|
||||||
databytes := "bar"
|
dataBytes := []byte(`
|
||||||
testBzzUrl := fmt.Sprintf("%s/bzz:/", srv.URL)
|
//
|
||||||
resp, err := http.Post(testBzzUrl, "text/plain", bytes.NewReader([]byte(databytes)))
|
// Create some data our manifest will point to. Data that could be very big and wouldn't fit in a feed update.
|
||||||
|
// So what we are going to do is upload it to Swarm bzz:// and obtain a **manifest hash** pointing to it:
|
||||||
|
//
|
||||||
|
// MANIFEST HASH --> DATA
|
||||||
|
//
|
||||||
|
// Then, we store that **manifest hash** into a Swarm Feed update. Once we have done this,
|
||||||
|
// we can use the **feed manifest hash** in bzz:// instead, this way: bzz://feed-manifest-hash.
|
||||||
|
//
|
||||||
|
// FEED MANIFEST HASH --> MANIFEST HASH --> DATA
|
||||||
|
//
|
||||||
|
// Given that we can update the feed at any time with a new **manifest hash** but the **feed manifest hash**
|
||||||
|
// stays constant, we have effectively created a fixed address to changing content. (Applause)
|
||||||
|
//
|
||||||
|
// FEED MANIFEST HASH (the same) --> MANIFEST HASH(2) --> DATA(2) ...
|
||||||
|
//
|
||||||
|
`)
|
||||||
|
|
||||||
|
// POST data to bzz and get back a content-addressed **manifest hash** pointing to it.
|
||||||
|
resp, err := http.Post(fmt.Sprintf("%s/bzz:/", srv.URL), "text/plain", bytes.NewReader([]byte(dataBytes)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("err %s", resp.Status)
|
t.Fatalf("err %s", resp.Status)
|
||||||
}
|
}
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
manifestAddressHex, err := ioutil.ReadAll(resp.Body)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
s := common.FromHex(string(b))
|
|
||||||
mh := multihash.ToMultihash(s)
|
|
||||||
|
|
||||||
log.Info("added data", "manifest", string(b), "data", common.ToHex(mh))
|
manifestAddress := common.FromHex(string(manifestAddressHex))
|
||||||
|
|
||||||
topic, _ := feed.NewTopic("foo.eth", nil)
|
log.Info("added data", "manifest", string(manifestAddressHex))
|
||||||
|
|
||||||
|
// At this point we have uploaded the data and have a manifest pointing to it
|
||||||
|
// Now store that manifest address in a feed update.
|
||||||
|
// We also want a feed manifest, so we can use it to refer to the feed.
|
||||||
|
|
||||||
|
// First, create a topic for our feed:
|
||||||
|
topic, _ := feed.NewTopic("interesting topic indeed", nil)
|
||||||
|
|
||||||
|
// Create a feed update request:
|
||||||
updateRequest := feed.NewFirstRequest(topic)
|
updateRequest := feed.NewFirstRequest(topic)
|
||||||
|
|
||||||
updateRequest.SetData(mh)
|
// Store the **manifest address** as data into the feed update.
|
||||||
|
updateRequest.SetData(manifestAddress)
|
||||||
|
|
||||||
|
// Sign the update
|
||||||
if err := updateRequest.Sign(signer); err != nil {
|
if err := updateRequest.Sign(signer); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Info("added data", "manifest", string(b), "data", common.ToHex(mh))
|
log.Info("added data", "data", common.ToHex(manifestAddress))
|
||||||
|
|
||||||
testUrl, err := url.Parse(fmt.Sprintf("%s/bzz-feed:/", srv.URL))
|
// Build the feed update http request:
|
||||||
|
feedUpdateURL, err := url.Parse(fmt.Sprintf("%s/bzz-feed:/", srv.URL))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
query := testUrl.Query()
|
query := feedUpdateURL.Query()
|
||||||
body := updateRequest.AppendValues(query) // this adds all query parameters and returns the data to be posted
|
body := updateRequest.AppendValues(query) // this adds all query parameters and returns the data to be posted
|
||||||
query.Set("manifest", "1") // indicate we want a manifest back
|
query.Set("manifest", "1") // indicate we want a feed manifest back
|
||||||
testUrl.RawQuery = query.Encode()
|
feedUpdateURL.RawQuery = query.Encode()
|
||||||
|
|
||||||
// create the multihash update
|
// submit the feed update request to Swarm
|
||||||
resp, err = http.Post(testUrl.String(), "application/octet-stream", bytes.NewReader(body))
|
resp, err = http.Post(feedUpdateURL.String(), "application/octet-stream", bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -130,24 +160,25 @@ func TestBzzFeedMultihash(t *testing.T) {
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("err %s", resp.Status)
|
t.Fatalf("err %s", resp.Status)
|
||||||
}
|
}
|
||||||
b, err = ioutil.ReadAll(resp.Body)
|
|
||||||
|
feedManifestAddressHex, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
rsrcResp := &storage.Address{}
|
feedManifestAddress := &storage.Address{}
|
||||||
err = json.Unmarshal(b, rsrcResp)
|
err = json.Unmarshal(feedManifestAddressHex, feedManifestAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("data %s could not be unmarshaled: %v", b, err)
|
t.Fatalf("data %s could not be unmarshaled: %v", feedManifestAddressHex, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
correctManifestAddrHex := "bb056a5264c295c2b0f613c8409b9c87ce9d71576ace02458160df4cc894210b"
|
correctManifestAddrHex := "747c402e5b9dc715a25a4393147512167bab018a007fad7cdcd9adc7fce1ced2"
|
||||||
if rsrcResp.Hex() != correctManifestAddrHex {
|
if feedManifestAddress.Hex() != correctManifestAddrHex {
|
||||||
t.Fatalf("Response feed manifest address mismatch, expected '%s', got '%s'", correctManifestAddrHex, rsrcResp.Hex())
|
t.Fatalf("Response feed manifest address mismatch, expected '%s', got '%s'", correctManifestAddrHex, feedManifestAddress.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
// get bzz manifest transparent feed update resolve
|
// get bzz manifest transparent feed update resolve
|
||||||
testBzzUrl = fmt.Sprintf("%s/bzz:/%s", srv.URL, rsrcResp)
|
getBzzURL := fmt.Sprintf("%s/bzz:/%s", srv.URL, feedManifestAddress)
|
||||||
resp, err = http.Get(testBzzUrl)
|
resp, err = http.Get(getBzzURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -155,12 +186,12 @@ func TestBzzFeedMultihash(t *testing.T) {
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
t.Fatalf("err %s", resp.Status)
|
t.Fatalf("err %s", resp.Status)
|
||||||
}
|
}
|
||||||
b, err = ioutil.ReadAll(resp.Body)
|
retrievedData, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(b, []byte(databytes)) {
|
if !bytes.Equal(retrievedData, []byte(dataBytes)) {
|
||||||
t.Fatalf("retrieved data mismatch, expected %x, got %x", databytes, b)
|
t.Fatalf("retrieved data mismatch, expected %x, got %x", dataBytes, retrievedData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,7 +276,8 @@ func TestBzzFeed(t *testing.T) {
|
||||||
t.Fatalf("Expected manifest Feed '%s', got '%s'", correctFeedHex, manifest.Entries[0].Feed.Hex())
|
t.Fatalf("Expected manifest Feed '%s', got '%s'", correctFeedHex, manifest.Entries[0].Feed.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
// get bzz manifest transparent feed update resolve
|
// take the chance to have bzz: crash on resolving a feed update that does not contain
|
||||||
|
// a swarm hash:
|
||||||
testBzzUrl := fmt.Sprintf("%s/bzz:/%s", srv.URL, rsrcResp)
|
testBzzUrl := fmt.Sprintf("%s/bzz:/%s", srv.URL, rsrcResp)
|
||||||
resp, err = http.Get(testBzzUrl)
|
resp, err = http.Get(testBzzUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -253,7 +285,7 @@ func TestBzzFeed(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if resp.StatusCode == http.StatusOK {
|
if resp.StatusCode == http.StatusOK {
|
||||||
t.Fatal("Expected error status since feed update does not contain multihash. Received 200 OK")
|
t.Fatal("Expected error status since feed update does not contain a Swarm hash. Received 200 OK")
|
||||||
}
|
}
|
||||||
_, err = ioutil.ReadAll(resp.Body)
|
_, err = ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package multihash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultMultihashLength = 32
|
|
||||||
defaultMultihashTypeCode = 0x1b
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
multihashTypeCode uint8
|
|
||||||
MultihashLength = defaultMultihashLength
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
multihashTypeCode = defaultMultihashTypeCode
|
|
||||||
MultihashLength = defaultMultihashLength
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if valid swarm multihash
|
|
||||||
func isSwarmMultihashType(code uint8) bool {
|
|
||||||
return code == multihashTypeCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMultihashLength returns the digest length of the provided multihash
|
|
||||||
// It will fail if the multihash is not a valid swarm mulithash
|
|
||||||
func GetMultihashLength(data []byte) (int, int, error) {
|
|
||||||
cursor := 0
|
|
||||||
typ, c := binary.Uvarint(data)
|
|
||||||
if c <= 0 {
|
|
||||||
return 0, 0, errors.New("unreadable hashtype field")
|
|
||||||
}
|
|
||||||
if !isSwarmMultihashType(uint8(typ)) {
|
|
||||||
return 0, 0, fmt.Errorf("hash code %x is not a swarm hashtype", typ)
|
|
||||||
}
|
|
||||||
cursor += c
|
|
||||||
hashlength, c := binary.Uvarint(data[cursor:])
|
|
||||||
if c <= 0 {
|
|
||||||
return 0, 0, errors.New("unreadable length field")
|
|
||||||
}
|
|
||||||
cursor += c
|
|
||||||
|
|
||||||
// we cheekily assume hashlength < maxint
|
|
||||||
inthashlength := int(hashlength)
|
|
||||||
if len(data[c:]) < inthashlength {
|
|
||||||
return 0, 0, errors.New("length mismatch")
|
|
||||||
}
|
|
||||||
return inthashlength, cursor, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromMulithash returns the digest portion of the multihash
|
|
||||||
// It will fail if the multihash is not a valid swarm multihash
|
|
||||||
func FromMultihash(data []byte) ([]byte, error) {
|
|
||||||
hashLength, _, err := GetMultihashLength(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[len(data)-hashLength:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToMulithash wraps the provided digest data with a swarm mulithash header
|
|
||||||
func ToMultihash(hashData []byte) []byte {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
b := make([]byte, 8)
|
|
||||||
c := binary.PutUvarint(b, uint64(multihashTypeCode))
|
|
||||||
buf.Write(b[:c])
|
|
||||||
c = binary.PutUvarint(b, uint64(len(hashData)))
|
|
||||||
buf.Write(b[:c])
|
|
||||||
buf.Write(hashData)
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Copyright 2018 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package multihash
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// parse multihash, and check that invalid multihashes fail
|
|
||||||
func TestCheckMultihash(t *testing.T) {
|
|
||||||
hashbytes := make([]byte, 32)
|
|
||||||
c, err := rand.Read(hashbytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if c < 32 {
|
|
||||||
t.Fatal("short read")
|
|
||||||
}
|
|
||||||
|
|
||||||
expected := ToMultihash(hashbytes)
|
|
||||||
|
|
||||||
l, hl, _ := GetMultihashLength(expected)
|
|
||||||
if l != 32 {
|
|
||||||
t.Fatalf("expected length %d, got %d", 32, l)
|
|
||||||
} else if hl != 2 {
|
|
||||||
t.Fatalf("expected header length %d, got %d", 2, hl)
|
|
||||||
}
|
|
||||||
if _, _, err := GetMultihashLength(expected[1:]); err == nil {
|
|
||||||
t.Fatal("expected failure on corrupt header")
|
|
||||||
}
|
|
||||||
if _, _, err := GetMultihashLength(expected[:len(expected)-2]); err == nil {
|
|
||||||
t.Fatal("expected failure on short content")
|
|
||||||
}
|
|
||||||
dh, _ := FromMultihash(expected)
|
|
||||||
if !bytes.Equal(dh, hashbytes) {
|
|
||||||
t.Fatalf("expected content hash %x, got %x", hashbytes, dh)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue