types.go 9.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// Copyright 2016 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 storage

import (
	"bytes"
21
	"context"
22
	"crypto"
E
ethersphere 已提交
23 24
	"crypto/rand"
	"encoding/binary"
25 26 27
	"fmt"
	"hash"
	"io"
B
Balint Gabor 已提交
28
	"io/ioutil"
29 30 31

	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto/sha3"
E
ethersphere 已提交
32
	"github.com/ethereum/go-ethereum/swarm/bmt"
B
Balint Gabor 已提交
33
	ch "github.com/ethereum/go-ethereum/swarm/chunk"
34 35
)

E
ethersphere 已提交
36
const MaxPO = 16
B
Balint Gabor 已提交
37
const AddressLength = 32
E
ethersphere 已提交
38

39
type Hasher func() hash.Hash
40
type SwarmHasher func() SwarmHash
41 42 43 44 45

// Peer is the recorded as Source on the chunk
// should probably not be here? but network should wrap chunk object
type Peer interface{}

E
ethersphere 已提交
46
type Address []byte
47

E
ethersphere 已提交
48 49
func (a Address) Size() uint {
	return uint(len(a))
50 51
}

E
ethersphere 已提交
52 53
func (a Address) isEqual(y Address) bool {
	return bytes.Equal(a, y)
54 55
}

E
ethersphere 已提交
56
func (a Address) bits(i, j uint) uint {
57 58
	ii := i >> 3
	jj := i & 7
E
ethersphere 已提交
59
	if ii >= a.Size() {
60 61 62 63
		return 0
	}

	if jj+j <= 8 {
E
ethersphere 已提交
64
		return uint((a[ii] >> jj) & ((1 << j) - 1))
65 66
	}

E
ethersphere 已提交
67
	res := uint(a[ii] >> jj)
68 69 70 71 72
	jj = 8 - jj
	j -= jj
	for j != 0 {
		ii++
		if j < 8 {
E
ethersphere 已提交
73
			res += uint(a[ii]&((1<<j)-1)) << jj
74 75
			return res
		}
E
ethersphere 已提交
76
		res += uint(a[ii]) << jj
77 78 79 80 81 82
		jj += 8
		j -= 8
	}
	return res
}

83 84 85 86 87 88 89 90 91 92 93 94 95
// Proximity(x, y) returns the proximity order of the MSB distance between x and y
//
// The distance metric MSB(x, y) of two equal length byte sequences x an y is the
// value of the binary integer cast of the x^y, ie., x and y bitwise xor-ed.
// the binary cast is big endian: most significant bit first (=MSB).
//
// Proximity(x, y) is a discrete logarithmic scaling of the MSB distance.
// It is defined as the reverse rank of the integer part of the base 2
// logarithm of the distance.
// It is calculated by counting the number of common leading zeros in the (MSB)
// binary representation of the x^y.
//
// (0 farthest, 255 closest, 256 self)
E
ethersphere 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
func Proximity(one, other []byte) (ret int) {
	b := (MaxPO-1)/8 + 1
	if b > len(one) {
		b = len(one)
	}
	m := 8
	for i := 0; i < b; i++ {
		oxo := one[i] ^ other[i]
		if i == b-1 {
			m = MaxPO % 8
		}
		for j := 0; j < m; j++ {
			if (oxo>>uint8(7-j))&0x01 != 0 {
				return i*8 + j
			}
		}
	}
	return MaxPO
}

func IsZeroAddr(addr Address) bool {
	return len(addr) == 0 || bytes.Equal(addr, ZeroAddr)
118 119
}

E
ethersphere 已提交
120
var ZeroAddr = Address(common.Hash{}.Bytes())
121

122
func MakeHashFunc(hash string) SwarmHasher {
123 124
	switch hash {
	case "SHA256":
125
		return func() SwarmHash { return &HashWithLength{crypto.SHA256.New()} }
126
	case "SHA3":
127 128 129 130
		return func() SwarmHash { return &HashWithLength{sha3.NewKeccak256()} }
	case "BMT":
		return func() SwarmHash {
			hasher := sha3.NewKeccak256
131
			hasherSize := hasher().Size()
B
Balint Gabor 已提交
132
			segmentCount := ch.DefaultSize / hasherSize
133
			pool := bmt.NewTreePool(hasher, segmentCount, bmt.PoolSize)
134 135
			return bmt.New(pool)
		}
136 137 138 139
	}
	return nil
}

E
ethersphere 已提交
140 141
func (a Address) Hex() string {
	return fmt.Sprintf("%064x", []byte(a[:]))
142 143
}

E
ethersphere 已提交
144 145 146
func (a Address) Log() string {
	if len(a[:]) < 8 {
		return fmt.Sprintf("%x", []byte(a[:]))
147
	}
E
ethersphere 已提交
148
	return fmt.Sprintf("%016x", []byte(a[:8]))
149 150
}

E
ethersphere 已提交
151
func (a Address) String() string {
152
	return fmt.Sprintf("%064x", []byte(a))
153 154
}

E
ethersphere 已提交
155 156
func (a Address) MarshalJSON() (out []byte, err error) {
	return []byte(`"` + a.String() + `"`), nil
157 158
}

E
ethersphere 已提交
159
func (a *Address) UnmarshalJSON(value []byte) error {
160
	s := string(value)
E
ethersphere 已提交
161
	*a = make([]byte, 32)
162
	h := common.Hex2Bytes(s[1 : len(s)-1])
E
ethersphere 已提交
163
	copy(*a, h)
164 165 166
	return nil
}

E
ethersphere 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
type AddressCollection []Address

func NewAddressCollection(l int) AddressCollection {
	return make(AddressCollection, l)
}

func (c AddressCollection) Len() int {
	return len(c)
}

func (c AddressCollection) Less(i, j int) bool {
	return bytes.Compare(c[i], c[j]) == -1
}

func (c AddressCollection) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
183 184
}

B
Balint Gabor 已提交
185 186 187 188 189 190 191
// Chunk interface implemented by context.Contexts and data chunks
type Chunk interface {
	Address() Address
	Payload() []byte
	SpanBytes() []byte
	Span() int64
	Data() []byte
192 193
}

B
Balint Gabor 已提交
194 195 196 197 198
type chunk struct {
	addr  Address
	sdata []byte
	span  int64
}
E
ethersphere 已提交
199

B
Balint Gabor 已提交
200 201 202 203 204 205
func NewChunk(addr Address, data []byte) *chunk {
	return &chunk{
		addr:  addr,
		sdata: data,
		span:  -1,
	}
E
ethersphere 已提交
206
}
207

B
Balint Gabor 已提交
208 209 210
func (c *chunk) Address() Address {
	return c.addr
}
211

B
Balint Gabor 已提交
212 213
func (c *chunk) SpanBytes() []byte {
	return c.sdata[:8]
E
ethersphere 已提交
214 215
}

B
Balint Gabor 已提交
216 217 218
func (c *chunk) Span() int64 {
	if c.span == -1 {
		c.span = int64(binary.LittleEndian.Uint64(c.sdata[:8]))
E
ethersphere 已提交
219
	}
B
Balint Gabor 已提交
220
	return c.span
E
ethersphere 已提交
221
}
222

B
Balint Gabor 已提交
223 224
func (c *chunk) Data() []byte {
	return c.sdata
E
ethersphere 已提交
225
}
226

B
Balint Gabor 已提交
227 228
func (c *chunk) Payload() []byte {
	return c.sdata[8:]
229 230
}

B
Balint Gabor 已提交
231 232 233
// String() for pretty printing
func (self *chunk) String() string {
	return fmt.Sprintf("Address: %v TreeSize: %v Chunksize: %v", self.addr.Log(), self.span, len(self.sdata))
234 235
}

B
Balint Gabor 已提交
236
func GenerateRandomChunk(dataSize int64) Chunk {
E
ethersphere 已提交
237
	hasher := MakeHashFunc(DefaultHash)()
B
Balint Gabor 已提交
238 239 240 241 242 243 244
	sdata := make([]byte, dataSize+8)
	rand.Read(sdata[8:])
	binary.LittleEndian.PutUint64(sdata[:8], uint64(dataSize))
	hasher.ResetWithLength(sdata[:8])
	hasher.Write(sdata[8:])
	return NewChunk(hasher.Sum(nil), sdata)
}
E
ethersphere 已提交
245

B
Balint Gabor 已提交
246 247 248 249 250 251 252
func GenerateRandomChunks(dataSize int64, count int) (chunks []Chunk) {
	if dataSize > ch.DefaultSize {
		dataSize = ch.DefaultSize
	}
	for i := 0; i < count; i++ {
		ch := GenerateRandomChunk(ch.DefaultSize)
		chunks = append(chunks, ch)
E
ethersphere 已提交
253 254
	}
	return chunks
255 256
}

B
Balint Gabor 已提交
257 258 259 260 261 262 263 264 265 266
func GenerateRandomData(l int) (r io.Reader, slice []byte) {
	slice, err := ioutil.ReadAll(io.LimitReader(rand.Reader, int64(l)))
	if err != nil {
		panic("rand error")
	}
	// log.Warn("generate random data", "len", len(slice), "data", common.Bytes2Hex(slice))
	r = io.LimitReader(bytes.NewReader(slice), int64(l))
	return r, slice
}

267 268
// Size, Seek, Read, ReadAt
type LazySectionReader interface {
269 270
	Context() context.Context
	Size(context.Context, chan bool) (int64, error)
271 272 273 274 275 276 277 278 279
	io.Seeker
	io.Reader
	io.ReaderAt
}

type LazyTestSectionReader struct {
	*io.SectionReader
}

280
func (r *LazyTestSectionReader) Size(context.Context, chan bool) (int64, error) {
E
ethersphere 已提交
281 282 283
	return r.SectionReader.Size(), nil
}

284 285 286 287
func (r *LazyTestSectionReader) Context() context.Context {
	return context.TODO()
}

E
ethersphere 已提交
288
type StoreParams struct {
B
Balint Gabor 已提交
289 290 291 292
	Hash          SwarmHasher `toml:"-"`
	DbCapacity    uint64
	CacheCapacity uint
	BaseKey       []byte
E
ethersphere 已提交
293 294 295
}

func NewDefaultStoreParams() *StoreParams {
B
Balint Gabor 已提交
296
	return NewStoreParams(defaultLDBCapacity, defaultCacheCapacity, nil, nil)
E
ethersphere 已提交
297 298
}

B
Balint Gabor 已提交
299
func NewStoreParams(ldbCap uint64, cacheCap uint, hash SwarmHasher, basekey []byte) *StoreParams {
E
ethersphere 已提交
300 301 302 303 304 305 306
	if basekey == nil {
		basekey = make([]byte, 32)
	}
	if hash == nil {
		hash = MakeHashFunc(DefaultHash)
	}
	return &StoreParams{
B
Balint Gabor 已提交
307 308 309 310
		Hash:          hash,
		DbCapacity:    ldbCap,
		CacheCapacity: cacheCap,
		BaseKey:       basekey,
E
ethersphere 已提交
311 312 313 314 315 316 317 318 319
	}
}

type ChunkData []byte

type Reference []byte

// Putter is responsible to store data and create a reference for it
type Putter interface {
320
	Put(context.Context, ChunkData) (Reference, error)
E
ethersphere 已提交
321 322 323 324 325
	// RefSize returns the length of the Reference created by this Putter
	RefSize() int64
	// Close is to indicate that no more chunk data will be Put on this Putter
	Close()
	// Wait returns if all data has been store and the Close() was called.
326
	Wait(context.Context) error
E
ethersphere 已提交
327 328 329 330
}

// Getter is an interface to retrieve a chunk's data by its reference
type Getter interface {
331
	Get(context.Context, Reference) (ChunkData, error)
E
ethersphere 已提交
332 333 334
}

// NOTE: this returns invalid data if chunk is encrypted
B
Balint Gabor 已提交
335 336
func (c ChunkData) Size() uint64 {
	return binary.LittleEndian.Uint64(c[:8])
E
ethersphere 已提交
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
}

func (c ChunkData) Data() []byte {
	return c[8:]
}

type ChunkValidator interface {
	Validate(addr Address, data []byte) bool
}

// Provides method for validation of content address in chunks
// Holds the corresponding hasher to create the address
type ContentAddressValidator struct {
	Hasher SwarmHasher
}

// Constructor
func NewContentAddressValidator(hasher SwarmHasher) *ContentAddressValidator {
	return &ContentAddressValidator{
		Hasher: hasher,
	}
}

// Validate that the given key is a valid content address for the given data
func (v *ContentAddressValidator) Validate(addr Address, data []byte) bool {
B
Balint Gabor 已提交
362 363
	if l := len(data); l < 9 || l > ch.DefaultSize+8 {
		// log.Error("invalid chunk size", "chunk", addr.Hex(), "size", l)
364 365 366
		return false
	}

E
ethersphere 已提交
367 368 369 370 371 372
	hasher := v.Hasher()
	hasher.ResetWithLength(data[:8])
	hasher.Write(data[8:])
	hash := hasher.Sum(nil)

	return bytes.Equal(hash, addr[:])
373
}
B
Balint Gabor 已提交
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

type ChunkStore interface {
	Put(ctx context.Context, ch Chunk) (err error)
	Get(rctx context.Context, ref Address) (ch Chunk, err error)
	Close()
}

// SyncChunkStore is a ChunkStore which supports syncing
type SyncChunkStore interface {
	ChunkStore
	BinIndex(po uint8) uint64
	Iterator(from uint64, to uint64, po uint8, f func(Address, uint64) bool) error
	FetchFunc(ctx context.Context, ref Address) func(context.Context) error
}

// FakeChunkStore doesn't store anything, just implements the ChunkStore interface
// It can be used to inject into a hasherStore if you don't want to actually store data just do the
// hashing
type FakeChunkStore struct {
}

// Put doesn't store anything it is just here to implement ChunkStore
func (f *FakeChunkStore) Put(_ context.Context, ch Chunk) error {
	return nil
}

// Gut doesn't store anything it is just here to implement ChunkStore
func (f *FakeChunkStore) Get(_ context.Context, ref Address) (Chunk, error) {
	panic("FakeChunkStore doesn't support Get")
}

// Close doesn't store anything it is just here to implement ChunkStore
func (f *FakeChunkStore) Close() {
}