diff --git a/api/admin/networking.go b/api/admin/networking.go index ef564e6..3207e46 100644 --- a/api/admin/networking.go +++ b/api/admin/networking.go @@ -10,14 +10,14 @@ import ( ) // Peerable can return a group of peers -type Peerable interface{ Peers() []utils.IPDesc } +type Peerable interface{ IPs() []utils.IPDesc } // Networking provides helper methods for tracking the current network state type Networking struct{ peers Peerable } // Peers returns the current peers func (n *Networking) Peers() ([]string, error) { - ipDescs := n.peers.Peers() + ipDescs := n.peers.IPs() ips := make([]string, len(ipDescs)) for i, ipDesc := range ipDescs { ips[i] = ipDesc.String() diff --git a/ids/id_test.go b/ids/id_test.go index b541ed5..c43dc3a 100644 --- a/ids/id_test.go +++ b/ids/id_test.go @@ -150,9 +150,9 @@ func TestIDUnmarshalJSON(t *testing.T) { func TestIDHex(t *testing.T) { id := NewID([32]byte{'a', 'v', 'a', ' ', 'l', 'a', 'b', 's'}) - expected := "617661206c61627300000000000000000000000000000000000000000000000000" + expected := "617661206c616273000000000000000000000000000000000000000000000000" actual := id.Hex() - if actual != actual { + if actual != expected { t.Fatalf("got %s, expected %s", actual, expected) } } diff --git a/main/main.go b/main/main.go index fc7ae99..1fab9ef 100644 --- a/main/main.go +++ b/main/main.go @@ -40,6 +40,10 @@ func main() { defer log.StopOnPanic() defer Config.DB.Close() + if Config.StakingIP.IsZero() { + log.Warn("NAT traversal has failed. If this node becomes a staker, it may lose its reward due to being unreachable.") + } + // Track if sybil control is enforced if !Config.EnableStaking { log.Warn("Staking and p2p encryption are disabled. Packet spoofing is possible.") diff --git a/main/params.go b/main/params.go index 0438a60..68b63c4 100644 --- a/main/params.go +++ b/main/params.go @@ -26,6 +26,10 @@ import ( "github.com/ava-labs/gecko/utils/wrappers" ) +const ( + dbVersion = "v0.1.0" +) + // Results of parsing the CLI var ( Config = node.Config{} @@ -143,7 +147,7 @@ func init() { // DB: if *db && err == nil { // TODO: Add better params here - dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID)) + dbPath := path.Join(*dbDir, genesis.NetworkName(Config.NetworkID), dbVersion) db, err := leveldb.New(dbPath, 0, 0, 0) Config.DB = db errs.Add(err) @@ -157,11 +161,9 @@ func init() { // If public IP is not specified, get it using shell command dig if *consensusIP == "" { ip, err = Config.Nat.IP() - errs.Add(fmt.Errorf( - "%s\n"+ - "If you are trying to create a local network, try adding --public-ip=127.0.0.1\n"+ - "If you are attempting to connect to a public network, you may need to manually report your IP and perform port forwarding", - err)) + if err != nil { + ip = net.IPv4zero + } } else { ip = net.ParseIP(*consensusIP) } diff --git a/networking/addrset.go b/networking/addrset.go deleted file mode 100644 index a03e0df..0000000 --- a/networking/addrset.go +++ /dev/null @@ -1,288 +0,0 @@ -// (c) 2019-2020, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package networking - -import ( - "fmt" - "sync" - - "github.com/ava-labs/salticidae-go" - - "github.com/ava-labs/gecko/ids" - "github.com/ava-labs/gecko/utils" -) - -// Connections provides an interface for what a group of connections will -// support. -type Connections interface { - Add(salticidae.NetAddr, ids.ShortID) - - GetIP(ids.ShortID) (salticidae.NetAddr, bool) - GetID(salticidae.NetAddr) (ids.ShortID, bool) - - ContainsIP(salticidae.NetAddr) bool - ContainsID(ids.ShortID) bool - - Remove(salticidae.NetAddr, ids.ShortID) - RemoveIP(salticidae.NetAddr) - RemoveID(ids.ShortID) - - Peers() []utils.IPDesc - IPs() []salticidae.NetAddr - IDs() ids.ShortSet - Conns() ([]utils.IPDesc, []ids.ShortID) - RawConns() ([]salticidae.NetAddr, []ids.ShortID) - - Len() int -} - -// AddrCert implements the Connections interface -type AddrCert struct { - // ip -> id - ipToID map[uint64]ids.ShortID - // id -> ip - idToIP map[[20]byte]salticidae.NetAddr - mux sync.Mutex -} - -// Add Assumes that addr is garbage collected normally -func (ac *AddrCert) Add(ip salticidae.NetAddr, id ids.ShortID) { - ac.mux.Lock() - defer ac.mux.Unlock() - - ac.add(ip, id) -} - -// GetIP returns the ip mapped to the id that is provided if one exists. -func (ac *AddrCert) GetIP(id ids.ShortID) (salticidae.NetAddr, bool) { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.getIP(id) -} - -// GetID returns the id mapped to the ip that is provided if one exists. -func (ac *AddrCert) GetID(ip salticidae.NetAddr) (ids.ShortID, bool) { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.getID(ip) -} - -// ContainsIP returns true if the ip is contained in the connection pool -func (ac *AddrCert) ContainsIP(ip salticidae.NetAddr) bool { - _, exists := ac.GetID(ip) - return exists -} - -// ContainsID returns true if the id is contained in the connection pool -func (ac *AddrCert) ContainsID(id ids.ShortID) bool { - _, exists := ac.GetIP(id) - return exists -} - -// Remove ensures that no connection will have any mapping containing [ip] or -// [id]. -func (ac *AddrCert) Remove(ip salticidae.NetAddr, id ids.ShortID) { - ac.mux.Lock() - defer ac.mux.Unlock() - - ac.remove(ip, id) -} - -// RemoveIP ensures that no connection will have a mapping containing [ip] -func (ac *AddrCert) RemoveIP(ip salticidae.NetAddr) { - ac.mux.Lock() - defer ac.mux.Unlock() - - ac.removeIP(ip) -} - -// RemoveID ensures that no connection will have a mapping containing [id] -func (ac *AddrCert) RemoveID(id ids.ShortID) { - ac.mux.Lock() - defer ac.mux.Unlock() - - ac.removeID(id) -} - -// Peers returns the full list of ips contained in this connection pool. -func (ac *AddrCert) Peers() []utils.IPDesc { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.peers() -} - -// IPs returns the full list of ips contained in this connection pool. This can -// be useful for gossiping a node's connections through the network. -func (ac *AddrCert) IPs() []salticidae.NetAddr { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.ips() -} - -// IDs return the set of IDs that are mapping in this connection pool. -func (ac *AddrCert) IDs() ids.ShortSet { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.ids() -} - -// Conns return the set of connections in this connection pool. -func (ac *AddrCert) Conns() ([]utils.IPDesc, []ids.ShortID) { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.conns() -} - -// RawConns return the set of connections in this connection pool. -func (ac *AddrCert) RawConns() ([]salticidae.NetAddr, []ids.ShortID) { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.rawConns() -} - -// Len returns the number of elements in the map -func (ac *AddrCert) Len() int { - ac.mux.Lock() - defer ac.mux.Unlock() - - return ac.len() -} - -func (ac *AddrCert) init() { - if ac.ipToID == nil { - ac.ipToID = make(map[uint64]ids.ShortID) - } - if ac.idToIP == nil { - ac.idToIP = make(map[[20]byte]salticidae.NetAddr) - } -} - -func (ac *AddrCert) add(ip salticidae.NetAddr, id ids.ShortID) { - ac.init() - - ac.removeIP(ip) - ac.removeID(id) - - ac.ipToID[addrToID(ip)] = id - ac.idToIP[id.Key()] = ip -} - -func (ac *AddrCert) getIP(id ids.ShortID) (salticidae.NetAddr, bool) { - ac.init() - - ip, exists := ac.idToIP[id.Key()] - return ip, exists -} - -func (ac *AddrCert) getID(ip salticidae.NetAddr) (ids.ShortID, bool) { - ac.init() - - id, exists := ac.ipToID[addrToID(ip)] - return id, exists -} - -func (ac *AddrCert) remove(ip salticidae.NetAddr, id ids.ShortID) { - ac.removeIP(ip) - ac.removeID(id) -} - -func (ac *AddrCert) removeIP(ip salticidae.NetAddr) { - ac.init() - - ipID := addrToID(ip) - if id, exists := ac.ipToID[ipID]; exists { - delete(ac.ipToID, ipID) - delete(ac.idToIP, id.Key()) - } -} - -func (ac *AddrCert) removeID(id ids.ShortID) { - ac.init() - - idKey := id.Key() - if ip, exists := ac.idToIP[idKey]; exists { - delete(ac.ipToID, addrToID(ip)) - delete(ac.idToIP, idKey) - } -} - -func (ac *AddrCert) peers() []utils.IPDesc { - ac.init() - - ips := []utils.IPDesc(nil) - for _, ip := range ac.idToIP { - ips = append(ips, toIPDesc(ip)) - } - return ips -} - -func (ac *AddrCert) ips() []salticidae.NetAddr { - ac.init() - - ips := []salticidae.NetAddr(nil) - for _, ip := range ac.idToIP { - ips = append(ips, ip) - } - return ips -} - -func (ac *AddrCert) ids() ids.ShortSet { - ac.init() - - ids := ids.ShortSet{} - for _, id := range ac.ipToID { - ids.Add(id) - } - return ids -} - -func (ac *AddrCert) conns() ([]utils.IPDesc, []ids.ShortID) { - ac.init() - - ipList := []utils.IPDesc(nil) - idList := []ids.ShortID(nil) - for id, ip := range ac.idToIP { - ipList = append(ipList, toIPDesc(ip)) - idList = append(idList, ids.NewShortID(id)) - } - return ipList, idList -} - -func (ac *AddrCert) rawConns() ([]salticidae.NetAddr, []ids.ShortID) { - ac.init() - - ipList := []salticidae.NetAddr(nil) - idList := []ids.ShortID(nil) - for id, ip := range ac.idToIP { - ipList = append(ipList, ip) - idList = append(idList, ids.NewShortID(id)) - } - return ipList, idList -} - -func (ac *AddrCert) len() int { return len(ac.ipToID) } - -func toIPDesc(addr salticidae.NetAddr) utils.IPDesc { - ip, err := ToIPDesc(addr) - HandshakeNet.log.AssertNoError(err) - return ip -} - -// ToIPDesc converts an address to an IP -func ToIPDesc(addr salticidae.NetAddr) (utils.IPDesc, error) { - ip := salticidae.FromBigEndianU32(addr.GetIP()) - port := salticidae.FromBigEndianU16(addr.GetPort()) - return utils.ToIPDesc(fmt.Sprintf("%d.%d.%d.%d:%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip), port)) -} - -func addrToID(addr salticidae.NetAddr) uint64 { - return uint64(addr.GetIP()) | (uint64(addr.GetPort()) << 32) -} diff --git a/networking/builder.go b/networking/builder.go index cbe451b..7d7f83a 100644 --- a/networking/builder.go +++ b/networking/builder.go @@ -16,10 +16,11 @@ type Builder struct{ Codec } func (m Builder) GetVersion() (Msg, error) { return m.Pack(GetVersion, nil) } // Version message -func (m Builder) Version(networkID uint32, myTime uint64, myVersion string) (Msg, error) { +func (m Builder) Version(networkID uint32, myTime uint64, ip utils.IPDesc, myVersion string) (Msg, error) { return m.Pack(Version, map[Field]interface{}{ NetworkID: networkID, MyTime: myTime, + IP: ip, VersionStr: myVersion, }) } diff --git a/networking/commands.go b/networking/commands.go index ffb5cbb..d2b610a 100644 --- a/networking/commands.go +++ b/networking/commands.go @@ -17,6 +17,7 @@ const ( VersionStr Field = iota // Used in handshake NetworkID // Used in handshake MyTime // Used in handshake + IP // Used in handshake Peers // Used in handshake ChainID // Used for dispatching RequestID // Used for all messages @@ -38,6 +39,8 @@ func (f Field) Packer() func(*wrappers.Packer, interface{}) { return wrappers.TryPackInt case MyTime: return wrappers.TryPackLong + case IP: + return wrappers.TryPackIP case Peers: return wrappers.TryPackIPList case ChainID: // TODO: This will be shortened to use a modified varint spec @@ -72,6 +75,8 @@ func (f Field) Unpacker() func(*wrappers.Packer) interface{} { return wrappers.TryUnpackInt case MyTime: return wrappers.TryUnpackLong + case IP: + return wrappers.TryUnpackIP case Peers: return wrappers.TryUnpackIPList case ChainID: // TODO: This will be shortened to use a modified varint spec @@ -105,6 +110,8 @@ func (f Field) String() string { return "NetworkID" case MyTime: return "MyTime" + case IP: + return "IP" case Peers: return "Peers" case ChainID: @@ -161,7 +168,7 @@ var ( Messages = map[salticidae.Opcode][]Field{ // Handshake: GetVersion: []Field{}, - Version: []Field{NetworkID, MyTime, VersionStr}, + Version: []Field{NetworkID, MyTime, IP, VersionStr}, GetPeerList: []Field{}, PeerList: []Field{Peers}, // Bootstrapping: diff --git a/networking/connections.go b/networking/connections.go new file mode 100644 index 0000000..4eaaaf7 --- /dev/null +++ b/networking/connections.go @@ -0,0 +1,280 @@ +// (c) 2019-2020, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package networking + +import ( + "fmt" + "sync" + + "github.com/ava-labs/salticidae-go" + + "github.com/ava-labs/gecko/ids" + "github.com/ava-labs/gecko/utils" +) + +// Connections provides an interface for what a group of connections will +// support. +type Connections interface { + Add(salticidae.PeerID, ids.ShortID, utils.IPDesc) + + GetPeerID(ids.ShortID) (salticidae.PeerID, bool) + GetID(salticidae.PeerID) (ids.ShortID, bool) + + ContainsPeerID(salticidae.PeerID) bool + ContainsID(ids.ShortID) bool + ContainsIP(utils.IPDesc) bool + + Remove(salticidae.PeerID, ids.ShortID) + RemovePeerID(salticidae.PeerID) + RemoveID(ids.ShortID) + + PeerIDs() []salticidae.PeerID + IDs() ids.ShortSet + IPs() []utils.IPDesc + Conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc) + + Len() int +} + +type connections struct { + mux sync.Mutex + // peerID -> id + peerIDToID map[[32]byte]ids.ShortID + // id -> peerID + idToPeerID map[[20]byte]salticidae.PeerID + // id -> ip + idToIP map[[20]byte]utils.IPDesc +} + +// NewConnections returns a new and empty connections object +func NewConnections() Connections { + return &connections{ + peerIDToID: make(map[[32]byte]ids.ShortID), + idToPeerID: make(map[[20]byte]salticidae.PeerID), + idToIP: make(map[[20]byte]utils.IPDesc), + } +} + +// Add Assumes that peer is garbage collected normally +func (c *connections) Add(peer salticidae.PeerID, id ids.ShortID, ip utils.IPDesc) { + c.mux.Lock() + defer c.mux.Unlock() + + c.add(peer, id, ip) +} + +// GetPeerID returns the peer mapped to the id that is provided if one exists. +func (c *connections) GetPeerID(id ids.ShortID) (salticidae.PeerID, bool) { + c.mux.Lock() + defer c.mux.Unlock() + + return c.getPeerID(id) +} + +// GetID returns the id mapped to the peer that is provided if one exists. +func (c *connections) GetID(peer salticidae.PeerID) (ids.ShortID, bool) { + c.mux.Lock() + defer c.mux.Unlock() + + return c.getID(peer) +} + +// ContainsPeerID returns true if the peer is contained in the connection pool +func (c *connections) ContainsPeerID(peer salticidae.PeerID) bool { + _, exists := c.GetID(peer) + return exists +} + +// ContainsID returns true if the id is contained in the connection pool +func (c *connections) ContainsID(id ids.ShortID) bool { + _, exists := c.GetPeerID(id) + return exists +} + +// ContainsIP returns true if the ip is contained in the connection pool +func (c *connections) ContainsIP(ip utils.IPDesc) bool { + for _, otherIP := range c.IPs() { + if ip.Equal(otherIP) { + return true + } + } + return false +} + +// Remove ensures that no connection will have any mapping containing [peer] or +// [id]. +func (c *connections) Remove(peer salticidae.PeerID, id ids.ShortID) { + c.mux.Lock() + defer c.mux.Unlock() + + c.remove(peer, id) +} + +// RemovePeerID ensures that no connection will have a mapping containing [peer] +func (c *connections) RemovePeerID(peer salticidae.PeerID) { + c.mux.Lock() + defer c.mux.Unlock() + + c.removePeerID(peer) +} + +// RemoveID ensures that no connection will have a mapping containing [id] +func (c *connections) RemoveID(id ids.ShortID) { + c.mux.Lock() + defer c.mux.Unlock() + + c.removeID(id) +} + +// PeerIDs returns the full list of peers contained in this connection pool. +func (c *connections) PeerIDs() []salticidae.PeerID { + c.mux.Lock() + defer c.mux.Unlock() + + return c.peerIDs() +} + +// IDs return the set of IDs that are mapping in this connection pool. +func (c *connections) IDs() ids.ShortSet { + c.mux.Lock() + defer c.mux.Unlock() + + return c.ids() +} + +// IPs return the set of IPs that are mapped in this connection pool. +func (c *connections) IPs() []utils.IPDesc { + c.mux.Lock() + defer c.mux.Unlock() + + return c.ips() +} + +// Conns return the set of connections in this connection pool. +func (c *connections) Conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc) { + c.mux.Lock() + defer c.mux.Unlock() + + return c.conns() +} + +// Len returns the number of elements in the map +func (c *connections) Len() int { + c.mux.Lock() + defer c.mux.Unlock() + + return c.len() +} + +func (c *connections) add(peer salticidae.PeerID, id ids.ShortID, ip utils.IPDesc) { + c.remove(peer, id) + + key := id.Key() + c.peerIDToID[toID(peer)] = id + c.idToPeerID[key] = peer + c.idToIP[key] = ip +} + +func (c *connections) getPeerID(id ids.ShortID) (salticidae.PeerID, bool) { + peer, exists := c.idToPeerID[id.Key()] + return peer, exists +} + +func (c *connections) getID(peer salticidae.PeerID) (ids.ShortID, bool) { + id, exists := c.peerIDToID[toID(peer)] + return id, exists +} + +func (c *connections) remove(peer salticidae.PeerID, id ids.ShortID) { + c.removePeerID(peer) + c.removeID(id) +} + +func (c *connections) removePeerID(peer salticidae.PeerID) { + peerID := toID(peer) + if id, exists := c.peerIDToID[peerID]; exists { + idKey := id.Key() + + delete(c.peerIDToID, peerID) + delete(c.idToPeerID, idKey) + delete(c.idToIP, idKey) + } +} + +func (c *connections) removeID(id ids.ShortID) { + idKey := id.Key() + if peer, exists := c.idToPeerID[idKey]; exists { + delete(c.peerIDToID, toID(peer)) + delete(c.idToPeerID, idKey) + delete(c.idToIP, idKey) + } +} + +func (c *connections) peerIDs() []salticidae.PeerID { + peers := make([]salticidae.PeerID, 0, len(c.idToPeerID)) + for _, peer := range c.idToPeerID { + peers = append(peers, peer) + } + return peers +} + +func (c *connections) ids() ids.ShortSet { + ids := ids.ShortSet{} + for _, id := range c.peerIDToID { + ids.Add(id) + } + return ids +} + +func (c *connections) ips() []utils.IPDesc { + ips := make([]utils.IPDesc, 0, len(c.idToIP)) + for _, ip := range c.idToIP { + ips = append(ips, ip) + } + return ips +} + +func (c *connections) conns() ([]salticidae.PeerID, []ids.ShortID, []utils.IPDesc) { + peers := make([]salticidae.PeerID, 0, len(c.idToPeerID)) + idList := make([]ids.ShortID, 0, len(c.idToPeerID)) + ips := make([]utils.IPDesc, 0, len(c.idToPeerID)) + for id, peer := range c.idToPeerID { + idList = append(idList, ids.NewShortID(id)) + peers = append(peers, peer) + ips = append(ips, c.idToIP[id]) + } + return peers, idList, ips +} + +func (c *connections) len() int { return len(c.idToPeerID) } + +func toID(peer salticidae.PeerID) [32]byte { + ds := salticidae.NewDataStream(false) + + peerInt := peer.AsUInt256() + peerInt.Serialize(ds) + + size := ds.Size() + dsb := ds.GetDataInPlace(size) + idBytes := dsb.Get() + + id := [32]byte{} + copy(id[:], idBytes) + + ds.Free() + return id +} + +func toIPDesc(addr salticidae.NetAddr) utils.IPDesc { + ip, err := ToIPDesc(addr) + HandshakeNet.log.AssertNoError(err) + return ip +} + +// ToIPDesc converts an address to an IP +func ToIPDesc(addr salticidae.NetAddr) (utils.IPDesc, error) { + ip := salticidae.FromBigEndianU32(addr.GetIP()) + port := salticidae.FromBigEndianU16(addr.GetPort()) + return utils.ToIPDesc(fmt.Sprintf("%d.%d.%d.%d:%d", byte(ip>>24), byte(ip>>16), byte(ip>>8), byte(ip), port)) +} diff --git a/networking/handshake_handlers.go b/networking/handshake_handlers.go index de6bb9c..9e0c9cf 100644 --- a/networking/handshake_handlers.go +++ b/networking/handshake_handlers.go @@ -4,7 +4,7 @@ package networking // #include "salticidae/network.h" -// bool checkPeerCertificate(msgnetwork_conn_t *, bool, void *); +// bool connHandler(msgnetwork_conn_t *, bool, void *); // void unknownPeerHandler(netaddr_t *, x509_t *, void *); // void peerHandler(peernetwork_conn_t *, bool, void *); // void ping(msg_t *, msgnetwork_conn_t *, void *); @@ -19,6 +19,8 @@ import ( "errors" "fmt" "math" + "strconv" + "strings" "sync" "time" "unsafe" @@ -61,9 +63,23 @@ Attempt reconnections node isn't connected to after awhile delete the connection. */ +// Version this avalanche instance is executing. +var ( + VersionPrefix = "avalanche/" + VersionSeparator = "." + MajorVersion = 0 + MinorVersion = 1 + PatchVersion = 0 + ClientVersion = fmt.Sprintf("%s%d%s%d%s%d", + VersionPrefix, + MajorVersion, + VersionSeparator, + MinorVersion, + VersionSeparator, + PatchVersion) +) + const ( - // CurrentVersion this avalanche instance is executing. - CurrentVersion = "avalanche/0.0.1" // MaxClockDifference allowed between connected nodes. MaxClockDifference = time.Minute // PeerListGossipSpacing is the amount of time to wait between pushing this @@ -74,6 +90,10 @@ const ( // PeerListStakerGossipFraction calculates the fraction of stakers that are // gossiped to. If set to 1, then only stakers will be gossiped to. PeerListStakerGossipFraction = 2 + + // ConnectTimeout is the amount of time to wait before attempt to connect to + // an unknown peer + ConnectTimeout = 6 * time.Second // GetVersionTimeout is the amount of time to wait before sending a // getVersion message to a partially connected peer GetVersionTimeout = 2 * time.Second @@ -96,23 +116,35 @@ var ( type Handshake struct { handshakeMetrics - networkID uint32 + networkID uint32 // ID of the network I'm running, used to prevent connecting to the wrong network log logging.Logger - vdrs validators.Set - myAddr salticidae.NetAddr - myID ids.ShortID - net salticidae.PeerNetwork - enableStaking bool // Should only be false for local tests + vdrs validators.Set // set of current validators in the AVAnet + myAddr salticidae.NetAddr // IP I communicate to peers + myID ids.ShortID // ID that identifies myself as a staker or not + net salticidae.PeerNetwork // C messaging network + enableStaking bool // Should only be false for local tests - clock timer.Clock - pending AddrCert // Connections that I haven't gotten version messages from - connections AddrCert // Connections that I think are connected + clock timer.Clock - versionTimeout timer.TimeoutManager - reconnectTimeout timer.TimeoutManager + // Connections that I have added by IP, but haven't gotten an ID from + requestedLock sync.Mutex + requested map[string]struct{} + requestedTimeout timer.TimeoutManager // keys are hashes of the ip:port string + + // Connections that I have added as a peer, but haven't gotten a version + // message from + pending Connections + versionTimeout timer.TimeoutManager // keys are the peer IDs + + // Connections that I have gotten a valid version message from + connections Connections + reconnectTimeout timer.TimeoutManager // keys are the peer IDs + + // IPs of nodes I'm connected to will be repeatedly gossiped throughout the network peerListGossiper *timer.Repeater + // If any chain is blocked on connecting to peers, track these blockers here awaitingLock sync.Mutex awaiting []*networking.AwaitingConnections } @@ -130,17 +162,37 @@ func (nm *Handshake) Initialize( networkID uint32, ) { log.AssertTrue(nm.net == nil, "Should only register network handlers once") + + nm.handshakeMetrics.Initialize(log, registerer) + + nm.networkID = networkID + nm.log = log nm.vdrs = vdrs nm.myAddr = myAddr nm.myID = myID nm.net = peerNet nm.enableStaking = enableStaking - nm.networkID = networkID + nm.requested = make(map[string]struct{}) + nm.requestedTimeout.Initialize(ConnectTimeout) + go nm.log.RecoverAndPanic(nm.requestedTimeout.Dispatch) + + nm.pending = NewConnections() + nm.versionTimeout.Initialize(GetVersionTimeout) + go nm.log.RecoverAndPanic(nm.versionTimeout.Dispatch) + + nm.connections = NewConnections() + nm.reconnectTimeout.Initialize(ReconnectTimeout) + go nm.log.RecoverAndPanic(nm.reconnectTimeout.Dispatch) + + nm.peerListGossiper = timer.NewRepeater(nm.gossipPeerList, PeerListGossipSpacing) + go nm.log.RecoverAndPanic(nm.peerListGossiper.Dispatch) + + // register c message callbacks net := peerNet.AsMsgNetwork() - net.RegConnHandler(salticidae.MsgNetworkConnCallback(C.checkPeerCertificate), nil) + net.RegConnHandler(salticidae.MsgNetworkConnCallback(C.connHandler), nil) peerNet.RegPeerHandler(salticidae.PeerNetworkPeerCallback(C.peerHandler), nil) peerNet.RegUnknownPeerHandler(salticidae.PeerNetworkUnknownPeerCallback(C.unknownPeerHandler), nil) net.RegHandler(Ping, salticidae.MsgNetworkMsgCallback(C.ping), nil) @@ -149,17 +201,95 @@ func (nm *Handshake) Initialize( net.RegHandler(Version, salticidae.MsgNetworkMsgCallback(C.version), nil) net.RegHandler(GetPeerList, salticidae.MsgNetworkMsgCallback(C.getPeerList), nil) net.RegHandler(PeerList, salticidae.MsgNetworkMsgCallback(C.peerList), nil) +} - nm.handshakeMetrics.Initialize(nm.log, registerer) +// ConnectTo add the peer as a connection and connects to them. +func (nm *Handshake) ConnectTo(peer salticidae.PeerID, stakerID ids.ShortID, addr salticidae.NetAddr) { + if nm.pending.ContainsPeerID(peer) || nm.connections.ContainsPeerID(peer) { + return + } - nm.versionTimeout.Initialize(GetVersionTimeout) - go nm.log.RecoverAndPanic(nm.versionTimeout.Dispatch) + nm.log.Info("Attempting to connect to %s", stakerID) - nm.reconnectTimeout.Initialize(ReconnectTimeout) - go nm.log.RecoverAndPanic(nm.reconnectTimeout.Dispatch) + nm.net.AddPeer(peer) + nm.net.SetPeerAddr(peer, addr) + nm.net.ConnPeer(peer, 600, 1) - nm.peerListGossiper = timer.NewRepeater(nm.gossipPeerList, PeerListGossipSpacing) - go nm.log.RecoverAndPanic(nm.peerListGossiper.Dispatch) + ip := toIPDesc(addr) + nm.pending.Add(peer, stakerID, ip) + + peerBytes := toID(peer) + peerID := ids.NewID(peerBytes) + + nm.reconnectTimeout.Put(peerID, func() { + nm.pending.Remove(peer, stakerID) + nm.connections.Remove(peer, stakerID) + nm.net.DelPeer(peer) + + nm.numPeers.Set(float64(nm.connections.Len())) + }) +} + +// Connect ... +func (nm *Handshake) Connect(addr salticidae.NetAddr) { + ip := toIPDesc(addr) + ipStr := ip.String() + if nm.pending.ContainsIP(ip) || nm.connections.ContainsIP(ip) { + return + } + + if !nm.enableStaking { + nm.log.Info("Adding peer %s", ip) + + peer := salticidae.NewPeerIDFromNetAddr(addr, true) + nm.ConnectTo(peer, toShortID(ip), addr) + return + } + + nm.requestedLock.Lock() + _, exists := nm.requested[ipStr] + nm.requestedLock.Unlock() + + if exists { + return + } + + nm.log.Info("Adding peer %s", ip) + + count := new(int) + *count = 100 + handler := new(func()) + *handler = func() { + nm.requestedLock.Lock() + defer nm.requestedLock.Unlock() + + if *count == 100 { + nm.requested[ipStr] = struct{}{} + } + + if _, exists := nm.requested[ipStr]; !exists { + return + } + + if *count <= 0 { + delete(nm.requested, ipStr) + return + } + *count-- + + if nm.pending.ContainsIP(ip) || nm.connections.ContainsIP(ip) { + return + } + + nm.log.Info("Attempting to discover peer at %s", ipStr) + + msgNet := nm.net.AsMsgNetwork() + msgNet.Connect(addr) + + ipID := ids.NewID(hashing.ComputeHash256Array([]byte(ipStr))) + nm.requestedTimeout.Put(ipID, *handler) + } + (*handler)() } // AwaitConnections ... @@ -209,19 +339,19 @@ func (nm *Handshake) gossipPeerList() { idsToSend = append(idsToSend, nonStakers[sampler.Sample()]) } - ips := []salticidae.NetAddr{} + peers := []salticidae.PeerID{} for _, id := range idsToSend { - if ip, exists := nm.connections.GetIP(id); exists { - ips = append(ips, ip) + if peer, exists := nm.connections.GetPeerID(id); exists { + peers = append(peers, peer) } } - nm.SendPeerList(ips...) + nm.SendPeerList(peers...) } // Connections returns the object that tracks the nodes that are currently // connected to this node. -func (nm *Handshake) Connections() Connections { return &nm.connections } +func (nm *Handshake) Connections() Connections { return nm.connections } // Shutdown the network func (nm *Handshake) Shutdown() { @@ -230,59 +360,60 @@ func (nm *Handshake) Shutdown() { } // SendGetVersion to the requested peer -func (nm *Handshake) SendGetVersion(addr salticidae.NetAddr) { +func (nm *Handshake) SendGetVersion(peer salticidae.PeerID) { build := Builder{} gv, err := build.GetVersion() nm.log.AssertNoError(err) - nm.send(gv, addr) + nm.send(gv, peer) nm.numGetVersionSent.Inc() } // SendVersion to the requested peer -func (nm *Handshake) SendVersion(addr salticidae.NetAddr) error { +func (nm *Handshake) SendVersion(peer salticidae.PeerID) error { build := Builder{} - v, err := build.Version(nm.networkID, nm.clock.Unix(), CurrentVersion) + v, err := build.Version(nm.networkID, nm.clock.Unix(), toIPDesc(nm.myAddr), ClientVersion) if err != nil { return fmt.Errorf("packing Version failed due to %s", err) } - nm.send(v, addr) + nm.send(v, peer) nm.numVersionSent.Inc() return nil } // SendPeerList to the requested peer -func (nm *Handshake) SendPeerList(addrs ...salticidae.NetAddr) error { - if len(addrs) == 0 { +func (nm *Handshake) SendPeerList(peers ...salticidae.PeerID) error { + if len(peers) == 0 { return nil } - ips, ids := nm.connections.Conns() + _, ids, ips := nm.connections.Conns() ipsToSend := []utils.IPDesc(nil) for i, id := range ids { - if nm.vdrs.Contains(id) { - ipsToSend = append(ipsToSend, ips[i]) + ip := ips[i] + if !ip.IsZero() && nm.vdrs.Contains(id) { + ipsToSend = append(ipsToSend, ip) } } if len(ipsToSend) == 0 { - nm.log.Debug("No IPs to send to %d peer(s)", len(addrs)) + nm.log.Debug("No IPs to send to %d peer(s)", len(peers)) return nil } - nm.log.Verbo("Sending %d ips to %d peer(s)", len(ipsToSend), len(addrs)) + nm.log.Verbo("Sending %d ips to %d peer(s)", len(ipsToSend), len(peers)) build := Builder{} pl, err := build.PeerList(ipsToSend) if err != nil { return fmt.Errorf("Packing Peerlist failed due to %w", err) } - nm.send(pl, addrs...) - nm.numPeerlistSent.Add(float64(len(addrs))) + nm.send(pl, peers...) + nm.numPeerlistSent.Add(float64(len(peers))) return nil } -func (nm *Handshake) send(msg Msg, addrs ...salticidae.NetAddr) { +func (nm *Handshake) send(msg Msg, peers ...salticidae.PeerID) { ds := msg.DataStream() defer ds.Free() ba := salticidae.NewByteArrayMovedFromDataStream(ds, false) @@ -290,81 +421,112 @@ func (nm *Handshake) send(msg Msg, addrs ...salticidae.NetAddr) { cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false) defer cMsg.Free() - switch len(addrs) { + switch len(peers) { case 0: case 1: - nm.net.SendMsg(cMsg, addrs[0]) + nm.net.SendMsg(cMsg, peers[0]) default: - nm.net.MulticastMsgByMove(cMsg, addrs) + nm.net.MulticastMsgByMove(cMsg, peers) } } -// checkPeerCertificate of a new inbound connection -//export checkPeerCertificate -func checkPeerCertificate(_ *C.struct_msgnetwork_conn_t, connected C.bool, _ unsafe.Pointer) C.bool { - return connected +// connHandler notifies of a new inbound connection +//export connHandler +func connHandler(_conn *C.struct_msgnetwork_conn_t, connected C.bool, _ unsafe.Pointer) C.bool { + if !HandshakeNet.enableStaking || !bool(connected) { + return connected + } + + HandshakeNet.requestedLock.Lock() + defer HandshakeNet.requestedLock.Unlock() + + conn := salticidae.MsgNetworkConnFromC(salticidae.CMsgNetworkConn(_conn)) + addr := conn.GetAddr() + ip := toIPDesc(addr) + ipStr := ip.String() + + ipID := ids.NewID(hashing.ComputeHash256Array([]byte(ipStr))) + HandshakeNet.requestedTimeout.Remove(ipID) + + if _, exists := HandshakeNet.requested[ipStr]; !exists { + HandshakeNet.log.Debug("connHandler called with %s", ip) + return true + } + delete(HandshakeNet.requested, ipStr) + + cert := conn.GetPeerCert() + peer := salticidae.NewPeerIDFromX509(cert, true) + + HandshakeNet.ConnectTo(peer, getCert(cert), addr) + return true } -func (nm *Handshake) connectedToPeer(conn *C.struct_peernetwork_conn_t, addr salticidae.NetAddr) { - ip := toIPDesc(addr) +func (nm *Handshake) connectedToPeer(conn *C.struct_peernetwork_conn_t, peer salticidae.PeerID) { + peerBytes := toID(peer) + peerID := ids.NewID(peerBytes) + // If we're enforcing staking, use a peer's certificate to uniquely identify them // Otherwise, use a hash of their ip to identify them cert := ids.ShortID{} - ipCert := toShortID(ip) if nm.enableStaking { cert = getPeerCert(conn) } else { - cert = ipCert + key := [20]byte{} + copy(key[:], peerID.Bytes()) + cert = ids.NewShortID(key) } - nm.log.Debug("Connected to %s", ip) + nm.log.Debug("Connected to %s", cert) - longCert := cert.LongID() - nm.reconnectTimeout.Remove(longCert) - nm.reconnectTimeout.Remove(ipCert.LongID()) - - nm.pending.Add(addr, cert) + nm.reconnectTimeout.Remove(peerID) handler := new(func()) *handler = func() { - if nm.pending.ContainsIP(addr) { - nm.SendGetVersion(addr) - nm.versionTimeout.Put(longCert, *handler) + if nm.pending.ContainsPeerID(peer) { + nm.SendGetVersion(peer) + nm.versionTimeout.Put(peerID, *handler) } } (*handler)() } -func (nm *Handshake) disconnectedFromPeer(addr salticidae.NetAddr) { +func (nm *Handshake) disconnectedFromPeer(peer salticidae.PeerID) { cert := ids.ShortID{} - if pendingCert, exists := nm.pending.GetID(addr); exists { + if pendingCert, exists := nm.pending.GetID(peer); exists { cert = pendingCert - } else if connectedCert, exists := nm.connections.GetID(addr); exists { + nm.log.Info("Disconnected from pending peer %s", cert) + } else if connectedCert, exists := nm.connections.GetID(peer); exists { cert = connectedCert + nm.log.Info("Disconnected from peer %s", cert) } else { return } - nm.log.Info("Disconnected from %s", toIPDesc(addr)) + peerBytes := toID(peer) + peerID := ids.NewID(peerBytes) + + nm.versionTimeout.Remove(peerID) + nm.connections.Remove(peer, cert) + nm.numPeers.Set(float64(nm.connections.Len())) - longCert := cert.LongID() if nm.vdrs.Contains(cert) { - nm.reconnectTimeout.Put(longCert, func() { - nm.net.DelPeer(addr) + nm.reconnectTimeout.Put(peerID, func() { + nm.pending.Remove(peer, cert) + nm.connections.Remove(peer, cert) + nm.net.DelPeer(peer) + + nm.numPeers.Set(float64(nm.connections.Len())) }) + nm.pending.Add(peer, cert, utils.IPDesc{}) } else { - nm.net.DelPeer(addr) + nm.pending.Remove(peer, cert) + nm.net.DelPeer(peer) } - nm.versionTimeout.Remove(longCert) if !nm.enableStaking { nm.vdrs.Remove(cert) } - nm.pending.RemoveIP(addr) - nm.connections.RemoveIP(addr) - nm.numPeers.Set(float64(nm.connections.Len())) - nm.awaitingLock.Lock() defer nm.awaitingLock.Unlock() for _, awaiting := range HandshakeNet.awaiting { @@ -372,57 +534,123 @@ func (nm *Handshake) disconnectedFromPeer(addr salticidae.NetAddr) { } } +// checkCompatibility Check to make sure that the peer and I speak the same language. +func (nm *Handshake) checkCompatibility(peerVersion string) bool { + if !strings.HasPrefix(peerVersion, VersionPrefix) { + nm.log.Warn("Peer attempted to connect with an invalid version prefix") + return false + } + peerVersion = peerVersion[len(VersionPrefix):] + splitPeerVersion := strings.SplitN(peerVersion, VersionSeparator, 3) + if len(splitPeerVersion) != 3 { + nm.log.Warn("Peer attempted to connect with an invalid number of subversions") + return false + } + + major, err := strconv.Atoi(splitPeerVersion[0]) + if err != nil { + nm.log.Warn("Peer attempted to connect with an invalid major version") + return false + } + minor, err := strconv.Atoi(splitPeerVersion[1]) + if err != nil { + nm.log.Warn("Peer attempted to connect with an invalid minor version") + return false + } + patch, err := strconv.Atoi(splitPeerVersion[2]) + if err != nil { + nm.log.Warn("Peer attempted to connect with an invalid patch version") + return false + } + + switch { + case major < MajorVersion: + // peers major version is too low + return false + case major > MajorVersion: + nm.log.Warn("Peer attempted to connect with a higher major version, this client may need to be updated") + return false + } + + switch { + case minor < MinorVersion: + // peers minor version is too low + return false + case minor > MinorVersion: + nm.log.Warn("Peer attempted to connect with a higher minor version, this client may need to be updated") + return false + } + + if patch > PatchVersion { + nm.log.Warn("Peer is connecting with a higher patch version, this client may need to be updated") + } + return true +} + // peerHandler notifies a change to the set of connected peers // connected is true if a new peer is connected // connected is false if a formerly connected peer has disconnected //export peerHandler func peerHandler(_conn *C.struct_peernetwork_conn_t, connected C.bool, _ unsafe.Pointer) { + HandshakeNet.log.Debug("peerHandler called") + pConn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn)) - addr := pConn.GetPeerAddr(true) + peer := pConn.GetPeerID(true) if connected { - HandshakeNet.connectedToPeer(_conn, addr) + HandshakeNet.connectedToPeer(_conn, peer) } else { - HandshakeNet.disconnectedFromPeer(addr) + HandshakeNet.disconnectedFromPeer(peer) } } // unknownPeerHandler notifies of an unknown peer connection attempt //export unknownPeerHandler func unknownPeerHandler(_addr *C.netaddr_t, _cert *C.x509_t, _ unsafe.Pointer) { + HandshakeNet.log.Debug("unknownPeerHandler called") + addr := salticidae.NetAddrFromC(salticidae.CNetAddr(_addr)).Copy(true) ip := toIPDesc(addr) + HandshakeNet.log.Info("Adding peer %s", ip) - cert := ids.ShortID{} + var peer salticidae.PeerID + var id ids.ShortID if HandshakeNet.enableStaking { - cert = getCert(salticidae.X509FromC(salticidae.CX509(_cert))) + cert := salticidae.X509FromC(salticidae.CX509(_cert)) + peer = salticidae.NewPeerIDFromX509(cert, true) + id = getCert(cert) } else { - cert = toShortID(ip) + peer = salticidae.NewPeerIDFromNetAddr(addr, true) + id = toShortID(ip) } - HandshakeNet.reconnectTimeout.Put(cert.LongID(), func() { - HandshakeNet.net.DelPeer(addr) + peerBytes := toID(peer) + peerID := ids.NewID(peerBytes) + + HandshakeNet.reconnectTimeout.Put(peerID, func() { + HandshakeNet.pending.Remove(peer, id) + HandshakeNet.connections.Remove(peer, id) + HandshakeNet.net.DelPeer(peer) + + HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len())) }) - HandshakeNet.net.AddPeer(addr) + HandshakeNet.pending.Add(peer, id, utils.IPDesc{}) + HandshakeNet.net.AddPeer(peer) } // ping handles the recept of a ping message //export ping func ping(_ *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.Pointer) { conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn)) - addr := conn.GetPeerAddr(false) - defer addr.Free() - if addr.IsNull() { - HandshakeNet.log.Warn("Ping sent from unknown peer") - return - } + peer := conn.GetPeerID(false) + defer peer.Free() build := Builder{} pong, err := build.Pong() HandshakeNet.log.AssertNoError(err) - HandshakeNet.send(pong, addr) + HandshakeNet.send(pong, peer) } // pong handles the recept of a pong message @@ -435,15 +663,10 @@ func getVersion(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsaf HandshakeNet.numGetVersionReceived.Inc() conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn)) - addr := conn.GetPeerAddr(false) - defer addr.Free() + peer := conn.GetPeerID(false) + defer peer.Free() - if addr.IsNull() { - HandshakeNet.log.Warn("GetVersion sent from unknown peer") - return - } - - HandshakeNet.SendVersion(addr) + HandshakeNet.SendVersion(peer) } // version handles the recept of a version message @@ -453,35 +676,33 @@ func version(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.P msg := salticidae.MsgFromC(salticidae.CMsg(_msg)) conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn)) - addr := conn.GetPeerAddr(true) - if addr.IsNull() { - HandshakeNet.log.Warn("Version sent from unknown peer") + peer := conn.GetPeerID(true) + + peerBytes := toID(peer) + peerID := ids.NewID(peerBytes) + + HandshakeNet.versionTimeout.Remove(peerID) + + id, exists := HandshakeNet.pending.GetID(peer) + if !exists { + HandshakeNet.log.Warn("Dropping Version message because the peer isn't pending") return } - - cert := ids.ShortID{} - if HandshakeNet.enableStaking { - cert = getMsgCert(_conn) - } else { - ip := toIPDesc(addr) - cert = toShortID(ip) - } - - defer HandshakeNet.pending.Remove(addr, cert) + HandshakeNet.pending.Remove(peer, id) build := Builder{} pMsg, err := build.Parse(Version, msg.GetPayloadByMove()) if err != nil { HandshakeNet.log.Warn("Failed to parse Version message") - HandshakeNet.net.DelPeer(addr) + HandshakeNet.net.DelPeer(peer) return } if networkID := pMsg.Get(NetworkID).(uint32); networkID != HandshakeNet.networkID { HandshakeNet.log.Warn("Peer's network ID doesn't match our networkID: Peer's = %d ; Ours = %d", networkID, HandshakeNet.networkID) - HandshakeNet.net.DelPeer(addr) + HandshakeNet.net.DelPeer(peer) return } @@ -489,36 +710,35 @@ func version(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.P if peerTime := float64(pMsg.Get(MyTime).(uint64)); math.Abs(peerTime-myTime) > MaxClockDifference.Seconds() { HandshakeNet.log.Warn("Peer's clock is too far out of sync with mine. His = %d, Mine = %d (seconds)", uint64(peerTime), uint64(myTime)) - HandshakeNet.net.DelPeer(addr) + HandshakeNet.net.DelPeer(peer) return } - if peerVersion := pMsg.Get(VersionStr).(string); !checkCompatibility(CurrentVersion, peerVersion) { - HandshakeNet.log.Warn("Bad version") + if peerVersion := pMsg.Get(VersionStr).(string); !HandshakeNet.checkCompatibility(peerVersion) { + HandshakeNet.log.Debug("Dropping connection due to an incompatible version from peer") - HandshakeNet.net.DelPeer(addr) + HandshakeNet.net.DelPeer(peer) return } - HandshakeNet.log.Debug("Finishing handshake with %s", toIPDesc(addr)) + ip := pMsg.Get(IP).(utils.IPDesc) - HandshakeNet.SendPeerList(addr) - HandshakeNet.connections.Add(addr, cert) + HandshakeNet.log.Debug("Finishing handshake with %s", ip) - HandshakeNet.versionTimeout.Remove(cert.LongID()) + HandshakeNet.SendPeerList(peer) + HandshakeNet.connections.Add(peer, id, ip) + HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len())) if !HandshakeNet.enableStaking { - HandshakeNet.vdrs.Add(validators.NewValidator(cert, 1)) + HandshakeNet.vdrs.Add(validators.NewValidator(id, 1)) } - HandshakeNet.numPeers.Set(float64(HandshakeNet.connections.Len())) - HandshakeNet.awaitingLock.Lock() defer HandshakeNet.awaitingLock.Unlock() for i := 0; i < len(HandshakeNet.awaiting); i++ { awaiting := HandshakeNet.awaiting[i] - awaiting.Add(cert) + awaiting.Add(id) if !awaiting.Ready() { continue } @@ -539,13 +759,10 @@ func getPeerList(_ *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe. HandshakeNet.numGetPeerlistReceived.Inc() conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn(_conn)) - addr := conn.GetPeerAddr(false) - defer addr.Free() - if addr.IsNull() { - HandshakeNet.log.Warn("GetPeerList sent from unknown peer") - return - } - HandshakeNet.SendPeerList(addr) + peer := conn.GetPeerID(false) + defer peer.Free() + + HandshakeNet.SendPeerList(peer) } // peerList handles the recept of a peerList message @@ -565,27 +782,15 @@ func peerList(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe. ips := pMsg.Get(Peers).([]utils.IPDesc) cErr := salticidae.NewError() for _, ip := range ips { - HandshakeNet.log.Verbo("Trying to adding peer %s", ip) addr := salticidae.NewNetAddrFromIPPortString(ip.String(), true, &cErr) - if cErr.GetCode() == 0 && !HandshakeNet.myAddr.IsEq(addr) { // Make sure not to connect to myself - ip := toIPDesc(addr) - ipCert := toShortID(ip) - if !HandshakeNet.pending.ContainsIP(addr) && !HandshakeNet.connections.ContainsIP(addr) { - HandshakeNet.log.Debug("Adding peer %s", ip) - - HandshakeNet.reconnectTimeout.Put(ipCert.LongID(), func() { - HandshakeNet.net.DelPeer(addr) - }) - HandshakeNet.net.AddPeer(addr) - } + if cErr.GetCode() != 0 || HandshakeNet.myAddr.IsEq(addr) { + // Make sure not to connect to myself + continue } - } -} -func getMsgCert(_conn *C.struct_msgnetwork_conn_t) ids.ShortID { - conn := salticidae.MsgNetworkConnFromC(salticidae.CMsgNetworkConn(_conn)) - return getCert(conn.GetPeerCert()) + HandshakeNet.Connect(addr) + } } func getPeerCert(_conn *C.struct_peernetwork_conn_t) ids.ShortID { @@ -595,29 +800,16 @@ func getPeerCert(_conn *C.struct_peernetwork_conn_t) ids.ShortID { func getCert(cert salticidae.X509) ids.ShortID { der := cert.GetDer(false) - defer der.Free() - certDS := salticidae.NewDataStreamMovedFromByteArray(der, false) - defer certDS.Free() - certBytes := certDS.GetDataInPlace(certDS.Size()).Get() certID, err := ids.ToShortID(hashing.PubkeyBytesToAddress(certBytes)) + + certDS.Free() + der.Free() HandshakeNet.log.AssertNoError(err) return certID } -// checkCompatibility Check to make sure that the peer and I speak the same language. -func checkCompatibility(myVersion string, peerVersion string) bool { - // At the moment, we are all compatible. - return true -} - -func toAddr(ip utils.IPDesc, autoFree bool) salticidae.NetAddr { - err := salticidae.NewError() - addr := salticidae.NewNetAddrFromIPPortString(ip.String(), autoFree, &err) - HandshakeNet.log.AssertTrue(err.GetCode() == 0, "IP Failed parsing") - return addr -} func toShortID(ip utils.IPDesc) ids.ShortID { return ids.NewShortID(hashing.ComputeHash160Array([]byte(ip.String()))) } diff --git a/networking/voting_handlers.go b/networking/voting_handlers.go index 4c7be62..a5746d4 100644 --- a/networking/voting_handlers.go +++ b/networking/voting_handlers.go @@ -89,12 +89,12 @@ func (s *Voting) Shutdown() { s.executor.Stop() } // Accept is called after every consensus decision func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error { - addrs := []salticidae.NetAddr(nil) + peers := []salticidae.PeerID(nil) - allAddrs, allIDs := s.conns.RawConns() + allPeers, allIDs, _ := s.conns.Conns() for i, id := range allIDs { if !s.vdrs.Contains(id) { - addrs = append(addrs, allAddrs[i]) + peers = append(peers, allPeers[i]) } } @@ -109,25 +109,25 @@ func (s *Voting) Accept(chainID, containerID ids.ID, container []byte) error { "\nChain: %s"+ "\nContainer ID: %s"+ "\nContainer:\n%s", - len(addrs), + len(peers), chainID, containerID, formatting.DumpBytes{Bytes: container}, ) - s.send(msg, addrs...) - s.numPutSent.Add(float64(len(addrs))) + s.send(msg, peers...) + s.numPutSent.Add(float64(len(peers))) return nil } // GetAcceptedFrontier implements the Sender interface. func (s *Voting) GetAcceptedFrontier(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32) { - addrs := []salticidae.NetAddr(nil) + peers := []salticidae.PeerID(nil) validatorIDList := validatorIDs.List() for _, validatorID := range validatorIDList { vID := validatorID - if addr, exists := s.conns.GetIP(vID); exists { - addrs = append(addrs, addr) - s.log.Verbo("Sending a GetAcceptedFrontier to %s", toIPDesc(addr)) + if peer, exists := s.conns.GetPeerID(vID); exists { + peers = append(peers, peer) + s.log.Verbo("Sending a GetAcceptedFrontier to %s", vID) } else { s.log.Debug("Attempted to send a GetAcceptedFrontier message to a disconnected validator: %s", vID) s.executor.Add(func() { s.router.GetAcceptedFrontierFailed(vID, chainID, requestID) }) @@ -142,17 +142,17 @@ func (s *Voting) GetAcceptedFrontier(validatorIDs ids.ShortSet, chainID ids.ID, "\nNumber of Validators: %d"+ "\nChain: %s"+ "\nRequest ID: %d", - len(addrs), + len(peers), chainID, requestID, ) - s.send(msg, addrs...) - s.numGetAcceptedFrontierSent.Add(float64(len(addrs))) + s.send(msg, peers...) + s.numGetAcceptedFrontierSent.Add(float64(len(peers))) } // AcceptedFrontier implements the Sender interface. func (s *Voting) AcceptedFrontier(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) { - addr, exists := s.conns.GetIP(validatorID) + peer, exists := s.conns.GetPeerID(validatorID) if !exists { s.log.Debug("Attempted to send an AcceptedFrontier message to a disconnected validator: %s", validatorID) return // Validator is not connected @@ -167,29 +167,27 @@ func (s *Voting) AcceptedFrontier(validatorID ids.ShortID, chainID ids.ID, reque s.log.Verbo("Sending an AcceptedFrontier message."+ "\nValidator: %s"+ - "\nDestination: %s"+ "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer IDs: %s", validatorID, - toIPDesc(addr), chainID, requestID, containerIDs, ) - s.send(msg, addr) + s.send(msg, peer) s.numAcceptedFrontierSent.Inc() } // GetAccepted implements the Sender interface. func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerIDs ids.Set) { - addrs := []salticidae.NetAddr(nil) + peers := []salticidae.PeerID(nil) validatorIDList := validatorIDs.List() for _, validatorID := range validatorIDList { vID := validatorID - if addr, exists := s.conns.GetIP(validatorID); exists { - addrs = append(addrs, addr) - s.log.Verbo("Sending a GetAccepted to %s", toIPDesc(addr)) + if peer, exists := s.conns.GetPeerID(validatorID); exists { + peers = append(peers, peer) + s.log.Verbo("Sending a GetAccepted to %s", vID) } else { s.log.Debug("Attempted to send a GetAccepted message to a disconnected validator: %s", vID) s.executor.Add(func() { s.router.GetAcceptedFailed(vID, chainID, requestID) }) @@ -199,8 +197,8 @@ func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestI build := Builder{} msg, err := build.GetAccepted(chainID, requestID, containerIDs) if err != nil { - for _, addr := range addrs { - if validatorID, exists := s.conns.GetID(addr); exists { + for _, peer := range peers { + if validatorID, exists := s.conns.GetID(peer); exists { s.executor.Add(func() { s.router.GetAcceptedFailed(validatorID, chainID, requestID) }) } } @@ -213,18 +211,18 @@ func (s *Voting) GetAccepted(validatorIDs ids.ShortSet, chainID ids.ID, requestI "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer IDs:%s", - len(addrs), + len(peers), chainID, requestID, containerIDs, ) - s.send(msg, addrs...) - s.numGetAcceptedSent.Add(float64(len(addrs))) + s.send(msg, peers...) + s.numGetAcceptedSent.Add(float64(len(peers))) } // Accepted implements the Sender interface. func (s *Voting) Accepted(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerIDs ids.Set) { - addr, exists := s.conns.GetIP(validatorID) + peer, exists := s.conns.GetPeerID(validatorID) if !exists { s.log.Debug("Attempted to send an Accepted message to a disconnected validator: %s", validatorID) return // Validator is not connected @@ -239,23 +237,21 @@ func (s *Voting) Accepted(validatorID ids.ShortID, chainID ids.ID, requestID uin s.log.Verbo("Sending an Accepted message."+ "\nValidator: %s"+ - "\nDestination: %s"+ "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer IDs: %s", validatorID, - toIPDesc(addr), chainID, requestID, containerIDs, ) - s.send(msg, addr) + s.send(msg, peer) s.numAcceptedSent.Inc() } // Get implements the Sender interface. func (s *Voting) Get(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID) { - addr, exists := s.conns.GetIP(validatorID) + peer, exists := s.conns.GetPeerID(validatorID) if !exists { s.log.Debug("Attempted to send a Get message to a disconnected validator: %s", validatorID) s.executor.Add(func() { s.router.GetFailed(validatorID, chainID, requestID, containerID) }) @@ -268,23 +264,21 @@ func (s *Voting) Get(validatorID ids.ShortID, chainID ids.ID, requestID uint32, s.log.Verbo("Sending a Get message."+ "\nValidator: %s"+ - "\nDestination: %s"+ "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer ID: %s", validatorID, - toIPDesc(addr), chainID, requestID, containerID, ) - s.send(msg, addr) + s.send(msg, peer) s.numGetSent.Inc() } // Put implements the Sender interface. func (s *Voting) Put(validatorID ids.ShortID, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) { - addr, exists := s.conns.GetIP(validatorID) + peer, exists := s.conns.GetPeerID(validatorID) if !exists { s.log.Debug("Attempted to send a Container message to a disconnected validator: %s", validatorID) return // Validator is not connected @@ -299,31 +293,29 @@ func (s *Voting) Put(validatorID ids.ShortID, chainID ids.ID, requestID uint32, s.log.Verbo("Sending a Container message."+ "\nValidator: %s"+ - "\nDestination: %s"+ "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer ID: %s"+ "\nContainer:\n%s", validatorID, - toIPDesc(addr), chainID, requestID, containerID, formatting.DumpBytes{Bytes: container}, ) - s.send(msg, addr) + s.send(msg, peer) s.numPutSent.Inc() } // PushQuery implements the Sender interface. func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID, container []byte) { - addrs := []salticidae.NetAddr(nil) + peers := []salticidae.PeerID(nil) validatorIDList := validatorIDs.List() for _, validatorID := range validatorIDList { vID := validatorID - if addr, exists := s.conns.GetIP(vID); exists { - addrs = append(addrs, addr) - s.log.Verbo("Sending a PushQuery to %s", toIPDesc(addr)) + if peer, exists := s.conns.GetPeerID(vID); exists { + peers = append(peers, peer) + s.log.Verbo("Sending a PushQuery to %s", vID) } else { s.log.Debug("Attempted to send a PushQuery message to a disconnected validator: %s", vID) s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) }) @@ -333,8 +325,8 @@ func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID build := Builder{} msg, err := build.PushQuery(chainID, requestID, containerID, container) if err != nil { - for _, addr := range addrs { - if validatorID, exists := s.conns.GetID(addr); exists { + for _, peer := range peers { + if validatorID, exists := s.conns.GetID(peer); exists { s.executor.Add(func() { s.router.QueryFailed(validatorID, chainID, requestID) }) } } @@ -348,25 +340,25 @@ func (s *Voting) PushQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID "\nRequest ID: %d"+ "\nContainer ID: %s"+ "\nContainer:\n%s", - len(addrs), + len(peers), chainID, requestID, containerID, formatting.DumpBytes{Bytes: container}, ) - s.send(msg, addrs...) - s.numPushQuerySent.Add(float64(len(addrs))) + s.send(msg, peers...) + s.numPushQuerySent.Add(float64(len(peers))) } // PullQuery implements the Sender interface. func (s *Voting) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID uint32, containerID ids.ID) { - addrs := []salticidae.NetAddr(nil) + peers := []salticidae.PeerID(nil) validatorIDList := validatorIDs.List() for _, validatorID := range validatorIDList { vID := validatorID - if addr, exists := s.conns.GetIP(vID); exists { - addrs = append(addrs, addr) - s.log.Verbo("Sending a PullQuery to %s", toIPDesc(addr)) + if peer, exists := s.conns.GetPeerID(vID); exists { + peers = append(peers, peer) + s.log.Verbo("Sending a PullQuery to %s", vID) } else { s.log.Warn("Attempted to send a PullQuery message to a disconnected validator: %s", vID) s.executor.Add(func() { s.router.QueryFailed(vID, chainID, requestID) }) @@ -382,18 +374,18 @@ func (s *Voting) PullQuery(validatorIDs ids.ShortSet, chainID ids.ID, requestID "\nChain: %s"+ "\nRequest ID: %d"+ "\nContainer ID: %s", - len(addrs), + len(peers), chainID, requestID, containerID, ) - s.send(msg, addrs...) - s.numPullQuerySent.Add(float64(len(addrs))) + s.send(msg, peers...) + s.numPullQuerySent.Add(float64(len(peers))) } // Chits implements the Sender interface. func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32, votes ids.Set) { - addr, exists := s.conns.GetIP(validatorID) + peer, exists := s.conns.GetPeerID(validatorID) if !exists { s.log.Debug("Attempted to send a Chits message to a disconnected validator: %s", validatorID) return // Validator is not connected @@ -408,21 +400,19 @@ func (s *Voting) Chits(validatorID ids.ShortID, chainID ids.ID, requestID uint32 s.log.Verbo("Sending a Chits message."+ "\nValidator: %s"+ - "\nDestination: %s"+ "\nChain: %s"+ "\nRequest ID: %d"+ "\nNumber of Chits: %d", validatorID, - toIPDesc(addr), chainID, requestID, votes.Len(), ) - s.send(msg, addr) + s.send(msg, peer) s.numChitsSent.Inc() } -func (s *Voting) send(msg Msg, addrs ...salticidae.NetAddr) { +func (s *Voting) send(msg Msg, peers ...salticidae.PeerID) { ds := msg.DataStream() defer ds.Free() ba := salticidae.NewByteArrayMovedFromDataStream(ds, false) @@ -430,12 +420,12 @@ func (s *Voting) send(msg Msg, addrs ...salticidae.NetAddr) { cMsg := salticidae.NewMsgMovedFromByteArray(msg.Op(), ba, false) defer cMsg.Free() - switch len(addrs) { + switch len(peers) { case 0: case 1: - s.net.SendMsg(cMsg, addrs[0]) + s.net.SendMsg(cMsg, peers[0]) default: - s.net.MulticastMsgByMove(cMsg, addrs) + s.net.MulticastMsgByMove(cMsg, peers) } } @@ -620,18 +610,16 @@ func chits(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, _ unsafe.Poi func (s *Voting) sanitize(_msg *C.struct_msg_t, _conn *C.struct_msgnetwork_conn_t, op salticidae.Opcode) (ids.ShortID, ids.ID, uint32, Msg, error) { conn := salticidae.PeerNetworkConnFromC(salticidae.CPeerNetworkConn((*C.peernetwork_conn_t)(_conn))) - addr := conn.GetPeerAddr(false) - defer addr.Free() - if addr.IsNull() { - return ids.ShortID{}, ids.ID{}, 0, nil, errConnectionDropped - } - s.log.Verbo("Receiving message from %s", toIPDesc(addr)) + peer := conn.GetPeerID(false) + defer peer.Free() - validatorID, exists := s.conns.GetID(addr) + validatorID, exists := s.conns.GetID(peer) if !exists { - return ids.ShortID{}, ids.ID{}, 0, nil, fmt.Errorf("message received from an un-registered source: %s", toIPDesc(addr)) + return ids.ShortID{}, ids.ID{}, 0, nil, fmt.Errorf("message received from an un-registered peer") } + s.log.Verbo("Receiving message from %s", validatorID) + msg := salticidae.MsgFromC(salticidae.CMsg(_msg)) codec := Codec{} pMsg, err := codec.Parse(op, msg.GetPayloadByMove()) diff --git a/node/node.go b/node/node.go index ed82378..3aafae6 100644 --- a/node/node.go +++ b/node/node.go @@ -5,7 +5,7 @@ package node // #include "salticidae/network.h" // void onTerm(int sig, void *); -// void errorHandler(SalticidaeCError *, bool, void *); +// void errorHandler(SalticidaeCError *, bool, int32_t, void *); import "C" import ( @@ -130,14 +130,14 @@ func onTerm(C.int, unsafe.Pointer) { } //export errorHandler -func errorHandler(_err *C.struct_SalticidaeCError, fatal C.bool, _ unsafe.Pointer) { +func errorHandler(_err *C.struct_SalticidaeCError, fatal C.bool, asyncID C.int32_t, _ unsafe.Pointer) { err := (*salticidae.Error)(unsafe.Pointer(_err)) if fatal { MainNode.Log.Fatal("Error during async call: %s", salticidae.StrError(err.GetCode())) MainNode.EC.Stop() return } - MainNode.Log.Error("Error during async call: %s", salticidae.StrError(err.GetCode())) + MainNode.Log.Debug("Error during async with ID %d call: %s", asyncID, salticidae.StrError(err.GetCode())) } func (n *Node) initNetlib() error { @@ -152,6 +152,8 @@ func (n *Node) initNetlib() error { // Create peer network config, may have tls enabled peerConfig := salticidae.NewPeerNetworkConfig() + peerConfig.ConnTimeout(60) + msgConfig := peerConfig.AsMsgNetworkConfig() msgConfig.MaxMsgSize(maxMessageSize) @@ -254,7 +256,7 @@ func (n *Node) StartConsensusServer() error { // Listen for P2P messages n.PeerNet.Listen(serverIP, &err) if code := err.GetCode(); code != 0 { - return fmt.Errorf("failed to start consensus server: %s", salticidae.StrError(code)) + return fmt.Errorf("failed to listen on consensus server at %s: %s", n.Config.StakingIP, salticidae.StrError(code)) } // Start a server to handle throughput tests if configuration says to. Disabled by default. @@ -268,18 +270,19 @@ func (n *Node) StartConsensusServer() error { n.ClientNet.Listen(clientIP, &err) if code := err.GetCode(); code != 0 { - return fmt.Errorf("failed to listen on xput server: %s", salticidae.StrError(code)) + return fmt.Errorf("failed to listen on xput server at 127.0.0.1:%d: %s", n.Config.ThroughputPort, salticidae.StrError(code)) } } // Add bootstrap nodes to the peer network for _, peer := range n.Config.BootstrapPeers { if !peer.IP.Equal(n.Config.StakingIP) { - bootstrapIP := salticidae.NewNetAddrFromIPPortString(peer.IP.String(), true, &err) + bootstrapAddr := salticidae.NewNetAddrFromIPPortString(peer.IP.String(), true, &err) if code := err.GetCode(); code != 0 { return fmt.Errorf("failed to create bootstrap ip addr: %s", salticidae.StrError(code)) } - n.PeerNet.AddPeer(bootstrapIP) + + n.ValidatorAPI.Connect(bootstrapAddr) } else { n.Log.Error("can't add self as a bootstrapper") } diff --git a/scripts/ansible/inventory.yml b/scripts/ansible/inventory.yml index 5315082..a115a90 100755 --- a/scripts/ansible/inventory.yml +++ b/scripts/ansible/inventory.yml @@ -28,6 +28,7 @@ borealis_bootstrap: staking_tls_enabled: true staking_tls_key_file: "/home/ubuntu/keys/staker.key" staking_tls_cert_file: "/home/ubuntu/keys/staker.crt" + plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins" log_dir: "/home/ubuntu/.gecko" log_level: debug snow_sample_size: 3 @@ -73,6 +74,7 @@ borealis_node: staking_tls_enabled: true staking_tls_key_file: "/home/ubuntu/keys/staker.key" staking_tls_cert_file: "/home/ubuntu/keys/staker.crt" + plugin_dir: "/home/ubuntu/go/src/github.com/ava-labs/gecko/build/plugins" log_dir: "/home/ubuntu/.gecko" log_level: debug snow_sample_size: 3 diff --git a/scripts/ansible/kill_playbook.yml b/scripts/ansible/kill_playbook.yml new file mode 100755 index 0000000..6e91645 --- /dev/null +++ b/scripts/ansible/kill_playbook.yml @@ -0,0 +1,9 @@ + +--- +- name: Update the network + connection: ssh + gather_facts: false + hosts: all + tasks: + - name: Kill Node + command: killall ava \ No newline at end of file diff --git a/scripts/ansible/ping_playbook.yml b/scripts/ansible/ping_playbook.yml new file mode 100755 index 0000000..0c47625 --- /dev/null +++ b/scripts/ansible/ping_playbook.yml @@ -0,0 +1,11 @@ + +--- +- name: Update the network + connection: ssh + gather_facts: false + hosts: all + tasks: + - name: Ping node + shell: "ls" + environment: + PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin diff --git a/scripts/ansible/restart_playbook.yml b/scripts/ansible/restart_playbook.yml index 48d44fb..deec014 100755 --- a/scripts/ansible/restart_playbook.yml +++ b/scripts/ansible/restart_playbook.yml @@ -7,8 +7,8 @@ vars: ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava repo_folder: ~/go/src/github.com/ava-labs/gecko - repo_name: ava-labs/gecko - repo_branch: cascade + repo_name: ava-labs/gecko-internal + repo_branch: retry-connections tasks: - name: Kill Node command: killall ava @@ -33,6 +33,6 @@ path: "{{ log_dir }}" state: absent - name: Start node - shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &" + shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --plugin-dir={{ plugin_dir }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &" environment: PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin diff --git a/scripts/ansible/update_playbook.yml b/scripts/ansible/update_playbook.yml index b28def3..e66dc17 100755 --- a/scripts/ansible/update_playbook.yml +++ b/scripts/ansible/update_playbook.yml @@ -7,8 +7,8 @@ vars: ava_binary: ~/go/src/github.com/ava-labs/gecko/build/ava repo_folder: ~/go/src/github.com/ava-labs/gecko - repo_name: ava-labs/gecko - repo_branch: cascade + repo_name: ava-labs/gecko-internal + repo_branch: retry-connections tasks: - name: Kill Node command: killall ava @@ -25,6 +25,6 @@ environment: PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin - name: Start node - shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &" + shell: "nohup {{ ava_binary }} --network-id={{ network_id }} --api-admin-enabled={{ api_admin_enabled }} --api-keystore-enabled={{ api_keystore_enabled }} --api-metrics-enabled={{ api_metrics_enabled }} --ava-tx-fee={{ ava_tx_fee }} --assertions-enabled={{ assertions_enabled }} --signature-verification-enabled={{ signature_verification_enabled }} --db-enabled={{ db_enabled }} --db-dir={{ db_dir }} --http-port={{ http_port }} --http-tls-enabled={{ http_tls_enabled }} --http-tls-key-file={{ http_tls_key_file }} --http-tls-cert-file={{ http_tls_cert_file }} --bootstrap-ips={{ bootstrap_ips }} --bootstrap-ids={{ bootstrap_ids }} --public-ip={{ ansible_host }} --staking-port={{ staking_port }} --staking-tls-enabled={{ staking_tls_enabled }} --staking-tls-key-file={{ staking_tls_key_file }} --staking-tls-cert-file={{ staking_tls_cert_file }} --plugin-dir={{ plugin_dir }} --log-dir={{ log_dir }} --log-level={{ log_level }} --snow-sample-size={{ snow_sample_size }} --snow-quorum-size={{ snow_quorum_size }} --snow-virtuous-commit-threshold={{ snow_virtuous_commit_threshold }} --snow-rogue-commit-threshold={{ snow_rogue_commit_threshold }} --snow-avalanche-num-parents={{ snow_avalanche_num_parents }} --snow-avalanche-batch-size={{ snow_avalanche_batch_size }} --api-ipcs-enabled={{ api_ipcs_enabled }} --xput-server-enabled={{ xput_server_enabled }} --xput-server-port={{ xput_server_port }} >/dev/null 2>&1 &" environment: PATH: /sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/snap/bin diff --git a/scripts/aws/create.py b/scripts/aws/create.py index 07d75e7..6558c11 100755 --- a/scripts/aws/create.py +++ b/scripts/aws/create.py @@ -6,14 +6,14 @@ Start a number of AVA nodes on Amazon EC2 import boto3 -bootstapNode = "Borealis-Bootstrap" -fullNode = "Borealis-Node" +bootstapNode = "Cascade-Bootstrap" +fullNode = "Cascade-Node" def runInstances(ec2, num: int, name: str): if num > 0: ec2.run_instances( - ImageId="ami-0badd1c10cb7673e9", + ImageId="ami-0c088c21fbcdb8a48", InstanceType="c5.large", MaxCount=num, MinCount=num, @@ -29,11 +29,9 @@ def runInstances(ec2, num: int, name: str): def main(): import argparse - parser = argparse.ArgumentParser( - description=__doc__, - ) - parser.add_argument('numBootstraps', type=int) - parser.add_argument('numNodes', type=int) + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("numBootstraps", type=int) + parser.add_argument("numNodes", type=int) args = parser.parse_args() ec2 = boto3.client("ec2") diff --git a/scripts/build.sh b/scripts/build.sh index b2d1984..46147a9 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -17,6 +17,9 @@ if [[ -d "$CORETH_PATH/.git" ]]; then else go get -t -v -d "$CORETH_PKG/..." fi +cd "$CORETH_PATH" +git -c advice.detachedHead=false checkout v0.1.0 +cd - GECKO_PKG=github.com/ava-labs/gecko GECKO_PATH="$GOPATH/src/$GECKO_PKG" @@ -31,3 +34,8 @@ fi go build -o "$PREFIX/ava" "$GECKO_PATH/main/"*.go go build -o "$PREFIX/xputtest" "$GECKO_PATH/xputtest/"*.go go build -o "$PLUGIN_PREFIX/evm" "$CORETH_PATH/plugin/"*.go +if [[ -f "$PREFIX/ava" && -f "$PREFIX/xputtest" && -f "$PLUGIN_PREFIX/evm" ]]; then + echo "Build Successful" +else + echo "Build failure" +fi \ No newline at end of file diff --git a/scripts/env.sh b/scripts/env.sh index 24141f9..7dd549b 100644 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -9,5 +9,5 @@ SALTICIDAE_GO_HOME="$GOPATH/src/github.com/ava-labs/salticidae-go/" if [[ -f "$SALTICIDAE_GO_HOME/salticidae/libsalticidae.a" ]]; then source "$SALTICIDAE_GO_HOME/scripts/env.sh" else - source /dev/stdin <<<"$(curl -sS https://raw.githubusercontent.com/ava-labs/salticidae-go/master/setup.sh)" + source /dev/stdin <<<"$(curl -sS https://raw.githubusercontent.com/ava-labs/salticidae-go/v0.1.0/setup.sh)" fi diff --git a/snow/consensus/snowball/parameters.go b/snow/consensus/snowball/parameters.go index 7d77405..230f2d7 100644 --- a/snow/consensus/snowball/parameters.go +++ b/snow/consensus/snowball/parameters.go @@ -10,20 +10,21 @@ import ( ) const ( - errMsg = "__________ .___\n" + - "\\______ \\____________ __| _/__.__.\n" + - " | | _/\\_ __ \\__ \\ / __ < | |\n" + - " | | \\ | | \\// __ \\_/ /_/ |\\___ |\n" + - " |______ / |__| (____ /\\____ |/ ____|\n" + - " \\/ \\/ \\/\\/\n" + + errMsg = "" + + `__________ .___` + "\n" + + `\______ \____________ __| _/__.__.` + "\n" + + ` | | _/\_ __ \__ \ / __ < | |` + "\n" + + ` | | \ | | \// __ \_/ /_/ |\___ |` + "\n" + + ` |______ / |__| (____ /\____ |/ ____|` + "\n" + + ` \/ \/ \/\/` + "\n" + "\n" + - "🏆 🏆 🏆 🏆 🏆 🏆\n" + - " ________ ________ ________________\n" + - " / _____/ \\_____ \\ / _ \\__ ___/\n" + - "/ \\ ___ / | \\ / /_\\ \\| |\n" + - "\\ \\_\\ \\/ | \\/ | \\ |\n" + - " \\______ /\\_______ /\\____|__ /____|\n" + - " \\/ \\/ \\/\n" + `🏆 🏆 🏆 🏆 🏆 🏆` + "\n" + + ` ________ ________ ________________` + "\n" + + ` / _____/ \_____ \ / _ \__ ___/` + "\n" + + `/ \ ___ / | \ / /_\ \| |` + "\n" + + `\ \_\ \/ | \/ | \ |` + "\n" + + ` \______ /\_______ /\____|__ /____|` + "\n" + + ` \/ \/ \/` + "\n" ) // Parameters required for snowball consensus diff --git a/snow/engine/avalanche/bootstrapper.go b/snow/engine/avalanche/bootstrapper.go index 0f58194..0fd86a3 100644 --- a/snow/engine/avalanche/bootstrapper.go +++ b/snow/engine/avalanche/bootstrapper.go @@ -177,9 +177,7 @@ func (b *bootstrapper) storeVertex(vtx avalanche.Vertex) { } } - for _, parent := range vtx.Parents() { - vts = append(vts, parent) - } + vts = append(vts, vtx.Parents()...) case choices.Accepted: b.BootstrapConfig.Context.Log.Verbo("Bootstrapping confirmed %s", vtxID) case choices.Rejected: @@ -207,6 +205,7 @@ func (b *bootstrapper) finish() { func (b *bootstrapper) executeAll(jobs *queue.Jobs, numBlocked prometheus.Gauge) { for job, err := jobs.Pop(); err == nil; job, err = jobs.Pop() { numBlocked.Dec() + b.BootstrapConfig.Context.Log.Debug("Executing: %s", job.ID()) if err := jobs.Execute(job); err != nil { b.BootstrapConfig.Context.Log.Warn("Error executing: %s", err) } diff --git a/snow/engine/avalanche/state/serializer.go b/snow/engine/avalanche/state/serializer.go index f076548..0034ad1 100644 --- a/snow/engine/avalanche/state/serializer.go +++ b/snow/engine/avalanche/state/serializer.go @@ -82,6 +82,10 @@ func (s *Serializer) ParseVertex(b []byte) (avacon.Vertex, error) { // BuildVertex implements the avalanche.State interface func (s *Serializer) BuildVertex(parentSet ids.Set, txs []snowstorm.Tx) (avacon.Vertex, error) { + if len(txs) == 0 { + return nil, errNoTxs + } + parentIDs := parentSet.List() ids.SortIDs(parentIDs) sortTxs(txs) diff --git a/snow/engine/avalanche/state/vertex.go b/snow/engine/avalanche/state/vertex.go index 928c11c..327a865 100644 --- a/snow/engine/avalanche/state/vertex.go +++ b/snow/engine/avalanche/state/vertex.go @@ -24,6 +24,7 @@ var ( errExtraSpace = errors.New("trailing buffer space") errInvalidParents = errors.New("vertex contains non-sorted or duplicated parentIDs") errInvalidTxs = errors.New("vertex contains non-sorted or duplicated transactions") + errNoTxs = errors.New("vertex contains no transactions") ) type vertex struct { @@ -45,6 +46,8 @@ func (vtx *vertex) Verify() error { switch { case !ids.IsSortedAndUniqueIDs(vtx.parentIDs): return errInvalidParents + case len(vtx.txs) == 0: + return errNoTxs case !isSortedAndUniqueTxs(vtx.txs): return errInvalidTxs default: @@ -55,7 +58,7 @@ func (vtx *vertex) Verify() error { /* * Vertex: * Codec | 04 Bytes - * Chain | 32 Bytes + * Chain | 32 Bytes * Height | 08 Bytes * NumParents | 04 Bytes * Repeated (NumParents): diff --git a/snow/engine/avalanche/transitive.go b/snow/engine/avalanche/transitive.go index 4de2aa5..fc55250 100644 --- a/snow/engine/avalanche/transitive.go +++ b/snow/engine/avalanche/transitive.go @@ -316,8 +316,37 @@ func (t *Transitive) batch(txs []snowstorm.Tx, force, empty bool) { } } - if len(batch) > 0 || (empty && !issued) { + if len(batch) > 0 { t.issueBatch(batch) + } else if empty && !issued { + t.issueRepoll() + } +} + +func (t *Transitive) issueRepoll() { + preferredIDs := t.Consensus.Preferences().List() + numPreferredIDs := len(preferredIDs) + if numPreferredIDs == 0 { + t.Config.Context.Log.Error("Re-query attempt was dropped due to no pending vertices") + return + } + + sampler := random.Uniform{N: len(preferredIDs)} + vtxID := preferredIDs[sampler.Sample()] + + p := t.Consensus.Parameters() + vdrs := t.Config.Validators.Sample(p.K) // Validators to sample + + vdrSet := ids.ShortSet{} // Validators to sample repr. as a set + for _, vdr := range vdrs { + vdrSet.Add(vdr.ID()) + } + + t.RequestID++ + if numVdrs := len(vdrs); numVdrs == p.K && t.polls.Add(t.RequestID, vdrSet.Len()) { + t.Config.Sender.PullQuery(vdrSet, t.RequestID, vtxID) + } else if numVdrs < p.K { + t.Config.Context.Log.Error("Re-query for %s was dropped due to an insufficient number of validators", vtxID) } } diff --git a/snow/engine/avalanche/transitive_test.go b/snow/engine/avalanche/transitive_test.go index defd2df..604afaf 100644 --- a/snow/engine/avalanche/transitive_test.go +++ b/snow/engine/avalanche/transitive_test.go @@ -698,23 +698,12 @@ func TestEngineScheduleRepoll(t *testing.T) { sender.PushQueryF = nil - st.buildVertex = func(_ ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) { - consumers := []snowstorm.Tx{} - for _, tx := range txs { - consumers = append(consumers, tx) - } - return &Vtx{ - parents: []avalanche.Vertex{gVtx, mVtx}, - id: GenerateID(), - txs: consumers, - status: choices.Processing, - bytes: []byte{1}, - }, nil - } - repolled := new(bool) - sender.PushQueryF = func(_ ids.ShortSet, _ uint32, _ ids.ID, _ []byte) { + sender.PullQueryF = func(_ ids.ShortSet, _ uint32, vtxID ids.ID) { *repolled = true + if !vtxID.Equals(vtx.ID()) { + t.Fatalf("Wrong vertex queried") + } } te.QueryFailed(vdr.ID(), *requestID) @@ -979,31 +968,14 @@ func TestEngineIssueRepoll(t *testing.T) { te.Initialize(config) te.finishBootstrapping() - newVtxID := new(ids.ID) - - st.buildVertex = func(s ids.Set, txs []snowstorm.Tx) (avalanche.Vertex, error) { - if len(txs) != 0 { - t.Fatalf("Wrong vertex issued") - } - if s.Len() != 2 || !s.Contains(gVtx.ID()) || !s.Contains(mVtx.ID()) { - t.Fatalf("Wrong vertex issued") - } - - vtx := &Vtx{ - parents: []avalanche.Vertex{gVtx, mVtx}, - id: GenerateID(), - status: choices.Processing, - bytes: []byte{1}, - } - *newVtxID = vtx.ID() - return vtx, nil - } - - sender.PushQueryF = func(vdrs ids.ShortSet, _ uint32, vtxID ids.ID, vtx []byte) { + sender.PullQueryF = func(vdrs ids.ShortSet, _ uint32, vtxID ids.ID) { vdrSet := ids.ShortSet{} vdrSet.Add(vdr.ID()) - if !vdrs.Equals(vdrSet) || !vtxID.Equals(*newVtxID) { - t.Fatalf("Wrong query message") + if !vdrs.Equals(vdrSet) { + t.Fatalf("Wrong query recipients") + } + if !vtxID.Equals(gVtx.ID()) && !vtxID.Equals(mVtx.ID()) { + t.Fatalf("Unknown re-query") } } diff --git a/snow/engine/avalanche/voter.go b/snow/engine/avalanche/voter.go index 7430495..6e52a34 100644 --- a/snow/engine/avalanche/voter.go +++ b/snow/engine/avalanche/voter.go @@ -79,12 +79,23 @@ func (v *voter) bubbleVotes(votes ids.UniqueBag) ids.UniqueBag { vtx := vts[0] vts = vts[1:] - if status := vtx.Status(); status.Fetched() && !v.t.Consensus.VertexIssued(vtx) { - vts = append(vts, vtx.Parents()...) - } else if !status.Decided() && v.t.Consensus.VertexIssued(vtx) { + status := vtx.Status() + if !status.Fetched() { + v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is unknown", set.Len(), vtx.ID()) + continue + } + + if status.Decided() { + v.t.Config.Context.Log.Verbo("Dropping %d vote(s) for %s because the vertex is decided", set.Len(), vtx.ID()) + continue + } + + if v.t.Consensus.VertexIssued(vtx) { + v.t.Config.Context.Log.Verbo("Applying %d vote(s) for %s", set.Len(), vtx.ID()) bubbledVotes.UnionSet(vtx.ID(), set) } else { - v.t.Config.Context.Log.Debug("Dropping %d vote(s) for %s because the vertex is invalid", set.Len(), vtx.ID()) + v.t.Config.Context.Log.Verbo("Bubbling %d vote(s) for %s because the vertex isn't issued", set.Len(), vtx.ID()) + vts = append(vts, vtx.Parents()...) } } } diff --git a/utils/ip.go b/utils/ip.go index 1b35fa7..f5f84db 100644 --- a/utils/ip.go +++ b/utils/ip.go @@ -10,6 +10,28 @@ import ( "strconv" ) +// This was taken from: https://stackoverflow.com/a/50825191/3478466 +var privateIPBlocks []*net.IPNet + +func init() { + for _, cidr := range []string{ + "127.0.0.0/8", // IPv4 loopback + "10.0.0.0/8", // RFC1918 + "172.16.0.0/12", // RFC1918 + "192.168.0.0/16", // RFC1918 + "169.254.0.0/16", // RFC3927 link-local + "::1/128", // IPv6 loopback + "fe80::/10", // IPv6 link-local + "fc00::/7", // IPv6 unique local addr + } { + _, block, err := net.ParseCIDR(cidr) + if err != nil { + panic(fmt.Errorf("parse error on %q: %v", cidr, err)) + } + privateIPBlocks = append(privateIPBlocks, block) + } +} + var ( errBadIP = errors.New("bad ip format") ) @@ -35,6 +57,32 @@ func (ipDesc IPDesc) String() string { return net.JoinHostPort(ipDesc.IP.String(), fmt.Sprintf("%d", ipDesc.Port)) } +// IsPrivate attempts to decide if the ip address in this descriptor is a local +// ip address. +// This function was taken from: https://stackoverflow.com/a/50825191/3478466 +func (ipDesc IPDesc) IsPrivate() bool { + ip := ipDesc.IP + if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() { + return true + } + + for _, block := range privateIPBlocks { + if block.Contains(ip) { + return true + } + } + return true +} + +// IsZero returns if the IP or port is zeroed out +func (ipDesc IPDesc) IsZero() bool { + ip := ipDesc.IP + return ipDesc.Port == 0 || + len(ip) == 0 || + ip.Equal(net.IPv4zero) || + ip.Equal(net.IPv6zero) +} + // ToIPDesc ... func ToIPDesc(str string) (IPDesc, error) { host, portStr, err := net.SplitHostPort(str) diff --git a/utils/logging/factory.go b/utils/logging/factory.go index 112d03c..c5d5f60 100644 --- a/utils/logging/factory.go +++ b/utils/logging/factory.go @@ -43,7 +43,7 @@ func (f *factory) Make() (Logger, error) { // MakeChain ... func (f *factory) MakeChain(chainID ids.ID, subdir string) (Logger, error) { config := f.config - config.MsgPrefix = "SN " + chainID.String() + config.MsgPrefix = "chain " + chainID.String() config.Directory = path.Join(config.Directory, "chain", chainID.String(), subdir) log, err := New(config) diff --git a/vms/avm/vm.go b/vms/avm/vm.go index fd68e44..180f4ae 100644 --- a/vms/avm/vm.go +++ b/vms/avm/vm.go @@ -91,8 +91,10 @@ func (cr *codecRegistry) RegisterType(val interface{}) error { cr.typeToFxIndex[valType] = cr.index return cr.codec.RegisterType(val) } -func (cr *codecRegistry) Marshal(val interface{}) ([]byte, error) { return cr.codec.Marshal(val) } -func (cr *codecRegistry) Unmarshal(b []byte, val interface{}) error { return cr.codec.Unmarshal(b, val) } +func (cr *codecRegistry) Marshal(val interface{}) ([]byte, error) { return cr.codec.Marshal(val) } +func (cr *codecRegistry) Unmarshal(b []byte, val interface{}) error { + return cr.codec.Unmarshal(b, val) +} /* ****************************************************************************** @@ -392,7 +394,9 @@ func (vm *VM) initAliases(genesisBytes []byte) error { txID := tx.ID() - vm.Alias(txID, genesisTx.Alias) + if err = vm.Alias(txID, genesisTx.Alias); err != nil { + return err + } } return nil @@ -461,7 +465,10 @@ func (vm *VM) parseTx(b []byte) (*UniqueTx, error) { if err := vm.state.SetTx(tx.ID(), tx.Tx); err != nil { return nil, err } - tx.setStatus(choices.Processing) + + if err := tx.setStatus(choices.Processing); err != nil { + return nil, err + } } return tx, nil diff --git a/vms/avm/vm_test.go b/vms/avm/vm_test.go index 36e0aa0..f495e87 100644 --- a/vms/avm/vm_test.go +++ b/vms/avm/vm_test.go @@ -943,3 +943,51 @@ func TestIssueProperty(t *testing.T) { t.Fatal(err) } } + +func TestVMFormat(t *testing.T) { + _, _, vm := GenesisVM(t) + defer ctx.Lock.Unlock() + defer vm.Shutdown() + + tests := []struct { + in string + expected string + }{ + {"", "3D7sudhzUKTYFkYj4Zoe7GgSKhuyP9bYwXunHwhZsmQe1z9Mp-45PJLL"}, + } + for _, tt := range tests { + t.Run(tt.in, func(t *testing.T) { + if res := vm.Format([]byte(tt.in)); tt.expected != res { + t.Errorf("Expected %q, got %q", tt.expected, res) + } + }) + } +} + +func TestVMFormatAliased(t *testing.T) { + _, _, vm := GenesisVM(t) + defer ctx.Lock.Unlock() + defer vm.Shutdown() + + origAliases := ctx.BCLookup + defer func() { ctx.BCLookup = origAliases }() + + tmpAliases := &ids.Aliaser{} + tmpAliases.Initialize() + tmpAliases.Alias(ctx.ChainID, "X") + ctx.BCLookup = tmpAliases + + tests := []struct { + in string + expected string + }{ + {"", "X-45PJLL"}, + } + for _, tt := range tests { + t.Run(tt.in, func(t *testing.T) { + if res := vm.Format([]byte(tt.in)); tt.expected != res { + t.Errorf("Expected %q, got %q", tt.expected, res) + } + }) + } +} diff --git a/vms/components/codec/codec.go b/vms/components/codec/codec.go index 8005f34..72192cb 100644 --- a/vms/components/codec/codec.go +++ b/vms/components/codec/codec.go @@ -341,8 +341,5 @@ func (c codec) unmarshal(p *wrappers.Packer, field reflect.Value) error { // Returns true iff [field] should be serialized func shouldSerialize(field reflect.StructField) bool { - if field.Tag.Get("serialize") == "true" { - return true - } - return false + return field.Tag.Get("serialize") == "true" } diff --git a/vms/components/core/snowman_vm.go b/vms/components/core/snowman_vm.go index f710edd..bc849af 100644 --- a/vms/components/core/snowman_vm.go +++ b/vms/components/core/snowman_vm.go @@ -95,10 +95,7 @@ func (svm *SnowmanVM) Shutdown() { // DBInitialized returns true iff [svm]'s database has values in it already func (svm *SnowmanVM) DBInitialized() bool { status := svm.State.GetStatus(svm.DB, dbInitializedID) - if status == choices.Accepted { - return true - } - return false + return status == choices.Accepted } // SetDBInitialized marks the database as initialized diff --git a/vms/manager.go b/vms/manager.go index 9f8cf3b..4449c27 100644 --- a/vms/manager.go +++ b/vms/manager.go @@ -131,6 +131,8 @@ func (m *manager) addStaticAPIEndpoints(vmID ids.ID) { // register the static endpoints for extension, service := range staticVM.CreateStaticHandlers() { m.log.Verbo("adding static API endpoint: %s", defaultEndpoint+extension) - m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log) + if err := m.apiServer.AddRoute(service, lock, defaultEndpoint, extension, m.log); err != nil { + m.log.Warn("failed to add static API endpoint %s: %v", fmt.Sprintf("%s%s", defaultEndpoint, extension), err) + } } } diff --git a/vms/platformvm/service.go b/vms/platformvm/service.go index b911c2a..86093b0 100644 --- a/vms/platformvm/service.go +++ b/vms/platformvm/service.go @@ -1227,6 +1227,10 @@ func (service *Service) chainExists(blockID ids.ID, chainID ids.ID) (bool, error db := block.onAccept() chains, err := service.vm.getChains(db) + if err != nil { + return false, err + } + for _, chain := range chains { if chain.ID().Equals(chainID) { return true, nil diff --git a/vms/platformvm/service_test.go b/vms/platformvm/service_test.go index 6efb8d6..051c2c0 100644 --- a/vms/platformvm/service_test.go +++ b/vms/platformvm/service_test.go @@ -9,7 +9,7 @@ import ( ) func TestAddDefaultSubnetValidator(t *testing.T) { - expectedJSONString := `{"startTime":"0","endtime":"0","id":null,"destination":null,"delegationFeeRate":"0","payerNonce":"0"}` + expectedJSONString := `{"startTime":"0","endTime":"0","id":null,"destination":null,"delegationFeeRate":"0","payerNonce":"0"}` args := AddDefaultSubnetValidatorArgs{} bytes, err := json.Marshal(&args) if err != nil { diff --git a/vms/platformvm/static_service.go b/vms/platformvm/static_service.go index 80b66d2..86af005 100644 --- a/vms/platformvm/static_service.go +++ b/vms/platformvm/static_service.go @@ -44,7 +44,7 @@ type APIAccount struct { // is sent when this staker is done staking. type APIValidator struct { StartTime json.Uint64 `json:"startTime"` - EndTime json.Uint64 `json:"endtime"` + EndTime json.Uint64 `json:"endTime"` Weight *json.Uint64 `json:"weight,omitempty"` StakeAmount *json.Uint64 `json:"stakeAmount,omitempty"` ID ids.ShortID `json:"id"`