mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
peer: Refactor peer code into its own package.
Contains the following upstream commits: -00bddf7540-250228c32f-f1bd2f8d6e-cbbe3a8bbe-aa03d68e1eIn addition to the normal required changes for syncing, the following changes have been made in order to facilitate integration of the new package into Decred: - Removed check in PushRejectMsg against protocol version since all peers since the initial version support it - Fixed leaked timer in `syncMiningStateAfterSync` function - Add the Decred-specific OnGetMiningState and OnMiningState handlers to the new peer package - Add handler for the new mining state messages to the 'serverPeer' type and register them - Use the new constant for wire.InitialProtocolVersion in the reject message instead of hard coded number - Remove logic specific to the regression network since Decred does not have it
This commit is contained in:
commit
756eff2fee
134
blockmanager.go
134
blockmanager.go
@ -46,48 +46,51 @@ const (
|
||||
maxResendLimit = 3
|
||||
)
|
||||
|
||||
// zeroHash is the zero value hash (all zeros). It is defined as a convenience.
|
||||
var zeroHash chainhash.Hash
|
||||
|
||||
// newPeerMsg signifies a newly connected peer to the block handler.
|
||||
type newPeerMsg struct {
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// blockMsg packages a decred block message and the peer it came from together
|
||||
// so the block handler has access to that information.
|
||||
type blockMsg struct {
|
||||
block *dcrutil.Block
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// invMsg packages a decred inv message and the peer it came from together
|
||||
// so the block handler has access to that information.
|
||||
type invMsg struct {
|
||||
inv *wire.MsgInv
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// headersMsg packages a decred headers message and the peer it came from
|
||||
// together so the block handler has access to that information.
|
||||
type headersMsg struct {
|
||||
headers *wire.MsgHeaders
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// donePeerMsg signifies a newly disconnected peer to the block handler.
|
||||
type donePeerMsg struct {
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// txMsg packages a decred tx message and the peer it came from together
|
||||
// so the block handler has access to that information.
|
||||
type txMsg struct {
|
||||
tx *dcrutil.Tx
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
}
|
||||
|
||||
// getSyncPeerMsg is a message type to be sent across the message channel for
|
||||
// retrieving the current sync peer.
|
||||
type getSyncPeerMsg struct {
|
||||
reply chan *peer
|
||||
reply chan *serverPeer
|
||||
}
|
||||
|
||||
// requestFromPeerMsg is a message type to be sent across the message channel
|
||||
@ -95,13 +98,13 @@ type getSyncPeerMsg struct {
|
||||
// this through the block manager so the block manager doesn't ban the peer
|
||||
// when it sends this information back.
|
||||
type requestFromPeerMsg struct {
|
||||
peer *peer
|
||||
peer *serverPeer
|
||||
blocks []*chainhash.Hash
|
||||
txs []*chainhash.Hash
|
||||
reply chan requestFromPeerResponse
|
||||
}
|
||||
|
||||
// requestFromPeerRespons eis a response sent to the reply channel of a
|
||||
// requestFromPeerResponse is a response sent to the reply channel of a
|
||||
// requestFromPeerMsg query.
|
||||
type requestFromPeerResponse struct {
|
||||
err error
|
||||
@ -567,7 +570,7 @@ type blockManager struct {
|
||||
receivedLogTx int64
|
||||
lastBlockLogTime time.Time
|
||||
processingReqs bool
|
||||
syncPeer *peer
|
||||
syncPeer *serverPeer
|
||||
msgChan chan interface{}
|
||||
chainState chainState
|
||||
wg sync.WaitGroup
|
||||
@ -686,11 +689,11 @@ func (b *blockManager) startSync(peers *list.List) {
|
||||
return
|
||||
}
|
||||
|
||||
var bestPeer *peer
|
||||
var bestPeer *serverPeer
|
||||
var enext *list.Element
|
||||
for e := peers.Front(); e != nil; e = enext {
|
||||
enext = e.Next()
|
||||
p := e.Value.(*peer)
|
||||
sp := e.Value.(*serverPeer)
|
||||
|
||||
// Remove sync candidate peers that are no longer candidates due
|
||||
// to passing their latest known block. NOTE: The < is
|
||||
@ -698,14 +701,14 @@ func (b *blockManager) startSync(peers *list.List) {
|
||||
// doesn't have a later block when it's equal, it will likely
|
||||
// have one soon so it is a reasonable choice. It also allows
|
||||
// the case where both are at 0 such as during regression test.
|
||||
if p.lastBlock < int32(height) {
|
||||
if sp.LastBlock() < int32(height) {
|
||||
peers.Remove(e)
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO(davec): Use a better algorithm to choose the best peer.
|
||||
// For now, just pick the first available candidate.
|
||||
bestPeer = p
|
||||
bestPeer = sp
|
||||
}
|
||||
|
||||
// Start syncing from the best peer if one was selected.
|
||||
@ -718,7 +721,7 @@ func (b *blockManager) startSync(peers *list.List) {
|
||||
}
|
||||
|
||||
bmgrLog.Infof("Syncing to block height %d from peer %v",
|
||||
bestPeer.lastBlock, bestPeer.addr)
|
||||
bestPeer.LastBlock(), bestPeer.Addr())
|
||||
|
||||
// When the current height is less than a known checkpoint we
|
||||
// can use block headers to learn about which blocks comprise
|
||||
@ -744,7 +747,7 @@ func (b *blockManager) startSync(peers *list.List) {
|
||||
b.headersFirstMode = true
|
||||
bmgrLog.Infof("Downloading headers for blocks %d to "+
|
||||
"%d from peer %s", height+1,
|
||||
b.nextCheckpoint.Height, bestPeer.addr)
|
||||
b.nextCheckpoint.Height, bestPeer.Addr())
|
||||
} else {
|
||||
bestPeer.PushGetBlocksMsg(locator, &zeroHash)
|
||||
}
|
||||
@ -756,9 +759,9 @@ func (b *blockManager) startSync(peers *list.List) {
|
||||
|
||||
// isSyncCandidate returns whether or not the peer is a candidate to consider
|
||||
// syncing from.
|
||||
func (b *blockManager) isSyncCandidate(p *peer) bool {
|
||||
func (b *blockManager) isSyncCandidate(sp *serverPeer) bool {
|
||||
// The peer is not a candidate for sync if it's not a full node.
|
||||
if p.services&wire.SFNodeNetwork != wire.SFNodeNetwork {
|
||||
if sp.Services()&wire.SFNodeNetwork != wire.SFNodeNetwork {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -769,14 +772,16 @@ func (b *blockManager) isSyncCandidate(p *peer) bool {
|
||||
// syncMiningStateAfterSync polls the blockMananger for the current sync
|
||||
// state; if the mananger is synced, it executes a call to the peer to
|
||||
// sync the mining state to the network.
|
||||
func (b *blockManager) syncMiningStateAfterSync(p *peer) {
|
||||
ticker := time.NewTicker(time.Second * 3)
|
||||
func (b *blockManager) syncMiningStateAfterSync(sp *serverPeer) {
|
||||
go func() {
|
||||
ticker := time.NewTicker(time.Second * 3)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if b.IsCurrent() {
|
||||
p.PushGetMiningStateMsg()
|
||||
msg := wire.NewMsgGetMiningState()
|
||||
sp.QueueMessage(msg, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -787,28 +792,28 @@ func (b *blockManager) syncMiningStateAfterSync(p *peer) {
|
||||
// handleNewPeerMsg deals with new peers that have signalled they may
|
||||
// be considered as a sync peer (they have already successfully negotiated). It
|
||||
// also starts syncing if needed. It is invoked from the syncHandler goroutine.
|
||||
func (b *blockManager) handleNewPeerMsg(peers *list.List, p *peer) {
|
||||
func (b *blockManager) handleNewPeerMsg(peers *list.List, sp *serverPeer) {
|
||||
// Ignore if in the process of shutting down.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bmgrLog.Infof("New valid peer %s (%s)", p, p.userAgent)
|
||||
bmgrLog.Infof("New valid peer %s (%s)", sp, sp.UserAgent())
|
||||
|
||||
// Ignore the peer if it's not a sync candidate.
|
||||
if !b.isSyncCandidate(p) {
|
||||
if !b.isSyncCandidate(sp) {
|
||||
return
|
||||
}
|
||||
|
||||
// Add the peer as a candidate to sync from.
|
||||
peers.PushBack(p)
|
||||
peers.PushBack(sp)
|
||||
|
||||
// Start syncing by choosing the best candidate if needed.
|
||||
b.startSync(peers)
|
||||
|
||||
// Grab the mining state from this peer after we're synced.
|
||||
if !cfg.NoMiningStateSync {
|
||||
b.syncMiningStateAfterSync(p)
|
||||
b.syncMiningStateAfterSync(sp)
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,20 +821,20 @@ func (b *blockManager) handleNewPeerMsg(peers *list.List, p *peer) {
|
||||
// removes the peer as a candidate for syncing and in the case where it was
|
||||
// the current sync peer, attempts to select a new best peer to sync from. It
|
||||
// is invoked from the syncHandler goroutine.
|
||||
func (b *blockManager) handleDonePeerMsg(peers *list.List, p *peer) {
|
||||
func (b *blockManager) handleDonePeerMsg(peers *list.List, sp *serverPeer) {
|
||||
// Remove the peer from the list of candidate peers.
|
||||
for e := peers.Front(); e != nil; e = e.Next() {
|
||||
if e.Value == p {
|
||||
if e.Value == sp {
|
||||
peers.Remove(e)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
bmgrLog.Infof("Lost peer %s", p)
|
||||
bmgrLog.Infof("Lost peer %s", sp)
|
||||
|
||||
// Remove requested transactions from the global map so that they will
|
||||
// be fetched from elsewhere next time we get an inv.
|
||||
for k := range p.requestedTxns {
|
||||
for k := range sp.requestedTxns {
|
||||
delete(b.requestedTxns, k)
|
||||
}
|
||||
|
||||
@ -837,14 +842,14 @@ func (b *blockManager) handleDonePeerMsg(peers *list.List, p *peer) {
|
||||
// fetched from elsewhere next time we get an inv.
|
||||
// TODO(oga) we could possibly here check which peers have these blocks
|
||||
// and request them now to speed things up a little.
|
||||
for k := range p.requestedBlocks {
|
||||
for k := range sp.requestedBlocks {
|
||||
delete(b.requestedBlocks, k)
|
||||
}
|
||||
|
||||
// Attempt to find a new peer to sync from if the quitting peer is the
|
||||
// sync peer. Also, reset the headers-first state if in headers-first
|
||||
// mode so
|
||||
if b.syncPeer != nil && b.syncPeer == p {
|
||||
if b.syncPeer != nil && b.syncPeer == sp {
|
||||
b.syncPeer = nil
|
||||
if b.headersFirstMode {
|
||||
// This really shouldn't fail. We have a fairly
|
||||
@ -967,7 +972,7 @@ func (b *blockManager) current() bool {
|
||||
// TODO(oga) we can get chain to return the height of each block when we
|
||||
// parse an orphan, which would allow us to update the height of peers
|
||||
// from what it was at initial handshake.
|
||||
if err != nil || height < int64(b.syncPeer.startingHeight) {
|
||||
if err != nil || height < int64(b.syncPeer.LastBlock()) {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -1157,24 +1162,24 @@ func (b *blockManager) checkBlockForHiddenVotes(block *dcrutil.Block) {
|
||||
func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
// If we didn't ask for this block then the peer is misbehaving.
|
||||
blockSha := bmsg.block.Sha()
|
||||
if _, ok := bmsg.peer.requestedBlocks[*blockSha]; !ok {
|
||||
if _, exists := bmsg.peer.requestedBlocks[*blockSha]; !exists {
|
||||
// Check to see if we ever requested this block, since it may
|
||||
// have been accidentally sent in duplicate. If it was,
|
||||
// increment the counter in the ever requested map and make
|
||||
// sure that the node isn't spamming us with these blocks.
|
||||
received, ok := b.requestedEverBlocks[*blockSha]
|
||||
if ok {
|
||||
received, exists := b.requestedEverBlocks[*blockSha]
|
||||
if exists {
|
||||
if received > maxResendLimit {
|
||||
bmgrLog.Warnf("Got duplicate block %v from %s -- "+
|
||||
"too many times, disconnecting", blockSha,
|
||||
bmsg.peer.addr)
|
||||
bmsg.peer.Addr())
|
||||
bmsg.peer.Disconnect()
|
||||
return
|
||||
}
|
||||
b.requestedEverBlocks[*blockSha]++
|
||||
} else {
|
||||
bmgrLog.Warnf("Got unrequested block %v from %s -- "+
|
||||
"disconnecting", blockSha, bmsg.peer.addr)
|
||||
"disconnecting", blockSha, bmsg.peer.Addr())
|
||||
bmsg.peer.Disconnect()
|
||||
return
|
||||
}
|
||||
@ -1424,12 +1429,12 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
err := bmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash)
|
||||
if err != nil {
|
||||
bmgrLog.Warnf("Failed to send getheaders message to "+
|
||||
"peer %s: %v", bmsg.peer.addr, err)
|
||||
"peer %s: %v", bmsg.peer.Addr(), err)
|
||||
return
|
||||
}
|
||||
bmgrLog.Infof("Downloading headers for blocks %d to %d from "+
|
||||
"peer %s", prevHeight+1, b.nextCheckpoint.Height,
|
||||
b.syncPeer.addr)
|
||||
b.syncPeer.Addr())
|
||||
return
|
||||
}
|
||||
|
||||
@ -1443,7 +1448,7 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
|
||||
err = bmsg.peer.PushGetBlocksMsg(locator, &zeroHash)
|
||||
if err != nil {
|
||||
bmgrLog.Warnf("Failed to send getblocks message to peer %s: %v",
|
||||
bmsg.peer.addr, err)
|
||||
bmsg.peer.Addr(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -1493,14 +1498,14 @@ func (b *blockManager) fetchHeaderBlocks() {
|
||||
}
|
||||
}
|
||||
|
||||
// handleHeadersMsghandles headers messages from all peers.
|
||||
// handleHeadersMsg handles headers messages from all peers.
|
||||
func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
|
||||
// The remote peer is misbehaving if we didn't request headers.
|
||||
msg := hmsg.headers
|
||||
numHeaders := len(msg.Headers)
|
||||
if !b.headersFirstMode {
|
||||
bmgrLog.Warnf("Got %d unrequested headers from %s -- "+
|
||||
"disconnecting", numHeaders, hmsg.peer.addr)
|
||||
"disconnecting", numHeaders, hmsg.peer.Addr())
|
||||
hmsg.peer.Disconnect()
|
||||
return
|
||||
}
|
||||
@ -1540,7 +1545,7 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
|
||||
} else {
|
||||
bmgrLog.Warnf("Received block header that does not "+
|
||||
"properly connect to the chain from peer %s "+
|
||||
"-- disconnecting", hmsg.peer.addr)
|
||||
"-- disconnecting", hmsg.peer.Addr())
|
||||
hmsg.peer.Disconnect()
|
||||
return
|
||||
}
|
||||
@ -1557,7 +1562,7 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
|
||||
"%s from peer %s does NOT match "+
|
||||
"expected checkpoint hash of %s -- "+
|
||||
"disconnecting", node.height,
|
||||
node.sha, hmsg.peer.addr,
|
||||
node.sha, hmsg.peer.Addr(),
|
||||
b.nextCheckpoint.Hash)
|
||||
hmsg.peer.Disconnect()
|
||||
return
|
||||
@ -1588,7 +1593,7 @@ func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
|
||||
err := hmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash)
|
||||
if err != nil {
|
||||
bmgrLog.Warnf("Failed to send getheaders message to "+
|
||||
"peer %s: %v", hmsg.peer.addr, err)
|
||||
"peer %s: %v", hmsg.peer.Addr(), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -2440,69 +2445,68 @@ func (b *blockManager) handleNotifyMsg(notification *blockchain.Notification) {
|
||||
}
|
||||
|
||||
// NewPeer informs the block manager of a newly active peer.
|
||||
func (b *blockManager) NewPeer(p *peer) {
|
||||
func (b *blockManager) NewPeer(sp *serverPeer) {
|
||||
// Ignore if we are shutting down.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &newPeerMsg{peer: p}
|
||||
b.msgChan <- &newPeerMsg{peer: sp}
|
||||
}
|
||||
|
||||
// QueueTx adds the passed transaction message and peer to the block handling
|
||||
// queue.
|
||||
func (b *blockManager) QueueTx(tx *dcrutil.Tx, p *peer) {
|
||||
func (b *blockManager) QueueTx(tx *dcrutil.Tx, sp *serverPeer) {
|
||||
// Don't accept more transactions if we're shutting down.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
p.txProcessed <- struct{}{}
|
||||
sp.txProcessed <- struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &txMsg{tx: tx, peer: p}
|
||||
b.msgChan <- &txMsg{tx: tx, peer: sp}
|
||||
}
|
||||
|
||||
// QueueBlock adds the passed block message and peer to the block handling queue.
|
||||
func (b *blockManager) QueueBlock(block *dcrutil.Block, p *peer) {
|
||||
func (b *blockManager) QueueBlock(block *dcrutil.Block, sp *serverPeer) {
|
||||
// Don't accept more blocks if we're shutting down.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
p.blockProcessed <- struct{}{}
|
||||
sp.blockProcessed <- struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &blockMsg{block: block, peer: p}
|
||||
b.msgChan <- &blockMsg{block: block, peer: sp}
|
||||
}
|
||||
|
||||
// QueueInv adds the passed inv message and peer to the block handling queue.
|
||||
func (b *blockManager) QueueInv(inv *wire.MsgInv, p *peer) {
|
||||
func (b *blockManager) QueueInv(inv *wire.MsgInv, sp *serverPeer) {
|
||||
// No channel handling here because peers do not need to block on inv
|
||||
// messages.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &invMsg{inv: inv, peer: p}
|
||||
b.msgChan <- &invMsg{inv: inv, peer: sp}
|
||||
}
|
||||
|
||||
// QueueHeaders adds the passed headers message and peer to the block handling
|
||||
// queue.
|
||||
func (b *blockManager) QueueHeaders(headers *wire.MsgHeaders, p *peer) {
|
||||
func (b *blockManager) QueueHeaders(headers *wire.MsgHeaders, sp *serverPeer) {
|
||||
// No channel handling here because peers do not need to block on
|
||||
// headers messages.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &headersMsg{headers: headers, peer: p}
|
||||
b.msgChan <- &headersMsg{headers: headers, peer: sp}
|
||||
}
|
||||
|
||||
// DonePeer informs the blockmanager that a peer has disconnected.
|
||||
func (b *blockManager) DonePeer(p *peer) {
|
||||
func (b *blockManager) DonePeer(sp *serverPeer) {
|
||||
// Ignore if we are shutting down.
|
||||
if atomic.LoadInt32(&b.shutdown) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b.msgChan <- &donePeerMsg{peer: p}
|
||||
b.msgChan <- &donePeerMsg{peer: sp}
|
||||
}
|
||||
|
||||
// Start begins the core block handler which processes block and inv messages.
|
||||
@ -2533,8 +2537,8 @@ func (b *blockManager) Stop() error {
|
||||
}
|
||||
|
||||
// SyncPeer returns the current sync peer.
|
||||
func (b *blockManager) SyncPeer() *peer {
|
||||
reply := make(chan *peer)
|
||||
func (b *blockManager) SyncPeer() *serverPeer {
|
||||
reply := make(chan *serverPeer)
|
||||
b.msgChan <- getSyncPeerMsg{reply: reply}
|
||||
return <-reply
|
||||
}
|
||||
@ -2542,7 +2546,7 @@ func (b *blockManager) SyncPeer() *peer {
|
||||
// RequestFromPeer allows an outside caller to request blocks or transactions
|
||||
// from a peer. The requests are logged in the blockmanager's internal map of
|
||||
// requests so they do not later ban the peer for sending the respective data.
|
||||
func (b *blockManager) RequestFromPeer(p *peer, blocks, txs []*chainhash.Hash) error {
|
||||
func (b *blockManager) RequestFromPeer(p *serverPeer, blocks, txs []*chainhash.Hash) error {
|
||||
reply := make(chan requestFromPeerResponse)
|
||||
b.msgChan <- requestFromPeerMsg{peer: p, blocks: blocks, txs: txs,
|
||||
reply: reply}
|
||||
@ -2551,7 +2555,7 @@ func (b *blockManager) RequestFromPeer(p *peer, blocks, txs []*chainhash.Hash) e
|
||||
return response.err
|
||||
}
|
||||
|
||||
func (b *blockManager) requestFromPeer(p *peer, blocks, txs []*chainhash.Hash) error {
|
||||
func (b *blockManager) requestFromPeer(p *serverPeer, blocks, txs []*chainhash.Hash) error {
|
||||
msgResp := wire.NewMsgGetData()
|
||||
|
||||
// Add the blocks to the request.
|
||||
|
||||
11
config.go
11
config.go
@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2013-2014 The btcsuite developers
|
||||
// Copyright (c) 2015 The Decred developers
|
||||
// Copyright (c) 2015-2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@ -66,6 +66,15 @@ var (
|
||||
// to parse and execute service commands specified via the -s flag.
|
||||
var runServiceCommand func(string) error
|
||||
|
||||
// minUint32 is a helper function to return the minimum of two uint32s.
|
||||
// This avoids a math import and the need to cast to floats.
|
||||
func minUint32(a, b uint32) uint32 {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// config defines the configuration options for dcrd.
|
||||
//
|
||||
// See loadConfig for details on the configuration load process.
|
||||
|
||||
400
docs/README.md
400
docs/README.md
@ -1,198 +1,202 @@
|
||||
### Table of Contents
|
||||
1. [About](#About)
|
||||
2. [Getting Started](#GettingStarted)
|
||||
1. [Installation](#Installation)
|
||||
1. [Windows](#WindowsInstallation)
|
||||
2. [Linux/BSD/MacOSX/POSIX](#PosixInstallation)
|
||||
2. [Configuration](#Configuration)
|
||||
3. [Controlling and Querying dcrd via dcrctl](#DcrctlConfig)
|
||||
4. [Mining](#Mining)
|
||||
3. [Help](#Help)
|
||||
1. [Startup](#Startup)
|
||||
1. [Using bootstrap.dat](#BootstrapDat)
|
||||
2. [Network Configuration](#NetworkConfig)
|
||||
3. [Wallet](#Wallet)
|
||||
4. [Contact](#Contact)
|
||||
1. [IRC](#ContactIRC)
|
||||
2. [Mailing Lists](#MailingLists)
|
||||
5. [Developer Resources](#DeveloperResources)
|
||||
1. [Code Contribution Guidelines](#ContributionGuidelines)
|
||||
2. [JSON-RPC Reference](#JSONRPCReference)
|
||||
3. [The Decred-related Go Packages](#GoPackages)
|
||||
|
||||
<a name="About" />
|
||||
### 1. About
|
||||
dcrd is a full node decred implementation written in [Go](http://golang.org),
|
||||
licensed under the [copyfree](http://www.copyfree.org) ISC License.
|
||||
|
||||
This project is currently under active development and is in a Beta state. It is
|
||||
extremely stable and has been in production use for over 6 months as of May
|
||||
2014, however there are still a couple of major features we want to add before
|
||||
we come out of beta.
|
||||
|
||||
It also properly relays newly mined blocks, maintains a transaction pool, and
|
||||
relays individual transactions that have not yet made it into a block. It
|
||||
ensures all individual transactions admitted to the pool follow the rules
|
||||
required into the block chain and also includes the vast majority of the more
|
||||
strict checks which filter transactions based on miner requirements ("standard"
|
||||
transactions).
|
||||
|
||||
<a name="GettingStarted" />
|
||||
### 2. Getting Started
|
||||
|
||||
<a name="Installation" />
|
||||
**2.1 Installation**<br />
|
||||
|
||||
The first step is to install dcrd. See one of the following sections for
|
||||
details on how to install on the supported operating systems.
|
||||
|
||||
<a name="WindowsInstallation" />
|
||||
**2.1.1 Windows Installation**<br />
|
||||
|
||||
* Install the MSI available at: https://github.com/decred/dcrd/releases
|
||||
* Launch dcrd from the Start Menu
|
||||
|
||||
<a name="PosixInstallation" />
|
||||
**2.1.2 Linux/BSD/MacOSX/POSIX Installation**<br />
|
||||
|
||||
* Install Go according to the installation instructions here: http://golang.org/doc/install
|
||||
* Run the following command to ensure your Go version is at least version 1.2: `$ go version`
|
||||
* Run the following command to obtain dcrd, its dependencies, and install it: `$ go get github.com/decred/dcrd/...`<br />
|
||||
* To upgrade, run the following command: `$ go get -u github.com/decred/dcrd/...`
|
||||
* Run dcrd: `$ dcrd`
|
||||
|
||||
<a name="Configuration" />
|
||||
**2.2 Configuration**<br />
|
||||
|
||||
dcrd has a number of [configuration](http://godoc.org/github.com/decred/dcrd)
|
||||
options, which can be viewed by running: `$ dcrd --help`.
|
||||
|
||||
<a name="DcrctlConfig" />
|
||||
**2.3 Controlling and Querying dcrd via dcrctl**<br />
|
||||
|
||||
dcrctl is a command line utility that can be used to both control and query dcrd
|
||||
via [RPC](http://www.wikipedia.org/wiki/Remote_procedure_call). dcrd does
|
||||
**not** enable its RPC server by default; You must configure at minimum both an
|
||||
RPC username and password or both an RPC limited username and password:
|
||||
|
||||
* dcrd.conf configuration file
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
rpclimituser=mylimituser
|
||||
rpclimitpass=Limitedp4ssw0rd
|
||||
```
|
||||
* dcrctl.conf configuration file
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
```
|
||||
OR
|
||||
```
|
||||
[Application Options]
|
||||
rpclimituser=mylimituser
|
||||
rpclimitpass=Limitedp4ssw0rd
|
||||
```
|
||||
For a list of available options, run: `$ dcrctl --help`
|
||||
|
||||
<a name="Mining" />
|
||||
**2.4 Mining**<br />
|
||||
dcrd supports both the `getwork` and `getblocktemplate` RPCs although the
|
||||
`getwork` RPC is deprecated and will likely be removed in a future release.
|
||||
The limited user cannot access these RPCs.<br />
|
||||
|
||||
**1. Add the payment addresses with the `miningaddr` option.**<br />
|
||||
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
miningaddr=12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX
|
||||
miningaddr=1M83ju3EChKYyysmM2FXtLNftbacagd8FR
|
||||
```
|
||||
|
||||
**2. Add dcrd's RPC TLS certificate to system Certificate Authority list.**<br />
|
||||
|
||||
`cgminer` uses [curl](http://curl.haxx.se/) to fetch data from the RPC server.
|
||||
Since curl validates the certificate by default, we must install the `dcrd` RPC
|
||||
certificate into the default system Certificate Authority list.
|
||||
|
||||
**Ubuntu**<br />
|
||||
|
||||
1. Copy rpc.cert to /usr/share/ca-certificates: `# cp /home/user/.dcrd/rpc.cert /usr/share/ca-certificates/dcrd.crt`<br />
|
||||
2. Add dcrd.crt to /etc/ca-certificates.conf: `# echo dcrd.crt >> /etc/ca-certificates.conf`<br />
|
||||
3. Update the CA certificate list: `# update-ca-certificates`<br />
|
||||
|
||||
**3. Set your mining software url to use https.**<br />
|
||||
|
||||
`$ cgminer -o https://127.0.0.1:9109 -u rpcuser -p rpcpassword`
|
||||
|
||||
<a name="Help" />
|
||||
### 3. Help
|
||||
|
||||
<a name="Startup" />
|
||||
**3.1 Startup**<br />
|
||||
|
||||
Typically dcrd will run and start downloading the block chain with no extra
|
||||
configuration necessary, however, there is an optional method to use a
|
||||
`bootstrap.dat` file that may speed up the initial block chain download process.
|
||||
|
||||
<a name="BootstrapDat" />
|
||||
**3.1.1 bootstrap.dat**<br />
|
||||
* [Using bootstrap.dat](https://github.com/decred/dcrd/tree/master/docs/using_bootstrap_dat.md)
|
||||
|
||||
<a name="NetworkConfig" />
|
||||
**3.1.2 Network Configuration**<br />
|
||||
* [What Ports Are Used by Default?](https://github.com/decred/dcrd/tree/master/docs/default_ports.md)
|
||||
* [How To Listen on Specific Interfaces](https://github.com/decred/dcrd/tree/master/docs/configure_peer_server_listen_interfaces.md)
|
||||
* [How To Configure RPC Server to Listen on Specific Interfaces](https://github.com/decred/dcrd/tree/master/docs/configure_rpc_server_listen_interfaces.md)
|
||||
* [Configuring dcrd with Tor](https://github.com/decred/dcrd/tree/master/docs/configuring_tor.md)
|
||||
|
||||
<a name="Wallet" />
|
||||
**3.1 Wallet**<br />
|
||||
|
||||
dcrd was intentionally developed without an integrated wallet for security
|
||||
reasons. Please see [dcrwallet](https://github.com/decred/dcrwallet) for more
|
||||
information.
|
||||
|
||||
<a name="Contact" />
|
||||
### 4. Contact
|
||||
|
||||
<a name="ContactIRC" />
|
||||
**4.1 IRC**<br />
|
||||
* [irc.freenode.net](irc://irc.freenode.net), channel #dcrd
|
||||
|
||||
<a name="MailingLists" />
|
||||
**4.2 Mailing Lists**<br />
|
||||
* <a href="mailto:dcrd+subscribe@opensource.conformal.com">dcrd</a>: discussion
|
||||
of dcrd and its packages.
|
||||
* <a href="mailto:dcrd-commits+subscribe@opensource.conformal.com">dcrd-commits</a>:
|
||||
readonly mail-out of source code changes.
|
||||
|
||||
<a name="DeveloperResources" />
|
||||
### 5. Developer Resources
|
||||
|
||||
<a name="ContributionGuidelines" />
|
||||
* [Code Contribution Guidelines](https://github.com/decred/dcrd/tree/master/docs/code_contribution_guidelines.md)
|
||||
<a name="JSONRPCReference" />
|
||||
* [JSON-RPC Reference](https://github.com/decred/dcrd/tree/master/docs/json_rpc_api.md)
|
||||
* [RPC Examples](https://github.com/decred/dcrd/tree/master/docs/json_rpc_api.md#ExampleCode)
|
||||
<a name="GoPackages" />
|
||||
* The Decred-related Go Packages:
|
||||
* [dcrrpcclient](https://github.com/decred/dcrrpcclient) - Implements a
|
||||
robust and easy to use Websocket-enabled Decred JSON-RPC client
|
||||
* [wire](https://github.com/decred/dcrd/tree/master/wire) - Implements the
|
||||
Decred wire protocol
|
||||
* [blockchain](https://github.com/decred/dcrd/tree/master/blockchain) -
|
||||
Implements Decred block handling and chain selection rules
|
||||
* [txscript](https://github.com/decred/dcrd/tree/master/txscript) -
|
||||
Implements the Decred transaction scripting language
|
||||
* [dcrec](https://github.com/decred/dcrd/tree/master/dcrec) - Implements
|
||||
support for the elliptic curve cryptographic functions needed for the
|
||||
Decred scripts
|
||||
* [database](https://github.com/decred/dcrd/tree/master/database) -
|
||||
Provides a database interface for the Decred block chain
|
||||
* [dcrutil](https://github.com/decred/dcrutil) - Provides Decred-specific
|
||||
convenience functions and types
|
||||
### Table of Contents
|
||||
1. [About](#About)
|
||||
2. [Getting Started](#GettingStarted)
|
||||
1. [Installation](#Installation)
|
||||
1. [Windows](#WindowsInstallation)
|
||||
2. [Linux/BSD/MacOSX/POSIX](#PosixInstallation)
|
||||
2. [Configuration](#Configuration)
|
||||
3. [Controlling and Querying dcrd via dcrctl](#DcrctlConfig)
|
||||
4. [Mining](#Mining)
|
||||
3. [Help](#Help)
|
||||
1. [Startup](#Startup)
|
||||
1. [Using bootstrap.dat](#BootstrapDat)
|
||||
2. [Network Configuration](#NetworkConfig)
|
||||
3. [Wallet](#Wallet)
|
||||
4. [Contact](#Contact)
|
||||
1. [IRC](#ContactIRC)
|
||||
2. [Mailing Lists](#MailingLists)
|
||||
5. [Developer Resources](#DeveloperResources)
|
||||
1. [Code Contribution Guidelines](#ContributionGuidelines)
|
||||
2. [JSON-RPC Reference](#JSONRPCReference)
|
||||
3. [The Decred-related Go Packages](#GoPackages)
|
||||
|
||||
<a name="About" />
|
||||
### 1. About
|
||||
dcrd is a full node decred implementation written in [Go](http://golang.org),
|
||||
licensed under the [copyfree](http://www.copyfree.org) ISC License.
|
||||
|
||||
This project is currently under active development and is in a Beta state. It is
|
||||
extremely stable and has been in production use for over 6 months as of May
|
||||
2014, however there are still a couple of major features we want to add before
|
||||
we come out of beta.
|
||||
|
||||
It also properly relays newly mined blocks, maintains a transaction pool, and
|
||||
relays individual transactions that have not yet made it into a block. It
|
||||
ensures all individual transactions admitted to the pool follow the rules
|
||||
required into the block chain and also includes the vast majority of the more
|
||||
strict checks which filter transactions based on miner requirements ("standard"
|
||||
transactions).
|
||||
|
||||
<a name="GettingStarted" />
|
||||
### 2. Getting Started
|
||||
|
||||
<a name="Installation" />
|
||||
**2.1 Installation**<br />
|
||||
|
||||
The first step is to install dcrd. See one of the following sections for
|
||||
details on how to install on the supported operating systems.
|
||||
|
||||
<a name="WindowsInstallation" />
|
||||
**2.1.1 Windows Installation**<br />
|
||||
|
||||
* Install the MSI available at: https://github.com/decred/dcrd/releases
|
||||
* Launch dcrd from the Start Menu
|
||||
|
||||
<a name="PosixInstallation" />
|
||||
**2.1.2 Linux/BSD/MacOSX/POSIX Installation**<br />
|
||||
|
||||
* Install Go according to the installation instructions here: http://golang.org/doc/install
|
||||
* Run the following command to ensure your Go version is at least version 1.2: `$ go version`
|
||||
* Run the following command to obtain dcrd, its dependencies, and install it: `$ go get github.com/decred/dcrd/...`<br />
|
||||
* To upgrade, run the following command: `$ go get -u github.com/decred/dcrd/...`
|
||||
* Run dcrd: `$ dcrd`
|
||||
|
||||
<a name="Configuration" />
|
||||
**2.2 Configuration**<br />
|
||||
|
||||
dcrd has a number of [configuration](http://godoc.org/github.com/decred/dcrd)
|
||||
options, which can be viewed by running: `$ dcrd --help`.
|
||||
|
||||
<a name="DcrctlConfig" />
|
||||
**2.3 Controlling and Querying dcrd via dcrctl**<br />
|
||||
|
||||
dcrctl is a command line utility that can be used to both control and query dcrd
|
||||
via [RPC](http://www.wikipedia.org/wiki/Remote_procedure_call). dcrd does
|
||||
**not** enable its RPC server by default; You must configure at minimum both an
|
||||
RPC username and password or both an RPC limited username and password:
|
||||
|
||||
* dcrd.conf configuration file
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
rpclimituser=mylimituser
|
||||
rpclimitpass=Limitedp4ssw0rd
|
||||
```
|
||||
* dcrctl.conf configuration file
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
```
|
||||
OR
|
||||
```
|
||||
[Application Options]
|
||||
rpclimituser=mylimituser
|
||||
rpclimitpass=Limitedp4ssw0rd
|
||||
```
|
||||
For a list of available options, run: `$ dcrctl --help`
|
||||
|
||||
<a name="Mining" />
|
||||
**2.4 Mining**<br />
|
||||
dcrd supports both the `getwork` and `getblocktemplate` RPCs although the
|
||||
`getwork` RPC is deprecated and will likely be removed in a future release.
|
||||
The limited user cannot access these RPCs.<br />
|
||||
|
||||
**1. Add the payment addresses with the `miningaddr` option.**<br />
|
||||
|
||||
```
|
||||
[Application Options]
|
||||
rpcuser=myuser
|
||||
rpcpass=SomeDecentp4ssw0rd
|
||||
miningaddr=12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX
|
||||
miningaddr=1M83ju3EChKYyysmM2FXtLNftbacagd8FR
|
||||
```
|
||||
|
||||
**2. Add dcrd's RPC TLS certificate to system Certificate Authority list.**<br />
|
||||
|
||||
`cgminer` uses [curl](http://curl.haxx.se/) to fetch data from the RPC server.
|
||||
Since curl validates the certificate by default, we must install the `dcrd` RPC
|
||||
certificate into the default system Certificate Authority list.
|
||||
|
||||
**Ubuntu**<br />
|
||||
|
||||
1. Copy rpc.cert to /usr/share/ca-certificates: `# cp /home/user/.dcrd/rpc.cert /usr/share/ca-certificates/dcrd.crt`<br />
|
||||
2. Add dcrd.crt to /etc/ca-certificates.conf: `# echo dcrd.crt >> /etc/ca-certificates.conf`<br />
|
||||
3. Update the CA certificate list: `# update-ca-certificates`<br />
|
||||
|
||||
**3. Set your mining software url to use https.**<br />
|
||||
|
||||
`$ cgminer -o https://127.0.0.1:9109 -u rpcuser -p rpcpassword`
|
||||
|
||||
<a name="Help" />
|
||||
### 3. Help
|
||||
|
||||
<a name="Startup" />
|
||||
**3.1 Startup**<br />
|
||||
|
||||
Typically dcrd will run and start downloading the block chain with no extra
|
||||
configuration necessary, however, there is an optional method to use a
|
||||
`bootstrap.dat` file that may speed up the initial block chain download process.
|
||||
|
||||
<a name="BootstrapDat" />
|
||||
**3.1.1 bootstrap.dat**<br />
|
||||
* [Using bootstrap.dat](https://github.com/decred/dcrd/tree/master/docs/using_bootstrap_dat.md)
|
||||
|
||||
<a name="NetworkConfig" />
|
||||
**3.1.2 Network Configuration**<br />
|
||||
* [What Ports Are Used by Default?](https://github.com/decred/dcrd/tree/master/docs/default_ports.md)
|
||||
* [How To Listen on Specific Interfaces](https://github.com/decred/dcrd/tree/master/docs/configure_peer_server_listen_interfaces.md)
|
||||
* [How To Configure RPC Server to Listen on Specific Interfaces](https://github.com/decred/dcrd/tree/master/docs/configure_rpc_server_listen_interfaces.md)
|
||||
* [Configuring dcrd with Tor](https://github.com/decred/dcrd/tree/master/docs/configuring_tor.md)
|
||||
|
||||
<a name="Wallet" />
|
||||
**3.1 Wallet**<br />
|
||||
|
||||
dcrd was intentionally developed without an integrated wallet for security
|
||||
reasons. Please see [dcrwallet](https://github.com/decred/dcrwallet) for more
|
||||
information.
|
||||
|
||||
<a name="Contact" />
|
||||
### 4. Contact
|
||||
|
||||
<a name="ContactIRC" />
|
||||
**4.1 IRC**<br />
|
||||
* [irc.freenode.net](irc://irc.freenode.net), channel #dcrd
|
||||
|
||||
<a name="MailingLists" />
|
||||
**4.2 Mailing Lists**<br />
|
||||
* <a href="mailto:dcrd+subscribe@opensource.conformal.com">dcrd</a>: discussion
|
||||
of dcrd and its packages.
|
||||
* <a href="mailto:dcrd-commits+subscribe@opensource.conformal.com">dcrd-commits</a>:
|
||||
readonly mail-out of source code changes.
|
||||
|
||||
<a name="DeveloperResources" />
|
||||
### 5. Developer Resources
|
||||
|
||||
<a name="ContributionGuidelines" />
|
||||
* [Code Contribution Guidelines](https://github.com/decred/dcrd/tree/master/docs/code_contribution_guidelines.md)
|
||||
<a name="JSONRPCReference" />
|
||||
* [JSON-RPC Reference](https://github.com/decred/dcrd/tree/master/docs/json_rpc_api.md)
|
||||
* [RPC Examples](https://github.com/decred/dcrd/tree/master/docs/json_rpc_api.md#ExampleCode)
|
||||
<a name="GoPackages" />
|
||||
* The Decred-related Go Packages:
|
||||
* [dcrrpcclient](https://github.com/decred/dcrrpcclient) - Implements a
|
||||
robust and easy to use Websocket-enabled Decred JSON-RPC client
|
||||
* [dcrjson](https://github.com/decred/dcrjson) - Provides an extensive API
|
||||
for the underlying JSON-RPC command and return values
|
||||
* [wire](https://github.com/decred/dcrd/tree/master/wire) - Implements the
|
||||
Decred wire protocol
|
||||
* [peer](https://github.com/decred/dcrd/tree/master/peer) -
|
||||
Provides a common base for creating and managing Decred network peers.
|
||||
* [blockchain](https://github.com/decred/dcrd/tree/master/blockchain) -
|
||||
Implements Decred block handling and chain selection rules
|
||||
* [txscript](https://github.com/decred/dcrd/tree/master/txscript) -
|
||||
Implements the Decred transaction scripting language
|
||||
* [dcrec](https://github.com/decred/dcrd/tree/master/dcrec) - Implements
|
||||
support for the elliptic curve cryptographic functions needed for the
|
||||
Decred scripts
|
||||
* [database](https://github.com/decred/dcrd/tree/master/database) -
|
||||
Provides a database interface for the Decred block chain
|
||||
* [dcrutil](https://github.com/decred/dcrutil) - Provides Decred-specific
|
||||
convenience functions and types
|
||||
|
||||
2
log.go
2
log.go
@ -19,6 +19,7 @@ import (
|
||||
"github.com/decred/dcrd/blockchain/stake"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/database"
|
||||
"github.com/decred/dcrd/peer"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
@ -123,6 +124,7 @@ func useLogger(subsystemID string, logger btclog.Logger) {
|
||||
|
||||
case "PEER":
|
||||
peerLog = logger
|
||||
peer.UseLogger(logger)
|
||||
|
||||
case "RPCS":
|
||||
rpcsLog = logger
|
||||
|
||||
87
peer/README.md
Normal file
87
peer/README.md
Normal file
@ -0,0 +1,87 @@
|
||||
peer
|
||||
====
|
||||
|
||||
[]
|
||||
(https://travis-ci.org/btcsuite/btcd) [![ISC License]
|
||||
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
|
||||
|
||||
Package peer provides a common base for creating and managing bitcoin network
|
||||
peers.
|
||||
|
||||
This package has intentionally been designed so it can be used as a standalone
|
||||
package for any projects needing a full featured bitcoin peer base to build on.
|
||||
|
||||
## Overview
|
||||
|
||||
This package builds upon the wire package, which provides the fundamental
|
||||
primitives necessary to speak the bitcoin wire protocol, in order to simplify
|
||||
the process of creating fully functional peers. In essence, it provides a
|
||||
common base for creating concurrent safe fully validating nodes, Simplified
|
||||
Payment Verification (SPV) nodes, proxies, etc.
|
||||
|
||||
A quick overview of the major features peer provides are as follows:
|
||||
|
||||
- Provides a basic concurrent safe bitcoin peer for handling bitcoin
|
||||
communications via the peer-to-peer protocol
|
||||
- Full duplex reading and writing of bitcoin protocol messages
|
||||
- Automatic handling of the initial handshake process including protocol
|
||||
version negotiation
|
||||
- Asynchronous message queueing of outbound messages with optional channel for
|
||||
notification when the message is actually sent
|
||||
- Flexible peer configuration
|
||||
- Caller is responsible for creating outgoing connections and listening for
|
||||
incoming connections so they have flexibility to establish connections as
|
||||
they see fit (proxies, etc)
|
||||
- User agent name and version
|
||||
- Bitcoin network
|
||||
- Service support signalling (full nodes, bloom filters, etc)
|
||||
- Maximum supported protocol version
|
||||
- Ability to register callbacks for handling bitcoin protocol messages
|
||||
- Inventory message batching and send trickling with known inventory detection
|
||||
and avoidance
|
||||
- Automatic periodic keep-alive pinging and pong responses
|
||||
- Random nonce generation and self connection detection
|
||||
- Proper handling of bloom filter related commands when the caller does not
|
||||
specify the related flag to signal support
|
||||
- Disconnects the peer when the protocol version is high enough
|
||||
- Does not invoke the related callbacks for older protocol versions
|
||||
- Snapshottable peer statistics such as the total number of bytes read and
|
||||
written, the remote address, user agent, and negotiated protocol version
|
||||
- Helper functions pushing addresses, getblocks, getheaders, and reject
|
||||
messages
|
||||
- These could all be sent manually via the standard message output function,
|
||||
but the helpers provide additional nice functionality such as duplicate
|
||||
filtering and address randomization
|
||||
- Ability to wait for shutdown/disconnect
|
||||
- Comprehensive test coverage
|
||||
|
||||
## Documentation
|
||||
|
||||
[]
|
||||
(http://godoc.org/github.com/btcsuite/btcd/peer)
|
||||
|
||||
Full `go doc` style documentation for the project can be viewed online without
|
||||
installing this package by using the GoDoc site here:
|
||||
http://godoc.org/github.com/btcsuite/btcd/peer
|
||||
|
||||
You can also view the documentation locally once the package is installed with
|
||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
||||
http://localhost:6060/pkg/github.com/btcsuite/btcd/peer
|
||||
|
||||
## Installation and Updating
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/btcsuite/btcd/peer
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [New Outbound Peer Example]
|
||||
(https://godoc.org/github.com/btcsuite/btcd/peer#example-package--NewOutboundPeer)
|
||||
Demonstrates the basic process for initializing and creating an outbound peer.
|
||||
Peers negotiate by exchanging version and verack messages. For demonstration,
|
||||
a simple handler for the version message is attached to the peer.
|
||||
|
||||
## License
|
||||
|
||||
Package peer is licensed under the [copyfree](http://copyfree.org) ISC License.
|
||||
154
peer/doc.go
Normal file
154
peer/doc.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package peer provides a common base for creating and managing Decred network
|
||||
peers.
|
||||
|
||||
Overview
|
||||
|
||||
This package builds upon the wire package, which provides the fundamental
|
||||
primitives necessary to speak the decred wire protocol, in order to simplify
|
||||
the process of creating fully functional peers. In essence, it provides a
|
||||
common base for creating concurrent safe fully validating nodes, Simplified
|
||||
Payment Verification (SPV) nodes, proxies, etc.
|
||||
|
||||
A quick overview of the major features peer provides are as follows:
|
||||
|
||||
- Provides a basic concurrent safe decred peer for handling decred
|
||||
communications via the peer-to-peer protocol
|
||||
- Full duplex reading and writing of decred protocol messages
|
||||
- Automatic handling of the initial handshake process including protocol
|
||||
version negotiation
|
||||
- Asynchronous message queueing of outbound messages with optional channel for
|
||||
notification when the message is actually sent
|
||||
- Flexible peer configuration
|
||||
- Caller is responsible for creating outgoing connections and listening for
|
||||
incoming connections so they have flexibility to establish connections as
|
||||
they see fit (proxies, etc)
|
||||
- User agent name and version
|
||||
- Decred network
|
||||
- Service support signalling (full nodes, bloom filters, etc)
|
||||
- Maximum supported protocol version
|
||||
- Ability to register callbacks for handling decred protocol messages
|
||||
- Inventory message batching and send trickling with known inventory detection
|
||||
and avoidance
|
||||
- Automatic periodic keep-alive pinging and pong responses
|
||||
- Random nonce generation and self connection detection
|
||||
- Proper handling of bloom filter related commands when the caller does not
|
||||
specify the related flag to signal support
|
||||
- Disconnects the peer when the protocol version is high enough
|
||||
- Does not invoke the related callbacks for older protocol versions
|
||||
- Snapshottable peer statistics such as the total number of bytes read and
|
||||
written, the remote address, user agent, and negotiated protocol version
|
||||
- Helper functions pushing addresses, getblocks, getheaders, and reject
|
||||
messages
|
||||
- These could all be sent manually via the standard message output function,
|
||||
but the helpers provide additional nice functionality such as duplicate
|
||||
filtering and address randomization
|
||||
- Ability to wait for shutdown/disconnect
|
||||
- Comprehensive test coverage
|
||||
|
||||
Peer Configuration
|
||||
|
||||
All peer configuration is handled with the Config struct. This allows the
|
||||
caller to specify things such as the user agent name and version, the decred
|
||||
network to use, which services it supports, and callbacks to invoke when decred
|
||||
messages are received. See the documentation for each field of the Config
|
||||
struct for more details.
|
||||
|
||||
Inbound and Outbound Peers
|
||||
|
||||
A peer can either be inbound or outbound. The caller is responsible for
|
||||
establishing the connection to remote peers and listening for incoming peers.
|
||||
This provides high flexibility for things such as connecting via proxies, acting
|
||||
as a proxy, creating bridge peers, choosing whether to listen for inbound peers,
|
||||
etc.
|
||||
|
||||
For outgoing peers, the NewOutboundPeer function must be used to specify the
|
||||
configuration followed by invoking Connect with the net.Conn instance. This
|
||||
will start all async I/O goroutines and initiate the initial negotiation
|
||||
process. Once that has been completed, the peer is fully functional.
|
||||
|
||||
For inbound peers, the NewInboundPeer function must be used to specify the
|
||||
configuration and net.Conn instance followed by invoking Start. This will start
|
||||
all async I/O goroutines and listen for the initial negotiation process. Once
|
||||
that has been completed, the peer is fully functional.
|
||||
|
||||
Callbacks
|
||||
|
||||
In order to do anything useful with a peer, it is necessary to react to decred
|
||||
messages. This is accomplished by creating an instance of the MessageListeners
|
||||
struct with the callbacks to be invoke specified and setting the Listeners field
|
||||
of the Config struct specified when creating a peer to it.
|
||||
|
||||
For convenience, a callback hook for all of the currently supported decred
|
||||
messages is exposed which receives the peer instance and the concrete message
|
||||
type. In addition, a hook for OnRead is provided so even custom messages types
|
||||
for which this package does not directly provide a hook, as long as they
|
||||
implement the wire.Message interface, can be used. Finally, the OnWrite hook
|
||||
is provided, which in conjunction with OnRead, can be used to track server-wide
|
||||
byte counts.
|
||||
|
||||
It is often useful to use closures which encapsulate state when specifying the
|
||||
callback handlers. This provides a clean method for accessing that state when
|
||||
callbacks are invoked.
|
||||
|
||||
Queuing Messages and Inventory
|
||||
|
||||
The QueueMessage function provides the fundamental means to send messages to the
|
||||
remote peer. As the name implies, this employs a non-blocking queue. A done
|
||||
channel which will be notified when the message is actually sent can optionally
|
||||
be specified. There are certain message types which are better sent using other
|
||||
functions which provide additional functionality.
|
||||
|
||||
Of special interest are inventory messages. Rather than manually sending MsgInv
|
||||
messages via Queuemessage, the inventory vectors should be queued using the
|
||||
QueueInventory function. It employs batching and trickling along with
|
||||
intelligent known remote peer inventory detection and avoidance through the use
|
||||
of a most-recently used algorithm.
|
||||
|
||||
Message Sending Helper Functions
|
||||
|
||||
In addition to the bare QueueMessage function previously described, the
|
||||
PushAddrMsg, PushGetBlocksMsg, PushGetHeadersMsg, and PushRejectMsg functions
|
||||
are provided as a convenience. While it is of course possible to create and
|
||||
send these message manually via QueueMessage, these helper functions provided
|
||||
additional useful functionality that is typically desired.
|
||||
|
||||
For example, the PushAddrMsg function automatically limits the addresses to the
|
||||
maximum number allowed by the message and randomizes the chosen addresses when
|
||||
there are too many. This allows the caller to simply provide a slice of known
|
||||
addresses, such as that returned by the addrmgr package, without having to worry
|
||||
about the details.
|
||||
|
||||
Next, the PushGetBlocksMsg and PushGetHeadersMsg functions will construct proper
|
||||
messages using a block locator and ignore back to back duplicate requests.
|
||||
|
||||
Finally, the PushRejectMsg function can be used to easily create and send an
|
||||
appropriate reject message based on the provided parameters as well as
|
||||
optionally provides a flag to cause it to block until the message is actually
|
||||
sent.
|
||||
|
||||
Peer Statistics
|
||||
|
||||
A snapshot of the current peer statistics can be obtained with the StatsSnapshot
|
||||
function. This includes statistics such as the total number of bytes read and
|
||||
written, the remote address, user agent, and negotiated protocol version.
|
||||
|
||||
Logging
|
||||
|
||||
This package provides extensive logging capabilities through the UseLogger
|
||||
function which allows a btclog.Logger to be specified. For example, logging at
|
||||
the debug level provides summaries of every message sent and received, and
|
||||
logging at the trace level provides full dumps of parsed messages as well as the
|
||||
raw message bytes using a format similar to hexdump -C.
|
||||
|
||||
Improvement Proposals
|
||||
|
||||
This package supports all improvement proposals supported by the wire packge.
|
||||
(https://godoc.org/github.com/decred/dcrd/wire#hdr-Bitcoin_Improvement_Proposals)
|
||||
*/
|
||||
package peer
|
||||
114
peer/example_test.go
Normal file
114
peer/example_test.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package peer_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/peer"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// mockRemotePeer creates a basic inbound peer listening on the simnet port for
|
||||
// use with Example_peerConnection. It does not return until the listner is
|
||||
// active.
|
||||
func mockRemotePeer() error {
|
||||
// Configure peer to act as a simnet node that offers no services.
|
||||
peerCfg := &peer.Config{
|
||||
UserAgentName: "peer", // User agent name to advertise.
|
||||
UserAgentVersion: "1.0.0", // User agent version to advertise.
|
||||
ChainParams: &chaincfg.SimNetParams,
|
||||
}
|
||||
|
||||
// Accept connections on the simnet port.
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:18555")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Printf("Accept: error %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and start the inbound peer.
|
||||
p := peer.NewInboundPeer(peerCfg, conn)
|
||||
if err := p.Start(); err != nil {
|
||||
fmt.Printf("Start: error %v\n", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This example demonstrates the basic process for initializing and creating an
|
||||
// outbound peer. Peers negotiate by exchanging version and verack messages.
|
||||
// For demonstration, a simple handler for version message is attached to the
|
||||
// peer.
|
||||
func Example_newOutboundPeer() {
|
||||
// Ordinarily this will not be needed since the outbound peer will be
|
||||
// connecting to a remote peer, however, since this example is executed
|
||||
// and tested, a mock remote peer is needed to listen for the outbound
|
||||
// peer.
|
||||
if err := mockRemotePeer(); err != nil {
|
||||
fmt.Printf("mockRemotePeer: unexpected error %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Create an outbound peer that is configured to act as a simnet node
|
||||
// that offers no services and has listeners for the version and verack
|
||||
// messages. The verack listener is used here to signal the code below
|
||||
// when the handshake has been finished by signalling a channel.
|
||||
verack := make(chan struct{})
|
||||
peerCfg := &peer.Config{
|
||||
UserAgentName: "peer", // User agent name to advertise.
|
||||
UserAgentVersion: "1.0.0", // User agent version to advertise.
|
||||
ChainParams: &chaincfg.SimNetParams,
|
||||
Services: 0,
|
||||
Listeners: peer.MessageListeners{
|
||||
OnVersion: func(p *peer.Peer, msg *wire.MsgVersion) {
|
||||
fmt.Println("outbound: received version")
|
||||
},
|
||||
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
||||
verack <- struct{}{}
|
||||
},
|
||||
},
|
||||
}
|
||||
p, err := peer.NewOutboundPeer(peerCfg, "127.0.0.1:18555")
|
||||
if err != nil {
|
||||
fmt.Printf("NewOutboundPeer: error %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Establish the connection to the peer address and mark it connected.
|
||||
conn, err := net.Dial("tcp", p.Addr())
|
||||
if err != nil {
|
||||
fmt.Printf("net.Dial: error %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p.Connect(conn); err != nil {
|
||||
fmt.Printf("Connect: error %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for the verack message or timeout in case of failure.
|
||||
select {
|
||||
case <-verack:
|
||||
case <-time.After(time.Second * 1):
|
||||
fmt.Printf("Example_peerConnection: verack timeout")
|
||||
}
|
||||
|
||||
// Shutdown the peer.
|
||||
p.Shutdown()
|
||||
|
||||
// Output:
|
||||
// outbound: received version
|
||||
}
|
||||
19
peer/export_test.go
Normal file
19
peer/export_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
This test file is part of the peer package rather than than the peer_test
|
||||
package so it can bridge access to the internals to properly test cases which
|
||||
are either not possible or can't reliably be tested via the public interface.
|
||||
The functions are only exported while the tests are being run.
|
||||
*/
|
||||
|
||||
package peer
|
||||
|
||||
// TstAllowSelfConns allows the test package to allow self connections by
|
||||
// disabling the detection logic.
|
||||
func TstAllowSelfConns() {
|
||||
allowSelfConns = true
|
||||
}
|
||||
243
peer/log.go
Normal file
243
peer/log.go
Normal file
@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package peer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/btclog"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/txscript"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxRejectReasonLen is the maximum length of a sanitized reject reason
|
||||
// that will be logged.
|
||||
maxRejectReasonLen = 250
|
||||
)
|
||||
|
||||
// log is a logger that is initialized with no output filters. This
|
||||
// means the package will not perform any logging by default until the caller
|
||||
// requests it.
|
||||
var log btclog.Logger
|
||||
|
||||
// The default amount of logging is none.
|
||||
func init() {
|
||||
DisableLog()
|
||||
}
|
||||
|
||||
// DisableLog disables all library log output. Logging output is disabled
|
||||
// by default until either UseLogger or SetLogWriter are called.
|
||||
func DisableLog() {
|
||||
log = btclog.Disabled
|
||||
}
|
||||
|
||||
// UseLogger uses a specified Logger to output package logging info.
|
||||
// This should be used in preference to SetLogWriter if the caller is also
|
||||
// using btclog.
|
||||
func UseLogger(logger btclog.Logger) {
|
||||
log = logger
|
||||
}
|
||||
|
||||
// SetLogWriter uses a specified io.Writer to output package logging info.
|
||||
// This allows a caller to direct package logging output without needing a
|
||||
// dependency on seelog. If the caller is also using btclog, UseLogger should
|
||||
// be used instead.
|
||||
func SetLogWriter(w io.Writer, level string) error {
|
||||
if w == nil {
|
||||
return errors.New("nil writer")
|
||||
}
|
||||
|
||||
lvl, ok := btclog.LogLevelFromString(level)
|
||||
if !ok {
|
||||
return errors.New("invalid log level")
|
||||
}
|
||||
|
||||
l, err := btclog.NewLoggerFromWriter(w, lvl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
UseLogger(l)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogClosure is a closure that can be printed with %v to be used to
|
||||
// generate expensive-to-create data for a detailed log level and avoid doing
|
||||
// the work if the data isn't printed.
|
||||
type logClosure func() string
|
||||
|
||||
func (c logClosure) String() string {
|
||||
return c()
|
||||
}
|
||||
|
||||
func newLogClosure(c func() string) logClosure {
|
||||
return logClosure(c)
|
||||
}
|
||||
|
||||
// directionString is a helper function that returns a string that represents
|
||||
// the direction of a connection (inbound or outbound).
|
||||
func directionString(inbound bool) string {
|
||||
if inbound {
|
||||
return "inbound"
|
||||
}
|
||||
return "outbound"
|
||||
}
|
||||
|
||||
// formatLockTime returns a transaction lock time as a human-readable string.
|
||||
func formatLockTime(lockTime uint32) string {
|
||||
// The lock time field of a transaction is either a block height at
|
||||
// which the transaction is finalized or a timestamp depending on if the
|
||||
// value is before the lockTimeThreshold. When it is under the
|
||||
// threshold it is a block height.
|
||||
if lockTime < txscript.LockTimeThreshold {
|
||||
return fmt.Sprintf("height %d", lockTime)
|
||||
}
|
||||
|
||||
return time.Unix(int64(lockTime), 0).String()
|
||||
}
|
||||
|
||||
// invSummary returns an inventory message as a human-readable string.
|
||||
func invSummary(invList []*wire.InvVect) string {
|
||||
// No inventory.
|
||||
invLen := len(invList)
|
||||
if invLen == 0 {
|
||||
return "empty"
|
||||
}
|
||||
|
||||
// One inventory item.
|
||||
if invLen == 1 {
|
||||
iv := invList[0]
|
||||
switch iv.Type {
|
||||
case wire.InvTypeError:
|
||||
return fmt.Sprintf("error %s", iv.Hash)
|
||||
case wire.InvTypeBlock:
|
||||
return fmt.Sprintf("block %s", iv.Hash)
|
||||
case wire.InvTypeTx:
|
||||
return fmt.Sprintf("tx %s", iv.Hash)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("unknown (%d) %s", uint32(iv.Type), iv.Hash)
|
||||
}
|
||||
|
||||
// More than one inv item.
|
||||
return fmt.Sprintf("size %d", invLen)
|
||||
}
|
||||
|
||||
// locatorSummary returns a block locator as a human-readable string.
|
||||
func locatorSummary(locator []*chainhash.Hash, stopHash *chainhash.Hash) string {
|
||||
if len(locator) > 0 {
|
||||
return fmt.Sprintf("locator %s, stop %s", locator[0], stopHash)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("no locator, stop %s", stopHash)
|
||||
|
||||
}
|
||||
|
||||
// sanitizeString strips any characters which are even remotely dangerous, such
|
||||
// as html control characters, from the passed string. It also limits it to
|
||||
// the passed maximum size, which can be 0 for unlimited. When the string is
|
||||
// limited, it will also add "..." to the string to indicate it was truncated.
|
||||
func sanitizeString(str string, maxLength uint) string {
|
||||
const safeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXY" +
|
||||
"Z01234567890 .,;_/:?@"
|
||||
|
||||
// Strip any characters not in the safeChars string removed.
|
||||
str = strings.Map(func(r rune) rune {
|
||||
if strings.IndexRune(safeChars, r) >= 0 {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, str)
|
||||
|
||||
// Limit the string to the max allowed length.
|
||||
if maxLength > 0 && uint(len(str)) > maxLength {
|
||||
str = str[:maxLength]
|
||||
str = str + "..."
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// messageSummary returns a human-readable string which summarizes a message.
|
||||
// Not all messages have or need a summary. This is used for debug logging.
|
||||
func messageSummary(msg wire.Message) string {
|
||||
switch msg := msg.(type) {
|
||||
case *wire.MsgVersion:
|
||||
return fmt.Sprintf("agent %s, pver %d, block %d",
|
||||
msg.UserAgent, msg.ProtocolVersion, msg.LastBlock)
|
||||
|
||||
case *wire.MsgVerAck:
|
||||
// No summary.
|
||||
|
||||
case *wire.MsgGetAddr:
|
||||
// No summary.
|
||||
|
||||
case *wire.MsgAddr:
|
||||
return fmt.Sprintf("%d addr", len(msg.AddrList))
|
||||
|
||||
case *wire.MsgPing:
|
||||
// No summary - perhaps add nonce.
|
||||
|
||||
case *wire.MsgPong:
|
||||
// No summary - perhaps add nonce.
|
||||
|
||||
case *wire.MsgAlert:
|
||||
// No summary.
|
||||
|
||||
case *wire.MsgMemPool:
|
||||
// No summary.
|
||||
|
||||
case *wire.MsgTx:
|
||||
return fmt.Sprintf("hash %s, %d inputs, %d outputs, lock %s",
|
||||
msg.TxSha(), len(msg.TxIn), len(msg.TxOut),
|
||||
formatLockTime(msg.LockTime))
|
||||
|
||||
case *wire.MsgBlock:
|
||||
header := &msg.Header
|
||||
return fmt.Sprintf("hash %s, ver %d, %d tx, %s", msg.BlockSha(),
|
||||
header.Version, len(msg.Transactions), header.Timestamp)
|
||||
|
||||
case *wire.MsgInv:
|
||||
return invSummary(msg.InvList)
|
||||
|
||||
case *wire.MsgNotFound:
|
||||
return invSummary(msg.InvList)
|
||||
|
||||
case *wire.MsgGetData:
|
||||
return invSummary(msg.InvList)
|
||||
|
||||
case *wire.MsgGetBlocks:
|
||||
return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
|
||||
|
||||
case *wire.MsgGetHeaders:
|
||||
return locatorSummary(msg.BlockLocatorHashes, &msg.HashStop)
|
||||
|
||||
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
|
||||
// control characters, etc. Also limit them to sane length for
|
||||
// logging.
|
||||
rejCommand := sanitizeString(msg.Cmd, wire.CommandSize)
|
||||
rejReason := sanitizeString(msg.Reason, maxRejectReasonLen)
|
||||
summary := fmt.Sprintf("cmd %v, code %v, reason %v", rejCommand,
|
||||
msg.Code, rejReason)
|
||||
if rejCommand == wire.CmdBlock || rejCommand == wire.CmdTx {
|
||||
summary += fmt.Sprintf(", hash %v", msg.Hash)
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
// No summary for other messages.
|
||||
return ""
|
||||
}
|
||||
65
peer/log_test.go
Normal file
65
peer/log_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright (c) 2015 The btcsuite developers Use of this source code is
|
||||
// governed by an ISC license that can be found in the LICENSE file.
|
||||
|
||||
package peer_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/decred/dcrd/peer"
|
||||
)
|
||||
|
||||
func TestSetLogWriter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
w io.Writer
|
||||
level string
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
name: "nil writer",
|
||||
w: nil,
|
||||
level: "trace",
|
||||
expected: errors.New("nil writer"),
|
||||
},
|
||||
{
|
||||
name: "invalid log level",
|
||||
w: bytes.NewBuffer(nil),
|
||||
level: "wrong",
|
||||
expected: errors.New("invalid log level"),
|
||||
},
|
||||
{
|
||||
name: "use off level",
|
||||
w: bytes.NewBuffer(nil),
|
||||
level: "off",
|
||||
expected: errors.New("min level can't be greater than max. Got min: 6, max: 5"),
|
||||
},
|
||||
{
|
||||
name: "pass",
|
||||
w: bytes.NewBuffer(nil),
|
||||
level: "debug",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
err := peer.SetLogWriter(test.w, test.level)
|
||||
if err != nil {
|
||||
if err.Error() != test.expected.Error() {
|
||||
t.Errorf("SetLogWriter #%d (%s) wrong result\n"+
|
||||
"got: %v\nwant: %v", i, test.name, err,
|
||||
test.expected)
|
||||
}
|
||||
} else {
|
||||
if test.expected != nil {
|
||||
t.Errorf("SetLogWriter #%d (%s) wrong result\n"+
|
||||
"got: %v\nwant: %v", i, test.name, err,
|
||||
test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,26 +3,34 @@
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
package peer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// MruInventoryMap provides a map that is limited to a maximum number of items
|
||||
// with eviction for the oldest entry when the limit is exceeded.
|
||||
// MruInventoryMap provides a concurrency safe map that is limited to a maximum
|
||||
// number of items with eviction for the oldest entry when the limit is
|
||||
// exceeded.
|
||||
type MruInventoryMap struct {
|
||||
invMtx sync.Mutex
|
||||
invMap map[wire.InvVect]*list.Element // nearly O(1) lookups
|
||||
invList *list.List // O(1) insert, update, delete
|
||||
limit uint
|
||||
}
|
||||
|
||||
// String returns the map as a human-readable string.
|
||||
func (m MruInventoryMap) String() string {
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *MruInventoryMap) String() string {
|
||||
m.invMtx.Lock()
|
||||
defer m.invMtx.Unlock()
|
||||
|
||||
lastEntryNum := len(m.invMap) - 1
|
||||
curEntry := 0
|
||||
buf := bytes.NewBufferString("[")
|
||||
@ -39,7 +47,12 @@ func (m MruInventoryMap) String() string {
|
||||
}
|
||||
|
||||
// Exists returns whether or not the passed inventory item is in the map.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *MruInventoryMap) Exists(iv *wire.InvVect) bool {
|
||||
m.invMtx.Lock()
|
||||
defer m.invMtx.Unlock()
|
||||
|
||||
if _, exists := m.invMap[*iv]; exists {
|
||||
return true
|
||||
}
|
||||
@ -49,7 +62,12 @@ func (m *MruInventoryMap) Exists(iv *wire.InvVect) bool {
|
||||
// Add adds the passed inventory to the map and handles eviction of the oldest
|
||||
// item if adding the new item would exceed the max limit. Adding an existing
|
||||
// item makes it the most recently used item.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *MruInventoryMap) Add(iv *wire.InvVect) {
|
||||
m.invMtx.Lock()
|
||||
defer m.invMtx.Unlock()
|
||||
|
||||
// When the limit is zero, nothing can be added to the map, so just
|
||||
// return.
|
||||
if m.limit == 0 {
|
||||
@ -88,7 +106,12 @@ func (m *MruInventoryMap) Add(iv *wire.InvVect) {
|
||||
}
|
||||
|
||||
// Delete deletes the passed inventory item from the map (if it exists).
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *MruInventoryMap) Delete(iv *wire.InvVect) {
|
||||
m.invMtx.Lock()
|
||||
defer m.invMtx.Unlock()
|
||||
|
||||
if node, exists := m.invMap[*iv]; exists {
|
||||
m.invList.Remove(node)
|
||||
delete(m.invMap, *iv)
|
||||
@ -3,7 +3,7 @@
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
package peer
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@ -53,7 +53,7 @@ testLoop:
|
||||
|
||||
// Ensure the limited number of most recent entries in the
|
||||
// inventory vector list exist.
|
||||
for j := numInvVects - 1; j >= numInvVects-test.limit; j-- {
|
||||
for j := numInvVects - test.limit; j < numInvVects; j++ {
|
||||
if !mruInvMap.Exists(invVects[j]) {
|
||||
t.Errorf("Exists #%d (%s) entry %s does not "+
|
||||
"exist", i, test.name, *invVects[j])
|
||||
@ -63,7 +63,7 @@ testLoop:
|
||||
|
||||
// Ensure the entries before the limited number of most recent
|
||||
// entries in the inventory vector list do not exist.
|
||||
for j := numInvVects - test.limit - 1; j >= 0; j-- {
|
||||
for j := 0; j < numInvVects-test.limit; j++ {
|
||||
if mruInvMap.Exists(invVects[j]) {
|
||||
t.Errorf("Exists #%d (%s) entry %s exists", i,
|
||||
test.name, *invVects[j])
|
||||
130
peer/mrunoncemap.go
Normal file
130
peer/mrunoncemap.go
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package peer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// mruNonceMap provides a concurrency safe map that is limited to a maximum
|
||||
// number of items with eviction for the oldest entry when the limit is
|
||||
// exceeded.
|
||||
type mruNonceMap struct {
|
||||
mtx sync.Mutex
|
||||
nonceMap map[uint64]*list.Element // nearly O(1) lookups
|
||||
nonceList *list.List // O(1) insert, update, delete
|
||||
limit uint
|
||||
}
|
||||
|
||||
// String returns the map as a human-readable string.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *mruNonceMap) String() string {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
lastEntryNum := len(m.nonceMap) - 1
|
||||
curEntry := 0
|
||||
buf := bytes.NewBufferString("[")
|
||||
for nonce := range m.nonceMap {
|
||||
buf.WriteString(fmt.Sprintf("%d", nonce))
|
||||
if curEntry < lastEntryNum {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
curEntry++
|
||||
}
|
||||
buf.WriteString("]")
|
||||
|
||||
return fmt.Sprintf("<%d>%s", m.limit, buf.String())
|
||||
}
|
||||
|
||||
// Exists returns whether or not the passed nonce is in the map.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *mruNonceMap) Exists(nonce uint64) bool {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
if _, exists := m.nonceMap[nonce]; exists {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add adds the passed nonce to the map and handles eviction of the oldest item
|
||||
// if adding the new item would exceed the max limit. Adding an existing item
|
||||
// makes it the most recently used item.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *mruNonceMap) Add(nonce uint64) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
// When the limit is zero, nothing can be added to the map, so just
|
||||
// return.
|
||||
if m.limit == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// When the entry already exists move it to the front of the list
|
||||
// thereby marking it most recently used.
|
||||
if node, exists := m.nonceMap[nonce]; exists {
|
||||
m.nonceList.MoveToFront(node)
|
||||
return
|
||||
}
|
||||
|
||||
// Evict the least recently used entry (back of the list) if the the new
|
||||
// entry would exceed the size limit for the map. Also reuse the list
|
||||
// node so a new one doesn't have to be allocated.
|
||||
if uint(len(m.nonceMap))+1 > m.limit {
|
||||
node := m.nonceList.Back()
|
||||
lru := node.Value.(uint64)
|
||||
|
||||
// Evict least recently used item.
|
||||
delete(m.nonceMap, lru)
|
||||
|
||||
// Reuse the list node of the item that was just evicted for the
|
||||
// new item.
|
||||
node.Value = nonce
|
||||
m.nonceList.MoveToFront(node)
|
||||
m.nonceMap[nonce] = node
|
||||
return
|
||||
}
|
||||
|
||||
// The limit hasn't been reached yet, so just add the new item.
|
||||
node := m.nonceList.PushFront(nonce)
|
||||
m.nonceMap[nonce] = node
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes the passed nonce from the map (if it exists).
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
func (m *mruNonceMap) Delete(nonce uint64) {
|
||||
m.mtx.Lock()
|
||||
defer m.mtx.Unlock()
|
||||
|
||||
if node, exists := m.nonceMap[nonce]; exists {
|
||||
m.nonceList.Remove(node)
|
||||
delete(m.nonceMap, nonce)
|
||||
}
|
||||
}
|
||||
|
||||
// newMruNonceMap returns a new nonce map that is limited to the number of
|
||||
// entries specified by limit. When the number of entries exceeds the limit,
|
||||
// the oldest (least recently used) entry will be removed to make room for the
|
||||
// new entry.
|
||||
func newMruNonceMap(limit uint) *mruNonceMap {
|
||||
m := mruNonceMap{
|
||||
nonceMap: make(map[uint64]*list.Element),
|
||||
nonceList: list.New(),
|
||||
limit: limit,
|
||||
}
|
||||
return &m
|
||||
}
|
||||
153
peer/mrunoncemap_test.go
Normal file
153
peer/mrunoncemap_test.go
Normal file
@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package peer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestMruNonceMap ensures the mruNonceMap behaves as expected including
|
||||
// limiting, eviction of least-recently used entries, specific entry removal,
|
||||
// and existence tests.
|
||||
func TestMruNonceMap(t *testing.T) {
|
||||
// Create a bunch of fake nonces to use in testing the mru nonce code.
|
||||
numNonces := 10
|
||||
nonces := make([]uint64, 0, numNonces)
|
||||
for i := 0; i < numNonces; i++ {
|
||||
nonces = append(nonces, uint64(i))
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
limit int
|
||||
}{
|
||||
{name: "limit 0", limit: 0},
|
||||
{name: "limit 1", limit: 1},
|
||||
{name: "limit 5", limit: 5},
|
||||
{name: "limit 7", limit: 7},
|
||||
{name: "limit one less than available", limit: numNonces - 1},
|
||||
{name: "limit all available", limit: numNonces},
|
||||
}
|
||||
|
||||
testLoop:
|
||||
for i, test := range tests {
|
||||
// Create a new mru nonce map limited by the specified test
|
||||
// limit and add all of the test nonces. This will cause
|
||||
// evicition since there are more test nonces than the limits.
|
||||
mruNonceMap := newMruNonceMap(uint(test.limit))
|
||||
for j := 0; j < numNonces; j++ {
|
||||
mruNonceMap.Add(nonces[j])
|
||||
}
|
||||
|
||||
// Ensure the limited number of most recent entries in the list
|
||||
// exist.
|
||||
for j := numNonces - test.limit; j < numNonces; j++ {
|
||||
if !mruNonceMap.Exists(nonces[j]) {
|
||||
t.Errorf("Exists #%d (%s) entry %d does not "+
|
||||
"exist", i, test.name, nonces[j])
|
||||
continue testLoop
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the entries before the limited number of most recent
|
||||
// entries in the list do not exist.
|
||||
for j := 0; j < numNonces-test.limit; j++ {
|
||||
if mruNonceMap.Exists(nonces[j]) {
|
||||
t.Errorf("Exists #%d (%s) entry %d exists", i,
|
||||
test.name, nonces[j])
|
||||
continue testLoop
|
||||
}
|
||||
}
|
||||
|
||||
// Readd the entry that should currently be the least-recently
|
||||
// used entry so it becomes the most-recently used entry, then
|
||||
// force an eviction by adding an entry that doesn't exist and
|
||||
// ensure the evicted entry is the new least-recently used
|
||||
// entry.
|
||||
//
|
||||
// This check needs at least 2 entries.
|
||||
if test.limit > 1 {
|
||||
origLruIndex := numNonces - test.limit
|
||||
mruNonceMap.Add(nonces[origLruIndex])
|
||||
|
||||
mruNonceMap.Add(uint64(numNonces) + 1)
|
||||
|
||||
// Ensure the original lru entry still exists since it
|
||||
// was updated and should've have become the mru entry.
|
||||
if !mruNonceMap.Exists(nonces[origLruIndex]) {
|
||||
t.Errorf("MRU #%d (%s) entry %d does not exist",
|
||||
i, test.name, nonces[origLruIndex])
|
||||
continue testLoop
|
||||
}
|
||||
|
||||
// Ensure the entry that should've become the new lru
|
||||
// entry was evicted.
|
||||
newLruIndex := origLruIndex + 1
|
||||
if mruNonceMap.Exists(nonces[newLruIndex]) {
|
||||
t.Errorf("MRU #%d (%s) entry %d exists", i,
|
||||
test.name, nonces[newLruIndex])
|
||||
continue testLoop
|
||||
}
|
||||
}
|
||||
|
||||
// Delete all of the entries in the list, including those that
|
||||
// don't exist in the map, and ensure they no longer exist.
|
||||
for j := 0; j < numNonces; j++ {
|
||||
mruNonceMap.Delete(nonces[j])
|
||||
if mruNonceMap.Exists(nonces[j]) {
|
||||
t.Errorf("Delete #%d (%s) entry %d exists", i,
|
||||
test.name, nonces[j])
|
||||
continue testLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMruNonceMapStringer tests the stringized output for the mruNonceMap type.
|
||||
func TestMruNonceMapStringer(t *testing.T) {
|
||||
// Create a couple of fake nonces to use in testing the mru nonce
|
||||
// stringer code.
|
||||
nonce1 := uint64(10)
|
||||
nonce2 := uint64(20)
|
||||
|
||||
// Create new mru nonce map and add the nonces.
|
||||
mruNonceMap := newMruNonceMap(uint(2))
|
||||
mruNonceMap.Add(nonce1)
|
||||
mruNonceMap.Add(nonce2)
|
||||
|
||||
// Ensure the stringer gives the expected result. Since map iteration
|
||||
// is not ordered, either entry could be first, so account for both
|
||||
// cases.
|
||||
wantStr1 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce1, nonce2)
|
||||
wantStr2 := fmt.Sprintf("<%d>[%d, %d]", 2, nonce2, nonce1)
|
||||
gotStr := mruNonceMap.String()
|
||||
if gotStr != wantStr1 && gotStr != wantStr2 {
|
||||
t.Fatalf("unexpected string representation - got %q, want %q "+
|
||||
"or %q", gotStr, wantStr1, wantStr2)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkMruNonceList performs basic benchmarks on the most recently used
|
||||
// nonce handling.
|
||||
func BenchmarkMruNonceList(b *testing.B) {
|
||||
// Create a bunch of fake nonces to use in benchmarking the mru nonce
|
||||
// code.
|
||||
b.StopTimer()
|
||||
numNonces := 100000
|
||||
nonces := make([]uint64, 0, numNonces)
|
||||
for i := 0; i < numNonces; i++ {
|
||||
nonces = append(nonces, uint64(i))
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
// Benchmark the add plus evicition code.
|
||||
limit := 20000
|
||||
mruNonceMap := newMruNonceMap(uint(limit))
|
||||
for i := 0; i < b.N; i++ {
|
||||
mruNonceMap.Add(nonces[i%numNonces])
|
||||
}
|
||||
}
|
||||
2044
peer/peer.go
Normal file
2044
peer/peer.go
Normal file
File diff suppressed because it is too large
Load Diff
666
peer/peer_test.go
Normal file
666
peer/peer_test.go
Normal file
@ -0,0 +1,666 @@
|
||||
// Copyright (c) 2015 The btcsuite developers
|
||||
// Copyright (c) 2016 The Decred developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package peer_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/btcsuite/go-socks/socks"
|
||||
"github.com/decred/dcrd/chaincfg"
|
||||
"github.com/decred/dcrd/chaincfg/chainhash"
|
||||
"github.com/decred/dcrd/peer"
|
||||
"github.com/decred/dcrd/wire"
|
||||
)
|
||||
|
||||
// conn mocks a network connection by implementing the net.Conn interface. It
|
||||
// is used to test peer connection without actually opening a network
|
||||
// connection.
|
||||
type conn struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
io.Closer
|
||||
|
||||
// local network, address for the connection.
|
||||
lnet, laddr string
|
||||
|
||||
// remote network, address for the connection.
|
||||
rnet, raddr string
|
||||
|
||||
// mocks socks proxy if true
|
||||
proxy bool
|
||||
}
|
||||
|
||||
// LocalAddr returns the local address for the connection.
|
||||
func (c conn) LocalAddr() net.Addr {
|
||||
return &addr{c.lnet, c.laddr}
|
||||
}
|
||||
|
||||
// Remote returns the remote address for the connection.
|
||||
func (c conn) RemoteAddr() net.Addr {
|
||||
if !c.proxy {
|
||||
return &addr{c.rnet, c.raddr}
|
||||
}
|
||||
host, strPort, _ := net.SplitHostPort(c.raddr)
|
||||
port, _ := strconv.Atoi(strPort)
|
||||
return &socks.ProxiedAddr{
|
||||
Net: c.rnet,
|
||||
Host: host,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
// Close handles closing the connection.
|
||||
func (c conn) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c conn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c conn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c conn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
// addr mocks a network address
|
||||
type addr struct {
|
||||
net, address string
|
||||
}
|
||||
|
||||
func (m addr) Network() string { return m.net }
|
||||
func (m addr) String() string { return m.address }
|
||||
|
||||
// pipe turns two mock connections into a full-duplex connection similar to
|
||||
// net.Pipe to allow pipe's with (fake) addresses.
|
||||
func pipe(c1, c2 *conn) (*conn, *conn) {
|
||||
r1, w1 := io.Pipe()
|
||||
r2, w2 := io.Pipe()
|
||||
|
||||
c1.Writer = w1
|
||||
c2.Reader = r1
|
||||
c1.Reader = r2
|
||||
c2.Writer = w2
|
||||
|
||||
return c1, c2
|
||||
}
|
||||
|
||||
// peerStats holds the expected peer stats used for testing peer.
|
||||
type peerStats struct {
|
||||
wantUserAgent string
|
||||
wantServices wire.ServiceFlag
|
||||
wantProtocolVersion uint32
|
||||
wantConnected bool
|
||||
wantVersionKnown bool
|
||||
wantVerAckReceived bool
|
||||
wantLastBlock int32
|
||||
wantStartingHeight int32
|
||||
wantLastPingTime time.Time
|
||||
wantLastPingNonce uint64
|
||||
wantLastPingMicros int64
|
||||
wantTimeOffset int64
|
||||
wantBytesSent uint64
|
||||
wantBytesReceived uint64
|
||||
}
|
||||
|
||||
// testPeer tests the given peer's flags and stats
|
||||
func testPeer(t *testing.T, p *peer.Peer, s peerStats) {
|
||||
if p.UserAgent() != s.wantUserAgent {
|
||||
t.Errorf("testPeer: wrong UserAgent - got %v, want %v", p.UserAgent(), s.wantUserAgent)
|
||||
return
|
||||
}
|
||||
|
||||
if p.Services() != s.wantServices {
|
||||
t.Errorf("testPeer: wrong Services - got %v, want %v", p.Services(), s.wantServices)
|
||||
return
|
||||
}
|
||||
|
||||
if !p.LastPingTime().Equal(s.wantLastPingTime) {
|
||||
t.Errorf("testPeer: wrong LastPingTime - got %v, want %v", p.LastPingTime(), s.wantLastPingTime)
|
||||
return
|
||||
}
|
||||
|
||||
if p.LastPingNonce() != s.wantLastPingNonce {
|
||||
t.Errorf("testPeer: wrong LastPingNonce - got %v, want %v", p.LastPingNonce(), s.wantLastPingNonce)
|
||||
return
|
||||
}
|
||||
|
||||
if p.LastPingMicros() != s.wantLastPingMicros {
|
||||
t.Errorf("testPeer: wrong LastPingMicros - got %v, want %v", p.LastPingMicros(), s.wantLastPingMicros)
|
||||
return
|
||||
}
|
||||
|
||||
if p.VerAckReceived() != s.wantVerAckReceived {
|
||||
t.Errorf("testPeer: wrong VerAckReceived - got %v, want %v", p.VerAckReceived(), s.wantVerAckReceived)
|
||||
return
|
||||
}
|
||||
|
||||
if p.VersionKnown() != s.wantVersionKnown {
|
||||
t.Errorf("testPeer: wrong VersionKnown - got %v, want %v", p.VersionKnown(), s.wantVersionKnown)
|
||||
return
|
||||
}
|
||||
|
||||
if p.ProtocolVersion() != s.wantProtocolVersion {
|
||||
t.Errorf("testPeer: wrong ProtocolVersion - got %v, want %v", p.ProtocolVersion(), s.wantProtocolVersion)
|
||||
return
|
||||
}
|
||||
|
||||
if p.LastBlock() != s.wantLastBlock {
|
||||
t.Errorf("testPeer: wrong LastBlock - got %v, want %v", p.LastBlock(), s.wantLastBlock)
|
||||
return
|
||||
}
|
||||
|
||||
if p.TimeOffset() != s.wantTimeOffset {
|
||||
t.Errorf("testPeer: wrong TimeOffset - got %v, want %v", p.TimeOffset(), s.wantTimeOffset)
|
||||
return
|
||||
}
|
||||
|
||||
if p.BytesSent() != s.wantBytesSent {
|
||||
t.Errorf("testPeer: wrong BytesSent - got %v, want %v", p.BytesSent(), s.wantBytesSent)
|
||||
return
|
||||
}
|
||||
|
||||
if p.BytesReceived() != s.wantBytesReceived {
|
||||
t.Errorf("testPeer: wrong BytesReceived - got %v, want %v", p.BytesReceived(), s.wantBytesReceived)
|
||||
return
|
||||
}
|
||||
|
||||
if p.StartingHeight() != s.wantStartingHeight {
|
||||
t.Errorf("testPeer: wrong StartingHeight - got %v, want %v", p.StartingHeight(), s.wantStartingHeight)
|
||||
return
|
||||
}
|
||||
|
||||
if p.Connected() != s.wantConnected {
|
||||
t.Errorf("testPeer: wrong Connected - got %v, want %v", p.Connected(), s.wantConnected)
|
||||
return
|
||||
}
|
||||
|
||||
stats := p.StatsSnapshot()
|
||||
|
||||
if p.ID() != stats.ID {
|
||||
t.Errorf("testPeer: wrong ID - got %v, want %v", p.ID(), stats.ID)
|
||||
return
|
||||
}
|
||||
|
||||
if p.Addr() != stats.Addr {
|
||||
t.Errorf("testPeer: wrong Addr - got %v, want %v", p.Addr(), stats.Addr)
|
||||
return
|
||||
}
|
||||
|
||||
if p.LastSend() != stats.LastSend {
|
||||
t.Errorf("testPeer: wrong LastSend - got %v, want %v", p.LastSend(), stats.LastSend)
|
||||
return
|
||||
}
|
||||
|
||||
if p.LastRecv() != stats.LastRecv {
|
||||
t.Errorf("testPeer: wrong LastRecv - got %v, want %v", p.LastRecv(), stats.LastRecv)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TestPeerConnection tests connection between inbound and outbound peers.
|
||||
func TestPeerConnection(t *testing.T) {
|
||||
verack := make(chan struct{}, 1)
|
||||
peerCfg := &peer.Config{
|
||||
Listeners: peer.MessageListeners{
|
||||
OnWrite: func(p *peer.Peer, bytesWritten int, msg wire.Message, err error) {
|
||||
switch msg.(type) {
|
||||
case *wire.MsgVerAck:
|
||||
verack <- struct{}{}
|
||||
}
|
||||
},
|
||||
},
|
||||
UserAgentName: "peer",
|
||||
UserAgentVersion: "1.0",
|
||||
ChainParams: &chaincfg.MainNetParams,
|
||||
Services: 0,
|
||||
}
|
||||
wantStats := peerStats{
|
||||
wantUserAgent: wire.DefaultUserAgent + "peer:1.0/",
|
||||
wantServices: 0,
|
||||
wantProtocolVersion: peer.MaxProtocolVersion,
|
||||
wantConnected: true,
|
||||
wantVersionKnown: true,
|
||||
wantVerAckReceived: true,
|
||||
wantLastPingTime: time.Time{},
|
||||
wantLastPingNonce: uint64(0),
|
||||
wantLastPingMicros: int64(0),
|
||||
wantTimeOffset: int64(0),
|
||||
wantBytesSent: 158, // 134 version + 24 verack
|
||||
wantBytesReceived: 158,
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func() (*peer.Peer, *peer.Peer, error)
|
||||
}{
|
||||
{
|
||||
"basic handshake",
|
||||
func() (*peer.Peer, *peer.Peer, error) {
|
||||
inConn, outConn := pipe(
|
||||
&conn{raddr: "10.0.0.1:8333"},
|
||||
&conn{raddr: "10.0.0.2:8333"},
|
||||
)
|
||||
inPeer := peer.NewInboundPeer(peerCfg, inConn)
|
||||
err := inPeer.Start()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := outPeer.Connect(outConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-verack:
|
||||
case <-time.After(time.Second * 1):
|
||||
return nil, nil, errors.New("verack timeout")
|
||||
}
|
||||
}
|
||||
return inPeer, outPeer, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
"socks proxy",
|
||||
func() (*peer.Peer, *peer.Peer, error) {
|
||||
inConn, outConn := pipe(
|
||||
&conn{raddr: "10.0.0.1:8333", proxy: true},
|
||||
&conn{raddr: "10.0.0.2:8333"},
|
||||
)
|
||||
inPeer := peer.NewInboundPeer(peerCfg, inConn)
|
||||
err := inPeer.Start()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.2:8333")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := outPeer.Connect(outConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-verack:
|
||||
case <-time.After(time.Second * 1):
|
||||
return nil, nil, errors.New("verack timeout")
|
||||
}
|
||||
}
|
||||
return inPeer, outPeer, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for i, test := range tests {
|
||||
inPeer, outPeer, err := test.setup()
|
||||
if err != nil {
|
||||
t.Errorf("TestPeerConnection setup #%d: unexpected err %v\n", i, err)
|
||||
return
|
||||
}
|
||||
testPeer(t, inPeer, wantStats)
|
||||
testPeer(t, outPeer, wantStats)
|
||||
|
||||
inPeer.Shutdown()
|
||||
outPeer.Shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
// TestPeerListeners tests that the peer listeners are called as expected.
|
||||
func TestPeerListeners(t *testing.T) {
|
||||
verack := make(chan struct{}, 1)
|
||||
ok := make(chan wire.Message, 20)
|
||||
peerCfg := &peer.Config{
|
||||
Listeners: peer.MessageListeners{
|
||||
OnGetAddr: func(p *peer.Peer, msg *wire.MsgGetAddr) {
|
||||
ok <- msg
|
||||
},
|
||||
OnAddr: func(p *peer.Peer, msg *wire.MsgAddr) {
|
||||
ok <- msg
|
||||
},
|
||||
OnPing: func(p *peer.Peer, msg *wire.MsgPing) {
|
||||
ok <- msg
|
||||
},
|
||||
OnPong: func(p *peer.Peer, msg *wire.MsgPong) {
|
||||
ok <- msg
|
||||
},
|
||||
OnAlert: func(p *peer.Peer, msg *wire.MsgAlert) {
|
||||
ok <- msg
|
||||
},
|
||||
OnMemPool: func(p *peer.Peer, msg *wire.MsgMemPool) {
|
||||
ok <- msg
|
||||
},
|
||||
OnTx: func(p *peer.Peer, msg *wire.MsgTx) {
|
||||
ok <- msg
|
||||
},
|
||||
OnBlock: func(p *peer.Peer, msg *wire.MsgBlock, buf []byte) {
|
||||
ok <- msg
|
||||
},
|
||||
OnInv: func(p *peer.Peer, msg *wire.MsgInv) {
|
||||
ok <- msg
|
||||
},
|
||||
OnHeaders: func(p *peer.Peer, msg *wire.MsgHeaders) {
|
||||
ok <- msg
|
||||
},
|
||||
OnNotFound: func(p *peer.Peer, msg *wire.MsgNotFound) {
|
||||
ok <- msg
|
||||
},
|
||||
OnGetData: func(p *peer.Peer, msg *wire.MsgGetData) {
|
||||
ok <- msg
|
||||
},
|
||||
OnGetBlocks: func(p *peer.Peer, msg *wire.MsgGetBlocks) {
|
||||
ok <- msg
|
||||
},
|
||||
OnGetHeaders: func(p *peer.Peer, msg *wire.MsgGetHeaders) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterAdd: func(p *peer.Peer, msg *wire.MsgFilterAdd) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterClear: func(p *peer.Peer, msg *wire.MsgFilterClear) {
|
||||
ok <- msg
|
||||
},
|
||||
OnFilterLoad: func(p *peer.Peer, msg *wire.MsgFilterLoad) {
|
||||
ok <- msg
|
||||
},
|
||||
OnMerkleBlock: func(p *peer.Peer, msg *wire.MsgMerkleBlock) {
|
||||
ok <- msg
|
||||
},
|
||||
OnVersion: func(p *peer.Peer, msg *wire.MsgVersion) {
|
||||
ok <- msg
|
||||
},
|
||||
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
||||
verack <- struct{}{}
|
||||
},
|
||||
OnReject: func(p *peer.Peer, msg *wire.MsgReject) {
|
||||
ok <- msg
|
||||
},
|
||||
},
|
||||
UserAgentName: "peer",
|
||||
UserAgentVersion: "1.0",
|
||||
ChainParams: &chaincfg.MainNetParams,
|
||||
Services: wire.SFNodeBloom,
|
||||
}
|
||||
inConn, outConn := pipe(
|
||||
&conn{raddr: "10.0.0.1:8333"},
|
||||
&conn{raddr: "10.0.0.2:8333"},
|
||||
)
|
||||
inPeer := peer.NewInboundPeer(peerCfg, inConn)
|
||||
err := inPeer.Start()
|
||||
if err != nil {
|
||||
t.Errorf("TestPeerListeners: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
peerCfg.Listeners = peer.MessageListeners{
|
||||
OnVerAck: func(p *peer.Peer, msg *wire.MsgVerAck) {
|
||||
verack <- struct{}{}
|
||||
},
|
||||
}
|
||||
outPeer, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333")
|
||||
if err != nil {
|
||||
t.Errorf("NewOutboundPeer: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := outPeer.Connect(outConn); err != nil {
|
||||
t.Errorf("TestPeerListeners: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-verack:
|
||||
case <-time.After(time.Second * 1):
|
||||
t.Errorf("TestPeerListeners: verack timeout\n")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
listener string
|
||||
msg wire.Message
|
||||
}{
|
||||
{
|
||||
"OnGetAddr",
|
||||
wire.NewMsgGetAddr(),
|
||||
},
|
||||
{
|
||||
"OnAddr",
|
||||
wire.NewMsgAddr(),
|
||||
},
|
||||
{
|
||||
"OnPing",
|
||||
wire.NewMsgPing(42),
|
||||
},
|
||||
{
|
||||
"OnPong",
|
||||
wire.NewMsgPong(42),
|
||||
},
|
||||
{
|
||||
"OnAlert",
|
||||
wire.NewMsgAlert([]byte("payload"), []byte("signature")),
|
||||
},
|
||||
{
|
||||
"OnMemPool",
|
||||
wire.NewMsgMemPool(),
|
||||
},
|
||||
{
|
||||
"OnTx",
|
||||
wire.NewMsgTx(),
|
||||
},
|
||||
{
|
||||
"OnBlock",
|
||||
wire.NewMsgBlock(wire.NewBlockHeader(0, &chainhash.Hash{},
|
||||
&chainhash.Hash{}, &chainhash.Hash{}, 1, [6]byte{},
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, [36]byte{})),
|
||||
},
|
||||
{
|
||||
"OnInv",
|
||||
wire.NewMsgInv(),
|
||||
},
|
||||
{
|
||||
"OnHeaders",
|
||||
wire.NewMsgHeaders(),
|
||||
},
|
||||
{
|
||||
"OnNotFound",
|
||||
wire.NewMsgNotFound(),
|
||||
},
|
||||
{
|
||||
"OnGetData",
|
||||
wire.NewMsgGetData(),
|
||||
},
|
||||
{
|
||||
"OnGetBlocks",
|
||||
wire.NewMsgGetBlocks(&chainhash.Hash{}),
|
||||
},
|
||||
{
|
||||
"OnGetHeaders",
|
||||
wire.NewMsgGetHeaders(),
|
||||
},
|
||||
{
|
||||
"OnFilterAdd",
|
||||
wire.NewMsgFilterAdd([]byte{0x01}),
|
||||
},
|
||||
{
|
||||
"OnFilterClear",
|
||||
wire.NewMsgFilterClear(),
|
||||
},
|
||||
{
|
||||
"OnFilterLoad",
|
||||
wire.NewMsgFilterLoad([]byte{0x01}, 10, 0, wire.BloomUpdateNone),
|
||||
},
|
||||
{
|
||||
"OnMerkleBlock",
|
||||
wire.NewMsgMerkleBlock(wire.NewBlockHeader(0,
|
||||
&chainhash.Hash{}, &chainhash.Hash{},
|
||||
&chainhash.Hash{}, 1, [6]byte{}, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, [36]byte{})),
|
||||
},
|
||||
// only one version message is allowed
|
||||
// only one verack message is allowed
|
||||
{
|
||||
"OnMsgReject",
|
||||
wire.NewMsgReject("block", wire.RejectDuplicate, "dupe block"),
|
||||
},
|
||||
}
|
||||
t.Logf("Running %d tests", len(tests))
|
||||
for _, test := range tests {
|
||||
// Queue the test message
|
||||
outPeer.QueueMessage(test.msg, nil)
|
||||
select {
|
||||
case <-ok:
|
||||
case <-time.After(time.Second * 1):
|
||||
t.Errorf("TestPeerListeners: %s timeout", test.listener)
|
||||
return
|
||||
}
|
||||
}
|
||||
inPeer.Shutdown()
|
||||
outPeer.Shutdown()
|
||||
}
|
||||
|
||||
// TestOutboundPeer tests that the outbound peer works as expected.
|
||||
func TestOutboundPeer(t *testing.T) {
|
||||
// Use a mock NewestBlock func to test errs
|
||||
var errBlockNotFound = errors.New("newest block not found")
|
||||
var mockNewestSha = func() (*chainhash.Hash, int64, error) {
|
||||
return nil, 0, errBlockNotFound
|
||||
}
|
||||
|
||||
peerCfg := &peer.Config{
|
||||
NewestBlock: mockNewestSha,
|
||||
UserAgentName: "peer",
|
||||
UserAgentVersion: "1.0",
|
||||
ChainParams: &chaincfg.MainNetParams,
|
||||
Services: 0,
|
||||
}
|
||||
|
||||
r, w := io.Pipe()
|
||||
c := &conn{raddr: "10.0.0.1:8333", Writer: w, Reader: r}
|
||||
|
||||
p, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333")
|
||||
if err != nil {
|
||||
t.Errorf("NewOutboundPeer: unexpected err - %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test Connect err
|
||||
wantErr := errBlockNotFound
|
||||
if err := p.Connect(c); err != wantErr {
|
||||
t.Errorf("Connect: expected err %v, got %v\n", wantErr, err)
|
||||
return
|
||||
}
|
||||
// Test already connected
|
||||
if err := p.Connect(c); err != nil {
|
||||
t.Errorf("Connect: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
// Test already started
|
||||
if err := p.Start(); err != nil {
|
||||
t.Errorf("Start: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test Queue Inv
|
||||
fakeBlockHash := &chainhash.Hash{0x00, 0x01}
|
||||
fakeInv := wire.NewInvVect(wire.InvTypeBlock, fakeBlockHash)
|
||||
p.QueueInventory(fakeInv)
|
||||
p.AddKnownInventory(fakeInv)
|
||||
p.QueueInventory(fakeInv)
|
||||
|
||||
// Test Queue Message
|
||||
fakeMsg := wire.NewMsgVerAck()
|
||||
p.QueueMessage(fakeMsg, nil)
|
||||
done := make(chan struct{}, 5)
|
||||
p.QueueMessage(fakeMsg, done)
|
||||
<-done
|
||||
p.Shutdown()
|
||||
|
||||
// Test NewestBlock
|
||||
var newestBlock = func() (*chainhash.Hash, int64, error) {
|
||||
hashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef"
|
||||
hash, err := chainhash.NewHashFromStr(hashStr)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return hash, 234439, nil
|
||||
}
|
||||
peerCfg.NewestBlock = newestBlock
|
||||
p1, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333")
|
||||
if err != nil {
|
||||
t.Errorf("NewOutboundPeer: unexpected err - %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p1.Connect(c); err != nil {
|
||||
t.Errorf("Connect: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test update latest block
|
||||
latestBlockSha, err := chainhash.NewHashFromStr("1a63f9cdff1752e6375c8c76e543a71d239e1a2e5c6db1aa679")
|
||||
if err != nil {
|
||||
t.Errorf("NewShaHashFromStr: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
p1.UpdateLastAnnouncedBlock(latestBlockSha)
|
||||
p1.UpdateLastBlockHeight(234440)
|
||||
if p1.LastAnnouncedBlock() != latestBlockSha {
|
||||
t.Errorf("LastAnnouncedBlock: wrong block - got %v, want %v",
|
||||
p1.LastAnnouncedBlock(), latestBlockSha)
|
||||
return
|
||||
}
|
||||
|
||||
// Test Queue Inv after connection
|
||||
p1.QueueInventory(fakeInv)
|
||||
p1.Shutdown()
|
||||
|
||||
// Test testnet
|
||||
peerCfg.ChainParams = &chaincfg.TestNetParams
|
||||
peerCfg.Services = wire.SFNodeBloom
|
||||
p2, err := peer.NewOutboundPeer(peerCfg, "10.0.0.1:8333")
|
||||
if err != nil {
|
||||
t.Errorf("NewOutboundPeer: unexpected err - %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p2.Connect(c); err != nil {
|
||||
t.Errorf("Connect: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Test PushXXX
|
||||
var addrs []*wire.NetAddress
|
||||
for i := 0; i < 5; i++ {
|
||||
na := wire.NetAddress{}
|
||||
addrs = append(addrs, &na)
|
||||
}
|
||||
if _, err := p2.PushAddrMsg(addrs); err != nil {
|
||||
t.Errorf("PushAddrMsg: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p2.PushGetBlocksMsg(nil, &chainhash.Hash{}); err != nil {
|
||||
t.Errorf("PushGetBlocksMsg: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
if err := p2.PushGetHeadersMsg(nil, &chainhash.Hash{}); err != nil {
|
||||
t.Errorf("PushGetHeadersMsg: unexpected err %v\n", err)
|
||||
return
|
||||
}
|
||||
p2.PushRejectMsg("block", wire.RejectMalformed, "malformed", nil, true)
|
||||
p2.PushRejectMsg("block", wire.RejectInvalid, "invalid", nil, false)
|
||||
|
||||
// Test Queue Messages
|
||||
p2.QueueMessage(wire.NewMsgGetAddr(), done)
|
||||
p2.QueueMessage(wire.NewMsgPing(1), done)
|
||||
p2.QueueMessage(wire.NewMsgMemPool(), done)
|
||||
p2.QueueMessage(wire.NewMsgGetData(), done)
|
||||
p2.QueueMessage(wire.NewMsgGetHeaders(), done)
|
||||
|
||||
p2.Shutdown()
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Allow self connection when running the tests.
|
||||
peer.TstAllowSelfConns()
|
||||
}
|
||||
58
rpcserver.go
58
rpcserver.go
@ -93,6 +93,9 @@ const (
|
||||
// sstxCommitmentString is the string to insert when a verbose
|
||||
// transaction output's pkscript type is a ticket commitment.
|
||||
sstxCommitmentString = "sstxcommitment"
|
||||
|
||||
// maxProtocolVersion is the max protocol version the server supports.
|
||||
maxProtocolVersion = 2
|
||||
)
|
||||
|
||||
var (
|
||||
@ -465,7 +468,7 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil && peerExists(s.server.PeerInfo(), addr, int32(nodeID)) {
|
||||
if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) {
|
||||
return nil, &dcrjson.RPCError{
|
||||
Code: dcrjson.ErrRPCMisc,
|
||||
Message: "can't disconnect a permanent peer, use remove",
|
||||
@ -488,7 +491,7 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil && peerExists(s.server.PeerInfo(), addr, int32(nodeID)) {
|
||||
if err != nil && peerExists(s.server.Peers(), addr, int32(nodeID)) {
|
||||
return nil, &dcrjson.RPCError{
|
||||
Code: dcrjson.ErrRPCMisc,
|
||||
Message: "can't remove a temporary peer, use disconnect",
|
||||
@ -533,9 +536,9 @@ func handleNode(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (inter
|
||||
// peerExists determines if a certain peer is currently connected given
|
||||
// information about all currently connected peers. Peer existence is
|
||||
// determined using either a target address or node id.
|
||||
func peerExists(peerInfos []*dcrjson.GetPeerInfoResult, addr string, nodeID int32) bool {
|
||||
for _, peerInfo := range peerInfos {
|
||||
if peerInfo.ID == nodeID || peerInfo.Addr == addr {
|
||||
func peerExists(peers []*serverPeer, addr string, nodeID int32) bool {
|
||||
for _, p := range peers {
|
||||
if p.ID() == nodeID || p.Addr() == addr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -2013,7 +2016,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
node := *c.Node
|
||||
found := false
|
||||
for i, peer := range peers {
|
||||
if peer.addr == node {
|
||||
if peer.Addr() == node {
|
||||
peers = peers[i : i+1]
|
||||
found = true
|
||||
}
|
||||
@ -2031,7 +2034,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
if !c.DNS {
|
||||
results := make([]string, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
results = append(results, peer.addr)
|
||||
results = append(results, peer.Addr())
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
@ -2043,15 +2046,15 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
// Set the "address" of the peer which could be an ip address
|
||||
// or a domain name.
|
||||
var result dcrjson.GetAddedNodeInfoResult
|
||||
result.AddedNode = peer.addr
|
||||
result.AddedNode = peer.Addr()
|
||||
result.Connected = dcrjson.Bool(peer.Connected())
|
||||
|
||||
// Split the address into host and port portions so we can do
|
||||
// a DNS lookup against the host. When no port is specified in
|
||||
// the address, just use the address as the host.
|
||||
host, _, err := net.SplitHostPort(peer.addr)
|
||||
host, _, err := net.SplitHostPort(peer.Addr())
|
||||
if err != nil {
|
||||
host = peer.addr
|
||||
host = peer.Addr()
|
||||
}
|
||||
|
||||
// Do a DNS lookup for the address. If the lookup fails, just
|
||||
@ -2075,7 +2078,7 @@ func handleGetAddedNodeInfo(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
addr.Address = ip
|
||||
addr.Connected = "false"
|
||||
if ip == host && peer.Connected() {
|
||||
addr.Connected = directionString(peer.inbound)
|
||||
addr.Connected = directionString(peer.Inbound())
|
||||
}
|
||||
addrs = append(addrs, addr)
|
||||
}
|
||||
@ -3703,7 +3706,38 @@ func handleGetNetworkHashPS(s *rpcServer, cmd interface{}, closeChan <-chan stru
|
||||
|
||||
// handleGetPeerInfo implements the getpeerinfo command.
|
||||
func handleGetPeerInfo(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
|
||||
return s.server.PeerInfo(), nil
|
||||
peers := s.server.Peers()
|
||||
syncPeer := s.server.blockManager.SyncPeer()
|
||||
infos := make([]*dcrjson.GetPeerInfoResult, 0, len(peers))
|
||||
for _, p := range peers {
|
||||
statsSnap := p.StatsSnapshot()
|
||||
info := &dcrjson.GetPeerInfoResult{
|
||||
ID: statsSnap.ID,
|
||||
Addr: statsSnap.Addr,
|
||||
Services: fmt.Sprintf("%08d", uint64(statsSnap.Services)),
|
||||
LastSend: statsSnap.LastSend.Unix(),
|
||||
LastRecv: statsSnap.LastRecv.Unix(),
|
||||
BytesSent: statsSnap.BytesSent,
|
||||
BytesRecv: statsSnap.BytesRecv,
|
||||
ConnTime: statsSnap.ConnTime.Unix(),
|
||||
PingTime: float64(statsSnap.LastPingMicros),
|
||||
TimeOffset: statsSnap.TimeOffset,
|
||||
Version: statsSnap.Version,
|
||||
SubVer: statsSnap.UserAgent,
|
||||
Inbound: statsSnap.Inbound,
|
||||
StartingHeight: statsSnap.StartingHeight,
|
||||
CurrentHeight: statsSnap.LastBlock,
|
||||
BanScore: 0,
|
||||
SyncNode: p == syncPeer,
|
||||
}
|
||||
if p.LastPingNonce() != 0 {
|
||||
wait := float64(time.Now().Sub(statsSnap.LastPingTime).Nanoseconds())
|
||||
// We actually want microseconds.
|
||||
info.PingWait = wait / 1000
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// handleGetRawMempool implements the getrawmempool command.
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
const (
|
||||
// InitialProcotolVersion is the initial protocol version for the
|
||||
// current network.
|
||||
// network.
|
||||
InitialProcotolVersion uint32 = 1
|
||||
|
||||
// ProtocolVersion is the latest protocol version this package supports.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user