Merge branch 'master' into api-error-message

This commit is contained in:
Stephen Buttolph 2020-05-10 19:40:51 -04:00 committed by GitHub
commit 50f88cf240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 830 additions and 215 deletions

View File

@ -40,6 +40,7 @@ import (
const ( const (
defaultChannelSize = 1000 defaultChannelSize = 1000
requestTimeout = 2 * time.Second requestTimeout = 2 * time.Second
gossipFrequency = 10 * time.Second
) )
// Manager manages the chains running on this node. // Manager manages the chains running on this node.
@ -146,7 +147,7 @@ func New(
timeoutManager.Initialize(requestTimeout) timeoutManager.Initialize(requestTimeout)
go log.RecoverAndPanic(timeoutManager.Dispatch) go log.RecoverAndPanic(timeoutManager.Dispatch)
router.Initialize(log, &timeoutManager) router.Initialize(log, &timeoutManager, gossipFrequency)
m := &manager{ m := &manager{
stakingEnabled: stakingEnabled, stakingEnabled: stakingEnabled,

View File

@ -18,6 +18,7 @@ import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"unsafe" "unsafe"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@ -29,9 +30,15 @@ import (
"github.com/ava-labs/gecko/snow/validators" "github.com/ava-labs/gecko/snow/validators"
"github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/logging" "github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/random"
"github.com/ava-labs/gecko/utils/timer" "github.com/ava-labs/gecko/utils/timer"
) )
// GossipSize is the maximum number of peers to gossip a container to
const (
GossipSize = 50
)
var ( var (
// VotingNet implements the SenderExternal interface. // VotingNet implements the SenderExternal interface.
VotingNet = Voting{} VotingNet = Voting{}
@ -89,34 +96,7 @@ func (s *Voting) Shutdown() { s.executor.Stop() }
// Accept is called after every consensus decision // Accept is called after every consensus decision
func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error { func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error {
peers := []salticidae.PeerID(nil) return s.gossip(chainID, containerID, container)
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
} }
// GetAcceptedFrontier implements the Sender interface. // GetAcceptedFrontier implements the Sender interface.
@ -412,6 +392,13 @@ func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32
s.numChitsSent.Inc() 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) { func (s *Voting) send(msg Msg, peers ...salticidae.PeerID) {
ds := msg.DataStream() ds := msg.DataStream()
defer ds.Free() 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 // getAcceptedFrontier handles the recept of a getAcceptedFrontier container
// message for a chain // message for a chain
//export getAcceptedFrontier //export getAcceptedFrontier

View File

@ -5,4 +5,4 @@
SRC_DIR="$(dirname "${BASH_SOURCE[0]}")" SRC_DIR="$(dirname "${BASH_SOURCE[0]}")"
source "$SRC_DIR/env.sh" 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" "errors"
"fmt" "fmt"
"testing" "testing"
"time"
"github.com/prometheus/client_golang/prometheus" "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) handler.Initialize(engine, make(chan common.Message), 1)
timeouts.Initialize(0) timeouts.Initialize(0)
router.Initialize(ctx.Log, timeouts) router.Initialize(ctx.Log, timeouts, time.Hour)
vtxBlocker, _ := queue.New(prefixdb.New([]byte("vtx"), db)) vtxBlocker, _ := queue.New(prefixdb.New([]byte("vtx"), db))
txBlocker, _ := queue.New(prefixdb.New([]byte("tx"), db)) txBlocker, _ := queue.New(prefixdb.New([]byte("tx"), db))

View File

@ -62,6 +62,26 @@ func (t *Transitive) finishBootstrapping() {
t.bootstrapped = true 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 // Shutdown implements the Engine interface
func (t *Transitive) Shutdown() { func (t *Transitive) Shutdown() {
t.Config.Context.Log.Info("Shutting down Avalanche consensus") t.Config.Context.Log.Info("Shutting down Avalanche consensus")

View File

@ -2536,3 +2536,54 @@ func TestEnginePartiallyValidVertex(t *testing.T) {
te.insert(vtx) 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 this engine.
Startup() Startup()
// Gossip to the network a container on the accepted frontier
Gossip()
// Shutdown this engine. // Shutdown this engine.
Shutdown() Shutdown()

View File

@ -14,6 +14,7 @@ type Sender interface {
AcceptedSender AcceptedSender
FetchSender FetchSender
QuerySender QuerySender
Gossiper
} }
// FrontierSender defines how a consensus engine sends frontier messages to // 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 sends chits to the specified validator
Chits(validatorID ids.ShortID, requestID uint32, votes ids.Set) 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 T *testing.T
CantStartup, CantStartup,
CantGossip,
CantShutdown, CantShutdown,
CantContext, CantContext,
@ -38,7 +39,7 @@ type EngineTest struct {
CantQueryFailed, CantQueryFailed,
CantChits bool CantChits bool
StartupF, ShutdownF func() StartupF, GossipF, ShutdownF func()
ContextF func() *snow.Context ContextF func() *snow.Context
NotifyF func(Message) NotifyF func(Message)
GetF, GetFailedF, PullQueryF func(validatorID ids.ShortID, requestID uint32, containerID ids.ID) GetF, GetFailedF, PullQueryF func(validatorID ids.ShortID, requestID uint32, containerID ids.ID)
@ -50,6 +51,7 @@ type EngineTest struct {
// Default ... // Default ...
func (e *EngineTest) Default(cant bool) { func (e *EngineTest) Default(cant bool) {
e.CantStartup = cant e.CantStartup = cant
e.CantGossip = cant
e.CantShutdown = cant e.CantShutdown = cant
e.CantContext = 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 ... // Shutdown ...
func (e *EngineTest) Shutdown() { func (e *EngineTest) Shutdown() {
if e.ShutdownF != nil { if e.ShutdownF != nil {

View File

@ -16,7 +16,8 @@ type SenderTest struct {
CantGetAcceptedFrontier, CantAcceptedFrontier, CantGetAcceptedFrontier, CantAcceptedFrontier,
CantGetAccepted, CantAccepted, CantGetAccepted, CantAccepted,
CantGet, CantPut, CantGet, CantPut,
CantPullQuery, CantPushQuery, CantChits bool CantPullQuery, CantPushQuery, CantChits,
CantGossip bool
GetAcceptedFrontierF func(ids.ShortSet, uint32) GetAcceptedFrontierF func(ids.ShortSet, uint32)
AcceptedFrontierF func(ids.ShortID, uint32, ids.Set) AcceptedFrontierF func(ids.ShortID, uint32, ids.Set)
@ -27,6 +28,7 @@ type SenderTest struct {
PushQueryF func(ids.ShortSet, uint32, ids.ID, []byte) PushQueryF func(ids.ShortSet, uint32, ids.ID, []byte)
PullQueryF func(ids.ShortSet, uint32, ids.ID) PullQueryF func(ids.ShortSet, uint32, ids.ID)
ChitsF func(ids.ShortID, uint32, ids.Set) ChitsF func(ids.ShortID, uint32, ids.Set)
GossipF func(ids.ID, []byte)
} }
// Default set the default callable value to [cant] // Default set the default callable value to [cant]
@ -40,6 +42,7 @@ func (s *SenderTest) Default(cant bool) {
s.CantPullQuery = cant s.CantPullQuery = cant
s.CantPushQuery = cant s.CantPushQuery = cant
s.CantChits = cant s.CantChits = cant
s.CantGossip = cant
} }
// GetAcceptedFrontier calls GetAcceptedFrontierF if it was initialized. If it // 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") 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" "errors"
"fmt" "fmt"
"testing" "testing"
"time"
"github.com/prometheus/client_golang/prometheus" "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) handler.Initialize(engine, make(chan common.Message), 1)
timeouts.Initialize(0) timeouts.Initialize(0)
router.Initialize(ctx.Log, timeouts) router.Initialize(ctx.Log, timeouts, time.Hour)
blocker, _ := queue.New(db) 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 // Shutdown implements the Engine interface
func (t *Transitive) Shutdown() { func (t *Transitive) Shutdown() {
t.Config.Context.Log.Info("Shutting down Snowman consensus") 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") 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) h.engine.Chits(msg.validatorID, msg.requestID, msg.containerIDs)
case notifyMsg: case notifyMsg:
h.engine.Notify(msg.notification) h.engine.Notify(msg.notification)
case gossipMsg:
h.engine.Gossip()
case shutdownMsg: case shutdownMsg:
h.engine.Shutdown() h.engine.Shutdown()
return false 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 // Shutdown shuts down the dispatcher
func (h *Handler) Shutdown() { h.msgs <- message{messageType: shutdownMsg}; h.wg.Wait() } func (h *Handler) Shutdown() { h.msgs <- message{messageType: shutdownMsg}; h.wg.Wait() }

View File

@ -29,6 +29,7 @@ const (
chitsMsg chitsMsg
queryFailedMsg queryFailedMsg
notifyMsg notifyMsg
gossipMsg
shutdownMsg shutdownMsg
) )
@ -87,6 +88,8 @@ func (t msgType) String() string {
return "Query Failed Message" return "Query Failed Message"
case notifyMsg: case notifyMsg:
return "Notify Message" return "Notify Message"
case gossipMsg:
return "Gossip Message"
case shutdownMsg: case shutdownMsg:
return "Shutdown Message" return "Shutdown Message"
default: default:

View File

@ -4,6 +4,8 @@
package router package router
import ( import (
"time"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/networking/handler" "github.com/ava-labs/gecko/snow/networking/handler"
"github.com/ava-labs/gecko/snow/networking/timeout" "github.com/ava-labs/gecko/snow/networking/timeout"
@ -19,7 +21,7 @@ type Router interface {
AddChain(chain *handler.Handler) AddChain(chain *handler.Handler)
RemoveChain(chainID ids.ID) RemoveChain(chainID ids.ID)
Shutdown() 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 // ExternalRouter routes messages from the network to the

View File

@ -5,11 +5,13 @@ package router
import ( import (
"sync" "sync"
"time"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow/networking/handler" "github.com/ava-labs/gecko/snow/networking/handler"
"github.com/ava-labs/gecko/snow/networking/timeout" "github.com/ava-labs/gecko/snow/networking/timeout"
"github.com/ava-labs/gecko/utils/logging" "github.com/ava-labs/gecko/utils/logging"
"github.com/ava-labs/gecko/utils/timer"
) )
// ChainRouter routes incoming messages from the validator network // ChainRouter routes incoming messages from the validator network
@ -21,15 +23,24 @@ type ChainRouter struct {
lock sync.RWMutex lock sync.RWMutex
chains map[[32]byte]*handler.Handler chains map[[32]byte]*handler.Handler
timeouts *timeout.Manager timeouts *timeout.Manager
gossiper *timer.Repeater
} }
// Initialize the router // 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 // When this router receives an incoming message, it cancels the timeout in
func (sr *ChainRouter) Initialize(log logging.Logger, timeouts *timeout.Manager) { // [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.log = log
sr.chains = make(map[[32]byte]*handler.Handler) sr.chains = make(map[[32]byte]*handler.Handler)
sr.timeouts = timeouts sr.timeouts = timeouts
sr.gossiper = timer.NewRepeater(sr.Gossip, gossipFrequency)
go log.RecoverAndPanic(sr.gossiper.Dispatch)
} }
// AddChain registers the specified chain so that incoming // AddChain registers the specified chain so that incoming
@ -255,4 +266,19 @@ func (sr *ChainRouter) shutdown() {
for _, chain := range sr.chains { for _, chain := range sr.chains {
chain.Shutdown() 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) 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) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID)
Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set) 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) 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() go tm.Dispatch()
router := router.ChainRouter{} router := router.ChainRouter{}
router.Initialize(logging.NoLog{}, &tm) router.Initialize(logging.NoLog{}, &tm, time.Hour)
sender := Sender{} sender := Sender{}
sender.Initialize(snow.DefaultContextTest(), &ExternalSenderTest{}, &router, &tm) sender.Initialize(snow.DefaultContextTest(), &ExternalSenderTest{}, &router, &tm)

View File

@ -17,7 +17,8 @@ type ExternalSenderTest struct {
CantGetAcceptedFrontier, CantAcceptedFrontier, CantGetAcceptedFrontier, CantAcceptedFrontier,
CantGetAccepted, CantAccepted, CantGetAccepted, CantAccepted,
CantGet, CantPut, CantGet, CantPut,
CantPullQuery, CantPushQuery, CantChits bool CantPullQuery, CantPushQuery, CantChits,
CantGossip bool
GetAcceptedFrontierF func(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32) GetAcceptedFrontierF func(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32)
AcceptedFrontierF func(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) 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) 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) 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) 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] // Default set the default callable value to [cant]
@ -41,6 +43,7 @@ func (s *ExternalSenderTest) Default(cant bool) {
s.CantPullQuery = cant s.CantPullQuery = cant
s.CantPushQuery = cant s.CantPushQuery = cant
s.CantChits = cant s.CantChits = cant
s.CantGossip = cant
} }
// GetAcceptedFrontier calls GetAcceptedFrontierF if it was initialized. If it // 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") 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

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

View File

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

View File

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

View File

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

View File

@ -9,44 +9,22 @@ import (
"github.com/ava-labs/gecko/snow/choices" "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/ids"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/formatting" "github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/vms/secp256k1fx"
) )
func setup(t *testing.T) ([]byte, *VM, *Service) { func setup(t *testing.T) ([]byte, *VM, *Service) {
genesisBytes := BuildGenesisTest(t) genesisBytes, _, vm := GenesisVM(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)
}
s := &Service{vm: vm} s := &Service{vm: vm}
return genesisBytes, vm, s return genesisBytes, vm, s
} }
func TestServiceIssueTx(t *testing.T) { func TestServiceIssueTx(t *testing.T) {
genesisBytes, vm, s := setup(t) genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
txArgs := &IssueTxArgs{} txArgs := &IssueTxArgs{}
txReply := &IssueTxReply{} txReply := &IssueTxReply{}
@ -68,8 +46,10 @@ func TestServiceIssueTx(t *testing.T) {
func TestServiceGetTxStatus(t *testing.T) { func TestServiceGetTxStatus(t *testing.T) {
genesisBytes, vm, s := setup(t) genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
statusArgs := &GetTxStatusArgs{} statusArgs := &GetTxStatusArgs{}
statusReply := &GetTxStatusReply{} statusReply := &GetTxStatusReply{}
@ -109,8 +89,10 @@ func TestServiceGetTxStatus(t *testing.T) {
func TestServiceGetUTXOsInvalidAddress(t *testing.T) { func TestServiceGetUTXOsInvalidAddress(t *testing.T) {
_, vm, s := setup(t) _, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
addr0 := keys[0].PublicKey().Address() addr0 := keys[0].PublicKey().Address()
tests := []struct { tests := []struct {
@ -137,8 +119,10 @@ func TestServiceGetUTXOsInvalidAddress(t *testing.T) {
func TestServiceGetUTXOs(t *testing.T) { func TestServiceGetUTXOs(t *testing.T) {
_, vm, s := setup(t) _, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
addr0 := keys[0].PublicKey().Address() addr0 := keys[0].PublicKey().Address()
tests := []struct { tests := []struct {
@ -187,8 +171,10 @@ func TestServiceGetUTXOs(t *testing.T) {
func TestGetAssetDescription(t *testing.T) { func TestGetAssetDescription(t *testing.T) {
genesisBytes, vm, s := setup(t) genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t) genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -212,8 +198,10 @@ func TestGetAssetDescription(t *testing.T) {
func TestGetBalance(t *testing.T) { func TestGetBalance(t *testing.T) {
genesisBytes, vm, s := setup(t) genesisBytes, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t) genesisTx := GetFirstTxFromGenesisTest(genesisBytes, t)
@ -235,8 +223,10 @@ func TestGetBalance(t *testing.T) {
func TestCreateFixedCapAsset(t *testing.T) { func TestCreateFixedCapAsset(t *testing.T) {
_, vm, s := setup(t) _, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
reply := CreateFixedCapAssetReply{} reply := CreateFixedCapAssetReply{}
err := s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{ err := s.CreateFixedCapAsset(nil, &CreateFixedCapAssetArgs{
@ -259,8 +249,10 @@ func TestCreateFixedCapAsset(t *testing.T) {
func TestCreateVariableCapAsset(t *testing.T) { func TestCreateVariableCapAsset(t *testing.T) {
_, vm, s := setup(t) _, vm, s := setup(t)
defer ctx.Lock.Unlock() defer func() {
defer vm.Shutdown() vm.Shutdown()
ctx.Lock.Unlock()
}()
reply := CreateVariableCapAssetReply{} reply := CreateVariableCapAssetReply{}
err := s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{ err := s.CreateVariableCapAsset(nil, &CreateVariableCapAssetArgs{

View File

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

View File

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

View File

@ -90,7 +90,9 @@ func (s *State) Status(id ids.ID) (choices.Status, error) {
} }
var status choices.Status 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) s.Cache.Put(id, status)
return status, nil return status, nil
@ -103,12 +105,12 @@ func (s *State) SetStatus(id ids.ID, status choices.Status) error {
return s.DB.Delete(id.Bytes()) return s.DB.Delete(id.Bytes())
} }
s.Cache.Put(id, status)
bytes, err := s.Codec.Marshal(status) bytes, err := s.Codec.Marshal(status)
if err != nil { if err != nil {
return err return err
} }
s.Cache.Put(id, status)
return s.DB.Put(id.Bytes(), bytes) 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()) return s.DB.Delete(id.Bytes())
} }
s.Cache.Put(id, idSlice)
bytes, err := s.Codec.Marshal(idSlice) bytes, err := s.Codec.Marshal(idSlice)
if err != nil { if err != nil {
return err return err
} }
s.Cache.Put(id, idSlice)
return s.DB.Put(id.Bytes(), bytes) return s.DB.Put(id.Bytes(), bytes)
} }

View File

@ -13,6 +13,11 @@ import (
func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) { func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
vm := defaultVM() vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil // Case 1: tx is nil
var tx *addDefaultSubnetDelegatorTx var tx *addDefaultSubnetDelegatorTx
@ -153,6 +158,11 @@ func TestAddDefaultSubnetDelegatorTxSyntacticVerify(t *testing.T) {
func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) { func TestAddDefaultSubnetDelegatorTxSemanticVerify(t *testing.T) {
vm := defaultVM() vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: Proposed validator currently validating default subnet // Case 1: Proposed validator currently validating default subnet
// but stops validating non-default subnet after stops 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) { func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
vm := defaultVM() vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: tx is nil // Case 1: tx is nil
var tx *addDefaultSubnetValidatorTx var tx *addDefaultSubnetValidatorTx
@ -216,6 +221,11 @@ func TestAddDefaultSubnetValidatorTxSyntacticVerify(t *testing.T) {
// Test AddDefaultSubnetValidatorTx.SemanticVerify // Test AddDefaultSubnetValidatorTx.SemanticVerify
func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) { func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
vm := defaultVM() vm := defaultVM()
vm.Ctx.Lock.Lock()
defer func() {
vm.Shutdown()
vm.Ctx.Lock.Unlock()
}()
// Case 1: Validator's start time too early // Case 1: Validator's start time too early
tx, err := vm.newAddDefaultSubnetValidatorTx( tx, err := vm.newAddDefaultSubnetValidatorTx(
@ -281,9 +291,9 @@ func TestAddDefaultSubnetValidatorTxSemanticVerify(t *testing.T) {
} }
startTime := defaultGenesisTime.Add(1 * time.Second) startTime := defaultGenesisTime.Add(1 * time.Second)
tx, err = vm.newAddDefaultSubnetValidatorTx( tx, err = vm.newAddDefaultSubnetValidatorTx(
defaultNonce+1, // nonce defaultNonce+1, // nonce
defaultStakeAmount, // stake amount defaultStakeAmount, // stake amount
uint64(startTime.Unix()), // start time uint64(startTime.Unix()), // start time
uint64(startTime.Add(MinimumStakingDuration).Unix()), // end time uint64(startTime.Add(MinimumStakingDuration).Unix()), // end time
key.PublicKey().Address(), // node ID key.PublicKey().Address(), // node ID
defaultKey.PublicKey().Address(), // destination defaultKey.PublicKey().Address(), // destination

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/ava-labs/gecko/database" "github.com/ava-labs/gecko/database"
"github.com/ava-labs/gecko/ids" "github.com/ava-labs/gecko/ids"
@ -76,7 +77,7 @@ type GetSubnetsResponse struct {
func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, response *GetSubnetsResponse) error { func (service *Service) GetSubnets(_ *http.Request, args *GetSubnetsArgs, response *GetSubnetsResponse) error {
subnets, err := service.vm.getSubnets(service.vm.DB) // all subnets subnets, err := service.vm.getSubnets(service.vm.DB) // all subnets
if err != nil { 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 getAll := len(args.IDs) == 0
@ -278,7 +279,7 @@ type GetAccountReply struct {
func (service *Service) GetAccount(_ *http.Request, args *GetAccountArgs, reply *GetAccountReply) error { func (service *Service) GetAccount(_ *http.Request, args *GetAccountArgs, reply *GetAccountReply) error {
account, err := service.vm.getAccount(service.vm.DB, args.Address) account, err := service.vm.getAccount(service.vm.DB, args.Address)
if err != nil && err != database.ErrNotFound { if err != nil && err != database.ErrNotFound {
return errGetAccount return fmt.Errorf("couldn't get account: %w", err)
} else if err == database.ErrNotFound { } else if err == database.ErrNotFound {
account = newAccount(args.Address, 0, 0) account = newAccount(args.Address, 0, 0)
} }
@ -308,7 +309,7 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
// db holds the user's info that pertains to the Platform Chain // db holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil { if err != nil {
return errGetUser return fmt.Errorf("couldn't get user: %w", err)
} }
// The user // The user
@ -319,14 +320,14 @@ func (service *Service) ListAccounts(_ *http.Request, args *ListAccountsArgs, re
// IDs of accounts controlled by this user // IDs of accounts controlled by this user
accountIDs, err := user.getAccountIDs() accountIDs, err := user.getAccountIDs()
if err != nil { if err != nil {
return errGetAccounts return fmt.Errorf("couldn't get accounts held by user: %w", err)
} }
reply.Accounts = []APIAccount{} reply.Accounts = []APIAccount{}
for _, accountID := range accountIDs { for _, accountID := range accountIDs {
account, err := service.vm.getAccount(service.vm.DB, accountID) // Get account whose ID is [accountID] account, err := service.vm.getAccount(service.vm.DB, accountID) // Get account whose ID is [accountID]
if err != nil && err != database.ErrNotFound { 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 continue
} else if err == database.ErrNotFound { } else if err == database.ErrNotFound {
account = newAccount(accountID, 0, 0) account = newAccount(accountID, 0, 0)
@ -370,7 +371,7 @@ func (service *Service) CreateAccount(_ *http.Request, args *CreateAccountArgs,
// userDB holds the user's info that pertains to the Platform Chain // userDB holds the user's info that pertains to the Platform Chain
userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) userDB, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil { if err != nil {
return errGetUser return fmt.Errorf("couldn't get user: %w", err)
} }
// The user creating a new account // The user creating a new account
@ -428,7 +429,7 @@ type CreateTxResponse struct {
type AddDefaultSubnetValidatorArgs struct { type AddDefaultSubnetValidatorArgs struct {
APIDefaultSubnetValidator 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"` PayerNonce json.Uint64 `json:"payerNonce"`
} }
@ -437,8 +438,13 @@ type AddDefaultSubnetValidatorArgs struct {
func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error { func (service *Service) AddDefaultSubnetValidator(_ *http.Request, args *AddDefaultSubnetValidatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("AddDefaultSubnetValidator called") 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 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 // Create the transaction
@ -482,8 +488,13 @@ type AddDefaultSubnetDelegatorArgs struct {
func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error { func (service *Service) AddDefaultSubnetDelegator(_ *http.Request, args *AddDefaultSubnetDelegatorArgs, reply *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("AddDefaultSubnetDelegator called") 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 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 // Create the transaction
@ -571,6 +582,11 @@ type CreateSubnetArgs struct {
func (service *Service) CreateSubnet(_ *http.Request, args *CreateSubnetArgs, response *CreateTxResponse) error { func (service *Service) CreateSubnet(_ *http.Request, args *CreateSubnetArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.createSubnet called") 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 // Create the transaction
tx := CreateSubnetTx{ tx := CreateSubnetTx{
UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{ UnsignedCreateSubnetTx: UnsignedCreateSubnetTx{
@ -612,6 +628,13 @@ type ExportAVAArgs struct {
func (service *Service) ExportAVA(_ *http.Request, args *ExportAVAArgs, response *CreateTxResponse) error { func (service *Service) ExportAVA(_ *http.Request, args *ExportAVAArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("platform.ExportAVA called") 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 // Create the transaction
tx := ExportTx{UnsignedExportTx: UnsignedExportTx{ tx := ExportTx{UnsignedExportTx: UnsignedExportTx{
NetworkID: service.vm.Ctx.NetworkID, NetworkID: service.vm.Ctx.NetworkID,
@ -667,6 +690,11 @@ type SignResponse struct {
func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignResponse) error { func (service *Service) Sign(_ *http.Request, args *SignArgs, reply *SignResponse) error {
service.vm.Ctx.Log.Debug("sign called") service.vm.Ctx.Log.Debug("sign called")
switch {
case args.Signer.Equals(ids.ShortEmpty):
return fmt.Errorf("signer not specified")
}
// Get the key of the Signer // Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil { if err != nil {
@ -719,7 +747,7 @@ func (service *Service) signAddDefaultSubnetValidatorTx(tx *addDefaultSubnetVali
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx) unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetValidatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
@ -742,7 +770,7 @@ func (service *Service) signAddDefaultSubnetDelegatorTx(tx *addDefaultSubnetDele
unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx) unsignedIntf := interface{}(&tx.UnsignedAddDefaultSubnetDelegatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
@ -765,7 +793,7 @@ func (service *Service) signCreateSubnetTx(tx *CreateSubnetTx, key *crypto.Priva
unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx) unsignedIntf := interface{}(&tx.UnsignedCreateSubnetTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
@ -788,7 +816,7 @@ func (service *Service) signExportTx(tx *ExportTx, key *crypto.PrivateKeySECP256
unsignedIntf := interface{}(&tx.UnsignedExportTx) unsignedIntf := interface{}(&tx.UnsignedExportTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
@ -816,7 +844,7 @@ func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubn
unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx) unsignedIntf := interface{}(&tx.UnsignedAddNonDefaultSubnetValidatorTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
if err != nil { if err != nil {
@ -829,7 +857,7 @@ func (service *Service) signAddNonDefaultSubnetValidatorTx(tx *addNonDefaultSubn
// Get information about the subnet // Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID()) subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID())
if err != nil { 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. // Find the location at which [key] should put its signature.
@ -861,7 +889,7 @@ type ImportAVAArgs struct {
// ID of the account that will receive the imported funds, and pay the transaction fee // ID of the account that will receive the imported funds, and pay the transaction fee
To ids.ShortID `json:"to"` To ids.ShortID `json:"to"`
// Next unused nonce of the account // Next nonce of the sender
PayerNonce json.Uint64 `json:"payerNonce"` PayerNonce json.Uint64 `json:"payerNonce"`
// User that controls the account // User that controls the account
@ -875,10 +903,15 @@ type ImportAVAArgs struct {
func (service *Service) ImportAVA(_ *http.Request, args *ImportAVAArgs, response *SignResponse) error { func (service *Service) ImportAVA(_ *http.Request, args *ImportAVAArgs, response *SignResponse) error {
service.vm.Ctx.Log.Debug("platform.ImportAVA called") service.vm.Ctx.Log.Debug("platform.ImportAVA called")
switch {
case args.PayerNonce == 0:
return fmt.Errorf("sender's next nonce not specified")
}
// Get the key of the Signer // Get the key of the Signer
db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password) db, err := service.vm.Ctx.Keystore.GetDatabase(args.Username, args.Password)
if err != nil { 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} user := user{db: db}
@ -991,7 +1024,7 @@ func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.Private
unsignedIntf := interface{}(&tx.UnsignedCreateChainTx) unsignedIntf := interface{}(&tx.UnsignedCreateChainTx)
unsignedTxBytes, err := Codec.Marshal(&unsignedIntf) unsignedTxBytes, err := Codec.Marshal(&unsignedIntf)
if err != nil { 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) sig, err := key.Sign(unsignedTxBytes)
if err != nil { if err != nil {
@ -1004,7 +1037,7 @@ func (service *Service) signCreateChainTx(tx *CreateChainTx, key *crypto.Private
// Get information about the subnet // Get information about the subnet
subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID) subnet, err := service.vm.getSubnet(service.vm.DB, tx.SubnetID)
if err != nil { 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. // Find the location at which [key] should put its signature.
@ -1099,7 +1132,7 @@ type CreateBlockchainArgs struct {
// Human-readable name for the new blockchain, not necessarily unique // Human-readable name for the new blockchain, not necessarily unique
Name string `json:"name"` Name string `json:"name"`
// Next unused nonce of the account paying the transaction fee // Next nonce of the sender
PayerNonce json.Uint64 `json:"payerNonce"` PayerNonce json.Uint64 `json:"payerNonce"`
// Genesis state of the blockchain being created // Genesis state of the blockchain being created
@ -1111,6 +1144,15 @@ type CreateBlockchainArgs struct {
func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, response *CreateTxResponse) error { func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchainArgs, response *CreateTxResponse) error {
service.vm.Ctx.Log.Debug("createBlockchain called") 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) vmID, err := service.vm.chainManager.LookupVM(args.VMID)
if err != nil { if err != nil {
return fmt.Errorf("no VM with ID '%s' found", args.VMID) return fmt.Errorf("no VM with ID '%s' found", args.VMID)
@ -1156,7 +1198,7 @@ func (service *Service) CreateBlockchain(_ *http.Request, args *CreateBlockchain
txBytes, err := Codec.Marshal(genericTx{Tx: &tx}) txBytes, err := Codec.Marshal(genericTx{Tx: &tx})
if err != nil { 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 return errCreatingTransaction
} }
@ -1180,6 +1222,11 @@ type GetBlockchainStatusReply struct {
func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error { func (service *Service) GetBlockchainStatus(_ *http.Request, args *GetBlockchainStatusArgs, reply *GetBlockchainStatusReply) error {
service.vm.Ctx.Log.Debug("getBlockchainStatus called") service.vm.Ctx.Log.Debug("getBlockchainStatus called")
switch {
case args.BlockchainID == "":
return errors.New("'blockchainID' not given")
}
_, err := service.vm.chainManager.Lookup(args.BlockchainID) _, err := service.vm.chainManager.Lookup(args.BlockchainID)
if err == nil { if err == nil {
reply.Status = Validating reply.Status = Validating
@ -1255,6 +1302,11 @@ type ValidatedByResponse struct {
func (service *Service) ValidatedBy(_ *http.Request, args *ValidatedByArgs, response *ValidatedByResponse) error { func (service *Service) ValidatedBy(_ *http.Request, args *ValidatedByArgs, response *ValidatedByResponse) error {
service.vm.Ctx.Log.Debug("validatedBy called") 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) chain, err := service.vm.getChain(service.vm.DB, args.BlockchainID)
if err != nil { if err != nil {
return err return err
@ -1277,6 +1329,11 @@ type ValidatesResponse struct {
func (service *Service) Validates(_ *http.Request, args *ValidatesArgs, response *ValidatesResponse) error { func (service *Service) Validates(_ *http.Request, args *ValidatesArgs, response *ValidatesResponse) error {
service.vm.Ctx.Log.Debug("validates called") 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 // Verify that the Subnet exists
if _, err := service.vm.getSubnet(service.vm.DB, args.SubnetID); err != nil { if _, err := service.vm.getSubnet(service.vm.DB, args.SubnetID); err != nil {
return err return err
@ -1322,7 +1379,7 @@ func (service *Service) GetBlockchains(_ *http.Request, args *struct{}, response
chains, err := service.vm.getChains(service.vm.DB) chains, err := service.vm.getChains(service.vm.DB)
if err != nil { 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 { for _, chain := range chains {

View File

@ -405,7 +405,12 @@ func (vm *VM) Shutdown() {
return 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.timer.Stop()
vm.Ctx.Lock.Lock()
if err := vm.DB.Close(); err != nil { if err := vm.DB.Close(); err != nil {
vm.Ctx.Log.Error("Closing the database failed with %s", err) vm.Ctx.Log.Error("Closing the database failed with %s", err)
} }

View File

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

View File

@ -62,10 +62,11 @@ func ConsensusLeader(numBlocks, numTxsPerBlock int, b *testing.B) {
go timeoutManager.Dispatch() go timeoutManager.Dispatch()
router := &router.ChainRouter{} router := &router.ChainRouter{}
router.Initialize(logging.NoLog{}, &timeoutManager) router.Initialize(logging.NoLog{}, &timeoutManager, time.Hour)
// Initialize the VM // Initialize the VM
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
ctx.Lock.Lock() ctx.Lock.Lock()
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil { if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil {
b.Fatal(err) b.Fatal(err)
@ -189,7 +190,7 @@ func ConsensusFollower(numBlocks, numTxsPerBlock int, b *testing.B) {
go timeoutManager.Dispatch() go timeoutManager.Dispatch()
router := &router.ChainRouter{} router := &router.ChainRouter{}
router.Initialize(logging.NoLog{}, &timeoutManager) router.Initialize(logging.NoLog{}, &timeoutManager, time.Hour)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(numBlocks) wg.Add(numBlocks)
@ -198,6 +199,7 @@ func ConsensusFollower(numBlocks, numTxsPerBlock int, b *testing.B) {
vm := &VM{ vm := &VM{
onAccept: func(ids.ID) { wg.Done() }, onAccept: func(ids.ID) { wg.Done() },
} }
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
ctx.Lock.Lock() ctx.Lock.Lock()
if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil { if err := vm.Initialize(ctx, vmDB, genesisData, msgChan, nil); err != nil {
b.Fatal(err) b.Fatal(err)

View File

@ -122,7 +122,11 @@ func (vm *VM) Shutdown() {
return 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.timer.Stop()
vm.ctx.Lock.Lock()
if err := vm.baseDB.Close(); err != nil { if err := vm.baseDB.Close(); err != nil {
vm.ctx.Log.Error("Closing the database failed with %s", err) vm.ctx.Log.Error("Closing the database failed with %s", err)
} }

View File

@ -73,6 +73,7 @@ func BenchmarkParseBlock(b *testing.B) {
/*testing=*/ b, /*testing=*/ b,
) )
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ ctx, /*ctx=*/ ctx,
/*db=*/ memdb.New(), /*db=*/ memdb.New(),
@ -106,6 +107,7 @@ func BenchmarkParseAndVerify(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ snow.DefaultContextTest(), /*ctx=*/ snow.DefaultContextTest(),
/*db=*/ memdb.New(), /*db=*/ memdb.New(),
@ -141,6 +143,8 @@ func BenchmarkAccept(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ snow.DefaultContextTest(), /*ctx=*/ snow.DefaultContextTest(),
/*db=*/ memdb.New(), /*db=*/ memdb.New(),
@ -178,6 +182,7 @@ func ParseAndVerifyAndAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ snow.DefaultContextTest(), /*ctx=*/ snow.DefaultContextTest(),
/*db=*/ memdb.New(), /*db=*/ memdb.New(),
@ -232,6 +237,7 @@ func ParseThenVerifyThenAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ snow.DefaultContextTest(), /*ctx=*/ snow.DefaultContextTest(),
/*db=*/ memdb.New(), /*db=*/ memdb.New(),
@ -292,6 +298,7 @@ func IssueAndVerifyAndAccept(numBlocks, numTxsPerBlock int, b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize( vm.Initialize(
/*ctx=*/ snow.DefaultContextTest(), /*ctx=*/ snow.DefaultContextTest(),
/*db=*/ memdb.New(), /*db=*/ memdb.New(),

View File

@ -67,6 +67,7 @@ func TestPayments(t *testing.T) {
blocker, _ := queue.New(bootstrappingDB) blocker, _ := queue.New(bootstrappingDB)
vm := &VM{} vm := &VM{}
defer func() { ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize(ctx, db, genesisData, msgChan, nil) vm.Initialize(ctx, db, genesisData, msgChan, nil)
sender := &common.SenderTest{} sender := &common.SenderTest{}

View File

@ -134,7 +134,11 @@ func (vm *VM) Shutdown() {
return 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.timer.Stop()
vm.ctx.Lock.Lock()
if err := vm.baseDB.Close(); err != nil { if err := vm.baseDB.Close(); err != nil {
vm.ctx.Log.Error("Closing the database failed with %s", err) 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) msgChan := make(chan common.Message, 1)
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0 vm.batchTimeout = 0
@ -172,6 +173,7 @@ func TestInvalidSpentTx(t *testing.T) {
msgChan := make(chan common.Message, 1) msgChan := make(chan common.Message, 1)
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
ctx.Lock.Lock() ctx.Lock.Lock()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
@ -258,6 +260,7 @@ func TestInvalidTxVerification(t *testing.T) {
msgChan := make(chan common.Message, 1) msgChan := make(chan common.Message, 1)
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
ctx.Lock.Lock() ctx.Lock.Lock()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
@ -319,6 +322,7 @@ func TestRPCAPI(t *testing.T) {
vmDB := memdb.New() vmDB := memdb.New()
msgChan := make(chan common.Message, 1) msgChan := make(chan common.Message, 1)
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0 vm.batchTimeout = 0
@ -526,6 +530,7 @@ func TestMultipleSend(t *testing.T) {
vmDB := memdb.New() vmDB := memdb.New()
msgChan := make(chan common.Message, 1) msgChan := make(chan common.Message, 1)
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
// Initialize these data structures // Initialize these data structures
@ -635,6 +640,7 @@ func TestIssuePendingDependency(t *testing.T) {
ctx.Lock.Lock() ctx.Lock.Lock()
vm := &VM{} vm := &VM{}
defer func() { vm.ctx.Lock.Lock(); vm.Shutdown(); vm.ctx.Lock.Unlock() }()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil) vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0 vm.batchTimeout = 0