|
|
|
package explorer
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/hex"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
common2 "github.com/harmony-one/harmony/internal/common"
|
|
|
|
|
|
|
|
"github.com/syndtr/goleveldb/leveldb"
|
|
|
|
)
|
|
|
|
|
|
|
|
var prefixTestData = []prefixTestEntry{
|
|
|
|
{[]byte("000001"), []byte("000001")},
|
|
|
|
{[]byte("000002"), []byte("000001")},
|
|
|
|
{[]byte("000003"), []byte("000001")},
|
|
|
|
{[]byte("000004"), []byte("000001")},
|
|
|
|
{[]byte("000005"), []byte("000001")},
|
|
|
|
{[]byte("100001"), []byte("000001")},
|
|
|
|
{[]byte("100002"), []byte("000001")},
|
|
|
|
{[]byte("000011"), []byte("000001")},
|
|
|
|
{[]byte("000012"), []byte("000001")},
|
|
|
|
}
|
|
|
|
|
|
|
|
type prefixTestEntry struct {
|
|
|
|
key, val []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLevelDBPrefixIterator(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
prefix []byte
|
|
|
|
expSize int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
prefix: []byte("00000"),
|
|
|
|
expSize: 5,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
prefix: []byte("0000"),
|
|
|
|
expSize: 7,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
prefix: []byte(""),
|
|
|
|
expSize: 9,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
prefix: []byte("2"),
|
|
|
|
expSize: 0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for i, test := range tests {
|
|
|
|
db := newTestLevelDB(t, i)
|
|
|
|
if err := prepareTestLvlDB(db); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
it := db.NewPrefixIterator(test.prefix)
|
|
|
|
count := 0
|
|
|
|
for it.Next() {
|
|
|
|
count++
|
|
|
|
}
|
|
|
|
if count != test.expSize {
|
|
|
|
t.Errorf("Test %v: unexpected iteration size %v / %v", i, count, test.expSize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func newTestLevelDB(t *testing.T, i int) database {
|
|
|
|
dbDir := tempTestDir(t, i)
|
|
|
|
db, err := newExplorerLvlDB(dbDir)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return db
|
|
|
|
}
|
|
|
|
|
|
|
|
func prepareTestLvlDB(db database) error {
|
|
|
|
for _, entry := range prefixTestData {
|
|
|
|
key, val := entry.key, entry.val
|
|
|
|
if err := db.Put(key, val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func tempTestDir(t *testing.T, index int) string {
|
|
|
|
tempDir := os.TempDir()
|
|
|
|
testDir := path.Join(tempDir, "harmony", "explorer_db", t.Name(), strconv.Itoa(index))
|
|
|
|
os.RemoveAll(testDir)
|
|
|
|
return testDir
|
|
|
|
}
|
|
|
|
|
|
|
|
type memDB struct {
|
|
|
|
keyValues map[string][]byte
|
|
|
|
lock sync.RWMutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func newMemDB() *memDB {
|
|
|
|
return &memDB{
|
|
|
|
keyValues: make(map[string][]byte),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *memDB) Put(key, val []byte) error {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
|
|
|
realKey := hex.EncodeToString(key)
|
|
|
|
db.keyValues[realKey] = val
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *memDB) Has(key []byte) (bool, error) {
|
|
|
|
db.lock.RLock()
|
|
|
|
defer db.lock.RUnlock()
|
|
|
|
|
|
|
|
realKey := hex.EncodeToString(key)
|
|
|
|
_, ok := db.keyValues[realKey]
|
|
|
|
return ok, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *memDB) Get(key []byte) ([]byte, error) {
|
|
|
|
db.lock.RLock()
|
|
|
|
defer db.lock.RUnlock()
|
|
|
|
|
|
|
|
realKey := hex.EncodeToString(key)
|
|
|
|
val, ok := db.keyValues[realKey]
|
|
|
|
if !ok {
|
|
|
|
return nil, leveldb.ErrNotFound
|
|
|
|
}
|
|
|
|
return val, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *memDB) NewBatch() batch {
|
|
|
|
return &memBatch{
|
|
|
|
keyValues: make(map[string][]byte),
|
|
|
|
db: db,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (db *memDB) NewPrefixIterator(prefix []byte) iterator {
|
|
|
|
db.lock.Lock()
|
|
|
|
defer db.lock.Unlock()
|
|
|
|
|
|
|
|
var (
|
|
|
|
pr = hex.EncodeToString(prefix)
|
|
|
|
keys = make([]string, 0, len(db.keyValues))
|
|
|
|
values = make([][]byte, 0, len(db.keyValues))
|
|
|
|
)
|
|
|
|
for key := range db.keyValues {
|
|
|
|
if strings.HasPrefix(key, pr) {
|
|
|
|
keys = append(keys, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
for _, key := range keys {
|
|
|
|
values = append(values, db.keyValues[key])
|
|
|
|
}
|
|
|
|
return &memPrefixIterator{
|
|
|
|
keys: keys,
|
|
|
|
values: values,
|
|
|
|
index: -1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: implement this and verify
|
|
|
|
func (db *memDB) NewSizedIterator(start []byte, size int) iterator {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type memBatch struct {
|
|
|
|
keyValues map[string][]byte
|
|
|
|
db *memDB
|
|
|
|
valueSize int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *memBatch) Put(key, val []byte) error {
|
|
|
|
b.keyValues[hex.EncodeToString(key)] = val
|
|
|
|
b.valueSize += len(val)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *memBatch) Write() error {
|
|
|
|
for k, v := range b.keyValues {
|
|
|
|
b.db.keyValues[k] = v
|
|
|
|
}
|
|
|
|
b.valueSize = 0
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *memBatch) ValueSize() int {
|
|
|
|
return b.valueSize
|
|
|
|
}
|
|
|
|
|
|
|
|
type memPrefixIterator struct {
|
|
|
|
keys []string
|
|
|
|
values [][]byte
|
|
|
|
index int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *memPrefixIterator) Key() []byte {
|
|
|
|
b, err := hex.DecodeString(it.keys[it.index])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *memPrefixIterator) Value() []byte {
|
|
|
|
return it.values[it.index]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *memPrefixIterator) Release() {}
|
|
|
|
|
|
|
|
func (it *memPrefixIterator) Next() bool {
|
|
|
|
if it.index >= len(it.keys)-1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
it.index++
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (it *memPrefixIterator) Error() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeTestTxHash(index int) common.Hash {
|
|
|
|
var h common.Hash
|
|
|
|
binary.BigEndian.PutUint64(h[:], uint64(index))
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
|
|
|
func makeOneAddress(index int) oneAddress {
|
|
|
|
var raw common.Address
|
|
|
|
binary.LittleEndian.PutUint64(raw[:], uint64(index))
|
|
|
|
oneAddr, err := common2.AddressToBech32(raw)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return oneAddress(oneAddr)
|
|
|
|
}
|