Merge branch 'master' into gossip

This commit is contained in:
Stephen Buttolph 2020-05-05 14:26:25 -04:00 committed by GitHub
commit 2e2e033562
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 144 additions and 67 deletions

View File

@ -12,6 +12,7 @@ import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/vms/components/codec"
)
@ -174,7 +175,7 @@ type batch struct {
}
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), utils.CopyBytes(value), false})
encValue, err := b.db.encrypt(value)
if err != nil {
return err
@ -183,7 +184,7 @@ func (b *batch) Put(key, value []byte) error {
}
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), nil, true})
return b.Batch.Delete(key)
}
@ -251,12 +252,6 @@ func (it *iterator) Error() error {
func (it *iterator) Value() []byte { return it.val }
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}
type encryptedValue struct {
Ciphertext []byte `serialize:"true"`
Nonce []byte `serialize:"true"`

View File

@ -17,46 +17,42 @@ package database
// iterator until exhaustion. An iterator is not safe for concurrent use, but it
// is safe to use multiple iterators concurrently.
type Iterator interface {
// Next moves the iterator to the next key/value pair. It returns whether the
// iterator is exhausted.
// Next moves the iterator to the next key/value pair. It returns whether
// the iterator is exhausted.
Next() bool
// Error returns any accumulated error. Exhausting all the key/value pairs
// is not considered to be an error.
Error() error
// Key returns the key of the current key/value pair, or nil if done. The caller
// should not modify the contents of the returned slice, and its contents may
// change on the next call to Next.
// Key returns the key of the current key/value pair, or nil if done.
Key() []byte
// Value returns the value of the current key/value pair, or nil if done. The
// caller should not modify the contents of the returned slice, and its contents
// may change on the next call to Next.
// Value returns the value of the current key/value pair, or nil if done.
Value() []byte
// Release releases associated resources. Release should always succeed and can
// be called multiple times without causing error.
// Release releases associated resources. Release should always succeed and
// can be called multiple times without causing error.
Release()
}
// Iteratee wraps the NewIterator methods of a backing data store.
type Iteratee interface {
// NewIterator creates a binary-alphabetical iterator over the entire keyspace
// contained within the key-value database.
// NewIterator creates a binary-alphabetical iterator over the entire
// keyspace contained within the key-value database.
NewIterator() Iterator
// NewIteratorWithStart creates a binary-alphabetical iterator over a subset of
// database content starting at a particular initial key (or after, if it does
// not exist).
// NewIteratorWithStart creates a binary-alphabetical iterator over a subset
// of database content starting at a particular initial key (or after, if it
// does not exist).
NewIteratorWithStart(start []byte) Iterator
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a subset
// of database content with a particular key prefix.
// NewIteratorWithPrefix creates a binary-alphabetical iterator over a
// subset of database content with a particular key prefix.
NewIteratorWithPrefix(prefix []byte) Iterator
// NewIteratorWithStartAndPrefix creates a binary-alphabetical iterator over a
// subset of database content with a particular key prefix starting at a
// NewIteratorWithStartAndPrefix creates a binary-alphabetical iterator over
// a subset of database content with a particular key prefix starting at a
// specified key.
NewIteratorWithStartAndPrefix(start, prefix []byte) Iterator
}

View File

@ -7,6 +7,7 @@ import (
"bytes"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/utils"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/errors"
"github.com/syndtr/goleveldb/leveldb/filter"
@ -208,7 +209,14 @@ func (r *replayer) Delete(key []byte) {
type iter struct{ iterator.Iterator }
func (i *iter) Error() error { return updateError(i.Iterator.Error()) }
// Error implements the Iterator interface
func (it *iter) Error() error { return updateError(it.Iterator.Error()) }
// Key implements the Iterator interface
func (it *iter) Key() []byte { return utils.CopyBytes(it.Iterator.Key()) }
// Value implements the Iterator interface
func (it *iter) Value() []byte { return utils.CopyBytes(it.Iterator.Value()) }
func updateError(err error) error {
switch err {

View File

@ -10,6 +10,7 @@ import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils"
)
// DefaultSize is the default initial size of the memory database
@ -62,7 +63,7 @@ func (db *Database) Get(key []byte) ([]byte, error) {
return nil, database.ErrClosed
}
if entry, ok := db.db[string(key)]; ok {
return copyBytes(entry), nil
return utils.CopyBytes(entry), nil
}
return nil, database.ErrNotFound
}
@ -75,7 +76,7 @@ func (db *Database) Put(key []byte, value []byte) error {
if db.db == nil {
return database.ErrClosed
}
db.db[string(key)] = copyBytes(value)
db.db[string(key)] = utils.CopyBytes(value)
return nil
}
@ -154,13 +155,13 @@ type batch struct {
}
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), utils.CopyBytes(value), false})
b.size += len(value)
return nil
}
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), nil, true})
b.size++
return nil
}
@ -253,9 +254,3 @@ func (it *iterator) Value() []byte {
// Release implements the Iterator interface
func (it *iterator) Release() { it.keys = nil; it.values = nil }
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

View File

@ -8,6 +8,7 @@ import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils"
"github.com/ava-labs/gecko/utils/hashing"
)
@ -174,13 +175,13 @@ type batch struct {
// Put implements the Batch interface
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), utils.CopyBytes(value), false})
return b.Batch.Put(b.db.prefix(key), value)
}
// Delete implements the Batch interface
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), nil, true})
return b.Batch.Delete(b.db.prefix(key))
}
@ -229,9 +230,3 @@ func (it *iterator) Key() []byte {
}
return key
}
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

View File

@ -11,6 +11,7 @@ import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/database/rpcdb/rpcdbproto"
"github.com/ava-labs/gecko/utils"
)
var (
@ -137,13 +138,13 @@ type batch struct {
}
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), utils.CopyBytes(value), false})
b.size += len(value)
return nil
}
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), nil, true})
b.size++
return nil
}
@ -246,12 +247,6 @@ func (it *iterator) Release() {
})
}
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}
func updateError(err error) error {
if err == nil {
return nil

View File

@ -24,6 +24,7 @@ var (
TestIteratorStart,
TestIteratorPrefix,
TestIteratorStartPrefix,
TestIteratorMemorySafety,
TestIteratorClosed,
TestStatNoPanic,
TestCompactNoPanic,
@ -622,6 +623,63 @@ func TestIteratorStartPrefix(t *testing.T, db Database) {
}
}
// TestIteratorMemorySafety ...
func TestIteratorMemorySafety(t *testing.T, db Database) {
key1 := []byte("hello1")
value1 := []byte("world1")
key2 := []byte("z")
value2 := []byte("world2")
key3 := []byte("hello3")
value3 := []byte("world3")
if err := db.Put(key1, value1); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key2, value2); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
} else if err := db.Put(key3, value3); err != nil {
t.Fatalf("Unexpected error on batch.Put: %s", err)
}
iterator := db.NewIterator()
if iterator == nil {
t.Fatalf("db.NewIterator returned nil")
}
defer iterator.Release()
keys := [][]byte{}
values := [][]byte{}
for iterator.Next() {
keys = append(keys, iterator.Key())
values = append(values, iterator.Value())
}
expectedKeys := [][]byte{
key1,
key3,
key2,
}
expectedValues := [][]byte{
value1,
value3,
value2,
}
for i, key := range keys {
value := values[i]
expectedKey := expectedKeys[i]
expectedValue := expectedValues[i]
if !bytes.Equal(key, expectedKey) {
t.Fatalf("Wrong key")
}
if !bytes.Equal(value, expectedValue) {
t.Fatalf("Wrong key")
}
}
}
// TestIteratorClosed ...
func TestIteratorClosed(t *testing.T, db Database) {
key1 := []byte("hello1")

View File

@ -11,6 +11,7 @@ import (
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/database/nodb"
"github.com/ava-labs/gecko/utils"
)
// Database implements the Database interface by living on top of another
@ -61,7 +62,7 @@ func (db *Database) Get(key []byte) ([]byte, error) {
if val.delete {
return nil, database.ErrNotFound
}
return copyBytes(val.value), nil
return utils.CopyBytes(val.value), nil
}
return db.db.Get(key)
}
@ -262,14 +263,14 @@ type batch struct {
// Put implements the Database interface
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), copyBytes(value), false})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), utils.CopyBytes(value), false})
b.size += len(value)
return nil
}
// Delete implements the Database interface
func (b *batch) Delete(key []byte) error {
b.writes = append(b.writes, keyValue{copyBytes(key), nil, true})
b.writes = append(b.writes, keyValue{utils.CopyBytes(key), nil, true})
b.size++
return nil
}
@ -414,9 +415,3 @@ func (it *iterator) Release() {
it.values = nil
it.Iterator.Release()
}
func copyBytes(bytes []byte) []byte {
copiedBytes := make([]byte, len(bytes))
copy(copiedBytes, bytes)
return copiedBytes
}

16
utils/bytes.go Normal file
View File

@ -0,0 +1,16 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package utils
// CopyBytes returns a copy of the provided byte slice. If nil is provided, nil
// will be returned.
func CopyBytes(b []byte) []byte {
if b == nil {
return nil
}
cb := make([]byte, len(b))
copy(cb, b)
return cb
}

24
utils/bytes_test.go Normal file
View File

@ -0,0 +1,24 @@
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package utils
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCopyBytesNil(t *testing.T) {
result := CopyBytes(nil)
assert.Nil(t, result, "CopyBytes(nil) should have returned nil")
}
func TestCopyBytes(t *testing.T) {
input := []byte{1}
result := CopyBytes(input)
assert.Equal(t, input, result, "CopyBytes should have returned equal bytes")
input[0] = 0
assert.NotEqual(t, input, result, "CopyBytes should have returned independent bytes")
}

View File

@ -130,8 +130,6 @@ func (s *state) SetStatus(id ids.ID, status choices.Status) error {
return s.vm.db.Delete(id.Bytes())
}
s.c.Put(id, status)
p := wrappers.Packer{Bytes: make([]byte, 4)}
p.PackInt(uint32(status))
@ -143,6 +141,8 @@ func (s *state) SetStatus(id ids.ID, status choices.Status) error {
if p.Errored() {
return p.Err
}
s.c.Put(id, status)
return s.vm.db.Put(id.Bytes(), p.Bytes)
}
@ -186,8 +186,6 @@ func (s *state) SetIDs(id ids.ID, idSlice []ids.ID) error {
return s.vm.db.Delete(id.Bytes())
}
s.c.Put(id, idSlice)
size := wrappers.IntLen + hashing.HashLen*len(idSlice)
p := wrappers.Packer{Bytes: make([]byte, size)}
@ -203,5 +201,7 @@ func (s *state) SetIDs(id ids.ID, idSlice []ids.ID) error {
if p.Errored() {
return p.Err
}
s.c.Put(id, idSlice)
return s.vm.db.Put(id.Bytes(), p.Bytes)
}