mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
initial commit for swarm/bzz - to include blockhash pkg https://github.com/nagydani/go-dpa
This commit is contained in:
parent
75f0412f9d
commit
e32cdc4244
5 changed files with 982 additions and 0 deletions
377
bzz/blockhash.go
Normal file
377
bzz/blockhash.go
Normal file
|
|
@ -0,0 +1,377 @@
|
||||||
|
/*
|
||||||
|
The blockhash package implements a hash tree based fixed block size distributed
|
||||||
|
data storage
|
||||||
|
The block hash of a byte array is defined as follows:
|
||||||
|
|
||||||
|
- if size is no more than BlockSize, it is stored in a single block
|
||||||
|
blockhash = sha256(int64(size) + data)
|
||||||
|
|
||||||
|
- if size is more than BlockSize*BlockHashCount^l, but no more than BlockSize*
|
||||||
|
BlockHashCount^(l+1), the data vector is split into slices of BlockSize*
|
||||||
|
BlockHashCount^l length (except the last one).
|
||||||
|
blockhash = sha256(int64(size) + blockhash(slice0) + blockhash(slice1) + ...)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package blockhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
const HashSize = 32
|
||||||
|
const BlockSize = 4096
|
||||||
|
const BlockHashCount = BlockSize / HashSize
|
||||||
|
|
||||||
|
type HashType []byte
|
||||||
|
|
||||||
|
/*
|
||||||
|
The layered (memory, disk, distributed) storage model provides two channels, one
|
||||||
|
for storing and one for retrieving blocks. The layers are chained so that every
|
||||||
|
layer can store blocks and try to retrieve them if the previous layer did not
|
||||||
|
succeed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type dpaStorage struct {
|
||||||
|
store_chn chan *dpaStoreReq
|
||||||
|
retrieve_chn chan *dpaRetrieveReq
|
||||||
|
chain *dpaStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
type dpaReaderAt struct {
|
||||||
|
hash HashType
|
||||||
|
store *dpaStorage
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type dpaNode struct {
|
||||||
|
data []byte
|
||||||
|
size int64 // denotes the size of data represented by the whole subtree
|
||||||
|
}
|
||||||
|
|
||||||
|
type dpaStoreReq struct {
|
||||||
|
dpaNode
|
||||||
|
hash HashType
|
||||||
|
}
|
||||||
|
|
||||||
|
type dpaRetrieveRes struct {
|
||||||
|
dpaNode
|
||||||
|
req_id int
|
||||||
|
}
|
||||||
|
|
||||||
|
type dpaRetrieveReq struct {
|
||||||
|
hash HashType
|
||||||
|
req_id int
|
||||||
|
result_chn chan *dpaRetrieveRes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HashType) bits(i, j uint) uint {
|
||||||
|
|
||||||
|
ii := i >> 3
|
||||||
|
jj := i & 7
|
||||||
|
if ii >= HashSize {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if jj+j <= 8 {
|
||||||
|
return uint((h[ii] >> jj) & ((1 << j) - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
res := uint(h[ii] >> jj)
|
||||||
|
jj = 8 - jj
|
||||||
|
j -= jj
|
||||||
|
for j != 0 {
|
||||||
|
ii++
|
||||||
|
if j < 8 {
|
||||||
|
res += uint(h[ii]&((1<<j)-1)) << jj
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
res += uint(h[ii]) << jj
|
||||||
|
jj += 8
|
||||||
|
j -= 8
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h HashType) isEqual(h2 HashType) bool {
|
||||||
|
|
||||||
|
for i := range h {
|
||||||
|
if h[i] != h2[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaStorage) Init() {
|
||||||
|
|
||||||
|
s.store_chn = make(chan *dpaStoreReq, 1000)
|
||||||
|
s.retrieve_chn = make(chan *dpaRetrieveReq, 1000)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the root hash of any data vector and store the blocks of the tree if store != nil
|
||||||
|
|
||||||
|
func GetDPAroot(data []byte, store *dpaStorage) HashType {
|
||||||
|
|
||||||
|
return GetDPAhash(io.NewSectionReader(bytes.NewReader(data), 0, int64(len(data))), store)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func goGetDPAhash(reader *io.SectionReader, store *dpaStorage, hash HashType, done chan<- bool) {
|
||||||
|
|
||||||
|
hh := GetDPAhash(reader, store)
|
||||||
|
if hh == nil {
|
||||||
|
done <- false
|
||||||
|
} else {
|
||||||
|
copy(hash[:], hh[:])
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDPAhash(reader *io.SectionReader, store *dpaStorage) HashType {
|
||||||
|
|
||||||
|
size := reader.Size()
|
||||||
|
var block []byte
|
||||||
|
|
||||||
|
if size <= BlockSize {
|
||||||
|
block = make([]byte, size)
|
||||||
|
br, _ := reader.Read(block)
|
||||||
|
if br < int(size) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stc := (size + BlockSize - 1) / BlockSize
|
||||||
|
SubtreeSize := int64(BlockSize)
|
||||||
|
|
||||||
|
for stc > BlockHashCount {
|
||||||
|
stc = (stc-1)/BlockHashCount + 1
|
||||||
|
SubtreeSize *= BlockHashCount
|
||||||
|
}
|
||||||
|
SubtreeCount := int(stc)
|
||||||
|
|
||||||
|
block = make([]byte, SubtreeCount*HashSize)
|
||||||
|
|
||||||
|
hdone := make(chan bool, SubtreeCount)
|
||||||
|
|
||||||
|
ptr := int64(0)
|
||||||
|
hptr := 0
|
||||||
|
for i := 0; i < SubtreeCount; i++ {
|
||||||
|
ptr2 := ptr + SubtreeSize
|
||||||
|
if ptr2 > size {
|
||||||
|
ptr2 = size
|
||||||
|
}
|
||||||
|
go goGetDPAhash(io.NewSectionReader(reader, ptr, ptr2-ptr), store, HashType(block[hptr:hptr+HashSize]), hdone)
|
||||||
|
ptr = ptr2
|
||||||
|
hptr += HashSize
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < SubtreeCount; i++ {
|
||||||
|
if !<-hdone {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashfn := sha256.New()
|
||||||
|
//binary.LittleEndian.PutUint16(b, uint16(i))
|
||||||
|
//fmt.Printf("%d\n", size)
|
||||||
|
binary.Write(hashfn, binary.LittleEndian, int64(size))
|
||||||
|
hashfn.Write(block)
|
||||||
|
hash := hashfn.Sum(nil)
|
||||||
|
|
||||||
|
if store != nil {
|
||||||
|
req := new(dpaStoreReq)
|
||||||
|
req.data = block
|
||||||
|
req.size = int64(size)
|
||||||
|
req.hash = hash
|
||||||
|
store.store_chn <- req
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// recursive function to retrieve a section of a subtree
|
||||||
|
// len(data) == stop-start
|
||||||
|
|
||||||
|
func getDPAblock(res *dpaRetrieveRes, data []byte, start int64, stop int64, bsize int64, retrv chan<- *dpaRetrieveReq, done chan<- bool) bool {
|
||||||
|
|
||||||
|
for bsize >= res.size {
|
||||||
|
if bsize == BlockSize {
|
||||||
|
bsize = 0
|
||||||
|
} else {
|
||||||
|
bsize /= BlockHashCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if bsize < BlockSize {
|
||||||
|
if res.size < stop {
|
||||||
|
if done != nil {
|
||||||
|
done <- false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
copy(data[:], res.data[start:stop])
|
||||||
|
if done != nil {
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
bstart := int(start / bsize)
|
||||||
|
bstop := int((stop + bsize - 1) / bsize)
|
||||||
|
|
||||||
|
if len(res.data) < bstop*HashSize {
|
||||||
|
if done != nil {
|
||||||
|
done <- false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
chn := make(chan *dpaRetrieveRes, bstop-bstart)
|
||||||
|
sdone := make(chan bool, bstop-bstart)
|
||||||
|
|
||||||
|
for i := bstart; i < bstop; i++ {
|
||||||
|
|
||||||
|
hash := HashType(res.data[i*HashSize : (i+1)*HashSize])
|
||||||
|
req := new(dpaRetrieveReq)
|
||||||
|
req.hash = hash
|
||||||
|
req.req_id = i
|
||||||
|
req.result_chn = chn
|
||||||
|
retrv <- req
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := bstart; j < bstop; j++ {
|
||||||
|
|
||||||
|
res := <-chn
|
||||||
|
|
||||||
|
i := int64(res.req_id)
|
||||||
|
a := i * bsize
|
||||||
|
aa := a
|
||||||
|
b := a + bsize
|
||||||
|
|
||||||
|
if a < start {
|
||||||
|
a = start
|
||||||
|
}
|
||||||
|
if b > stop {
|
||||||
|
b = stop
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.size < b-aa {
|
||||||
|
if done != nil {
|
||||||
|
done <- false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bsize == BlockSize {
|
||||||
|
getDPAblock(res, data[a-start:b-start], a-aa, b-aa, 0, retrv, sdone)
|
||||||
|
} else {
|
||||||
|
go getDPAblock(res, data[a-start:b-start], a-aa, b-aa, bsize/BlockHashCount, retrv, sdone)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dd := true
|
||||||
|
for j := bstart; j < bstop; j++ {
|
||||||
|
if !<-sdone {
|
||||||
|
dd = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if done != nil {
|
||||||
|
done <- dd
|
||||||
|
}
|
||||||
|
return dd
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *dpaReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
|
||||||
|
|
||||||
|
chn := make(chan *dpaRetrieveRes)
|
||||||
|
|
||||||
|
req := new(dpaRetrieveReq)
|
||||||
|
req.hash = r.hash
|
||||||
|
req.req_id = 0
|
||||||
|
req.result_chn = chn
|
||||||
|
|
||||||
|
r.store.retrieve_chn <- req
|
||||||
|
res := <-chn
|
||||||
|
|
||||||
|
if res.size == 0 {
|
||||||
|
return 0, fmt.Errorf("Block hash %064x not found", r.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.size = res.size
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bsize := int64(0)
|
||||||
|
if res.size > BlockSize {
|
||||||
|
bsize = int64(BlockSize)
|
||||||
|
for bsize*BlockHashCount < res.size {
|
||||||
|
bsize *= BlockHashCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = error(nil)
|
||||||
|
|
||||||
|
eoff := off + int64(len(p))
|
||||||
|
if eoff > res.size {
|
||||||
|
eoff = res.size
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if !getDPAblock(res, p, off, eoff, bsize, r.store.retrieve_chn, nil) {
|
||||||
|
return 0, fmt.Errorf("Can't load section [%d:%d] of block hash %064x", off, eoff, r.hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(eoff - off), err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDPAreader(hash HashType, st *dpaStorage) *io.SectionReader {
|
||||||
|
|
||||||
|
rd := new(dpaReaderAt)
|
||||||
|
rd.hash = hash
|
||||||
|
rd.store = st
|
||||||
|
rd.size = -1
|
||||||
|
|
||||||
|
rd.ReadAt(nil, 0)
|
||||||
|
|
||||||
|
if rd.size >= 0 {
|
||||||
|
return io.NewSectionReader(rd, 0, rd.size)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieve a data vector of a given block hash from the given storage
|
||||||
|
|
||||||
|
func GetDPAdata(hash HashType, st *dpaStorage) []byte {
|
||||||
|
|
||||||
|
sr := GetDPAreader(hash, st)
|
||||||
|
if sr == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
size := sr.Size()
|
||||||
|
|
||||||
|
data := make([]byte, int(size))
|
||||||
|
br, _ := sr.Read(data)
|
||||||
|
if int64(br) == size {
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
97
bzz/blockhash_test.go
Normal file
97
bzz/blockhash_test.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// test bench for the package blockhash
|
||||||
|
|
||||||
|
package blockhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func maketest(l int) []byte {
|
||||||
|
|
||||||
|
r := rand.New(rand.NewSource(int64(l)))
|
||||||
|
|
||||||
|
test := make([]byte, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
test[i] = byte(r.Intn(256))
|
||||||
|
}
|
||||||
|
|
||||||
|
return test
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmptest(a, b []byte) bool {
|
||||||
|
|
||||||
|
if len(a) != len(b) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const testcnt = 10
|
||||||
|
|
||||||
|
func testlen(i int) int {
|
||||||
|
|
||||||
|
/* if i == 79 {
|
||||||
|
return 16777217
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return int(0.5 + math.Exp2(3.0+float64(i)/5))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlockHashStorage(t *testing.T) {
|
||||||
|
t.Logf("Creating DBStorage...")
|
||||||
|
|
||||||
|
dbstore := new(dpaDBStorage)
|
||||||
|
dbstore.Init(nil)
|
||||||
|
go dbstore.Run()
|
||||||
|
|
||||||
|
t.Logf("Creating MemStorage...")
|
||||||
|
|
||||||
|
memstore := new(dpaMemStorage)
|
||||||
|
memstore.Init(&dbstore.dpaStorage)
|
||||||
|
go memstore.Run()
|
||||||
|
|
||||||
|
t.Logf("Storing test vectors...")
|
||||||
|
|
||||||
|
test := make([][]byte, testcnt)
|
||||||
|
hash := make([]HashType, testcnt)
|
||||||
|
for i := 0; i < testcnt; i++ {
|
||||||
|
test[i] = maketest(testlen(i))
|
||||||
|
//t.Logf("Test[%d] = %x", i, test[i])
|
||||||
|
hash[i] = GetDPAroot(test[i], &memstore.dpaStorage)
|
||||||
|
//t.Logf("Hash[%d] = %x", i, hash[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Retrieving test vectors...")
|
||||||
|
|
||||||
|
rnd := rand.New(rand.NewSource(0))
|
||||||
|
|
||||||
|
for i := 0; i < testcnt; i++ {
|
||||||
|
|
||||||
|
tt := GetDPAdata(hash[i], &memstore.dpaStorage) // get the whole vector with byte array wrapper
|
||||||
|
|
||||||
|
sr := GetDPAreader(hash[i], &memstore.dpaStorage)
|
||||||
|
size := int(sr.Size())
|
||||||
|
pos := rnd.Intn(size - 1)
|
||||||
|
slen := rnd.Intn(size-1-pos) + 1
|
||||||
|
sr.Seek(int64(pos), 0)
|
||||||
|
br, _ := sr.Read(tt[pos : pos+slen]) // re-read a random section
|
||||||
|
|
||||||
|
if (br == slen) && cmptest(test[i], tt) {
|
||||||
|
t.Logf("Test case %d passed (test vector length %d)", i, len(tt))
|
||||||
|
} else {
|
||||||
|
t.Errorf("Test case %d failed", i)
|
||||||
|
if size < 20 {
|
||||||
|
t.Errorf("pos = %d slen = %d br = %d vector = %x instead of %x", pos, slen, br, tt, test[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
89
bzz/database.go
Normal file
89
bzz/database.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package blockhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/compression/rle"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LDBDatabase struct {
|
||||||
|
db *leveldb.DB
|
||||||
|
comp bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLDBDatabase(name string) (*LDBDatabase, error) {
|
||||||
|
dbPath := path.Join(ethutil.Config.ExecPath, name)
|
||||||
|
|
||||||
|
// Open the db
|
||||||
|
db, err := leveldb.OpenFile(dbPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
database := &LDBDatabase{db: db, comp: true}
|
||||||
|
|
||||||
|
return database, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Put(key []byte, value []byte) {
|
||||||
|
if self.comp {
|
||||||
|
value = rle.Compress(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := self.db.Put(key, value, nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error put", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Get(key []byte) ([]byte, error) {
|
||||||
|
dat, err := self.db.Get(key, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.comp {
|
||||||
|
return rle.Decompress(dat)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Delete(key []byte) error {
|
||||||
|
return self.db.Delete(key, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) LastKnownTD() []byte {
|
||||||
|
data, _ := self.Get([]byte("LTD"))
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
data = []byte{0x0}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) NewIterator() iterator.Iterator {
|
||||||
|
return self.db.NewIterator(nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Close() {
|
||||||
|
// Close the leveldb database
|
||||||
|
self.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *LDBDatabase) Print() {
|
||||||
|
iter := self.db.NewIterator(nil, nil)
|
||||||
|
for iter.Next() {
|
||||||
|
key := iter.Key()
|
||||||
|
value := iter.Value()
|
||||||
|
|
||||||
|
fmt.Printf("%x(%d): ", key, len(key))
|
||||||
|
node := ethutil.NewValueFromBytes(value)
|
||||||
|
fmt.Printf("%v\n", node)
|
||||||
|
}
|
||||||
|
}
|
||||||
117
bzz/dbstore.go
Normal file
117
bzz/dbstore.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// disk storage layer for the package blockhash
|
||||||
|
// inefficient work-in-progress version
|
||||||
|
|
||||||
|
package blockhash
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "crypto/sha256"
|
||||||
|
// "encoding/binary"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
// "github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethutil"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dpaDBStorage struct {
|
||||||
|
dpaStorage
|
||||||
|
// db *ethdb.LDBDatabase
|
||||||
|
db *LDBDatabase
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) add(entry *dpaStoreReq) {
|
||||||
|
|
||||||
|
data := ethutil.Encode([]interface{}{entry.data, entry.size})
|
||||||
|
|
||||||
|
s.db.Put(entry.hash, data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) find(hash HashType) (entry dpaNode) {
|
||||||
|
|
||||||
|
fmt.Printf("mao")
|
||||||
|
|
||||||
|
data, err := s.db.Get(hash)
|
||||||
|
if err != nil {
|
||||||
|
panic("hi")
|
||||||
|
}
|
||||||
|
fmt.Printf("mao")
|
||||||
|
|
||||||
|
dec := rlp.NewListStream(bytes.NewReader(data), uint64(len(data)))
|
||||||
|
dec.Decode(&entry)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) process_store(req *dpaStoreReq) {
|
||||||
|
|
||||||
|
s.add(req)
|
||||||
|
|
||||||
|
if s.chain != nil {
|
||||||
|
s.chain.store_chn <- req
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) process_retrieve(req *dpaRetrieveReq) {
|
||||||
|
|
||||||
|
fmt.Printf("mao")
|
||||||
|
entry := s.find(req.hash)
|
||||||
|
|
||||||
|
fmt.Printf("%v", entry.size)
|
||||||
|
if entry.data == nil {
|
||||||
|
if s.chain != nil {
|
||||||
|
s.chain.retrieve_chn <- req
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := new(dpaRetrieveRes)
|
||||||
|
if entry.data != nil {
|
||||||
|
res.dpaNode = entry
|
||||||
|
}
|
||||||
|
res.req_id = req.req_id
|
||||||
|
req.result_chn <- res
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) Init(ch *dpaStorage) {
|
||||||
|
|
||||||
|
// dbPath := path.Join(ethutil.Config.ExecPath, "bzz")
|
||||||
|
dbPath := path.Join(".", "bzz")
|
||||||
|
|
||||||
|
// Open the db
|
||||||
|
db, err := leveldb.OpenFile(dbPath, nil)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// s.db = ðdb.LDBDatabase{db: db, comp: false}
|
||||||
|
s.db = &LDBDatabase{db: db, comp: false}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaDBStorage) Run() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
bb := true
|
||||||
|
for bb {
|
||||||
|
select {
|
||||||
|
case store := <-s.store_chn:
|
||||||
|
s.process_store(store)
|
||||||
|
default:
|
||||||
|
bb = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case store := <-s.store_chn:
|
||||||
|
s.process_store(store)
|
||||||
|
case retrv := <-s.retrieve_chn:
|
||||||
|
s.process_retrieve(retrv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
302
bzz/memstore.go
Normal file
302
bzz/memstore.go
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
// memory storage layer for the package blockhash
|
||||||
|
|
||||||
|
package blockhash
|
||||||
|
|
||||||
|
const MaxEntries = 500 // max number of stored (cached) blocks
|
||||||
|
const MemTreeLW = 2 // log2(subtree count) of the subtrees
|
||||||
|
const MemTreeFLW = 14 // log2(subtree count) of the root layer
|
||||||
|
|
||||||
|
type dpaMemStorage struct {
|
||||||
|
dpaStorage
|
||||||
|
memtree *dpaMemTree
|
||||||
|
entry_cnt uint // stored entries
|
||||||
|
access_cnt uint64 // access counter; oldest is thrown away when full
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
a hash prefix subtree containing subtrees or one storage entry (but never both)
|
||||||
|
|
||||||
|
- access[0] stores the smallest (oldest) access count value in this subtree
|
||||||
|
- if it contains more subtrees and its subtree count is at least 4, access[1:2]
|
||||||
|
stores the smallest access count in the first and second halves of subtrees
|
||||||
|
(so that access[0] = min(access[1], access[2])
|
||||||
|
- likewise, if subtree count is at least 8,
|
||||||
|
access[1] = min(access[3], access[4])
|
||||||
|
access[2] = min(access[5], access[6])
|
||||||
|
(access[] is a binary tree inside the multi-bit leveled hash tree)
|
||||||
|
*/
|
||||||
|
|
||||||
|
type dpaMemTree struct {
|
||||||
|
subtree []*dpaMemTree
|
||||||
|
parent *dpaMemTree
|
||||||
|
parent_idx uint
|
||||||
|
|
||||||
|
bits uint // log2(subtree count)
|
||||||
|
width uint // subtree count
|
||||||
|
|
||||||
|
entry *dpaStoreReq // if subtrees are present, entry should be nil
|
||||||
|
access []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTreeNode(b uint, parent *dpaMemTree, pidx uint) (node *dpaMemTree) {
|
||||||
|
|
||||||
|
node = new(dpaMemTree)
|
||||||
|
node.bits = b
|
||||||
|
node.width = 1 << uint(b)
|
||||||
|
node.subtree = make([]*dpaMemTree, node.width)
|
||||||
|
node.access = make([]uint64, node.width-1)
|
||||||
|
node.parent = parent
|
||||||
|
node.parent_idx = pidx
|
||||||
|
if parent != nil {
|
||||||
|
parent.subtree[pidx] = node
|
||||||
|
}
|
||||||
|
|
||||||
|
return node
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *dpaMemTree) update_access(a uint64) {
|
||||||
|
|
||||||
|
aidx := uint(0)
|
||||||
|
var aa uint64
|
||||||
|
oa := node.access[0]
|
||||||
|
for node.access[aidx] == oa {
|
||||||
|
node.access[aidx] = a
|
||||||
|
if aidx > 0 {
|
||||||
|
aa = node.access[((aidx-1)^1)+1]
|
||||||
|
aidx = (aidx - 1) >> 1
|
||||||
|
} else {
|
||||||
|
pidx := node.parent_idx
|
||||||
|
node = node.parent
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nn := node.subtree[pidx^1]
|
||||||
|
if nn != nil {
|
||||||
|
aa = nn.access[0]
|
||||||
|
} else {
|
||||||
|
aa = 0
|
||||||
|
}
|
||||||
|
aidx = (node.width + pidx - 2) >> 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aa != 0) && (aa < a) {
|
||||||
|
a = aa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) add(entry *dpaStoreReq) {
|
||||||
|
|
||||||
|
s.access_cnt++
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
bitpos := uint(0)
|
||||||
|
for node.entry == nil {
|
||||||
|
l := entry.hash.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newTreeNode(MemTreeLW, node, l)
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.entry != nil {
|
||||||
|
|
||||||
|
if node.entry.hash.isEqual(entry.hash) {
|
||||||
|
node.update_access(s.access_cnt)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for node.entry != nil {
|
||||||
|
|
||||||
|
l := node.entry.hash.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newTreeNode(MemTreeLW, node, l)
|
||||||
|
}
|
||||||
|
st.entry = node.entry
|
||||||
|
node.entry = nil
|
||||||
|
st.update_access(node.access[0])
|
||||||
|
|
||||||
|
l = entry.hash.bits(bitpos, node.bits)
|
||||||
|
st = node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
st = newTreeNode(MemTreeLW, node, l)
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.entry = entry
|
||||||
|
node.update_access(s.access_cnt)
|
||||||
|
s.entry_cnt++
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) find(hash HashType) (entry *dpaStoreReq) {
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
bitpos := uint(0)
|
||||||
|
for node.entry == nil {
|
||||||
|
l := hash.bits(bitpos, node.bits)
|
||||||
|
st := node.subtree[l]
|
||||||
|
if st == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bitpos += node.bits
|
||||||
|
node = st
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.entry.hash.isEqual(hash) {
|
||||||
|
s.access_cnt++
|
||||||
|
node.update_access(s.access_cnt)
|
||||||
|
return node.entry
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) remove_oldest() {
|
||||||
|
|
||||||
|
node := s.memtree
|
||||||
|
|
||||||
|
for node.entry == nil {
|
||||||
|
|
||||||
|
aidx := uint(0)
|
||||||
|
av := node.access[aidx]
|
||||||
|
|
||||||
|
for aidx < node.width/2-1 {
|
||||||
|
if av == node.access[aidx*2+1] {
|
||||||
|
node.access[aidx] = node.access[aidx*2+2]
|
||||||
|
aidx = aidx*2 + 1
|
||||||
|
} else if av == node.access[aidx*2+2] {
|
||||||
|
node.access[aidx] = node.access[aidx*2+1]
|
||||||
|
aidx = aidx*2 + 2
|
||||||
|
} else {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pidx := aidx*2 + 2 - node.width
|
||||||
|
if (node.subtree[pidx] != nil) && (av == node.subtree[pidx].access[0]) {
|
||||||
|
if node.subtree[pidx+1] != nil {
|
||||||
|
node.access[aidx] = node.subtree[pidx+1].access[0]
|
||||||
|
} else {
|
||||||
|
node.access[aidx] = 0
|
||||||
|
}
|
||||||
|
} else if (node.subtree[pidx+1] != nil) && (av == node.subtree[pidx+1].access[0]) {
|
||||||
|
if node.subtree[pidx] != nil {
|
||||||
|
node.access[aidx] = node.subtree[pidx].access[0]
|
||||||
|
} else {
|
||||||
|
node.access[aidx] = 0
|
||||||
|
}
|
||||||
|
pidx++
|
||||||
|
} else {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
//fmt.Println(pidx)
|
||||||
|
node = node.subtree[pidx]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
node.entry = nil
|
||||||
|
s.entry_cnt--
|
||||||
|
node.access[0] = 0
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
aidx := uint(0)
|
||||||
|
for {
|
||||||
|
aa := node.access[aidx]
|
||||||
|
if aidx > 0 {
|
||||||
|
aidx = (aidx - 1) >> 1
|
||||||
|
} else {
|
||||||
|
pidx := node.parent_idx
|
||||||
|
node = node.parent
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
aidx = (node.width + pidx - 2) >> 1
|
||||||
|
}
|
||||||
|
if (aa != 0) && ((aa < node.access[aidx]) || (node.access[aidx] == 0)) {
|
||||||
|
node.access[aidx] = aa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// process store channel requests
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) process_store(req *dpaStoreReq) {
|
||||||
|
|
||||||
|
if s.entry_cnt >= MaxEntries {
|
||||||
|
s.remove_oldest()
|
||||||
|
}
|
||||||
|
s.add(req)
|
||||||
|
|
||||||
|
if s.chain != nil {
|
||||||
|
s.chain.store_chn <- req
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// process retrieve channel requests
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) process_retrieve(req *dpaRetrieveReq) {
|
||||||
|
|
||||||
|
entry := s.find(req.hash)
|
||||||
|
if entry == nil {
|
||||||
|
if s.chain != nil {
|
||||||
|
s.chain.retrieve_chn <- req
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res := new(dpaRetrieveRes)
|
||||||
|
if entry != nil {
|
||||||
|
res.dpaNode = entry.dpaNode
|
||||||
|
}
|
||||||
|
res.req_id = req.req_id
|
||||||
|
req.result_chn <- res
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) Init(ch *dpaStorage) {
|
||||||
|
|
||||||
|
s.dpaStorage.Init()
|
||||||
|
s.memtree = newTreeNode(MemTreeFLW, nil, 0)
|
||||||
|
s.chain = ch
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage main goroutine; always processes store messages first
|
||||||
|
|
||||||
|
func (s *dpaMemStorage) Run() {
|
||||||
|
|
||||||
|
for {
|
||||||
|
bb := true
|
||||||
|
for bb {
|
||||||
|
select {
|
||||||
|
case store := <-s.store_chn:
|
||||||
|
s.process_store(store)
|
||||||
|
default:
|
||||||
|
bb = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case store := <-s.store_chn:
|
||||||
|
s.process_store(store)
|
||||||
|
case retrv := <-s.retrieve_chn:
|
||||||
|
s.process_retrieve(retrv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue