diff --git a/.ci/run_e2e_tests.sh b/.ci/run_e2e_tests.sh index ac03e0617612..f8ac332ba21c 100755 --- a/.ci/run_e2e_tests.sh +++ b/.ci/run_e2e_tests.sh @@ -7,7 +7,7 @@ AVALANCHE_IMAGE=$(docker image ls --format="{{.Repository}}" | head -n 1) DOCKER_REPO="avaplatform" E2E_TESTING_REMOTE="https://github.com/ava-labs/avalanche-testing.git" -E2E_TAG="v0.9.2-dev" +E2E_TAG="v0.9.3-dev.1" mkdir -p "$E2E_TEST_HOME" git clone "$E2E_TESTING_REMOTE" "$E2E_TEST_HOME" diff --git a/api/admin/service.go b/api/admin/service.go index 01e12e98108c..c22b40cf38c0 100644 --- a/api/admin/service.go +++ b/api/admin/service.go @@ -5,6 +5,7 @@ package admin import ( "errors" + "io/ioutil" "net/http" "github.com/gorilla/rpc/v2" @@ -19,6 +20,9 @@ import ( const ( maxAliasLength = 512 + + // Name of file that stacktraces are written to + stacktraceFile = "stacktrace.txt" ) var ( @@ -124,15 +128,11 @@ func (service *Admin) AliasChain(_ *http.Request, args *AliasChainArgs, reply *a return service.httpServer.AddAliasesWithReadLock("bc/"+chainID.String(), "bc/"+args.Alias) } -// StacktraceReply are the results from calling Stacktrace -type StacktraceReply struct { - Stacktrace string `json:"stacktrace"` -} - // Stacktrace returns the current global stacktrace -func (service *Admin) Stacktrace(_ *http.Request, _ *struct{}, reply *StacktraceReply) error { +func (service *Admin) Stacktrace(_ *http.Request, _ *struct{}, reply *api.SuccessResponse) error { service.log.Info("Admin: Stacktrace called") - reply.Stacktrace = logging.Stacktrace{Global: true}.String() - return nil + reply.Success = true + stacktrace := []byte(logging.Stacktrace{Global: true}.String()) + return ioutil.WriteFile(stacktraceFile, stacktrace, 0644) } diff --git a/network/network.go b/network/network.go index f4ed5d6ed978..b9249f497219 100644 --- a/network/network.go +++ b/network/network.go @@ -4,6 +4,7 @@ package network import ( + "errors" "fmt" "math/rand" "net" @@ -47,6 +48,12 @@ const ( defaultPingFrequency = 3 * defaultPingPongTimeout / 4 ) +var ( + errNetworkClosed = errors.New("network closed") + errPeerIsMyself = errors.New("peer is myself") + errDuplicatedConnection = errors.New("duplicated connection") +) + func init() { rand.Seed(time.Now().UnixNano()) } // Network defines the functionality of the networking library. @@ -635,15 +642,15 @@ func (n *network) Dispatch() error { for { conn, err := n.listener.Accept() if err != nil { - n.stateLock.Lock() - closed := n.closed - n.stateLock.Unlock() - - if closed { - return err + if netErr, ok := err.(net.Error); ok && netErr.Temporary() { + // Sleep for a small amount of time to try to wait for the + // temporary error to go away. + time.Sleep(time.Millisecond) + continue } + n.log.Debug("error during server accept: %s", err) - continue + return err } go n.upgrade(&peer{ net: n, @@ -675,17 +682,17 @@ func (n *network) Peers() []PeerID { // Close implements the Network interface func (n *network) Close() error { + err := n.listener.Close() + if err != nil { + n.log.Debug("closing network listener failed with: %s", err) + } + n.stateLock.Lock() if n.closed { n.stateLock.Unlock() return nil } - n.closed = true - err := n.listener.Close() - if err != nil { - n.log.Debug("closing network listener failed with: %s", err) - } peersToClose := []*peer(nil) for _, peer := range n.peers { @@ -935,7 +942,17 @@ func (n *network) upgrade(p *peer, upgrader Upgrader) error { p.id = id p.conn = conn - key := id.Key() + if err := n.tryAddPeer(p); err != nil { + _ = p.conn.Close() + n.log.Debug("dropping peer connection due to: %s", err) + } + return nil +} + +// assumes the stateLock is not held. Returns an error if the peer couldn't be +// added. +func (n *network) tryAddPeer(p *peer) error { + key := p.id.Key() n.stateLock.Lock() defer n.stateLock.Unlock() @@ -943,13 +960,12 @@ func (n *network) upgrade(p *peer, upgrader Upgrader) error { if n.closed { // the network is closing, so make sure that no further reconnect // attempts are made. - _ = p.conn.Close() - return nil + return errNetworkClosed } // if this connection is myself, then I should delete the connection and // mark the IP as one of mine. - if id.Equals(n.id) { + if p.id.Equals(n.id) { if !p.ip.IsZero() { // if n.ip is less useful than p.ip set it to this IP if n.ip.IsZero() { @@ -962,10 +978,7 @@ func (n *network) upgrade(p *peer, upgrader Upgrader) error { delete(n.retryDelay, str) n.myIPs[str] = struct{}{} } - // don't attempt to reconnect to myself, so return nil even if closing - // returns an error - _ = p.conn.Close() - return nil + return errPeerIsMyself } // If I am already connected to this peer, then I should close this new @@ -976,10 +989,7 @@ func (n *network) upgrade(p *peer, upgrader Upgrader) error { delete(n.disconnectedIPs, str) delete(n.retryDelay, str) } - // I'm already connected to this peer, so don't attempt to reconnect to - // this ip, even if an error occurres during closing - _ = p.conn.Close() - return nil + return errDuplicatedConnection } n.peers[key] = p diff --git a/network/peer.go b/network/peer.go index 719a6b0b32aa..e7c1018a2c85 100644 --- a/network/peer.go +++ b/network/peer.go @@ -322,13 +322,13 @@ func (p *peer) Close() { p.once.Do(p.close) } // assumes only `peer.Close` calls this func (p *peer) close() { - p.net.stateLock.Lock() - defer p.net.stateLock.Unlock() - if err := p.conn.Close(); err != nil { p.net.log.Debug("closing peer %s resulted in an error: %s", p.id, err) } + p.net.stateLock.Lock() + defer p.net.stateLock.Unlock() + p.closed = true close(p.sender) p.net.disconnected(p) diff --git a/node/node.go b/node/node.go index 7e30656f7699..00fa9550cb5f 100644 --- a/node/node.go +++ b/node/node.go @@ -64,7 +64,7 @@ var ( genesisHashKey = []byte("genesisID") // Version is the version of this code - Version = version.NewDefaultVersion(constants.PlatformName, 0, 8, 2) + Version = version.NewDefaultVersion(constants.PlatformName, 0, 8, 3) versionParser = version.NewDefaultParser() ) diff --git a/snow/engine/common/bootstrapper.go b/snow/engine/common/bootstrapper.go index b55f6f0db6b1..41be35801b48 100644 --- a/snow/engine/common/bootstrapper.go +++ b/snow/engine/common/bootstrapper.go @@ -45,7 +45,8 @@ type Bootstrapper struct { acceptedVotes map[[32]byte]uint64 // current weight - weight uint64 + started bool + weight uint64 } // Initialize implements the Engine interface. @@ -67,6 +68,7 @@ func (b *Bootstrapper) Initialize(config Config) error { // Startup implements the Engine interface. func (b *Bootstrapper) Startup() error { + b.started = true if b.pendingAcceptedFrontier.Len() == 0 { b.Ctx.Log.Info("Bootstrapping skipped due to no provided bootstraps") return b.Bootstrapable.ForceAccepted(ids.Set{}) @@ -180,6 +182,9 @@ func (b *Bootstrapper) Accepted(validatorID ids.ShortID, requestID uint32, conta // Connected implements the Engine interface. func (b *Bootstrapper) Connected(validatorID ids.ShortID) error { + if b.started { + return nil + } weight, ok := b.Beacons.GetWeight(validatorID) if !ok { return nil