Commit 238764b2 authored by Rodrigo Q. Saramago's avatar Rodrigo Q. Saramago Committed by GitHub

Move cac validator to cac package (#1363)

parent be92828b
package cac
import (
"bytes"
"encoding/binary"
"errors"
......@@ -71,3 +72,19 @@ func hasher(data []byte) func([]byte) ([]byte, error) {
return hasher.Sum(nil), nil
}
}
// Valid checks whether the given chunk is a valid content-addressed chunk.
func Valid(c swarm.Chunk) bool {
data := c.Data()
if len(data) < swarm.SpanSize {
return false
}
if len(data) > swarm.ChunkSize+swarm.SpanSize {
return false
}
h := hasher(data[swarm.SpanSize:])
hash, _ := h(data[:swarm.SpanSize])
return bytes.Equal(hash, c.Address().Bytes())
}
......@@ -3,6 +3,7 @@ package cac_test
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"strings"
......@@ -101,3 +102,85 @@ func TestChunkInvariants(t *testing.T) {
}
}
}
// TestValid checks whether a chunk is a valid content-addressed chunk
func TestValid(t *testing.T) {
bmtHashOfFoo := "2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
address := swarm.MustParseHexAddress(bmtHashOfFoo)
foo := "foo"
fooLength := len(foo)
fooBytes := make([]byte, swarm.SpanSize+fooLength)
binary.LittleEndian.PutUint64(fooBytes, uint64(fooLength))
copy(fooBytes[swarm.SpanSize:], foo)
ch := swarm.NewChunk(address, fooBytes)
if !cac.Valid(ch) {
t.Fatalf("data '%s' should have validated to hash '%s'", ch.Data(), ch.Address())
}
}
/// TestInvalid checks whether a chunk is not a valid content-addressed chunk
func TestInvalid(t *testing.T) {
// Generates a chunk with the given data. No validation is performed here,
// the chunks are create as it is.
chunker := func(addr string, dataBytes []byte) swarm.Chunk {
// Decoding errors are ignored here since they will be captured
// when validating
addrBytes, _ := hex.DecodeString(addr)
address := swarm.NewAddress(addrBytes)
return swarm.NewChunk(address, dataBytes)
}
// Appends span to given input data.
dataWithSpan := func(inputData []byte) []byte {
dataLength := len(inputData)
dataBytes := make([]byte, swarm.SpanSize+dataLength)
binary.LittleEndian.PutUint64(dataBytes, uint64(dataLength))
copy(dataBytes[swarm.SpanSize:], inputData)
return dataBytes
}
for _, c := range []struct {
name string
address string
data []byte
}{
{
name: "wrong address",
address: "0000e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48",
data: dataWithSpan([]byte("foo")),
},
{
name: "empty address",
address: "",
data: dataWithSpan([]byte("foo")),
},
{
name: "zero data",
address: "anything",
data: []byte{},
},
{
name: "nil data",
address: "anything",
data: nil,
},
{
name: "small data",
address: "6251dbc53257832ae80d0e9f1cc41bd54d5b6c704c9c7349709c07fefef0aea6",
data: []byte("small"),
},
{
name: "large data",
address: "ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83",
data: []byte(strings.Repeat("a", swarm.ChunkSize+swarm.SpanSize+1)),
},
} {
t.Run(c.name, func(t *testing.T) {
ch := chunker(c.address, c.data)
if cac.Valid(ch) {
t.Fatalf("data '%s' should not have validated to hash '%s'", ch.Data(), ch.Address())
}
})
}
}
// Copyright 2020 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 content exposes content-addressed validation.
package content
import (
"bytes"
"github.com/ethersphere/bee/pkg/bmtpool"
"github.com/ethersphere/bee/pkg/swarm"
)
// Valid checks whether the given chunk is a valid content-addressed chunk.
func Valid(c swarm.Chunk) bool {
data := c.Data()
if len(data) < swarm.SpanSize {
return false
}
span := data[:swarm.SpanSize]
content := data[swarm.SpanSize:]
hasher := bmtpool.Get()
defer bmtpool.Put(hasher)
// execute hash, compare and return result
err := hasher.SetSpanBytes(span)
if err != nil {
return false
}
_, err = hasher.Write(content)
if err != nil {
return false
}
s := hasher.Sum(nil)
return bytes.Equal(s, c.Address().Bytes())
}
// Copyright 2020 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 content_test
import (
"encoding/binary"
"testing"
"github.com/ethersphere/bee/pkg/content"
"github.com/ethersphere/bee/pkg/swarm"
)
// TestValidator checks that the validator evaluates correctly
// on valid and invalid input
func TestValidator(t *testing.T) {
// generate address from pre-generated hex of 'foo' from legacy bmt
bmtHashOfFoo := "2387e8e7d8a48c2a9339c97c1dc3461a9a7aa07e994c5cb8b38fd7c1b3e6ea48"
address := swarm.MustParseHexAddress(bmtHashOfFoo)
// set up a chunk object with correct expected length prefix
// and test validation result
foo := "foo"
fooLength := len(foo)
fooBytes := make([]byte, 8+fooLength)
binary.LittleEndian.PutUint64(fooBytes, uint64(fooLength))
copy(fooBytes[8:], foo)
ch := swarm.NewChunk(address, fooBytes)
if !content.Valid(ch) {
t.Fatalf("data '%s' should have validated to hash '%s'", ch.Data(), ch.Address())
}
// now test with incorrect data
ch = swarm.NewChunk(address, fooBytes[:len(fooBytes)-1])
if content.Valid(ch) {
t.Fatalf("data '%s' should not have validated to hash '%s'", ch.Data(), ch.Address())
}
}
......@@ -17,7 +17,7 @@ import (
"time"
"github.com/ethersphere/bee/pkg/bitvector"
"github.com/ethersphere/bee/pkg/content"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/p2p/protobuf"
......@@ -219,7 +219,7 @@ func (s *Syncer) SyncInterval(ctx context.Context, peer swarm.Address, bin uint8
s.metrics.DeliveryCounter.Inc()
chunk := swarm.NewChunk(addr, delivery.Data)
if content.Valid(chunk) {
if cac.Valid(chunk) {
go s.unwrap(chunk)
} else if !soc.Valid(chunk) {
return 0, ru.Ruid, swarm.ErrInvalidChunk
......@@ -312,7 +312,7 @@ func (s *Syncer) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream) (er
}
}
time.Sleep(50 * time.Millisecond) //because of test, getting EOF w/o
time.Sleep(50 * time.Millisecond) // because of test, getting EOF w/o
return nil
}
......
......@@ -13,7 +13,7 @@ import (
"time"
"github.com/ethersphere/bee/pkg/accounting"
"github.com/ethersphere/bee/pkg/content"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/p2p/protobuf"
......@@ -109,7 +109,7 @@ func (ps *PushSync) handler(ctx context.Context, p p2p.Peer, stream p2p.Stream)
chunk := swarm.NewChunk(swarm.NewAddress(ch.Address), ch.Data)
if content.Valid(chunk) {
if cac.Valid(chunk) {
if ps.unwrap != nil {
go ps.unwrap(chunk)
}
......
......@@ -16,7 +16,7 @@ import (
"time"
"github.com/ethersphere/bee/pkg/accounting"
"github.com/ethersphere/bee/pkg/content"
"github.com/ethersphere/bee/pkg/cac"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/p2p/protobuf"
......@@ -242,7 +242,7 @@ func (s *Service) retrieveChunk(ctx context.Context, addr swarm.Address, sp *ski
s.metrics.TotalRetrieved.Inc()
chunk = swarm.NewChunk(addr, d.Data)
if !content.Valid(chunk) {
if !cac.Valid(chunk) {
if !soc.Valid(chunk) {
s.metrics.InvalidChunkRetrieved.Inc()
s.metrics.TotalErrors.Inc()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment