mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-03-30 15:08:33 +00:00
[NOD-545] Remove headers first related logic (#621)
* [NOD-545] Remove headers first related logic * [NOD-545] Fix tests * [NOD-545] Change getTopHeadersMaxHeaders to be equal to getHeadersMaxHeaders
This commit is contained in:
parent
4ffb5daa37
commit
45dc1a3e7b
@ -1741,7 +1741,7 @@ func (dag *BlockDAG) antiPastHeadersBetween(lowHash, highHash *daghash.Hash, max
|
||||
}
|
||||
|
||||
// GetTopHeaders returns the top wire.MaxBlockHeadersPerMsg block headers ordered by blue score.
|
||||
func (dag *BlockDAG) GetTopHeaders(highHash *daghash.Hash) ([]*wire.BlockHeader, error) {
|
||||
func (dag *BlockDAG) GetTopHeaders(highHash *daghash.Hash, maxHeaders uint64) ([]*wire.BlockHeader, error) {
|
||||
highNode := &dag.virtual.blockNode
|
||||
if highHash != nil {
|
||||
highNode = dag.index.LookupNode(highHash)
|
||||
@ -1754,7 +1754,7 @@ func (dag *BlockDAG) GetTopHeaders(highHash *daghash.Hash) ([]*wire.BlockHeader,
|
||||
queue.pushSet(highNode.parents)
|
||||
|
||||
visited := newSet()
|
||||
for i := uint32(0); queue.Len() > 0 && len(headers) < wire.MaxBlockHeadersPerMsg; i++ {
|
||||
for i := uint32(0); queue.Len() > 0 && uint64(len(headers)) < maxHeaders; i++ {
|
||||
var current *blockNode
|
||||
current = queue.pop()
|
||||
if !visited.contains(current) {
|
||||
@ -1791,9 +1791,9 @@ func (dag *BlockDAG) RUnlock() {
|
||||
// wire.MaxBlockHeadersPerMsg block headers.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (dag *BlockDAG) AntiPastHeadersBetween(lowHash, highHash *daghash.Hash) ([]*wire.BlockHeader, error) {
|
||||
func (dag *BlockDAG) AntiPastHeadersBetween(lowHash, highHash *daghash.Hash, maxHeaders uint64) ([]*wire.BlockHeader, error) {
|
||||
dag.dagLock.RLock()
|
||||
headers, err := dag.antiPastHeadersBetween(lowHash, highHash, wire.MaxBlockHeadersPerMsg)
|
||||
headers, err := dag.antiPastHeadersBetween(lowHash, highHash, maxHeaders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -143,10 +143,6 @@ func messageSummary(msg wire.Message) string {
|
||||
return fmt.Sprintf("low hash %s, high hash %s", msg.LowHash,
|
||||
msg.HighHash)
|
||||
|
||||
case *wire.MsgGetHeaders:
|
||||
return fmt.Sprintf("low hash %s, high hash %s", msg.LowHash,
|
||||
msg.HighHash)
|
||||
|
||||
case *wire.MsgGetBlockLocator:
|
||||
return fmt.Sprintf("high hash %s, low hash %s", msg.HighHash,
|
||||
msg.LowHash)
|
||||
@ -157,9 +153,6 @@ func messageSummary(msg wire.Message) string {
|
||||
}
|
||||
return fmt.Sprintf("no locator")
|
||||
|
||||
case *wire.MsgHeaders:
|
||||
return fmt.Sprintf("num %d", len(msg.Headers))
|
||||
|
||||
case *wire.MsgReject:
|
||||
// Ensure the variable length strings don't contain any
|
||||
// characters which are even remotely dangerous such as HTML
|
||||
|
82
peer/peer.go
82
peer/peer.go
@ -164,10 +164,6 @@ type MessageListeners struct {
|
||||
// OnReject is invoked when a peer receives a reject kaspa message.
|
||||
OnReject func(p *Peer, msg *wire.MsgReject)
|
||||
|
||||
// OnSendHeaders is invoked when a peer receives a sendheaders kaspa
|
||||
// message.
|
||||
OnSendHeaders func(p *Peer, msg *wire.MsgSendHeaders)
|
||||
|
||||
// OnGetSelectedTip is invoked when a peer receives a getSelectedTip kaspa
|
||||
// message.
|
||||
OnGetSelectedTip func()
|
||||
@ -410,16 +406,15 @@ type Peer struct {
|
||||
cfg Config
|
||||
inbound bool
|
||||
|
||||
flagsMtx sync.Mutex // protects the peer flags below
|
||||
na *wire.NetAddress
|
||||
id int32
|
||||
userAgent string
|
||||
services wire.ServiceFlag
|
||||
versionKnown bool
|
||||
advertisedProtoVer uint32 // protocol version advertised by remote
|
||||
protocolVersion uint32 // negotiated protocol version
|
||||
sendHeadersPreferred bool // peer sent a sendheaders message
|
||||
verAckReceived bool
|
||||
flagsMtx sync.Mutex // protects the peer flags below
|
||||
na *wire.NetAddress
|
||||
id int32
|
||||
userAgent string
|
||||
services wire.ServiceFlag
|
||||
versionKnown bool
|
||||
advertisedProtoVer uint32 // protocol version advertised by remote
|
||||
protocolVersion uint32 // negotiated protocol version
|
||||
verAckReceived bool
|
||||
|
||||
knownInventory *mruInventoryMap
|
||||
prevGetBlockInvsMtx sync.Mutex
|
||||
@ -717,18 +712,6 @@ func (p *Peer) TimeOffset() int64 {
|
||||
return timeOffset
|
||||
}
|
||||
|
||||
// WantsHeaders returns if the peer wants header messages instead of
|
||||
// inventory vectors for blocks.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (p *Peer) WantsHeaders() bool {
|
||||
p.flagsMtx.Lock()
|
||||
sendHeadersPreferred := p.sendHeadersPreferred
|
||||
p.flagsMtx.Unlock()
|
||||
|
||||
return sendHeadersPreferred
|
||||
}
|
||||
|
||||
// localVersionMsg creates a version message that can be used to send to the
|
||||
// remote peer.
|
||||
func (p *Peer) localVersionMsg() (*wire.MsgVersion, error) {
|
||||
@ -878,37 +861,6 @@ func (p *Peer) PushBlockLocatorMsg(locator blockdag.BlockLocator) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushGetHeadersMsg sends a getblockinvs message for the provided block locator
|
||||
// and low hash. It will ignore back-to-back duplicate requests.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (p *Peer) PushGetHeadersMsg(lowHash, highHash *daghash.Hash) error {
|
||||
// Filter duplicate getheaders requests.
|
||||
p.prevGetHdrsMtx.Lock()
|
||||
isDuplicate := p.prevGetHdrsHigh != nil && p.prevGetHdrsLow != nil &&
|
||||
lowHash != nil && highHash.IsEqual(p.prevGetHdrsHigh) &&
|
||||
lowHash.IsEqual(p.prevGetHdrsLow)
|
||||
p.prevGetHdrsMtx.Unlock()
|
||||
|
||||
if isDuplicate {
|
||||
log.Tracef("Filtering duplicate [getheaders] with low hash %s",
|
||||
lowHash)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the getheaders request and queue it to be sent.
|
||||
msg := wire.NewMsgGetHeaders(lowHash, highHash)
|
||||
p.QueueMessage(msg, nil)
|
||||
|
||||
// Update the previous getheaders request information for filtering
|
||||
// duplicates.
|
||||
p.prevGetHdrsMtx.Lock()
|
||||
p.prevGetHdrsLow = lowHash
|
||||
p.prevGetHdrsHigh = highHash
|
||||
p.prevGetHdrsMtx.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushRejectMsg sends a reject message for the provided command, reject code,
|
||||
// reject reason, and hash. The hash will only be used when the command is a tx
|
||||
// or block and should be nil in other cases. The wait parameter will cause the
|
||||
@ -1181,13 +1133,6 @@ func (p *Peer) maybeAddDeadline(pendingResponses map[string]time.Time, msgCmd st
|
||||
pendingResponses[wire.CmdTx] = deadline
|
||||
pendingResponses[wire.CmdNotFound] = deadline
|
||||
|
||||
case wire.CmdGetHeaders:
|
||||
// Expects a headers message. Use a longer deadline since it
|
||||
// can take a while for the remote peer to load all of the
|
||||
// headers.
|
||||
deadline = time.Now().Add(stallResponseTimeout * 3)
|
||||
pendingResponses[wire.CmdHeaders] = deadline
|
||||
|
||||
case wire.CmdGetSelectedTip:
|
||||
// Expects a selected tip message.
|
||||
pendingResponses[wire.CmdSelectedTip] = deadline
|
||||
@ -1510,15 +1455,6 @@ out:
|
||||
p.cfg.Listeners.OnReject(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgSendHeaders:
|
||||
p.flagsMtx.Lock()
|
||||
p.sendHeadersPreferred = true
|
||||
p.flagsMtx.Unlock()
|
||||
|
||||
if p.cfg.Listeners.OnSendHeaders != nil {
|
||||
p.cfg.Listeners.OnSendHeaders(p, msg)
|
||||
}
|
||||
|
||||
case *wire.MsgGetSelectedTip:
|
||||
if p.cfg.Listeners.OnGetSelectedTip != nil {
|
||||
p.cfg.Listeners.OnGetSelectedTip()
|
||||
|
@ -394,9 +394,6 @@ func TestPeerListeners(t *testing.T) {
|
||||
OnReject: func(p *peer.Peer, msg *wire.MsgReject) {
|
||||
ok <- msg
|
||||
},
|
||||
OnSendHeaders: func(p *peer.Peer, msg *wire.MsgSendHeaders) {
|
||||
ok <- msg
|
||||
},
|
||||
},
|
||||
UserAgentName: "peer",
|
||||
UserAgentVersion: "1.0",
|
||||
@ -505,10 +502,6 @@ func TestPeerListeners(t *testing.T) {
|
||||
"OnReject",
|
||||
wire.NewMsgReject("block", wire.RejectDuplicate, "dupe block"),
|
||||
},
|
||||
{
|
||||
"OnSendHeaders",
|
||||
wire.NewMsgSendHeaders(),
|
||||
},
|
||||
}
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for _, test := range tests {
|
||||
@ -618,10 +611,6 @@ func TestOutboundPeer(t *testing.T) {
|
||||
t.Errorf("PushGetBlockInvsMsg: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p2.PushGetHeadersMsg(nil, &daghash.Hash{}); err != nil {
|
||||
t.Errorf("PushGetHeadersMsg: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
p2.PushRejectMsg("block", wire.RejectMalformed, "malformed", nil, false)
|
||||
p2.PushRejectMsg("block", wire.RejectInvalid, "invalid", nil, false)
|
||||
@ -630,7 +619,6 @@ func TestOutboundPeer(t *testing.T) {
|
||||
p2.QueueMessage(wire.NewMsgGetAddr(false, nil), nil)
|
||||
p2.QueueMessage(wire.NewMsgPing(1), nil)
|
||||
p2.QueueMessage(wire.NewMsgGetData(), nil)
|
||||
p2.QueueMessage(wire.NewMsgGetHeaders(&daghash.ZeroHash, &daghash.ZeroHash), nil)
|
||||
p2.QueueMessage(wire.NewMsgFeeFilter(20000), nil)
|
||||
|
||||
p2.Disconnect()
|
||||
|
@ -750,26 +750,6 @@ func (s *Server) handleRelayInvMsg(state *peerState, msg relayMsg) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the inventory is a block and the peer prefers headers,
|
||||
// generate and send a headers message instead of an inventory
|
||||
// message.
|
||||
if msg.invVect.Type == wire.InvTypeBlock && sp.WantsHeaders() {
|
||||
blockHeader, ok := msg.data.(wire.BlockHeader)
|
||||
if !ok {
|
||||
peerLog.Warnf("Underlying data for headers" +
|
||||
" is not a block header")
|
||||
return true
|
||||
}
|
||||
msgHeaders := wire.NewMsgHeaders()
|
||||
if err := msgHeaders.AddBlockHeader(&blockHeader); err != nil {
|
||||
peerLog.Errorf("Failed to add block"+
|
||||
" header: %s", err)
|
||||
return true
|
||||
}
|
||||
sp.QueueMessage(msgHeaders, nil)
|
||||
return true
|
||||
}
|
||||
|
||||
if msg.invVect.Type == wire.InvTypeTx {
|
||||
// Don't relay the transaction to the peer when it has
|
||||
// transaction relaying disabled.
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
const getHeadersMaxHeaders = 2000
|
||||
|
||||
// handleGetHeaders implements the getHeaders command.
|
||||
func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
c := cmd.(*rpcmodel.GetHeadersCmd)
|
||||
@ -25,7 +27,7 @@ func handleGetHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (in
|
||||
return nil, rpcDecodeHexError(c.HighHash)
|
||||
}
|
||||
}
|
||||
headers, err := s.cfg.SyncMgr.AntiPastHeadersBetween(lowHash, highHash)
|
||||
headers, err := s.cfg.SyncMgr.AntiPastHeadersBetween(lowHash, highHash, getHeadersMaxHeaders)
|
||||
if err != nil {
|
||||
return nil, &rpcmodel.RPCError{
|
||||
Code: rpcmodel.ErrRPCMisc,
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
const getTopHeadersMaxHeaders = getHeadersMaxHeaders
|
||||
|
||||
// handleGetTopHeaders implements the getTopHeaders command.
|
||||
func handleGetTopHeaders(s *Server, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
c := cmd.(*rpcmodel.GetTopHeadersCmd)
|
||||
@ -19,7 +21,7 @@ func handleGetTopHeaders(s *Server, cmd interface{}, closeChan <-chan struct{})
|
||||
return nil, rpcDecodeHexError(*c.HighHash)
|
||||
}
|
||||
}
|
||||
headers, err := s.cfg.DAG.GetTopHeaders(highHash)
|
||||
headers, err := s.cfg.DAG.GetTopHeaders(highHash, getTopHeadersMaxHeaders)
|
||||
if err != nil {
|
||||
return nil, internalRPCError(err.Error(),
|
||||
"Failed to get top headers")
|
||||
|
@ -275,6 +275,6 @@ func (b *rpcSyncMgr) SyncPeerID() int32 {
|
||||
//
|
||||
// This function is safe for concurrent access and is part of the
|
||||
// rpcserverSyncManager interface implementation.
|
||||
func (b *rpcSyncMgr) AntiPastHeadersBetween(lowHash, highHash *daghash.Hash) ([]*wire.BlockHeader, error) {
|
||||
return b.server.DAG.AntiPastHeadersBetween(lowHash, highHash)
|
||||
func (b *rpcSyncMgr) AntiPastHeadersBetween(lowHash, highHash *daghash.Hash, maxHeaders uint64) ([]*wire.BlockHeader, error) {
|
||||
return b.server.DAG.AntiPastHeadersBetween(lowHash, highHash, maxHeaders)
|
||||
}
|
||||
|
@ -739,7 +739,7 @@ type rpcserverSyncManager interface {
|
||||
// AntiPastHeadersBetween returns the headers of the blocks between the
|
||||
// lowHash's antiPast and highHash's antiPast, or up to
|
||||
// wire.MaxBlockHeadersPerMsg block headers.
|
||||
AntiPastHeadersBetween(lowHash, highHash *daghash.Hash) ([]*wire.BlockHeader, error)
|
||||
AntiPastHeadersBetween(lowHash, highHash *daghash.Hash, maxHeaders uint64) ([]*wire.BlockHeader, error)
|
||||
}
|
||||
|
||||
// rpcserverConfig is a descriptor containing the RPC server configuration.
|
||||
|
@ -374,69 +374,6 @@ func BenchmarkWriteBlockHeader(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDecodeGetHeaders performs a benchmark on how long it takes to
|
||||
// decode a getheaders message.
|
||||
func BenchmarkDecodeGetHeaders(b *testing.B) {
|
||||
pver := ProtocolVersion
|
||||
var m MsgGetHeaders
|
||||
m.LowHash = &daghash.Hash{1}
|
||||
m.HighHash = &daghash.Hash{1}
|
||||
|
||||
// Serialize it so the bytes are available to test the decode below.
|
||||
var bb bytes.Buffer
|
||||
if err := m.KaspaEncode(&bb, pver); err != nil {
|
||||
b.Fatalf("MsgGetHeaders.KaspaEncode: unexpected error: %v", err)
|
||||
}
|
||||
buf := bb.Bytes()
|
||||
|
||||
r := bytes.NewReader(buf)
|
||||
var msg MsgGetHeaders
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Seek(0, 0)
|
||||
msg.KaspaDecode(r, pver)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDecodeHeaders performs a benchmark on how long it takes to
|
||||
// decode a headers message with the maximum number of headers and maximum number of
|
||||
// parent hashes per header.
|
||||
func BenchmarkDecodeHeaders(b *testing.B) {
|
||||
// Create a message with the maximum number of headers.
|
||||
pver := ProtocolVersion
|
||||
var m MsgHeaders
|
||||
for i := 0; i < MaxBlockHeadersPerMsg; i++ {
|
||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x", i))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
parentHashes := make([]*daghash.Hash, MaxNumParentBlocks)
|
||||
for j := byte(0); j < MaxNumParentBlocks; j++ {
|
||||
hash, err := daghash.NewHashFromStr(fmt.Sprintf("%x%x", i, j))
|
||||
if err != nil {
|
||||
b.Fatalf("NewHashFromStr: unexpected error: %v", err)
|
||||
}
|
||||
parentHashes[i] = hash
|
||||
}
|
||||
m.AddBlockHeader(NewBlockHeader(1, parentHashes, hash, hash, hash, 0, uint64(i)))
|
||||
}
|
||||
|
||||
// Serialize it so the bytes are available to test the decode below.
|
||||
var bb bytes.Buffer
|
||||
if err := m.KaspaEncode(&bb, pver); err != nil {
|
||||
b.Fatalf("MsgHeaders.KaspaEncode: unexpected error: %v", err)
|
||||
}
|
||||
buf := bb.Bytes()
|
||||
|
||||
r := bytes.NewReader(buf)
|
||||
var msg MsgHeaders
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
r.Seek(0, 0)
|
||||
msg.KaspaDecode(r, pver)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDecodeGetBlockInvs performs a benchmark on how long it takes to
|
||||
// decode a getblockinvs message.
|
||||
func BenchmarkDecodeGetBlockInvs(b *testing.B) {
|
||||
|
@ -39,8 +39,6 @@ const (
|
||||
CmdNotFound = "notfound"
|
||||
CmdBlock = "block"
|
||||
CmdTx = "tx"
|
||||
CmdGetHeaders = "getheaders"
|
||||
CmdHeaders = "headers"
|
||||
CmdPing = "ping"
|
||||
CmdPong = "pong"
|
||||
CmdFilterAdd = "filteradd"
|
||||
@ -48,7 +46,6 @@ const (
|
||||
CmdFilterLoad = "filterload"
|
||||
CmdMerkleBlock = "merkleblock"
|
||||
CmdReject = "reject"
|
||||
CmdSendHeaders = "sendheaders"
|
||||
CmdFeeFilter = "feefilter"
|
||||
CmdGetBlockLocator = "getlocator"
|
||||
CmdBlockLocator = "locator"
|
||||
@ -114,12 +111,6 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
case CmdPong:
|
||||
msg = &MsgPong{}
|
||||
|
||||
case CmdGetHeaders:
|
||||
msg = &MsgGetHeaders{}
|
||||
|
||||
case CmdHeaders:
|
||||
msg = &MsgHeaders{}
|
||||
|
||||
case CmdFilterAdd:
|
||||
msg = &MsgFilterAdd{}
|
||||
|
||||
@ -135,9 +126,6 @@ func makeEmptyMessage(command string) (Message, error) {
|
||||
case CmdReject:
|
||||
msg = &MsgReject{}
|
||||
|
||||
case CmdSendHeaders:
|
||||
msg = &MsgSendHeaders{}
|
||||
|
||||
case CmdFeeFilter:
|
||||
msg = &MsgFeeFilter{}
|
||||
|
||||
|
@ -60,12 +60,9 @@ func TestMessage(t *testing.T) {
|
||||
msgTx := NewNativeMsgTx(1, nil, nil)
|
||||
msgPing := NewMsgPing(123123)
|
||||
msgPong := NewMsgPong(123123)
|
||||
msgGetHeaders := NewMsgGetHeaders(&daghash.Hash{}, &daghash.Hash{})
|
||||
msgGetBlockLocator := NewMsgGetBlockLocator(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||
msgBlockLocator := NewMsgBlockLocator()
|
||||
msgSendHeaders := NewMsgSendHeaders()
|
||||
msgFeeFilter := NewMsgFeeFilter(123456)
|
||||
msgHeaders := NewMsgHeaders()
|
||||
msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
|
||||
msgFilterClear := NewMsgFilterClear()
|
||||
msgFilterLoad := NewMsgFilterLoad([]byte{0x01}, 10, 0, BloomUpdateNone)
|
||||
@ -92,12 +89,9 @@ func TestMessage(t *testing.T) {
|
||||
{msgTx, msgTx, pver, Mainnet, 58},
|
||||
{msgPing, msgPing, pver, Mainnet, 32},
|
||||
{msgPong, msgPong, pver, Mainnet, 32},
|
||||
{msgGetHeaders, msgGetHeaders, pver, Mainnet, 88},
|
||||
{msgGetBlockLocator, msgGetBlockLocator, pver, Mainnet, 88},
|
||||
{msgBlockLocator, msgBlockLocator, pver, Mainnet, 25},
|
||||
{msgSendHeaders, msgSendHeaders, pver, Mainnet, 24},
|
||||
{msgFeeFilter, msgFeeFilter, pver, Mainnet, 32},
|
||||
{msgHeaders, msgHeaders, pver, Mainnet, 25},
|
||||
{msgFilterAdd, msgFilterAdd, pver, Mainnet, 26},
|
||||
{msgFilterClear, msgFilterClear, pver, Mainnet, 24},
|
||||
{msgFilterLoad, msgFilterLoad, pver, Mainnet, 35},
|
||||
|
@ -1,78 +0,0 @@
|
||||
// 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"
|
||||
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
// MsgGetHeaders implements the Message interface and represents a kaspa
|
||||
// getheaders message. It is used to request a list of block headers for
|
||||
// blocks starting after the last known hash in the slice of block locator
|
||||
// hashes. The list is returned via a headers message (MsgHeaders) and is
|
||||
// limited by a specific hash to stop at or the maximum number of block headers
|
||||
// per message, which is currently 2000.
|
||||
//
|
||||
// Set the HighHash field to the hash at which to stop and use
|
||||
// AddBlockLocatorHash to build up the list of block locator hashes.
|
||||
//
|
||||
// The algorithm for building the block locator hashes should be to add the
|
||||
// hashes in reverse order until you reach the genesis block. In order to keep
|
||||
// the list of locator hashes to a resonable number of entries, first add the
|
||||
// most recent 10 block hashes, then double the step each loop iteration to
|
||||
// exponentially decrease the number of hashes the further away from head and
|
||||
// closer to the genesis block you get.
|
||||
type MsgGetHeaders struct {
|
||||
LowHash *daghash.Hash
|
||||
HighHash *daghash.Hash
|
||||
}
|
||||
|
||||
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) KaspaDecode(r io.Reader, pver uint32) error {
|
||||
msg.LowHash = &daghash.Hash{}
|
||||
err := ReadElement(r, msg.LowHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.HighHash = &daghash.Hash{}
|
||||
return ReadElement(r, msg.HighHash)
|
||||
}
|
||||
|
||||
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) KaspaEncode(w io.Writer, pver uint32) error {
|
||||
err := WriteElement(w, msg.LowHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return WriteElement(w, msg.HighHash)
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) Command() string {
|
||||
return CmdGetHeaders
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgGetHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||
// low hash + high hash.
|
||||
return 2 * daghash.HashSize
|
||||
}
|
||||
|
||||
// NewMsgGetHeaders returns a new kaspa getheaders message that conforms to
|
||||
// the Message interface. See MsgGetHeaders for details.
|
||||
func NewMsgGetHeaders(lowHash, highHash *daghash.Hash) *MsgGetHeaders {
|
||||
return &MsgGetHeaders{
|
||||
LowHash: lowHash,
|
||||
HighHash: highHash,
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
// 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"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
// TestGetHeaders tests the MsgGetHeader API.
|
||||
func TestGetHeaders(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Block 99500 hash.
|
||||
hashStr := "000000000002e7ad7b9eef9479e4aabc65cb831269cc20d2632c13684406dee0"
|
||||
lowHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "getheaders"
|
||||
msg := NewMsgGetHeaders(lowHash, &daghash.ZeroHash)
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgGetHeaders: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is low hash (32 bytes) + high hash (32 bytes)..
|
||||
wantPayload := uint32(64)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetHeadersWire tests the MsgGetHeaders wire encode and decode.
|
||||
func TestGetHeadersWire(t *testing.T) {
|
||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||
lowHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||
highHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// MsgGetHeaders message with no block locators or high hash.
|
||||
noLowAndHighHash := NewMsgGetHeaders(&daghash.ZeroHash, &daghash.ZeroHash)
|
||||
noLowAndHighHashEncoded := []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, // Low hash
|
||||
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, // High hash
|
||||
}
|
||||
|
||||
// MsgGetHeaders message with multiple block locators and a high hash.
|
||||
withLowAndHighHash := NewMsgGetHeaders(lowHash, highHash)
|
||||
withLowAndHighHashEncoded := []byte{
|
||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Low hash
|
||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgGetHeaders // Message to encode
|
||||
out *MsgGetHeaders // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
// Message with no low hash and high hash.
|
||||
{
|
||||
noLowAndHighHash,
|
||||
noLowAndHighHash,
|
||||
noLowAndHighHashEncoded,
|
||||
ProtocolVersion,
|
||||
},
|
||||
|
||||
// Message with low hash and high hash.
|
||||
{
|
||||
withLowAndHighHash,
|
||||
withLowAndHighHash,
|
||||
withLowAndHighHashEncoded,
|
||||
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 MsgGetHeaders
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetHeadersWireErrors performs negative tests against wire encode and
|
||||
// decode of MsgGetHeaders to confirm error paths work correctly.
|
||||
func TestGetHeadersWireErrors(t *testing.T) {
|
||||
// Set protocol inside getheaders message.
|
||||
pver := ProtocolVersion
|
||||
|
||||
hashStr := "2710f40c87ec93d010a6fd95f42c59a2cbacc60b18cf6b7957535"
|
||||
lowHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
||||
highHash, err := daghash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
t.Errorf("NewHashFromStr: %v", err)
|
||||
}
|
||||
|
||||
// MsgGetHeaders message with multiple block locators and a high hash.
|
||||
baseGetHeaders := NewMsgGetHeaders(lowHash, highHash)
|
||||
baseGetHeadersEncoded := []byte{
|
||||
0x35, 0x75, 0x95, 0xb7, 0xf6, 0x8c, 0xb1, 0x60,
|
||||
0xcc, 0xba, 0x2c, 0x9a, 0xc5, 0x42, 0x5f, 0xd9,
|
||||
0x6f, 0x0a, 0x01, 0x3d, 0xc9, 0x7e, 0xc8, 0x40,
|
||||
0x0f, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Low hash
|
||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39,
|
||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2,
|
||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa,
|
||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // High hash
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgGetHeaders // Value to encode
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Force error in low hash.
|
||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||
// Force error in high hash.
|
||||
{baseGetHeaders, baseGetHeadersEncoded, pver, 32, io.ErrShortWrite, io.EOF},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to wire format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := test.in.KaspaEncode(w, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
|
||||
if err != test.writeErr {
|
||||
t.Errorf("KaspaEncode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgGetHeaders
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.KaspaDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
|
||||
if err != test.readErr {
|
||||
t.Errorf("KaspaDecode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// MaxBlockHeadersPerMsg is the maximum number of block headers that can be in
|
||||
// a single kaspa headers message.
|
||||
const MaxBlockHeadersPerMsg = 2000
|
||||
|
||||
// MsgHeaders implements the Message interface and represents a kaspa headers
|
||||
// message. It is used to deliver block header information in response
|
||||
// to a getheaders message (MsgGetHeaders). The maximum number of block headers
|
||||
// per message is currently 2000. See MsgGetHeaders for details on requesting
|
||||
// the headers.
|
||||
type MsgHeaders struct {
|
||||
Headers []*BlockHeader
|
||||
}
|
||||
|
||||
// AddBlockHeader adds a new block header to the message.
|
||||
func (msg *MsgHeaders) AddBlockHeader(bh *BlockHeader) error {
|
||||
if len(msg.Headers)+1 > MaxBlockHeadersPerMsg {
|
||||
str := fmt.Sprintf("too many block headers in message [max %d]",
|
||||
MaxBlockHeadersPerMsg)
|
||||
return messageError("MsgHeaders.AddBlockHeader", str)
|
||||
}
|
||||
|
||||
msg.Headers = append(msg.Headers, bh)
|
||||
return nil
|
||||
}
|
||||
|
||||
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgHeaders) KaspaDecode(r io.Reader, pver uint32) error {
|
||||
count, err := ReadVarInt(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Limit to max block headers per message.
|
||||
if count > MaxBlockHeadersPerMsg {
|
||||
str := fmt.Sprintf("too many block headers for message "+
|
||||
"[count %d, max %d]", count, MaxBlockHeadersPerMsg)
|
||||
return messageError("MsgHeaders.KaspaDecode", str)
|
||||
}
|
||||
|
||||
// Create a contiguous slice of headers to deserialize into in order to
|
||||
// reduce the number of allocations.
|
||||
headers := make([]BlockHeader, count)
|
||||
msg.Headers = make([]*BlockHeader, 0, count)
|
||||
for i := uint64(0); i < count; i++ {
|
||||
bh := &headers[i]
|
||||
err := readBlockHeader(r, pver, bh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
txCount, err := ReadVarInt(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure the transaction count is zero for headers.
|
||||
if txCount > 0 {
|
||||
str := fmt.Sprintf("block headers may not contain "+
|
||||
"transactions [count %d]", txCount)
|
||||
return messageError("MsgHeaders.KaspaDecode", str)
|
||||
}
|
||||
msg.AddBlockHeader(bh)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// KaspaEncode encodes the receiver to w using the kaspa protocol encoding.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgHeaders) KaspaEncode(w io.Writer, pver uint32) error {
|
||||
// Limit to max block headers per message.
|
||||
count := len(msg.Headers)
|
||||
if count > MaxBlockHeadersPerMsg {
|
||||
str := fmt.Sprintf("too many block headers for message "+
|
||||
"[count %d, max %d]", count, MaxBlockHeadersPerMsg)
|
||||
return messageError("MsgHeaders.KaspaEncode", str)
|
||||
}
|
||||
|
||||
err := WriteVarInt(w, uint64(count))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, bh := range msg.Headers {
|
||||
err := writeBlockHeader(w, pver, bh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The wire protocol encoding always includes a 0 for the number
|
||||
// of transactions on header messages. This is really just an
|
||||
// artifact of the way the original implementation serializes
|
||||
// block headers, but it is required.
|
||||
err = WriteVarInt(w, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Command returns the protocol command string for the message. This is part
|
||||
// of the Message interface implementation.
|
||||
func (msg *MsgHeaders) Command() string {
|
||||
return CmdHeaders
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||
// Num headers (varInt) + max allowed headers (header length + 1 byte
|
||||
// for the number of transactions which is always 0).
|
||||
return MaxVarIntPayload + ((MaxBlockHeaderPayload + 1) *
|
||||
MaxBlockHeadersPerMsg)
|
||||
}
|
||||
|
||||
// NewMsgHeaders returns a new kaspa headers message that conforms to the
|
||||
// Message interface. See MsgHeaders for details.
|
||||
func NewMsgHeaders() *MsgHeaders {
|
||||
return &MsgHeaders{
|
||||
Headers: make([]*BlockHeader, 0, MaxBlockHeadersPerMsg),
|
||||
}
|
||||
}
|
@ -1,329 +0,0 @@
|
||||
// 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"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/kaspanet/kaspad/util/daghash"
|
||||
)
|
||||
|
||||
// TestHeaders tests the MsgHeaders API.
|
||||
func TestHeaders(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "headers"
|
||||
msg := NewMsgHeaders()
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgHeaders: wrong command - got %v want %v",
|
||||
cmd, wantCmd)
|
||||
}
|
||||
|
||||
// Ensure max payload is expected value for latest protocol version.
|
||||
// Num headers (varInt) + max allowed headers (header length + 1 byte
|
||||
// for the number of transactions which is always 0).
|
||||
wantPayload := uint32(16564009)
|
||||
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 headers are added properly.
|
||||
bh := &blockOne.Header
|
||||
msg.AddBlockHeader(bh)
|
||||
if !reflect.DeepEqual(msg.Headers[0], bh) {
|
||||
t.Errorf("AddHeader: wrong header - got %v, want %v",
|
||||
spew.Sdump(msg.Headers),
|
||||
spew.Sdump(bh))
|
||||
}
|
||||
|
||||
// Ensure adding more than the max allowed headers per message returns
|
||||
// error.
|
||||
var err error
|
||||
for i := 0; i < MaxBlockHeadersPerMsg+1; i++ {
|
||||
err = msg.AddBlockHeader(bh)
|
||||
}
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(&MessageError{}) {
|
||||
t.Errorf("AddBlockHeader: expected error on too many headers " +
|
||||
"not received")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeadersWire tests the MsgHeaders wire encode and decode for various
|
||||
// numbers of headers and protocol versions.
|
||||
func TestHeadersWire(t *testing.T) {
|
||||
hashes := []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash}
|
||||
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
||||
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
||||
utxoCommitment := blockOne.Header.UTXOCommitment
|
||||
bits := uint32(0x1d00ffff)
|
||||
nonce := uint64(0x9962e301)
|
||||
bh := NewBlockHeader(1, hashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||
bh.Version = blockOne.Header.Version
|
||||
bh.Timestamp = blockOne.Header.Timestamp
|
||||
|
||||
// Empty headers message.
|
||||
noHeaders := NewMsgHeaders()
|
||||
noHeadersEncoded := []byte{
|
||||
0x00, // Varint for number of headers
|
||||
}
|
||||
|
||||
// Headers message with one header.
|
||||
oneHeader := NewMsgHeaders()
|
||||
oneHeader.AddBlockHeader(bh)
|
||||
oneHeaderEncoded := []byte{
|
||||
0x01, // VarInt for number of headers.
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
0x02, // NumParentBlocks
|
||||
0xdc, 0x5f, 0x5b, 0x5b, 0x1d, 0xc2, 0xa7, 0x25, // mainnetGenesisHash
|
||||
0x49, 0xd5, 0x1d, 0x4d, 0xee, 0xd7, 0xa4, 0x8b,
|
||||
0xaf, 0xd3, 0x14, 0x4b, 0x56, 0x78, 0x98, 0xb1,
|
||||
0x8c, 0xfd, 0x9f, 0x69, 0xdd, 0xcf, 0xbb, 0x63,
|
||||
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||
0x4a, 0x5e, 0x1e, 0x4b, 0xaa, 0xb8, 0x9f, 0x3a, // MerkleRoot
|
||||
0x32, 0x51, 0x8a, 0x88, 0xc3, 0x1b, 0xc8, 0x7f,
|
||||
0x61, 0x8f, 0x76, 0x67, 0x3e, 0x2c, 0xc7, 0x7a,
|
||||
0xb2, 0x12, 0x7b, 0x7a, 0xfd, 0xed, 0xa3, 0x3b,
|
||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x61, 0xbc, 0x66, 0x49, 0x00, 0x00, 0x00, 0x00, // Timestamp
|
||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce
|
||||
0x00, // TxnCount (0 for headers message)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgHeaders // Message to encode
|
||||
out *MsgHeaders // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
// Latest protocol version with no headers.
|
||||
{
|
||||
noHeaders,
|
||||
noHeaders,
|
||||
noHeadersEncoded,
|
||||
ProtocolVersion,
|
||||
},
|
||||
|
||||
// Latest protocol version with one header.
|
||||
{
|
||||
oneHeader,
|
||||
oneHeader,
|
||||
oneHeaderEncoded,
|
||||
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 MsgHeaders
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestHeadersWireErrors performs negative tests against wire encode and decode
|
||||
// of MsgHeaders to confirm error paths work correctly.
|
||||
func TestHeadersWireErrors(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
wireErr := &MessageError{}
|
||||
|
||||
hashes := []*daghash.Hash{mainnetGenesisHash, simnetGenesisHash}
|
||||
hashMerkleRoot := blockOne.Header.HashMerkleRoot
|
||||
acceptedIDMerkleRoot := blockOne.Header.AcceptedIDMerkleRoot
|
||||
utxoCommitment := blockOne.Header.UTXOCommitment
|
||||
bits := uint32(0x1d00ffff)
|
||||
nonce := uint64(0x9962e301)
|
||||
bh := NewBlockHeader(1, hashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||
bh.Version = blockOne.Header.Version
|
||||
bh.Timestamp = blockOne.Header.Timestamp
|
||||
|
||||
// Headers message with one header.
|
||||
oneHeader := NewMsgHeaders()
|
||||
oneHeader.AddBlockHeader(bh)
|
||||
oneHeaderEncoded := []byte{
|
||||
0x01, // VarInt for number of headers.
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
0x02, // NumParentBlocks
|
||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, // mainnetGenesisHash
|
||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, // MerkleRoot
|
||||
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
||||
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
||||
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
|
||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x61, 0xbc, 0x66, 0x49, 0x00, 0x00, 0x00, 0x00, // Timestamp
|
||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce
|
||||
0x00, // TxnCount (0 for headers message)
|
||||
}
|
||||
|
||||
// Message that forces an error by having more than the max allowed
|
||||
// headers.
|
||||
maxHeaders := NewMsgHeaders()
|
||||
for i := 0; i < MaxBlockHeadersPerMsg; i++ {
|
||||
maxHeaders.AddBlockHeader(bh)
|
||||
}
|
||||
maxHeaders.Headers = append(maxHeaders.Headers, bh)
|
||||
maxHeadersEncoded := []byte{
|
||||
0xfd, 0xd1, 0x07, // Varint for number of addresses (2001)7D1
|
||||
}
|
||||
|
||||
// Intentionally invalid block header that has a transaction count used
|
||||
// to force errors.
|
||||
bhTrans := NewBlockHeader(1, hashes, hashMerkleRoot, acceptedIDMerkleRoot, utxoCommitment, bits, nonce)
|
||||
bhTrans.Version = blockOne.Header.Version
|
||||
bhTrans.Timestamp = blockOne.Header.Timestamp
|
||||
|
||||
transHeader := NewMsgHeaders()
|
||||
transHeader.AddBlockHeader(bhTrans)
|
||||
transHeaderEncoded := []byte{
|
||||
0x01, // VarInt for number of headers.
|
||||
0x01, 0x00, 0x00, 0x00, // Version 1
|
||||
0x02, // NumParentBlocks
|
||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, // mainnetGenesisHash
|
||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f,
|
||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c,
|
||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xf6, 0x7a, 0xd7, 0x69, 0x5d, 0x9b, 0x66, 0x2a, // simnetGenesisHash
|
||||
0x72, 0xff, 0x3d, 0x8e, 0xdb, 0xbb, 0x2d, 0xe0,
|
||||
0xbf, 0xa6, 0x7b, 0x13, 0x97, 0x4b, 0xb9, 0x91,
|
||||
0x0d, 0x11, 0x6d, 0x5c, 0xbd, 0x86, 0x3e, 0x68,
|
||||
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, // HashMerkleRoot
|
||||
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61,
|
||||
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32,
|
||||
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a,
|
||||
0x09, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // AcceptedIDMerkleRoot
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x10, 0x3B, 0xC7, 0xE3, 0x67, 0x11, 0x7B, 0x3C, // UTXOCommitment
|
||||
0x30, 0xC1, 0xF8, 0xFD, 0xD0, 0xD9, 0x72, 0x87,
|
||||
0x7F, 0x16, 0xC5, 0x96, 0x2E, 0x8B, 0xD9, 0x63,
|
||||
0x65, 0x9C, 0x79, 0x3C, 0xE3, 0x70, 0xD9, 0x5F,
|
||||
0x61, 0xbc, 0x66, 0x49, 0x00, 0x00, 0x00, 0x00, // Timestamp
|
||||
0xff, 0xff, 0x00, 0x1d, // Bits
|
||||
0x01, 0xe3, 0x62, 0x99, 0x00, 0x00, 0x00, 0x00, // Fake Nonce. TODO: (Ori) Replace to a real nonce
|
||||
0x01, // TxnCount (should be 0 for headers message, but 1 to force error)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgHeaders // Value to encode
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
max int // Max size of fixed buffer to induce errors
|
||||
writeErr error // Expected write error
|
||||
readErr error // Expected read error
|
||||
}{
|
||||
// Latest protocol version with intentional read/write errors.
|
||||
// Force error in header count.
|
||||
{oneHeader, oneHeaderEncoded, pver, 0, io.ErrShortWrite, io.EOF},
|
||||
// Force error in block header.
|
||||
{oneHeader, oneHeaderEncoded, pver, 5, io.ErrShortWrite, io.EOF},
|
||||
// Force error with greater than max headers.
|
||||
{maxHeaders, maxHeadersEncoded, pver, 3, wireErr, wireErr},
|
||||
// Force error with number of transactions.
|
||||
{transHeader, transHeaderEncoded, pver, 178, io.ErrShortWrite, io.EOF},
|
||||
// Force error with included transactions.
|
||||
{transHeader, transHeaderEncoded, pver, len(transHeaderEncoded), nil, wireErr},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
// Encode to wire format.
|
||||
w := newFixedWriter(test.max)
|
||||
err := test.in.KaspaEncode(w, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
|
||||
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
|
||||
if err != test.writeErr {
|
||||
t.Errorf("KaspaEncode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.writeErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Decode from wire format.
|
||||
var msg MsgHeaders
|
||||
r := newFixedReader(test.max, test.buf)
|
||||
err = msg.KaspaDecode(r, test.pver)
|
||||
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
|
||||
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
|
||||
i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
|
||||
// For errors which are not of type MessageError, check them for
|
||||
// equality.
|
||||
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
|
||||
if err != test.readErr {
|
||||
t.Errorf("KaspaDecode #%d wrong error got: %v, "+
|
||||
"want: %v", i, err, test.readErr)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// Copyright (c) 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"
|
||||
)
|
||||
|
||||
// MsgSendHeaders implements the Message interface and represents a kaspa
|
||||
// sendheaders message. It is used to request the peer send block headers
|
||||
// rather than inventory vectors.
|
||||
//
|
||||
// This message has no payload.
|
||||
type MsgSendHeaders struct{}
|
||||
|
||||
// KaspaDecode decodes r using the kaspa protocol encoding into the receiver.
|
||||
// This is part of the Message interface implementation.
|
||||
func (msg *MsgSendHeaders) 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 *MsgSendHeaders) 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 *MsgSendHeaders) Command() string {
|
||||
return CmdSendHeaders
|
||||
}
|
||||
|
||||
// MaxPayloadLength returns the maximum length the payload can be for the
|
||||
// receiver. This is part of the Message interface implementation.
|
||||
func (msg *MsgSendHeaders) MaxPayloadLength(pver uint32) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// NewMsgSendHeaders returns a new kaspa sendheaders message that conforms to
|
||||
// the Message interface. See MsgSendHeaders for details.
|
||||
func NewMsgSendHeaders() *MsgSendHeaders {
|
||||
return &MsgSendHeaders{}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
// Copyright (c) 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"
|
||||
)
|
||||
|
||||
// TestSendHeaders tests the MsgSendHeaders API against the latest protocol
|
||||
// version.
|
||||
func TestSendHeaders(t *testing.T) {
|
||||
pver := ProtocolVersion
|
||||
|
||||
// Ensure the command is expected value.
|
||||
wantCmd := "sendheaders"
|
||||
msg := NewMsgSendHeaders()
|
||||
if cmd := msg.Command(); cmd != wantCmd {
|
||||
t.Errorf("NewMsgSendHeaders: 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)
|
||||
}
|
||||
|
||||
// Test encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.KaspaEncode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg,
|
||||
err)
|
||||
}
|
||||
|
||||
// Test decode with latest protocol version.
|
||||
readmsg := NewMsgSendHeaders()
|
||||
err = readmsg.KaspaDecode(&buf, pver)
|
||||
if err != nil {
|
||||
t.Errorf("decode of MsgSendHeaders failed [%v] err <%v>", buf,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendHeadersCrossProtocol tests the MsgSendHeaders API when encoding with
|
||||
// the latest protocol version and decoding with SendHeadersVersion.
|
||||
func TestSendHeadersCrossProtocol(t *testing.T) {
|
||||
msg := NewMsgSendHeaders()
|
||||
|
||||
// Encode with latest protocol version.
|
||||
var buf bytes.Buffer
|
||||
err := msg.KaspaEncode(&buf, ProtocolVersion)
|
||||
if err != nil {
|
||||
t.Errorf("encode of MsgSendHeaders failed %v err <%v>", msg,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendHeadersWire tests the MsgSendHeaders wire encode and decode for
|
||||
// various protocol versions.
|
||||
func TestSendHeadersWire(t *testing.T) {
|
||||
msgSendHeaders := NewMsgSendHeaders()
|
||||
msgSendHeadersEncoded := []byte{}
|
||||
|
||||
tests := []struct {
|
||||
in *MsgSendHeaders // Message to encode
|
||||
out *MsgSendHeaders // Expected decoded message
|
||||
buf []byte // Wire encoding
|
||||
pver uint32 // Protocol version for wire encoding
|
||||
}{
|
||||
// Latest protocol version.
|
||||
{
|
||||
msgSendHeaders,
|
||||
msgSendHeaders,
|
||||
msgSendHeadersEncoded,
|
||||
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 MsgSendHeaders
|
||||
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