Remove multihash from Swarm bzz:// for Feeds (#18175)

pull/18185/head
Javier Peletier 6 years ago committed by Anton Evangelatov
parent 0699287440
commit 2714e8f091
  1. 6
      cmd/swarm/swarm-smoke/feed_upload_and_sync.go
  2. 1
      swarm/OWNERS
  3. 25
      swarm/api/api.go
  4. 112
      swarm/api/client/client_test.go
  5. 104
      swarm/api/http/server_test.go
  6. 92
      swarm/multihash/multihash.go
  7. 53
      swarm/multihash/multihash_test.go

@ -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(`
//
swarmHash, err := client.UploadRaw(bytes.NewReader(databytes), int64(len(databytes)), false) // 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)
//
`)
// Create a virtual File out of memory containing the above data
f := &File{
ReadCloser: ioutil.NopCloser(bytes.NewReader(dataBytes)),
ManifestEntry: api.ManifestEntry{
ContentType: "text/plain",
Mode: 0660,
Size: int64(len(dataBytes)),
},
}
// upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded.
manifestAddressHex, err := swarmClient.Upload(f, "", false)
if err != nil { if err != nil {
t.Fatalf("Error uploading raw test data: %s", err) t.Fatalf("Error creating manifest: %s", err)
} }
s := common.FromHex(swarmHash) // convert the hex-encoded manifest hash to a 32-byte slice
mh := multihash.ToMultihash(s) manifestAddress := common.FromHex(manifestAddressHex)
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))
}
// our feed topic // Now create a **feed manifest**. For that, we need a topic:
topic, _ := feed.NewTopic("foo.eth", nil) topic, _ := feed.NewTopic("interesting topic indeed", nil)
createRequest := feed.NewFirstRequest(topic) // Build a feed request to update data
request := feed.NewFirstRequest(topic)
createRequest.SetData(mh) // Put the 32-byte address of the manifest into the feed update
if err := createRequest.Sign(signer); err != nil { 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…
Cancel
Save