mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-09-15 22:10:12 +00:00
Compare commits
2 Commits
master
...
v0.7.2-rc1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b88e34fd84 | ||
![]() |
689098082f |
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
45
app/appmessage/rpc_get_headers.go
Normal file
45
app/appmessage/rpc_get_headers.go
Normal 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,
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ func NewGetPeerAddressesRequestMessage() *GetPeerAddressesRequestMessage {
|
||||
type GetPeerAddressesResponseMessage struct {
|
||||
baseMessage
|
||||
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,
|
||||
BannedAddresses: bannedAddresses,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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...)
|
||||
|
@ -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) {
|
||||
|
52
app/rpc/rpchandlers/get_headers.go
Normal file
52
app/rpc/rpchandlers/get_headers.go
Normal 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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
infrastructure/network/addressmanager/addressrandomize.go
Normal file
43
infrastructure/network/addressmanager/addressrandomize.go
Normal 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
|
||||
}
|
27
infrastructure/network/addressmanager/config.go
Normal file
27
infrastructure/network/addressmanager/config.go
Normal 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,
|
||||
}
|
||||
}
|
@ -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}
|
||||
}
|
@ -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
|
||||
}
|
@ -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.")
|
||||
}
|
||||
}
|
400
infrastructure/network/addressmanager/localaddressmanager.go
Normal file
400
infrastructure/network/addressmanager/localaddressmanager.go
Normal 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{}
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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{}{}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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) {}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -19,8 +19,13 @@ 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,
|
||||
BannedAddresses: bannedAddresses,
|
||||
Error: err,
|
||||
}, nil
|
||||
}
|
||||
@ -34,8 +39,13 @@ 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,
|
||||
BannedAddresses: bannedAddresses,
|
||||
Error: err,
|
||||
}
|
||||
return nil
|
||||
|
@ -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
|
||||
}
|
||||
|
20
infrastructure/network/rpcclient/rpc_get_headers.go
Normal file
20
infrastructure/network/rpcclient/rpc_get_headers.go
Normal 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
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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'),
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user