mirror of https://github.com/poanetwork/gecko.git
Merge branch 'master' into get-address-api
This commit is contained in:
commit
56deaf7c9d
|
@ -40,6 +40,7 @@ import (
|
|||
const (
|
||||
defaultChannelSize = 1000
|
||||
requestTimeout = 2 * time.Second
|
||||
gossipFrequency = 10 * time.Second
|
||||
)
|
||||
|
||||
// Manager manages the chains running on this node.
|
||||
|
@ -146,7 +147,7 @@ func New(
|
|||
timeoutManager.Initialize(requestTimeout)
|
||||
go log.RecoverAndPanic(timeoutManager.Dispatch)
|
||||
|
||||
router.Initialize(log, &timeoutManager)
|
||||
router.Initialize(log, &timeoutManager, gossipFrequency)
|
||||
|
||||
m := &manager{
|
||||
stakingEnabled: stakingEnabled,
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import "C"
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
@ -29,9 +30,15 @@ import (
|
|||
"github.com/ava-labs/gecko/snow/validators"
|
||||
"github.com/ava-labs/gecko/utils/formatting"
|
||||
"github.com/ava-labs/gecko/utils/logging"
|
||||
"github.com/ava-labs/gecko/utils/random"
|
||||
"github.com/ava-labs/gecko/utils/timer"
|
||||
)
|
||||
|
||||
// GossipSize is the maximum number of peers to gossip a container to
|
||||
const (
|
||||
GossipSize = 50
|
||||
)
|
||||
|
||||
var (
|
||||
// VotingNet implements the SenderExternal interface.
|
||||
VotingNet = Voting{}
|
||||
|
@ -89,34 +96,7 @@ func (s *Voting) Shutdown() { s.executor.Stop() }
|
|||
|
||||
// Accept is called after every consensus decision
|
||||
func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error {
|
||||
peers := []salticidae.PeerID(nil)
|
||||
|
||||
allPeers, allIDs, _ := s.conns.Conns()
|
||||
for i, id := range allIDs {
|
||||
if !s.vdrs.Contains(id) {
|
||||
peers = append(peers, allPeers[i])
|
||||
}
|
||||
}
|
||||
|
||||
build := Builder{}
|
||||
msg, err := build.Put(chainID, 0, containerID, container)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Attempted to pack too large of a Put message.\nContainer length: %d: %w", len(container), err)
|
||||
}
|
||||
|
||||
s.log.Verbo("Sending a Put message to non-validators."+
|
||||
"\nNumber of Non-Validators: %d"+
|
||||
"\nChain: %s"+
|
||||
"\nContainer ID: %s"+
|
||||
"\nContainer:\n%s",
|
||||
len(peers),
|
||||
chainID,
|
||||
containerID,
|
||||
formatting.DumpBytes{Bytes: container},
|
||||
)
|
||||
s.send(msg, peers...)
|
||||
s.numPutSent.Add(float64(len(peers)))
|
||||
return nil
|
||||
return s.gossip(chainID, containerID, container)
|
||||
}
|
||||
|
||||
// GetAcceptedFrontier implements the Sender interface.
|
||||
|
@ -412,6 +392,13 @@ func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32
|
|||
s.numChitsSent.Inc()
|
||||
}
|
||||
|
||||
// Gossip attempts to gossip the container to the network
|
||||
func (s *Voting) Gossip(chainID, containerID ids.ID, container []byte) {
|
||||
if err := s.gossip(chainID, containerID, container); err != nil {
|
||||
s.log.Error("Error gossiping container %s to %s\n%s", containerID, chainID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Voting) send(msg Msg, peers ...salticidae.PeerID) {
|
||||
ds := msg.DataStream()
|
||||
defer ds.Free()
|
||||
|
@ -429,6 +416,41 @@ func (s *Voting) send(msg Msg, peers ...salticidae.PeerID) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Voting) gossip(chainID, containerID ids.ID, container []byte) error {
|
||||
allPeers := s.conns.PeerIDs()
|
||||
|
||||
numToGossip := GossipSize
|
||||
if numToGossip > len(allPeers) {
|
||||
numToGossip = len(allPeers)
|
||||
}
|
||||
peers := make([]salticidae.PeerID, numToGossip)
|
||||
|
||||
sampler := random.Uniform{N: len(allPeers)}
|
||||
for i := range peers {
|
||||
peers[i] = allPeers[sampler.Sample()]
|
||||
}
|
||||
|
||||
build := Builder{}
|
||||
msg, err := build.Put(chainID, math.MaxUint32, containerID, container)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Attempted to pack too large of a Put message.\nContainer length: %d: %w", len(container), err)
|
||||
}
|
||||
|
||||
s.log.Verbo("Sending a Put message to peers."+
|
||||
"\nNumber of Peers: %d"+
|
||||
"\nChain: %s"+
|
||||
"\nContainer ID: %s"+
|
||||
"\nContainer:\n%s",
|
||||
len(peers),
|
||||
chainID,
|
||||
containerID,
|
||||
formatting.DumpBytes{Bytes: container},
|
||||
)
|
||||
s.send(msg, peers...)
|
||||
s.numPutSent.Add(float64(len(peers)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// getAcceptedFrontier handles the recept of a getAcceptedFrontier container
|
||||
// message for a chain
|
||||
//export getAcceptedFrontier
|
||||
|
|
|
@ -5,4 +5,4 @@
|
|||
SRC_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
||||
source "$SRC_DIR/env.sh"
|
||||
|
||||
go test -race -coverprofile=coverage.out -covermode=atomic ./...
|
||||
go test -race -timeout="30s" -coverprofile="coverage.out" -covermode="atomic" ./...
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
|
@ -60,7 +61,7 @@ func newConfig(t *testing.T) (BootstrapConfig, ids.ShortID, *common.SenderTest,
|
|||
|
||||
handler.Initialize(engine, make(chan common.Message), 1)
|
||||
timeouts.Initialize(0)
|
||||
router.Initialize(ctx.Log, timeouts)
|
||||
router.Initialize(ctx.Log, timeouts, time.Hour)
|
||||
|
||||
vtxBlocker, _ := queue.New(prefixdb.New([]byte("vtx"), db))
|
||||
txBlocker, _ := queue.New(prefixdb.New([]byte("tx"), db))
|
||||
|
|
|
@ -62,6 +62,26 @@ func (t *Transitive) finishBootstrapping() {
|
|||
t.bootstrapped = true
|
||||
}
|
||||
|
||||
// Gossip implements the Engine interface
|
||||
func (t *Transitive) Gossip() {
|
||||
edge := t.Config.State.Edge()
|
||||
if len(edge) == 0 {
|
||||
t.Config.Context.Log.Debug("Dropping gossip request as no vertices have been accepted")
|
||||
return
|
||||
}
|
||||
|
||||
sampler := random.Uniform{N: len(edge)}
|
||||
vtxID := edge[sampler.Sample()]
|
||||
vtx, err := t.Config.State.GetVertex(vtxID)
|
||||
if err != nil {
|
||||
t.Config.Context.Log.Warn("Dropping gossip request as %s couldn't be loaded due to %s", vtxID, err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Config.Context.Log.Debug("Gossiping %s as accepted to the network", vtxID)
|
||||
t.Config.Sender.Gossip(vtxID, vtx.Bytes())
|
||||
}
|
||||
|
||||
// Shutdown implements the Engine interface
|
||||
func (t *Transitive) Shutdown() {
|
||||
t.Config.Context.Log.Info("Shutting down Avalanche consensus")
|
||||
|
|
|
@ -2536,3 +2536,54 @@ func TestEnginePartiallyValidVertex(t *testing.T) {
|
|||
|
||||
te.insert(vtx)
|
||||
}
|
||||
|
||||
func TestEngineGossip(t *testing.T) {
|
||||
config := DefaultConfig()
|
||||
|
||||
sender := &common.SenderTest{}
|
||||
sender.T = t
|
||||
config.Sender = sender
|
||||
|
||||
sender.Default(true)
|
||||
|
||||
st := &stateTest{t: t}
|
||||
config.State = st
|
||||
|
||||
gVtx := &Vtx{
|
||||
id: GenerateID(),
|
||||
status: choices.Accepted,
|
||||
}
|
||||
|
||||
te := &Transitive{}
|
||||
te.Initialize(config)
|
||||
te.finishBootstrapping()
|
||||
|
||||
st.edge = func() []ids.ID { return []ids.ID{gVtx.ID()} }
|
||||
st.getVertex = func(vtxID ids.ID) (avalanche.Vertex, error) {
|
||||
switch {
|
||||
case vtxID.Equals(gVtx.ID()):
|
||||
return gVtx, nil
|
||||
}
|
||||
t.Fatal(errUnknownVertex)
|
||||
return nil, errUnknownVertex
|
||||
}
|
||||
|
||||
called := new(bool)
|
||||
sender.GossipF = func(vtxID ids.ID, vtxBytes []byte) {
|
||||
*called = true
|
||||
switch {
|
||||
case !vtxID.Equals(gVtx.ID()):
|
||||
t.Fatal(errUnknownVertex)
|
||||
}
|
||||
switch {
|
||||
case !bytes.Equal(vtxBytes, gVtx.Bytes()):
|
||||
t.Fatal(errUnknownVertex)
|
||||
}
|
||||
}
|
||||
|
||||
te.Gossip()
|
||||
|
||||
if !*called {
|
||||
t.Fatalf("Should have gossiped the vertex")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,9 @@ type InternalHandler interface {
|
|||
// Startup this engine.
|
||||
Startup()
|
||||
|
||||
// Gossip to the network a container on the accepted frontier
|
||||
Gossip()
|
||||
|
||||
// Shutdown this engine.
|
||||
Shutdown()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ type Sender interface {
|
|||
AcceptedSender
|
||||
FetchSender
|
||||
QuerySender
|
||||
Gossiper
|
||||
}
|
||||
|
||||
// FrontierSender defines how a consensus engine sends frontier messages to
|
||||
|
@ -70,3 +71,10 @@ type QuerySender interface {
|
|||
// Chits sends chits to the specified validator
|
||||
Chits(validatorID ids.ShortID, requestID uint32, votes ids.Set)
|
||||
}
|
||||
|
||||
// Gossiper defines how a consensus engine gossips a container on the accepted
|
||||
// frontier to other validators
|
||||
type Gossiper interface {
|
||||
// Gossip gossips the provided container throughout the network
|
||||
Gossip(containerID ids.ID, container []byte)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ type EngineTest struct {
|
|||
T *testing.T
|
||||
|
||||
CantStartup,
|
||||
CantGossip,
|
||||
CantShutdown,
|
||||
|
||||
CantContext,
|
||||
|
@ -38,7 +39,7 @@ type EngineTest struct {
|
|||
CantQueryFailed,
|
||||
CantChits bool
|
||||
|
||||
StartupF, ShutdownF func()
|
||||
StartupF, GossipF, ShutdownF func()
|
||||
ContextF func() *snow.Context
|
||||
NotifyF func(Message)
|
||||
GetF, GetFailedF, PullQueryF func(validatorID ids.ShortID, requestID uint32, containerID ids.ID)
|
||||
|
@ -50,6 +51,7 @@ type EngineTest struct {
|
|||
// Default ...
|
||||
func (e *EngineTest) Default(cant bool) {
|
||||
e.CantStartup = cant
|
||||
e.CantGossip = cant
|
||||
e.CantShutdown = cant
|
||||
|
||||
e.CantContext = cant
|
||||
|
@ -83,6 +85,15 @@ func (e *EngineTest) Startup() {
|
|||
}
|
||||
}
|
||||
|
||||
// Gossip ...
|
||||
func (e *EngineTest) Gossip() {
|
||||
if e.GossipF != nil {
|
||||
e.GossipF()
|
||||
} else if e.CantGossip && e.T != nil {
|
||||
e.T.Fatalf("Unexpectedly called Gossip")
|
||||
}
|
||||
}
|
||||
|
||||
// Shutdown ...
|
||||
func (e *EngineTest) Shutdown() {
|
||||
if e.ShutdownF != nil {
|
||||
|
|
|
@ -16,7 +16,8 @@ type SenderTest struct {
|
|||
CantGetAcceptedFrontier, CantAcceptedFrontier,
|
||||
CantGetAccepted, CantAccepted,
|
||||
CantGet, CantPut,
|
||||
CantPullQuery, CantPushQuery, CantChits bool
|
||||
CantPullQuery, CantPushQuery, CantChits,
|
||||
CantGossip bool
|
||||
|
||||
GetAcceptedFrontierF func(ids.ShortSet, uint32)
|
||||
AcceptedFrontierF func(ids.ShortID, uint32, ids.Set)
|
||||
|
@ -27,6 +28,7 @@ type SenderTest struct {
|
|||
PushQueryF func(ids.ShortSet, uint32, ids.ID, []byte)
|
||||
PullQueryF func(ids.ShortSet, uint32, ids.ID)
|
||||
ChitsF func(ids.ShortID, uint32, ids.Set)
|
||||
GossipF func(ids.ID, []byte)
|
||||
}
|
||||
|
||||
// Default set the default callable value to [cant]
|
||||
|
@ -40,6 +42,7 @@ func (s *SenderTest) Default(cant bool) {
|
|||
s.CantPullQuery = cant
|
||||
s.CantPushQuery = cant
|
||||
s.CantChits = cant
|
||||
s.CantGossip = cant
|
||||
}
|
||||
|
||||
// GetAcceptedFrontier calls GetAcceptedFrontierF if it was initialized. If it
|
||||
|
@ -140,3 +143,14 @@ func (s *SenderTest) Chits(vdr ids.ShortID, requestID uint32, votes ids.Set) {
|
|||
s.T.Fatalf("Unexpectedly called Chits")
|
||||
}
|
||||
}
|
||||
|
||||
// Gossip calls GossipF if it was initialized. If it wasn't initialized and this
|
||||
// function shouldn't be called and testing was initialized, then testing will
|
||||
// fail.
|
||||
func (s *SenderTest) Gossip(containerID ids.ID, container []byte) {
|
||||
if s.GossipF != nil {
|
||||
s.GossipF(containerID, container)
|
||||
} else if s.CantGossip && s.T != nil {
|
||||
s.T.Fatalf("Unexpectedly called Gossip")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
|
@ -54,7 +55,7 @@ func newConfig(t *testing.T) (BootstrapConfig, ids.ShortID, *common.SenderTest,
|
|||
|
||||
handler.Initialize(engine, make(chan common.Message), 1)
|
||||
timeouts.Initialize(0)
|
||||
router.Initialize(ctx.Log, timeouts)
|
||||
router.Initialize(ctx.Log, timeouts, time.Hour)
|
||||
|
||||
blocker, _ := queue.New(db)
|
||||
|
||||
|
|
|
@ -65,6 +65,19 @@ func (t *Transitive) finishBootstrapping() {
|
|||
}
|
||||
}
|
||||
|
||||
// Gossip implements the Engine interface
|
||||
func (t *Transitive) Gossip() {
|
||||
blkID := t.Config.VM.LastAccepted()
|
||||
blk, err := t.Config.VM.GetBlock(blkID)
|
||||
if err != nil {
|
||||
t.Config.Context.Log.Warn("Dropping gossip request as %s couldn't be loaded due to %s", blkID, err)
|
||||
return
|
||||
}
|
||||
|
||||
t.Config.Context.Log.Debug("Gossiping %s as accepted to the network", blkID)
|
||||
t.Config.Sender.Gossip(blkID, blk.Bytes())
|
||||
}
|
||||
|
||||
// Shutdown implements the Engine interface
|
||||
func (t *Transitive) Shutdown() {
|
||||
t.Config.Context.Log.Info("Shutting down Snowman consensus")
|
||||
|
|
|
@ -1187,3 +1187,36 @@ func TestEngineUndeclaredDependencyDeadlock(t *testing.T) {
|
|||
t.Fatalf("Should have bubbled invalid votes to the valid parent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineGossip(t *testing.T) {
|
||||
_, _, sender, vm, te, gBlk := setup(t)
|
||||
|
||||
vm.LastAcceptedF = func() ids.ID { return gBlk.ID() }
|
||||
vm.GetBlockF = func(blkID ids.ID) (snowman.Block, error) {
|
||||
switch {
|
||||
case blkID.Equals(gBlk.ID()):
|
||||
return gBlk, nil
|
||||
}
|
||||
t.Fatal(errUnknownBlock)
|
||||
return nil, errUnknownBlock
|
||||
}
|
||||
|
||||
called := new(bool)
|
||||
sender.GossipF = func(blkID ids.ID, blkBytes []byte) {
|
||||
*called = true
|
||||
switch {
|
||||
case !blkID.Equals(gBlk.ID()):
|
||||
t.Fatal(errUnknownBlock)
|
||||
}
|
||||
switch {
|
||||
case !bytes.Equal(blkBytes, gBlk.Bytes()):
|
||||
t.Fatal(errUnknownBytes)
|
||||
}
|
||||
}
|
||||
|
||||
te.Gossip()
|
||||
|
||||
if !*called {
|
||||
t.Fatalf("Should have gossiped the block")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ func (h *Handler) dispatchMsg(msg message) bool {
|
|||
h.engine.Chits(msg.validatorID, msg.requestID, msg.containerIDs)
|
||||
case notifyMsg:
|
||||
h.engine.Notify(msg.notification)
|
||||
case gossipMsg:
|
||||
h.engine.Gossip()
|
||||
case shutdownMsg:
|
||||
h.engine.Shutdown()
|
||||
return false
|
||||
|
@ -232,6 +234,9 @@ func (h *Handler) QueryFailed(validatorID ids.ShortID, requestID uint32) {
|
|||
}
|
||||
}
|
||||
|
||||
// Gossip passes a gossip request to the consensus engine
|
||||
func (h *Handler) Gossip() { h.msgs <- message{messageType: gossipMsg} }
|
||||
|
||||
// Shutdown shuts down the dispatcher
|
||||
func (h *Handler) Shutdown() { h.msgs <- message{messageType: shutdownMsg}; h.wg.Wait() }
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ const (
|
|||
chitsMsg
|
||||
queryFailedMsg
|
||||
notifyMsg
|
||||
gossipMsg
|
||||
shutdownMsg
|
||||
)
|
||||
|
||||
|
@ -87,6 +88,8 @@ func (t msgType) String() string {
|
|||
return "Query Failed Message"
|
||||
case notifyMsg:
|
||||
return "Notify Message"
|
||||
case gossipMsg:
|
||||
return "Gossip Message"
|
||||
case shutdownMsg:
|
||||
return "Shutdown Message"
|
||||
default:
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
package router
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow/networking/handler"
|
||||
"github.com/ava-labs/gecko/snow/networking/timeout"
|
||||
|
@ -19,7 +21,7 @@ type Router interface {
|
|||
AddChain(chain *handler.Handler)
|
||||
RemoveChain(chainID ids.ID)
|
||||
Shutdown()
|
||||
Initialize(log logging.Logger, timeouts *timeout.Manager)
|
||||
Initialize(log logging.Logger, timeouts *timeout.Manager, gossipFrequency time.Duration)
|
||||
}
|
||||
|
||||
// ExternalRouter routes messages from the network to the
|
||||
|
|
|
@ -5,11 +5,13 @@ package router
|
|||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow/networking/handler"
|
||||
"github.com/ava-labs/gecko/snow/networking/timeout"
|
||||
"github.com/ava-labs/gecko/utils/logging"
|
||||
"github.com/ava-labs/gecko/utils/timer"
|
||||
)
|
||||
|
||||
// ChainRouter routes incoming messages from the validator network
|
||||
|
@ -21,15 +23,24 @@ type ChainRouter struct {
|
|||
lock sync.RWMutex
|
||||
chains map[[32]byte]*handler.Handler
|
||||
timeouts *timeout.Manager
|
||||
gossiper *timer.Repeater
|
||||
}
|
||||
|
||||
// Initialize the router
|
||||
// When this router receives an incoming message, it cancels the timeout in [timeouts]
|
||||
// associated with the request that caused the incoming message, if applicable
|
||||
func (sr *ChainRouter) Initialize(log logging.Logger, timeouts *timeout.Manager) {
|
||||
// Initialize the router.
|
||||
//
|
||||
// When this router receives an incoming message, it cancels the timeout in
|
||||
// [timeouts] associated with the request that caused the incoming message, if
|
||||
// applicable.
|
||||
//
|
||||
// This router also fires a gossip event every [gossipFrequency] to the engine,
|
||||
// notifying the engine it should gossip it's accepted set.
|
||||
func (sr *ChainRouter) Initialize(log logging.Logger, timeouts *timeout.Manager, gossipFrequency time.Duration) {
|
||||
sr.log = log
|
||||
sr.chains = make(map[[32]byte]*handler.Handler)
|
||||
sr.timeouts = timeouts
|
||||
sr.gossiper = timer.NewRepeater(sr.Gossip, gossipFrequency)
|
||||
|
||||
go log.RecoverAndPanic(sr.gossiper.Dispatch)
|
||||
}
|
||||
|
||||
// AddChain registers the specified chain so that incoming
|
||||
|
@ -255,4 +266,19 @@ func (sr *ChainRouter) shutdown() {
|
|||
for _, chain := range sr.chains {
|
||||
chain.Shutdown()
|
||||
}
|
||||
sr.gossiper.Stop()
|
||||
}
|
||||
|
||||
// Gossip accepted containers
|
||||
func (sr *ChainRouter) Gossip() {
|
||||
sr.lock.RLock()
|
||||
defer sr.lock.RUnlock()
|
||||
|
||||
sr.gossip()
|
||||
}
|
||||
|
||||
func (sr *ChainRouter) gossip() {
|
||||
for _, chain := range sr.chains {
|
||||
chain.Gossip()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,6 @@ type ExternalSender interface {
|
|||
PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte)
|
||||
PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID)
|
||||
Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set)
|
||||
|
||||
Gossip(chainID ids.ID, containerID ids.ID, container []byte)
|
||||
}
|
||||
|
|
|
@ -163,3 +163,9 @@ func (s *Sender) Chits(validatorID ids.ShortID, requestID uint32, votes ids.Set)
|
|||
}
|
||||
s.sender.Chits(validatorID, s.ctx.ChainID, requestID, votes)
|
||||
}
|
||||
|
||||
// Gossip the provided container
|
||||
func (s *Sender) Gossip(containerID ids.ID, container []byte) {
|
||||
s.ctx.Log.Verbo("Gossiping %s", containerID)
|
||||
s.sender.Gossip(s.ctx.ChainID, containerID, container)
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestTimeout(t *testing.T) {
|
|||
go tm.Dispatch()
|
||||
|
||||
router := router.ChainRouter{}
|
||||
router.Initialize(logging.NoLog{}, &tm)
|
||||
router.Initialize(logging.NoLog{}, &tm, time.Hour)
|
||||
|
||||
sender := Sender{}
|
||||
sender.Initialize(snow.DefaultContextTest(), &ExternalSenderTest{}, &router, &tm)
|
||||
|
|
|
@ -17,7 +17,8 @@ type ExternalSenderTest struct {
|
|||
CantGetAcceptedFrontier, CantAcceptedFrontier,
|
||||
CantGetAccepted, CantAccepted,
|
||||
CantGet, CantPut,
|
||||
CantPullQuery, CantPushQuery, CantChits bool
|
||||
CantPullQuery, CantPushQuery, CantChits,
|
||||
CantGossip bool
|
||||
|
||||
GetAcceptedFrontierF func(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32)
|
||||
AcceptedFrontierF func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set)
|
||||
|
@ -28,6 +29,7 @@ type ExternalSenderTest struct {
|
|||
PushQueryF func(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte)
|
||||
PullQueryF func(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID)
|
||||
ChitsF func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set)
|
||||
GossipF func(chainID ids.ID, containerID ids.ID, container []byte)
|
||||
}
|
||||
|
||||
// Default set the default callable value to [cant]
|
||||
|
@ -41,6 +43,7 @@ func (s *ExternalSenderTest) Default(cant bool) {
|
|||
s.CantPullQuery = cant
|
||||
s.CantPushQuery = cant
|
||||
s.CantChits = cant
|
||||
s.CantGossip = cant
|
||||
}
|
||||
|
||||
// GetAcceptedFrontier calls GetAcceptedFrontierF if it was initialized. If it
|
||||
|
@ -159,3 +162,16 @@ func (s *ExternalSenderTest) Chits(vdr ids.ShortID, chainID ids.ID, requestID ui
|
|||
s.B.Fatalf("Unexpectedly called Chits")
|
||||
}
|
||||
}
|
||||
|
||||
// Gossip calls GossipF if it was initialized. If it wasn't initialized and this
|
||||
// function shouldn't be called and testing was initialized, then testing will
|
||||
// fail.
|
||||
func (s *ExternalSenderTest) Gossip(chainID ids.ID, containerID ids.ID, container []byte) {
|
||||
if s.GossipF != nil {
|
||||
s.GossipF(chainID, containerID, container)
|
||||
} else if s.CantGossip && s.T != nil {
|
||||
s.T.Fatalf("Unexpectedly called Gossip")
|
||||
} else if s.CantGossip && s.B != nil {
|
||||
s.B.Fatalf("Unexpectedly called Gossip")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -624,6 +624,7 @@ func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
|
|||
func TestBaseTxSemanticVerify(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
|
@ -688,6 +689,7 @@ func TestBaseTxSemanticVerify(t *testing.T) {
|
|||
func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||
|
||||
|
@ -737,6 +739,7 @@ func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
|
|||
func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||
|
||||
|
@ -809,6 +812,7 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
|
@ -894,6 +898,7 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
|
|||
func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
|
@ -945,6 +950,7 @@ func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
|
|||
func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
|
@ -1009,6 +1015,7 @@ func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
|
|||
func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
|
@ -1141,6 +1148,7 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.PendingTxs()
|
||||
|
||||
|
@ -1272,6 +1280,7 @@ func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.PendingTxs()
|
||||
|
||||
|
@ -1437,6 +1446,7 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.PendingTxs()
|
||||
|
||||
|
@ -1586,6 +1596,7 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
vm.PendingTxs()
|
||||
|
||||
|
|
|
@ -217,6 +217,7 @@ func TestIssueExportTx(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
|
@ -350,6 +351,7 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
defer vm.Shutdown()
|
||||
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
|
|
|
@ -222,12 +222,14 @@ func TestIssueImportTx(t *testing.T) {
|
|||
}
|
||||
|
||||
ctx.Lock.Unlock()
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
msg := <-issuer
|
||||
if msg != common.PendingTxs {
|
||||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
// FIXME?: Is it safe to call vm.PendingTXs() called without the lock?
|
||||
txs := vm.PendingTxs()
|
||||
if len(txs) != 1 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 1)
|
||||
|
@ -265,6 +267,7 @@ func TestForceAcceptImportTx(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{platform: platformID}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
|
|
|
@ -18,8 +18,12 @@ import (
|
|||
func TestPrefixedSetsAndGets(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
ctx.Lock.Unlock()
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state
|
||||
|
||||
// FIXME? is it safe to call vm.codec.RegisterType() without the lock?
|
||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||
|
||||
utxo := &ava.UTXO{
|
||||
|
@ -51,6 +55,7 @@ func TestPrefixedSetsAndGets(t *testing.T) {
|
|||
}},
|
||||
}}
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.Marshal() without the lock?
|
||||
unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -70,6 +75,7 @@ func TestPrefixedSetsAndGets(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.Marshal() without the lock?
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -113,8 +119,12 @@ func TestPrefixedSetsAndGets(t *testing.T) {
|
|||
func TestPrefixedFundingNoAddresses(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
ctx.Lock.Unlock()
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state
|
||||
|
||||
// FIXME? is it safe to call vm.codec.RegisterType() without the lock?
|
||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||
|
||||
utxo := &ava.UTXO{
|
||||
|
@ -137,8 +147,12 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
|
|||
func TestPrefixedFundingAddresses(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
ctx.Lock.Unlock()
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state
|
||||
|
||||
// FIXME? is it safe to call vm.codec.RegisterType() without the lock?
|
||||
vm.codec.RegisterType(&testAddressable{})
|
||||
|
||||
utxo := &ava.UTXO{
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
|
@ -15,7 +16,7 @@ import (
|
|||
"github.com/ava-labs/gecko/utils/formatting"
|
||||
"github.com/ava-labs/gecko/utils/hashing"
|
||||
"github.com/ava-labs/gecko/utils/json"
|
||||
"github.com/ava-labs/gecko/utils/math"
|
||||
safemath "github.com/ava-labs/gecko/utils/math"
|
||||
"github.com/ava-labs/gecko/vms/components/ava"
|
||||
"github.com/ava-labs/gecko/vms/components/verify"
|
||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||
|
@ -221,7 +222,7 @@ func (service *Service) GetBalance(r *http.Request, args *GetBalanceArgs, reply
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
amt, err := math.Add64(transferable.Amount(), uint64(reply.Balance))
|
||||
amt, err := safemath.Add64(transferable.Amount(), uint64(reply.Balance))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -231,6 +232,78 @@ func (service *Service) GetBalance(r *http.Request, args *GetBalanceArgs, reply
|
|||
return nil
|
||||
}
|
||||
|
||||
// Balance ...
|
||||
type Balance struct {
|
||||
AssetID string `json:"asset"`
|
||||
Balance json.Uint64 `json:"balance"`
|
||||
}
|
||||
|
||||
// GetAllBalancesArgs are arguments for calling into GetAllBalances
|
||||
type GetAllBalancesArgs struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// GetAllBalancesReply is the response from a call to GetAllBalances
|
||||
type GetAllBalancesReply struct {
|
||||
Balances []Balance `json:"balances"`
|
||||
}
|
||||
|
||||
// GetAllBalances returns a map where:
|
||||
// Key: ID of an asset such that [args.Address] has a non-zero balance of the asset
|
||||
// Value: The balance of the asset held by the address
|
||||
// Note that balances include assets that the address only _partially_ owns
|
||||
// (ie is one of several addresses specified in a multi-sig)
|
||||
func (service *Service) GetAllBalances(r *http.Request, args *GetAllBalancesArgs, reply *GetAllBalancesReply) error {
|
||||
service.vm.ctx.Log.Verbo("GetAllBalances called with address: %s", args.Address)
|
||||
|
||||
address, err := service.vm.Parse(args.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't parse given address: %s", err)
|
||||
}
|
||||
addrAsSet := ids.Set{}
|
||||
addrAsSet.Add(ids.NewID(hashing.ComputeHash256Array(address)))
|
||||
|
||||
utxos, err := service.vm.GetUTXOs(addrAsSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't get address's UTXOs: %s", err)
|
||||
}
|
||||
|
||||
assetIDs := ids.Set{} // IDs of assets the address has a non-zero balance of
|
||||
balances := make(map[[32]byte]uint64, 0) // key: ID (as bytes). value: balance of that asset
|
||||
for _, utxo := range utxos {
|
||||
transferable, ok := utxo.Out.(ava.Transferable)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
assetID := utxo.AssetID()
|
||||
assetIDs.Add(assetID)
|
||||
balance := balances[assetID.Key()] // 0 if key doesn't exist
|
||||
balance, err := safemath.Add64(transferable.Amount(), balance)
|
||||
if err != nil {
|
||||
balances[assetID.Key()] = math.MaxUint64
|
||||
} else {
|
||||
balances[assetID.Key()] = balance
|
||||
}
|
||||
}
|
||||
|
||||
reply.Balances = make([]Balance, assetIDs.Len())
|
||||
for i, assetID := range assetIDs.List() {
|
||||
if alias, err := service.vm.PrimaryAlias(assetID); err == nil {
|
||||
reply.Balances[i] = Balance{
|
||||
AssetID: alias,
|
||||
Balance: json.Uint64(balances[assetID.Key()]),
|
||||
}
|
||||
} else {
|
||||
reply.Balances[i] = Balance{
|
||||
AssetID: assetID.String(),
|
||||
Balance: json.Uint64(balances[assetID.Key()]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateFixedCapAssetArgs are arguments for passing into CreateFixedCapAsset requests
|
||||
type CreateFixedCapAssetArgs struct {
|
||||
Username string `json:"username"`
|
||||
|
@ -646,7 +719,7 @@ func (service *Service) Send(r *http.Request, args *SendArgs, reply *SendReply)
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
spent, err := math.Add64(amountSpent, input.Amount())
|
||||
spent, err := safemath.Add64(amountSpent, input.Amount())
|
||||
if err != nil {
|
||||
return errSpendOverflow
|
||||
}
|
||||
|
@ -1053,7 +1126,7 @@ func (service *Service) ImportAVA(_ *http.Request, args *ImportAVAArgs, reply *I
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
spent, err := math.Add64(amount, input.Amount())
|
||||
spent, err := safemath.Add64(amount, input.Amount())
|
||||
if err != nil {
|
||||
return errSpendOverflow
|
||||
}
|
||||
|
@ -1197,7 +1270,7 @@ func (service *Service) ExportAVA(_ *http.Request, args *ExportAVAArgs, reply *E
|
|||
if !ok {
|
||||
continue
|
||||
}
|
||||
spent, err := math.Add64(amountSpent, input.Amount())
|
||||
spent, err := safemath.Add64(amountSpent, input.Amount())
|
||||
if err != nil {
|
||||
return errSpendOverflow
|
||||
}
|
||||
|
|
|
@ -9,36 +9,12 @@ import (
|
|||
|
||||
"github.com/ava-labs/gecko/snow/choices"
|
||||
|
||||
"github.com/ava-labs/gecko/database/memdb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/snow/engine/common"
|
||||
"github.com/ava-labs/gecko/utils/formatting"
|
||||
"github.com/ava-labs/gecko/vms/secp256k1fx"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) ([]byte, *VM, *Service) {
|
||||
genesisBytes := BuildGenesisTest(t)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
|
||||
// This VM initilialzation is very similar to that done by GenesisVM().
|
||||
// However replacing the body of this function, with a call to GenesisVM
|
||||
// causes a timeout while executing the test suite.
|
||||
// https://github.com/ava-labs/gecko/pull/59#pullrequestreview-392478636
|
||||
vm := &VM{}
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
genesisBytes,
|
||||
make(chan common.Message, 1),
|
||||
[]*common.Fx{&common.Fx{
|
||||
ID: ids.Empty,
|
||||
Fx: &secp256k1fx.Fx{},
|
||||
}},
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
genesisBytes, _, vm := GenesisVM(t)
|
||||
s := &Service{vm: vm}
|
||||
return genesisBytes, vm, s
|
||||
}
|
||||
|
|
|
@ -16,7 +16,10 @@ import (
|
|||
|
||||
func TestStateIDs(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state.state
|
||||
|
||||
id0 := ids.NewID([32]byte{0xff, 0})
|
||||
|
@ -126,7 +129,10 @@ func TestStateIDs(t *testing.T) {
|
|||
|
||||
func TestStateStatuses(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state.state
|
||||
|
||||
if _, err := state.Status(ids.Empty); err == nil {
|
||||
|
@ -175,9 +181,13 @@ func TestStateStatuses(t *testing.T) {
|
|||
|
||||
func TestStateUTXOs(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state.state
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.RegisterType() without the lock?
|
||||
vm.codec.RegisterType(&ava.TestVerifiable{})
|
||||
|
||||
if _, err := state.UTXO(ids.Empty); err == nil {
|
||||
|
@ -246,9 +256,13 @@ func TestStateUTXOs(t *testing.T) {
|
|||
|
||||
func TestStateTXs(t *testing.T) {
|
||||
_, _, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Unlock()
|
||||
|
||||
// FIXME? is it safe to access vm.state.state without the lock?
|
||||
state := vm.state.state
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.RegisterType() without the lock?
|
||||
vm.codec.RegisterType(&ava.TestTransferable{})
|
||||
|
||||
if _, err := state.Tx(ids.Empty); err == nil {
|
||||
|
@ -275,6 +289,7 @@ func TestStateTXs(t *testing.T) {
|
|||
}},
|
||||
}}
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.Marshal() without the lock?
|
||||
unsignedBytes, err := vm.codec.Marshal(tx.UnsignedTx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -294,6 +309,7 @@ func TestStateTXs(t *testing.T) {
|
|||
},
|
||||
})
|
||||
|
||||
// FIXME? Is it safe to call vm.codec.Marshal() without the lock?
|
||||
b, err := vm.codec.Marshal(tx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -204,7 +204,12 @@ func (vm *VM) Shutdown() {
|
|||
return
|
||||
}
|
||||
|
||||
// There is a potential deadlock if the timer is about to execute a timeout.
|
||||
// So, the lock must be released before stopping the timer.
|
||||
vm.ctx.Lock.Unlock()
|
||||
vm.timer.Stop()
|
||||
vm.ctx.Lock.Lock()
|
||||
|
||||
if err := vm.baseDB.Close(); err != nil {
|
||||
vm.ctx.Log.Error("Closing the database failed with %s", err)
|
||||
}
|
||||
|
|
|
@ -396,6 +396,7 @@ func TestInvalidGenesis(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
/*context=*/ ctx,
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -415,6 +416,7 @@ func TestInvalidFx(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
/*context=*/ ctx,
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -436,6 +438,7 @@ func TestFxInitializationFailure(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
/*context=*/ ctx,
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -457,6 +460,7 @@ func (tx *testTxBytes) UnsignedBytes() []byte { return tx.unsignedBytes }
|
|||
|
||||
func TestIssueTx(t *testing.T) {
|
||||
genesisBytes, issuer, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
newTx := NewTx(t, genesisBytes, vm)
|
||||
|
||||
|
@ -474,6 +478,7 @@ func TestIssueTx(t *testing.T) {
|
|||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
// FIXME? vm.PendingTxs called after lock released.
|
||||
if txs := vm.PendingTxs(); len(txs) != 1 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 1)
|
||||
}
|
||||
|
@ -503,6 +508,7 @@ func TestGenesisGetUTXOs(t *testing.T) {
|
|||
// transaction should be issued successfully.
|
||||
func TestIssueDependentTx(t *testing.T) {
|
||||
genesisBytes, issuer, vm := GenesisVM(t)
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); ctx.Lock.Unlock() }()
|
||||
|
||||
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
|
||||
|
||||
|
@ -623,6 +629,7 @@ func TestIssueDependentTx(t *testing.T) {
|
|||
t.Fatalf("Wrong message")
|
||||
}
|
||||
|
||||
// FIXME? vm.PendingTxs called after lock released.
|
||||
if txs := vm.PendingTxs(); len(txs) != 2 {
|
||||
t.Fatalf("Should have returned %d tx(s)", 2)
|
||||
}
|
||||
|
@ -638,6 +645,7 @@ func TestIssueNFT(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
|
@ -796,6 +804,7 @@ func TestIssueProperty(t *testing.T) {
|
|||
defer ctx.Lock.Unlock()
|
||||
|
||||
vm := &VM{}
|
||||
defer vm.Shutdown()
|
||||
err := vm.Initialize(
|
||||
ctx,
|
||||
memdb.New(),
|
||||
|
|
|
@ -90,7 +90,9 @@ func (s *State) Status(id ids.ID) (choices.Status, error) {
|
|||
}
|
||||
|
||||
var status choices.Status
|
||||
s.Codec.Unmarshal(bytes, &status)
|
||||
if err := s.Codec.Unmarshal(bytes, &status); err != nil {
|
||||
return choices.Unknown, err
|
||||
}
|
||||
|
||||
s.Cache.Put(id, status)
|
||||
return status, nil
|
||||
|
@ -103,12 +105,12 @@ func (s *State) SetStatus(id ids.ID, status choices.Status) error {
|
|||
return s.DB.Delete(id.Bytes())
|
||||
}
|
||||
|
||||
s.Cache.Put(id, status)
|
||||
|
||||
bytes, err := s.Codec.Marshal(status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Cache.Put(id, status)
|
||||
return s.DB.Put(id.Bytes(), bytes)
|
||||
}
|
||||
|
||||
|
@ -142,12 +144,11 @@ func (s *State) SetIDs(id ids.ID, idSlice []ids.ID) error {
|
|||
return s.DB.Delete(id.Bytes())
|
||||
}
|
||||
|
||||
s.Cache.Put(id, idSlice)
|
||||
|
||||
bytes, err := s.Codec.Marshal(idSlice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Cache.Put(id, idSlice)
|
||||
return s.DB.Put(id.Bytes(), bytes)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: tx is nil
|
||||
var tx *addDefaultSubnetDelegatorTx
|
||||
|
@ -153,6 +154,7 @@ func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
|
|||
|
||||
func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: Proposed validator currently validating default subnet
|
||||
// but stops validating non-default subnet after stops validating default subnet
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
|
||||
func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: tx is nil
|
||||
var tx *addDefaultSubnetValidatorTx
|
||||
|
@ -216,6 +217,7 @@ func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
|
|||
// Test AddDefaultSubnetValidatorTx.SemanticVerify
|
||||
func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: Validator's start time too early
|
||||
tx, err := vm.newAddDefaultSubnetValidatorTx(
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
|
||||
func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: tx is nil
|
||||
var tx *addNonDefaultSubnetValidatorTx
|
||||
|
@ -202,6 +203,7 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
|
|||
|
||||
func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: Proposed validator currently validating default subnet
|
||||
// but stops validating non-default subnet after stops validating default subnet
|
||||
|
@ -596,6 +598,7 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
|
|||
// Test that marshalling/unmarshalling works
|
||||
func TestAddNonDefaultSubnetValidatorMarshal(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// valid tx
|
||||
tx, err := vm.newAddNonDefaultSubnetValidatorTx(
|
||||
|
|
|
@ -17,6 +17,8 @@ func TestAdvanceTimeTxSyntacticVerify(t *testing.T) {
|
|||
|
||||
// Case 2: Timestamp is ahead of synchrony bound
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
tx = &advanceTimeTx{
|
||||
Time: uint64(defaultGenesisTime.Add(Delta).Add(1 * time.Second).Unix()),
|
||||
vm: vm,
|
||||
|
@ -38,6 +40,7 @@ func TestAdvanceTimeTxSyntacticVerify(t *testing.T) {
|
|||
// Ensure semantic verification fails when proposed timestamp is at or before current timestamp
|
||||
func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
tx := &advanceTimeTx{
|
||||
Time: uint64(defaultGenesisTime.Unix()),
|
||||
|
@ -52,6 +55,7 @@ func TestAdvanceTimeTxTimestampTooEarly(t *testing.T) {
|
|||
// Ensure semantic verification fails when proposed timestamp is after next validator set change time
|
||||
func TestAdvanceTimeTxTimestampTooLate(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: Timestamp is after next validator start time
|
||||
// Add a pending validator
|
||||
|
@ -117,6 +121,7 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) {
|
|||
// Ensure semantic verification updates the current and pending validator sets correctly
|
||||
func TestAdvanceTimeTxUpdateValidators(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: Timestamp is after next validator start time
|
||||
// Add a pending validator
|
||||
|
@ -196,6 +201,7 @@ func TestAdvanceTimeTxUpdateValidators(t *testing.T) {
|
|||
// Test method InitiallyPrefersCommit
|
||||
func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Proposed advancing timestamp to 1 second after current timestamp
|
||||
tx, err := vm.newAdvanceTimeTx(defaultGenesisTime.Add(1 * time.Second))
|
||||
|
@ -217,6 +223,7 @@ func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) {
|
|||
// Ensure marshaling/unmarshaling works
|
||||
func TestAdvanceTimeTxUnmarshal(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
tx, err := vm.newAdvanceTimeTx(defaultGenesisTime)
|
||||
if err != nil {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
// test method SyntacticVerify
|
||||
func TestCreateChainTxSyntacticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: tx is nil
|
||||
var tx *CreateChainTx
|
||||
|
@ -142,6 +143,7 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
|
|||
// Ensure SemanticVerify fails when there are not enough control sigs
|
||||
func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Case 1: No control sigs (2 are needed)
|
||||
tx, err := vm.newCreateChainTx(
|
||||
|
@ -189,6 +191,7 @@ func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
|
|||
// Ensure SemanticVerify fails when an incorrect control signature is given
|
||||
func TestCreateChainTxWrongControlSig(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Generate new, random key to sign tx with
|
||||
factory := crypto.FactorySECP256K1R{}
|
||||
|
@ -222,6 +225,7 @@ func TestCreateChainTxWrongControlSig(t *testing.T) {
|
|||
// its validator set doesn't exist
|
||||
func TestCreateChainTxNoSuchSubnet(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
tx, err := vm.newCreateChainTx(
|
||||
defaultNonce+1,
|
||||
|
@ -245,6 +249,7 @@ func TestCreateChainTxNoSuchSubnet(t *testing.T) {
|
|||
|
||||
func TestCreateChainTxAlreadyExists(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// create a tx
|
||||
tx, err := vm.newCreateChainTx(
|
||||
|
@ -276,6 +281,7 @@ func TestCreateChainTxAlreadyExists(t *testing.T) {
|
|||
// Ensure valid tx passes semanticVerify
|
||||
func TestCreateChainTxValid(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// create a valid tx
|
||||
tx, err := vm.newCreateChainTx(
|
||||
|
|
|
@ -11,6 +11,8 @@ import (
|
|||
|
||||
func TestTxHeapStart(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
txHeap := EventHeap{SortByStartTime: true}
|
||||
|
||||
validator0, err := vm.newAddDefaultSubnetValidatorTx(
|
||||
|
@ -78,6 +80,8 @@ func TestTxHeapStart(t *testing.T) {
|
|||
|
||||
func TestTxHeapStop(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
txHeap := EventHeap{}
|
||||
|
||||
validator0, err := vm.newAddDefaultSubnetValidatorTx(
|
||||
|
@ -145,6 +149,8 @@ func TestTxHeapStop(t *testing.T) {
|
|||
|
||||
func TestTxHeapStartValidatorVsDelegatorOrdering(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
txHeap := EventHeap{SortByStartTime: true}
|
||||
|
||||
validator, err := vm.newAddDefaultSubnetValidatorTx(
|
||||
|
@ -186,6 +192,8 @@ func TestTxHeapStartValidatorVsDelegatorOrdering(t *testing.T) {
|
|||
|
||||
func TestTxHeapStopValidatorVsDelegatorOrdering(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
txHeap := EventHeap{}
|
||||
|
||||
validator, err := vm.newAddDefaultSubnetValidatorTx(
|
||||
|
|
|
@ -63,7 +63,7 @@ func (tx *rewardValidatorTx) SemanticVerify(db database.Database) (*versiondb.Da
|
|||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if db == nil {
|
||||
return nil, nil, nil, nil, errDbNil
|
||||
return nil, nil, nil, nil, errDBNil
|
||||
}
|
||||
|
||||
currentEvents, err := tx.vm.getCurrentValidators(db, DefaultSubnetID)
|
||||
|
|
|
@ -18,6 +18,8 @@ func TestRewardValidatorTxSyntacticVerify(t *testing.T) {
|
|||
}
|
||||
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
txID := ids.NewID([32]byte{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
tests := []test{
|
||||
|
@ -54,6 +56,8 @@ func TestRewardValidatorTxSyntacticVerify(t *testing.T) {
|
|||
|
||||
func TestRewardValidatorTxSemanticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
var nextToRemove *addDefaultSubnetValidatorTx
|
||||
currentValidators, err := vm.getCurrentValidators(vm.DB, DefaultSubnetID)
|
||||
if err != nil {
|
||||
|
@ -130,6 +134,7 @@ func TestRewardValidatorTxSemanticVerify(t *testing.T) {
|
|||
|
||||
func TestRewardDelegatorTxSemanticVerify(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
keyIntf1, err := vm.factory.NewPrivateKey()
|
||||
if err != nil {
|
||||
|
|
|
@ -322,7 +322,7 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
|
|||
return errGetAccounts
|
||||
}
|
||||
|
||||
var accounts []APIAccount
|
||||
reply.Accounts = []APIAccount{}
|
||||
for _, accountID := range accountIDs {
|
||||
account, err := service.vm.getAccount(service.vm.DB, accountID) // Get account whose ID is [accountID]
|
||||
if err != nil && err != database.ErrNotFound {
|
||||
|
@ -331,13 +331,12 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
|
|||
} else if err == database.ErrNotFound {
|
||||
account = newAccount(accountID, 0, 0)
|
||||
}
|
||||
accounts = append(accounts, APIAccount{
|
||||
reply.Accounts = append(reply.Accounts, APIAccount{
|
||||
Address: accountID,
|
||||
Nonce: json.Uint64(account.Nonce),
|
||||
Balance: json.Uint64(account.Balance),
|
||||
})
|
||||
}
|
||||
reply.Accounts = accounts
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,10 @@ import (
|
|||
// account IDs this user controls
|
||||
var accountIDsKey = ids.Empty.Bytes()
|
||||
|
||||
var errDbNil = errors.New("db uninitialized")
|
||||
var (
|
||||
errDBNil = errors.New("db uninitialized")
|
||||
errKeyNil = errors.New("key uninitialized")
|
||||
)
|
||||
|
||||
type user struct {
|
||||
// This user's database, acquired from the keystore
|
||||
|
@ -25,7 +28,7 @@ type user struct {
|
|||
// Get the IDs of the accounts controlled by this user
|
||||
func (u *user) getAccountIDs() ([]ids.ShortID, error) {
|
||||
if u.db == nil {
|
||||
return nil, errDbNil
|
||||
return nil, errDBNil
|
||||
}
|
||||
|
||||
// If user has no accounts, return empty list
|
||||
|
@ -34,8 +37,9 @@ func (u *user) getAccountIDs() ([]ids.ShortID, error) {
|
|||
return nil, errDB
|
||||
}
|
||||
if !hasAccounts {
|
||||
return make([]ids.ShortID, 0), nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// User has accounts. Get them.
|
||||
bytes, err := u.db.Get(accountIDsKey)
|
||||
if err != nil {
|
||||
|
@ -50,21 +54,24 @@ func (u *user) getAccountIDs() ([]ids.ShortID, error) {
|
|||
|
||||
// controlsAccount returns true iff this user controls the account
|
||||
// with the specified ID
|
||||
func (u *user) controlsAccount(ID ids.ShortID) (bool, error) {
|
||||
func (u *user) controlsAccount(accountID ids.ShortID) (bool, error) {
|
||||
if u.db == nil {
|
||||
return false, errDbNil
|
||||
return false, errDBNil
|
||||
}
|
||||
|
||||
if _, err := u.db.Get(ID.Bytes()); err == nil {
|
||||
return true, nil
|
||||
if accountID.IsZero() {
|
||||
return false, errEmptyAccountAddress
|
||||
}
|
||||
return false, nil
|
||||
return u.db.Has(accountID.Bytes())
|
||||
}
|
||||
|
||||
// putAccount persists that this user controls the account whose ID is
|
||||
// [privKey].PublicKey().Address()
|
||||
func (u *user) putAccount(privKey *crypto.PrivateKeySECP256K1R) error {
|
||||
newAccountID := privKey.PublicKey().Address() // Account thie privKey controls
|
||||
if privKey == nil {
|
||||
return errKeyNil
|
||||
}
|
||||
|
||||
newAccountID := privKey.PublicKey().Address() // Account the privKey controls
|
||||
controlsAccount, err := u.controlsAccount(newAccountID)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -102,7 +109,10 @@ func (u *user) putAccount(privKey *crypto.PrivateKeySECP256K1R) error {
|
|||
// Key returns the private key that controls the account with the specified ID
|
||||
func (u *user) getKey(accountID ids.ShortID) (*crypto.PrivateKeySECP256K1R, error) {
|
||||
if u.db == nil {
|
||||
return nil, errDbNil
|
||||
return nil, errDBNil
|
||||
}
|
||||
if accountID.IsZero() {
|
||||
return nil, errEmptyAccountAddress
|
||||
}
|
||||
|
||||
factory := crypto.FactorySECP256K1R{}
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package platformvm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/ava-labs/gecko/database/memdb"
|
||||
"github.com/ava-labs/gecko/ids"
|
||||
"github.com/ava-labs/gecko/utils/crypto"
|
||||
)
|
||||
|
||||
func TestUserNilDB(t *testing.T) {
|
||||
u := user{}
|
||||
|
||||
_, err := u.getAccountIDs()
|
||||
assert.Error(t, err, "nil db should have caused an error")
|
||||
|
||||
_, err = u.controlsAccount(ids.ShortEmpty)
|
||||
assert.Error(t, err, "nil db should have caused an error")
|
||||
|
||||
_, err = u.getKey(ids.ShortEmpty)
|
||||
assert.Error(t, err, "nil db should have caused an error")
|
||||
|
||||
factory := crypto.FactorySECP256K1R{}
|
||||
sk, err := factory.NewPrivateKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = u.putAccount(sk.(*crypto.PrivateKeySECP256K1R))
|
||||
assert.Error(t, err, "nil db should have caused an error")
|
||||
}
|
||||
|
||||
func TestUserClosedDB(t *testing.T) {
|
||||
db := memdb.New()
|
||||
err := db.Close()
|
||||
assert.NoError(t, err)
|
||||
|
||||
u := user{db: db}
|
||||
|
||||
_, err = u.getAccountIDs()
|
||||
assert.Error(t, err, "closed db should have caused an error")
|
||||
|
||||
_, err = u.controlsAccount(ids.ShortEmpty)
|
||||
assert.Error(t, err, "closed db should have caused an error")
|
||||
|
||||
_, err = u.getKey(ids.ShortEmpty)
|
||||
assert.Error(t, err, "closed db should have caused an error")
|
||||
|
||||
factory := crypto.FactorySECP256K1R{}
|
||||
sk, err := factory.NewPrivateKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = u.putAccount(sk.(*crypto.PrivateKeySECP256K1R))
|
||||
assert.Error(t, err, "closed db should have caused an error")
|
||||
}
|
||||
|
||||
func TestUserNilSK(t *testing.T) {
|
||||
u := user{db: memdb.New()}
|
||||
|
||||
err := u.putAccount(nil)
|
||||
assert.Error(t, err, "nil key should have caused an error")
|
||||
}
|
||||
|
||||
func TestUserNilAccount(t *testing.T) {
|
||||
u := user{db: memdb.New()}
|
||||
|
||||
_, err := u.controlsAccount(ids.ShortID{})
|
||||
assert.Error(t, err, "nil accountID should have caused an error")
|
||||
|
||||
_, err = u.getKey(ids.ShortID{})
|
||||
assert.Error(t, err, "nil accountID should have caused an error")
|
||||
}
|
||||
|
||||
func TestUser(t *testing.T) {
|
||||
u := user{db: memdb.New()}
|
||||
|
||||
accountIDs, err := u.getAccountIDs()
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, accountIDs, "new user shouldn't have accounts")
|
||||
|
||||
factory := crypto.FactorySECP256K1R{}
|
||||
sk, err := factory.NewPrivateKey()
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = u.putAccount(sk.(*crypto.PrivateKeySECP256K1R))
|
||||
assert.NoError(t, err)
|
||||
|
||||
addr := sk.PublicKey().Address()
|
||||
|
||||
ok, err := u.controlsAccount(addr)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, ok, "added account should have been marked as controlled")
|
||||
|
||||
savedSk, err := u.getKey(addr)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sk.Bytes(), savedSk.Bytes(), "wrong key returned")
|
||||
|
||||
accountIDs, err = u.getAccountIDs()
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, accountIDs, 1, "account should have been added")
|
||||
|
||||
savedAddr := accountIDs[0]
|
||||
equals := addr.Equals(savedAddr)
|
||||
assert.True(t, equals, "saved address should match provided address")
|
||||
}
|
|
@ -405,7 +405,12 @@ func (vm *VM) Shutdown() {
|
|||
return
|
||||
}
|
||||
|
||||
// There is a potential deadlock if the timer is about to execute a timeout.
|
||||
// So, the lock must be released before stopping the timer.
|
||||
vm.Ctx.Lock.Unlock()
|
||||
vm.timer.Stop()
|
||||
vm.Ctx.Lock.Lock()
|
||||
|
||||
if err := vm.DB.Close(); err != nil {
|
||||
vm.Ctx.Log.Error("Closing the database failed with %s", err)
|
||||
}
|
||||
|
|
|
@ -142,6 +142,8 @@ func defaultVM() *VM {
|
|||
db := memdb.New()
|
||||
msgChan := make(chan common.Message, 1)
|
||||
ctx := defaultContext()
|
||||
ctx.Lock.Lock()
|
||||
defer ctx.Lock.Unlock()
|
||||
if err := vm.Initialize(ctx, db, genesisBytes, msgChan, nil); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -233,8 +235,10 @@ func GenesisCurrentValidators() *EventHeap {
|
|||
// Ensure genesis state is parsed from bytes and stored correctly
|
||||
func TestGenesis(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Ensure the genesis block has been accepted and stored
|
||||
// FIXME? Calling vm.LastAccepted() without the lock
|
||||
genesisBlockID := vm.LastAccepted() // lastAccepted should be ID of genesis block
|
||||
genesisBlock, err := vm.getBlock(genesisBlockID)
|
||||
if err != nil {
|
||||
|
@ -302,6 +306,8 @@ func TestGenesis(t *testing.T) {
|
|||
// accept proposal to add validator to default subnet
|
||||
func TestAddDefaultSubnetValidatorCommit(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
startTime := defaultGenesisTime.Add(Delta).Add(1 * time.Second)
|
||||
endTime := startTime.Add(MinimumStakingDuration)
|
||||
key, _ := vm.factory.NewPrivateKey()
|
||||
|
@ -369,6 +375,8 @@ func TestAddDefaultSubnetValidatorCommit(t *testing.T) {
|
|||
// Reject proposal to add validator to default subnet
|
||||
func TestAddDefaultSubnetValidatorReject(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
startTime := defaultGenesisTime.Add(Delta).Add(1 * time.Second)
|
||||
endTime := startTime.Add(MinimumStakingDuration)
|
||||
key, _ := vm.factory.NewPrivateKey()
|
||||
|
@ -440,6 +448,8 @@ func TestAddDefaultSubnetValidatorReject(t *testing.T) {
|
|||
// Accept proposal to add validator to non-default subnet
|
||||
func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
startTime := defaultValidateStartTime.Add(Delta).Add(1 * time.Second)
|
||||
endTime := startTime.Add(MinimumStakingDuration)
|
||||
|
||||
|
@ -511,6 +521,8 @@ func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
|
|||
// Reject proposal to add validator to non-default subnet
|
||||
func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
startTime := defaultValidateStartTime.Add(Delta).Add(1 * time.Second)
|
||||
endTime := startTime.Add(MinimumStakingDuration)
|
||||
key, _ := vm.factory.NewPrivateKey()
|
||||
|
@ -584,6 +596,7 @@ func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
|
|||
// Test case where default subnet validator rewarded
|
||||
func TestRewardValidatorAccept(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Fast forward clock to time for genesis validators to leave
|
||||
vm.clock.Set(defaultValidateEndTime)
|
||||
|
@ -676,6 +689,7 @@ func TestRewardValidatorAccept(t *testing.T) {
|
|||
// Test case where default subnet validator not rewarded
|
||||
func TestRewardValidatorReject(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// Fast forward clock to time for genesis validators to leave
|
||||
vm.clock.Set(defaultValidateEndTime)
|
||||
|
@ -768,7 +782,9 @@ func TestRewardValidatorReject(t *testing.T) {
|
|||
// Ensure BuildBlock errors when there is no block to build
|
||||
func TestUnneededBuildBlock(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
// FIXME? Calling vm.BuildBlock without the lock
|
||||
if _, err := vm.BuildBlock(); err == nil {
|
||||
t.Fatalf("Should have errored on BuildBlock")
|
||||
}
|
||||
|
@ -777,6 +793,7 @@ func TestUnneededBuildBlock(t *testing.T) {
|
|||
// test acceptance of proposal to create a new chain
|
||||
func TestCreateChain(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
tx, err := vm.newCreateChainTx(
|
||||
defaultNonce+1,
|
||||
|
@ -839,6 +856,7 @@ func TestCreateChain(t *testing.T) {
|
|||
// 4) Advance timestamp to validator's end time (removing validator from current)
|
||||
func TestCreateSubnet(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
createSubnetTx, err := vm.newCreateSubnetTx(
|
||||
testNetworkID,
|
||||
|
@ -1084,6 +1102,7 @@ func TestCreateSubnet(t *testing.T) {
|
|||
// test asset import
|
||||
func TestAtomicImport(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
avmID := ids.Empty.Prefix(0)
|
||||
utxoID := ava.UTXOID{
|
||||
|
@ -1175,6 +1194,7 @@ func TestAtomicImport(t *testing.T) {
|
|||
// test optimistic asset import
|
||||
func TestOptimisticAtomicImport(t *testing.T) {
|
||||
vm := defaultVM()
|
||||
defer func() { vm.Ctx.Lock.Lock(); vm.Shutdown(); vm.Ctx.Lock.Unlock() }()
|
||||
|
||||
avmID := ids.Empty.Prefix(0)
|
||||
utxoID := ava.UTXOID{
|
||||
|
@ -1271,6 +1291,8 @@ func TestRestartPartiallyAccepted(t *testing.T) {
|
|||
|
||||
firstVM.clock.Set(defaultGenesisTime)
|
||||
firstCtx := defaultContext()
|
||||
firstCtx.Lock.Lock()
|
||||
|
||||
firstMsgChan := make(chan common.Message, 1)
|
||||
if err := firstVM.Initialize(firstCtx, db, genesisBytes, firstMsgChan, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1318,6 +1340,7 @@ func TestRestartPartiallyAccepted(t *testing.T) {
|
|||
}
|
||||
|
||||
firstVM.Shutdown()
|
||||
firstCtx.Lock.Unlock()
|
||||
|
||||
secondVM := &VM{
|
||||
SnowmanVM: &core.SnowmanVM{},
|
||||
|
@ -1330,6 +1353,9 @@ func TestRestartPartiallyAccepted(t *testing.T) {
|
|||
|
||||
secondVM.clock.Set(defaultGenesisTime)
|
||||
secondCtx := defaultContext()
|
||||
secondCtx.Lock.Lock()
|
||||
defer secondCtx.Lock.Unlock()
|
||||
|
||||
secondMsgChan := make(chan common.Message, 1)
|
||||
if err := secondVM.Initialize(secondCtx, db, genesisBytes, secondMsgChan, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1371,6 +1397,8 @@ func TestRestartFullyAccepted(t *testing.T) {
|
|||
|
||||
firstVM.clock.Set(defaultGenesisTime)
|
||||
firstCtx := defaultContext()
|
||||
firstCtx.Lock.Lock()
|
||||
|
||||
firstMsgChan := make(chan common.Message, 1)
|
||||
if err := firstVM.Initialize(firstCtx, db, genesisBytes, firstMsgChan, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1418,6 +1446,7 @@ func TestRestartFullyAccepted(t *testing.T) {
|
|||
}
|
||||
|
||||
firstVM.Shutdown()
|
||||
firstCtx.Lock.Unlock()
|
||||
|
||||
secondVM := &VM{
|
||||
SnowmanVM: &core.SnowmanVM{},
|
||||
|
@ -1430,6 +1459,9 @@ func TestRestartFullyAccepted(t *testing.T) {
|
|||
|
||||
secondVM.clock.Set(defaultGenesisTime)
|
||||
secondCtx := defaultContext()
|
||||
secondCtx.Lock.Lock()
|
||||
defer secondCtx.Lock.Unlock()
|
||||
|
||||
secondMsgChan := make(chan common.Message, 1)
|
||||
if err := secondVM.Initialize(secondCtx, db, genesisBytes, secondMsgChan, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -1471,7 +1503,6 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
|
|||
SnowmanVM: &core.SnowmanVM{},
|
||||
chainManager: chains.MockManager{},
|
||||
}
|
||||
defer vm.Shutdown()
|
||||
|
||||
defaultSubnet := validators.NewSet()
|
||||
vm.validators = validators.NewManager()
|
||||
|
@ -1479,9 +1510,9 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
|
|||
|
||||
vm.clock.Set(defaultGenesisTime)
|
||||
ctx := defaultContext()
|
||||
msgChan := make(chan common.Message, 1)
|
||||
|
||||
ctx.Lock.Lock()
|
||||
|
||||
msgChan := make(chan common.Message, 1)
|
||||
if err := vm.Initialize(ctx, vmDB, genesisBytes, msgChan, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1510,7 +1541,7 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
|
|||
go timeoutManager.Dispatch()
|
||||
|
||||
router := &router.ChainRouter{}
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager)
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager, time.Hour)
|
||||
|
||||
externalSender := &sender.ExternalSenderTest{T: t}
|
||||
externalSender.Default(true)
|
||||
|
|
|
@ -62,10 +62,11 @@ func ConsensusLeader(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
go timeoutManager.Dispatch()
|
||||
|
||||
router := &router.ChainRouter{}
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager)
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager, time.Hour)
|
||||
|
||||
// Initialize the VM
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Lock()
|
||||
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil {
|
||||
b.Fatal(err)
|
||||
|
@ -189,7 +190,7 @@ func ConsensusFollower(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
go timeoutManager.Dispatch()
|
||||
|
||||
router := &router.ChainRouter{}
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager)
|
||||
router.Initialize(logging.NoLog{}, &timeoutManager, time.Hour)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(numBlocks)
|
||||
|
@ -198,6 +199,7 @@ func ConsensusFollower(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
vm := &VM{
|
||||
onAccept: func(ids.ID) { wg.Done() },
|
||||
}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
ctx.Lock.Lock()
|
||||
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil {
|
||||
b.Fatal(err)
|
||||
|
|
|
@ -122,7 +122,11 @@ func (vm *VM) Shutdown() {
|
|||
return
|
||||
}
|
||||
|
||||
// There is a potential deadlock if the timer is about to execute a timeout.
|
||||
// So, the lock must be released before stopping the timer.
|
||||
vm.ctx.Lock.Unlock()
|
||||
vm.timer.Stop()
|
||||
vm.ctx.Lock.Lock()
|
||||
if err := vm.baseDB.Close(); err != nil {
|
||||
vm.ctx.Log.Error("Closing the database failed with %s", err)
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ func BenchmarkParseBlock(b *testing.B) {
|
|||
/*testing=*/ b,
|
||||
)
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(
|
||||
/*ctx=*/ ctx,
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -106,6 +107,7 @@ func BenchmarkParseAndVerify(b *testing.B) {
|
|||
|
||||
for n := 0; n < b.N; n++ {
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(
|
||||
/*ctx=*/ snow.DefaultContextTest(),
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -141,6 +143,8 @@ func BenchmarkAccept(b *testing.B) {
|
|||
|
||||
for n := 0; n < b.N; n++ {
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
|
||||
vm.Initialize(
|
||||
/*ctx=*/ snow.DefaultContextTest(),
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -178,6 +182,7 @@ func ParseAndVerifyAndAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
|
||||
for n := 0; n < b.N; n++ {
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(
|
||||
/*ctx=*/ snow.DefaultContextTest(),
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -232,6 +237,7 @@ func ParseThenVerifyThenAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
|
||||
for n := 0; n < b.N; n++ {
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(
|
||||
/*ctx=*/ snow.DefaultContextTest(),
|
||||
/*db=*/ memdb.New(),
|
||||
|
@ -292,6 +298,7 @@ func IssueAndVerifyAndAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
|
|||
|
||||
for n := 0; n < b.N; n++ {
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(
|
||||
/*ctx=*/ snow.DefaultContextTest(),
|
||||
/*db=*/ memdb.New(),
|
||||
|
|
|
@ -67,6 +67,7 @@ func TestPayments(t *testing.T) {
|
|||
blocker, _ := queue.New(bootstrappingDB)
|
||||
|
||||
vm := &VM{}
|
||||
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(ctx, db, genesisData, msgChan, nil)
|
||||
|
||||
sender := &common.SenderTest{}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -134,7 +134,11 @@ func (vm *VM) Shutdown() {
|
|||
return
|
||||
}
|
||||
|
||||
// There is a potential deadlock if the timer is about to execute a timeout.
|
||||
// So, the lock must be released before stopping the timer.
|
||||
vm.ctx.Lock.Unlock()
|
||||
vm.timer.Stop()
|
||||
vm.ctx.Lock.Lock()
|
||||
if err := vm.baseDB.Close(); err != nil {
|
||||
vm.ctx.Log.Error("Closing the database failed with %s", err)
|
||||
}
|
||||
|
|
|
@ -91,6 +91,7 @@ func TestAva(t *testing.T) {
|
|||
msgChan := make(chan common.Message, 1)
|
||||
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
vm.batchTimeout = 0
|
||||
|
||||
|
@ -172,6 +173,7 @@ func TestInvalidSpentTx(t *testing.T) {
|
|||
msgChan := make(chan common.Message, 1)
|
||||
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
|
||||
ctx.Lock.Lock()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
|
@ -258,6 +260,7 @@ func TestInvalidTxVerification(t *testing.T) {
|
|||
msgChan := make(chan common.Message, 1)
|
||||
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
|
||||
ctx.Lock.Lock()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
|
@ -319,6 +322,7 @@ func TestRPCAPI(t *testing.T) {
|
|||
vmDB := memdb.New()
|
||||
msgChan := make(chan common.Message, 1)
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
vm.batchTimeout = 0
|
||||
|
||||
|
@ -526,6 +530,7 @@ func TestMultipleSend(t *testing.T) {
|
|||
vmDB := memdb.New()
|
||||
msgChan := make(chan common.Message, 1)
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
|
||||
// Initialize these data structures
|
||||
|
@ -635,6 +640,7 @@ func TestIssuePendingDependency(t *testing.T) {
|
|||
|
||||
ctx.Lock.Lock()
|
||||
vm := &VM{}
|
||||
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
|
||||
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
|
||||
vm.batchTimeout = 0
|
||||
|
||||
|
|
Loading…
Reference in New Issue