Compare commits

...

2 Commits

Author SHA1 Message Date
Kirill
b88e34fd84
[NOD-1313] Refactor AddressManager (#918)
* [NOD-1313] Refactor AddressManager.

* [NOD-1313]Remove old tests.Minor improvements,fixes.

* [NOD-1313] After merge fixes. Fix import cycle.

* [NOD-1313] Integration tests fixes.

* [NOD-1313] Allocate new slice for the returned key.

* [NOD-1313] AddressManager improvements and fixes.

* Move local and banned addresses to separate lists.
* Move AddressManager config to the separate file.
* Add LocalAddressManager.
* Remove redundant KnownAddress structure.
* Restore local addresses functionality.
* Call initListeners from the LocalAddressManager.
* AddressManager minor improvements and fixes.

* [NOD-1313] Minor fixes.

* [NOD-1313] Implement HandleGetPeerAddresses. Refactoring.

* [NOD-1313] After-merge fixes.

* [NOD-1313] Minor improvements.

* AddressManager: added BannedAddresses() method.
* AddressManager: HandleGetPeerAddresses() add banned addresses
  separately.
* AddressManager: remove addressEntry redundant struct.
* ConnectionManager: checkOutgoingConnections() minor improvements and
  fixes.
* Minor refactoring.
* Minor fixes.

* [NOD-1313] GetPeerAddresses RPC message update

* GetPeerAddresses RPC: add BannedAddresses in the separate field.
* Update protobuf.
2020-10-08 17:05:47 +03:00
Ori Newman
689098082f [NOD-1444] Implement getHeaders RPC command (#944)
* [NOD-1444] Implement getHeaders RPC command

* [NOD-1444] Fix tests and comments

* [NOD-1444] Fix error message

* [NOD-1444] Make GetHeaders propagate header serialization errors

* [NOD-1444] RLock the dag on GetHeaders

* [NOD-1444] Change the error field number to 1000
2020-10-06 12:40:32 +03:00
38 changed files with 1952 additions and 3174 deletions

View File

@ -101,6 +101,8 @@ const (
CmdGetMempoolEntriesResponseMessage
CmdShutDownRequestMessage
CmdShutDownResponseMessage
CmdGetHeadersRequestMessage
CmdGetHeadersResponseMessage
)
// ProtocolMessageCommandToString maps all MessageCommands to their string representation
@ -176,6 +178,8 @@ var RPCMessageCommandToString = map[MessageCommand]string{
CmdFinalityConflictResolvedNotificationMessage: "FinalityConflictResolvedNotification",
CmdGetMempoolEntriesRequestMessage: "GetMempoolEntriesRequestMessage",
CmdGetMempoolEntriesResponseMessage: "GetMempoolEntriesResponseMessage",
CmdGetHeadersRequestMessage: "GetHeadersRequest",
CmdGetHeadersResponseMessage: "GetHeadersResponse",
}
// Message is an interface that describes a kaspa message. A type that

View File

@ -18,7 +18,7 @@ func TestRequstIBDBlocks(t *testing.T) {
t.Errorf("NewHashFromStr: %v", err)
}
hashStr = "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
hashStr = "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
highHash, err := daghash.NewHashFromStr(hashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)

View File

@ -23,7 +23,7 @@ import (
func TestTx(t *testing.T) {
pver := ProtocolVersion
txIDStr := "3ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
txIDStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
txID, err := daghash.NewTxIDFromStr(txIDStr)
if err != nil {
t.Errorf("NewTxIDFromStr: %v", err)

View File

@ -0,0 +1,45 @@
package appmessage
// GetHeadersRequestMessage is an appmessage corresponding to
// its respective RPC message
type GetHeadersRequestMessage struct {
baseMessage
StartHash string
Limit uint64
IsAscending bool
}
// Command returns the protocol command string for the message
func (msg *GetHeadersRequestMessage) Command() MessageCommand {
return CmdGetHeadersRequestMessage
}
// NewGetHeadersRequestMessage returns a instance of the message
func NewGetHeadersRequestMessage(startHash string, limit uint64, isAscending bool) *GetHeadersRequestMessage {
return &GetHeadersRequestMessage{
StartHash: startHash,
Limit: limit,
IsAscending: isAscending,
}
}
// GetHeadersResponseMessage is an appmessage corresponding to
// its respective RPC message
type GetHeadersResponseMessage struct {
baseMessage
Headers []string
Error *RPCError
}
// Command returns the protocol command string for the message
func (msg *GetHeadersResponseMessage) Command() MessageCommand {
return CmdGetHeadersResponseMessage
}
// NewGetHeadersResponseMessage returns a instance of the message
func NewGetHeadersResponseMessage(headers []string) *GetHeadersResponseMessage {
return &GetHeadersResponseMessage{
Headers: headers,
}
}

View File

@ -20,7 +20,8 @@ func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage {
// its respective RPC message
type GetPeerAddressesResponseMessage struct {
baseMessage
Addresses []*GetPeerAddressesKnownAddressMessage
Addresses []*GetPeerAddressesKnownAddressMessage
BannedAddresses []*GetPeerAddressesKnownAddressMessage
Error *RPCError
}
@ -31,9 +32,10 @@ func (msg *GetPeerAddressesResponseMessage) Command() MessageCommand {
}
// NewGetPeerAddressesResponseMessage returns a instance of the message
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
func NewGetPeerAddressesResponseMessage(addresses []*GetPeerAddressesKnownAddressMessage, bannedAddresses []*GetPeerAddressesKnownAddressMessage) *GetPeerAddressesResponseMessage {
return &GetPeerAddressesResponseMessage{
Addresses: addresses,
Addresses: addresses,
BannedAddresses: bannedAddresses,
}
}

View File

@ -72,11 +72,6 @@ func (a *ComponentManager) Stop() {
log.Errorf("Error stopping the net adapter: %+v", err)
}
err = a.addressManager.Stop()
if err != nil {
log.Errorf("Error stopping address manager: %s", err)
}
return
}
@ -99,18 +94,22 @@ func NewComponentManager(cfg *config.Config, databaseContext *dbaccess.DatabaseC
if err != nil {
return nil, err
}
addressManager, err := addressmanager.New(cfg, databaseContext)
addressManager, err := addressmanager.New(addressmanager.NewConfig(cfg))
if err != nil {
return nil, err
}
connectionManager, err := connmanager.New(cfg, netAdapter, addressManager)
if err != nil {
return nil, err
}
protocolManager, err := protocol.NewManager(cfg, dag, netAdapter, addressManager, txMempool, connectionManager)
if err != nil {
return nil, err
}
rpcManager := setupRPC(cfg, txMempool, dag, sigCache, netAdapter, protocolManager, connectionManager, addressManager, acceptanceIndex, interrupt)
return &ComponentManager{
@ -187,14 +186,14 @@ func (a *ComponentManager) maybeSeedFromDNS() {
// Kaspad uses a lookup of the dns seeder here. Since seeder returns
// IPs of nodes and not its own IP, we can not know real IP of
// source. So we'll take first returned address as source.
a.addressManager.AddAddresses(addresses, addresses[0], nil)
a.addressManager.AddAddresses(addresses...)
})
}
if a.cfg.GRPCSeed != "" {
dnsseed.SeedFromGRPC(a.cfg.NetParams(), a.cfg.GRPCSeed, appmessage.SFNodeNetwork, false, nil,
func(addresses []*appmessage.NetAddress) {
a.addressManager.AddAddresses(addresses, addresses[0], nil)
a.addressManager.AddAddresses(addresses...)
})
}
}

View File

@ -20,10 +20,6 @@ type ReceiveAddressesContext interface {
func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Route, outgoingRoute *router.Route,
peer *peerpkg.Peer) error {
if !context.AddressManager().NeedMoreAddresses() {
return nil
}
subnetworkID := peer.SubnetworkID()
msgGetAddresses := appmessage.NewMsgRequestAddresses(false, subnetworkID)
err := outgoingRoute.Enqueue(msgGetAddresses)
@ -51,7 +47,6 @@ func ReceiveAddresses(context ReceiveAddressesContext, incomingRoute *router.Rou
context.Config().SubnetworkID, msgAddresses.Command(), msgAddresses.SubnetworkID)
}
sourceAddress := peer.Connection().NetAddress()
context.AddressManager().AddAddresses(msgAddresses.AddrList, sourceAddress, msgAddresses.SubnetworkID)
context.AddressManager().AddAddresses(msgAddresses.AddrList...)
return nil
}

View File

@ -1,10 +1,11 @@
package addressexchange
import (
"math/rand"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"math/rand"
)
// SendAddressesContext is the interface for the context needed for the SendAddresses flow.
@ -20,8 +21,7 @@ func SendAddresses(context SendAddressesContext, incomingRoute *router.Route, ou
}
msgGetAddresses := message.(*appmessage.MsgRequestAddresses)
addresses := context.AddressManager().AddressCache(msgGetAddresses.IncludeAllSubnetworks,
msgGetAddresses.SubnetworkID)
addresses := context.AddressManager().Addresses()
msgAddresses := appmessage.NewMsgAddresses(msgGetAddresses.IncludeAllSubnetworks, msgGetAddresses.SubnetworkID)
err = msgAddresses.AddAddresses(shuffleAddresses(addresses)...)
if err != nil {

View File

@ -85,9 +85,7 @@ func HandleHandshake(context HandleHandshakeContext, netConnection *netadapter.N
}
if peerAddress != nil {
subnetworkID := peer.SubnetworkID()
context.AddressManager().AddAddress(peerAddress, peerAddress, subnetworkID)
context.AddressManager().Good(peerAddress, subnetworkID)
context.AddressManager().AddAddresses(peerAddress)
}
context.StartIBDIfRequired()

View File

@ -50,7 +50,7 @@ func (flow *sendVersionFlow) start() error {
subnetworkID := flow.Config().SubnetworkID
// Version message.
localAddress := flow.AddressManager().GetBestLocalAddress(flow.peer.Connection().NetAddress())
localAddress := flow.AddressManager().BestLocalAddress(flow.peer.Connection().NetAddress())
msg := appmessage.NewMsgVersion(localAddress, flow.NetAdapter().ID(),
flow.Config().ActiveNetParams.Name, selectedTipHash, subnetworkID)
msg.AddUserAgent(userAgentName, userAgentVersion, flow.Config().UserAgentComments...)

View File

@ -32,7 +32,8 @@ var handlers = map[appmessage.MessageCommand]handler{
appmessage.CmdResolveFinalityConflictRequestMessage: rpchandlers.HandleResolveFinalityConflict,
appmessage.CmdNotifyFinalityConflictsRequestMessage: rpchandlers.HandleNotifyFinalityConflicts,
appmessage.CmdGetMempoolEntriesRequestMessage: rpchandlers.HandleGetMempoolEntries,
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleShutDown,
appmessage.CmdShutDownRequestMessage: rpchandlers.HandleGetMempoolEntries,
appmessage.CmdGetHeadersRequestMessage: rpchandlers.HandleGetHeaders,
}
func (m *Manager) routerInitializer(router *router.Router, netConnection *netadapter.NetConnection) {

View File

@ -0,0 +1,52 @@
package rpchandlers
import (
"bytes"
"encoding/hex"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
"github.com/kaspanet/kaspad/util/daghash"
)
// HandleGetHeaders handles the respectively named RPC command
func HandleGetHeaders(context *rpccontext.Context, _ *router.Router, request appmessage.Message) (appmessage.Message, error) {
getHeadersRequest := request.(*appmessage.GetHeadersRequestMessage)
dag := context.DAG
var startHash *daghash.Hash
if getHeadersRequest.StartHash != "" {
var err error
startHash, err = daghash.NewHashFromStr(getHeadersRequest.StartHash)
if err != nil {
errorMessage := &appmessage.GetHeadersResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Start hash could not be parsed: %s", err)
return errorMessage, nil
}
}
const getHeadersDefaultLimit uint64 = 2000
limit := getHeadersDefaultLimit
if getHeadersRequest.Limit != 0 {
limit = getHeadersRequest.Limit
}
headers, err := dag.GetHeaders(startHash, limit, getHeadersRequest.IsAscending)
if err != nil {
errorMessage := &appmessage.GetHeadersResponseMessage{}
errorMessage.Error = appmessage.RPCErrorf("Error getting the headers: %s", err)
return errorMessage, nil
}
headersHex := make([]string, len(headers))
var buf bytes.Buffer
for i, header := range headers {
err := header.Serialize(&buf)
if err != nil {
return nil, err
}
headersHex[i] = hex.EncodeToString(buf.Bytes())
buf.Reset()
}
return appmessage.NewGetHeadersResponseMessage(headersHex), nil
}

View File

@ -1,6 +1,9 @@
package rpchandlers
import (
"net"
"strconv"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/app/rpc/rpccontext"
"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
@ -8,14 +11,20 @@ import (
// HandleGetPeerAddresses handles the respectively named RPC command
func HandleGetPeerAddresses(context *rpccontext.Context, _ *router.Router, _ appmessage.Message) (appmessage.Message, error) {
peersState, err := context.AddressManager.PeersStateForSerialization()
if err != nil {
return nil, err
netAddresses := context.AddressManager.Addresses()
addressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(netAddresses))
for i, netAddress := range netAddresses {
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
addressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
}
addresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(peersState.Addresses))
for i, address := range peersState.Addresses {
addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: string(address.Address)}
bannedAddresses := context.AddressManager.BannedAddresses()
bannedAddressMessages := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(bannedAddresses))
for i, netAddress := range bannedAddresses {
addressWithPort := net.JoinHostPort(netAddress.IP.String(), strconv.FormatUint(uint64(netAddress.Port), 10))
bannedAddressMessages[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: addressWithPort}
}
response := appmessage.NewGetPeerAddressesResponseMessage(addresses)
response := appmessage.NewGetPeerAddressesResponseMessage(addressMessages, bannedAddressMessages)
return response, nil
}

View File

@ -412,17 +412,30 @@ func (dag *BlockDAG) isAnyInPastOf(nodes blockSet, other *blockNode) (bool, erro
return false, nil
}
// GetTopHeaders returns the top appmessage.MaxBlockHeadersPerMsg block headers ordered by blue score.
func (dag *BlockDAG) GetTopHeaders(highHash *daghash.Hash, maxHeaders uint64) ([]*appmessage.BlockHeader, error) {
// GetHeaders returns DAG headers ordered by blue score, starts from the given hash with the given direction.
func (dag *BlockDAG) GetHeaders(startHash *daghash.Hash, maxHeaders uint64,
isAscending bool) ([]*appmessage.BlockHeader, error) {
dag.RLock()
defer dag.RUnlock()
if isAscending {
return dag.getHeadersAscending(startHash, maxHeaders)
}
return dag.getHeadersDescending(startHash, maxHeaders)
}
func (dag *BlockDAG) getHeadersDescending(highHash *daghash.Hash, maxHeaders uint64) ([]*appmessage.BlockHeader, error) {
highNode := dag.virtual.blockNode
if highHash != nil {
var ok bool
highNode, ok = dag.index.LookupNode(highHash)
if !ok {
return nil, errors.Errorf("Couldn't find the high hash %s in the dag", highHash)
return nil, errors.Errorf("Couldn't find the start hash %s in the dag", highHash)
}
}
headers := make([]*appmessage.BlockHeader, 0, highNode.blueScore)
headers := make([]*appmessage.BlockHeader, 0, maxHeaders)
queue := newDownHeap()
queue.pushSet(highNode.parents)
@ -438,6 +451,31 @@ func (dag *BlockDAG) GetTopHeaders(highHash *daghash.Hash, maxHeaders uint64) ([
return headers, nil
}
func (dag *BlockDAG) getHeadersAscending(lowHash *daghash.Hash, maxHeaders uint64) ([]*appmessage.BlockHeader, error) {
lowNode := dag.genesis
if lowHash != nil {
var ok bool
lowNode, ok = dag.index.LookupNode(lowHash)
if !ok {
return nil, errors.Errorf("Couldn't find the start hash %s in the dag", lowHash)
}
}
headers := make([]*appmessage.BlockHeader, 0, maxHeaders)
queue := newUpHeap()
queue.pushSet(lowNode.children)
visited := newBlockSet()
for i := uint32(0); queue.Len() > 0 && uint64(len(headers)) < maxHeaders; i++ {
current := queue.pop()
if !visited.contains(current) {
visited.add(current)
headers = append(headers, current.Header())
queue.pushSet(current.children)
}
}
return headers, nil
}
// ForEachHash runs the given fn on every hash that's currently known to
// the DAG.
//

View File

@ -214,7 +214,7 @@ func TestIsKnownBlock(t *testing.T) {
{hash: "732c891529619d43b5aeb3df42ba25dea483a8c0aded1cf585751ebabea28f29", want: true},
// Random hashes should not be available.
{hash: "123", want: false},
{hash: "1234567812345678123456781234567812345678123456781234567812345678", want: false},
}
for i, test := range tests {

View File

@ -627,10 +627,7 @@ func TestProcessTransaction(t *testing.T) {
t.Fatalf("Script: error creating wrappedP2shNonSigScript: %v", err)
}
dummyPrevOutTxID, err := daghash.NewTxIDFromStr("01")
if err != nil {
t.Fatalf("NewShaHashFromStr: unexpected error: %v", err)
}
dummyPrevOutTxID := &daghash.TxID{}
dummyPrevOut := appmessage.Outpoint{TxID: *dummyPrevOutTxID, Index: 1}
dummySigScript := bytes.Repeat([]byte{0x00}, 65)

View File

@ -168,10 +168,7 @@ func TestDust(t *testing.T) {
// TestCheckTransactionStandard tests the checkTransactionStandard API.
func TestCheckTransactionStandard(t *testing.T) {
// Create some dummy, but otherwise standard, data for transactions.
prevOutTxID, err := daghash.NewTxIDFromStr("01")
if err != nil {
t.Fatalf("NewShaHashFromStr: unexpected error: %v", err)
}
prevOutTxID := &daghash.TxID{}
dummyPrevOut := appmessage.Outpoint{TxID: *prevOutTxID, Index: 1}
dummySigScript := bytes.Repeat([]byte{0x00}, 65)
dummyTxIn := appmessage.TxIn{

File diff suppressed because it is too large Load Diff

View File

@ -5,518 +5,27 @@
package addressmanager
import (
"fmt"
"github.com/kaspanet/kaspad/app/appmessage"
"io/ioutil"
"net"
"reflect"
"testing"
"time"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/infrastructure/config"
"github.com/kaspanet/kaspad/infrastructure/db/dbaccess"
"github.com/kaspanet/kaspad/util/mstime"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/pkg/errors"
)
// naTest is used to describe a test to be performed against the NetAddressKey
// method.
type naTest struct {
in appmessage.NetAddress
want AddressKey
}
// naTests houses all of the tests to be performed against the NetAddressKey
// method.
var naTests = make([]naTest, 0)
// Put some IP in here for convenience. Points to google.
var someIP = "173.194.115.66"
// addNaTests
func addNaTests() {
// IPv4
// Localhost
addNaTest("127.0.0.1", 16111, "127.0.0.1:16111")
addNaTest("127.0.0.1", 16110, "127.0.0.1:16110")
// Class A
addNaTest("1.0.0.1", 16111, "1.0.0.1:16111")
addNaTest("2.2.2.2", 16110, "2.2.2.2:16110")
addNaTest("27.253.252.251", 8335, "27.253.252.251:8335")
addNaTest("123.3.2.1", 8336, "123.3.2.1:8336")
// Private Class A
addNaTest("10.0.0.1", 16111, "10.0.0.1:16111")
addNaTest("10.1.1.1", 16110, "10.1.1.1:16110")
addNaTest("10.2.2.2", 8335, "10.2.2.2:8335")
addNaTest("10.10.10.10", 8336, "10.10.10.10:8336")
// Class B
addNaTest("128.0.0.1", 16111, "128.0.0.1:16111")
addNaTest("129.1.1.1", 16110, "129.1.1.1:16110")
addNaTest("180.2.2.2", 8335, "180.2.2.2:8335")
addNaTest("191.10.10.10", 8336, "191.10.10.10:8336")
// Private Class B
addNaTest("172.16.0.1", 16111, "172.16.0.1:16111")
addNaTest("172.16.1.1", 16110, "172.16.1.1:16110")
addNaTest("172.16.2.2", 8335, "172.16.2.2:8335")
addNaTest("172.16.172.172", 8336, "172.16.172.172:8336")
// Class C
addNaTest("193.0.0.1", 16111, "193.0.0.1:16111")
addNaTest("200.1.1.1", 16110, "200.1.1.1:16110")
addNaTest("205.2.2.2", 8335, "205.2.2.2:8335")
addNaTest("223.10.10.10", 8336, "223.10.10.10:8336")
// Private Class C
addNaTest("192.168.0.1", 16111, "192.168.0.1:16111")
addNaTest("192.168.1.1", 16110, "192.168.1.1:16110")
addNaTest("192.168.2.2", 8335, "192.168.2.2:8335")
addNaTest("192.168.192.192", 8336, "192.168.192.192:8336")
// IPv6
// Localhost
addNaTest("::1", 16111, "[::1]:16111")
addNaTest("fe80::1", 16110, "[fe80::1]:16110")
// Link-local
addNaTest("fe80::1:1", 16111, "[fe80::1:1]:16111")
addNaTest("fe91::2:2", 16110, "[fe91::2:2]:16110")
addNaTest("fea2::3:3", 8335, "[fea2::3:3]:8335")
addNaTest("feb3::4:4", 8336, "[feb3::4:4]:8336")
// Site-local
addNaTest("fec0::1:1", 16111, "[fec0::1:1]:16111")
addNaTest("fed1::2:2", 16110, "[fed1::2:2]:16110")
addNaTest("fee2::3:3", 8335, "[fee2::3:3]:8335")
addNaTest("fef3::4:4", 8336, "[fef3::4:4]:8336")
}
func addNaTest(ip string, port uint16, want AddressKey) {
nip := net.ParseIP(ip)
na := *appmessage.NewNetAddressIPPort(nip, port, appmessage.SFNodeNetwork)
test := naTest{na, want}
naTests = append(naTests, test)
}
func lookupFuncForTest(host string) ([]net.IP, error) {
return nil, errors.New("not implemented")
}
func newAddrManagerForTest(t *testing.T, testName string,
localSubnetworkID *subnetworkid.SubnetworkID) (addressManager *AddressManager, teardown func()) {
func newAddrManagerForTest(t *testing.T, testName string) (addressManager *AddressManager, teardown func()) {
cfg := config.DefaultConfig()
cfg.SubnetworkID = localSubnetworkID
dbPath, err := ioutil.TempDir("", testName)
if err != nil {
t.Fatalf("Error creating temporary directory: %s", err)
}
databaseContext, err := dbaccess.New(dbPath)
if err != nil {
t.Fatalf("error creating db: %s", err)
}
addressManager, err = New(cfg, databaseContext)
addressManager, err := New(NewConfig(cfg))
if err != nil {
t.Fatalf("error creating address manager: %s", err)
}
return addressManager, func() {
err := databaseContext.Close()
if err != nil {
t.Fatalf("error closing the database: %s", err)
}
}
}
func TestStartStop(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestStartStop", nil)
defer teardown()
err := amgr.Start()
if err != nil {
t.Fatalf("Address Manager failed to start: %v", err)
}
err = amgr.Stop()
if err != nil {
t.Fatalf("Address Manager failed to stop: %v", err)
}
}
func TestAddAddressByIP(t *testing.T) {
fmtErr := errors.Errorf("")
addrErr := &net.AddrError{}
var tests = []struct {
addrIP string
err error
}{
{
someIP + ":16111",
nil,
},
{
someIP,
addrErr,
},
{
someIP[:12] + ":8333",
fmtErr,
},
{
someIP + ":abcd",
fmtErr,
},
}
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil)
defer teardown()
for i, test := range tests {
err := AddAddressByIP(amgr, test.addrIP, nil)
if test.err != nil && err == nil {
t.Errorf("TestAddAddressByIP test %d failed expected an error and got none", i)
continue
}
if test.err == nil && err != nil {
t.Errorf("TestAddAddressByIP test %d failed expected no error and got one", i)
continue
}
if reflect.TypeOf(err) != reflect.TypeOf(test.err) {
t.Errorf("TestAddAddressByIP test %d failed got %v, want %v", i,
reflect.TypeOf(err), reflect.TypeOf(test.err))
continue
}
}
}
func TestAddLocalAddress(t *testing.T) {
var tests = []struct {
address appmessage.NetAddress
priority AddressPriority
valid bool
}{
{
appmessage.NetAddress{IP: net.ParseIP("192.168.0.100")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
InterfacePrio,
true,
},
{
appmessage.NetAddress{IP: net.ParseIP("204.124.1.1")},
BoundPrio,
true,
},
{
appmessage.NetAddress{IP: net.ParseIP("::1")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("fe80::1")},
InterfacePrio,
false,
},
{
appmessage.NetAddress{IP: net.ParseIP("2620:100::1")},
InterfacePrio,
true,
},
}
amgr, teardown := newAddrManagerForTest(t, "TestAddLocalAddress", nil)
defer teardown()
for x, test := range tests {
result := amgr.AddLocalAddress(&test.address, test.priority)
if result == nil && !test.valid {
t.Errorf("TestAddLocalAddress test #%d failed: %s should have "+
"been accepted", x, test.address.IP)
continue
}
if result != nil && test.valid {
t.Errorf("TestAddLocalAddress test #%d failed: %s should not have "+
"been accepted", x, test.address.IP)
continue
}
}
}
func TestAttempt(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestAttempt", nil)
defer teardown()
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := amgr.GetAddress()
if !ka.LastAttempt().IsZero() {
t.Errorf("Address should not have attempts, but does")
}
na := ka.NetAddress()
amgr.Attempt(na)
if ka.LastAttempt().IsZero() {
t.Errorf("Address should have an attempt, but does not")
}
}
func TestConnected(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestConnected", nil)
defer teardown()
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8333", nil)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := amgr.GetAddress()
na := ka.NetAddress()
// make it an hour ago
na.Timestamp = mstime.Now().Add(time.Hour * -1)
amgr.Connected(na)
if !ka.NetAddress().Timestamp.After(na.Timestamp) {
t.Errorf("Address should have a new timestamp, but does not")
}
}
func TestNeedMoreAddresses(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestNeedMoreAddresses", nil)
defer teardown()
addrsToAdd := 1500
b := amgr.NeedMoreAddresses()
if !b {
t.Errorf("Expected that we need more addresses")
}
addrs := make([]*appmessage.NetAddress, addrsToAdd)
var err error
for i := 0; i < addrsToAdd; i++ {
s := AddressKey(fmt.Sprintf("%d.%d.173.147:8333", i/128+60, i%128+60))
addrs[i], err = amgr.DeserializeNetAddress(s)
if err != nil {
t.Errorf("Failed to turn %s into an address: %v", s, err)
}
}
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
amgr.AddAddresses(addrs, srcAddr, nil)
numAddrs := amgr.TotalNumAddresses()
if numAddrs > addrsToAdd {
t.Errorf("Number of addresses is too many %d vs %d", numAddrs, addrsToAdd)
}
b = amgr.NeedMoreAddresses()
if b {
t.Errorf("Expected that we don't need more addresses")
}
}
func TestGood(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestGood", nil)
defer teardown()
addrsToAdd := 64 * 64
addrs := make([]*appmessage.NetAddress, addrsToAdd)
subnetworkCount := 32
subnetworkIDs := make([]*subnetworkid.SubnetworkID, subnetworkCount)
var err error
for i := 0; i < addrsToAdd; i++ {
s := AddressKey(fmt.Sprintf("%d.173.147.%d:8333", i/64+60, i%64+60))
addrs[i], err = amgr.DeserializeNetAddress(s)
if err != nil {
t.Errorf("Failed to turn %s into an address: %v", s, err)
}
}
for i := 0; i < subnetworkCount; i++ {
subnetworkIDs[i] = &subnetworkid.SubnetworkID{0xff - byte(i)}
}
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
amgr.AddAddresses(addrs, srcAddr, nil)
for i, addr := range addrs {
amgr.Good(addr, subnetworkIDs[i%subnetworkCount])
}
numAddrs := amgr.TotalNumAddresses()
if numAddrs >= addrsToAdd {
t.Errorf("Number of addresses is too many: %d vs %d", numAddrs, addrsToAdd)
}
numCache := len(amgr.AddressCache(true, nil))
if numCache == 0 || numCache >= numAddrs/4 {
t.Errorf("Number of addresses in cache: got %d, want positive and less than %d",
numCache, numAddrs/4)
}
for i := 0; i < subnetworkCount; i++ {
numCache = len(amgr.AddressCache(false, subnetworkIDs[i]))
if numCache == 0 || numCache >= numAddrs/subnetworkCount {
t.Errorf("Number of addresses in subnetwork cache: got %d, want positive and less than %d",
numCache, numAddrs/4/subnetworkCount)
}
}
}
func TestGoodChangeSubnetworkID(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestGoodChangeSubnetworkID", nil)
defer teardown()
addr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
addrKey := NetAddressKey(addr)
srcAddr := appmessage.NewNetAddressIPPort(net.IPv4(173, 144, 173, 111), 8333, 0)
oldSubnetwork := subnetworkid.SubnetworkIDNative
amgr.AddAddress(addr, srcAddr, oldSubnetwork)
amgr.Good(addr, oldSubnetwork)
// make sure address was saved to addressIndex under oldSubnetwork
ka := amgr.knownAddress(addr)
if ka == nil {
t.Fatalf("Address was not found after first time .Good called")
}
if !ka.SubnetworkID().IsEqual(oldSubnetwork) {
t.Fatalf("Address index did not point to oldSubnetwork")
}
// make sure address was added to correct bucket under oldSubnetwork
bucket := amgr.subnetworkTriedAddresBucketArrays[*oldSubnetwork][amgr.triedAddressBucketIndex(addr)]
wasFound := false
for _, ka := range bucket {
if NetAddressKey(ka.NetAddress()) == addrKey {
wasFound = true
}
}
if !wasFound {
t.Fatalf("Address was not found in the correct bucket in oldSubnetwork")
}
// now call .Good again with a different subnetwork
newSubnetwork := subnetworkid.SubnetworkIDRegistry
amgr.Good(addr, newSubnetwork)
// make sure address was updated in addressIndex under newSubnetwork
ka = amgr.knownAddress(addr)
if ka == nil {
t.Fatalf("Address was not found after second time .Good called")
}
if !ka.SubnetworkID().IsEqual(newSubnetwork) {
t.Fatalf("Address index did not point to newSubnetwork")
}
// make sure address was removed from bucket under oldSubnetwork
bucket = amgr.subnetworkTriedAddresBucketArrays[*oldSubnetwork][amgr.triedAddressBucketIndex(addr)]
wasFound = false
for _, ka := range bucket {
if NetAddressKey(ka.NetAddress()) == addrKey {
wasFound = true
}
}
if wasFound {
t.Fatalf("Address was not removed from bucket in oldSubnetwork")
}
// make sure address was added to correct bucket under newSubnetwork
bucket = amgr.subnetworkTriedAddresBucketArrays[*newSubnetwork][amgr.triedAddressBucketIndex(addr)]
wasFound = false
for _, ka := range bucket {
if NetAddressKey(ka.NetAddress()) == addrKey {
wasFound = true
}
}
if !wasFound {
t.Fatalf("Address was not found in the correct bucket in newSubnetwork")
}
}
func TestGetAddress(t *testing.T) {
localSubnetworkID := &subnetworkid.SubnetworkID{0xff}
amgr, teardown := newAddrManagerForTest(t, "TestGetAddress", localSubnetworkID)
defer teardown()
// Get an address from an empty set (should error)
if rv := amgr.GetAddress(); rv != nil {
t.Errorf("GetAddress failed: got: %v want: %v\n", rv, nil)
}
// Add a new address and get it
err := AddAddressByIP(amgr, someIP+":8332", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka := amgr.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
amgr.Attempt(ka.NetAddress())
// Checks that we don't get it if we find that it has other subnetwork ID than expected.
actualSubnetworkID := &subnetworkid.SubnetworkID{0xfe}
amgr.Good(ka.NetAddress(), actualSubnetworkID)
ka = amgr.GetAddress()
if ka != nil {
t.Errorf("Didn't expect to get an address because there shouldn't be any address from subnetwork ID %s or nil", localSubnetworkID)
}
// Checks that the total number of addresses incremented although the new address is not full node or a partial node of the same subnetwork as the local node.
numAddrs := amgr.TotalNumAddresses()
if numAddrs != 1 {
t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1)
}
// Now we repeat the same process, but now the address has the expected subnetwork ID.
// Add a new address and get it
err = AddAddressByIP(amgr, someIP+":8333", localSubnetworkID)
if err != nil {
t.Fatalf("Adding address failed: %v", err)
}
ka = amgr.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
}
if !ka.SubnetworkID().IsEqual(localSubnetworkID) {
t.Errorf("Wrong Subnetwork ID: got %v, want %v", *ka.SubnetworkID(), localSubnetworkID)
}
amgr.Attempt(ka.NetAddress())
// Mark this as a good address and get it
amgr.Good(ka.NetAddress(), localSubnetworkID)
ka = amgr.GetAddress()
if ka == nil {
t.Fatalf("Did not get an address where there is one in the pool")
}
if ka.NetAddress().IP.String() != someIP {
t.Errorf("Wrong IP: got %v, want %v", ka.NetAddress().IP.String(), someIP)
}
if *ka.SubnetworkID() != *localSubnetworkID {
t.Errorf("Wrong Subnetwork ID: got %v, want %v", ka.SubnetworkID(), localSubnetworkID)
}
numAddrs = amgr.TotalNumAddresses()
if numAddrs != 2 {
t.Errorf("Wrong number of addresses: got %d, want %d", numAddrs, 1)
}
}
func TestGetBestLocalAddress(t *testing.T) {
func TestBestLocalAddress(t *testing.T) {
localAddrs := []appmessage.NetAddress{
{IP: net.ParseIP("192.168.0.100")},
{IP: net.ParseIP("::1")},
@ -557,12 +66,12 @@ func TestGetBestLocalAddress(t *testing.T) {
},
}
amgr, teardown := newAddrManagerForTest(t, "TestGetBestLocalAddress", nil)
amgr, teardown := newAddrManagerForTest(t, "TestGetBestLocalAddress")
defer teardown()
// Test against default when there's no address
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
got := amgr.BestLocalAddress(&test.remoteAddr)
if !test.want0.IP.Equal(got.IP) {
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want1.IP, got.IP)
@ -571,12 +80,12 @@ func TestGetBestLocalAddress(t *testing.T) {
}
for _, localAddr := range localAddrs {
amgr.AddLocalAddress(&localAddr, InterfacePrio)
amgr.localAddresses.addLocalNetAddress(&localAddr, InterfacePrio)
}
// Test against want1
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
got := amgr.BestLocalAddress(&test.remoteAddr)
if !test.want1.IP.Equal(got.IP) {
t.Errorf("TestGetBestLocalAddress test1 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want1.IP, got.IP)
@ -586,42 +95,15 @@ func TestGetBestLocalAddress(t *testing.T) {
// Add a public IP to the list of local addresses.
localAddr := appmessage.NetAddress{IP: net.ParseIP("204.124.8.100")}
amgr.AddLocalAddress(&localAddr, InterfacePrio)
amgr.localAddresses.addLocalNetAddress(&localAddr, InterfacePrio)
// Test against want2
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
got := amgr.BestLocalAddress(&test.remoteAddr)
if !test.want2.IP.Equal(got.IP) {
t.Errorf("TestGetBestLocalAddress test2 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want2.IP, got.IP)
continue
}
}
/*
// Add a Tor generated IP address
localAddr = appmessage.NetAddress{IP: net.ParseIP("fd87:d87e:eb43:25::1")}
amgr.AddLocalAddress(&localAddr, ManualPrio)
// Test against want3
for x, test := range tests {
got := amgr.GetBestLocalAddress(&test.remoteAddr)
if !test.want3.IP.Equal(got.IP) {
t.Errorf("TestGetBestLocalAddress test3 #%d failed for remote address %s: want %s got %s",
x, test.remoteAddr.IP, test.want3.IP, got.IP)
continue
}
}
*/
}
func TestNetAddressKey(t *testing.T) {
addNaTests()
t.Logf("Running %d tests", len(naTests))
for i, test := range naTests {
key := NetAddressKey(&test.in)
if key != test.want {
t.Errorf("NetAddressKey #%d\n got: %s want: %s", i, key, test.want)
continue
}
}
}

View File

@ -0,0 +1,43 @@
package addressmanager
import (
"math/rand"
"time"
"github.com/kaspanet/kaspad/app/appmessage"
)
// AddressRandomize implement AddressRandomizer interface
type AddressRandomize struct {
random *rand.Rand
}
// NewAddressRandomize returns a new RandomizeAddress.
func NewAddressRandomize() *AddressRandomize {
return &AddressRandomize{
random: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}
// RandomAddress returns a random address from input list
func (amc *AddressRandomize) RandomAddress(addresses []*appmessage.NetAddress) *appmessage.NetAddress {
if len(addresses) > 0 {
randomIndex := rand.Intn(len(addresses))
return addresses[randomIndex]
}
return nil
}
// RandomAddresses returns count addresses at random from input list
func (amc *AddressRandomize) RandomAddresses(addresses []*appmessage.NetAddress, count int) []*appmessage.NetAddress {
result := make([]*appmessage.NetAddress, 0, count)
if len(addresses) > 0 {
randomIndexes := rand.Perm(len(addresses))
for i := 0; i < count; i++ {
result = append(result, addresses[randomIndexes[i]])
}
}
return result
}

View File

@ -0,0 +1,27 @@
package addressmanager
import (
"net"
"github.com/kaspanet/kaspad/infrastructure/config"
)
// Config is a descriptor which specifies the AddressManager instance configuration.
type Config struct {
AcceptUnroutable bool
DefaultPort string
ExternalIPs []string
Listeners []string
Lookup func(string) ([]net.IP, error)
}
// NewConfig returns a new address manager Config.
func NewConfig(cfg *config.Config) *Config {
return &Config{
AcceptUnroutable: cfg.NetParams().AcceptUnroutable,
DefaultPort: cfg.NetParams().DefaultPort,
ExternalIPs: cfg.ExternalIPs,
Listeners: cfg.Listeners,
Lookup: cfg.Lookup,
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/mstime"
)
func TstKnownAddressIsBad(ka *KnownAddress) bool {
return ka.isBad()
}
func TstKnownAddressChance(ka *KnownAddress) float64 {
return ka.chance()
}
func TstNewKnownAddress(na *appmessage.NetAddress, attempts int,
lastattempt, lastsuccess mstime.Time, tried bool, refs int) *KnownAddress {
return &KnownAddress{netAddress: na, attempts: attempts, lastAttempt: lastattempt,
lastSuccess: lastsuccess, tried: tried, referenceCount: refs}
}

View File

@ -1,107 +0,0 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/mstime"
"time"
"github.com/kaspanet/kaspad/util/subnetworkid"
)
// KnownAddress tracks information about a known network address that is used
// to determine how viable an address is.
type KnownAddress struct {
netAddress *appmessage.NetAddress
sourceAddress *appmessage.NetAddress
attempts int
lastAttempt mstime.Time
lastSuccess mstime.Time
tried bool
referenceCount int // reference count of new buckets
subnetworkID *subnetworkid.SubnetworkID
isBanned bool
bannedTime mstime.Time
}
// NetAddress returns the underlying appmessage.NetAddress associated with the
// known address.
func (ka *KnownAddress) NetAddress() *appmessage.NetAddress {
return ka.netAddress
}
// SubnetworkID returns the subnetwork ID of the known address.
func (ka *KnownAddress) SubnetworkID() *subnetworkid.SubnetworkID {
return ka.subnetworkID
}
// LastAttempt returns the last time the known address was attempted.
func (ka *KnownAddress) LastAttempt() mstime.Time {
return ka.lastAttempt
}
// chance returns the selection probability for a known address. The priority
// depends upon how recently the address has been seen, how recently it was last
// attempted and how often attempts to connect to it have failed.
func (ka *KnownAddress) chance() float64 {
now := mstime.Now()
lastAttempt := now.Sub(ka.lastAttempt)
if lastAttempt < 0 {
lastAttempt = 0
}
c := 1.0
// Very recent attempts are less likely to be retried.
if lastAttempt < 10*time.Minute {
c *= 0.01
}
// Failed attempts deprioritise.
for i := ka.attempts; i > 0; i-- {
c /= 1.5
}
return c
}
// isBad returns true if the address in question has not been tried in the last
// minute and meets one of the following criteria:
// 1) It claims to be from the future
// 2) It hasn't been seen in over a month
// 3) It has failed at least three times and never succeeded
// 4) It has failed ten times in the last week
// All addresses that meet these criteria are assumed to be worthless and not
// worth keeping hold of.
func (ka *KnownAddress) isBad() bool {
if ka.lastAttempt.After(mstime.Now().Add(-1 * time.Minute)) {
return false
}
// From the future?
if ka.netAddress.Timestamp.After(mstime.Now().Add(10 * time.Minute)) {
return true
}
// Over a month old?
if ka.netAddress.Timestamp.Before(mstime.Now().Add(-1 * numMissingDays * time.Hour * 24)) {
return true
}
// Never succeeded?
if ka.lastSuccess.IsZero() && ka.attempts >= numRetries {
return true
}
// Hasn't succeeded in too long?
if !ka.lastSuccess.After(mstime.Now().Add(-1*minBadDays*time.Hour*24)) &&
ka.attempts >= maxFailures {
return true
}
return false
}

View File

@ -1,115 +0,0 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package addressmanager_test
import (
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/mstime"
"math"
"testing"
"time"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
)
func TestChance(t *testing.T) {
now := mstime.Now()
var tests = []struct {
addr *addressmanager.KnownAddress
expected float64
}{
{
//Test normal case
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1.0,
}, {
//Test case in which lastseen < 0
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(20 * time.Second)},
0, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1.0,
}, {
//Test case in which lastAttempt < 0
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(30*time.Minute), mstime.Now(), false, 0),
1.0 * .01,
}, {
//Test case in which lastAttempt < ten minutes
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
0, mstime.Now().Add(-5*time.Minute), mstime.Now(), false, 0),
1.0 * .01,
}, {
//Test case with several failed attempts.
addressmanager.TstNewKnownAddress(&appmessage.NetAddress{Timestamp: now.Add(-35 * time.Second)},
2, mstime.Now().Add(-30*time.Minute), mstime.Now(), false, 0),
1 / 1.5 / 1.5,
},
}
err := .0001
for i, test := range tests {
chance := addressmanager.TstKnownAddressChance(test.addr)
if math.Abs(test.expected-chance) >= err {
t.Errorf("case %d: got %f, expected %f", i, chance, test.expected)
}
}
}
func TestIsBad(t *testing.T) {
now := mstime.Now()
future := now.Add(35 * time.Minute)
monthOld := now.Add(-43 * time.Hour * 24)
secondsOld := now.Add(-2 * time.Second)
minutesOld := now.Add(-27 * time.Minute)
hoursOld := now.Add(-5 * time.Hour)
zeroTime := mstime.Time{}
futureNa := &appmessage.NetAddress{Timestamp: future}
minutesOldNa := &appmessage.NetAddress{Timestamp: minutesOld}
monthOldNa := &appmessage.NetAddress{Timestamp: monthOld}
currentNa := &appmessage.NetAddress{Timestamp: secondsOld}
//Test addresses that have been tried in the last minute.
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(futureNa, 3, secondsOld, zeroTime, false, 0)) {
t.Errorf("test case 1: addresses that have been tried in the last minute are not bad.")
}
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(monthOldNa, 3, secondsOld, zeroTime, false, 0)) {
t.Errorf("test case 2: addresses that have been tried in the last minute are not bad.")
}
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 3, secondsOld, zeroTime, false, 0)) {
t.Errorf("test case 3: addresses that have been tried in the last minute are not bad.")
}
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 3, secondsOld, monthOld, true, 0)) {
t.Errorf("test case 4: addresses that have been tried in the last minute are not bad.")
}
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(currentNa, 2, secondsOld, secondsOld, true, 0)) {
t.Errorf("test case 5: addresses that have been tried in the last minute are not bad.")
}
//Test address that claims to be from the future.
if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(futureNa, 0, minutesOld, hoursOld, true, 0)) {
t.Errorf("test case 6: addresses that claim to be from the future are bad.")
}
//Test address that has not been seen in over a month.
if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(monthOldNa, 0, minutesOld, hoursOld, true, 0)) {
t.Errorf("test case 7: addresses more than a month old are bad.")
}
//It has failed at least three times and never succeeded.
if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 3, minutesOld, zeroTime, true, 0)) {
t.Errorf("test case 8: addresses that have never succeeded are bad.")
}
//It has failed ten times in the last week
if !addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 10, minutesOld, monthOld, true, 0)) {
t.Errorf("test case 9: addresses that have not succeeded in too long are bad.")
}
//Test an address that should work.
if addressmanager.TstKnownAddressIsBad(addressmanager.TstNewKnownAddress(minutesOldNa, 2, minutesOld, hoursOld, true, 0)) {
t.Errorf("test case 10: This should be a valid address.")
}
}

View File

@ -0,0 +1,400 @@
package addressmanager
import (
"net"
"runtime"
"strconv"
"strings"
"sync"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/pkg/errors"
)
// AddressPriority type is used to describe the hierarchy of local address
// discovery methods.
type AddressPriority int
const (
// InterfacePrio signifies the address is on a local interface
InterfacePrio AddressPriority = iota
// BoundPrio signifies the address has been explicitly bounded to.
BoundPrio
// UpnpPrio signifies the address was obtained from UPnP.
UpnpPrio
// HTTPPrio signifies the address was obtained from an external HTTP service.
HTTPPrio
// ManualPrio signifies the address was provided by --externalip.
ManualPrio
)
type localAddress struct {
netAddress *appmessage.NetAddress
score AddressPriority
}
type localAddressManager struct {
localAddresses map[AddressKey]*localAddress
lookupFunc func(string) ([]net.IP, error)
cfg *Config
mutex sync.Mutex
}
func newLocalAddressManager(cfg *Config) (*localAddressManager, error) {
localAddressManager := localAddressManager{
localAddresses: map[AddressKey]*localAddress{},
cfg: cfg,
lookupFunc: cfg.Lookup,
}
err := localAddressManager.initListeners()
if err != nil {
return nil, err
}
return &localAddressManager, nil
}
// addLocalNetAddress adds netAddress to the list of known local addresses to advertise
// with the given priority.
func (lam *localAddressManager) addLocalNetAddress(netAddress *appmessage.NetAddress, priority AddressPriority) error {
if !IsRoutable(netAddress, lam.cfg.AcceptUnroutable) {
return errors.Errorf("address %s is not routable", netAddress.IP)
}
lam.mutex.Lock()
defer lam.mutex.Unlock()
addressKey := netAddressKey(netAddress)
address, ok := lam.localAddresses[addressKey]
if !ok || address.score < priority {
if ok {
address.score = priority + 1
} else {
lam.localAddresses[addressKey] = &localAddress{
netAddress: netAddress,
score: priority,
}
}
}
return nil
}
// bestLocalAddress returns the most appropriate local address to use
// for the given remote address.
func (lam *localAddressManager) bestLocalAddress(remoteAddress *appmessage.NetAddress) *appmessage.NetAddress {
lam.mutex.Lock()
defer lam.mutex.Unlock()
bestReach := 0
var bestScore AddressPriority
var bestAddress *appmessage.NetAddress
for _, localAddress := range lam.localAddresses {
reach := reachabilityFrom(localAddress.netAddress, remoteAddress, lam.cfg.AcceptUnroutable)
if reach > bestReach ||
(reach == bestReach && localAddress.score > bestScore) {
bestReach = reach
bestScore = localAddress.score
bestAddress = localAddress.netAddress
}
}
if bestAddress == nil {
// Send something unroutable if nothing suitable.
var ip net.IP
if !IsIPv4(remoteAddress) {
ip = net.IPv6zero
} else {
ip = net.IPv4zero
}
services := appmessage.SFNodeNetwork | appmessage.SFNodeBloom
bestAddress = appmessage.NewNetAddressIPPort(ip, 0, services)
}
return bestAddress
}
// addLocalAddress adds an address that this node is listening on to the
// address manager so that it may be relayed to peers.
func (lam *localAddressManager) addLocalAddress(addr string) error {
host, portStr, err := net.SplitHostPort(addr)
if err != nil {
return err
}
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return err
}
if ip := net.ParseIP(host); ip != nil && ip.IsUnspecified() {
// If bound to unspecified address, advertise all local interfaces
addrs, err := net.InterfaceAddrs()
if err != nil {
return err
}
for _, addr := range addrs {
ifaceIP, _, err := net.ParseCIDR(addr.String())
if err != nil {
continue
}
// If bound to 0.0.0.0, do not add IPv6 interfaces and if bound to
// ::, do not add IPv4 interfaces.
if (ip.To4() == nil) != (ifaceIP.To4() == nil) {
continue
}
netAddr := appmessage.NewNetAddressIPPort(ifaceIP, uint16(port), appmessage.DefaultServices)
lam.addLocalNetAddress(netAddr, BoundPrio)
}
} else {
netAddr, err := lam.hostToNetAddress(host, uint16(port), appmessage.DefaultServices)
if err != nil {
return err
}
lam.addLocalNetAddress(netAddr, BoundPrio)
}
return nil
}
// initListeners initializes the configured net listeners and adds any bound
// addresses to the address manager
func (lam *localAddressManager) initListeners() error {
if len(lam.cfg.ExternalIPs) != 0 {
defaultPort, err := strconv.ParseUint(lam.cfg.DefaultPort, 10, 16)
if err != nil {
log.Errorf("Can not parse default port %s for active DAG: %s",
lam.cfg.DefaultPort, err)
return err
}
for _, sip := range lam.cfg.ExternalIPs {
eport := uint16(defaultPort)
host, portstr, err := net.SplitHostPort(sip)
if err != nil {
// no port, use default.
host = sip
} else {
port, err := strconv.ParseUint(portstr, 10, 16)
if err != nil {
log.Warnf("Can not parse port from %s for "+
"externalip: %s", sip, err)
continue
}
eport = uint16(port)
}
na, err := lam.hostToNetAddress(host, eport, appmessage.DefaultServices)
if err != nil {
log.Warnf("Not adding %s as externalip: %s", sip, err)
continue
}
err = lam.addLocalNetAddress(na, ManualPrio)
if err != nil {
log.Warnf("Skipping specified external IP: %s", err)
}
}
} else {
// Listen for TCP connections at the configured addresses
netAddrs, err := parseListeners(lam.cfg.Listeners)
if err != nil {
return err
}
// Add bound addresses to address manager to be advertised to peers.
for _, addr := range netAddrs {
listener, err := net.Listen(addr.Network(), addr.String())
if err != nil {
log.Warnf("Can't listen on %s: %s", addr, err)
continue
}
addr := listener.Addr().String()
err = listener.Close()
if err != nil {
return err
}
err = lam.addLocalAddress(addr)
if err != nil {
log.Warnf("Skipping bound address %s: %s", addr, err)
}
}
}
return nil
}
// hostToNetAddress returns a netaddress given a host address. If
// the host is not an IP address it will be resolved.
func (lam *localAddressManager) hostToNetAddress(host string, port uint16, services appmessage.ServiceFlag) (*appmessage.NetAddress, error) {
ip := net.ParseIP(host)
if ip == nil {
ips, err := lam.lookupFunc(host)
if err != nil {
return nil, err
}
if len(ips) == 0 {
return nil, errors.Errorf("no addresses found for %s", host)
}
ip = ips[0]
}
return appmessage.NewNetAddressIPPort(ip, port, services), nil
}
// parseListeners determines whether each listen address is IPv4 and IPv6 and
// returns a slice of appropriate net.Addrs to listen on with TCP. It also
// properly detects addresses which apply to "all interfaces" and adds the
// address as both IPv4 and IPv6.
func parseListeners(addrs []string) ([]net.Addr, error) {
netAddrs := make([]net.Addr, 0, len(addrs)*2)
for _, addr := range addrs {
host, _, err := net.SplitHostPort(addr)
if err != nil {
// Shouldn't happen due to already being normalized.
return nil, err
}
// Empty host or host of * on plan9 is both IPv4 and IPv6.
if host == "" || (host == "*" && runtime.GOOS == "plan9") {
netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr})
netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr})
continue
}
// Strip IPv6 zone id if present since net.ParseIP does not
// handle it.
zoneIndex := strings.LastIndex(host, "%")
if zoneIndex > 0 {
host = host[:zoneIndex]
}
// Parse the IP.
ip := net.ParseIP(host)
if ip == nil {
hostAddrs, err := net.LookupHost(host)
if err != nil {
return nil, err
}
ip = net.ParseIP(hostAddrs[0])
if ip == nil {
return nil, errors.Errorf("Cannot resolve IP address for host '%s'", host)
}
}
// To4 returns nil when the IP is not an IPv4 address, so use
// this determine the address type.
if ip.To4() == nil {
netAddrs = append(netAddrs, simpleAddr{net: "tcp6", addr: addr})
} else {
netAddrs = append(netAddrs, simpleAddr{net: "tcp4", addr: addr})
}
}
return netAddrs, nil
}
// reachabilityFrom returns the relative reachability of the provided local
// address to the provided remote address.
func reachabilityFrom(localAddress, remoteAddress *appmessage.NetAddress, acceptUnroutable bool) int {
const (
Unreachable = 0
Default = iota
Teredo
Ipv6Weak
Ipv4
Ipv6Strong
Private
)
IsRoutable := func(na *appmessage.NetAddress) bool {
if acceptUnroutable {
return !IsLocal(na)
}
return IsValid(na) && !(IsRFC1918(na) || IsRFC2544(na) ||
IsRFC3927(na) || IsRFC4862(na) || IsRFC3849(na) ||
IsRFC4843(na) || IsRFC5737(na) || IsRFC6598(na) ||
IsLocal(na) || (IsRFC4193(na)))
}
if !IsRoutable(remoteAddress) {
return Unreachable
}
if IsRFC4380(remoteAddress) {
if !IsRoutable(localAddress) {
return Default
}
if IsRFC4380(localAddress) {
return Teredo
}
if IsIPv4(localAddress) {
return Ipv4
}
return Ipv6Weak
}
if IsIPv4(remoteAddress) {
if IsRoutable(localAddress) && IsIPv4(localAddress) {
return Ipv4
}
return Unreachable
}
/* ipv6 */
var tunnelled bool
// Is our v6 is tunnelled?
if IsRFC3964(localAddress) || IsRFC6052(localAddress) || IsRFC6145(localAddress) {
tunnelled = true
}
if !IsRoutable(localAddress) {
return Default
}
if IsRFC4380(localAddress) {
return Teredo
}
if IsIPv4(localAddress) {
return Ipv4
}
if tunnelled {
// only prioritise ipv6 if we aren't tunnelling it.
return Ipv6Weak
}
return Ipv6Strong
}
// simpleAddr implements the net.Addr interface with two struct fields
type simpleAddr struct {
net, addr string
}
// String returns the address.
//
// This is part of the net.Addr interface.
func (a simpleAddr) String() string {
return a.addr
}
// Network returns the network.
//
// This is part of the net.Addr interface.
func (a simpleAddr) Network() string {
return a.net
}
// Ensure simpleAddr implements the net.Addr interface.
var _ net.Addr = simpleAddr{}

View File

@ -5,8 +5,9 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"net"
"github.com/kaspanet/kaspad/app/appmessage"
)
var (
@ -77,6 +78,13 @@ var (
heNet = ipNet("2001:470::", 32, 128)
)
const (
// GetAddressesMax is the most addresses that we will send in response
// to a getAddress (in practise the most addresses we will return from a
// call to AddressCache()).
GetAddressesMax = 2500
)
// ipNet returns a net.IPNet struct given the passed IP address string, number
// of one bits to include at the start of the mask, and the total number of bits
// for the mask.
@ -199,8 +207,8 @@ func IsValid(na *appmessage.NetAddress) bool {
// IsRoutable returns whether or not the passed address is routable over
// the public internet. This is true as long as the address is valid and is not
// in any reserved ranges.
func (am *AddressManager) IsRoutable(na *appmessage.NetAddress) bool {
if am.cfg.NetParams().AcceptUnroutable {
func IsRoutable(na *appmessage.NetAddress, acceptUnroutable bool) bool {
if acceptUnroutable {
return !IsLocal(na)
}
@ -218,7 +226,7 @@ func (am *AddressManager) GroupKey(na *appmessage.NetAddress) string {
if IsLocal(na) {
return "local"
}
if !am.IsRoutable(na) {
if !IsRoutable(na, am.cfg.AcceptUnroutable) {
return "unroutable"
}
if IsIPv4(na) {

View File

@ -5,15 +5,16 @@
package addressmanager
import (
"github.com/kaspanet/kaspad/app/appmessage"
"net"
"testing"
"github.com/kaspanet/kaspad/app/appmessage"
)
// TestIPTypes ensures the various functions which determine the type of an IP
// address based on RFCs work as intended.
func TestIPTypes(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil)
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP")
defer teardown()
type ipTest struct {
in appmessage.NetAddress
@ -136,7 +137,7 @@ func TestIPTypes(t *testing.T) {
t.Errorf("IsValid %s\n got: %v want: %v", test.in.IP, rv, test.valid)
}
if rv := amgr.IsRoutable(&test.in); rv != test.routable {
if rv := IsRoutable(&test.in, amgr.cfg.AcceptUnroutable); rv != test.routable {
t.Errorf("IsRoutable %s\n got: %v want: %v", test.in.IP, rv, test.routable)
}
}
@ -145,7 +146,7 @@ func TestIPTypes(t *testing.T) {
// TestGroupKey tests the GroupKey function to ensure it properly groups various
// IP addresses.
func TestGroupKey(t *testing.T) {
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP", nil)
amgr, teardown := newAddrManagerForTest(t, "TestAddAddressByIP")
defer teardown()
tests := []struct {

View File

@ -1,11 +1,12 @@
package addressmanager
import (
"net"
"strconv"
"github.com/kaspanet/kaspad/app/appmessage"
"github.com/kaspanet/kaspad/util/subnetworkid"
"github.com/pkg/errors"
"net"
"strconv"
)
// AddAddressByIP adds an address where we are given an ip:port and not a
@ -26,6 +27,6 @@ func AddAddressByIP(am *AddressManager, addressIP string, subnetworkID *subnetwo
return errors.Errorf("invalid port %s: %s", portString, err)
}
netAddress := appmessage.NewNetAddressIPPort(ip, uint16(port), 0)
am.AddAddress(netAddress, netAddress, subnetworkID)
am.AddAddresses(netAddress)
return nil
}

View File

@ -1,5 +1,7 @@
package connmanager
import "github.com/kaspanet/kaspad/app/appmessage"
// checkOutgoingConnections goes over all activeOutgoing and makes sure they are still active.
// Then it opens connections so that we have targetOutgoing active connections
func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) {
@ -14,6 +16,12 @@ func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) {
delete(c.activeOutgoing, address)
}
connections := c.netAdapter.P2PConnections()
connectedAddresses := make([]*appmessage.NetAddress, len(connections))
for i, connection := range connections {
connectedAddresses[i] = connection.NetAddress()
}
liveConnections := len(c.activeOutgoing)
if c.targetOutgoing == liveConnections {
return
@ -23,47 +31,21 @@ func (c *ConnectionManager) checkOutgoingConnections(connSet connectionSet) {
liveConnections, c.targetOutgoing, c.targetOutgoing-liveConnections)
connectionsNeededCount := c.targetOutgoing - len(c.activeOutgoing)
connectionAttempts := connectionsNeededCount * 2
for i := 0; i < connectionAttempts; i++ {
// Return in case we've already reached or surpassed our target
if len(c.activeOutgoing) >= c.targetOutgoing {
return
}
netAddresses := c.addressManager.RandomAddresses(connectionsNeededCount, connectedAddresses)
address := c.addressManager.GetAddress()
if address == nil {
log.Warnf("No more addresses available")
return
}
for _, netAddress := range netAddresses {
addressString := netAddress.TCPAddress().String()
netAddress := address.NetAddress()
tcpAddress := netAddress.TCPAddress()
addressString := tcpAddress.String()
if c.connectionExists(addressString) {
log.Debugf("Fetched address %s from address manager but it's already connected. Skipping...", addressString)
continue
}
isBanned, err := c.addressManager.IsBanned(netAddress)
if err != nil {
log.Infof("Couldn't resolve whether %s is banned: %s", addressString, err)
continue
}
if isBanned {
continue
}
c.addressManager.Attempt(netAddress)
log.Debugf("Connecting to %s because we have %d outgoing connections and the target is "+
"%d", addressString, len(c.activeOutgoing), c.targetOutgoing)
err = c.initiateConnection(addressString)
err := c.initiateConnection(addressString)
if err != nil {
log.Infof("Couldn't connect to %s: %s", addressString, err)
c.addressManager.RemoveAddress(netAddress)
continue
}
c.addressManager.Connected(netAddress)
c.activeOutgoing[addressString] = struct{}{}
}
}

View File

@ -74,6 +74,8 @@ message KaspadMessage {
GetMempoolEntriesResponseMessage getMempoolEntriesResponse = 1044;
ShutDownRequestMessage shutDownRequest = 1045;
ShutDownResponseMessage shutDownResponse = 1046;
GetHeadersRequestMessage getHeadersRequest = 10347;
GetHeadersResponseMessage getHeadersResponse = 1048;
}
}
@ -350,6 +352,7 @@ message GetPeerAddressesRequestMessage{
message GetPeerAddressesResponseMessage{
repeated GetPeerAddressesKnownAddressMessage addresses = 1;
repeated GetPeerAddressesKnownAddressMessage bannedAddresses = 2;
RPCError error = 1000;
}
@ -621,6 +624,17 @@ message ShutDownResponseMessage{
RPCError error = 1000;
}
message GetHeadersRequestMessage{
string startHash = 1;
uint64 limit = 2;
bool isAscending = 3;
}
message GetHeadersResponseMessage{
repeated string headers = 1;
RPCError error = 1000;
}
service RPC {
rpc MessageStream (stream KaspadMessage) returns (stream KaspadMessage) {}
}

View File

@ -0,0 +1,45 @@
package protowire
import (
"github.com/kaspanet/kaspad/app/appmessage"
)
func (x *KaspadMessage_GetHeadersRequest) toAppMessage() (appmessage.Message, error) {
return &appmessage.GetHeadersRequestMessage{
StartHash: x.GetHeadersRequest.StartHash,
Limit: x.GetHeadersRequest.Limit,
IsAscending: x.GetHeadersRequest.IsAscending,
}, nil
}
func (x *KaspadMessage_GetHeadersRequest) fromAppMessage(message *appmessage.GetHeadersRequestMessage) error {
x.GetHeadersRequest = &GetHeadersRequestMessage{
StartHash: message.StartHash,
Limit: message.Limit,
IsAscending: message.IsAscending,
}
return nil
}
func (x *KaspadMessage_GetHeadersResponse) toAppMessage() (appmessage.Message, error) {
var err *appmessage.RPCError
if x.GetHeadersResponse.Error != nil {
err = &appmessage.RPCError{Message: x.GetHeadersResponse.Error.Message}
}
return &appmessage.GetHeadersResponseMessage{
Headers: x.GetHeadersResponse.Headers,
Error: err,
}, nil
}
func (x *KaspadMessage_GetHeadersResponse) fromAppMessage(message *appmessage.GetHeadersResponseMessage) error {
var err *RPCError
if message.Error != nil {
err = &RPCError{Message: message.Error.Message}
}
x.GetHeadersResponse = &GetHeadersResponseMessage{
Headers: message.Headers,
Error: err,
}
return nil
}

View File

@ -19,9 +19,14 @@ func (x *KaspadMessage_GetPeerAddressesResponse) toAppMessage() (appmessage.Mess
for i, address := range x.GetPeerAddressesResponse.Addresses {
addresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: address.Addr}
}
bannedAddresses := make([]*appmessage.GetPeerAddressesKnownAddressMessage, len(x.GetPeerAddressesResponse.BannedAddresses))
for i, address := range x.GetPeerAddressesResponse.BannedAddresses {
bannedAddresses[i] = &appmessage.GetPeerAddressesKnownAddressMessage{Addr: address.Addr}
}
return &appmessage.GetPeerAddressesResponseMessage{
Addresses: addresses,
Error: err,
Addresses: addresses,
BannedAddresses: bannedAddresses,
Error: err,
}, nil
}
@ -34,9 +39,14 @@ func (x *KaspadMessage_GetPeerAddressesResponse) fromAppMessage(message *appmess
for i, address := range message.Addresses {
addresses[i] = &GetPeerAddressesKnownAddressMessage{Addr: address.Addr}
}
bannedAddresses := make([]*GetPeerAddressesKnownAddressMessage, len(message.BannedAddresses))
for i, address := range message.BannedAddresses {
bannedAddresses[i] = &GetPeerAddressesKnownAddressMessage{Addr: address.Addr}
}
x.GetPeerAddressesResponse = &GetPeerAddressesResponseMessage{
Addresses: addresses,
Error: err,
Addresses: addresses,
BannedAddresses: bannedAddresses,
Error: err,
}
return nil
}

View File

@ -534,6 +534,20 @@ func toRPCPayload(message appmessage.Message) (isKaspadMessage_Payload, error) {
return nil, err
}
return payload, nil
case *appmessage.GetHeadersRequestMessage:
payload := new(KaspadMessage_GetHeadersRequest)
err := payload.fromAppMessage(message)
if err != nil {
return nil, err
}
return payload, nil
case *appmessage.GetHeadersResponseMessage:
payload := new(KaspadMessage_GetHeadersResponse)
err := payload.fromAppMessage(message)
if err != nil {
return nil, err
}
return payload, nil
default:
return nil, nil
}

View File

@ -0,0 +1,20 @@
package rpcclient
import "github.com/kaspanet/kaspad/app/appmessage"
// GetHeaders sends an RPC request respective to the function's name and returns the RPC server's response
func (c *RPCClient) GetHeaders(startHash string, limit uint64, isAscending bool) (*appmessage.GetHeadersResponseMessage, error) {
err := c.rpcRouter.outgoingRoute().Enqueue(appmessage.NewGetHeadersRequestMessage(startHash, limit, isAscending))
if err != nil {
return nil, err
}
response, err := c.route(appmessage.CmdGetHeadersResponseMessage).DequeueWithTimeout(c.timeout)
if err != nil {
return nil, err
}
getHeadersResponse := response.(*appmessage.GetHeadersResponseMessage)
if getHeadersResponse.Error != nil {
return nil, c.convertRPCError(getHeadersResponse.Error)
}
return getHeadersResponse, nil
}

View File

@ -1,8 +1,9 @@
package integration
import (
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
"testing"
"github.com/kaspanet/kaspad/infrastructure/network/addressmanager"
)
func TestAddressExchange(t *testing.T) {

View File

@ -19,12 +19,12 @@ const HashSize = 32
// TxIDSize of array used to store TxID. See TxID.
const TxIDSize = HashSize
// MaxHashStringSize is the maximum length of a Hash hash string.
const MaxHashStringSize = HashSize * 2
// HashStringSize is the length of a Hash hash string.
const HashStringSize = HashSize * 2
// ErrHashStrSize describes an error that indicates the caller specified a hash
// string that has too many characters.
var ErrHashStrSize = errors.Errorf("max hash string length is %d bytes", MaxHashStringSize)
// string that hasn't the correct number of characters.
var ErrHashStrSize = errors.Errorf("hash string length should be %d bytes", HashStringSize)
// Hash is used in several of the kaspa messages and common structures. It
// typically represents the double sha256 of data.
@ -172,7 +172,7 @@ func NewTxIDFromStr(idStr string) (*TxID, error) {
// destination.
func Decode(dst *Hash, src string) error {
// Return error if hash string is too long.
if len(src) > MaxHashStringSize {
if len(src) != HashStringSize {
return ErrHashStrSize
}

View File

@ -26,7 +26,7 @@ var mainnetGenesisHash = Hash([HashSize]byte{
// TestHash tests the Hash API.
func TestHash(t *testing.T) {
// Hash of block 234439.
blockHashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef"
blockHashStr := "d2f0fb908b59cd20d8687fadca033495d355dccf7718d50e2f9b4826a1f853a8"
blockHash, err := NewHashFromStr(blockHashStr)
if err != nil {
t.Errorf("NewHashFromStr: %v", err)
@ -158,42 +158,25 @@ func TestNewHashFromStr(t *testing.T) {
nil,
},
// Genesis hash with stripped leading zeros.
{
"63bbcfdd699ffd8cb19878564b14d3af8ba4d7ee4d1dd54925a7c21d5b5b5fdc",
mainnetGenesisHash,
nil,
},
// Empty string.
{
"",
Hash{},
nil,
ErrHashStrSize,
},
// Single digit hash.
{
"1",
Hash([HashSize]byte{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
nil,
Hash{},
ErrHashStrSize,
},
// Block 203707 with stripped leading zeros.
{
"3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc",
Hash([HashSize]byte{
0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
}),
nil,
Hash{},
ErrHashStrSize,
},
// Hash string that is too long.
@ -205,7 +188,7 @@ func TestNewHashFromStr(t *testing.T) {
// Hash string that is contains non-hex chars.
{
"abcdefg",
"abcdefgggggggggggggggggggggggggggggggggggggggggggggggggggggggggg",
Hash{},
hex.InvalidByteError('g'),
},