mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-06-25 07:22:31 +00:00
[NOD-1125] Implement the IBD flow (#800)
* [NOD-1125] Write a skeleton for starting IBD. * [NOD-1125] Add WaitForIBDStart to Peer. * [NOD-1125] Move functions around. * [NOD-1125] Fix merge errors. * [NOD-1125] Fix a comment. * [NOD-1125] Implement sendGetBlockLocator. * [NOD-1125] Begin implementing findIBDLowHash. * [NOD-1125] Finish implementing findIBDLowHash. * [NOD-1125] Rename findIBDLowHash to findHighestSharedBlockHash. * [NOD-1125] Implement downloadBlocks. * [NOD-1125] Implement msgIBDBlock. * [NOD-1125] Implement msgIBDBlock. * [NOD-1125] Fix message types for HandleIBD. * [NOD-1125] Write a skeleton for requesting selected tip hashes. * [NOD-1125] Write a skeleton for the rest of the IBD requests. * [NOD-1125] Implement HandleGetBlockLocator. * [NOD-1125] Fix wrong timeout. * [NOD-1125] Fix compilation error. * [NOD-1125] Implement HandleGetBlocks. * [NOD-1125] Fix compilation errors. * [NOD-1125] Fix merge errors. * [NOD-1125] Implement selectPeerForIBD. * [NOD-1125] Implement RequestSelectedTip. * [NOD-1125] Implement HandleGetSelectedTip. * [NOD-1125] Make go lint happy. * [NOD-1125] Add minGetSelectedTipInterval. * [NOD-1125] Call StartIBDIfRequired where needed. * [NOD-1125] Fix merge errors. * [NOD-1125] Remove a redundant line. * [NOD-1125] Rename shouldContinue to shouldStop. * [NOD-1125] Lowercasify an error message. * [NOD-1125] Shuffle statements around in findHighestSharedBlockHash. * [NOD-1125] Rename hasRecentlyReceivedBlock to isDAGTimeCurrent. * [NOD-1125] Scope minGetSelectedTipInterval. * [NOD-1125] Handle an unhandled error. * [NOD-1125] Use AddUint32 instead of LoadUint32 + StoreUint32. * [NOD-1125] Use AddUint32 instead of LoadUint32 + StoreUint32. * [NOD-1125] Use SwapUint32 instead of AddUint32. * [NOD-1125] Remove error from requestSelectedTips. * [NOD-1125] Actually stop IBD when it should stop. * [NOD-1125] Actually stop RequestSelectedTip when it should stop. * [NOD-1125] Don't ban peers that send us delayed blocks during IBD. * [NOD-1125] Make unexpected message type messages nicer. * [NOD-1125] Remove Peer.ready and make HandleHandshake return it to guarantee we never operate on a non-initialized peer. * [NOD-1125] Remove errors associated with Peer.ready. * [NOD-1125] Extract maxHashesInMsgIBDBlocks to a const. * [NOD-1125] Move the ibd package into flows. * [NOD-1125] Start IBD if required after getting an unknown block inv. * [NOD-1125] Don't request blocks during relay if we're in the middle of IBD. * [NOD-1125] Remove AddBlockLocatorHash. * [NOD-1125] Extract runIBD to a seperate function. * [NOD-1125] Extract runSelectedTipRequest to a seperate function. * [NOD-1125] Remove EnqueueWithTimeout. * [NOD-1125] Increase the capacity of the outgoingRoute. * [NOD-1125] Fix some bad names. * [NOD-1125] Fix a comment. * [NOD-1125] Simplify a comment. * [NOD-1125] Move WaitFor... functions into their respective run... functions. * [NOD-1125] Return default values in case of error. * [NOD-1125] Use CmdXXX in error messages. * [NOD-1125] Use MaxInvPerMsg in outgoingRouteMaxMessages instead of MaxBlockLocatorsPerMsg. * [NOD-1125] Fix a comment. * [NOD-1125] Disconnect a peer that sends us a delayed block during IBD. * [NOD-1125] Use StoreUint32 instead of SwapUint32. * [NOD-1125] Add a comment. * [NOD-1125] Don't ban peers that send us delayed blocks.
This commit is contained in:
parent
aa5bc34280
commit
4773f87875
@ -53,6 +53,7 @@ var (
|
|||||||
ntarLog = BackendLog.Logger("NTAR")
|
ntarLog = BackendLog.Logger("NTAR")
|
||||||
dnssLog = BackendLog.Logger("DNSS")
|
dnssLog = BackendLog.Logger("DNSS")
|
||||||
snvrLog = BackendLog.Logger("SNVR")
|
snvrLog = BackendLog.Logger("SNVR")
|
||||||
|
ibdsLog = BackendLog.Logger("IBDS")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SubsystemTags is an enum of all sub system tags
|
// SubsystemTags is an enum of all sub system tags
|
||||||
@ -81,7 +82,8 @@ var SubsystemTags = struct {
|
|||||||
P2PS,
|
P2PS,
|
||||||
NTAR,
|
NTAR,
|
||||||
DNSS,
|
DNSS,
|
||||||
SNVR string
|
SNVR,
|
||||||
|
IBDS string
|
||||||
}{
|
}{
|
||||||
ADXR: "ADXR",
|
ADXR: "ADXR",
|
||||||
AMGR: "AMGR",
|
AMGR: "AMGR",
|
||||||
@ -108,6 +110,7 @@ var SubsystemTags = struct {
|
|||||||
NTAR: "NTAR",
|
NTAR: "NTAR",
|
||||||
DNSS: "DNSS",
|
DNSS: "DNSS",
|
||||||
SNVR: "SNVR",
|
SNVR: "SNVR",
|
||||||
|
IBDS: "IBDS",
|
||||||
}
|
}
|
||||||
|
|
||||||
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
// subsystemLoggers maps each subsystem identifier to its associated logger.
|
||||||
@ -137,6 +140,7 @@ var subsystemLoggers = map[string]*logs.Logger{
|
|||||||
SubsystemTags.NTAR: ntarLog,
|
SubsystemTags.NTAR: ntarLog,
|
||||||
SubsystemTags.DNSS: dnssLog,
|
SubsystemTags.DNSS: dnssLog,
|
||||||
SubsystemTags.SNVR: snvrLog,
|
SubsystemTags.SNVR: snvrLog,
|
||||||
|
SubsystemTags.IBDS: ibdsLog,
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitLog attaches log file and error log file to the backend log.
|
// InitLog attaches log file and error log file to the backend log.
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxMessages = 100
|
defaultMaxMessages = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrTimeout signifies that one of the router functions had a timeout.
|
// ErrTimeout signifies that one of the router functions had a timeout.
|
||||||
@ -32,8 +32,12 @@ type Route struct {
|
|||||||
|
|
||||||
// NewRoute create a new Route
|
// NewRoute create a new Route
|
||||||
func NewRoute() *Route {
|
func NewRoute() *Route {
|
||||||
|
return newRouteWithCapacity(defaultMaxMessages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRouteWithCapacity(capacity int) *Route {
|
||||||
return &Route{
|
return &Route{
|
||||||
channel: make(chan wire.Message, maxMessages),
|
channel: make(chan wire.Message, capacity),
|
||||||
closed: false,
|
closed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,33 +50,13 @@ func (r *Route) Enqueue(message wire.Message) (isOpen bool) {
|
|||||||
if r.closed {
|
if r.closed {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if len(r.channel) == maxMessages {
|
if len(r.channel) == defaultMaxMessages {
|
||||||
r.onCapacityReachedHandler()
|
r.onCapacityReachedHandler()
|
||||||
}
|
}
|
||||||
r.channel <- message
|
r.channel <- message
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnqueueWithTimeout attempts to enqueue a message to the Route
|
|
||||||
// and returns an error if the given timeout expires first.
|
|
||||||
func (r *Route) EnqueueWithTimeout(message wire.Message, timeout time.Duration) (isOpen bool, err error) {
|
|
||||||
r.closeLock.Lock()
|
|
||||||
defer r.closeLock.Unlock()
|
|
||||||
|
|
||||||
if r.closed {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if len(r.channel) == maxMessages {
|
|
||||||
r.onCapacityReachedHandler()
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-time.After(timeout):
|
|
||||||
return false, errors.Wrapf(ErrTimeout, "got timeout after %s", timeout)
|
|
||||||
case r.channel <- message:
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dequeue dequeues a message from the Route
|
// Dequeue dequeues a message from the Route
|
||||||
func (r *Route) Dequeue() (message wire.Message, isOpen bool) {
|
func (r *Route) Dequeue() (message wire.Message, isOpen bool) {
|
||||||
message, isOpen = <-r.channel
|
message, isOpen = <-r.channel
|
||||||
|
@ -5,6 +5,8 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const outgoingRouteMaxMessages = wire.MaxInvPerMsg + defaultMaxMessages
|
||||||
|
|
||||||
// OnRouteCapacityReachedHandler is a function that is to
|
// OnRouteCapacityReachedHandler is a function that is to
|
||||||
// be called when one of the routes reaches capacity.
|
// be called when one of the routes reaches capacity.
|
||||||
type OnRouteCapacityReachedHandler func()
|
type OnRouteCapacityReachedHandler func()
|
||||||
@ -22,7 +24,7 @@ type Router struct {
|
|||||||
func NewRouter() *Router {
|
func NewRouter() *Router {
|
||||||
router := Router{
|
router := Router{
|
||||||
incomingRoutes: make(map[wire.MessageCommand]*Route),
|
incomingRoutes: make(map[wire.MessageCommand]*Route),
|
||||||
outgoingRoute: NewRoute(),
|
outgoingRoute: newRouteWithCapacity(outgoingRouteMaxMessages),
|
||||||
}
|
}
|
||||||
router.outgoingRoute.setOnCapacityReachedHandler(func() {
|
router.outgoingRoute.setOnCapacityReachedHandler(func() {
|
||||||
router.onRouteCapacityReachedHandler()
|
router.onRouteCapacityReachedHandler()
|
||||||
|
@ -131,7 +131,7 @@ func messageSummary(msg wire.Message) string {
|
|||||||
case *wire.MsgGetData:
|
case *wire.MsgGetData:
|
||||||
return invSummary(msg.InvList)
|
return invSummary(msg.InvList)
|
||||||
|
|
||||||
case *wire.MsgGetBlockInvs:
|
case *wire.MsgGetBlocks:
|
||||||
return fmt.Sprintf("low hash %s, high hash %s", msg.LowHash,
|
return fmt.Sprintf("low hash %s, high hash %s", msg.LowHash,
|
||||||
msg.HighHash)
|
msg.HighHash)
|
||||||
|
|
||||||
|
16
peer/peer.go
16
peer/peer.go
@ -138,7 +138,7 @@ type MessageListeners struct {
|
|||||||
|
|
||||||
// OnGetBlockInvs is invoked when a peer receives a getblockinvs kaspa
|
// OnGetBlockInvs is invoked when a peer receives a getblockinvs kaspa
|
||||||
// message.
|
// message.
|
||||||
OnGetBlockInvs func(p *Peer, msg *wire.MsgGetBlockInvs)
|
OnGetBlockInvs func(p *Peer, msg *wire.MsgGetBlocks)
|
||||||
|
|
||||||
// OnFeeFilter is invoked when a peer receives a feefilter kaspa message.
|
// OnFeeFilter is invoked when a peer receives a feefilter kaspa message.
|
||||||
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
|
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
|
||||||
@ -803,7 +803,7 @@ func (p *Peer) PushGetBlockInvsMsg(lowHash, highHash *daghash.Hash) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the getblockinvs request and queue it to be sent.
|
// Construct the getblockinvs request and queue it to be sent.
|
||||||
msg := wire.NewMsgGetBlockInvs(lowHash, highHash)
|
msg := wire.NewMsgGetBlocks(lowHash, highHash)
|
||||||
p.QueueMessage(msg, nil)
|
p.QueueMessage(msg, nil)
|
||||||
|
|
||||||
// Update the previous getblockinvs request information for filtering
|
// Update the previous getblockinvs request information for filtering
|
||||||
@ -820,13 +820,7 @@ func (p *Peer) PushGetBlockInvsMsg(lowHash, highHash *daghash.Hash) error {
|
|||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
func (p *Peer) PushBlockLocatorMsg(locator blockdag.BlockLocator) error {
|
func (p *Peer) PushBlockLocatorMsg(locator blockdag.BlockLocator) error {
|
||||||
// Construct the locator request and queue it to be sent.
|
// Construct the locator request and queue it to be sent.
|
||||||
msg := wire.NewMsgBlockLocator()
|
msg := wire.NewMsgBlockLocator(locator)
|
||||||
for _, hash := range locator {
|
|
||||||
err := msg.AddBlockLocatorHash(hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.QueueMessage(msg, nil)
|
p.QueueMessage(msg, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -1107,7 +1101,7 @@ func (p *Peer) maybeAddDeadline(pendingResponses map[wire.MessageCommand]time.Ti
|
|||||||
// Expects a verack message.
|
// Expects a verack message.
|
||||||
pendingResponses[wire.CmdVerAck] = deadline
|
pendingResponses[wire.CmdVerAck] = deadline
|
||||||
|
|
||||||
case wire.CmdGetBlockInvs:
|
case wire.CmdGetBlocks:
|
||||||
// Expects an inv message.
|
// Expects an inv message.
|
||||||
pendingResponses[wire.CmdInv] = deadline
|
pendingResponses[wire.CmdInv] = deadline
|
||||||
|
|
||||||
@ -1401,7 +1395,7 @@ out:
|
|||||||
p.cfg.Listeners.OnBlockLocator(p, msg)
|
p.cfg.Listeners.OnBlockLocator(p, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *wire.MsgGetBlockInvs:
|
case *wire.MsgGetBlocks:
|
||||||
if p.cfg.Listeners.OnGetBlockInvs != nil {
|
if p.cfg.Listeners.OnGetBlockInvs != nil {
|
||||||
p.cfg.Listeners.OnGetBlockInvs(p, msg)
|
p.cfg.Listeners.OnGetBlockInvs(p, msg)
|
||||||
}
|
}
|
||||||
|
7
protocol/common/common.go
Normal file
7
protocol/common/common.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// DefaultTimeout is the default duration to wait for enqueuing/dequeuing
|
||||||
|
// to/from routes.
|
||||||
|
const DefaultTimeout = 30 * time.Second
|
@ -17,20 +17,13 @@ const timeout = 30 * time.Second
|
|||||||
func ReceiveAddresses(incomingRoute *router.Route, outgoingRoute *router.Route,
|
func ReceiveAddresses(incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||||
peer *peerpkg.Peer, addressManager *addrmgr.AddrManager) (routeClosed bool, err error) {
|
peer *peerpkg.Peer, addressManager *addrmgr.AddrManager) (routeClosed bool, err error) {
|
||||||
|
|
||||||
subnetworkID, err := peer.SubnetworkID()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !addressManager.NeedMoreAddresses() {
|
if !addressManager.NeedMoreAddresses() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subnetworkID := peer.SubnetworkID()
|
||||||
msgGetAddresses := wire.NewMsgGetAddresses(false, subnetworkID)
|
msgGetAddresses := wire.NewMsgGetAddresses(false, subnetworkID)
|
||||||
isOpen, err := outgoingRoute.EnqueueWithTimeout(msgGetAddresses, timeout)
|
isOpen := outgoingRoute.Enqueue(msgGetAddresses)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package addressexchange
|
package addressexchange
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kaspanet/kaspad/addrmgr"
|
"github.com/kaspanet/kaspad/addrmgr"
|
||||||
"github.com/kaspanet/kaspad/netadapter/router"
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
"math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendAddresses sends addresses to a peer that requests it.
|
// SendAddresses sends addresses to a peer that requests it.
|
||||||
@ -26,11 +24,7 @@ func SendAddresses(incomingRoute *router.Route, outgoingRoute *router.Route,
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeout = 30 * time.Second
|
isOpen = outgoingRoute.Enqueue(msgAddresses)
|
||||||
isOpen, err = outgoingRoute.EnqueueWithTimeout(msgAddresses, timeout)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -33,10 +33,7 @@ func HandleRelayBlockRequests(incomingRoute *router.Route, outgoingRoute *router
|
|||||||
// If we are a full node and the peer is a partial node, we must convert
|
// If we are a full node and the peer is a partial node, we must convert
|
||||||
// the block to a partial block.
|
// the block to a partial block.
|
||||||
nodeSubnetworkID := dag.SubnetworkID()
|
nodeSubnetworkID := dag.SubnetworkID()
|
||||||
peerSubnetworkID, err := peer.SubnetworkID()
|
peerSubnetworkID := peer.SubnetworkID()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
isNodeFull := nodeSubnetworkID == nil
|
isNodeFull := nodeSubnetworkID == nil
|
||||||
isPeerFull := peerSubnetworkID == nil
|
isPeerFull := peerSubnetworkID == nil
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/netadapter"
|
"github.com/kaspanet/kaspad/netadapter"
|
||||||
"github.com/kaspanet/kaspad/netadapter/router"
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
"github.com/kaspanet/kaspad/protocol/blocklogger"
|
"github.com/kaspanet/kaspad/protocol/blocklogger"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/flows/ibd"
|
||||||
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||||
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
||||||
"github.com/kaspanet/kaspad/util"
|
"github.com/kaspanet/kaspad/util"
|
||||||
@ -41,6 +42,12 @@ func HandleRelayInvs(incomingRoute *router.Route, outgoingRoute *router.Route,
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ibd.StartIBDIfRequired(dag)
|
||||||
|
if ibd.IsInIBD() {
|
||||||
|
// Block relay is disabled during IBD
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
requestQueue := newHashesQueueSet()
|
requestQueue := newHashesQueueSet()
|
||||||
requestQueue.enqueueIfNotExists(inv.Hash)
|
requestQueue.enqueueIfNotExists(inv.Hash)
|
||||||
|
|
||||||
@ -102,10 +109,7 @@ func requestBlocks(netAdapater *netadapter.NetAdapter, outgoingRoute *router.Rou
|
|||||||
defer requestedBlocks.removeSet(pendingBlocks)
|
defer requestedBlocks.removeSet(pendingBlocks)
|
||||||
|
|
||||||
getRelayBlocksMsg := wire.NewMsgGetRelayBlocks(filteredHashesToRequest)
|
getRelayBlocksMsg := wire.NewMsgGetRelayBlocks(filteredHashesToRequest)
|
||||||
isOpen, err := outgoingRoute.EnqueueWithTimeout(getRelayBlocksMsg, timeout)
|
isOpen := outgoingRoute.Enqueue(getRelayBlocksMsg)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
@ -224,5 +228,8 @@ func processAndRelayBlock(netAdapter *netadapter.NetAdapter, peer *peerpkg.Peer,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ibd.StartIBDIfRequired(dag)
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/blockdag"
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
"github.com/kaspanet/kaspad/netadapter"
|
"github.com/kaspanet/kaspad/netadapter"
|
||||||
routerpkg "github.com/kaspanet/kaspad/netadapter/router"
|
routerpkg "github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/flows/ibd"
|
||||||
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||||
"github.com/kaspanet/kaspad/util/locks"
|
"github.com/kaspanet/kaspad/util/locks"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
@ -16,8 +17,8 @@ import (
|
|||||||
|
|
||||||
// HandleHandshake sets up the handshake protocol - It sends a version message and waits for an incoming
|
// HandleHandshake sets up the handshake protocol - It sends a version message and waits for an incoming
|
||||||
// version message, as well as a verack for the sent version
|
// version message, as well as a verack for the sent version
|
||||||
func HandleHandshake(router *routerpkg.Router, netAdapter *netadapter.NetAdapter, peer *peerpkg.Peer,
|
func HandleHandshake(router *routerpkg.Router, netAdapter *netadapter.NetAdapter,
|
||||||
dag *blockdag.BlockDAG, addressManager *addrmgr.AddrManager) (closed bool, err error) {
|
dag *blockdag.BlockDAG, addressManager *addrmgr.AddrManager) (peer *peerpkg.Peer, closed bool, err error) {
|
||||||
|
|
||||||
receiveVersionRoute, err := router.AddIncomingRoute([]wire.MessageCommand{wire.CmdVersion})
|
receiveVersionRoute, err := router.AddIncomingRoute([]wire.MessageCommand{wire.CmdVersion})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -71,42 +72,39 @@ func HandleHandshake(router *routerpkg.Router, netAdapter *netadapter.NetAdapter
|
|||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return nil, true, nil
|
||||||
case <-locks.ReceiveFromChanWhenDone(func() { wg.Wait() }):
|
case <-locks.ReceiveFromChanWhenDone(func() { wg.Wait() }):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
peer = peerpkg.New()
|
||||||
err = peerpkg.AddToReadyPeers(peer)
|
err = peerpkg.AddToReadyPeers(peer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, peerpkg.ErrPeerWithSameIDExists) {
|
if errors.Is(err, peerpkg.ErrPeerWithSameIDExists) {
|
||||||
return false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
peerID, err := peer.ID()
|
peerID := peer.ID()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = netAdapter.AssociateRouterID(router, peerID)
|
err = netAdapter.AssociateRouterID(router, peerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if peerAddress != nil {
|
if peerAddress != nil {
|
||||||
subnetworkID, err := peer.SubnetworkID()
|
subnetworkID := peer.SubnetworkID()
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
addressManager.AddAddress(peerAddress, peerAddress, subnetworkID)
|
addressManager.AddAddress(peerAddress, peerAddress, subnetworkID)
|
||||||
addressManager.Good(peerAddress, subnetworkID)
|
addressManager.Good(peerAddress, subnetworkID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ibd.StartIBDIfRequired(dag)
|
||||||
|
|
||||||
err = router.RemoveRoute([]wire.MessageCommand{wire.CmdVersion, wire.CmdVerAck})
|
err = router.RemoveRoute([]wire.MessageCommand{wire.CmdVersion, wire.CmdVerAck})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return false, nil
|
|
||||||
|
return peer, false, nil
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,7 @@ func ReceiveVersion(incomingRoute *router.Route, outgoingRoute *router.Route, ne
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
peer.UpdateFieldsFromMsgVersion(msgVersion)
|
peer.UpdateFieldsFromMsgVersion(msgVersion)
|
||||||
isOpen, err = outgoingRoute.EnqueueWithTimeout(wire.NewMsgVerAck(), timeout)
|
isOpen = outgoingRoute.Enqueue(wire.NewMsgVerAck())
|
||||||
if err != nil {
|
|
||||||
return nil, false, err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return nil, true, nil
|
return nil, true, nil
|
||||||
}
|
}
|
||||||
|
@ -51,10 +51,7 @@ func SendVersion(incomingRoute *router.Route, outgoingRoute *router.Route, netAd
|
|||||||
// Advertise if inv messages for transactions are desired.
|
// Advertise if inv messages for transactions are desired.
|
||||||
msg.DisableRelayTx = config.ActiveConfig().BlocksOnly
|
msg.DisableRelayTx = config.ActiveConfig().BlocksOnly
|
||||||
|
|
||||||
isOpen, err := outgoingRoute.EnqueueWithTimeout(msg, timeout)
|
isOpen := outgoingRoute.Enqueue(msg)
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
51
protocol/flows/ibd/handle_get_block_locator.go
Normal file
51
protocol/flows/ibd/handle_get_block_locator.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package ibd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleGetBlockLocator handles getBlockLocator messages
|
||||||
|
func HandleGetBlockLocator(incomingRoute *router.Route, outgoingRoute *router.Route, dag *blockdag.BlockDAG) error {
|
||||||
|
for {
|
||||||
|
lowHash, highHash, shouldStop, err := receiveGetBlockLocator(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
locator, err := dag.BlockLocatorFromHashes(highHash, lowHash)
|
||||||
|
if err != nil || len(locator) == 0 {
|
||||||
|
return protocolerrors.Errorf(true, "couldn't build a block "+
|
||||||
|
"locator between blocks %s and %s", lowHash, highHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldStop = sendBlockLocator(outgoingRoute, locator)
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveGetBlockLocator(incomingRoute *router.Route) (lowHash *daghash.Hash,
|
||||||
|
highHash *daghash.Hash, shouldStop bool, err error) {
|
||||||
|
|
||||||
|
message, isOpen := incomingRoute.Dequeue()
|
||||||
|
if !isOpen {
|
||||||
|
return nil, nil, true, nil
|
||||||
|
}
|
||||||
|
msgGetBlockLocator := message.(*wire.MsgGetBlockLocator)
|
||||||
|
|
||||||
|
return msgGetBlockLocator.LowHash, msgGetBlockLocator.HighHash, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendBlockLocator(outgoingRoute *router.Route, locator blockdag.BlockLocator) (shouldStop bool) {
|
||||||
|
msgBlockLocator := wire.NewMsgBlockLocator(locator)
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgBlockLocator)
|
||||||
|
return !isOpen
|
||||||
|
}
|
74
protocol/flows/ibd/handle_get_blocks.go
Normal file
74
protocol/flows/ibd/handle_get_blocks.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package ibd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandleGetBlocks handles getBlocks messages
|
||||||
|
func HandleGetBlocks(incomingRoute *router.Route, outgoingRoute *router.Route, dag *blockdag.BlockDAG) error {
|
||||||
|
for {
|
||||||
|
lowHash, highHash, shouldStop, err := receiveGetBlocks(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
msgIBDBlocks, err := buildMsgIBDBlocks(lowHash, highHash, dag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldStop = sendMsgIBDBlocks(outgoingRoute, msgIBDBlocks)
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveGetBlocks(incomingRoute *router.Route) (lowHash *daghash.Hash,
|
||||||
|
highHash *daghash.Hash, shouldStop bool, err error) {
|
||||||
|
|
||||||
|
message, isOpen := incomingRoute.Dequeue()
|
||||||
|
if !isOpen {
|
||||||
|
return nil, nil, true, nil
|
||||||
|
}
|
||||||
|
msgGetBlocks := message.(*wire.MsgGetBlocks)
|
||||||
|
|
||||||
|
return msgGetBlocks.LowHash, msgGetBlocks.HighHash, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildMsgIBDBlocks(lowHash *daghash.Hash, highHash *daghash.Hash,
|
||||||
|
dag *blockdag.BlockDAG) ([]*wire.MsgIBDBlock, error) {
|
||||||
|
|
||||||
|
const maxHashesInMsgIBDBlocks = wire.MaxInvPerMsg
|
||||||
|
blockHashes, err := dag.AntiPastHashesBetween(lowHash, highHash, maxHashesInMsgIBDBlocks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
msgIBDBlocks := make([]*wire.MsgIBDBlock, len(blockHashes))
|
||||||
|
for i, blockHash := range blockHashes {
|
||||||
|
block, err := dag.BlockByHash(blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
msgIBDBlocks[i] = wire.NewMsgIBDBlock(block.MsgBlock())
|
||||||
|
}
|
||||||
|
|
||||||
|
return msgIBDBlocks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMsgIBDBlocks(outgoingRoute *router.Route, msgIBDBlocks []*wire.MsgIBDBlock) (shouldStop bool) {
|
||||||
|
for _, msgIBDBlock := range msgIBDBlocks {
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgIBDBlock)
|
||||||
|
if !isOpen {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
237
protocol/flows/ibd/ibd.go
Normal file
237
protocol/flows/ibd/ibd.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package ibd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/common"
|
||||||
|
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
||||||
|
"github.com/kaspanet/kaspad/util"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
isIBDRunning uint32
|
||||||
|
startIBDMutex sync.Mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// StartIBDIfRequired selects a peer and starts IBD against it
|
||||||
|
// if required
|
||||||
|
func StartIBDIfRequired(dag *blockdag.BlockDAG) {
|
||||||
|
startIBDMutex.Lock()
|
||||||
|
defer startIBDMutex.Unlock()
|
||||||
|
|
||||||
|
if IsInIBD() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
peer := selectPeerForIBD(dag)
|
||||||
|
if peer == nil {
|
||||||
|
requestSelectedTipsIfRequired(dag)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.StoreUint32(&isIBDRunning, 1)
|
||||||
|
peer.StartIBD()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInIBD is true if IBD is currently running
|
||||||
|
func IsInIBD() bool {
|
||||||
|
return atomic.LoadUint32(&isIBDRunning) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// selectPeerForIBD returns the first peer whose selected tip
|
||||||
|
// hash is not in our DAG
|
||||||
|
func selectPeerForIBD(dag *blockdag.BlockDAG) *peerpkg.Peer {
|
||||||
|
for _, peer := range peerpkg.ReadyPeers() {
|
||||||
|
peerSelectedTipHash := peer.SelectedTipHash()
|
||||||
|
if !dag.IsInDAG(peerSelectedTipHash) {
|
||||||
|
return peer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleIBD waits for IBD start and handles it when IBD is triggered for this peer
|
||||||
|
func HandleIBD(incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||||
|
peer *peerpkg.Peer, dag *blockdag.BlockDAG) error {
|
||||||
|
|
||||||
|
for {
|
||||||
|
shouldStop, err := runIBD(incomingRoute, outgoingRoute, peer, dag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runIBD(incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||||
|
peer *peerpkg.Peer, dag *blockdag.BlockDAG) (shouldStop bool, err error) {
|
||||||
|
|
||||||
|
peer.WaitForIBDStart()
|
||||||
|
defer finishIBD(dag)
|
||||||
|
|
||||||
|
peerSelectedTipHash := peer.SelectedTipHash()
|
||||||
|
highestSharedBlockHash, shouldStop, err := findHighestSharedBlockHash(incomingRoute, outgoingRoute, dag, peerSelectedTipHash)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if dag.IsKnownFinalizedBlock(highestSharedBlockHash) {
|
||||||
|
return false, protocolerrors.Errorf(false, "cannot initiate "+
|
||||||
|
"IBD with peer %s because the highest shared chain block (%s) is "+
|
||||||
|
"below the finality point", peer, highestSharedBlockHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldStop, err = downloadBlocks(incomingRoute, outgoingRoute, dag, highestSharedBlockHash, peerSelectedTipHash)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return shouldStop, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findHighestSharedBlockHash(incomingRoute *router.Route, outgoingRoute *router.Route, dag *blockdag.BlockDAG,
|
||||||
|
peerSelectedTipHash *daghash.Hash) (lowHash *daghash.Hash, shouldStop bool, err error) {
|
||||||
|
|
||||||
|
lowHash = dag.Params.GenesisHash
|
||||||
|
highHash := peerSelectedTipHash
|
||||||
|
|
||||||
|
for {
|
||||||
|
shouldStop = sendGetBlockLocator(outgoingRoute, lowHash, highHash)
|
||||||
|
if shouldStop {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
blockLocatorHashes, shouldStop, err := receiveBlockLocator(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check whether the locator's highest hash is in the local DAG.
|
||||||
|
// If it is, return it. If it isn't, we need to narrow our
|
||||||
|
// getBlockLocator request and try again.
|
||||||
|
locatorHighHash := blockLocatorHashes[0]
|
||||||
|
if dag.IsInDAG(locatorHighHash) {
|
||||||
|
return locatorHighHash, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
highHash, lowHash = dag.FindNextLocatorBoundaries(blockLocatorHashes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendGetBlockLocator(outgoingRoute *router.Route, lowHash *daghash.Hash,
|
||||||
|
highHash *daghash.Hash) (shouldStop bool) {
|
||||||
|
|
||||||
|
msgGetBlockLocator := wire.NewMsgGetBlockLocator(highHash, lowHash)
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgGetBlockLocator)
|
||||||
|
return !isOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveBlockLocator(incomingRoute *router.Route) (blockLocatorHashes []*daghash.Hash,
|
||||||
|
shouldStop bool, err error) {
|
||||||
|
|
||||||
|
message, isOpen, err := incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if !isOpen {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
msgBlockLocator, ok := message.(*wire.MsgBlockLocator)
|
||||||
|
if !ok {
|
||||||
|
return nil, false,
|
||||||
|
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||||
|
"expected: %s, got: %s", wire.CmdBlockLocator, message.Command())
|
||||||
|
}
|
||||||
|
return msgBlockLocator.BlockLocatorHashes, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadBlocks(incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||||
|
dag *blockdag.BlockDAG, highestSharedBlockHash *daghash.Hash, peerSelectedTipHash *daghash.Hash) (shouldStop bool, err error) {
|
||||||
|
|
||||||
|
shouldStop = sendGetBlocks(outgoingRoute, highestSharedBlockHash, peerSelectedTipHash)
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
msgIBDBlock, shouldStop, err := receiveIBDBlock(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
shouldStop, err = processIBDBlock(dag, msgIBDBlock)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
if msgIBDBlock.BlockHash().IsEqual(peerSelectedTipHash) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendGetBlocks(outgoingRoute *router.Route, highestSharedBlockHash *daghash.Hash,
|
||||||
|
peerSelectedTipHash *daghash.Hash) (shouldStop bool) {
|
||||||
|
|
||||||
|
msgGetBlockInvs := wire.NewMsgGetBlocks(highestSharedBlockHash, peerSelectedTipHash)
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgGetBlockInvs)
|
||||||
|
return !isOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveIBDBlock(incomingRoute *router.Route) (msgIBDBlock *wire.MsgIBDBlock, shouldStop bool, err error) {
|
||||||
|
message, isOpen, err := incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if !isOpen {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
msgIBDBlock, ok := message.(*wire.MsgIBDBlock)
|
||||||
|
if !ok {
|
||||||
|
return nil, false,
|
||||||
|
protocolerrors.Errorf(true, "received unexpected message type. "+
|
||||||
|
"expected: %s, got: %s", wire.CmdIBDBlock, message.Command())
|
||||||
|
}
|
||||||
|
return msgIBDBlock, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processIBDBlock(dag *blockdag.BlockDAG, msgIBDBlock *wire.MsgIBDBlock) (shouldStop bool, err error) {
|
||||||
|
block := util.NewBlock(&msgIBDBlock.MsgBlock)
|
||||||
|
if dag.IsInDAG(block.Hash()) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
isOrphan, isDelayed, err := dag.ProcessBlock(block, blockdag.BFNone)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if isOrphan {
|
||||||
|
return false, protocolerrors.Errorf(true, "received orphan block %s "+
|
||||||
|
"during IBD", block.Hash())
|
||||||
|
}
|
||||||
|
if isDelayed {
|
||||||
|
return false, protocolerrors.Errorf(false, "received delayed block %s "+
|
||||||
|
"during IBD", block.Hash())
|
||||||
|
}
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func finishIBD(dag *blockdag.BlockDAG) {
|
||||||
|
atomic.StoreUint32(&isIBDRunning, 0)
|
||||||
|
|
||||||
|
StartIBDIfRequired(dag)
|
||||||
|
}
|
9
protocol/flows/ibd/log.go
Normal file
9
protocol/flows/ibd/log.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package ibd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/logger"
|
||||||
|
"github.com/kaspanet/kaspad/util/panics"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log, _ = logger.Get(logger.SubsystemTags.IBDS)
|
||||||
|
var spawn = panics.GoroutineWrapperFunc(log)
|
127
protocol/flows/ibd/selected_tip.go
Normal file
127
protocol/flows/ibd/selected_tip.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package ibd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
|
"github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/common"
|
||||||
|
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||||
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
|
"github.com/kaspanet/kaspad/wire"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const minDurationToRequestSelectedTips = time.Minute
|
||||||
|
|
||||||
|
func requestSelectedTipsIfRequired(dag *blockdag.BlockDAG) {
|
||||||
|
if isDAGTimeCurrent(dag) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
requestSelectedTips()
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDAGTimeCurrent(dag *blockdag.BlockDAG) bool {
|
||||||
|
return dag.Now().Sub(dag.SelectedTipHeader().Timestamp) > minDurationToRequestSelectedTips
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestSelectedTips() {
|
||||||
|
for _, peer := range peerpkg.ReadyPeers() {
|
||||||
|
peer.RequestSelectedTipIfRequired()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestSelectedTip waits for selected tip requests and handles them
|
||||||
|
func RequestSelectedTip(incomingRoute *router.Route,
|
||||||
|
outgoingRoute *router.Route, peer *peerpkg.Peer, dag *blockdag.BlockDAG) error {
|
||||||
|
for {
|
||||||
|
shouldStop, err := runSelectedTipRequest(incomingRoute, outgoingRoute, peer, dag)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSelectedTipRequest(incomingRoute *router.Route, outgoingRoute *router.Route,
|
||||||
|
peer *peerpkg.Peer, dag *blockdag.BlockDAG) (shouldStop bool, err error) {
|
||||||
|
|
||||||
|
peer.WaitForSelectedTipRequests()
|
||||||
|
defer peer.FinishRequestingSelectedTip()
|
||||||
|
|
||||||
|
shouldStop = requestSelectedTip(outgoingRoute)
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
peerSelectedTipHash, shouldStop, err := receiveSelectedTip(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
peer.SetSelectedTipHash(peerSelectedTipHash)
|
||||||
|
StartIBDIfRequired(dag)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestSelectedTip(outgoingRoute *router.Route) (shouldStop bool) {
|
||||||
|
msgGetSelectedTip := wire.NewMsgGetSelectedTip()
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgGetSelectedTip)
|
||||||
|
return !isOpen
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveSelectedTip(incomingRoute *router.Route) (selectedTipHash *daghash.Hash, shouldStop bool, err error) {
|
||||||
|
message, isOpen, err := incomingRoute.DequeueWithTimeout(common.DefaultTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if !isOpen {
|
||||||
|
return nil, true, nil
|
||||||
|
}
|
||||||
|
msgSelectedTip := message.(*wire.MsgSelectedTip)
|
||||||
|
|
||||||
|
return msgSelectedTip.SelectedTipHash, false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleGetSelectedTip handles getSelectedTip messages
|
||||||
|
func HandleGetSelectedTip(incomingRoute *router.Route, outgoingRoute *router.Route, dag *blockdag.BlockDAG) error {
|
||||||
|
for {
|
||||||
|
shouldStop, err := receiveGetSelectedTip(incomingRoute)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedTipHash := dag.SelectedTipHash()
|
||||||
|
shouldStop = sendSelectedTipHash(outgoingRoute, selectedTipHash)
|
||||||
|
if shouldStop {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveGetSelectedTip(incomingRoute *router.Route) (shouldStop bool, err error) {
|
||||||
|
message, isOpen := incomingRoute.Dequeue()
|
||||||
|
if !isOpen {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
_, ok := message.(*wire.MsgGetSelectedTip)
|
||||||
|
if !ok {
|
||||||
|
panic(errors.Errorf("received unexpected message type. "+
|
||||||
|
"expected: %s, got: %s", wire.CmdGetSelectedTip, message.Command()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendSelectedTipHash(outgoingRoute *router.Route, selectedTipHash *daghash.Hash) (shouldStop bool) {
|
||||||
|
msgSelectedTip := wire.NewMsgSelectedTip(selectedTipHash)
|
||||||
|
isOpen := outgoingRoute.Enqueue(msgSelectedTip)
|
||||||
|
return !isOpen
|
||||||
|
}
|
@ -23,10 +23,7 @@ func ReceivePings(incomingRoute *router.Route, outgoingRoute *router.Route) erro
|
|||||||
pingMessage := message.(*wire.MsgPing)
|
pingMessage := message.(*wire.MsgPing)
|
||||||
|
|
||||||
pongMessage := wire.NewMsgPong(pingMessage.Nonce)
|
pongMessage := wire.NewMsgPong(pingMessage.Nonce)
|
||||||
isOpen, err := outgoingRoute.EnqueueWithTimeout(pongMessage, pingTimeout)
|
isOpen = outgoingRoute.Enqueue(pongMessage)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -49,10 +46,7 @@ func SendPings(incomingRoute *router.Route, outgoingRoute *router.Route, peer *p
|
|||||||
peer.SetPingPending(nonce)
|
peer.SetPingPending(nonce)
|
||||||
|
|
||||||
pingMessage := wire.NewMsgPing(nonce)
|
pingMessage := wire.NewMsgPing(nonce)
|
||||||
isOpen, err := outgoingRoute.EnqueueWithTimeout(pingMessage, pingTimeout)
|
isOpen := outgoingRoute.Enqueue(pingMessage)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !isOpen {
|
if !isOpen {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/netadapter/id"
|
"github.com/kaspanet/kaspad/netadapter/id"
|
||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
mathUtil "github.com/kaspanet/kaspad/util/math"
|
mathUtil "github.com/kaspanet/kaspad/util/math"
|
||||||
|
"github.com/kaspanet/kaspad/util/mstime"
|
||||||
"github.com/kaspanet/kaspad/util/subnetworkid"
|
"github.com/kaspanet/kaspad/util/subnetworkid"
|
||||||
"github.com/kaspanet/kaspad/wire"
|
"github.com/kaspanet/kaspad/wire"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -15,8 +16,6 @@ import (
|
|||||||
|
|
||||||
// Peer holds data about a peer.
|
// Peer holds data about a peer.
|
||||||
type Peer struct {
|
type Peer struct {
|
||||||
ready uint32
|
|
||||||
|
|
||||||
selectedTipHashMtx sync.RWMutex
|
selectedTipHashMtx sync.RWMutex
|
||||||
selectedTipHash *daghash.Hash
|
selectedTipHash *daghash.Hash
|
||||||
|
|
||||||
@ -32,52 +31,45 @@ type Peer struct {
|
|||||||
lastPingNonce uint64 // The nonce of the last ping we sent
|
lastPingNonce uint64 // The nonce of the last ping we sent
|
||||||
lastPingTime time.Time // Time we sent last ping
|
lastPingTime time.Time // Time we sent last ping
|
||||||
lastPingDuration time.Duration // Time for last ping to return
|
lastPingDuration time.Duration // Time for last ping to return
|
||||||
|
|
||||||
|
isSelectedTipRequested uint32
|
||||||
|
selectedTipRequestChan chan struct{}
|
||||||
|
lastSelectedTipRequest mstime.Time
|
||||||
|
|
||||||
|
ibdStartChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new Peer
|
||||||
|
func New() *Peer {
|
||||||
|
return &Peer{
|
||||||
|
selectedTipRequestChan: make(chan struct{}),
|
||||||
|
ibdStartChan: make(chan struct{}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectedTipHash returns the selected tip of the peer.
|
// SelectedTipHash returns the selected tip of the peer.
|
||||||
func (p *Peer) SelectedTipHash() (*daghash.Hash, error) {
|
func (p *Peer) SelectedTipHash() *daghash.Hash {
|
||||||
if atomic.LoadUint32(&p.ready) == 0 {
|
|
||||||
return nil, errors.New("peer is not ready yet")
|
|
||||||
}
|
|
||||||
p.selectedTipHashMtx.RLock()
|
p.selectedTipHashMtx.RLock()
|
||||||
defer p.selectedTipHashMtx.RUnlock()
|
defer p.selectedTipHashMtx.RUnlock()
|
||||||
return p.selectedTipHash, nil
|
return p.selectedTipHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSelectedTipHash sets the selected tip of the peer.
|
// SetSelectedTipHash sets the selected tip of the peer.
|
||||||
func (p *Peer) SetSelectedTipHash(hash *daghash.Hash) error {
|
func (p *Peer) SetSelectedTipHash(hash *daghash.Hash) {
|
||||||
if atomic.LoadUint32(&p.ready) == 0 {
|
|
||||||
return errors.New("peer is not ready yet")
|
|
||||||
}
|
|
||||||
p.selectedTipHashMtx.Lock()
|
p.selectedTipHashMtx.Lock()
|
||||||
defer p.selectedTipHashMtx.Unlock()
|
defer p.selectedTipHashMtx.Unlock()
|
||||||
p.selectedTipHash = hash
|
p.selectedTipHash = hash
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubnetworkID returns the subnetwork the peer is associated with.
|
// SubnetworkID returns the subnetwork the peer is associated with.
|
||||||
// It is nil in full nodes.
|
// It is nil in full nodes.
|
||||||
func (p *Peer) SubnetworkID() (*subnetworkid.SubnetworkID, error) {
|
func (p *Peer) SubnetworkID() *subnetworkid.SubnetworkID {
|
||||||
if atomic.LoadUint32(&p.ready) == 0 {
|
return p.subnetworkID
|
||||||
return nil, errors.New("peer is not ready yet")
|
|
||||||
}
|
|
||||||
return p.subnetworkID, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID returns the peer ID.
|
// ID returns the peer ID.
|
||||||
func (p *Peer) ID() (*id.ID, error) {
|
func (p *Peer) ID() *id.ID {
|
||||||
if atomic.LoadUint32(&p.ready) == 0 {
|
return p.id
|
||||||
return nil, errors.New("peer is not ready yet")
|
|
||||||
}
|
|
||||||
return p.id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarkAsReady marks the peer as ready.
|
|
||||||
func (p *Peer) MarkAsReady() error {
|
|
||||||
if atomic.AddUint32(&p.ready, 1) != 1 {
|
|
||||||
return errors.New("peer is already ready")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateFieldsFromMsgVersion updates the peer with the data from the version message.
|
// UpdateFieldsFromMsgVersion updates the peer with the data from the version message.
|
||||||
@ -143,11 +135,6 @@ func AddToReadyPeers(peer *Peer) error {
|
|||||||
return errors.Wrapf(ErrPeerWithSameIDExists, "peer with ID %s already exists", peer.id)
|
return errors.Wrapf(ErrPeerWithSameIDExists, "peer with ID %s already exists", peer.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
err := peer.MarkAsReady()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
readyPeers[peer.id] = peer
|
readyPeers[peer.id] = peer
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -165,8 +152,51 @@ func GetReadyPeerIDs() []*id.ID {
|
|||||||
return peerIDs
|
return peerIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
// IDExists returns whether there's a peer with the given ID.
|
// ReadyPeers returns a copy of the currently ready peers
|
||||||
func IDExists(peerID *id.ID) bool {
|
func ReadyPeers() []*Peer {
|
||||||
_, ok := readyPeers[peerID]
|
peers := make([]*Peer, 0, len(readyPeers))
|
||||||
return ok
|
for _, readyPeer := range readyPeers {
|
||||||
|
peers = append(peers, readyPeer)
|
||||||
|
}
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestSelectedTipIfRequired notifies the peer that requesting
|
||||||
|
// a selected tip is required. This triggers the selected tip
|
||||||
|
// request flow.
|
||||||
|
func (p *Peer) RequestSelectedTipIfRequired() {
|
||||||
|
if atomic.SwapUint32(&p.isSelectedTipRequested, 1) != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const minGetSelectedTipInterval = time.Minute
|
||||||
|
if mstime.Since(p.lastSelectedTipRequest) < minGetSelectedTipInterval {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.lastSelectedTipRequest = mstime.Now()
|
||||||
|
p.selectedTipRequestChan <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForSelectedTipRequests blocks the current thread until
|
||||||
|
// a selected tip is requested from this peer
|
||||||
|
func (p *Peer) WaitForSelectedTipRequests() {
|
||||||
|
<-p.selectedTipRequestChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinishRequestingSelectedTip finishes requesting the selected
|
||||||
|
// tip from this peer
|
||||||
|
func (p *Peer) FinishRequestingSelectedTip() {
|
||||||
|
atomic.StoreUint32(&p.isSelectedTipRequested, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartIBD starts the IBD process for this peer
|
||||||
|
func (p *Peer) StartIBD() {
|
||||||
|
p.ibdStartChan <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitForIBDStart blocks the current thread until
|
||||||
|
// IBD start is requested from this peer
|
||||||
|
func (p *Peer) WaitForIBDStart() {
|
||||||
|
<-p.ibdStartChan
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/blockdag"
|
"github.com/kaspanet/kaspad/blockdag"
|
||||||
"github.com/kaspanet/kaspad/netadapter"
|
"github.com/kaspanet/kaspad/netadapter"
|
||||||
routerpkg "github.com/kaspanet/kaspad/netadapter/router"
|
routerpkg "github.com/kaspanet/kaspad/netadapter/router"
|
||||||
|
"github.com/kaspanet/kaspad/protocol/flows/ibd"
|
||||||
"github.com/kaspanet/kaspad/protocol/flows/ping"
|
"github.com/kaspanet/kaspad/protocol/flows/ping"
|
||||||
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
peerpkg "github.com/kaspanet/kaspad/protocol/peer"
|
||||||
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
"github.com/kaspanet/kaspad/protocol/protocolerrors"
|
||||||
@ -58,15 +59,13 @@ func newRouterInitializer(netAdapter *netadapter.NetAdapter,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startFlows(netAdapter *netadapter.NetAdapter, router *routerpkg.Router, dag *blockdag.BlockDAG,
|
func startFlows(netAdapter *netadapter.NetAdapter, router *routerpkg.Router,
|
||||||
addressManager *addrmgr.AddrManager) error {
|
dag *blockdag.BlockDAG, addressManager *addrmgr.AddrManager) error {
|
||||||
|
|
||||||
stop := make(chan error)
|
stop := make(chan error)
|
||||||
stopped := uint32(0)
|
stopped := uint32(0)
|
||||||
|
|
||||||
outgoingRoute := router.OutgoingRoute()
|
peer, closed, err := handshake.HandleHandshake(router, netAdapter, dag, addressManager)
|
||||||
peer := new(peerpkg.Peer)
|
|
||||||
|
|
||||||
closed, err := handshake.HandleHandshake(router, netAdapter, peer, dag, addressManager)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -74,44 +73,101 @@ func startFlows(netAdapter *netadapter.NetAdapter, router *routerpkg.Router, dag
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
addOneTimeFlow("SendAddresses", router, []wire.MessageCommand{wire.CmdGetAddresses}, &stopped, stop,
|
addAddressFlows(router, &stopped, stop, peer, addressManager)
|
||||||
|
addBlockRelayFlows(netAdapter, router, &stopped, stop, peer, dag)
|
||||||
|
addPingFlows(router, &stopped, stop, peer)
|
||||||
|
addIBDFlows(router, &stopped, stop, peer, dag)
|
||||||
|
|
||||||
|
err = <-stop
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func addAddressFlows(router *routerpkg.Router, stopped *uint32, stop chan error,
|
||||||
|
peer *peerpkg.Peer, addressManager *addrmgr.AddrManager) {
|
||||||
|
|
||||||
|
outgoingRoute := router.OutgoingRoute()
|
||||||
|
|
||||||
|
addOneTimeFlow("SendAddresses", router, []wire.MessageCommand{wire.CmdGetAddresses}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) (routeClosed bool, err error) {
|
func(incomingRoute *routerpkg.Route) (routeClosed bool, err error) {
|
||||||
return addressexchange.SendAddresses(incomingRoute, outgoingRoute, addressManager)
|
return addressexchange.SendAddresses(incomingRoute, outgoingRoute, addressManager)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
addOneTimeFlow("ReceiveAddresses", router, []wire.MessageCommand{wire.CmdAddress}, &stopped, stop,
|
addOneTimeFlow("ReceiveAddresses", router, []wire.MessageCommand{wire.CmdAddress}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) (routeClosed bool, err error) {
|
func(incomingRoute *routerpkg.Route) (routeClosed bool, err error) {
|
||||||
return addressexchange.ReceiveAddresses(incomingRoute, outgoingRoute, peer, addressManager)
|
return addressexchange.ReceiveAddresses(incomingRoute, outgoingRoute, peer, addressManager)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
addFlow("HandleRelayInvs", router, []wire.MessageCommand{wire.CmdInvRelayBlock, wire.CmdBlock}, &stopped, stop,
|
func addBlockRelayFlows(netAdapter *netadapter.NetAdapter, router *routerpkg.Router,
|
||||||
|
stopped *uint32, stop chan error, peer *peerpkg.Peer, dag *blockdag.BlockDAG) {
|
||||||
|
|
||||||
|
outgoingRoute := router.OutgoingRoute()
|
||||||
|
|
||||||
|
addFlow("HandleRelayInvs", router, []wire.MessageCommand{wire.CmdInvRelayBlock, wire.CmdBlock}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) error {
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
return blockrelay.HandleRelayInvs(incomingRoute, outgoingRoute, peer, netAdapter, dag)
|
return blockrelay.HandleRelayInvs(incomingRoute, outgoingRoute, peer, netAdapter, dag)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
addFlow("HandleRelayBlockRequests", router, []wire.MessageCommand{wire.CmdGetRelayBlocks}, &stopped, stop,
|
addFlow("HandleRelayBlockRequests", router, []wire.MessageCommand{wire.CmdGetRelayBlocks}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) error {
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
return blockrelay.HandleRelayBlockRequests(incomingRoute, outgoingRoute, peer, dag)
|
return blockrelay.HandleRelayBlockRequests(incomingRoute, outgoingRoute, peer, dag)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
addFlow("ReceivePings", router, []wire.MessageCommand{wire.CmdPing}, &stopped, stop,
|
func addPingFlows(router *routerpkg.Router, stopped *uint32, stop chan error, peer *peerpkg.Peer) {
|
||||||
|
outgoingRoute := router.OutgoingRoute()
|
||||||
|
|
||||||
|
addFlow("ReceivePings", router, []wire.MessageCommand{wire.CmdPing}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) error {
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
return ping.ReceivePings(incomingRoute, outgoingRoute)
|
return ping.ReceivePings(incomingRoute, outgoingRoute)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
addFlow("SendPings", router, []wire.MessageCommand{wire.CmdPong}, &stopped, stop,
|
addFlow("SendPings", router, []wire.MessageCommand{wire.CmdPong}, stopped, stop,
|
||||||
func(incomingRoute *routerpkg.Route) error {
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
return ping.SendPings(incomingRoute, outgoingRoute, peer)
|
return ping.SendPings(incomingRoute, outgoingRoute, peer)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
err = <-stop
|
func addIBDFlows(router *routerpkg.Router, stopped *uint32, stop chan error,
|
||||||
return err
|
peer *peerpkg.Peer, dag *blockdag.BlockDAG) {
|
||||||
|
|
||||||
|
outgoingRoute := router.OutgoingRoute()
|
||||||
|
|
||||||
|
addFlow("HandleIBD", router, []wire.MessageCommand{wire.CmdBlockLocator, wire.CmdIBDBlock}, stopped, stop,
|
||||||
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
|
return ibd.HandleIBD(incomingRoute, outgoingRoute, peer, dag)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
addFlow("RequestSelectedTip", router, []wire.MessageCommand{wire.CmdSelectedTip}, stopped, stop,
|
||||||
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
|
return ibd.RequestSelectedTip(incomingRoute, outgoingRoute, peer, dag)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
addFlow("HandleGetSelectedTip", router, []wire.MessageCommand{wire.CmdGetSelectedTip}, stopped, stop,
|
||||||
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
|
return ibd.HandleGetSelectedTip(incomingRoute, outgoingRoute, dag)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
addFlow("HandleGetBlockLocator", router, []wire.MessageCommand{wire.CmdGetBlockLocator}, stopped, stop,
|
||||||
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
|
return ibd.HandleGetBlockLocator(incomingRoute, outgoingRoute, dag)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
addFlow("HandleGetBlocks", router, []wire.MessageCommand{wire.CmdGetBlocks}, stopped, stop,
|
||||||
|
func(incomingRoute *routerpkg.Route) error {
|
||||||
|
return ibd.HandleGetBlocks(incomingRoute, outgoingRoute, dag)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addFlow(name string, router *routerpkg.Router, messageTypes []wire.MessageCommand, stopped *uint32,
|
func addFlow(name string, router *routerpkg.Router, messageTypes []wire.MessageCommand, stopped *uint32,
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
// message.
|
// message.
|
||||||
// It finds the blue future between msg.LowHash and msg.HighHash
|
// It finds the blue future between msg.LowHash and msg.HighHash
|
||||||
// and send the invs to the requesting peer.
|
// and send the invs to the requesting peer.
|
||||||
func (sp *Peer) OnGetBlockInvs(_ *peer.Peer, msg *wire.MsgGetBlockInvs) {
|
func (sp *Peer) OnGetBlockInvs(_ *peer.Peer, msg *wire.MsgGetBlocks) {
|
||||||
dag := sp.server.DAG
|
dag := sp.server.DAG
|
||||||
// We want to prevent a situation where the syncing peer needs
|
// We want to prevent a situation where the syncing peer needs
|
||||||
// to call getblocks once again, but the block we sent it
|
// to call getblocks once again, but the block we sent it
|
||||||
@ -24,7 +24,7 @@ func (sp *Peer) OnGetBlockInvs(_ *peer.Peer, msg *wire.MsgGetBlockInvs) {
|
|||||||
hashList, err := dag.AntiPastHashesBetween(msg.LowHash, msg.HighHash,
|
hashList, err := dag.AntiPastHashesBetween(msg.LowHash, msg.HighHash,
|
||||||
wire.MaxInvPerMsg)
|
wire.MaxInvPerMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sp.AddBanScoreAndPushRejectMsg(wire.CmdGetBlockInvs, wire.RejectInvalid, nil,
|
sp.AddBanScoreAndPushRejectMsg(wire.CmdGetBlocks, wire.RejectInvalid, nil,
|
||||||
peer.BanScoreInvalidMsgGetBlockInvs, 0,
|
peer.BanScoreInvalidMsgGetBlockInvs, 0,
|
||||||
fmt.Sprintf("error getting antiPast hashes between %s and %s: %s", msg.LowHash, msg.HighHash, err))
|
fmt.Sprintf("error getting antiPast hashes between %s and %s: %s", msg.LowHash, msg.HighHash, err))
|
||||||
return
|
return
|
||||||
|
@ -378,19 +378,19 @@ func BenchmarkWriteBlockHeader(b *testing.B) {
|
|||||||
// decode a getblockinvs message.
|
// decode a getblockinvs message.
|
||||||
func BenchmarkDecodeGetBlockInvs(b *testing.B) {
|
func BenchmarkDecodeGetBlockInvs(b *testing.B) {
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
var m MsgGetBlockInvs
|
var m MsgGetBlocks
|
||||||
m.LowHash = &daghash.Hash{1}
|
m.LowHash = &daghash.Hash{1}
|
||||||
m.HighHash = &daghash.Hash{1}
|
m.HighHash = &daghash.Hash{1}
|
||||||
|
|
||||||
// Serialize it so the bytes are available to test the decode below.
|
// Serialize it so the bytes are available to test the decode below.
|
||||||
var bb bytes.Buffer
|
var bb bytes.Buffer
|
||||||
if err := m.KaspaEncode(&bb, pver); err != nil {
|
if err := m.KaspaEncode(&bb, pver); err != nil {
|
||||||
b.Fatalf("MsgGetBlockInvs.KaspaEncode: unexpected error: %v", err)
|
b.Fatalf("MsgGetBlocks.KaspaEncode: unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
buf := bb.Bytes()
|
buf := bb.Bytes()
|
||||||
|
|
||||||
r := bytes.NewReader(buf)
|
r := bytes.NewReader(buf)
|
||||||
var msg MsgGetBlockInvs
|
var msg MsgGetBlocks
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
r.Seek(0, 0)
|
r.Seek(0, 0)
|
||||||
|
@ -40,7 +40,7 @@ const (
|
|||||||
CmdVerAck MessageCommand = 1
|
CmdVerAck MessageCommand = 1
|
||||||
CmdGetAddresses MessageCommand = 2
|
CmdGetAddresses MessageCommand = 2
|
||||||
CmdAddress MessageCommand = 3
|
CmdAddress MessageCommand = 3
|
||||||
CmdGetBlockInvs MessageCommand = 4
|
CmdGetBlocks MessageCommand = 4
|
||||||
CmdInv MessageCommand = 5
|
CmdInv MessageCommand = 5
|
||||||
CmdGetData MessageCommand = 6
|
CmdGetData MessageCommand = 6
|
||||||
CmdNotFound MessageCommand = 7
|
CmdNotFound MessageCommand = 7
|
||||||
@ -61,6 +61,7 @@ const (
|
|||||||
CmdInvRelayBlock MessageCommand = 22
|
CmdInvRelayBlock MessageCommand = 22
|
||||||
CmdGetRelayBlocks MessageCommand = 23
|
CmdGetRelayBlocks MessageCommand = 23
|
||||||
CmdRejectMalformed MessageCommand = 24 // Used only for reject message
|
CmdRejectMalformed MessageCommand = 24 // Used only for reject message
|
||||||
|
CmdIBDBlock MessageCommand = 25
|
||||||
)
|
)
|
||||||
|
|
||||||
var messageCommandToString = map[MessageCommand]string{
|
var messageCommandToString = map[MessageCommand]string{
|
||||||
@ -68,7 +69,7 @@ var messageCommandToString = map[MessageCommand]string{
|
|||||||
CmdVerAck: "VerAck",
|
CmdVerAck: "VerAck",
|
||||||
CmdGetAddresses: "GetAddress",
|
CmdGetAddresses: "GetAddress",
|
||||||
CmdAddress: "Address",
|
CmdAddress: "Address",
|
||||||
CmdGetBlockInvs: "GetBlockInvs",
|
CmdGetBlocks: "GetBlocks",
|
||||||
CmdInv: "Inv",
|
CmdInv: "Inv",
|
||||||
CmdGetData: "GetData",
|
CmdGetData: "GetData",
|
||||||
CmdNotFound: "NotFound",
|
CmdNotFound: "NotFound",
|
||||||
@ -89,6 +90,7 @@ var messageCommandToString = map[MessageCommand]string{
|
|||||||
CmdInvRelayBlock: "InvRelayBlock",
|
CmdInvRelayBlock: "InvRelayBlock",
|
||||||
CmdGetRelayBlocks: "GetRelayBlocks",
|
CmdGetRelayBlocks: "GetRelayBlocks",
|
||||||
CmdRejectMalformed: "RejectMalformed",
|
CmdRejectMalformed: "RejectMalformed",
|
||||||
|
CmdIBDBlock: "IBDBlock",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message is an interface that describes a kaspa message. A type that
|
// Message is an interface that describes a kaspa message. A type that
|
||||||
@ -119,8 +121,8 @@ func MakeEmptyMessage(command MessageCommand) (Message, error) {
|
|||||||
case CmdAddress:
|
case CmdAddress:
|
||||||
msg = &MsgAddresses{}
|
msg = &MsgAddresses{}
|
||||||
|
|
||||||
case CmdGetBlockInvs:
|
case CmdGetBlocks:
|
||||||
msg = &MsgGetBlockInvs{}
|
msg = &MsgGetBlocks{}
|
||||||
|
|
||||||
case CmdBlock:
|
case CmdBlock:
|
||||||
msg = &MsgBlock{}
|
msg = &MsgBlock{}
|
||||||
@ -173,6 +175,9 @@ func MakeEmptyMessage(command MessageCommand) (Message, error) {
|
|||||||
case CmdSelectedTip:
|
case CmdSelectedTip:
|
||||||
msg = &MsgSelectedTip{}
|
msg = &MsgSelectedTip{}
|
||||||
|
|
||||||
|
case CmdIBDBlock:
|
||||||
|
msg = &MsgIBDBlock{}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.Errorf("unhandled command [%s]", command)
|
return nil, errors.Errorf("unhandled command [%s]", command)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func TestMessage(t *testing.T) {
|
|||||||
msgVerack := NewMsgVerAck()
|
msgVerack := NewMsgVerAck()
|
||||||
msgGetAddresses := NewMsgGetAddresses(false, nil)
|
msgGetAddresses := NewMsgGetAddresses(false, nil)
|
||||||
msgAddresses := NewMsgAddresses(false, nil)
|
msgAddresses := NewMsgAddresses(false, nil)
|
||||||
msgGetBlockInvs := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
|
msgGetBlockInvs := NewMsgGetBlocks(&daghash.Hash{}, &daghash.Hash{})
|
||||||
msgBlock := &blockOne
|
msgBlock := &blockOne
|
||||||
msgInv := NewMsgInv()
|
msgInv := NewMsgInv()
|
||||||
msgGetData := NewMsgGetData()
|
msgGetData := NewMsgGetData()
|
||||||
@ -66,7 +66,7 @@ func TestMessage(t *testing.T) {
|
|||||||
msgPing := NewMsgPing(123123)
|
msgPing := NewMsgPing(123123)
|
||||||
msgPong := NewMsgPong(123123)
|
msgPong := NewMsgPong(123123)
|
||||||
msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||||
msgBlockLocator := NewMsgBlockLocator()
|
msgBlockLocator := NewMsgBlockLocator([]*daghash.Hash{})
|
||||||
msgFeeFilter := NewMsgFeeFilter(123456)
|
msgFeeFilter := NewMsgFeeFilter(123456)
|
||||||
msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
|
msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
|
||||||
msgFilterClear := NewMsgFilterClear()
|
msgFilterClear := NewMsgFilterClear()
|
||||||
|
@ -18,18 +18,6 @@ type MsgBlockLocator struct {
|
|||||||
BlockLocatorHashes []*daghash.Hash
|
BlockLocatorHashes []*daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBlockLocatorHash adds a new block locator hash to the message.
|
|
||||||
func (msg *MsgBlockLocator) AddBlockLocatorHash(hash *daghash.Hash) error {
|
|
||||||
if len(msg.BlockLocatorHashes) >= MaxBlockLocatorsPerMsg {
|
|
||||||
str := fmt.Sprintf("too many block locator hashes for message [max %d]",
|
|
||||||
MaxBlockLocatorsPerMsg)
|
|
||||||
return messageError("MsgBlockLocator.AddBlockLocatorHash", str)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||||
// This is part of the Message interface implementation.
|
// This is part of the Message interface implementation.
|
||||||
func (msg *MsgBlockLocator) KaspaDecode(r io.Reader, pver uint32) error {
|
func (msg *MsgBlockLocator) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
@ -54,7 +42,8 @@ func (msg *MsgBlockLocator) KaspaDecode(r io.Reader, pver uint32) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = msg.AddBlockLocatorHash(hash)
|
msg.BlockLocatorHashes = append(msg.BlockLocatorHashes, hash)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -104,9 +93,8 @@ func (msg *MsgBlockLocator) MaxPayloadLength(pver uint32) uint32 {
|
|||||||
|
|
||||||
// NewMsgBlockLocator returns a new kaspa locator message that conforms to
|
// NewMsgBlockLocator returns a new kaspa locator message that conforms to
|
||||||
// the Message interface. See MsgBlockLocator for details.
|
// the Message interface. See MsgBlockLocator for details.
|
||||||
func NewMsgBlockLocator() *MsgBlockLocator {
|
func NewMsgBlockLocator(locatorHashes []*daghash.Hash) *MsgBlockLocator {
|
||||||
return &MsgBlockLocator{
|
return &MsgBlockLocator{
|
||||||
BlockLocatorHashes: make([]*daghash.Hash, 0,
|
BlockLocatorHashes: locatorHashes,
|
||||||
MaxBlockLocatorsPerMsg),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func TestBlockLocator(t *testing.T) {
|
|||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := NewMsgBlockLocator()
|
msg := NewMsgBlockLocator([]*daghash.Hash{locatorHash})
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := MessageCommand(19)
|
wantCmd := MessageCommand(19)
|
||||||
@ -42,26 +42,12 @@ func TestBlockLocator(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure block locator hashes are added properly.
|
// Ensure block locator hashes are added properly.
|
||||||
err = msg.AddBlockLocatorHash(locatorHash)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("AddBlockLocatorHash: %v", err)
|
|
||||||
}
|
|
||||||
if msg.BlockLocatorHashes[0] != locatorHash {
|
if msg.BlockLocatorHashes[0] != locatorHash {
|
||||||
t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
|
t.Errorf("AddBlockLocatorHash: wrong block locator added - "+
|
||||||
"got %v, want %v",
|
"got %v, want %v",
|
||||||
spew.Sprint(msg.BlockLocatorHashes[0]),
|
spew.Sprint(msg.BlockLocatorHashes[0]),
|
||||||
spew.Sprint(locatorHash))
|
spew.Sprint(locatorHash))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure adding more than the max allowed block locator hashes per
|
|
||||||
// message returns an error.
|
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
|
||||||
err = msg.AddBlockLocatorHash(locatorHash)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("AddBlockLocatorHash: expected error on too many " +
|
|
||||||
"block locator hashes not received")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestBlockLocatorWire tests the MsgBlockLocator wire encode and decode for various
|
// TestBlockLocatorWire tests the MsgBlockLocator wire encode and decode for various
|
||||||
@ -80,15 +66,13 @@ func TestBlockLocatorWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MsgBlockLocator message with no block locators.
|
// MsgBlockLocator message with no block locators.
|
||||||
noLocators := NewMsgBlockLocator()
|
noLocators := NewMsgBlockLocator([]*daghash.Hash{})
|
||||||
noLocatorsEncoded := []byte{
|
noLocatorsEncoded := []byte{
|
||||||
0x00, // Varint for number of block locator hashes
|
0x00, // Varint for number of block locator hashes
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgBlockLocator message with multiple block locators.
|
// MsgBlockLocator message with multiple block locators.
|
||||||
multiLocators := NewMsgBlockLocator()
|
multiLocators := NewMsgBlockLocator([]*daghash.Hash{hashLocator2, hashLocator})
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator2)
|
|
||||||
multiLocators.AddBlockLocatorHash(hashLocator)
|
|
||||||
multiLocatorsEncoded := []byte{
|
multiLocatorsEncoded := []byte{
|
||||||
0x02, // Varint for number of block locator hashes
|
0x02, // Varint for number of block locator hashes
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
||||||
@ -177,9 +161,7 @@ func TestBlockLocatorWireErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MsgBlockLocator message with multiple block locators and a low hash.
|
// MsgBlockLocator message with multiple block locators and a low hash.
|
||||||
baseGetBlocks := NewMsgBlockLocator()
|
baseGetBlocks := NewMsgBlockLocator([]*daghash.Hash{hashLocator2, hashLocator})
|
||||||
baseGetBlocks.AddBlockLocatorHash(hashLocator2)
|
|
||||||
baseGetBlocks.AddBlockLocatorHash(hashLocator)
|
|
||||||
baseGetBlocksEncoded := []byte{
|
baseGetBlocksEncoded := []byte{
|
||||||
0x02, // Varint for number of block locator hashes
|
0x02, // Varint for number of block locator hashes
|
||||||
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
0xe0, 0xde, 0x06, 0x44, 0x68, 0x13, 0x2c, 0x63,
|
||||||
@ -194,10 +176,11 @@ func TestBlockLocatorWireErrors(t *testing.T) {
|
|||||||
|
|
||||||
// Message that forces an error by having more than the max allowed
|
// Message that forces an error by having more than the max allowed
|
||||||
// block locator hashes.
|
// block locator hashes.
|
||||||
maxGetBlocks := NewMsgBlockLocator()
|
maxLocaterHashesSlice := make([]*daghash.Hash, MaxBlockLocatorsPerMsg)
|
||||||
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
for i := 0; i < MaxBlockLocatorsPerMsg; i++ {
|
||||||
maxGetBlocks.AddBlockLocatorHash(mainnetGenesisHash)
|
maxLocaterHashesSlice[i] = mainnetGenesisHash
|
||||||
}
|
}
|
||||||
|
maxGetBlocks := NewMsgBlockLocator(maxLocaterHashesSlice)
|
||||||
maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
|
maxGetBlocks.BlockLocatorHashes = append(maxGetBlocks.BlockLocatorHashes,
|
||||||
mainnetGenesisHash)
|
mainnetGenesisHash)
|
||||||
maxGetBlocksEncoded := []byte{
|
maxGetBlocksEncoded := []byte{
|
||||||
|
@ -10,17 +10,17 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgGetBlockInvs implements the Message interface and represents a kaspa
|
// MsgGetBlocks implements the Message interface and represents a kaspa
|
||||||
// getblockinvs message. It is used to request a list of blocks starting after the
|
// getblocks message. It is used to request a list of blocks starting after the
|
||||||
// low hash and until the high hash.
|
// low hash and until the high hash.
|
||||||
type MsgGetBlockInvs struct {
|
type MsgGetBlocks struct {
|
||||||
LowHash *daghash.Hash
|
LowHash *daghash.Hash
|
||||||
HighHash *daghash.Hash
|
HighHash *daghash.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||||
// This is part of the Message interface implementation.
|
// This is part of the Message interface implementation.
|
||||||
func (msg *MsgGetBlockInvs) KaspaDecode(r io.Reader, pver uint32) error {
|
func (msg *MsgGetBlocks) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
msg.LowHash = &daghash.Hash{}
|
msg.LowHash = &daghash.Hash{}
|
||||||
err := ReadElement(r, msg.LowHash)
|
err := ReadElement(r, msg.LowHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -33,7 +33,7 @@ func (msg *MsgGetBlockInvs) KaspaDecode(r io.Reader, pver uint32) error {
|
|||||||
|
|
||||||
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||||
// This is part of the Message interface implementation.
|
// This is part of the Message interface implementation.
|
||||||
func (msg *MsgGetBlockInvs) KaspaEncode(w io.Writer, pver uint32) error {
|
func (msg *MsgGetBlocks) KaspaEncode(w io.Writer, pver uint32) error {
|
||||||
err := WriteElement(w, msg.LowHash)
|
err := WriteElement(w, msg.LowHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -44,22 +44,22 @@ func (msg *MsgGetBlockInvs) KaspaEncode(w io.Writer, pver uint32) error {
|
|||||||
|
|
||||||
// Command returns the protocol command string for the message. This is part
|
// Command returns the protocol command string for the message. This is part
|
||||||
// of the Message interface implementation.
|
// of the Message interface implementation.
|
||||||
func (msg *MsgGetBlockInvs) Command() MessageCommand {
|
func (msg *MsgGetBlocks) Command() MessageCommand {
|
||||||
return CmdGetBlockInvs
|
return CmdGetBlocks
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 *MsgGetBlockInvs) MaxPayloadLength(pver uint32) uint32 {
|
func (msg *MsgGetBlocks) MaxPayloadLength(pver uint32) uint32 {
|
||||||
// low hash + high hash.
|
// low hash + high hash.
|
||||||
return 2 * daghash.HashSize
|
return 2 * daghash.HashSize
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMsgGetBlockInvs returns a new kaspa getblockinvs message that conforms to the
|
// NewMsgGetBlocks returns a new kaspa getblocks 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 NewMsgGetBlockInvs(lowHash, highHash *daghash.Hash) *MsgGetBlockInvs {
|
func NewMsgGetBlocks(lowHash, highHash *daghash.Hash) *MsgGetBlocks {
|
||||||
return &MsgGetBlockInvs{
|
return &MsgGetBlocks{
|
||||||
LowHash: lowHash,
|
LowHash: lowHash,
|
||||||
HighHash: highHash,
|
HighHash: highHash,
|
||||||
}
|
}
|
@ -15,8 +15,8 @@ import (
|
|||||||
"github.com/kaspanet/kaspad/util/daghash"
|
"github.com/kaspanet/kaspad/util/daghash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestGetBlockInvs tests the MsgGetBlockInvs API.
|
// TestGetBlocks tests the MsgGetBlocks API.
|
||||||
func TestGetBlockInvs(t *testing.T) {
|
func TestGetBlocks(t *testing.T) {
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
|
|
||||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||||
@ -32,16 +32,16 @@ func TestGetBlockInvs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we get the same data back out.
|
// Ensure we get the same data back out.
|
||||||
msg := NewMsgGetBlockInvs(lowHash, highHash)
|
msg := NewMsgGetBlocks(lowHash, highHash)
|
||||||
if !msg.HighHash.IsEqual(highHash) {
|
if !msg.HighHash.IsEqual(highHash) {
|
||||||
t.Errorf("NewMsgGetBlockInvs: wrong high hash - got %v, want %v",
|
t.Errorf("NewMsgGetBlocks: wrong high hash - got %v, want %v",
|
||||||
msg.HighHash, highHash)
|
msg.HighHash, highHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the command is expected value.
|
// Ensure the command is expected value.
|
||||||
wantCmd := MessageCommand(4)
|
wantCmd := MessageCommand(4)
|
||||||
if cmd := msg.Command(); cmd != wantCmd {
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
t.Errorf("NewMsgGetBlockInvs: wrong command - got %v want %v",
|
t.Errorf("NewMsgGetBlocks: wrong command - got %v want %v",
|
||||||
cmd, wantCmd)
|
cmd, wantCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +55,9 @@ func TestGetBlockInvs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetBlockInvsWire tests the MsgGetBlockInvs wire encode and decode for various
|
// TestGetBlocksWire tests the MsgGetBlocks wire encode and decode for various
|
||||||
// numbers of block locator hashes and protocol versions.
|
// numbers of block locator hashes and protocol versions.
|
||||||
func TestGetBlockInvsWire(t *testing.T) {
|
func TestGetBlocksWire(t *testing.T) {
|
||||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||||
lowHash, err := daghash.NewHashFromStr(hashStr)
|
lowHash, err := daghash.NewHashFromStr(hashStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -71,7 +71,7 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetBlocks message with no start or high hash.
|
// MsgGetBlocks message with no start or high hash.
|
||||||
noStartOrStop := NewMsgGetBlockInvs(&daghash.Hash{}, &daghash.Hash{})
|
noStartOrStop := NewMsgGetBlocks(&daghash.Hash{}, &daghash.Hash{})
|
||||||
noStartOrStopEncoded := []byte{
|
noStartOrStopEncoded := []byte{
|
||||||
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, 0x00, 0x00, 0x00,
|
||||||
@ -83,8 +83,8 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetBlockInvs message with a low hash and a high hash.
|
// MsgGetBlocks message with a low hash and a high hash.
|
||||||
withLowAndHighHash := NewMsgGetBlockInvs(lowHash, highHash)
|
withLowAndHighHash := NewMsgGetBlocks(lowHash, highHash)
|
||||||
withLowAndHighHashEncoded := []byte{
|
withLowAndHighHashEncoded := []byte{
|
||||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
@ -97,10 +97,10 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in *MsgGetBlockInvs // Message to encode
|
in *MsgGetBlocks // Message to encode
|
||||||
out *MsgGetBlockInvs // Expected decoded message
|
out *MsgGetBlocks // Expected decoded message
|
||||||
buf []byte // Wire encoding
|
buf []byte // Wire encoding
|
||||||
pver uint32 // Protocol version for wire encoding
|
pver uint32 // Protocol version for wire encoding
|
||||||
}{
|
}{
|
||||||
// Latest protocol version with no block locators.
|
// Latest protocol version with no block locators.
|
||||||
{
|
{
|
||||||
@ -135,7 +135,7 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode the message from wire format.
|
// Decode the message from wire format.
|
||||||
var msg MsgGetBlockInvs
|
var msg MsgGetBlocks
|
||||||
rbuf := bytes.NewReader(test.buf)
|
rbuf := bytes.NewReader(test.buf)
|
||||||
err = msg.KaspaDecode(rbuf, test.pver)
|
err = msg.KaspaDecode(rbuf, test.pver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -150,9 +150,9 @@ func TestGetBlockInvsWire(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestGetBlockInvsWireErrors performs negative tests against wire encode and
|
// TestGetBlocksWireErrors performs negative tests against wire encode and
|
||||||
// decode of MsgGetBlockInvs to confirm error paths work correctly.
|
// decode of MsgGetBlocks to confirm error paths work correctly.
|
||||||
func TestGetBlockInvsWireErrors(t *testing.T) {
|
func TestGetBlocksWireErrors(t *testing.T) {
|
||||||
// Set protocol inside getheaders message.
|
// Set protocol inside getheaders message.
|
||||||
pver := ProtocolVersion
|
pver := ProtocolVersion
|
||||||
|
|
||||||
@ -168,8 +168,8 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
|||||||
t.Errorf("NewHashFromStr: %v", err)
|
t.Errorf("NewHashFromStr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MsgGetBlockInvs message with multiple block locators and a high hash.
|
// MsgGetBlocks message with multiple block locators and a high hash.
|
||||||
baseGetBlocks := NewMsgGetBlockInvs(lowHash, highHash)
|
baseGetBlocks := NewMsgGetBlocks(lowHash, highHash)
|
||||||
baseGetBlocksEncoded := []byte{
|
baseGetBlocksEncoded := []byte{
|
||||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||||
@ -182,12 +182,12 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
in *MsgGetBlockInvs // Value to encode
|
in *MsgGetBlocks // Value to encode
|
||||||
buf []byte // Wire encoding
|
buf []byte // Wire encoding
|
||||||
pver uint32 // Protocol version for wire encoding
|
pver uint32 // Protocol version for wire encoding
|
||||||
max int // Max size of fixed buffer to induce errors
|
max int // Max size of fixed buffer to induce errors
|
||||||
writeErr error // Expected write error
|
writeErr error // Expected write error
|
||||||
readErr error // Expected read error
|
readErr error // Expected read error
|
||||||
}{
|
}{
|
||||||
// Force error in low hash.
|
// Force error in low hash.
|
||||||
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
{baseGetBlocks, baseGetBlocksEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||||
@ -218,7 +218,7 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decode from wire format.
|
// Decode from wire format.
|
||||||
var msg MsgGetBlockInvs
|
var msg MsgGetBlocks
|
||||||
r := newFixedReader(test.max, test.buf)
|
r := newFixedReader(test.max, test.buf)
|
||||||
err = msg.KaspaDecode(r, test.pver)
|
err = msg.KaspaDecode(r, test.pver)
|
||||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
44
wire/msgibdblock.go
Normal file
44
wire/msgibdblock.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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 "io"
|
||||||
|
|
||||||
|
// MsgIBDBlock implements the Message interface and represents a kaspa
|
||||||
|
// ibdblock message. It is used to deliver block and transaction information in
|
||||||
|
// response to a getblocks message (MsgGetBlocks).
|
||||||
|
type MsgIBDBlock struct {
|
||||||
|
MsgBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgIBDBlock) KaspaDecode(r io.Reader, pver uint32) error {
|
||||||
|
return msg.MsgBlock.KaspaDecode(r, pver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||||
|
// This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgIBDBlock) KaspaEncode(w io.Writer, pver uint32) error {
|
||||||
|
return msg.MsgBlock.KaspaEncode(w, pver)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the protocol command string for the message. This is part
|
||||||
|
// of the Message interface implementation.
|
||||||
|
func (msg *MsgIBDBlock) Command() MessageCommand {
|
||||||
|
return CmdIBDBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||||
|
// receiver. This is part of the Message interface implementation.
|
||||||
|
func (msg *MsgIBDBlock) MaxPayloadLength(pver uint32) uint32 {
|
||||||
|
return MaxMessagePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMsgIBDBlock returns a new kaspa ibdblock message that conforms to the
|
||||||
|
// Message interface. See MsgIBDBlock for details.
|
||||||
|
func NewMsgIBDBlock(msgBlock *MsgBlock) *MsgIBDBlock {
|
||||||
|
return &MsgIBDBlock{*msgBlock}
|
||||||
|
}
|
117
wire/msgibdblock_test.go
Normal file
117
wire/msgibdblock_test.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestIBDBlock tests the MsgIBDBlock API.
|
||||||
|
func TestIBDBlock(t *testing.T) {
|
||||||
|
pver := ProtocolVersion
|
||||||
|
|
||||||
|
// Block 1 header.
|
||||||
|
parentHashes := blockOne.Header.ParentHashes
|
||||||
|
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
||||||
|
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
||||||
|
utxoCommitment := blockOne.Header.UTXOCommitment
|
||||||
|
bits := blockOne.Header.Bits
|
||||||
|
nonce := blockOne.Header.Nonce
|
||||||
|
bh := NewBlockHeader(1, parentHashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||||
|
|
||||||
|
// Ensure the command is expected value.
|
||||||
|
wantCmd := MessageCommand(25)
|
||||||
|
msg := NewMsgIBDBlock(NewMsgBlock(bh))
|
||||||
|
if cmd := msg.Command(); cmd != wantCmd {
|
||||||
|
t.Errorf("NewMsgIBDBlock: wrong command - got %v want %v",
|
||||||
|
cmd, wantCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure max payload is expected value for latest protocol version.
|
||||||
|
wantPayload := uint32(1024 * 1024 * 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure we get the same block header data back out.
|
||||||
|
if !reflect.DeepEqual(&msg.Header, bh) {
|
||||||
|
t.Errorf("NewMsgIBDBlock: wrong block header - got %v, want %v",
|
||||||
|
spew.Sdump(&msg.Header), spew.Sdump(bh))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are added properly.
|
||||||
|
tx := blockOne.Transactions[0].Copy()
|
||||||
|
msg.AddTransaction(tx)
|
||||||
|
if !reflect.DeepEqual(msg.Transactions, blockOne.Transactions) {
|
||||||
|
t.Errorf("AddTransaction: wrong transactions - got %v, want %v",
|
||||||
|
spew.Sdump(msg.Transactions),
|
||||||
|
spew.Sdump(blockOne.Transactions))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure transactions are properly cleared.
|
||||||
|
msg.ClearTransactions()
|
||||||
|
if len(msg.Transactions) != 0 {
|
||||||
|
t.Errorf("ClearTransactions: wrong transactions - got %v, want %v",
|
||||||
|
len(msg.Transactions), 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIBDBlockWire tests the MsgIBDBlock wire encode and decode for various numbers
|
||||||
|
// of transaction inputs and outputs and protocol versions.
|
||||||
|
func TestIBDBlockWire(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in *MsgIBDBlock // Message to encode
|
||||||
|
out *MsgIBDBlock // Expected decoded message
|
||||||
|
buf []byte // Wire encoding
|
||||||
|
txLocs []TxLoc // Expected transaction locations
|
||||||
|
pver uint32 // Protocol version for wire encoding
|
||||||
|
}{
|
||||||
|
// Latest protocol version.
|
||||||
|
{
|
||||||
|
&MsgIBDBlock{blockOne},
|
||||||
|
&MsgIBDBlock{blockOne},
|
||||||
|
blockOneBytes,
|
||||||
|
blockOneTxLocs,
|
||||||
|
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 MsgIBDBlock
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user