Merge branch 'master' into platform_nil_shortids

This commit is contained in:
Stephen Buttolph 2020-05-11 14:09:59 -04:00 committed by GitHub
commit 81fd81e055
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 825 additions and 211 deletions

View File

@ -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,

View File

@ -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

View File

@ -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" ./...

View File

@ -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))

View File

@ -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")

View File

@ -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")
}
}

View File

@ -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()

View File

@ -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)
}

View File

@ -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 {

View File

@ -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")
}
}

View File

@ -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)

View File

@ -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")

View File

@ -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")
}
}

View File

@ -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() }

View File

@ -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:

View File

@ -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

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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")
}
}

View File

@ -50,3 +50,10 @@ func (r *request) Method() (string, error) {
uppercaseRune := string(unicode.ToUpper(firstRune))
return fmt.Sprintf("%s.%s%s", class, string(uppercaseRune), function[runeLen:]), nil
}
func (r *request) ReadRequest(args interface{}) error {
if err := r.CodecRequest.ReadRequest(args); err != nil {
return errors.New("couldn't unmarshal an argument. Ensure arguments are valid and properly formatted. See documentation for example calls")
}
return nil
}

View File

@ -623,7 +623,10 @@ func TestBaseTxSyntacticVerifyUninitialized(t *testing.T) {
func TestBaseTxSemanticVerify(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -687,7 +690,10 @@ func TestBaseTxSemanticVerify(t *testing.T) {
func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.codec.RegisterType(&ava.TestVerifiable{})
@ -736,7 +742,10 @@ func TestBaseTxSemanticVerifyUnknownFx(t *testing.T) {
func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.codec.RegisterType(&ava.TestVerifiable{})
@ -801,14 +810,15 @@ func TestBaseTxSemanticVerifyWrongAssetID(t *testing.T) {
}
func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
err := vm.Initialize(
ctx,
memdb.New(),
@ -893,7 +903,10 @@ func TestBaseTxSemanticVerifyUnauthorizedFx(t *testing.T) {
func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -944,7 +957,10 @@ func TestBaseTxSemanticVerifyInvalidSignature(t *testing.T) {
func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -1008,7 +1024,10 @@ func TestBaseTxSemanticVerifyMissingUTXO(t *testing.T) {
func TestBaseTxSemanticVerifyInvalidUTXO(t *testing.T) {
genesisBytes, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -1140,7 +1159,10 @@ func TestBaseTxSemanticVerifyPendingInvalidUTXO(t *testing.T) {
<-issuer
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.PendingTxs()
@ -1271,7 +1293,10 @@ func TestBaseTxSemanticVerifyPendingWrongAssetID(t *testing.T) {
<-issuer
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.PendingTxs()
@ -1436,7 +1461,10 @@ func TestBaseTxSemanticVerifyPendingUnauthorizedFx(t *testing.T) {
<-issuer
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.PendingTxs()
@ -1585,7 +1613,10 @@ func TestBaseTxSemanticVerifyPendingInvalidSignature(t *testing.T) {
<-issuer
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
vm.PendingTxs()

View File

@ -216,7 +216,10 @@ func TestIssueExportTx(t *testing.T) {
}
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
txs := vm.PendingTxs()
if len(txs) != 1 {
@ -349,7 +352,10 @@ func TestClearForceAcceptedExportTx(t *testing.T) {
}
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
txs := vm.PendingTxs()
if len(txs) != 1 {

View File

@ -220,7 +220,6 @@ func TestIssueImportTx(t *testing.T) {
if _, err := vm.IssueTx(tx.Bytes(), nil); err != nil {
t.Fatalf("should have issued the transaction correctly but errored: %s", err)
}
ctx.Lock.Unlock()
msg := <-issuer
@ -228,6 +227,12 @@ func TestIssueImportTx(t *testing.T) {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
txs := vm.PendingTxs()
if len(txs) != 1 {
t.Fatalf("Should have returned %d tx(s)", 1)
@ -261,10 +266,13 @@ func TestForceAcceptImportTx(t *testing.T) {
platformID := ids.Empty.Prefix(0)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{platform: platformID}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
err := vm.Initialize(
ctx,
memdb.New(),

View File

@ -17,7 +17,11 @@ import (
func TestPrefixedSetsAndGets(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state
vm.codec.RegisterType(&ava.TestVerifiable{})
@ -112,7 +116,11 @@ func TestPrefixedSetsAndGets(t *testing.T) {
func TestPrefixedFundingNoAddresses(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state
vm.codec.RegisterType(&ava.TestVerifiable{})
@ -136,7 +144,11 @@ func TestPrefixedFundingNoAddresses(t *testing.T) {
func TestPrefixedFundingAddresses(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state
vm.codec.RegisterType(&testAddressable{})

View File

@ -9,44 +9,22 @@ 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
}
func TestServiceIssueTx(t *testing.T) {
genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
txArgs := &IssueTxArgs{}
txReply := &IssueTxReply{}
@ -68,8 +46,10 @@ func TestServiceIssueTx(t *testing.T) {
func TestServiceGetTxStatus(t *testing.T) {
genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
statusArgs := &GetTxStatusArgs{}
statusReply := &GetTxStatusReply{}
@ -109,8 +89,10 @@ func TestServiceGetTxStatus(t *testing.T) {
func TestServiceGetUTXOsInvalidAddress(t *testing.T) {
_, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
addr0 := keys[0].PublicKey().Address()
tests := []struct {
@ -137,8 +119,10 @@ func TestServiceGetUTXOsInvalidAddress(t *testing.T) {
func TestServiceGetUTXOs(t *testing.T) {
_, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
addr0 := keys[0].PublicKey().Address()
tests := []struct {
@ -187,8 +171,10 @@ func TestServiceGetUTXOs(t *testing.T) {
func TestGetAssetDescription(t *testing.T) {
genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -212,8 +198,10 @@ func TestGetAssetDescription(t *testing.T) {
func TestGetBalance(t *testing.T) {
genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -235,8 +223,10 @@ func TestGetBalance(t *testing.T) {
func TestCreateFixedCapAsset(t *testing.T) {
_, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
reply := CreateFixedCapAssetReply{}
err := s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
@ -259,8 +249,10 @@ func TestCreateFixedCapAsset(t *testing.T) {
func TestCreateVariableCapAsset(t *testing.T) {
_, vm, s := setup(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
reply := CreateVariableCapAssetReply{}
err := s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{

View File

@ -16,7 +16,11 @@ import (
func TestStateIDs(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state.state
id0 := ids.NewID([32]byte{0xff, 0})
@ -126,7 +130,11 @@ func TestStateIDs(t *testing.T) {
func TestStateStatuses(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state.state
if _, err := state.Status(ids.Empty); err == nil {
@ -175,7 +183,11 @@ func TestStateStatuses(t *testing.T) {
func TestStateUTXOs(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state.state
vm.codec.RegisterType(&ava.TestVerifiable{})
@ -246,7 +258,11 @@ func TestStateUTXOs(t *testing.T) {
func TestStateTXs(t *testing.T) {
_, _, vm := GenesisVM(t)
ctx.Lock.Unlock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
state := vm.state.state
vm.codec.RegisterType(&ava.TestTransferable{})

View File

@ -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)
}

View File

@ -392,10 +392,13 @@ func TestTxSerialization(t *testing.T) {
}
func TestInvalidGenesis(t *testing.T) {
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
err := vm.Initialize(
/*context=*/ ctx,
/*db=*/ memdb.New(),
@ -409,12 +412,14 @@ func TestInvalidGenesis(t *testing.T) {
}
func TestInvalidFx(t *testing.T) {
genesisBytes := BuildGenesisTest(t)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisBytes := BuildGenesisTest(t)
err := vm.Initialize(
/*context=*/ ctx,
/*db=*/ memdb.New(),
@ -430,12 +435,14 @@ func TestInvalidFx(t *testing.T) {
}
func TestFxInitializationFailure(t *testing.T) {
genesisBytes := BuildGenesisTest(t)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisBytes := BuildGenesisTest(t)
err := vm.Initialize(
/*context=*/ ctx,
/*db=*/ memdb.New(),
@ -457,6 +464,10 @@ func (tx *testTxBytes) UnsignedBytes() []byte { return tx.unsignedBytes }
func TestIssueTx(t *testing.T) {
genesisBytes, issuer, vm := GenesisVM(t)
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
newTx := NewTx(t, genesisBytes, vm)
@ -473,6 +484,7 @@ func TestIssueTx(t *testing.T) {
if msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("Should have returned %d tx(s)", 1)
@ -481,6 +493,10 @@ func TestIssueTx(t *testing.T) {
func TestGenesisGetUTXOs(t *testing.T) {
_, _, vm := GenesisVM(t)
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
shortAddr := keys[0].PublicKey().Address()
addr := ids.NewID(hashing.ComputeHash256Array(shortAddr.Bytes()))
@ -491,8 +507,6 @@ func TestGenesisGetUTXOs(t *testing.T) {
if err != nil {
t.Fatal(err)
}
vm.Shutdown()
ctx.Lock.Unlock()
if len(utxos) != 7 {
t.Fatalf("Wrong number of utxos. Expected (%d) returned (%d)", 7, len(utxos))
@ -503,6 +517,10 @@ func TestGenesisGetUTXOs(t *testing.T) {
// transaction should be issued successfully.
func TestIssueDependentTx(t *testing.T) {
genesisBytes, issuer, vm := GenesisVM(t)
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -615,13 +633,13 @@ func TestIssueDependentTx(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx.Lock.Unlock()
msg := <-issuer
if msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 2 {
t.Fatalf("Should have returned %d tx(s)", 2)
@ -630,14 +648,15 @@ func TestIssueDependentTx(t *testing.T) {
// Test issuing a transaction that creates an NFT family
func TestIssueNFT(t *testing.T) {
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
err := vm.Initialize(
ctx,
memdb.New(),
@ -788,14 +807,15 @@ func TestIssueNFT(t *testing.T) {
// Test issuing a transaction that creates an Property family
func TestIssueProperty(t *testing.T) {
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
ctx.Lock.Lock()
defer ctx.Lock.Unlock()
vm := &VM{}
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisBytes := BuildGenesisTest(t)
issuer := make(chan common.Message, 1)
err := vm.Initialize(
ctx,
memdb.New(),
@ -937,8 +957,10 @@ func TestIssueProperty(t *testing.T) {
func TestVMFormat(t *testing.T) {
_, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
tests := []struct {
in string
@ -957,8 +979,10 @@ func TestVMFormat(t *testing.T) {
func TestVMFormatAliased(t *testing.T) {
_, _, vm := GenesisVM(t)
defer ctx.Lock.Unlock()
defer vm.Shutdown()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
origAliases := ctx.BCLookup
defer func() { ctx.BCLookup = origAliases }()

View File

@ -13,6 +13,11 @@ import (
func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil
var tx *addDefaultSubnetDelegatorTx
@ -153,6 +158,11 @@ func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
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

View File

@ -12,6 +12,11 @@ import (
func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil
var tx *addDefaultSubnetValidatorTx
@ -216,6 +221,11 @@ func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
// Test AddDefaultSubnetValidatorTx.SemanticVerify
func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: Validator's start time too early
tx, err := vm.newAddDefaultSubnetValidatorTx(
@ -281,9 +291,9 @@ func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
}
startTime := defaultGenesisTime.Add(1 * time.Second)
tx, err = vm.newAddDefaultSubnetValidatorTx(
defaultNonce+1, // nonce
defaultStakeAmount, // stake amount
uint64(startTime.Unix()), // start time
defaultNonce+1, // nonce
defaultStakeAmount, // stake amount
uint64(startTime.Unix()), // start time
uint64(startTime.Add(MinimumStakingDuration).Unix()), // end time
key.PublicKey().Address(), // node ID
defaultKey.PublicKey().Address(), // destination

View File

@ -14,6 +14,11 @@ import (
func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil
var tx *addNonDefaultSubnetValidatorTx
@ -202,6 +207,11 @@ func TestAddNonDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
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
@ -590,12 +600,16 @@ func TestAddNonDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
if err == nil {
t.Fatal("should have failed verification because validator already in pending validator set of the specified subnet")
}
}
// Test that marshalling/unmarshalling works
func TestAddNonDefaultSubnetValidatorMarshal(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// valid tx
tx, err := vm.newAddNonDefaultSubnetValidatorTx(

View File

@ -17,6 +17,12 @@ func TestAdvanceTimeTxSyntacticVerify(t *testing.T) {
// Case 2: Timestamp is ahead of synchrony bound
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
tx = &advanceTimeTx{
Time: uint64(defaultGenesisTime.Add(Delta).Add(1 * time.Second).Unix()),
vm: vm,
@ -38,6 +44,11 @@ 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()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
tx := &advanceTimeTx{
Time: uint64(defaultGenesisTime.Unix()),
@ -52,6 +63,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()
vm.Ctx.Lock.Lock()
// Case 1: Timestamp is after next validator start time
// Add a pending validator
@ -94,9 +106,16 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) {
if err == nil {
t.Fatal("should've failed verification because proposed timestamp is after pending validator start time")
}
vm.Shutdown()
vm.Ctx.Lock.Unlock()
// Case 2: Timestamp is after next validator end time
vm = defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// fast forward clock to 10 seconds before genesis validators stop validating
vm.clock.Set(defaultValidateEndTime.Add(-10 * time.Second))
@ -117,6 +136,11 @@ func TestAdvanceTimeTxTimestampTooLate(t *testing.T) {
// Ensure semantic verification updates the current and pending validator sets correctly
func TestAdvanceTimeTxUpdateValidators(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: Timestamp is after next validator start time
// Add a pending validator
@ -196,6 +220,11 @@ func TestAdvanceTimeTxUpdateValidators(t *testing.T) {
// Test method InitiallyPrefersCommit
func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
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 +246,11 @@ func TestAdvanceTimeTxInitiallyPrefersCommit(t *testing.T) {
// Ensure marshaling/unmarshaling works
func TestAdvanceTimeTxUnmarshal(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
tx, err := vm.newAdvanceTimeTx(defaultGenesisTime)
if err != nil {

View File

@ -14,6 +14,11 @@ import (
// test method SyntacticVerify
func TestCreateChainTxSyntacticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil
var tx *CreateChainTx
@ -142,6 +147,11 @@ func TestCreateChainTxSyntacticVerify(t *testing.T) {
// Ensure SemanticVerify fails when there are not enough control sigs
func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: No control sigs (2 are needed)
tx, err := vm.newCreateChainTx(
@ -189,6 +199,11 @@ func TestCreateChainTxInsufficientControlSigs(t *testing.T) {
// Ensure SemanticVerify fails when an incorrect control signature is given
func TestCreateChainTxWrongControlSig(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Generate new, random key to sign tx with
factory := crypto.FactorySECP256K1R{}
@ -222,6 +237,11 @@ func TestCreateChainTxWrongControlSig(t *testing.T) {
// its validator set doesn't exist
func TestCreateChainTxNoSuchSubnet(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
tx, err := vm.newCreateChainTx(
defaultNonce+1,
@ -245,6 +265,11 @@ func TestCreateChainTxNoSuchSubnet(t *testing.T) {
func TestCreateChainTxAlreadyExists(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// create a tx
tx, err := vm.newCreateChainTx(
@ -276,6 +301,11 @@ func TestCreateChainTxAlreadyExists(t *testing.T) {
// Ensure valid tx passes semanticVerify
func TestCreateChainTxValid(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// create a valid tx
tx, err := vm.newCreateChainTx(

View File

@ -11,6 +11,12 @@ import (
func TestTxHeapStart(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
txHeap := EventHeap{SortByStartTime: true}
validator0, err := vm.newAddDefaultSubnetValidatorTx(
@ -78,6 +84,12 @@ func TestTxHeapStart(t *testing.T) {
func TestTxHeapStop(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
txHeap := EventHeap{}
validator0, err := vm.newAddDefaultSubnetValidatorTx(
@ -145,6 +157,12 @@ func TestTxHeapStop(t *testing.T) {
func TestTxHeapStartValidatorVsDelegatorOrdering(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
txHeap := EventHeap{SortByStartTime: true}
validator, err := vm.newAddDefaultSubnetValidatorTx(
@ -186,6 +204,12 @@ func TestTxHeapStartValidatorVsDelegatorOrdering(t *testing.T) {
func TestTxHeapStopValidatorVsDelegatorOrdering(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
txHeap := EventHeap{}
validator, err := vm.newAddDefaultSubnetValidatorTx(

View File

@ -18,6 +18,12 @@ func TestRewardValidatorTxSyntacticVerify(t *testing.T) {
}
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
txID := ids.NewID([32]byte{1, 2, 3, 4, 5, 6, 7})
tests := []test{
@ -54,6 +60,12 @@ func TestRewardValidatorTxSyntacticVerify(t *testing.T) {
func TestRewardValidatorTxSemanticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
var nextToRemove *addDefaultSubnetValidatorTx
currentValidators, err := vm.getCurrentValidators(vm.DB, DefaultSubnetID)
if err != nil {
@ -130,6 +142,11 @@ func TestRewardValidatorTxSemanticVerify(t *testing.T) {
func TestRewardDelegatorTxSemanticVerify(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
keyIntf1, err := vm.factory.NewPrivateKey()
if err != nil {

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"net/http"
"time"
"github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids"
@ -78,7 +79,7 @@ type GetSubnetsResponse struct {
func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, response *GetSubnetsResponse) error {
subnets, err := service.vm.getSubnets(service.vm.DB) // all subnets
if err != nil {
return fmt.Errorf("error getting subnets from database: %v", err)
return fmt.Errorf("error getting subnets from database: %w", err)
}
getAll := len(args.IDs) == 0
@ -280,7 +281,7 @@ type GetAccountReply struct {
func (service *Service) GetAccount(_ *http.Request, args *GetAccountArgs, reply *GetAccountReply) error {
account, err := service.vm.getAccount(service.vm.DB, args.Address)
if err != nil && err != database.ErrNotFound {
return errGetAccount
return fmt.Errorf("couldn't get account: %w", err)
} else if err == database.ErrNotFound {
account = newAccount(args.Address, 0, 0)
}
@ -310,7 +311,7 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
// db holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil {
return errGetUser
return fmt.Errorf("couldn't get user: %w", err)
}
// The user
@ -321,14 +322,14 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
// IDs of accounts controlled by this user
accountIDs, err := user.getAccountIDs()
if err != nil {
return errGetAccounts
return fmt.Errorf("couldn't get accounts held by user: %w", err)
}
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 {
service.vm.Ctx.Log.Error("couldn't get account from database: %v", err)
service.vm.Ctx.Log.Error("couldn't get account from database: %w", err)
continue
} else if err == database.ErrNotFound {
account = newAccount(accountID, 0, 0)
@ -372,7 +373,7 @@ func (service *Service) CreateAccount(_ *http.Request, args *CreateAccountArgs,
// userDB holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil {
return errGetUser
return fmt.Errorf("couldn't get user: %w", err)
}
// The user creating a new account
@ -430,7 +431,7 @@ type CreateTxResponse struct {
type AddDefaultSubnetValidatorArgs struct {
APIDefaultSubnetValidator
// Next unused nonce of the account the staked $AVA and tx fee are paid from
// Next nonce of the sender
PayerNonce json.Uint64 `json:"payerNonce"`
}
@ -439,8 +440,13 @@ type AddDefaultSubnetValidatorArgs struct {
func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("AddDefaultSubnetValidator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
switch {
case args.ID.IsZero(): // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID
case args.PayerNonce == 0:
return fmt.Errorf("sender's next nonce not specified")
case int64(args.StartTime) < time.Now().Unix():
return fmt.Errorf("start time must be in the future")
}
// Create the transaction
@ -484,8 +490,13 @@ type AddDefaultSubnetDelegatorArgs struct {
func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("AddDefaultSubnetDelegator called")
if args.ID.IsZero() { // If ID unspecified, use this node's ID as validator ID
switch {
case args.ID.IsZero(): // If ID unspecified, use this node's ID as validator ID
args.ID = service.vm.Ctx.NodeID
case args.PayerNonce == 0:
return fmt.Errorf("sender's next unused nonce not specified")
case int64(args.StartTime) < time.Now().Unix():
return fmt.Errorf("start time must be in the future")
}
// Create the transaction
@ -573,6 +584,11 @@ type CreateSubnetArgs struct {
func (service *Service) CreateSubnet(_ *http.Request, args *CreateSubnetArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.createSubnet called")
switch {
case args.PayerNonce == 0:
return fmt.Errorf("sender's next nonce not specified")
}
// Create the transaction
tx := CreateSubnetTx{
UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
@ -614,6 +630,13 @@ type ExportAVAArgs struct {
func (service *Service) ExportAVA(_ *http.Request, args *ExportAVAArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.ExportAVA called")
switch {
case args.PayerNonce == 0:
return fmt.Errorf("sender's next nonce not specified")
case uint64(args.Amount) == 0:
return fmt.Errorf("amount must be >0")
}
// Create the transaction
tx := ExportTx{UnsignedExportTx: UnsignedExportTx{
NetworkID: service.vm.Ctx.NetworkID,
@ -725,7 +748,7 @@ func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetVali
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
@ -748,7 +771,7 @@ func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDele
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
@ -771,7 +794,7 @@ func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.Priva
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
@ -794,7 +817,7 @@ func (service *Service) signExportTx(tx *ExportTx, key *crypto.PrivateKeySECP256
unsignedIntf := interface{}(&tx.UnsignedExportTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
@ -822,7 +845,7 @@ func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubn
unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
if err != nil {
@ -835,7 +858,7 @@ func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubn
// Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID())
if err != nil {
return nil, fmt.Errorf("problem getting subnet information: %v", err)
return nil, fmt.Errorf("problem getting subnet information: %w", err)
}
// Find the location at which [key] should put its signature.
@ -867,7 +890,7 @@ type ImportAVAArgs struct {
// ID of the account that will receive the imported funds, and pay the transaction fee
To ids.ShortID `json:"to"`
// Next unused nonce of the account
// Next nonce of the sender
PayerNonce json.Uint64 `json:"payerNonce"`
// User that controls the account
@ -881,14 +904,17 @@ type ImportAVAArgs struct {
func (service *Service) ImportAVA(_ *http.Request, args *ImportAVAArgs, response *SignResponse) error {
service.vm.Ctx.Log.Debug("platform.ImportAVA called")
if args.To.IsZero() {
switch {
case args.To.IsZero():
return errNilTo
case args.PayerNonce == 0:
return fmt.Errorf("sender's next nonce not specified")
}
// Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil {
return fmt.Errorf("couldn't get data for user '%s'. Does user exist?", args.Username)
return fmt.Errorf("couldn't get user: %w", err)
}
user := user{db: db}
@ -1001,7 +1027,7 @@ func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.Private
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil {
return nil, fmt.Errorf("error serializing unsigned tx: %v", err)
return nil, fmt.Errorf("error serializing unsigned tx: %w", err)
}
sig, err := key.Sign(unsignedTxBytes)
if err != nil {
@ -1014,7 +1040,7 @@ func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.Private
// Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID)
if err != nil {
return nil, fmt.Errorf("problem getting subnet information: %v", err)
return nil, fmt.Errorf("problem getting subnet information: %w", err)
}
// Find the location at which [key] should put its signature.
@ -1109,7 +1135,7 @@ type CreateBlockchainArgs struct {
// Human-readable name for the new blockchain, not necessarily unique
Name string `json:"name"`
// Next unused nonce of the account paying the transaction fee
// Next nonce of the sender
PayerNonce json.Uint64 `json:"payerNonce"`
// Genesis state of the blockchain being created
@ -1121,6 +1147,15 @@ type CreateBlockchainArgs struct {
func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("createBlockchain called")
switch {
case args.PayerNonce == 0:
return errors.New("sender's next nonce not specified")
case args.VMID == "":
return errors.New("VM not specified")
case args.SubnetID.Equals(ids.Empty):
return errors.New("subnet not specified")
}
vmID, err := service.vm.chainManager.LookupVM(args.VMID)
if err != nil {
return fmt.Errorf("no VM with ID '%s' found", args.VMID)
@ -1166,7 +1201,7 @@ func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchain
txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil {
service.vm.Ctx.Log.Error("problem marshaling createChainTx: %v", err)
service.vm.Ctx.Log.Error("problem marshaling createChainTx: %w", err)
return errCreatingTransaction
}
@ -1190,6 +1225,11 @@ type GetBlockchainStatusReply struct {
func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error {
service.vm.Ctx.Log.Debug("getBlockchainStatus called")
switch {
case args.BlockchainID == "":
return errors.New("'blockchainID' not given")
}
_, err := service.vm.chainManager.Lookup(args.BlockchainID)
if err == nil {
reply.Status = Validating
@ -1265,6 +1305,11 @@ type ValidatedByResponse struct {
func (service *Service) ValidatedBy(_ *http.Request, args *ValidatedByArgs, response *ValidatedByResponse) error {
service.vm.Ctx.Log.Debug("validatedBy called")
switch {
case args.BlockchainID.Equals(ids.Empty):
return errors.New("'blockchainID' not specified")
}
chain, err := service.vm.getChain(service.vm.DB, args.BlockchainID)
if err != nil {
return err
@ -1287,6 +1332,11 @@ type ValidatesResponse struct {
func (service *Service) Validates(_ *http.Request, args *ValidatesArgs, response *ValidatesResponse) error {
service.vm.Ctx.Log.Debug("validates called")
switch {
case args.SubnetID.Equals(ids.Empty):
return errors.New("'subnetID' not specified")
}
// Verify that the Subnet exists
if _, err := service.vm.getSubnet(service.vm.DB, args.SubnetID); err != nil {
return err
@ -1332,7 +1382,7 @@ func (service *Service) GetBlockchains(_ *http.Request, args *struct{}, response
chains, err := service.vm.getChains(service.vm.DB)
if err != nil {
return fmt.Errorf("couldn't retrieve blockchains: %v", err)
return fmt.Errorf("couldn't retrieve blockchains: %w", err)
}
for _, chain := range chains {

View File

@ -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)
}

View File

@ -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,6 +235,11 @@ func GenesisCurrentValidators() *EventHeap {
// Ensure genesis state is parsed from bytes and stored correctly
func TestGenesis(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Ensure the genesis block has been accepted and stored
genesisBlockID := vm.LastAccepted() // lastAccepted should be ID of genesis block
@ -302,6 +309,12 @@ func TestGenesis(t *testing.T) {
// accept proposal to add validator to default subnet
func TestAddDefaultSubnetValidatorCommit(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
startTime := defaultGenesisTime.Add(Delta).Add(1 * time.Second)
endTime := startTime.Add(MinimumStakingDuration)
key, _ := vm.factory.NewPrivateKey()
@ -325,12 +338,10 @@ func TestAddDefaultSubnetValidatorCommit(t *testing.T) {
// trigger block creation
vm.unissuedEvents.Add(tx)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock()
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -369,6 +380,12 @@ func TestAddDefaultSubnetValidatorCommit(t *testing.T) {
// Reject proposal to add validator to default subnet
func TestAddDefaultSubnetValidatorReject(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
startTime := defaultGenesisTime.Add(Delta).Add(1 * time.Second)
endTime := startTime.Add(MinimumStakingDuration)
key, _ := vm.factory.NewPrivateKey()
@ -392,12 +409,10 @@ func TestAddDefaultSubnetValidatorReject(t *testing.T) {
// trigger block creation
vm.unissuedEvents.Add(tx)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock()
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -440,6 +455,12 @@ func TestAddDefaultSubnetValidatorReject(t *testing.T) {
// Accept proposal to add validator to non-default subnet
func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
startTime := defaultValidateStartTime.Add(Delta).Add(1 * time.Second)
endTime := startTime.Add(MinimumStakingDuration)
@ -463,12 +484,10 @@ func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
// trigger block creation
vm.unissuedEvents.Add(tx)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock()
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -511,6 +530,12 @@ func TestAddNonDefaultSubnetValidatorAccept(t *testing.T) {
// Reject proposal to add validator to non-default subnet
func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
startTime := defaultValidateStartTime.Add(Delta).Add(1 * time.Second)
endTime := startTime.Add(MinimumStakingDuration)
key, _ := vm.factory.NewPrivateKey()
@ -536,12 +561,10 @@ func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
// trigger block creation
vm.unissuedEvents.Add(tx)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock()
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -584,16 +607,19 @@ func TestAddNonDefaultSubnetValidatorReject(t *testing.T) {
// Test case where default subnet validator rewarded
func TestRewardValidatorAccept(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Fast forward clock to time for genesis validators to leave
vm.clock.Set(defaultValidateEndTime)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock() // should contain proposal to advance time
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -630,12 +656,10 @@ func TestRewardValidatorAccept(t *testing.T) {
t.Fatal("expected timestamp to have advanced")
}
vm.Ctx.Lock.Lock()
blk, err = vm.BuildBlock() // should contain proposal to reward genesis validator
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block = blk.(*ProposalBlock)
@ -676,16 +700,19 @@ func TestRewardValidatorAccept(t *testing.T) {
// Test case where default subnet validator not rewarded
func TestRewardValidatorReject(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Fast forward clock to time for genesis validators to leave
vm.clock.Set(defaultValidateEndTime)
vm.Ctx.Lock.Lock()
blk, err := vm.BuildBlock() // should contain proposal to advance time
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block := blk.(*ProposalBlock)
@ -722,12 +749,10 @@ func TestRewardValidatorReject(t *testing.T) {
t.Fatal("expected timestamp to have advanced")
}
vm.Ctx.Lock.Lock()
blk, err = vm.BuildBlock() // should contain proposal to reward genesis validator
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
block = blk.(*ProposalBlock)
@ -768,6 +793,11 @@ func TestRewardValidatorReject(t *testing.T) {
// Ensure BuildBlock errors when there is no block to build
func TestUnneededBuildBlock(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
if _, err := vm.BuildBlock(); err == nil {
t.Fatalf("Should have errored on BuildBlock")
@ -777,6 +807,11 @@ func TestUnneededBuildBlock(t *testing.T) {
// test acceptance of proposal to create a new chain
func TestCreateChain(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
tx, err := vm.newCreateChainTx(
defaultNonce+1,
@ -793,13 +828,11 @@ func TestCreateChain(t *testing.T) {
t.Fatal(err)
}
vm.Ctx.Lock.Lock()
vm.unissuedDecisionTxs = append(vm.unissuedDecisionTxs, tx)
blk, err := vm.BuildBlock() // should contain proposal to create chain
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
if err := blk.Verify(); err != nil {
t.Fatal(err)
@ -839,6 +872,11 @@ func TestCreateChain(t *testing.T) {
// 4) Advance timestamp to validator's end time (removing validator from current)
func TestCreateSubnet(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
createSubnetTx, err := vm.newCreateSubnetTx(
testNetworkID,
@ -854,13 +892,11 @@ func TestCreateSubnet(t *testing.T) {
t.Fatal(err)
}
vm.Ctx.Lock.Lock()
vm.unissuedDecisionTxs = append(vm.unissuedDecisionTxs, createSubnetTx)
blk, err := vm.BuildBlock() // should contain proposal to create subnet
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
if err := blk.Verify(); err != nil {
t.Fatal(err)
@ -917,13 +953,11 @@ func TestCreateSubnet(t *testing.T) {
t.Fatal(err)
}
vm.Ctx.Lock.Lock()
vm.unissuedEvents.Push(addValidatorTx)
blk, err = vm.BuildBlock() // should add validator to the new subnet
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
// and accept the proposal/commit
@ -971,12 +1005,10 @@ func TestCreateSubnet(t *testing.T) {
// from pending to current validator set
vm.clock.Set(startTime)
vm.Ctx.Lock.Lock()
blk, err = vm.BuildBlock() // should be advance time tx
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
// and accept the proposal/commit
@ -1031,12 +1063,10 @@ func TestCreateSubnet(t *testing.T) {
// fast forward clock to time validator should stop validating
vm.clock.Set(endTime)
vm.Ctx.Lock.Lock()
blk, err = vm.BuildBlock() // should be advance time tx
if err != nil {
t.Fatal(err)
}
vm.Ctx.Lock.Unlock()
// Assert preferences are correct
// and accept the proposal/commit
@ -1084,6 +1114,11 @@ func TestCreateSubnet(t *testing.T) {
// test asset import
func TestAtomicImport(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
avmID := ids.Empty.Prefix(0)
utxoID := ava.UTXOID{
@ -1117,9 +1152,6 @@ func TestAtomicImport(t *testing.T) {
t.Fatal(err)
}
vm.Ctx.Lock.Lock()
defer vm.Ctx.Lock.Unlock()
vm.ava = assetID
vm.avm = avmID
@ -1175,6 +1207,11 @@ func TestAtomicImport(t *testing.T) {
// test optimistic asset import
func TestOptimisticAtomicImport(t *testing.T) {
vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
avmID := ids.Empty.Prefix(0)
utxoID := ava.UTXOID{
@ -1208,9 +1245,6 @@ func TestOptimisticAtomicImport(t *testing.T) {
t.Fatal(err)
}
vm.Ctx.Lock.Lock()
defer vm.Ctx.Lock.Unlock()
vm.ava = assetID
vm.avm = avmID
@ -1271,6 +1305,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 +1354,7 @@ func TestRestartPartiallyAccepted(t *testing.T) {
}
firstVM.Shutdown()
firstCtx.Lock.Unlock()
secondVM := &VM{
SnowmanVM: &core.SnowmanVM{},
@ -1330,6 +1367,12 @@ func TestRestartPartiallyAccepted(t *testing.T) {
secondVM.clock.Set(defaultGenesisTime)
secondCtx := defaultContext()
secondCtx.Lock.Lock()
defer func() {
secondVM.Shutdown()
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 +1414,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 +1463,7 @@ func TestRestartFullyAccepted(t *testing.T) {
}
firstVM.Shutdown()
firstCtx.Lock.Unlock()
secondVM := &VM{
SnowmanVM: &core.SnowmanVM{},
@ -1430,6 +1476,12 @@ func TestRestartFullyAccepted(t *testing.T) {
secondVM.clock.Set(defaultGenesisTime)
secondCtx := defaultContext()
secondCtx.Lock.Lock()
defer func() {
secondVM.Shutdown()
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 +1523,6 @@ func TestBootstrapPartiallyAccepted(t *testing.T) {
SnowmanVM: &core.SnowmanVM{},
chainManager: chains.MockManager{},
}
defer vm.Shutdown()
defaultSubnet := validators.NewSet()
vm.validators = validators.NewManager()
@ -1479,9 +1530,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 +1561,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)
@ -1627,6 +1678,12 @@ func TestUnverifiedParent(t *testing.T) {
vm.clock.Set(defaultGenesisTime)
ctx := defaultContext()
ctx.Lock.Lock()
defer func() {
vm.Shutdown()
ctx.Lock.Unlock()
}()
msgChan := make(chan common.Message, 1)
if err := vm.Initialize(ctx, db, genesisBytes, msgChan, nil); err != nil {
t.Fatal(err)

View File

@ -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)

View File

@ -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)
}

View File

@ -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(),

View File

@ -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{}

View File

@ -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)
}

View File

@ -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