[NOD-652] Add selected tip and get selected tip messages (#595)

* [NOD-652] Add selectedTip and getSelectedTip messages

* [NOD-652] Remove peerSyncState.isSelectedTipKnown

* [NOD-652] Do nothing on OnSelectedTip if the peer selected tip hasn't changed

* [NOD-652] Handle selected tip message with block handler

* [NOD-652] Add comments

* [NOD-652] go mod tidy

* [NOD-652] Fix TestVersion

* [NOD-652] Use dag.AdjustedTime instead of dag.timeSource.AdjustedTime

* [NOD-652] Create shouldQueryPeerSelectedTips and queueMsgGetSelectedTip functions

* [NOD-652] Change selectedTip to selectedTipHash where needed

* [NOD-652] add minDAGTimeDelay constant

* [NOD-652] add comments

* [NOD-652] Fix names and comments

* [NOD-652] Put msg.reply push in the right place

* [NOD-652] Fix comments and names
This commit is contained in:
Ori Newman 2020-01-22 16:34:21 +02:00 committed by Svarog
parent 94ec159147
commit 29bcc271b5
26 changed files with 529 additions and 123 deletions

View File

@ -114,7 +114,7 @@ func (dag *BlockDAG) newBlockNode(blockHeader *wire.BlockHeader, parents blockSe
parents: parents, parents: parents,
children: make(blockSet), children: make(blockSet),
blueScore: math.MaxUint64, // Initialized to the max value to avoid collisions with the genesis block blueScore: math.MaxUint64, // Initialized to the max value to avoid collisions with the genesis block
timestamp: dag.timeSource.AdjustedTime().Unix(), timestamp: dag.AdjustedTime().Unix(),
bluesAnticoneSizes: make(map[daghash.Hash]uint32), bluesAnticoneSizes: make(map[daghash.Hash]uint32),
} }

View File

@ -1274,10 +1274,17 @@ func (dag *BlockDAG) isCurrent() bool {
} else { } else {
dagTimestamp = selectedTip.timestamp dagTimestamp = selectedTip.timestamp
} }
minus24Hours := dag.timeSource.AdjustedTime().Add(-24 * time.Hour).Unix() minus24Hours := dag.AdjustedTime().Add(-24 * time.Hour).Unix()
return dagTimestamp >= minus24Hours return dagTimestamp >= minus24Hours
} }
// AdjustedTime returns the adjusted time according to
// dag.timeSource. See MedianTimeSource.AdjustedTime for
// more details.
func (dag *BlockDAG) AdjustedTime() time.Time {
return dag.timeSource.AdjustedTime()
}
// IsCurrent returns whether or not the DAG believes it is current. Several // IsCurrent returns whether or not the DAG believes it is current. Several
// factors are used to guess, but the key factors that allow the DAG to // factors are used to guess, but the key factors that allow the DAG to
// believe it is current are: // believe it is current are:
@ -1774,7 +1781,7 @@ func (dag *BlockDAG) SubnetworkID() *subnetworkid.SubnetworkID {
} }
func (dag *BlockDAG) addDelayedBlock(block *util.Block, delay time.Duration) error { func (dag *BlockDAG) addDelayedBlock(block *util.Block, delay time.Duration) error {
processTime := dag.timeSource.AdjustedTime().Add(delay) processTime := dag.AdjustedTime().Add(delay)
log.Debugf("Adding block to delayed blocks queue (block hash: %s, process time: %s)", block.Hash().String(), processTime) log.Debugf("Adding block to delayed blocks queue (block hash: %s, process time: %s)", block.Hash().String(), processTime)
delayedBlock := &delayedBlock{ delayedBlock := &delayedBlock{
block: block, block: block,
@ -1792,7 +1799,7 @@ func (dag *BlockDAG) processDelayedBlocks() error {
// Check if the delayed block with the earliest process time should be processed // Check if the delayed block with the earliest process time should be processed
for dag.delayedBlocksQueue.Len() > 0 { for dag.delayedBlocksQueue.Len() > 0 {
earliestDelayedBlockProcessTime := dag.peekDelayedBlock().processTime earliestDelayedBlockProcessTime := dag.peekDelayedBlock().processTime
if earliestDelayedBlockProcessTime.After(dag.timeSource.AdjustedTime()) { if earliestDelayedBlockProcessTime.After(dag.AdjustedTime()) {
break break
} }
delayedBlock := dag.popDelayedBlock() delayedBlock := dag.popDelayedBlock()

View File

@ -114,7 +114,7 @@ func (dag *BlockDAG) NextBlockTime() time.Time {
// timestamp is truncated to a second boundary before comparison since a // timestamp is truncated to a second boundary before comparison since a
// block timestamp does not supported a precision greater than one // block timestamp does not supported a precision greater than one
// second. // second.
newTimestamp := dag.timeSource.AdjustedTime() newTimestamp := dag.AdjustedTime()
minTimestamp := dag.NextBlockMinimumTime() minTimestamp := dag.NextBlockMinimumTime()
if newTimestamp.Before(minTimestamp) { if newTimestamp.Before(minTimestamp) {
newTimestamp = minTimestamp newTimestamp = minTimestamp

View File

@ -251,7 +251,7 @@ func (dag *BlockDAG) maxDelayOfParents(parentHashes []*daghash.Hash) (delay time
for _, parentHash := range parentHashes { for _, parentHash := range parentHashes {
if delayedParent, exists := dag.delayedBlocks[*parentHash]; exists { if delayedParent, exists := dag.delayedBlocks[*parentHash]; exists {
isDelayed = true isDelayed = true
parentDelay := delayedParent.processTime.Sub(dag.timeSource.AdjustedTime()) parentDelay := delayedParent.processTime.Sub(dag.AdjustedTime())
if parentDelay > delay { if parentDelay > delay {
delay = parentDelay delay = parentDelay
} }

View File

@ -435,7 +435,7 @@ func (dag *BlockDAG) checkBlockHeaderSanity(header *wire.BlockHeader, flags Beha
// the duration of time that should be waited before the block becomes valid. // the duration of time that should be waited before the block becomes valid.
// This check needs to be last as it does not return an error but rather marks the // This check needs to be last as it does not return an error but rather marks the
// header as delayed (and valid). // header as delayed (and valid).
maxTimestamp := dag.timeSource.AdjustedTime().Add(time.Second * maxTimestamp := dag.AdjustedTime().Add(time.Second *
time.Duration(int64(dag.TimestampDeviationTolerance)*dag.targetTimePerBlock)) time.Duration(int64(dag.TimestampDeviationTolerance)*dag.targetTimePerBlock))
if header.Timestamp.After(maxTimestamp) { if header.Timestamp.After(maxTimestamp) {
return header.Timestamp.Sub(maxTimestamp), nil return header.Timestamp.Sub(maxTimestamp), nil

4
go.mod
View File

@ -16,8 +16,8 @@ require (
github.com/jrick/logrotate v1.0.0 github.com/jrick/logrotate v1.0.0
github.com/kr/pretty v0.1.0 // indirect github.com/kr/pretty v0.1.0 // indirect
github.com/pkg/errors v0.8.1 github.com/pkg/errors v0.8.1
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect golang.org/x/net v0.0.0-20190620200207-3b0461eec859 // indirect
golang.org/x/sys v0.0.0-20190426135247-a129542de9ae // indirect golang.org/x/sys v0.0.0-20190426135247-a129542de9ae // indirect
golang.org/x/text v0.3.2 // indirect golang.org/x/text v0.3.2 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect

8
go.sum
View File

@ -34,12 +34,12 @@ github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU= golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA=

View File

@ -18,6 +18,7 @@ import (
"net" "net"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
) )
const ( const (
@ -32,6 +33,10 @@ const (
// maxRequestedTxns is the maximum number of requested transactions // maxRequestedTxns is the maximum number of requested transactions
// hashes to store in memory. // hashes to store in memory.
maxRequestedTxns = wire.MaxInvPerMsg maxRequestedTxns = wire.MaxInvPerMsg
minGetSelectedTipInterval = time.Minute
minDAGTimeDelay = time.Minute
) )
// newPeerMsg signifies a newly connected peer to the block handler. // newPeerMsg signifies a newly connected peer to the block handler.
@ -113,6 +118,12 @@ type pauseMsg struct {
unpause <-chan struct{} unpause <-chan struct{}
} }
type selectedTipMsg struct {
selectedTipHash *daghash.Hash
peer *peerpkg.Peer
reply chan struct{}
}
type requestQueueAndSet struct { type requestQueueAndSet struct {
queue []*wire.InvVect queue []*wire.InvVect
set map[daghash.Hash]struct{} set map[daghash.Hash]struct{}
@ -122,6 +133,8 @@ type requestQueueAndSet struct {
// about a peer. // about a peer.
type peerSyncState struct { type peerSyncState struct {
syncCandidate bool syncCandidate bool
lastSelectedTipRequest time.Time
isPendingForSelectedTip bool
requestQueueMtx sync.Mutex requestQueueMtx sync.Mutex
requestQueues map[wire.InvType]*requestQueueAndSet requestQueues map[wire.InvType]*requestQueueAndSet
requestedTxns map[daghash.TxID]struct{} requestedTxns map[daghash.TxID]struct{}
@ -169,8 +182,7 @@ func (sm *SyncManager) startSync() {
continue continue
} }
if !peer.IsSyncCandidate() { if !peer.IsSelectedTipKnown() {
state.syncCandidate = false
continue continue
} }
@ -187,13 +199,38 @@ func (sm *SyncManager) startSync() {
sm.requestedBlocks = make(map[daghash.Hash]struct{}) sm.requestedBlocks = make(map[daghash.Hash]struct{})
log.Infof("Syncing to block %s from peer %s", log.Infof("Syncing to block %s from peer %s",
syncPeer.SelectedTip(), syncPeer.Addr()) syncPeer.SelectedTipHash(), syncPeer.Addr())
syncPeer.PushGetBlockLocatorMsg(syncPeer.SelectedTip(), sm.dagParams.GenesisHash) syncPeer.PushGetBlockLocatorMsg(syncPeer.SelectedTipHash(), sm.dagParams.GenesisHash)
sm.syncPeer = syncPeer sm.syncPeer = syncPeer
} else { return
log.Warnf("No sync peer candidates available")
} }
log.Warnf("No sync peer candidates available")
if sm.shouldQueryPeerSelectedTips() {
for peer, state := range sm.peerStates {
if !state.syncCandidate {
continue
}
if time.Now().Sub(state.lastSelectedTipRequest) < minGetSelectedTipInterval {
continue
}
queueMsgGetSelectedTip(peer, state)
}
}
return
}
func (sm *SyncManager) shouldQueryPeerSelectedTips() bool {
return sm.dag.AdjustedTime().Sub(sm.dag.CalcPastMedianTime()) > minDAGTimeDelay
}
func queueMsgGetSelectedTip(peer *peerpkg.Peer, state *peerSyncState) {
state.lastSelectedTipRequest = time.Now()
state.isPendingForSelectedTip = true
peer.QueueMessage(wire.NewMsgGetSelectedTip(), nil)
} }
// isSyncCandidate returns whether or not the peer is a candidate to consider // isSyncCandidate returns whether or not the peer is a candidate to consider
@ -827,6 +864,23 @@ func (sm *SyncManager) limitHashMap(m map[daghash.Hash]struct{}, limit int) {
} }
} }
func (sm *SyncManager) handleSelectedTipMsg(msg *selectedTipMsg) {
peer := msg.peer
selectedTipHash := msg.selectedTipHash
state := sm.peerStates[peer]
if !state.isPendingForSelectedTip {
log.Warnf("Got unrequested selected tip message from %s -- "+
"disconnecting", peer.Addr())
peer.Disconnect()
}
state.isPendingForSelectedTip = false
if selectedTipHash.IsEqual(peer.SelectedTipHash()) {
return
}
peer.SetSelectedTipHash(selectedTipHash)
sm.startSync()
}
// blockHandler is the main handler for the sync manager. It must be run as a // blockHandler is the main handler for the sync manager. It must be run as a
// goroutine. It processes block and inv messages in a separate goroutine // goroutine. It processes block and inv messages in a separate goroutine
// from the peer handlers so the block (MsgBlock) messages are handled by a // from the peer handlers so the block (MsgBlock) messages are handled by a
@ -894,6 +948,10 @@ out:
// Wait until the sender unpauses the manager. // Wait until the sender unpauses the manager.
<-msg.unpause <-msg.unpause
case *selectedTipMsg:
sm.handleSelectedTipMsg(msg)
msg.reply <- struct{}{}
default: default:
log.Warnf("Invalid message type in block "+ log.Warnf("Invalid message type in block "+
"handler: %T", msg) "handler: %T", msg)
@ -992,6 +1050,17 @@ func (sm *SyncManager) QueueInv(inv *wire.MsgInv, peer *peerpkg.Peer) {
sm.msgChan <- &invMsg{inv: inv, peer: peer} sm.msgChan <- &invMsg{inv: inv, peer: peer}
} }
// QueueSelectedTipMsg adds the passed selected tip message and peer to the
// block handling queue. Responds to the done channel argument after it finished
// handling the message.
func (sm *SyncManager) QueueSelectedTipMsg(msg *wire.MsgSelectedTip, peer *peerpkg.Peer, done chan struct{}) {
sm.msgChan <- &selectedTipMsg{
selectedTipHash: msg.SelectedTipHash,
peer: peer,
reply: done,
}
}
// DonePeer informs the blockmanager that a peer has disconnected. // DonePeer informs the blockmanager that a peer has disconnected.
func (sm *SyncManager) DonePeer(peer *peerpkg.Peer) { func (sm *SyncManager) DonePeer(peer *peerpkg.Peer) {
// Ignore if we are shutting down. // Ignore if we are shutting down.

View File

@ -23,7 +23,7 @@ func mockRemotePeer() error {
UserAgentName: "peer", // User agent name to advertise. UserAgentName: "peer", // User agent name to advertise.
UserAgentVersion: "1.0.0", // User agent version to advertise. UserAgentVersion: "1.0.0", // User agent version to advertise.
DAGParams: &dagconfig.SimnetParams, DAGParams: &dagconfig.SimnetParams,
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
// Accept connections on the simnet port. // Accept connections on the simnet port.
@ -78,7 +78,7 @@ func Example_newOutboundPeer() {
verack <- struct{}{} verack <- struct{}{}
}, },
}, },
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
p, err := peer.NewOutboundPeer(peerCfg, "127.0.0.1:18555") p, err := peer.NewOutboundPeer(peerCfg, "127.0.0.1:18555")
if err != nil { if err != nil {

View File

@ -96,7 +96,7 @@ func messageSummary(msg wire.Message) string {
switch msg := msg.(type) { switch msg := msg.(type) {
case *wire.MsgVersion: case *wire.MsgVersion:
return fmt.Sprintf("agent %s, pver %d, selected tip %s", return fmt.Sprintf("agent %s, pver %d, selected tip %s",
msg.UserAgent, msg.ProtocolVersion, msg.SelectedTip) msg.UserAgent, msg.ProtocolVersion, msg.SelectedTipHash)
case *wire.MsgVerAck: case *wire.MsgVerAck:
// No summary. // No summary.

View File

@ -168,6 +168,14 @@ type MessageListeners struct {
// message. // message.
OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders) OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
// OnGetSelectedTip is invoked when a peer receives a getSelectedTip kaspa
// message.
OnGetSelectedTip func()
// OnSelectedTip is invoked when a peer receives a selectedTip kaspa
// message.
OnSelectedTip func(p *Peer, msg *wire.MsgSelectedTip)
// OnRead is invoked when a peer receives a kaspa message. It // OnRead is invoked when a peer receives a kaspa message. It
// consists of the number of bytes read, the message, and whether or not // consists of the number of bytes read, the message, and whether or not
// an error in the read occurred. Typically, callers will opt to use // an error in the read occurred. Typically, callers will opt to use
@ -186,12 +194,12 @@ type MessageListeners struct {
// Config is the struct to hold configuration options useful to Peer. // Config is the struct to hold configuration options useful to Peer.
type Config struct { type Config struct {
// SelectedTip specifies a callback which provides the selected tip // SelectedTipHash specifies a callback which provides the selected tip
// to the peer as needed. // to the peer as needed.
SelectedTip func() *daghash.Hash SelectedTipHash func() *daghash.Hash
// SelectedTip specifies a callback which provides the selected tip // BlockExists determines whether a block with the given hash exists in
// to the peer as needed. // the DAG.
BlockExists func(*daghash.Hash) bool BlockExists func(*daghash.Hash) bool
// HostToNetAddress returns the netaddress for the given host. This can be // HostToNetAddress returns the netaddress for the given host. This can be
@ -341,7 +349,7 @@ type StatsSnap struct {
Version uint32 Version uint32
UserAgent string UserAgent string
Inbound bool Inbound bool
SelectedTip *daghash.Hash SelectedTipHash *daghash.Hash
LastPingNonce uint64 LastPingNonce uint64
LastPingTime time.Time LastPingTime time.Time
LastPingMicros int64 LastPingMicros int64
@ -426,7 +434,7 @@ type Peer struct {
statsMtx sync.RWMutex statsMtx sync.RWMutex
timeOffset int64 timeOffset int64
timeConnected time.Time timeConnected time.Time
selectedTip *daghash.Hash selectedTipHash *daghash.Hash
lastPingNonce uint64 // Set to nonce if we have a pending ping. lastPingNonce uint64 // Set to nonce if we have a pending ping.
lastPingTime time.Time // Time we sent last ping. lastPingTime time.Time // Time we sent last ping.
lastPingMicros int64 // Time for last ping to return. lastPingMicros int64 // Time for last ping to return.
@ -486,7 +494,7 @@ func (p *Peer) StatsSnapshot() *StatsSnap {
TimeOffset: p.timeOffset, TimeOffset: p.timeOffset,
Version: protocolVersion, Version: protocolVersion,
Inbound: p.inbound, Inbound: p.inbound,
SelectedTip: p.selectedTip, SelectedTipHash: p.selectedTipHash,
LastPingNonce: p.lastPingNonce, LastPingNonce: p.lastPingNonce,
LastPingMicros: p.lastPingMicros, LastPingMicros: p.lastPingMicros,
LastPingTime: p.lastPingTime, LastPingTime: p.lastPingTime,
@ -633,22 +641,28 @@ func (p *Peer) ProtocolVersion() uint32 {
return protocolVersion return protocolVersion
} }
// SelectedTip returns the selected tip of the peer. // SelectedTipHash returns the selected tip of the peer.
// //
// This function is safe for concurrent access. // This function is safe for concurrent access.
func (p *Peer) SelectedTip() *daghash.Hash { func (p *Peer) SelectedTipHash() *daghash.Hash {
p.statsMtx.RLock() p.statsMtx.RLock()
selectedTip := p.selectedTip selectedTipHash := p.selectedTipHash
p.statsMtx.RUnlock() p.statsMtx.RUnlock()
return selectedTip return selectedTipHash
} }
// IsSyncCandidate returns whether or not this peer is a sync candidate. // SetSelectedTipHash sets the selected tip of the peer.
func (p *Peer) SetSelectedTipHash(selectedTipHash *daghash.Hash) {
p.selectedTipHash = selectedTipHash
}
// IsSelectedTipKnown returns whether or not this peer selected
// tip is a known block.
// //
// This function is safe for concurrent access. // This function is safe for concurrent access.
func (p *Peer) IsSyncCandidate() bool { func (p *Peer) IsSelectedTipKnown() bool {
return !p.cfg.BlockExists(p.selectedTip) return !p.cfg.BlockExists(p.selectedTipHash)
} }
// LastSend returns the last send time of the peer. // LastSend returns the last send time of the peer.
@ -718,7 +732,7 @@ func (p *Peer) WantsHeaders() bool {
// localVersionMsg creates a version message that can be used to send to the // localVersionMsg creates a version message that can be used to send to the
// remote peer. // remote peer.
func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) { func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
selectedTip := p.cfg.SelectedTip() selectedTipHash := p.cfg.SelectedTipHash()
theirNA := p.na theirNA := p.na
// If we are behind a proxy and the connection comes from the proxy then // If we are behind a proxy and the connection comes from the proxy then
@ -754,7 +768,7 @@ func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
subnetworkID := p.cfg.SubnetworkID subnetworkID := p.cfg.SubnetworkID
// Version message. // Version message.
msg := wire.NewMsgVersion(ourNA, theirNA, nonce, selectedTip, subnetworkID) msg := wire.NewMsgVersion(ourNA, theirNA, nonce, selectedTipHash, subnetworkID)
msg.AddUserAgent(p.cfg.UserAgentName, p.cfg.UserAgentVersion, msg.AddUserAgent(p.cfg.UserAgentName, p.cfg.UserAgentVersion,
p.cfg.UserAgentComments...) p.cfg.UserAgentComments...)
@ -960,7 +974,7 @@ func (p *Peer) handleRemoteVersionMsg(msg *wire.MsgVersion) error {
// Updating a bunch of stats including block based stats, and the // Updating a bunch of stats including block based stats, and the
// peer's time offset. // peer's time offset.
p.statsMtx.Lock() p.statsMtx.Lock()
p.selectedTip = msg.SelectedTip p.selectedTipHash = msg.SelectedTipHash
p.timeOffset = msg.Timestamp.Unix() - time.Now().Unix() p.timeOffset = msg.Timestamp.Unix() - time.Now().Unix()
p.statsMtx.Unlock() p.statsMtx.Unlock()
@ -1172,6 +1186,10 @@ func (p *Peer) maybeAddDeadline(pendingResponses map[string]time.Time, msgCmd st
// headers. // headers.
deadline = time.Now().Add(stallResponseTimeout * 3) deadline = time.Now().Add(stallResponseTimeout * 3)
pendingResponses[wire.CmdHeaders] = deadline pendingResponses[wire.CmdHeaders] = deadline
case wire.CmdGetSelectedTip:
// Expects a selected tip message.
pendingResponses[wire.CmdSelectedTip] = deadline
} }
} }
@ -1500,6 +1518,16 @@ out:
p.cfg.Listeners.OnSendHeaders(p, msg) p.cfg.Listeners.OnSendHeaders(p, msg)
} }
case *wire.MsgGetSelectedTip:
if p.cfg.Listeners.OnGetSelectedTip != nil {
p.cfg.Listeners.OnGetSelectedTip()
}
case *wire.MsgSelectedTip:
if p.cfg.Listeners.OnSelectedTip != nil {
p.cfg.Listeners.OnSelectedTip(p, msg)
}
default: default:
log.Debugf("Received unhandled message of type %s "+ log.Debugf("Received unhandled message of type %s "+
"from %s", rmsg.Command(), p) "from %s", rmsg.Command(), p)

View File

@ -218,7 +218,7 @@ func TestPeerConnection(t *testing.T) {
DAGParams: &dagconfig.MainnetParams, DAGParams: &dagconfig.MainnetParams,
ProtocolVersion: wire.ProtocolVersion, // Configure with older version ProtocolVersion: wire.ProtocolVersion, // Configure with older version
Services: 0, Services: 0,
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
peer2Cfg := &peer.Config{ peer2Cfg := &peer.Config{
Listeners: peer1Cfg.Listeners, Listeners: peer1Cfg.Listeners,
@ -228,7 +228,7 @@ func TestPeerConnection(t *testing.T) {
DAGParams: &dagconfig.MainnetParams, DAGParams: &dagconfig.MainnetParams,
ProtocolVersion: wire.ProtocolVersion + 1, ProtocolVersion: wire.ProtocolVersion + 1,
Services: wire.SFNodeNetwork, Services: wire.SFNodeNetwork,
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
wantStats1 := peerStats{ wantStats1 := peerStats{
@ -403,7 +403,7 @@ func TestPeerListeners(t *testing.T) {
UserAgentComments: []string{"comment"}, UserAgentComments: []string{"comment"},
DAGParams: &dagconfig.MainnetParams, DAGParams: &dagconfig.MainnetParams,
Services: wire.SFNodeBloom, Services: wire.SFNodeBloom,
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
inConn, outConn := pipe( inConn, outConn := pipe(
&conn{raddr: "10.0.0.1:16111"}, &conn{raddr: "10.0.0.1:16111"},
@ -528,7 +528,7 @@ func TestPeerListeners(t *testing.T) {
// TestOutboundPeer tests that the outbound peer works as expected. // TestOutboundPeer tests that the outbound peer works as expected.
func TestOutboundPeer(t *testing.T) { func TestOutboundPeer(t *testing.T) {
peerCfg := &peer.Config{ peerCfg := &peer.Config{
SelectedTip: func() *daghash.Hash { SelectedTipHash: func() *daghash.Hash {
return &daghash.ZeroHash return &daghash.ZeroHash
}, },
UserAgentName: "peer", UserAgentName: "peer",
@ -568,8 +568,8 @@ func TestOutboundPeer(t *testing.T) {
<-done <-done
p.Disconnect() p.Disconnect()
// Test SelectedTip // Test SelectedTipHashAndBlueScore
var selectedTip = func() *daghash.Hash { var selectedTipHash = func() *daghash.Hash {
hashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" hashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef"
hash, err := daghash.NewHashFromStr(hashStr) hash, err := daghash.NewHashFromStr(hashStr)
if err != nil { if err != nil {
@ -578,7 +578,7 @@ func TestOutboundPeer(t *testing.T) {
return hash return hash
} }
peerCfg.SelectedTip = selectedTip peerCfg.SelectedTipHash = selectedTipHash
r1, w1 := io.Pipe() r1, w1 := io.Pipe()
c1 := &conn{raddr: "10.0.0.1:16111", Writer: w1, Reader: r1} c1 := &conn{raddr: "10.0.0.1:16111", Writer: w1, Reader: r1}
p1, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:16111") p1, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:16111")
@ -645,7 +645,7 @@ func TestUnsupportedVersionPeer(t *testing.T) {
UserAgentComments: []string{"comment"}, UserAgentComments: []string{"comment"},
DAGParams: &dagconfig.MainnetParams, DAGParams: &dagconfig.MainnetParams,
Services: 0, Services: 0,
SelectedTip: fakeSelectedTipFn, SelectedTipHash: fakeSelectedTipFn,
} }
localNA := wire.NewNetAddressIPPort( localNA := wire.NewNetAddressIPPort(

View File

@ -33,7 +33,7 @@ func (sp *Peer) OnBlockLocator(_ *peer.Peer, msg *wire.MsgBlockLocator) {
// This is not a mistake. The invs we desire start from the highest // This is not a mistake. The invs we desire start from the highest
// hash that we know of and end at the highest hash that the peer // hash that we know of and end at the highest hash that the peer
// knows of. // knows of.
err := sp.Peer.PushGetBlockInvsMsg(highHash, sp.Peer.SelectedTip()) err := sp.Peer.PushGetBlockInvsMsg(highHash, sp.Peer.SelectedTipHash())
if err != nil { if err != nil {
peerLog.Errorf("Failed pushing get blocks message for peer %s: %s", peerLog.Errorf("Failed pushing get blocks message for peer %s: %s",
sp, err) sp, err)

View File

@ -0,0 +1,11 @@
package p2p
import (
"github.com/kaspanet/kaspad/wire"
)
// OnGetSelectedTip is invoked when a peer receives a getSelectedTip kaspa
// message.
func (sp *Peer) OnGetSelectedTip() {
sp.QueueMessage(wire.NewMsgSelectedTip(sp.selectedTipHash()), nil)
}

View File

@ -0,0 +1,14 @@
package p2p
import (
"github.com/kaspanet/kaspad/peer"
"github.com/kaspanet/kaspad/wire"
)
// OnSelectedTip is invoked when a peer receives a selectedTip kaspa
// message.
func (sp *Peer) OnSelectedTip(peer *peer.Peer, msg *wire.MsgSelectedTip) {
done := make(chan struct{})
sp.server.SyncManager.QueueSelectedTipMsg(msg, peer, done)
<-done
}

View File

@ -296,8 +296,8 @@ func newServerPeer(s *Server, isPersistent bool) *Peer {
} }
} }
// selectedTip returns the current selected tip // selectedTipHash returns the current selected tip hash
func (sp *Peer) selectedTip() *daghash.Hash { func (sp *Peer) selectedTipHash() *daghash.Hash {
return sp.server.DAG.SelectedTipHash() return sp.server.DAG.SelectedTipHash()
} }
@ -894,7 +894,7 @@ func (s *Server) handleQuery(state *peerState, querymsg interface{}) {
shouldMineOnGenesis := true shouldMineOnGenesis := true
if state.Count() != 0 { if state.Count() != 0 {
shouldMineOnGenesis = state.forAllPeers(func(sp *Peer) bool { shouldMineOnGenesis = state.forAllPeers(func(sp *Peer) bool {
if !sp.SelectedTip().IsEqual(s.DAGParams.GenesisHash) { if !sp.SelectedTipHash().IsEqual(s.DAGParams.GenesisHash) {
return false return false
} }
return true return true
@ -1046,10 +1046,12 @@ func newPeerConfig(sp *Peer) *peer.Config {
OnFilterLoad: sp.OnFilterLoad, OnFilterLoad: sp.OnFilterLoad,
OnGetAddr: sp.OnGetAddr, OnGetAddr: sp.OnGetAddr,
OnAddr: sp.OnAddr, OnAddr: sp.OnAddr,
OnGetSelectedTip: sp.OnGetSelectedTip,
OnSelectedTip: sp.OnSelectedTip,
OnRead: sp.OnRead, OnRead: sp.OnRead,
OnWrite: sp.OnWrite, OnWrite: sp.OnWrite,
}, },
SelectedTip: sp.selectedTip, SelectedTipHash: sp.selectedTipHash,
BlockExists: sp.blockExists, BlockExists: sp.blockExists,
HostToNetAddress: sp.server.addrManager.HostToNetAddress, HostToNetAddress: sp.server.addrManager.HostToNetAddress,
Proxy: config.ActiveConfig().Proxy, Proxy: config.ActiveConfig().Proxy,

View File

@ -28,7 +28,7 @@ func handleGetPeerInfo(s *Server, cmd interface{}, closeChan <-chan struct{}) (i
Version: statsSnap.Version, Version: statsSnap.Version,
SubVer: statsSnap.UserAgent, SubVer: statsSnap.UserAgent,
Inbound: statsSnap.Inbound, Inbound: statsSnap.Inbound,
SelectedTip: statsSnap.SelectedTip.String(), SelectedTip: statsSnap.SelectedTipHash.String(),
BanScore: int32(p.BanScore()), BanScore: int32(p.BanScore()),
FeeFilter: p.FeeFilter(), FeeFilter: p.FeeFilter(),
SyncNode: statsSnap.ID == syncPeerID, SyncNode: statsSnap.ID == syncPeerID,

View File

@ -364,7 +364,7 @@ var helpDescsEnUS = map[string]string{
// GetChainFromBlockResult help. // GetChainFromBlockResult help.
"getChainFromBlockResult-removedChainBlockHashes": "List chain-block hashes that were removed from the selected parent chain in top-to-bottom order", "getChainFromBlockResult-removedChainBlockHashes": "List chain-block hashes that were removed from the selected parent chain in top-to-bottom order",
"getChainFromBlockResult-addedChainBlocks": "List of ChainBlocks from Virtual.SelectedTip to StartHash (excluding StartHash) ordered bottom-to-top.", "getChainFromBlockResult-addedChainBlocks": "List of ChainBlocks from Virtual.SelectedTipHashAndBlueScore to StartHash (excluding StartHash) ordered bottom-to-top.",
"getChainFromBlockResult-blocks": "If includeBlocks=true - contains the contents of all chain and accepted blocks in the AddedChainBlocks. Otherwise - omitted.", "getChainFromBlockResult-blocks": "If includeBlocks=true - contains the contents of all chain and accepted blocks in the AddedChainBlocks. Otherwise - omitted.",
// GetConnectionCountCmd help. // GetConnectionCountCmd help.

View File

@ -52,6 +52,8 @@ const (
CmdFeeFilter = "feefilter" CmdFeeFilter = "feefilter"
CmdGetBlockLocator = "getlocator" CmdGetBlockLocator = "getlocator"
CmdBlockLocator = "locator" CmdBlockLocator = "locator"
CmdSelectedTip = "selectedtip"
CmdGetSelectedTip = "getseltip"
) )
// Message is an interface that describes a kaspa message. A type that // Message is an interface that describes a kaspa message. A type that
@ -139,6 +141,12 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdFeeFilter: case CmdFeeFilter:
msg = &MsgFeeFilter{} msg = &MsgFeeFilter{}
case CmdGetSelectedTip:
msg = &MsgGetSelectedTip{}
case CmdSelectedTip:
msg = &MsgSelectedTip{}
default: default:
return nil, errors.Errorf("unhandled command [%s]", command) return nil, errors.Errorf("unhandled command [%s]", command)
} }

View File

@ -9,8 +9,6 @@ import (
// MsgGetBlockLocator implements the Message interface and represents a kaspa // MsgGetBlockLocator implements the Message interface and represents a kaspa
// getlocator message. It is used to request a block locator between start and stop hash. // getlocator message. It is used to request a block locator between start and stop hash.
// The locator is returned via a locator message (MsgBlockLocator). // The locator is returned via a locator message (MsgBlockLocator).
//
// This message has no payload.
type MsgGetBlockLocator struct { type MsgGetBlockLocator struct {
HighHash *daghash.Hash HighHash *daghash.Hash
LowHash *daghash.Hash LowHash *daghash.Hash

41
wire/msggetselectedtip.go Normal file
View File

@ -0,0 +1,41 @@
package wire
import (
"io"
)
// MsgGetSelectedTip implements the Message interface and represents a kaspa
// getseltip message. It is used to request the selected tip of another peer.
//
// This message has no payload.
type MsgGetSelectedTip struct{}
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgGetSelectedTip) KaspaDecode(r io.Reader, pver uint32) error {
return nil
}
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetSelectedTip) KaspaEncode(w io.Writer, pver uint32) error {
return nil
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetSelectedTip) Command() string {
return CmdGetSelectedTip
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetSelectedTip) MaxPayloadLength(pver uint32) uint32 {
return 0
}
// NewMsgGetSelectedTip returns a new kaspa getseltip message that conforms to the
// Message interface.
func NewMsgGetSelectedTip() *MsgGetSelectedTip {
return &MsgGetSelectedTip{}
}

View File

@ -0,0 +1,87 @@
// Copyright (c) 2013-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
import (
"bytes"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestGetSelectedTip tests the MsgGetSelectedTip API.
func TestGetSelectedTip(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "getseltip"
msg := NewMsgGetSelectedTip()
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgGetSelectedTip: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
wantPayload := uint32(0)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
}
// TestGetSelectedTipWire tests the MsgGetSelectedTip wire encode and decode for various
// protocol versions.
func TestGetSelectedTipWire(t *testing.T) {
msgGetSelectedTip := NewMsgGetSelectedTip()
msgGetSelectedTipEncoded := []byte{}
tests := []struct {
in *MsgGetSelectedTip // Message to encode
out *MsgGetSelectedTip // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
msgGetSelectedTip,
msgGetSelectedTip,
msgGetSelectedTipEncoded,
ProtocolVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.KaspaEncode(&buf, test.pver)
if err != nil {
t.Errorf("KaspaEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgGetSelectedTip
rbuf := bytes.NewReader(test.buf)
err = msg.KaspaDecode(rbuf, test.pver)
if err != nil {
t.Errorf("KaspaDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}

53
wire/msgselectedtip.go Normal file
View File

@ -0,0 +1,53 @@
package wire
import (
"github.com/kaspanet/kaspad/util/daghash"
"io"
)
// MsgSelectedTip implements the Message interface and represents a kaspa
// selectedtip message. It is used to answer getseltip messages and tell
// the asking peer what is the selected tip of this peer.
type MsgSelectedTip struct {
// The selected tip hash of the generator of the message.
SelectedTipHash *daghash.Hash
}
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
// This is part of the Message interface implementation.
func (msg *MsgSelectedTip) KaspaDecode(r io.Reader, pver uint32) error {
msg.SelectedTipHash = &daghash.Hash{}
err := ReadElement(r, msg.SelectedTipHash)
if err != nil {
return err
}
return nil
}
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgSelectedTip) KaspaEncode(w io.Writer, pver uint32) error {
return WriteElement(w, msg.SelectedTipHash)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgSelectedTip) Command() string {
return CmdSelectedTip
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgSelectedTip) MaxPayloadLength(_ uint32) uint32 {
// selected tip hash 32 bytes
return daghash.HashSize
}
// NewMsgSelectedTip returns a new kaspa selectedtip message that conforms to the
// Message interface.
func NewMsgSelectedTip(selectedTipHash *daghash.Hash) *MsgSelectedTip {
return &MsgSelectedTip{
SelectedTipHash: selectedTipHash,
}
}

View File

@ -0,0 +1,90 @@
package wire
import (
"bytes"
"github.com/kaspanet/kaspad/util/daghash"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// TestSelectedTip tests the MsgSelectedTip API.
func TestSelectedTip(t *testing.T) {
pver := ProtocolVersion
// Ensure the command is expected value.
wantCmd := "selectedtip"
msg := NewMsgSelectedTip(&daghash.ZeroHash)
if cmd := msg.Command(); cmd != wantCmd {
t.Errorf("NewMsgSelectedTip: wrong command - got %v want %v",
cmd, wantCmd)
}
// Ensure max payload is expected value.
wantPayload := uint32(32)
maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+
"protocol version %d - got %v, want %v", pver,
maxPayload, wantPayload)
}
}
// TestSelectedTipWire tests the MsgSelectedTip wire encode and decode for various
// protocol versions.
func TestSelectedTipWire(t *testing.T) {
hash := &daghash.Hash{1, 2, 3}
msgSelectedTip := NewMsgSelectedTip(hash)
msgSelectedTipEncoded := []byte{
0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}
tests := []struct {
in *MsgSelectedTip // Message to encode
out *MsgSelectedTip // Expected decoded message
buf []byte // Wire encoding
pver uint32 // Protocol version for wire encoding
}{
// Latest protocol version.
{
msgSelectedTip,
msgSelectedTip,
msgSelectedTipEncoded,
ProtocolVersion,
},
}
t.Logf("Running %d tests", len(tests))
for i, test := range tests {
// Encode the message to wire format.
var buf bytes.Buffer
err := test.in.KaspaEncode(&buf, test.pver)
if err != nil {
t.Errorf("KaspaEncode #%d error %v", i, err)
continue
}
if !bytes.Equal(buf.Bytes(), test.buf) {
t.Errorf("KaspaEncode #%d\n got: %s want: %s", i,
spew.Sdump(buf.Bytes()), spew.Sdump(test.buf))
continue
}
// Decode the message from wire format.
var msg MsgSelectedTip
rbuf := bytes.NewReader(test.buf)
err = msg.KaspaDecode(rbuf, test.pver)
if err != nil {
t.Errorf("KaspaDecode #%d error %v", i, err)
continue
}
if !reflect.DeepEqual(&msg, test.out) {
t.Errorf("KaspaDecode #%d\n got: %s want: %s", i,
spew.Sdump(msg), spew.Sdump(test.out))
continue
}
}
}

View File

@ -55,8 +55,8 @@ type MsgVersion struct {
// on the wire. This has a max length of MaxUserAgentLen. // on the wire. This has a max length of MaxUserAgentLen.
UserAgent string UserAgent string
// The selected tip of the generator of the version message. // The selected tip hash of the generator of the version message.
SelectedTip *daghash.Hash SelectedTipHash *daghash.Hash
// Don't announce transactions to peer. // Don't announce transactions to peer.
DisableRelayTx bool DisableRelayTx bool
@ -137,8 +137,8 @@ func (msg *MsgVersion) KaspaDecode(r io.Reader, pver uint32) error {
} }
msg.UserAgent = userAgent msg.UserAgent = userAgent
msg.SelectedTip = &daghash.Hash{} msg.SelectedTipHash = &daghash.Hash{}
err = ReadElement(buf, msg.SelectedTip) err = ReadElement(buf, msg.SelectedTipHash)
if err != nil { if err != nil {
return err return err
} }
@ -200,7 +200,7 @@ func (msg *MsgVersion) KaspaEncode(w io.Writer, pver uint32) error {
return err return err
} }
err = WriteElement(w, msg.SelectedTip) err = WriteElement(w, msg.SelectedTipHash)
if err != nil { if err != nil {
return err return err
} }
@ -223,21 +223,19 @@ func (msg *MsgVersion) Command() string {
// MaxPayloadLength returns the maximum length the payload can be for the // MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation. // receiver. This is part of the Message interface implementation.
func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 { func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
// XXX: <= 106 different
// Protocol version 4 bytes + services 8 bytes + timestamp 16 bytes + // Protocol version 4 bytes + services 8 bytes + timestamp 16 bytes +
// remote and local net addresses + nonce 8 bytes + length of user // remote and local net addresses + nonce 8 bytes + length of user
// agent (varInt) + max allowed useragent length + last block 4 bytes + // agent (varInt) + max allowed useragent length + selected tip hash length +
// relay transactions flag 1 byte. // relay transactions flag 1 byte.
return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload + return 29 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload +
MaxUserAgentLen MaxUserAgentLen + daghash.HashSize
} }
// NewMsgVersion returns a new kaspa version message that conforms to the // NewMsgVersion returns a new kaspa version message that conforms to the
// Message interface using the passed parameters and defaults for the remaining // Message interface using the passed parameters and defaults for the remaining
// fields. // fields.
func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64, func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
selectedTip *daghash.Hash, subnetworkID *subnetworkid.SubnetworkID) *MsgVersion { selectedTipHash *daghash.Hash, subnetworkID *subnetworkid.SubnetworkID) *MsgVersion {
// Limit the timestamp to one second precision since the protocol // Limit the timestamp to one second precision since the protocol
// doesn't support better. // doesn't support better.
@ -249,7 +247,7 @@ func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64,
AddrMe: *me, AddrMe: *me,
Nonce: nonce, Nonce: nonce,
UserAgent: DefaultUserAgent, UserAgent: DefaultUserAgent,
SelectedTip: selectedTip, SelectedTipHash: selectedTipHash,
DisableRelayTx: false, DisableRelayTx: false,
SubnetworkID: subnetworkID, SubnetworkID: subnetworkID,
} }

View File

@ -23,7 +23,7 @@ func TestVersion(t *testing.T) {
pver := ProtocolVersion pver := ProtocolVersion
// Create version message data. // Create version message data.
selectedTip := &daghash.Hash{12, 34} selectedTipHash := &daghash.Hash{12, 34}
tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111} tcpAddrMe := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 16111}
me := NewNetAddress(tcpAddrMe, SFNodeNetwork) me := NewNetAddress(tcpAddrMe, SFNodeNetwork)
tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 16111} tcpAddrYou := &net.TCPAddr{IP: net.ParseIP("192.168.0.1"), Port: 16111}
@ -34,7 +34,7 @@ func TestVersion(t *testing.T) {
} }
// Ensure we get the correct data back out. // Ensure we get the correct data back out.
msg := NewMsgVersion(me, you, nonce, selectedTip, nil) msg := NewMsgVersion(me, you, nonce, selectedTipHash, nil)
if msg.ProtocolVersion != int32(pver) { if msg.ProtocolVersion != int32(pver) {
t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v", t.Errorf("NewMsgVersion: wrong protocol version - got %v, want %v",
msg.ProtocolVersion, pver) msg.ProtocolVersion, pver)
@ -55,9 +55,9 @@ func TestVersion(t *testing.T) {
t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v", t.Errorf("NewMsgVersion: wrong user agent - got %v, want %v",
msg.UserAgent, DefaultUserAgent) msg.UserAgent, DefaultUserAgent)
} }
if !msg.SelectedTip.IsEqual(selectedTip) { if !msg.SelectedTipHash.IsEqual(selectedTipHash) {
t.Errorf("NewMsgVersion: wrong selected tip - got %s, want %s", t.Errorf("NewMsgVersion: wrong selected tip hash - got %s, want %s",
msg.SelectedTip, selectedTip) msg.SelectedTipHash, selectedTipHash)
} }
if msg.DisableRelayTx { if msg.DisableRelayTx {
t.Errorf("NewMsgVersion: disable relay tx is not false by "+ t.Errorf("NewMsgVersion: disable relay tx is not false by "+
@ -106,10 +106,10 @@ func TestVersion(t *testing.T) {
// Ensure max payload is expected value. // Ensure max payload is expected value.
// Protocol version 4 bytes + services 8 bytes + timestamp 16 bytes + // Protocol version 4 bytes + services 8 bytes + timestamp 16 bytes +
// remote and local net addresses + nonce 8 bytes + length of user agent // remote and local net addresses + nonce 8 bytes + length of user
// (varInt) + max allowed user agent length + last block 4 bytes + // agent (varInt) + max allowed useragent length + selected tip hash length +
// relay transactions flag 1 byte. // relay transactions flag 1 byte.
wantPayload := uint32(366) wantPayload := uint32(394)
maxPayload := msg.MaxPayloadLength(pver) maxPayload := msg.MaxPayloadLength(pver)
if maxPayload != wantPayload { if maxPayload != wantPayload {
t.Errorf("MaxPayloadLength: wrong max payload length for "+ t.Errorf("MaxPayloadLength: wrong max payload length for "+
@ -323,7 +323,7 @@ var baseVersion = &MsgVersion{
}, },
Nonce: 123123, // 0x1e0f3 Nonce: 123123, // 0x1e0f3
UserAgent: "/kaspadtest:0.0.1/", UserAgent: "/kaspadtest:0.0.1/",
SelectedTip: &daghash.Hash{0x12, 0x34}, SelectedTipHash: &daghash.Hash{0x12, 0x34},
} }
// baseVersionEncoded is the wire encoded bytes for baseVersion using protocol // baseVersionEncoded is the wire encoded bytes for baseVersion using protocol
@ -373,7 +373,7 @@ var baseVersionWithRelayTx = &MsgVersion{
}, },
Nonce: 123123, // 0x1e0f3 Nonce: 123123, // 0x1e0f3
UserAgent: "/kaspadtest:0.0.1/", UserAgent: "/kaspadtest:0.0.1/",
SelectedTip: &daghash.Hash{0x12, 0x34}, SelectedTipHash: &daghash.Hash{0x12, 0x34},
} }
// baseVersionWithRelayTxEncoded is the wire encoded bytes for // baseVersionWithRelayTxEncoded is the wire encoded bytes for