Refactor PeerFinder:

* Revise documentation in README.md
* Inject abstract_clock in Manager
* Introduce the Slot object as a replacement for Peer
* New bullet-proof method for slot accounting
* Replace Peer with Slot for tracking connections
* Prevent duplicate outbound connection attempts
* Improved connection and bootstrap business logic
* Refactor PeerImp, PeersImp private interfaces
* Give PeersImp access to the PeerImp interface
* Handle errors retrieving endpoints from asio sockets
* Use weak_ptr to manage PeerImp lifetime
* Better handling of socket closure in PeerImp
* Improve the orderly shutdown logic of PeersImp
This commit is contained in:
Vinnie Falco
2014-02-06 12:23:13 -08:00
parent 12748e7539
commit 3a1a5d12de
35 changed files with 2427 additions and 2455 deletions

View File

@@ -133,7 +133,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\Resolver.cpp"> <ClCompile Include="..\..\src\ripple\peerfinder\impl\SlotImp.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -1144,7 +1144,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\Peer.cpp"> <ClCompile Include="..\..\src\ripple_app\peers\PeerImp.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -2235,22 +2235,20 @@
<ClInclude Include="..\..\src\ripple\peerfinder\api\Config.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\api\Config.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\api\Endpoint.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\api\Endpoint.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\api\Manager.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\api\Manager.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\api\Slot.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\api\Types.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\api\Types.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Bootcache.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Bootcache.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Checker.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Checker.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\FixedPeer.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Fixed.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Giveaways.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Giveaways.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\iosformat.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\iosformat.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Livecache.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Livecache.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Logic.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Logic.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\LogicType.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Peer.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\PrivateTypes.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\PrivateTypes.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Reporting.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Reporting.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Resolver.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Seen.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Seen.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Slots.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Counts.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Sorts.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Sorts.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Source.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Source.h" />
<ClInclude Include="..\..\src\ripple\peerfinder\impl\SourceStrings.h" /> <ClInclude Include="..\..\src\ripple\peerfinder\impl\SourceStrings.h" />

View File

@@ -702,9 +702,6 @@
<ClCompile Include="..\..\src\ripple_app\paths\RippleState.cpp"> <ClCompile Include="..\..\src\ripple_app\paths\RippleState.cpp">
<Filter>[2] Old Ripple\ripple_app\paths</Filter> <Filter>[2] Old Ripple\ripple_app\paths</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\Peer.cpp">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\Peers.cpp"> <ClCompile Include="..\..\src\ripple_app\peers\Peers.cpp">
<Filter>[2] Old Ripple\ripple_app\peers</Filter> <Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClCompile> </ClCompile>
@@ -1098,15 +1095,9 @@
<ClCompile Include="..\..\src\ripple\json\impl\Tests.cpp"> <ClCompile Include="..\..\src\ripple\json\impl\Tests.cpp">
<Filter>[1] Ripple\json\impl</Filter> <Filter>[1] Ripple\json\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\types\impl\JsonPropertyStream.cpp">
<Filter>[1] Ripple\types\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\Checker.cpp"> <ClCompile Include="..\..\src\ripple\peerfinder\impl\Checker.cpp">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\Cache.cpp">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\resource\ripple_resource.cpp"> <ClCompile Include="..\..\src\ripple\resource\ripple_resource.cpp">
<Filter>[1] Ripple\resource</Filter> <Filter>[1] Ripple\resource</Filter>
</ClCompile> </ClCompile>
@@ -1446,9 +1437,6 @@
<ClCompile Include="..\..\src\ripple\peerfinder\impl\Manager.cpp"> <ClCompile Include="..\..\src\ripple\peerfinder\impl\Manager.cpp">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\Resolver.cpp">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\SourceStrings.cpp"> <ClCompile Include="..\..\src\ripple\peerfinder\impl\SourceStrings.cpp">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile> </ClCompile>
@@ -1470,6 +1458,12 @@
<ClCompile Include="..\..\src\ripple\json\impl\JsonPropertyStream.cpp"> <ClCompile Include="..\..\src\ripple\json\impl\JsonPropertyStream.cpp">
<Filter>[1] Ripple\json\impl</Filter> <Filter>[1] Ripple\json\impl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\src\ripple\peerfinder\impl\SlotImp.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple_app\peers\PeerImp.h">
<Filter>[2] Old Ripple\ripple_app\peers</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\..\src\ripple_basics\containers\RangeSet.h"> <ClInclude Include="..\..\src\ripple_basics\containers\RangeSet.h">
@@ -2475,30 +2469,15 @@
<ClInclude Include="..\..\src\ripple\validators\impl\Count.h"> <ClInclude Include="..\..\src\ripple\validators\impl\Count.h">
<Filter>[1] Ripple\validators\impl</Filter> <Filter>[1] Ripple\validators\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\types\api\JsonPropertyStream.h">
<Filter>[1] Ripple\types\api</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Checker.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Checker.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Tuning.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Tuning.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\LegacyEndpoint.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\LegacyEndpointCache.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Cache.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\CachedEndpoint.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\resource\ripple_resource.h"> <ClInclude Include="..\..\src\ripple\resource\ripple_resource.h">
<Filter>[1] Ripple\resource</Filter> <Filter>[1] Ripple\resource</Filter>
</ClInclude> </ClInclude>
@@ -2937,9 +2916,6 @@
<ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\CheckerAdapter.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\FixedPeer.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Giveaways.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Giveaways.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
@@ -2952,27 +2928,15 @@
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Logic.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Logic.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\LogicType.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Peer.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\PrivateTypes.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\PrivateTypes.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Reporting.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Reporting.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Resolver.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Seen.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Seen.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Slots.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Sorts.h"> <ClInclude Include="..\..\src\ripple\peerfinder\impl\Sorts.h">
<Filter>[1] Ripple\peerfinder\impl</Filter> <Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude> </ClInclude>
@@ -3048,6 +3012,15 @@
<ClInclude Include="..\..\src\ripple\json\api\JsonPropertyStream.h"> <ClInclude Include="..\..\src\ripple\json\api\JsonPropertyStream.h">
<Filter>[1] Ripple\json\api</Filter> <Filter>[1] Ripple\json\api</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\api\Slot.h">
<Filter>[1] Ripple\peerfinder\api</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Counts.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\peerfinder\impl\Fixed.h">
<Filter>[1] Ripple\peerfinder\impl</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto"> <CustomBuild Include="..\..\src\ripple_data\protocol\ripple.proto">

View File

@@ -184,12 +184,4 @@
#define RIPPLE_USE_VALIDATORS 0 #define RIPPLE_USE_VALIDATORS 0
#endif #endif
// Turning this on will use the new PeerFinder logic to establish connections
// to other peers. Even with this off, PeerFinder will still send mtENDPOINTS
// messages as needed, and collect legacy IP endpoint information.
//
#ifndef RIPPLE_USE_PEERFINDER
#define RIPPLE_USE_PEERFINDER 0
#endif
#endif #endif

View File

@@ -1,234 +1,366 @@
# PeerFinder # PeerFinder
The PeerFinder module has these responsibilities: ## Introduction
- Maintain a set of addresses suitable for bootstrapping into the overlay. Each _peer_ (a running instance of the **rippled** program) on the Ripple network
- Send and receive protocol messages for peer address discovery. maintains multiple TCP/IP connections to other peers (neighbors) who themselves
- Provide network addresses to other peers that need them. have neighboring peers. The resulting network is called a _peer to peer overlay
- Maintain connections to the configured set of fixed peers. network_, or just [_overlay network_][overlay_network]. Messages passed along these
- Track and manage peer connection slots. connections travel between peers and implement the communication layer of the
_Ripple peer protocol_.
## Description When a peer comes online it needs a set of IP addresses to connect to in order to
gain initial entry into the overlay in a process called _bootstrapping_. Once they
have established an initial set of these outbound peer connections, they need to
gain additional addresses to establish more outbound peer connections until the
desired limit is reached. Furthermore, they need a mechanism to advertise their
IP address to new or existing peers in the overlay so they may receive inbound
connections up to some desired limit. And finally, they need a mechanism to provide
inbound connection requests with an alternate set of IP addresses to try when they
have already reached their desired maximum number of inbound connections.
## Terms PeerFinder is a self contained module that provides these services, along with some
additional overlay network management services such as _fixed slots_ and _cluster
slots_.
<table> ## Features
<tr>
<td>Bootstrap</td>
<td>The process by which a Ripple peer obtains the initial set of
connections into the Ripple payment network overlay.
</td></tr>
</tr>
<tr>
<td>Overlay</td>
<td>The connected graph of Ripple peers, overlaid on the public Internet.
</td>
</tr>
<tr>
<td>Peer</td>
<td>A network server running the **rippled** daemon.
</td>
</tr>
</table>
### Exposition PeerFinder has these responsibilities
(Formerly in Manager.cpp, needs to be reformatted and tidied) * Maintain a persistent set of endpoint addresses suitable for bootstrapping
into the peer to peer overlay, ranked by relative locally observed utility.
PeerFinder * Send and receive protocol messages for discovery of endpoint addresses.
----------
Implements the logic for announcing and discovering IP addresses for * Provide endpoint addresses to new peers that need them.
for connecting into the Ripple network.
Introduction * Maintain connections to a configured set of fixed peers.
------------
Each Peer (a computer running rippled) on the Ripple network requires a certain * Impose limits on the various slots consumed by peer connections.
number of connections to other peers. These connections form an "overlay
network." When a new peer wants to join the network, they need a robust source
of network addresses (IP addresses) in order to establish outgoing connections.
Once they have joined the network, they need a method of announcing their
availaibility of accepting incoming connections.
The Ripple network, like all peer to peer networks, defines a "directed graph" * Initiate outgoing connection attempts to endpoint addresses to maintain the
where each node represents a computer running the rippled software, and each overlay connectivity and fixed peer policies.
vertex indicates a network connection. The direction of the connection tells
us whether it is an outbound or inbound connection (from the perspective of
a particular node).
Fact #1: * Verify the connectivity of neighbors who advertise inbound connection slots.
The total inbound and outbound connections of any overlay must be equal.
This follows that for each node that has an established outbound connection, * Prevent duplicate connections and connections to self.
there must exist another node that has received the corresponding inbound
connection.
When a new peer joins the network it may or may not wish to receive inbound ---
connections. Some peers are unable to accept incoming connections for various.
For security reasons they may be behind a firewall that blocks accept requests.
The administers may decide they don't want the connection traffic. Or they
may wish to connect only to specific peers. Or they may simply be misconfigured.
If a peer decides that it wishes to receive incoming connections, it needs # Concepts
a method to announce its IP address and port number, the features that it
offers (for example, that it also services client requests), and the number
of available connection slots. This is to handle the case where the peer
reaches its desired number of peer connections, but may still want to inform
the network that it will service clients. It may also be desired to indicate
the number of free client slots.
Once a peer is connected to the network we need a way both to inform our ## Manager
neighbors of our status with respect to accepting connections, and also to
learn about new fresh addresses to connect to. For this we will define the
mtENDPOINTS message.
"Bootstrap Strategy" The `Manager` is an application singleton which provides the primary interface
-------------------- to interaction with the PeerFinder.
Fresh endpoints are ones we have seen recently via mtENDPOINTS. ### Autoconnect
These are best to give out to someone who needs additional
connections as quickly as possible, since it is very likely
that the fresh endpoints have open incoming slots.
Reliable endpoints are ones which are highly likely to be The Autoconnect feature of PeerFinder automatically establishes outgoing
connectible over long periods of time. They might not necessarily connections using addresses learned from various sources including the
have an incoming slot, but they are good for bootstrapping when configuration file, the result of domain name lookups, and messages received
there are no peers yet. Typically these are what we would want from the overlay itself.
to store in a database or local config file for a future launch.
Nouns: ### Callback
bootstrap_ip PeerFinder is an isolated code module with few external dependencies. To perform
numeric IPAddress socket specific activities such as establishing outgoing connections or sending
messages to connected peers, the Manager is constructed with an abstract
bootstrap_domain interface called the `Callback`. An instance of this interface performs the
domain name / port combinations, resolution only actual required operations, making PeerFinder independent of the calling code.
bootstrap_url ### Config
URL leading to a text file, with a series of entries.
ripple.txt The `Config` structure defines the operational parameters of the PeerFinder.
Separately parsed entity outside of PeerFinder that can provide Some values come from the configuration file while others are calculated via
bootstrap_ip, bootstrap_domain, and bootstrap_url items. tuned heuristics. The fields are as follows:
The process of obtaining the initial peer connections for accessing the Ripple * `autoConnect`
peer to peer network, when there are no current connections, is called
"bootstrapping." The algorithm is as follows: A flag indicating whether or not the Autoconnect feature is enabled.
1. If ( unusedLiveEndpoints.count() > 0 * `wantIncoming`
OR activeConnectionAttempts.count() > 0)
Try addresses from unusedLiveEndpoints
return;
2. If ( domainNames.count() > 0 AND (
unusedBootstrapIPs.count() == 0
OR activeNameResolutions.count() > 0) )
ForOneOrMore (DomainName that hasn't been resolved recently)
Contact DomainName and add entries to the unusedBootstrapIPs
return;
3. If (unusedBootstrapIPs.count() > 0)
Try addresses from unusedBootstrapIPs
return;
4. Try entries from [ips]
5. Try entries from [ips_urls]
6. Increment generation number and go to 1
- Keep a map of all current outgoing connection attempts A flag indicating whether or not the peer desires inbound connections. When
this flag is turned off, a peer will not advertise itself in Endpoint
messages.
"Connection Strategy" * `listeningPort`
---------------------
This is the overall strategy a peer uses to maintain its position in the Ripple The port number to use when creating the listening socket for peer
network graph connections.
We define these values: * `maxPeers`
peerCount (calculated) The largest number of active peer connections to allow. This includes inbound
The number of currently connected and established peers and outbound connections, but excludes fixed and cluster peers. There is an
implementation defined floor on this value.
outCount (calculated) * `outPeers`
The number of peers in PeerCount that are outbound connections.
MinOutCount (hard-coded constant) The number of automatic outbound connections that PeerFinder will maintain
The minimum number of OutCount we want. This also puts a floor when the Autoconnect feature is enabled. The value is computed with fractional
on PeerCount. This protects against sybil attacks and makes precision as an implementation defined percentage of `maxPeers` subject to
sure that ledgers can get retrieved reliably. an implementation defined floor. An instance of the PeerFinder rounds the
10 is the proposed value. fractional part up or down using a uniform random number generated at
program startup. This allows the outdegree of the overlay network to be
controlled with fractional precision, ensuring that all inbound network
connection slots are not consumed (which would it difficult for new
participants to enter the network).
MaxPeerCount (a constant set in the rippled.cfg) Here's an example of how the network might be structured with a fractional
The maximum number of peer connections, inbound or outbound, value for outPeers:
that a peer wishes to maintain. Setting MaxPeerCount equal to
or below MinOutCount would disallow incoming connections.
OutPercent (a baked-in program constant for now) **(Need example here)**
The peer's target value for OutCount. When the value of OutCount
is below this number, the peer will employ the Outgoing Strategy
to raise its value of OutCount. This value is initially a constant
in the program, defined by the developers. However, it
may be changed through the consensus process.
15% is a proposed value.
However, lets consider the case where OutDesired is exactly equal to MaxPeerCount / 2.
In this case, a stable state will be reached when every peer is full, and
has exactly the same number of inbound and outbound connections. The problem
here is that there are now no available incoming connection slots. No new
peers can enter the network.
Lets consider the case where OutDesired is exactly equal to (MaxPeerCount / 2) - 1.
The stable state for this network (assuming all peers can accept incoming) will
leave us with network degree equal to MaxPeerCount - 2, with all peers having two
available incoming connection slots. The global number of incoming connection slots
will be equal to twice the number of nodes on the network. While this might seem to
be a desirable outcome, note that the connectedness (degree of the overlay) plays
a large part in determining the levels of traffic and ability to receive validations
from desired nodes. Having every node with available incoming connections also
means that entries in pong caches will continually fall out with new values and
information will become less useful.
For this reason, we advise that the value of OutDesired be fractional. Upon startup,
a node will use its node ID (its 160 bit unique ID) to decide whether to round the
value of OutDesired up or down. Using this method, we can precisely control the
global number of available incoming connection slots.
"Outgoing Strategy"
-------------------
This is the method a peer uses to establish outgoing connections into the
Ripple network.
A peer whose PeerCount is zero will use these steps:
1. Attempt addresses from a local database of addresses
2. Attempt addresses from a set of "well known" domains in rippled.cfg
This is the method used by a peer that is already connected to the Ripple network,
to adjust the number of outgoing connections it is maintaining.
### Livecache
"Incoming Strategy" The Livecache holds relayed IP addresses that have been received recently in
------------------------------ the form of Endpoint messages via the peer to peer overlay. A peer periodically
broadcasts the Endpoint message to its neighbors when it has open inbound
connection slots. Peers store these messages in the Livecache and periodically
forward their neighbors a handful of random entries from their Livecache, with
an incremented hop count for each forwarded entry.
This is the method used by a peer to announce its ability and desire to receive The algorithm for sending a neighbor a set of Endpoint messages chooses evenly
incoming connections both for the purpose of obtaining additional peer connections from all available hop counts on each send. This ensures that each peer
and also for receiving requests from clients. will see some entries with the farthest hops at each iteration. The result is
to expand a peer's horizon with respect to which overlay endpoints are visible.
This is designed to force the overlay to become highly connected and reduce
the network diameter with each connection establishment.
Overlay Network When a peer receives an Endpoint message that originates from a neighbor
http://en.wikipedia.org/wiki/Overlay_network (identified by a hop count of zero) for the first time, it performs an incoming
connection test on that neighbor by initiating an outgoing connection to the
remote IP address as seen on the connection combined with the port advertised
in the Endpoint message. If the test fails, then the peer considers its neighbor
firewalled (intentionally or due to misconfiguration) and no longer forwards
Endpoint messages for that peer. This prevents poor quality unconnectible
addresses from landing in the caches. If the incoming connection test passes,
then the peer fills in the Endpoint message with the remote address as seen on
the connection before storing it in its cache and forwarding it to other peers.
This relieves the neighbor from the responsibility of knowing its own IP address
before it can start receiving incoming connections.
Directed Graph Livecache entries expire quickly. Since a peer stops advertising itself when
http://en.wikipedia.org/wiki/Directed_graph it no longer has available inbound slots, its address will shortly after stop
being handed out by other peers. Livecache entries are very likely to result
in both a successful connection establishment and the acquisition of an active
outbound slot. Compare this with Bootcache addresses, which are very likely to
be connectible but unlikely to have an open slot.
References: Because entries in the Livecache are ephemeral, they are not persisted across
launches in the database. The Livecache is continually updated and expired as
Endpoint messages are received from the overlay over time.
Gnutella 0.6 Protocol ### Bootcache
2.2.2 Ping (0x00)
2.2.3 Pong (0x01)
2.2.4 Use of Ping and Pong messages
2.2.4.1 A simple pong caching scheme
2.2.4.2 Other pong caching schemes
http://rfc-gnutella.sourceforge.net/src/rfc-0_6-draft.html
Revised Gnutella Ping Pong Scheme The `Bootcache` stores IP addresses useful for gaining initial connections.
By Christopher Rohrs and Vincent Falco Each address is associated with the following metadata:
http://rfc-gnutella.sourceforge.net/src/pong-caching.html
* **Uptime**
The number of seconds that the address has maintained an active
peer connection, cumulative, without a connection attempt failure.
* **Valence**
A signed integer which represents the number of successful
consecutive connection attempts when positive, and the number of
failed consecutive connection attempts when negative. If an outgoing
connection attempt to the corresponding IP address fails to complete the
handshake, the valence is reset to negative one, and all accrued uptime is
reset to zero. This harsh penalty is intended to prevent popular servers
from forever remaining top ranked in all peer databases.
When choosing addresses from the boot cache for the purpose of
establishing outgoing connections, addresses are ranked in decreasing
order of high uptime, with valence as the tie breaker. The Bootcache is
persistent. Entries are periodically inserted and updated in the corresponding
SQLite database during program operation. When **rippled** is launched, the
existing Bootcache database data is accessed and loaded to accelerate the
bootstrap process.
Desirable entries in the Bootcache are addresses for servers which are known to
have high uptimes, and for which connection attempts usually succeed. However,
these servers do not necessarily have available inbound connection slots.
However, it is assured that these servers will have a well populated Livecache
since they will have moved towards the core of the overlay over their high
uptime. When a connected server is full it will return a handful of new
addresses from its Livecache and gracefully close the connection. Addresses
from the Livecache are highly likely to have inbound connection slots and be
connectible.
For security, all information that contributes to the ranking of Bootcache
entries is observed locally. PeerFinder never trusts external sources of information.
### Slot
Each TCP/IP socket that can participate in the peer to peer overlay occupies
a slot. Slots have properties and state associated with them:
#### State (Slot)
The slot state represents the current stage of the connection as it passes
through the business logic for establishing peer connections.
* `accept`
The accept state is an initial state resulting from accepting an incoming
connection request on a listening socket. The remote IP address and port
are known, and a handshake is expected next.
* `connect`
The connect state is an initial state used when actively establishing outbound
connection attempts. The desired remote IP address and port are known.
* `connected`
When an outbound connection attempt succeeds, it moves to the connected state.
The handshake is initiated but not completed.
* `active`
The state becomes Active when a connection in either the Accepted or Connected
state completes the handshake process, and a slot is available based on the
properties. If no slot is available when the handshake completes, the socket
is gracefully closed.
* `closing`
The Closing state represents a connected socket in the process of being
gracefully closed.
#### Properties (Slot)
Slot properties may be combined and are not mutually exclusive.
* **Inbound**
An inbound slot is the condition of a socket which has accepted an incoming
connection request. A connection which is not inbound is by definition
outbound.
* **Fixed**
A fixed slot is a desired connection to a known peer identified by IP address,
usually entered manually in the configuration file. For the purpose of
establishing outbound connections, the peer also has an associated port number
although only the IP address is checked to determine if the fixed peer is
already connected. Fixed slots do not count towards connection limits.
* **Cluster**
A cluster slot is a connection which has completed the handshake stage, whose
public key matches a known public key usually entered manually in the
configuration file or learned through overlay messages from other trusted
peers. Cluster slots do not count towards connection limits.
* **Superpeer** (2.0)
A superpeer slot is a connection to a peer which can accept incoming
connections, meets certain resource availaibility requirements (such as
bandwidth, CPU, and storage capacity), and operates full duplex in the
overlay. Connections which are not superpeers are by definition leaves. A
leaf slot is a connection to a peer which does not route overlay messages to
other peers, and operates in a partial half duplex fashion in the overlay.
#### Fixed Slots
Fixed slots are identified by IP address and set up during the initialization
of the Manager, usually from the configuration file. The Logic will always make
outgoing connection attempts to each fixed slot which is not currently
connected. If we receive an inbound connection from an endpoint whose address
portion (without port) matches a fixed slot address, we consider the fixed
slot to be connected.
#### Cluster Slots
Cluster slots are identified by the public key and set up during the
initialization of the manager or discovered upon receipt of messages in the
overlay from trusted connections.
--------------------------------------------------------------------------------
# Algorithms
## Connection Strategy
The _Connection Strategy_ applies the configuration settings to establish
desired outbound connections. It runs periodically and progresses through a
series of stages, remaining in each stage until a condition is met
### Stage 1: Fixed Slots
This stage is invoked when the number of active fixed connections is below the
number of fixed connections specified in the configuration, and one of the
following is true:
* There are eligible fixed addresses to try
* Any outbound connection attempts are in progress
Each fixed address is associated with a retry timer. On a fixed connection
failure, the timer is reset so that the address is not tried for some amount
of time, which increases according to a scheduled sequence up to some maximum
which is currently set to approximately one hour between retries. A fixed
address is considered eligible if we are not currently connected or attempting
the address, and its retry timer has expired.
The PeerFinder makes its best effort to become fully connected to the fixed
addresses specified in the configuration file before moving on to establish
outgoing connections to foreign peers. This security feature helps rippled
establish itself with a trusted set of peers first before accepting untrusted
data from the network.
### Stage 2: Livecache
The Livecache is invoked when Stage 1 is not active, autoconnect is enabled,
and the number of active outbound connections is below the number desired. The
stage remains active while:
* The Livecache has addresses to try
* Any outbound connection attempts are in progress
PeerFinder makes its best effort to exhaust addresses in the Livecache before
moving on to the Bootcache, because Livecache addresses are highly likely
to be connectible (since they are known to have been online within the last
minute), and highly likely to have an open slot for an incoming connection
(because peers only advertise themselves in the Livecache when they have
open slots).
### Stage 3: Bootcache
The Bootcache is invoked when Stage 1 and Stage 2 are not active, autoconnect
is enabled, and the number of active outbound connections is below the number
desired. The stage remains active while:
* There are addresses in the cache that have not been tried recently.
Entries in the Bootcache are ranked, with high uptime and highly connectible
addresses preferred over others. Connection attempts to Bootcache addresses
are very likely to succeed but unlikely to produce an active connection since
the peers likely do not have open slots. Before the remote peer closes the
connection it will send a handful of addresses from its Livecache to help the
new peer coming online obtain connections.
--------------------------------------------------------------------------------
# References
Much of the work in PeerFinder was inspired by earlier work in Gnutella:
[Revised Gnutella Ping Pong Scheme](http://rfc-gnutella.sourceforge.net/src/pong-caching.html)<br>
_By Christopher Rohrs and Vincent Falco_
[Gnutella 0.6 Protocol:](http://rfc-gnutella.sourceforge.net/src/rfc-0_6-draft.html) Sections:
* 2.2.2 Ping (0x00)
* 2.2.3 Pong (0x01)
* 2.2.4 Use of Ping and Pong messages
* 2.2.4.1 A simple pong caching scheme
* 2.2.4.2 Other pong caching schemes
[overlay_network]: http://en.wikipedia.org/wiki/Overlay_network

View File

@@ -0,0 +1,127 @@
## Bootstrap Strategy
Fresh endpoints are ones we have seen recently via mtENDPOINTS.
These are best to give out to someone who needs additional
connections as quickly as possible, since it is very likely
that the fresh endpoints have open incoming slots.
Reliable endpoints are ones which are highly likely to be
connectible over long periods of time. They might not necessarily
have an incoming slot, but they are good for bootstrapping when
there are no peers yet. Typically these are what we would want
to store in a database or local config file for a future launch.
Nouns:
bootstrap_ip
numeric IPAddress
bootstrap_domain
domain name / port combinations, resolution only
bootstrap_url
URL leading to a text file, with a series of entries.
ripple.txt
Separately parsed entity outside of PeerFinder that can provide
bootstrap_ip, bootstrap_domain, and bootstrap_url items.
The process of obtaining the initial peer connections for accessing the Ripple
peer to peer network, when there are no current connections, is called
"bootstrapping." The algorithm is as follows:
1. If ( unusedLiveEndpoints.count() > 0
OR activeConnectionAttempts.count() > 0)
Try addresses from unusedLiveEndpoints
return;
2. If ( domainNames.count() > 0 AND (
unusedBootstrapIPs.count() == 0
OR activeNameResolutions.count() > 0) )
ForOneOrMore (DomainName that hasn't been resolved recently)
Contact DomainName and add entries to the unusedBootstrapIPs
return;
3. If (unusedBootstrapIPs.count() > 0)
Try addresses from unusedBootstrapIPs
return;
4. Try entries from [ips]
5. Try entries from [ips_urls]
6. Increment generation number and go to 1
- Keep a map of all current outgoing connection attempts
"Connection Strategy"
---------------------
This is the overall strategy a peer uses to maintain its position in the Ripple
network graph
We define these values:
peerCount (calculated)
The number of currently connected and established peers
outCount (calculated)
The number of peers in PeerCount that are outbound connections.
MinOutCount (hard-coded constant)
The minimum number of OutCount we want. This also puts a floor
on PeerCount. This protects against sybil attacks and makes
sure that ledgers can get retrieved reliably.
10 is the proposed value.
MaxPeerCount (a constant set in the rippled.cfg)
The maximum number of peer connections, inbound or outbound,
that a peer wishes to maintain. Setting MaxPeerCount equal to
or below MinOutCount would disallow incoming connections.
OutPercent (a baked-in program constant for now)
The peer's target value for OutCount. When the value of OutCount
is below this number, the peer will employ the Outgoing Strategy
to raise its value of OutCount. This value is initially a constant
in the program, defined by the developers. However, it
may be changed through the consensus process.
15% is a proposed value.
However, lets consider the case where OutDesired is exactly equal to MaxPeerCount / 2.
In this case, a stable state will be reached when every peer is full, and
has exactly the same number of inbound and outbound connections. The problem
here is that there are now no available incoming connection slots. No new
peers can enter the network.
Lets consider the case where OutDesired is exactly equal to (MaxPeerCount / 2) - 1.
The stable state for this network (assuming all peers can accept incoming) will
leave us with network degree equal to MaxPeerCount - 2, with all peers having two
available incoming connection slots. The global number of incoming connection slots
will be equal to twice the number of nodes on the network. While this might seem to
be a desirable outcome, note that the connectedness (degree of the overlay) plays
a large part in determining the levels of traffic and ability to receive validations
from desired nodes. Having every node with available incoming connections also
means that entries in pong caches will continually fall out with new values and
information will become less useful.
For this reason, we advise that the value of OutDesired be fractional. Upon startup,
a node will use its node ID (its 160 bit unique ID) to decide whether to round the
value of OutDesired up or down. Using this method, we can precisely control the
global number of available incoming connection slots.
"Outgoing Strategy"
-------------------
This is the method a peer uses to establish outgoing connections into the
Ripple network.
A peer whose PeerCount is zero will use these steps:
1. Attempt addresses from a local database of addresses
2. Attempt addresses from a set of "well known" domains in rippled.cfg
This is the method used by a peer that is already connected to the Ripple network,
to adjust the number of outgoing connections it is maintaining.
"Incoming Strategy"
------------------------------
This is the method used by a peer to announce its ability and desire to receive
incoming connections both for the purpose of obtaining additional peer connections
and also for receiving requests from clients.

View File

@@ -31,21 +31,18 @@ namespace PeerFinder {
struct Callback struct Callback
{ {
/** Initiate outgoing Peer connections to the specified set of endpoints. */ /** Initiate outgoing Peer connections to the specified set of endpoints. */
virtual void connectPeers (IPAddresses const& addresses) = 0; virtual void connect (IPAddresses const& addresses) = 0;
/** Activate the handshaked peer with the specified address. */
virtual void activate (Slot::ptr const& slot) = 0;
/** Sends a set of Endpoint records to the specified peer. */
virtual void send (Slot::ptr const& slot, Endpoints const& endpoints) = 0;
/** Disconnect the handshaked peer with the specified address. /** Disconnect the handshaked peer with the specified address.
@param graceful `true` to wait for send buffers to drain before closing. @param graceful `true` to wait for send buffers to drain before closing.
*/ */
virtual void disconnectPeer ( virtual void disconnect (Slot::ptr const& slot, bool graceful) = 0;
IPAddress const& remote_address, bool graceful) = 0;
/** Activate the handshaked peer with the specified address. */
virtual void activatePeer (
IPAddress const& remote_address) = 0;
/** Sends a set of Endpoint records to the specified peer. */
virtual void sendEndpoints (IPAddress const& remote_address,
Endpoints const& endpoints) = 0;
}; };
} }

View File

@@ -20,6 +20,9 @@
#ifndef RIPPLE_PEERFINDER_MANAGER_H_INCLUDED #ifndef RIPPLE_PEERFINDER_MANAGER_H_INCLUDED
#define RIPPLE_PEERFINDER_MANAGER_H_INCLUDED #define RIPPLE_PEERFINDER_MANAGER_H_INCLUDED
#include "Slot.h"
#include "Types.h"
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
@@ -37,6 +40,7 @@ public:
Stoppable& parent, Stoppable& parent,
SiteFiles::Manager& siteFiles, SiteFiles::Manager& siteFiles,
Callback& callback, Callback& callback,
clock_type& clock,
Journal journal); Journal journal);
/** Destroy the object. /** Destroy the object.
@@ -70,45 +74,52 @@ public:
/** Add a URL as a fallback location to obtain IPAddress sources. /** Add a URL as a fallback location to obtain IPAddress sources.
@param name A label used for diagnostics. @param name A label used for diagnostics.
*/ */
/* VFALCO NOTE Unimplemented
virtual void addFallbackURL (std::string const& name, virtual void addFallbackURL (std::string const& name,
std::string const& url) = 0; std::string const& url) = 0;
*/
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** Called when a peer connection is accepted. */ /** Create a new inbound slot with the specified remote endpoint.
virtual void onPeerAccept (IPAddress const& local_address, If nullptr is returned, then the slot could not be assigned.
IPAddress const& remote_address) = 0; Usually this is because of a detected self-connection.
/** Called when an outgoing peer connection is attempted. */
virtual void onPeerConnect (IPAddress const& address) = 0;
/** Called when an outgoing peer connection attempt succeeds. */
virtual void onPeerConnected (IPAddress const& local_address,
IPAddress const& remote_address) = 0;
/** Called when the real public address is discovered.
Currently this happens when we receive a PROXY handshake. The
protocol HELLO message will happen after the PROXY handshake.
*/ */
virtual void onPeerAddressChanged ( virtual Slot::ptr new_inbound_slot (
IPAddress const& currentAddress, IPAddress const& newAddress) = 0; IP::Endpoint const& local_endpoint,
IP::Endpoint const& remote_endpoint) = 0;
/** Called when a peer connection finishes the protocol handshake. /** Create a new outbound slot with the specified remote endpoint.
@param id The node public key of the peer. If nullptr is returned, then the slot could not be assigned.
@param inCluster The peer is a member of our cluster. Usually this is because of a duplicate connection.
*/ */
virtual void onPeerHandshake ( virtual Slot::ptr new_outbound_slot (
IPAddress const& address, PeerID const& id, bool inCluster) = 0; IP::Endpoint const& remote_endpoint) = 0;
/** Always called when the socket closes. */ /** Called when an outbound connection attempt succeeds.
virtual void onPeerClosed (IPAddress const& address) = 0; The local endpoint must be valid. If the caller receives an error
when retrieving the local endpoint from the socket, it should
proceed as if the connection attempt failed by calling on_closed
instead of on_connected.
*/
virtual void on_connected (Slot::ptr const& slot,
IP::Endpoint const& local_endpoint) = 0;
/** Called when a handshake is completed. */
virtual void on_handshake (Slot::ptr const& slot,
RipplePublicKey const& key, bool cluster) = 0;
/** Called when mtENDPOINTS is received. */ /** Called when mtENDPOINTS is received. */
virtual void onPeerEndpoints (IPAddress const& address, virtual void on_endpoints (Slot::ptr const& slot,
Endpoints const& endpoints) = 0; Endpoints const& endpoints) = 0;
/** Called when legacy IP/port addresses are received. */ /** Called when legacy IP/port addresses are received. */
virtual void onLegacyEndpoints (IPAddresses const& addresses) = 0; virtual void on_legacy_endpoints (IPAddresses const& addresses) = 0;
/** Called when the slot is closed.
This always happens when the socket is closed.
*/
virtual void on_closed (Slot::ptr const& slot) = 0;
}; };
} }

View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SLOT_H_INCLUDED
#define RIPPLE_PEERFINDER_SLOT_H_INCLUDED
#include "../../beast/beast/net/IPEndpoint.h"
#include <boost/optional.hpp>
#include <memory>
namespace ripple {
namespace PeerFinder {
/** Properties and state associated with a peer to peer overlay connection. */
class Slot
{
public:
typedef std::shared_ptr <Slot> ptr;
enum State
{
accept,
connect,
connected,
active,
closing
};
virtual ~Slot () = 0;
/** Returns `true` if this is an inbound connection. */
virtual bool inbound () const = 0;
/** Returns `true` if this is a fixed connection.
A connection is fixed if its remote endpoint is in the list of
remote endpoints for fixed connections.
*/
virtual bool fixed () const = 0;
/** Returns `true` if this is a cluster connection.
This is only known after then handshake completes.
*/
virtual bool cluster () const = 0;
/** Returns the state of the connection. */
virtual State state () const = 0;
/** The remote endpoint of socket. */
virtual IP::Endpoint const& remote_endpoint () const = 0;
/** The local endpoint of the socket, when known. */
virtual boost::optional <IP::Endpoint> const& local_endpoint () const = 0;
/** The peer's public key, when known.
The public key is established when the handshake is complete.
*/
virtual boost::optional <RipplePublicKey> const& public_key () const = 0;
};
}
}
#endif

View File

@@ -20,18 +20,19 @@
#ifndef RIPPLE_PEERFINDER_TYPES_H_INCLUDED #ifndef RIPPLE_PEERFINDER_TYPES_H_INCLUDED
#define RIPPLE_PEERFINDER_TYPES_H_INCLUDED #define RIPPLE_PEERFINDER_TYPES_H_INCLUDED
#include "beast/beast/chrono/abstract_clock.h"
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
/** Used to identify peers. */
typedef RipplePublicKey PeerID;
/** Represents a set of addresses. */ /** Represents a set of addresses. */
typedef std::vector <IPAddress> IPAddresses; typedef std::vector <IPAddress> IPAddresses;
/** A set of Endpoint used for connecting. */ /** A set of Endpoint used for connecting. */
typedef std::vector <Endpoint> Endpoints; typedef std::vector <Endpoint> Endpoints;
typedef beast::abstract_clock <std::chrono::seconds> clock_type;
} }
} }

View File

@@ -24,9 +24,25 @@ namespace ripple {
namespace PeerFinder { namespace PeerFinder {
/** Stores IP addresses useful for gaining initial connections. /** Stores IP addresses useful for gaining initial connections.
Ideal bootstrap addresses have the following attributes:
- High uptime This is one of the caches that is consulted when additional outgoing
- Many successful connect attempts connections are needed. Along with the address, each entry has this
additional metadata:
Uptime
The number of seconds that the address has maintained an active
peer connection, cumulative, without a connection attempt failure.
Valence
A signed integer which represents the number of successful
consecutive connection attempts when positive, and the number of
failed consecutive connection attempts when negative.
When choosing addresses from the boot cache for the purpose of
establishing outgoing connections, addresses are ranked in decreasing
order of high uptime, with valence as the tie breaker.
*/ */
class Bootcache class Bootcache
{ {
@@ -41,7 +57,8 @@ public:
{ {
} }
Endpoint (IPAddress const& address, int uptime, int valence) Endpoint (IPAddress const& address,
std::chrono::seconds uptime, int valence)
: m_address (address) : m_address (address)
, m_uptime (uptime) , m_uptime (uptime)
, m_valence (valence) , m_valence (valence)
@@ -53,7 +70,7 @@ public:
return m_address; return m_address;
} }
int uptime () const std::chrono::seconds uptime () const
{ {
return m_uptime; return m_uptime;
} }
@@ -65,7 +82,7 @@ public:
private: private:
IPAddress m_address; IPAddress m_address;
int m_uptime; std::chrono::seconds m_uptime;
int m_valence; int m_valence;
}; };
@@ -77,33 +94,33 @@ public:
struct Entry struct Entry
{ {
Entry () Entry ()
: cumulativeUptimeSeconds (0) : cumulativeUptime (0)
, sessionUptimeSeconds (0) , sessionUptime (0)
, connectionValence (0) , connectionValence (0)
, active (false) , active (false)
{ {
} }
/** Update the uptime measurement based on the time. */ /** Update the uptime measurement based on the time. */
void update (DiscreteTime const now) void update (clock_type::time_point const& now)
{ {
// Must be active! // Must be active!
consistency_check (active); assert (active);
// Clock must be monotonically increasing // Clock must be monotonically increasing
consistency_check (now >= whenActive); assert (now >= whenActive);
// Remove the uptime we added earlier in the // Remove the uptime we added earlier in the
// session and add back in the new uptime measurement. // session and add back in the new uptime measurement.
DiscreteTime const uptimeSeconds (now - whenActive); auto const uptime (now - whenActive);
cumulativeUptimeSeconds -= sessionUptimeSeconds; cumulativeUptime -= sessionUptime;
cumulativeUptimeSeconds += uptimeSeconds; cumulativeUptime += uptime;
sessionUptimeSeconds = uptimeSeconds; sessionUptime = uptime;
} }
/** Our cumulative uptime with this address with no failures. */ /** Our cumulative uptime with this address with no failures. */
int cumulativeUptimeSeconds; std::chrono::seconds cumulativeUptime;
/** Amount of uptime from the current session (if any). */ /** Amount of uptime from the current session (if any). */
int sessionUptimeSeconds; std::chrono::seconds sessionUptime;
/** Number of consecutive connection successes or failures. /** Number of consecutive connection successes or failures.
If the number is positive, indicates the number of If the number is positive, indicates the number of
@@ -117,7 +134,7 @@ public:
bool active; bool active;
/** Time when the peer became active. */ /** Time when the peer became active. */
DiscreteTime whenActive; clock_type::time_point whenActive;
}; };
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -139,14 +156,14 @@ public:
Entry const& lhs (lhs_iter->second); Entry const& lhs (lhs_iter->second);
Entry const& rhs (rhs_iter->second); Entry const& rhs (rhs_iter->second);
// Higher cumulative uptime always wins // Higher cumulative uptime always wins
if (lhs.cumulativeUptimeSeconds > rhs.cumulativeUptimeSeconds) if (lhs.cumulativeUptime > rhs.cumulativeUptime)
return true; return true;
else if (lhs.cumulativeUptimeSeconds <= rhs.cumulativeUptimeSeconds else if (lhs.cumulativeUptime <= rhs.cumulativeUptime
&& rhs.cumulativeUptimeSeconds != 0) && rhs.cumulativeUptime.count() != 0)
return false; return false;
// At this point both uptimes will be zero // At this point both uptimes will be zero
consistency_check (lhs.cumulativeUptimeSeconds == 0 && consistency_check (lhs.cumulativeUptime.count() == 0 &&
rhs.cumulativeUptimeSeconds == 0); rhs.cumulativeUptime.count() == 0);
if (lhs.connectionValence > rhs.connectionValence) if (lhs.connectionValence > rhs.connectionValence)
return true; return true;
return false; return false;
@@ -160,24 +177,24 @@ public:
typedef std::vector <Entries::iterator> SortedEntries; typedef std::vector <Entries::iterator> SortedEntries;
Store& m_store; Store& m_store;
DiscreteClock <DiscreteTime> m_clock; clock_type& m_clock;
Journal m_journal; Journal m_journal;
Entries m_entries; Entries m_entries;
// Time after which we can update the database again // Time after which we can update the database again
DiscreteTime m_whenUpdate; clock_type::time_point m_whenUpdate;
// Set to true when a database update is needed // Set to true when a database update is needed
bool m_needsUpdate; bool m_needsUpdate;
Bootcache ( Bootcache (
Store& store, Store& store,
DiscreteClock <DiscreteTime> clock, clock_type& clock,
Journal journal) Journal journal)
: m_store (store) : m_store (store)
, m_clock (clock) , m_clock (clock)
, m_journal (journal) , m_journal (journal)
, m_whenUpdate (clock()) , m_whenUpdate (m_clock.now ())
{ {
} }
@@ -206,7 +223,7 @@ public:
{ {
++count; ++count;
Entry& entry (result.first->second); Entry& entry (result.first->second);
entry.cumulativeUptimeSeconds = iter->cumulativeUptimeSeconds; entry.cumulativeUptime = iter->cumulativeUptime;
entry.connectionValence = iter->connectionValence; entry.connectionValence = iter->connectionValence;
} }
else else
@@ -254,7 +271,7 @@ public:
for (Entries::const_iterator iter (m_entries.begin ()); for (Entries::const_iterator iter (m_entries.begin ());
iter != m_entries.end (); ++iter) iter != m_entries.end (); ++iter)
result.emplace_back (iter->first, result.emplace_back (iter->first,
iter->second.cumulativeUptimeSeconds, iter->second.cumulativeUptime,
iter->second.connectionValence); iter->second.connectionValence);
return result; return result;
} }
@@ -295,8 +312,8 @@ public:
// with resetting uptime to prevent the entire network // with resetting uptime to prevent the entire network
// from settling on just a handful of addresses. // from settling on just a handful of addresses.
// //
entry.cumulativeUptimeSeconds = 0; entry.cumulativeUptime = std::chrono::seconds (0);
entry.sessionUptimeSeconds = 0 ; entry.sessionUptime = std::chrono::seconds (0);
// Increment the number of consecutive failures. // Increment the number of consecutive failures.
if (entry.connectionValence > 0) if (entry.connectionValence > 0)
entry.connectionValence = 0; entry.connectionValence = 0;
@@ -320,7 +337,7 @@ public:
// Can't already be active! // Can't already be active!
consistency_check (! entry.active); consistency_check (! entry.active);
// Reset session uptime // Reset session uptime
entry.sessionUptimeSeconds = 0; entry.sessionUptime = std::chrono::seconds (0);
// Count this as a connection success // Count this as a connection success
if (entry.connectionValence < 0) if (entry.connectionValence < 0)
entry.connectionValence = 0; entry.connectionValence = 0;
@@ -329,7 +346,7 @@ public:
if (action == doActivate) if (action == doActivate)
{ {
entry.active = true; entry.active = true;
entry.whenActive = m_clock(); entry.whenActive = m_clock.now();
} }
else else
{ {
@@ -359,17 +376,20 @@ public:
// Must exist! // Must exist!
consistency_check (! result.second); consistency_check (! result.second);
Entry& entry (result.first->second); Entry& entry (result.first->second);
entry.update (m_clock()); entry.update (m_clock.now());
flagForUpdate(); flagForUpdate();
} }
template <typename Seconds> template <class Rep, class Period>
static std::string uptime_phrase (Seconds seconds) static std::string uptime_phrase (
std::chrono::duration <Rep, Period> const& elapsed)
{ {
if (seconds > 0) if (elapsed.count() > 0)
return std::string (" with ") + {
RelativeTime (seconds).to_string() + std::stringstream ss;
" uptime"; ss << " with " << elapsed << " uptime";
return ss.str();
}
return std::string (); return std::string ();
} }
/** Called when an active outbound connection closes. */ /** Called when an active outbound connection closes. */
@@ -383,9 +403,9 @@ public:
consistency_check (entry.active); consistency_check (entry.active);
if (m_journal.trace) m_journal.trace << leftw (18) << if (m_journal.trace) m_journal.trace << leftw (18) <<
"Bootcache close " << address << "Bootcache close " << address <<
uptime_phrase (entry.cumulativeUptimeSeconds); uptime_phrase (entry.cumulativeUptime);
entry.update (m_clock()); entry.update (m_clock.now());
entry.sessionUptimeSeconds = 0; entry.sessionUptime = std::chrono::seconds (0);
entry.active = false; entry.active = false;
flagForUpdate(); flagForUpdate();
} }
@@ -420,7 +440,7 @@ public:
{ {
ss << std::endl << ss << std::endl <<
(*iter)->first << ", " << (*iter)->first << ", " <<
RelativeTime ((*iter)->second.cumulativeUptimeSeconds) << ", " (*iter)->second.cumulativeUptime << ", "
<< valenceString ((*iter)->second.connectionValence); << valenceString ((*iter)->second.connectionValence);
if ((*iter)->second.active) if ((*iter)->second.active)
ss << ss <<
@@ -429,9 +449,6 @@ public:
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
//
//--------------------------------------------------------------------------
private: private:
// Returns a vector of entry iterators sorted by descending score // Returns a vector of entry iterators sorted by descending score
std::vector <Entries::const_iterator> csort () const std::vector <Entries::const_iterator> csort () const
@@ -480,7 +497,7 @@ private:
continue; continue;
if (m_journal.trace) m_journal.trace << leftw (18) << if (m_journal.trace) m_journal.trace << leftw (18) <<
"Bootcache pruned" << (*iter)->first << "Bootcache pruned" << (*iter)->first <<
uptime_phrase (entry.cumulativeUptimeSeconds) << uptime_phrase (entry.cumulativeUptime) <<
" and valence " << entry.connectionValence; " and valence " << entry.connectionValence;
m_entries.erase (*iter); m_entries.erase (*iter);
--count; --count;
@@ -504,20 +521,20 @@ private:
{ {
Store::SavedBootstrapAddress entry; Store::SavedBootstrapAddress entry;
entry.address = iter->first; entry.address = iter->first;
entry.cumulativeUptimeSeconds = iter->second.cumulativeUptimeSeconds; entry.cumulativeUptime = iter->second.cumulativeUptime;
entry.connectionValence = iter->second.connectionValence; entry.connectionValence = iter->second.connectionValence;
list.push_back (entry); list.push_back (entry);
} }
m_store.updateBootstrapCache (list); m_store.updateBootstrapCache (list);
// Reset the flag and cooldown timer // Reset the flag and cooldown timer
m_needsUpdate = false; m_needsUpdate = false;
m_whenUpdate = m_clock() + Tuning::bootcacheCooldownSeconds; m_whenUpdate = m_clock.now() + Tuning::bootcacheCooldownTime;
} }
// Checks the clock and calls update if we are off the cooldown. // Checks the clock and calls update if we are off the cooldown.
void checkUpdate () void checkUpdate ()
{ {
if (m_needsUpdate && m_whenUpdate < m_clock()) if (m_needsUpdate && m_whenUpdate < m_clock.now())
update (); update ();
} }

View File

@@ -0,0 +1,366 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_COUNTS_H_INCLUDED
#define RIPPLE_PEERFINDER_COUNTS_H_INCLUDED
#include "../api/Slot.h"
namespace ripple {
namespace PeerFinder {
/** Manages the count of available connections for the various slots. */
class Counts
{
public:
explicit Counts (clock_type& clock)
: m_clock (clock)
, m_attempts (0)
, m_active (0)
, m_in_max (0)
, m_in_active (0)
, m_out_max (0)
, m_out_active (0)
, m_fixed (0)
, m_fixed_active (0)
, m_cluster (0)
, m_acceptCount (0)
, m_closingCount (0)
{
#if 0
std::random_device rd;
std::mt19937 gen (rd());
m_roundingThreshold =
std::generate_canonical <double, 10> (gen);
#else
m_roundingThreshold = Random::getSystemRandom().nextDouble();
#endif
}
//--------------------------------------------------------------------------
/** Adds the slot state and properties to the slot counts. */
void add (Slot const& s)
{
adjust (s, 1);
}
/** Removes the slot state and properties from the slot counts. */
void remove (Slot const& s)
{
adjust (s, -1);
}
/** Returns `true` if the slot can become active. */
bool can_activate (Slot const& s) const
{
// Must be handshaked and in the right state
assert (s.state() == Slot::connected || s.state() == Slot::accept);
if (s.fixed () || s.cluster ())
return true;
if (s.inbound ())
return m_in_active < m_in_max;
return m_out_active < m_out_max;
}
/** Returns the number of attempts needed to bring us to the max. */
std::size_t attempts_needed () const
{
if (m_attempts >= Tuning::maxConnectAttempts)
return 0;
return Tuning::maxConnectAttempts - m_attempts;
}
/** Returns the number of outbound connection attempts. */
std::size_t attempts () const
{
return m_attempts;
};
/** Returns the total number of outbound slots. */
int out_max () const
{
return m_out_max;
}
/** Returns the number of outbound peers assigned an open slot.
Fixed peers do not count towards outbound slots used.
*/
int out_active () const
{
return m_out_active;
}
/** Returns the number of fixed connections. */
std::size_t fixed () const
{
return m_fixed;
}
/** Returns the number of active fixed connections. */
std::size_t fixed_active () const
{
return m_fixed_active;
}
//--------------------------------------------------------------------------
/** Called when the config is set or changed. */
void onConfig (Config const& config)
{
// Calculate the number of outbound peers we want. If we dont want or can't
// accept incoming, this will simply be equal to maxPeers. Otherwise
// we calculate a fractional amount based on percentages and pseudo-randomly
// round up or down.
//
if (config.wantIncoming)
{
// Round outPeers upwards using a Bernoulli distribution
m_out_max = std::floor (config.outPeers);
if (m_roundingThreshold < (config.outPeers - m_out_max))
++m_out_max;
}
else
{
m_out_max = config.maxPeers;
}
// Calculate the largest number of inbound connections we could take.
if (config.maxPeers >= m_out_max)
m_in_max = config.maxPeers - m_out_max;
else
m_in_max = 0;
}
/** Returns the number of accepted connections that haven't handshaked. */
int acceptCount() const
{
return m_acceptCount;
}
/** Returns the number of connection attempts currently active. */
int connectCount() const
{
return m_attempts;
}
/** Returns the number of connections that are gracefully closing. */
int closingCount () const
{
return m_closingCount;
}
/** Returns the total number of inbound slots. */
int inboundSlots () const
{
return m_in_max;
}
/** Returns the number of inbound peers assigned an open slot. */
int inboundActive () const
{
return m_in_active;
}
/** Returns the total number of active peers excluding fixed peers. */
int totalActive () const
{
return m_in_active + m_out_active;
}
/** Returns the number of unused inbound slots.
Fixed peers do not deduct from inbound slots or count towards totals.
*/
int inboundSlotsFree () const
{
if (m_in_active < m_in_max)
return m_in_max - m_in_active;
return 0;
}
/** Returns the number of unused outbound slots.
Fixed peers do not deduct from outbound slots or count towards totals.
*/
int outboundSlotsFree () const
{
if (m_out_active < m_out_max)
return m_out_max - m_out_active;
return 0;
}
//--------------------------------------------------------------------------
/** Returns the number of new connection attempts we should make. */
int additionalAttemptsNeeded () const
{
// Don't go over the maximum concurrent attempt limit
if (m_attempts >= Tuning::maxConnectAttempts)
return 0;
int needed (outboundSlotsFree ());
// This is the most we could attempt right now
int const available (
Tuning::maxConnectAttempts - m_attempts);
//return std::min (needed, available);
return available;
}
/** Returns true if the slot logic considers us "connected" to the network. */
bool isConnectedToNetwork () const
{
// We will consider ourselves connected if we have reached
// the number of outgoing connections desired, or if connect
// automatically is false.
//
// Fixed peers do not count towards the active outgoing total.
if (m_out_max > 0)
return false;
return true;
}
/** Output statistics. */
void onWrite (PropertyStream::Map& map)
{
map ["accept"] = acceptCount ();
map ["connect"] = connectCount ();
map ["close"] = closingCount ();
map ["in"] << m_in_active << "/" << m_in_max;
map ["out"] << m_out_active << "/" << m_out_max;
map ["fixed"] = m_fixed_active;
map ["cluster"] = m_cluster;
map ["total"] = m_active;
}
/** Records the state for diagnostics. */
std::string state_string () const
{
std::stringstream ss;
ss <<
m_out_active << "/" << m_out_max << " out, " <<
m_in_active << "/" << m_in_max << " in, " <<
connectCount() << " connecting, " <<
closingCount() << " closing"
;
return ss.str();
}
//--------------------------------------------------------------------------
private:
// Adjusts counts based on the specified slot, in the direction indicated.
void adjust (Slot const& s, int const n)
{
if (s.fixed ())
m_fixed += n;
if (s.cluster ())
m_cluster += n;
switch (s.state ())
{
case Slot::accept:
assert (s.inbound ());
m_acceptCount += n;
break;
case Slot::connect:
case Slot::connected:
assert (! s.inbound ());
m_attempts += n;
break;
case Slot::active:
if (s.fixed ())
m_fixed_active += n;
if (! s.fixed () && ! s.cluster ())
{
if (s.inbound ())
m_in_active += n;
else
m_out_active += n;
}
m_active += n;
break;
case Slot::closing:
m_closingCount += n;
break;
default:
assert (false);
break;
};
}
private:
clock_type& m_clock;
/** Outbound connection attempts. */
int m_attempts;
/** Active connections, including fixed and cluster. */
std::size_t m_active;
/** Total number of inbound slots. */
std::size_t m_in_max;
/** Number of inbound slots assigned to active peers. */
std::size_t m_in_active;
/** Maximum desired outbound slots. */
std::size_t m_out_max;
/** Active outbound slots. */
std::size_t m_out_active;
/** Fixed connections. */
std::size_t m_fixed;
/** Active fixed connections. */
std::size_t m_fixed_active;
/** Cluster connections. */
std::size_t m_cluster;
// Number of inbound connections that are
// not active or gracefully closing.
int m_acceptCount;
// Number of connections that are gracefully closing.
int m_closingCount;
/** Fractional threshold below which we round down.
This is used to round the value of Config::outPeers up or down in
such a way that the network-wide average number of outgoing
connections approximates the recommended, fractional value.
*/
double m_roundingThreshold;
};
}
}
#endif

View File

@@ -17,38 +17,51 @@
*/ */
//============================================================================== //==============================================================================
#ifndef RIPPLE_PEERFINDER_LOGICTYPE_H_INCLUDED #ifndef RIPPLE_PEERFINDER_FIXED_H_INCLUDED
#define RIPPLE_PEERFINDER_LOGICTYPE_H_INCLUDED #define RIPPLE_PEERFINDER_FIXED_H_INCLUDED
#include "Tuning.h"
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
template <class DiscreteClockSourceType> /** Metadata for a Fixed slot. */
class LogicType class Fixed
: private BaseFromMember <DiscreteClockSourceType>
, public Logic
{ {
public: public:
typedef typename DiscreteClockSourceType::DiscreteClockType DiscreteClockType; explicit Fixed (clock_type& clock)
: m_when (clock.now ())
LogicType ( , m_failures (0)
Callback& callback,
Store& store,
Checker& checker,
Journal journal)
: Logic (
BaseFromMember <DiscreteClockSourceType>::member(),
callback,
store,
checker,
journal)
{ {
} }
DiscreteClockSourceType& get_clock() Fixed (Fixed const&) = default;
/** Returns the time after which we shoud allow a connection attempt. */
clock_type::time_point const& when () const
{ {
return BaseFromMember <DiscreteClockSourceType>::member(); return m_when;
} }
/** Updates metadata to reflect a failed connection. */
void failure (clock_type::time_point const& now)
{
m_failures = std::min (m_failures + 1,
Tuning::connectionBackoff.size() - 1);
m_when = now + std::chrono::minutes (
Tuning::connectionBackoff [m_failures]);
}
/** Updates metadata to reflect a successful connection. */
void success (clock_type::time_point const& now)
{
m_failures = 0;
m_when = now;
}
private:
clock_type::time_point m_when;
std::size_t m_failures;
}; };
} }

View File

@@ -1,79 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_FIXEDPEER_H_INCLUDED
#define RIPPLE_PEERFINDER_FIXEDPEER_H_INCLUDED
namespace ripple {
namespace PeerFinder {
/** Stores information about a fixed peer.
A fixed peer is defined in the config file and can be specified using
either an IP address or a hostname (which may resolve to zero or more
addresses).
A fixed peer which has multiple IP addresses is considered connected
if there is a connection to any one of its addresses.
*/
class FixedPeer
{
public:
/* The config name */
std::string const m_name;
/* The corresponding IP address(es) */
IPAddresses m_addresses;
FixedPeer (std::string const& name,
IPAddresses const& addresses)
: m_name (name)
, m_addresses (addresses)
{
bassert (!m_addresses.empty ());
// NIKB TODO add support for multiple IPs
m_addresses.resize (1);
}
// NIKB TODO support peers which resolve to more than a single address
IPAddress getAddress () const
{
if (m_addresses.size ())
return m_addresses.at(0);
return IPAddress ();
}
template <typename Comparator>
bool hasAddress (IPAddress const& address, Comparator compare) const
{
for (IPAddresses::const_iterator iter = m_addresses.cbegin();
iter != m_addresses.cend(); ++iter)
{
if (compare (*iter, address))
return true;
}
return false;
}
};
}
}
#endif

View File

@@ -23,7 +23,7 @@ namespace PeerFinder {
class LivecacheTests : public UnitTest class LivecacheTests : public UnitTest
{ {
public: public:
ManualClock m_clock_source; manual_clock <clock_type::duration> m_clock;
// Add the address as an endpoint // Add the address as an endpoint
void add (uint32 index, uint16 port, Livecache& c) void add (uint32 index, uint16 port, Livecache& c)
@@ -39,7 +39,7 @@ public:
{ {
beginTestCase ("fetch"); beginTestCase ("fetch");
Livecache c (m_clock_source, Journal()); Livecache c (m_clock, Journal());
add (1, 1, c); add (1, 1, c);
add (2, 1, c); add (2, 1, c);

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED #ifndef RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED
#define RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED #define RIPPLE_PEERFINDER_LIVECACHE_H_INCLUDED
#include <unordered_map>
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
@@ -44,20 +46,21 @@ public:
struct Entry : public EntryList::Node struct Entry : public EntryList::Node
{ {
Entry (Endpoint const& endpoint_, DiscreteTime whenExpires_) Entry (Endpoint const& endpoint_,
clock_type::time_point const& whenExpires_)
: endpoint (endpoint_) : endpoint (endpoint_)
, whenExpires (whenExpires_) , whenExpires (whenExpires_)
{ {
} }
Endpoint endpoint; Endpoint endpoint;
DiscreteTime whenExpires; clock_type::time_point whenExpires;
}; };
typedef std::set <Endpoint, LessEndpoints> SortedTable; typedef std::set <Endpoint, LessEndpoints> SortedTable;
typedef boost::unordered_map <IPAddress, Entry> AddressTable; typedef std::unordered_map <IPAddress, Entry> AddressTable;
DiscreteClock <DiscreteTime> m_clock; clock_type& m_clock;
Journal m_journal; Journal m_journal;
AddressTable m_byAddress; AddressTable m_byAddress;
SortedTable m_bySorted; SortedTable m_bySorted;
@@ -68,8 +71,8 @@ public:
public: public:
/** Create the cache. */ /** Create the cache. */
explicit Livecache ( Livecache (
DiscreteClock <DiscreteTime> clock, clock_type& clock,
Journal journal) Journal journal)
: m_clock (clock) : m_clock (clock)
, m_journal (journal) , m_journal (journal)
@@ -91,7 +94,7 @@ public:
/** Erase entries whose time has expired. */ /** Erase entries whose time has expired. */
void sweep () void sweep ()
{ {
DiscreteTime const now (m_clock()); auto const now (m_clock.now ());
AddressTable::size_type count (0); AddressTable::size_type count (0);
for (EntryList::iterator iter (m_list.begin()); for (EntryList::iterator iter (m_list.begin());
iter != m_list.end();) iter != m_list.end();)
@@ -124,13 +127,12 @@ public:
{ {
// Caller is responsible for validation // Caller is responsible for validation
check_precondition (endpoint.hops <= Tuning::maxHops); check_precondition (endpoint.hops <= Tuning::maxHops);
DiscreteTime const now (m_clock()); auto now (m_clock.now ());
DiscreteTime const whenExpires ( auto const whenExpires (now + Tuning::liveCacheSecondsToLive);
now + Tuning::liveCacheSecondsToLive);
std::pair <AddressTable::iterator, bool> result ( std::pair <AddressTable::iterator, bool> result (
m_byAddress.emplace (boost::unordered::piecewise_construct, m_byAddress.emplace (std::piecewise_construct,
boost::make_tuple (endpoint.address), std::make_tuple (endpoint.address),
boost::make_tuple (endpoint, whenExpires))); std::make_tuple (endpoint, whenExpires)));
Entry& entry (result.first->second); Entry& entry (result.first->second);
// Drop duplicates at higher hops // Drop duplicates at higher hops
if (! result.second && (endpoint.hops > entry.endpoint.hops)) if (! result.second && (endpoint.hops > entry.endpoint.hops))

File diff suppressed because it is too large Load Diff

View File

@@ -30,11 +30,12 @@ class ManagerImp
public: public:
ServiceQueue m_queue; ServiceQueue m_queue;
SiteFiles::Manager& m_siteFiles; SiteFiles::Manager& m_siteFiles;
clock_type& m_clock;
Journal m_journal; Journal m_journal;
StoreSqdb m_store; StoreSqdb m_store;
SerializedContext m_context; SerializedContext m_context;
CheckerAdapter m_checker; CheckerAdapter m_checker;
LogicType <SimpleMonotonicClock> m_logic; Logic m_logic;
DeadlineTimer m_connectTimer; DeadlineTimer m_connectTimer;
DeadlineTimer m_messageTimer; DeadlineTimer m_messageTimer;
DeadlineTimer m_cacheTimer; DeadlineTimer m_cacheTimer;
@@ -45,14 +46,16 @@ public:
Stoppable& stoppable, Stoppable& stoppable,
SiteFiles::Manager& siteFiles, SiteFiles::Manager& siteFiles,
Callback& callback, Callback& callback,
clock_type& clock,
Journal journal) Journal journal)
: Manager (stoppable) : Manager (stoppable)
, Thread ("PeerFinder") , Thread ("PeerFinder")
, m_siteFiles (siteFiles) , m_siteFiles (siteFiles)
, m_clock (clock)
, m_journal (journal) , m_journal (journal)
, m_store (journal) , m_store (journal)
, m_checker (m_context, m_queue) , m_checker (m_context, m_queue)
, m_logic (callback, m_store, m_checker, journal) , m_logic (clock, callback, m_store, m_checker, journal)
, m_connectTimer (this) , m_connectTimer (this)
, m_messageTimer (this) , m_messageTimer (this)
, m_cacheTimer (this) , m_cacheTimer (this)
@@ -103,72 +106,48 @@ public:
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
void onPeerAccept (IPAddress const& local_address, Slot::ptr new_inbound_slot (
IPAddress const& remote_address) IP::Endpoint const& local_endpoint,
IP::Endpoint const& remote_endpoint)
{ {
m_queue.dispatch ( return m_logic.new_inbound_slot (local_endpoint, remote_endpoint);
m_context.wrap (
bind (&Logic::onPeerAccept, &m_logic,
local_address, remote_address)));
} }
void onPeerConnect (IPAddress const& address) Slot::ptr new_outbound_slot (IP::Endpoint const& remote_endpoint)
{ {
m_queue.dispatch ( return m_logic.new_outbound_slot (remote_endpoint);
m_context.wrap (
bind (&Logic::onPeerConnect, &m_logic,
address)));
} }
void onPeerConnected (IPAddress const& local_address, void on_connected (Slot::ptr const& slot,
IPAddress const& remote_address) IP::Endpoint const& local_endpoint)
{ {
m_queue.dispatch ( SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
m_context.wrap ( m_logic.on_connected (impl, local_endpoint);
bind (&Logic::onPeerConnected, &m_logic,
local_address, remote_address)));
} }
void onPeerAddressChanged ( void on_handshake (Slot::ptr const& slot,
IPAddress const& currentAddress, IPAddress const& newAddress) RipplePublicKey const& key, bool cluster)
{ {
m_queue.dispatch ( SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
m_context.wrap ( m_logic.on_handshake (impl, key, cluster);
bind (&Logic::onPeerAddressChanged, &m_logic,
currentAddress, newAddress)));
} }
void onPeerHandshake (IPAddress const& address, PeerID const& id, void on_endpoints (Slot::ptr const& slot,
bool cluster)
{
m_queue.dispatch (
m_context.wrap (
bind (&Logic::onPeerHandshake, &m_logic,
address, id, cluster)));
}
void onPeerClosed (IPAddress const& address)
{
m_queue.dispatch (
m_context.wrap (
bind (&Logic::onPeerClosed, &m_logic,
address)));
}
void onPeerEndpoints (IPAddress const& address,
Endpoints const& endpoints) Endpoints const& endpoints)
{ {
m_queue.dispatch ( SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
beast::bind (&Logic::onPeerEndpoints, &m_logic, m_logic.on_endpoints (impl, endpoints);
address, endpoints));
} }
void onLegacyEndpoints (IPAddresses const& addresses) void on_legacy_endpoints (IPAddresses const& addresses)
{ {
m_queue.dispatch ( m_logic.on_legacy_endpoints (addresses);
m_context.wrap ( }
beast::bind (&Logic::onLegacyEndpoints, &m_logic,
addresses))); void on_closed (Slot::ptr const& slot)
{
SlotImp::ptr impl (std::dynamic_pointer_cast <SlotImp> (slot));
m_logic.on_closed (impl);
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -281,7 +260,7 @@ public:
{ {
m_queue.dispatch ( m_queue.dispatch (
m_context.wrap ( m_context.wrap (
bind (&Logic::sendEndpoints, &m_logic))); bind (&Logic::broadcast, &m_logic)));
m_messageTimer.setExpiration (Tuning::secondsPerMessage); m_messageTimer.setExpiration (Tuning::secondsPerMessage);
} }
@@ -359,9 +338,10 @@ Manager* Manager::New (
Stoppable& parent, Stoppable& parent,
SiteFiles::Manager& siteFiles, SiteFiles::Manager& siteFiles,
Callback& callback, Callback& callback,
clock_type& clock,
Journal journal) Journal journal)
{ {
return new ManagerImp (parent, siteFiles, callback, journal); return new ManagerImp (parent, siteFiles, callback, clock, journal);
} }
} }

View File

@@ -23,9 +23,6 @@
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
/** Time in seconds since some baseline event in the past. */
typedef int DiscreteTime;
/** Indicates the action the logic will take after a handshake. */ /** Indicates the action the logic will take after a handshake. */
enum HandshakeAction enum HandshakeAction
{ {

View File

@@ -1,185 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#if 0
namespace ripple {
namespace PeerFinder {
class ResolverImp
: public Resolver
, private Thread
, private LeakChecked <ResolverImp>
{
private:
class Request;
struct State
{
List <Request> list;
};
typedef SharedData <State> SharedState;
SharedState m_state;
boost::asio::io_service m_io_service;
boost::optional <boost::asio::io_service::work> m_work;
//--------------------------------------------------------------------------
static boost::asio::ip::tcp::endpoint fromIPAddress (
IPAddress const& ipEndpoint)
{
if (ipEndpoint.is_v4 ())
{
return boost::asio::ip::tcp::endpoint (
boost::asio::ip::address_v4 (
ipEndpoint.to_v4().value),
ipEndpoint.port ());
}
bassertfalse;
return boost::asio::ip::tcp::endpoint ();
}
//--------------------------------------------------------------------------
class Request
: public SharedObject
, public List <Request>::Node
, private LeakChecked <Request>
{
public:
typedef SharedPtr <Request> Ptr;
typedef boost::asio::ip::tcp Protocol;
typedef boost::system::error_code error_code;
typedef Protocol::socket socket_type;
typedef Protocol::endpoint endpoint_type;
ResolverImp& m_owner;
boost::asio::io_service& m_io_service;
IPAddress m_address;
AbstractHandler <void (Result)> m_handler;
socket_type m_socket;
boost::system::error_code m_error;
bool m_canAccept;
Request (ResolverImp& owner, boost::asio::io_service& io_service,
IPAddress const& address, AbstractHandler <void (Result)> handler)
: m_owner (owner)
, m_io_service (io_service)
, m_address (address)
, m_handler (handler)
, m_socket (m_io_service)
, m_canAccept (false)
{
m_owner.add (*this);
m_socket.async_connect (fromIPAddress (m_address),
wrapHandler (boost::bind (&Request::handle_connect, Ptr(this),
boost::asio::placeholders::error), m_handler));
}
~Request ()
{
Result result;
result.address = m_address;
result.error = m_error;
m_io_service.wrap (m_handler) (result);
m_owner.remove (*this);
}
void cancel ()
{
m_socket.cancel();
}
void handle_connect (boost::system::error_code ec)
{
m_error = ec;
if (ec)
return;
m_canAccept = true;
}
};
//--------------------------------------------------------------------------
void add (Request& request)
{
SharedState::Access state (m_state);
state->list.push_back (request);
}
void remove (Request& request)
{
SharedState::Access state (m_state);
state->list.erase (state->list.iterator_to (request));
}
void run ()
{
m_io_service.run ();
}
public:
ResolverImp ()
: Thread ("PeerFinder::Resolver")
, m_work (boost::in_place (boost::ref (m_io_service)))
{
startThread ();
}
~ResolverImp ()
{
// cancel pending i/o
cancel();
// destroy the io_service::work object
m_work = boost::none;
// signal and wait for the thread to exit gracefully
stopThread ();
}
void cancel ()
{
SharedState::Access state (m_state);
for (List <Request>::iterator iter (state->list.begin());
iter != state->list.end(); ++iter)
iter->cancel();
}
void async_test (IPAddress const& endpoint,
AbstractHandler <void (Result)> handler)
{
new Request (*this, m_io_service, endpoint, handler);
}
};
//------------------------------------------------------------------------------
Resolver* Resolver::New ()
{
return new ResolverImp;
}
}
}
#endif

View File

@@ -1,88 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_RESOLVER_H_INCLUDED
#define RIPPLE_PEERFINDER_RESOLVER_H_INCLUDED
namespace ripple {
namespace PeerFinder {
/** Performs asynchronous domain name resolution. */
class Resolver
{
public:
/** Create the service.
This will automatically start the associated thread and io_service.
*/
static Resolver* New ();
/** Destroy the service.
Any pending I/O operations will be canceled. This call blocks until
all pending operations complete (either with success or with
operation_aborted) and the associated thread and io_service have
no more work remaining.
*/
virtual ~Resolver () { }
/** Cancel pending I/O.
This issues cancel orders for all pending I/O operations and then
returns immediately. Handlers will receive operation_aborted errors,
or if they were already queued they will complete normally.
*/
virtual void cancel () = 0;
struct Result
{
Result ()
{ }
/** The original name string */
std::string name;
/** The error code from the operation. */
boost::system::error_code error;
/** The resolved address.
Only defined if there is no error.
If the original name string contains a port specification,
it will be set in the resolved IPAddress.
*/
IPAddress address;
};
/** Performs an async resolution on the specified name.
The port information, if present, will be passed through.
*/
template <typename Handler>
void async_resolve (std::string const& name,
BEAST_MOVE_ARG(Handler) handler)
{
async_resolve (name,
AbstractHandler <void (Result)> (
BEAST_MOVE_CAST(Handler)(handler)));
}
virtual void async_resolve (std::string const& name,
AbstractHandler <void (Result)> handler) = 0;
};
}
}
#endif

View File

@@ -17,147 +17,148 @@
*/ */
//============================================================================== //==============================================================================
#ifndef RIPPLE_PEERFINDER_PEER_H_INCLUDED #ifndef RIPPLE_PEERFINDER_SLOTIMP_H_INCLUDED
#define RIPPLE_PEERFINDER_PEER_H_INCLUDED #define RIPPLE_PEERFINDER_SLOTIMP_H_INCLUDED
#include "../api/Slot.h"
#include <boost/optional.hpp>
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
/** Metadata for an open peer socket. */ class SlotImp : public Slot
class Peer
{ {
public: public:
enum State typedef std::shared_ptr <SlotImp> ptr;
{
/** Accepted inbound connection, no handshake. */
stateAccept,
/** Outbound connection attempt. */ // inbound
stateConnect, SlotImp (IP::Endpoint const& local_endpoint,
IP::Endpoint const& remote_endpoint, bool fixed)
/** Outbound connection, no handshake. */ : m_inbound (true)
stateConnected,
/** Active peer (handshake completed). */
stateActive,
/** Graceful close in progress. */
stateClosing
};
Peer (IPAddress const& remote_address, bool inbound, bool fixed)
: m_inbound (inbound)
, m_remote_address (remote_address)
, m_state (inbound ? stateAccept : stateConnect)
, m_fixed (fixed) , m_fixed (fixed)
, m_cluster (false) , m_cluster (false)
, checked (inbound ? false : true) , m_state (accept)
, canAccept (inbound ? false : true) , m_remote_endpoint (remote_endpoint)
, m_local_endpoint (local_endpoint)
, checked (false)
, canAccept (false)
, connectivityCheckInProgress (false) , connectivityCheckInProgress (false)
{ {
} }
/** Returns the local address on the socket if known. */ // outbound
IPAddress const& local_address () const SlotImp (IP::Endpoint const& remote_endpoint, bool fixed)
: m_inbound (false)
, m_fixed (fixed)
, m_cluster (false)
, m_state (connect)
, m_remote_endpoint (remote_endpoint)
, checked (true)
, canAccept (true)
, connectivityCheckInProgress (false)
{ {
return m_local_address;
} }
/** Sets the local address on the socket. */ ~SlotImp ()
void local_address (IPAddress const& address)
{ {
consistency_check (is_unspecified (m_local_address));
m_local_address = address;
} }
/** Returns the remote address on the socket. */
IPAddress const& remote_address () const
{
return m_remote_address;
}
/** Returns `true` if this is an inbound connection. */
bool inbound () const bool inbound () const
{ {
return m_inbound; return m_inbound;
} }
/** Returns `true` if this is an outbound connection. */
bool outbound () const
{
return ! m_inbound;
}
/** Marks a connection as belonging to a fixed peer. */
void fixed (bool fix)
{
m_fixed = fix;
}
/** Marks `true` if this is a connection belonging to a fixed peer. */
bool fixed () const bool fixed () const
{ {
return m_fixed; return m_fixed;
} }
void cluster (bool cluster)
{
m_cluster = cluster;
}
bool cluster () const bool cluster () const
{ {
return m_cluster; return m_cluster;
} }
State state() const State state () const
{ {
return m_state; return m_state;
} }
void state (State s) IP::Endpoint const& remote_endpoint () const
{ {
m_state = s; return m_remote_endpoint;
} }
PeerID const& id () const boost::optional <IP::Endpoint> const& local_endpoint () const
{ {
return m_id; return m_local_endpoint;
} }
void activate (PeerID const& id, DiscreteTime now) boost::optional <RipplePublicKey> const& public_key () const
{ {
m_state = stateActive; return m_public_key;
m_id = id; }
//--------------------------------------------------------------------------
void state (State state_)
{
// Must go through activate() to set active state
assert (state_ != active);
// The state must be different
assert (state_ != m_state);
// You can't transition into the initial states
assert (state_ != accept && state_ != connect);
// Can only become connected from outbound connect state
assert (state_ != connected || (! m_inbound && m_state == connect));
// Can't gracefully close on an outbound connection attempt
assert (state_ != closing || m_state != connect);
m_state = state_;
}
void activate (clock_type::time_point const& now)
{
// Can only become active from the accept or connected state
assert (m_state == accept || m_state == connected);
m_state = active;
whenSendEndpoints = now; whenSendEndpoints = now;
whenAcceptEndpoints = now; whenAcceptEndpoints = now;
} }
void local_endpoint (IP::Endpoint const& endpoint)
{
m_local_endpoint = endpoint;
}
void remote_endpoint (IP::Endpoint const& endpoint)
{
m_remote_endpoint = endpoint;
}
void public_key (RipplePublicKey const& key)
{
m_public_key = key;
}
void cluster (bool cluster_)
{
m_cluster = cluster_;
}
private: private:
// `true` if the connection is incoming
bool const m_inbound; bool const m_inbound;
bool const m_fixed;
// The local address on the socket, when it is known.
IPAddress m_local_address;
// The remote address on the socket.
IPAddress m_remote_address;
// Current state of this connection
State m_state;
// The public key. Valid after a handshake.
PeerID m_id;
// Set to indicate that this is a fixed peer.
bool m_fixed;
// Set to indicate that this is a peer that belongs in our cluster
// and does not consume a slot. Valid after a handshake.
bool m_cluster; bool m_cluster;
State m_state;
//-------------------------------------------------------------------------- IP::Endpoint m_remote_endpoint;
boost::optional <IP::Endpoint> m_local_endpoint;
boost::optional <RipplePublicKey> m_public_key;
public: public:
// DEPRECATED public data members // DEPRECATED public data members
@@ -175,13 +176,13 @@ public:
bool connectivityCheckInProgress; bool connectivityCheckInProgress;
// The time after which we will send the peer mtENDPOINTS // The time after which we will send the peer mtENDPOINTS
DiscreteTime whenSendEndpoints; clock_type::time_point whenSendEndpoints;
// The time after which we will accept mtENDPOINTS from the peer // The time after which we will accept mtENDPOINTS from the peer
// This is to prevent flooding or spamming. Receipt of mtENDPOINTS // This is to prevent flooding or spamming. Receipt of mtENDPOINTS
// sooner than the allotted time should impose a load charge. // sooner than the allotted time should impose a load charge.
// //
DiscreteTime whenAcceptEndpoints; clock_type::time_point whenAcceptEndpoints;
// The set of all recent IPAddress that we have seen from this peer. // The set of all recent IPAddress that we have seen from this peer.
// We try to avoid sending a peer the same addresses they gave us. // We try to avoid sending a peer the same addresses they gave us.
@@ -189,6 +190,12 @@ public:
//std::set <IPAddress> received; //std::set <IPAddress> received;
}; };
//------------------------------------------------------------------------------
Slot::~Slot ()
{
}
} }
} }

View File

@@ -1,463 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_PEERFINDER_SLOTS_H_INCLUDED
#define RIPPLE_PEERFINDER_SLOTS_H_INCLUDED
namespace ripple {
namespace PeerFinder {
class Slots
{
public:
explicit Slots (DiscreteClock <DiscreteTime> clock)
: m_clock (clock)
, m_inboundSlots (0)
, m_inboundActive (0)
, m_outboundSlots (0)
, m_outboundActive (0)
, m_fixedPeerConnections (0)
, m_clusterPeerConnections (0)
, m_acceptCount (0)
, m_connectCount (0)
, m_closingCount (0)
{
#if 0
std::random_device rd;
std::mt19937 gen (rd());
m_roundingThreshold =
std::generate_canonical <double, 10> (gen);
#else
m_roundingThreshold = Random::getSystemRandom().nextDouble();
#endif
}
/** Called when the config is set or changed. */
void onConfig (Config const& config)
{
// Calculate the number of outbound peers we want. If we dont want or can't
// accept incoming, this will simply be equal to maxPeers. Otherwise
// we calculate a fractional amount based on percentages and pseudo-randomly
// round up or down.
//
if (config.wantIncoming)
{
// Round outPeers upwards using a Bernoulli distribution
m_outboundSlots = std::floor (config.outPeers);
if (m_roundingThreshold < (config.outPeers - m_outboundSlots))
++m_outboundSlots;
}
else
{
m_outboundSlots = config.maxPeers;
}
// Calculate the largest number of inbound connections we could take.
if (config.maxPeers >= m_outboundSlots)
m_inboundSlots = config.maxPeers - m_outboundSlots;
else
m_inboundSlots = 0;
}
/** Returns the number of accepted connections that haven't handshaked. */
int acceptCount() const
{
return m_acceptCount;
}
/** Returns the number of connection attempts currently active. */
int connectCount() const
{
return m_connectCount;
}
/** Returns the number of connections that are gracefully closing. */
int closingCount () const
{
return m_closingCount;
}
/** Returns the total number of inbound slots. */
int inboundSlots () const
{
return m_inboundSlots;
}
/** Returns the total number of outbound slots. */
int outboundSlots () const
{
return m_outboundSlots;
}
/** Returns the number of inbound peers assigned an open slot. */
int inboundActive () const
{
return m_inboundActive;
}
/** Returns the number of outbound peers assigned an open slot.
Fixed peers do not count towards outbound slots used.
*/
int outboundActive () const
{
return m_outboundActive;
}
/** Returns the total number of active peers excluding fixed peers. */
int totalActive () const
{
return m_inboundActive + m_outboundActive;
}
/** Returns the number of unused inbound slots.
Fixed peers do not deduct from inbound slots or count towards totals.
*/
int inboundSlotsFree () const
{
if (m_inboundActive < m_inboundSlots)
return m_inboundSlots - m_inboundActive;
return 0;
}
/** Returns the number of unused outbound slots.
Fixed peers do not deduct from outbound slots or count towards totals.
*/
int outboundSlotsFree () const
{
if (m_outboundActive < m_outboundSlots)
return m_outboundSlots - m_outboundActive;
return 0;
}
/** Returns the number of fixed peers we have connections to
Fixed peers do not deduct from outbound or inbound slots or count
towards totals.
*/
int fixedPeers () const
{
return m_fixedPeerConnections;
}
/** Returns the number of cluster peers we have connections to
Cluster nodes do not deduct from outbound or inbound slots or
count towards totals, but they are tracked if they are also
configured as fixed peers.
*/
int clusterPeers () const
{
return m_clusterPeerConnections;
}
//--------------------------------------------------------------------------
/** Called when an inbound connection is accepted. */
void onPeerAccept ()
{
++m_acceptCount;
}
/** Called when a new outbound connection is attempted. */
void onPeerConnect ()
{
++m_connectCount;
}
/** Determines if an outbound slot is available and assigns it */
HandshakeAction grabOutboundSlot(bool self, bool fixed,
bool available, bool cluster)
{
// If this is a connection to ourselves, we bail.
if (self)
{
++m_closingCount;
return doClose;
}
// Fixed and cluster peers are tracked but are not subject
// to limits and don't consume slots. They are always allowed
// to connect.
if (fixed || cluster)
{
if (fixed)
++m_fixedPeerConnections;
if (cluster)
++m_clusterPeerConnections;
return doActivate;
}
// If we don't have any slots for this peer then reject the
// connection.
if (!available)
{
++m_closingCount;
return doClose;
}
++m_outboundActive;
return doActivate;
}
/** Determines if an inbound slot is available and assigns it */
HandshakeAction grabInboundSlot(bool self, bool fixed,
bool available, bool cluster)
{
// If this is a connection to ourselves, we bail.
if (self)
{
++m_closingCount;
return doClose;
}
// Fixed and cluster peers are tracked but are not subject
// to limits and don't consume slots. They are always allowed
// to connect.
if (fixed || cluster)
{
if (fixed)
++m_fixedPeerConnections;
if (cluster)
++m_clusterPeerConnections;
return doActivate;
}
// If we don't have any slots for this peer then reject the
// connection and redirect them.
if (!available)
{
++m_closingCount;
return doRedirect;
}
++m_inboundActive;
return doActivate;
}
/** Called when a peer handshakes.
Returns the disposition for this peer, including whether we should
activate the connection, issue a redirect or simply close it.
*/
HandshakeAction onPeerHandshake (bool inbound, bool self, bool fixed, bool cluster)
{
if (cluster)
return doActivate;
if (inbound)
{
// Must not be zero!
consistency_check (m_acceptCount > 0);
--m_acceptCount;
return grabInboundSlot (self, fixed,
inboundSlotsFree () > 0, cluster);
}
// Must not be zero!
consistency_check (m_connectCount > 0);
--m_connectCount;
return grabOutboundSlot (self, fixed,
outboundSlotsFree () > 0, cluster);
}
/** Called when a peer socket is closed gracefully. */
void onPeerGracefulClose ()
{
// Must not be zero!
consistency_check (m_closingCount > 0);
--m_closingCount;
}
/** Called when a peer socket is closed.
A value of `true` for active means the peer was assigned an open slot.
*/
void onPeerClosed (bool inbound, bool active, bool fixed, bool cluster)
{
if (active)
{
if (inbound)
{
// Fixed peer connections are tracked but don't count towards slots
if (fixed || cluster)
{
if (fixed)
{
consistency_check (m_fixedPeerConnections > 0);
--m_fixedPeerConnections;
}
if (cluster)
{
consistency_check (m_clusterPeerConnections > 0);
--m_clusterPeerConnections;
}
}
else
{
// Must not be zero!
consistency_check (m_inboundActive > 0);
--m_inboundActive;
}
}
else
{
// Fixed peer connections are tracked but don't count towards slots
if (fixed || cluster)
{
if (fixed)
{
consistency_check (m_fixedPeerConnections > 0);
--m_fixedPeerConnections;
}
if (cluster)
{
consistency_check (m_clusterPeerConnections > 0);
--m_clusterPeerConnections;
}
}
else
{
// Must not be zero!
consistency_check (m_outboundActive > 0);
--m_outboundActive;
}
}
}
else if (inbound)
{
// Must not be zero!
consistency_check (m_acceptCount > 0);
--m_acceptCount;
}
else
{
// Must not be zero!
consistency_check (m_connectCount > 0);
--m_connectCount;
}
}
//--------------------------------------------------------------------------
/** Returns the number of new connection attempts we should make. */
int additionalAttemptsNeeded () const
{
// Don't go over the maximum concurrent attempt limit
if (m_connectCount >= Tuning::maxConnectAttempts)
return 0;
int needed (outboundSlotsFree ());
// This is the most we could attempt right now
int const available (
Tuning::maxConnectAttempts - m_connectCount);
return std::min (needed, available);
}
/** Returns true if the slot logic considers us "connected" to the network. */
bool isConnectedToNetwork () const
{
// We will consider ourselves connected if we have reached
// the number of outgoing connections desired, or if connect
// automatically is false.
//
// Fixed peers do not count towards the active outgoing total.
if (m_outboundSlots > 0)
return false;
return true;
}
/** Output statistics. */
void onWrite (PropertyStream::Map& map)
{
map ["accept"] = acceptCount();
map ["connect"] = connectCount();
map ["close"] = closingCount();
map ["in"] << inboundActive() << "/" << inboundSlots();
map ["out"] << outboundActive() << "/" << outboundSlots();
map ["fixed"] = fixedPeers();
}
/** Records the state for diagnostics. */
std::string state_string () const
{
std::stringstream ss;
ss <<
outboundActive() << "/" << outboundSlots() << " out, " <<
inboundActive() << "/" << inboundSlots() << " in, " <<
connectCount() << " connecting, " <<
closingCount() << " closing"
;
return ss.str();
}
//--------------------------------------------------------------------------
private:
DiscreteClock <DiscreteTime> m_clock;
/** Total number of inbound slots. */
int m_inboundSlots;
/** Number of inbound slots assigned to active peers. */
int m_inboundActive;
/** Total number of outbound slots. */
int m_outboundSlots;
/** Number of outbound slots assigned to active peers. */
int m_outboundActive;
/** Number of fixed peer connections that we have. */
int m_fixedPeerConnections;
/** Number of cluster peer connections that we have. */
int m_clusterPeerConnections;
// Number of inbound connections that are
// not active or gracefully closing.
int m_acceptCount;
// Number of outgoing connections that are
// not active or gracefully closing.
//
int m_connectCount;
// Number of connections that are gracefully closing.
int m_closingCount;
// Number of connections that are currently assigned an open slot
//int m_activeCount;
/** Fractional threshold below which we round down.
This is used to round the value of Config::outPeers up or down in
such a way that the network-wide average number of outgoing
connections approximates the recommended, fractional value.
*/
double m_roundingThreshold;
};
}
}
#endif

View File

@@ -32,7 +32,7 @@ public:
struct SavedBootstrapAddress struct SavedBootstrapAddress
{ {
IPAddress address; IPAddress address;
int cumulativeUptimeSeconds; std::chrono::seconds cumulativeUptime;
int connectionValence; int connectionValence;
}; };

View File

@@ -91,7 +91,7 @@ public:
{ {
std::string s; std::string s;
int uptimeSeconds; std::chrono::seconds::rep uptimeSeconds;
int connectionValence; int connectionValence;
sqdb::statement st = (m_session.prepare << sqdb::statement st = (m_session.prepare <<
@@ -115,7 +115,7 @@ public:
if (! is_unspecified (entry.address)) if (! is_unspecified (entry.address))
{ {
entry.cumulativeUptimeSeconds = uptimeSeconds; entry.cumulativeUptime = std::chrono::seconds (uptimeSeconds);
entry.connectionValence = connectionValence; entry.connectionValence = connectionValence;
list.push_back (entry); list.push_back (entry);
@@ -153,7 +153,7 @@ public:
if (! error) if (! error)
{ {
std::string s; std::string s;
int uptimeSeconds; std::chrono::seconds::rep uptimeSeconds;
int connectionValence; int connectionValence;
sqdb::statement st = (m_session.prepare << sqdb::statement st = (m_session.prepare <<
@@ -173,7 +173,7 @@ public:
list.begin()); !error && iter != list.end(); ++iter) list.begin()); !error && iter != list.end(); ++iter)
{ {
s = to_string (iter->address); s = to_string (iter->address);
uptimeSeconds = iter->cumulativeUptimeSeconds; uptimeSeconds = iter->cumulativeUptime.count ();
connectionValence = iter->connectionValence; connectionValence = iter->connectionValence;
st.execute_and_fetch (error); st.execute_and_fetch (error);

View File

@@ -20,6 +20,8 @@
#ifndef RIPPLE_PEERFINDER_TUNING_H_INCLUDED #ifndef RIPPLE_PEERFINDER_TUNING_H_INCLUDED
#define RIPPLE_PEERFINDER_TUNING_H_INCLUDED #define RIPPLE_PEERFINDER_TUNING_H_INCLUDED
#include <array>
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
@@ -39,7 +41,7 @@ enum
secondsPerConnect = 10 secondsPerConnect = 10
/** Maximum number of simultaneous connection attempts. */ /** Maximum number of simultaneous connection attempts. */
,maxConnectAttempts = 5 ,maxConnectAttempts = 20
/** The percentage of total peer slots that are outbound. /** The percentage of total peer slots that are outbound.
The number of outbound peers will be the larger of the The number of outbound peers will be the larger of the
@@ -59,33 +61,57 @@ enum
//--------------------------------------------------------- //---------------------------------------------------------
// //
// Bootcache // LegacyEndpoints
// //
//--------------------------------------------------------- //---------------------------------------------------------
// How many legacy endpoints to keep in our cache
,legacyEndpointCacheSize = 1000
// How many cache mutations between each database update
,legacyEndpointMutationsPerUpdate = 50
};
//------------------------------------------------------------------------------
//
// Fixed
//
//------------------------------------------------------------------------------
static std::array <int, 10> const connectionBackoff
{{ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 }};
//------------------------------------------------------------------------------
//
// Bootcache
//
//------------------------------------------------------------------------------
enum
{
// Threshold of cache entries above which we trim. // Threshold of cache entries above which we trim.
,bootcacheSize = 1000 bootcacheSize = 1000
// The percentage of addresses we prune when we trim the cache. // The percentage of addresses we prune when we trim the cache.
,bootcachePrunePercent = 10 ,bootcachePrunePercent = 10
};
// The cool down wait between database updates // The cool down wait between database updates
// Ideally this should be larger than the time it takes a full // Ideally this should be larger than the time it takes a full
// peer to send us a set of addresses and then disconnect. // peer to send us a set of addresses and then disconnect.
// //
,bootcacheCooldownSeconds = 60 static std::chrono::seconds const bootcacheCooldownTime (60);
//--------------------------------------------------------- //------------------------------------------------------------------------------
// //
// Livecache // Livecache
// //
//--------------------------------------------------------- //------------------------------------------------------------------------------
enum
{
// Drop incoming messages with hops greater than this number // Drop incoming messages with hops greater than this number
,maxHops = 10 maxHops = 10
// How often we send or accept mtENDPOINTS messages per peer
,secondsPerMessage = 5
// How many Endpoint to send in each mtENDPOINTS // How many Endpoint to send in each mtENDPOINTS
,numberOfEndpoints = 10 ,numberOfEndpoints = 10
@@ -93,10 +119,6 @@ enum
// The most Endpoint we will accept in mtENDPOINTS // The most Endpoint we will accept in mtENDPOINTS
,numberOfEndpointsMax = 20 ,numberOfEndpointsMax = 20
// How long an Endpoint will stay in the cache
// This should be a small multiple of the broadcast frequency
,liveCacheSecondsToLive = 60
// The maximum number of hops that we allow. Peers farther // The maximum number of hops that we allow. Peers farther
// away than this are dropped. // away than this are dropped.
,maxPeerHopCount = 10 ,maxPeerHopCount = 10
@@ -107,22 +129,16 @@ enum
/** Number of addresses we provide when redirecting. */ /** Number of addresses we provide when redirecting. */
,redirectEndpointCount = 10 ,redirectEndpointCount = 10
//---------------------------------------------------------
//
// LegacyEndpoints
//
//---------------------------------------------------------
// How many legacy endpoints to keep in our cache
,legacyEndpointCacheSize = 1000
// How many cache mutations between each database update
,legacyEndpointMutationsPerUpdate = 50
}; };
/** @} */
// How often we send or accept mtENDPOINTS messages per peer
static std::chrono::seconds const secondsPerMessage (5);
// How long an Endpoint will stay in the cache
// This should be a small multiple of the broadcast frequency
static std::chrono::seconds const liveCacheSecondsToLive (60);
} }
/** @} */
} }
} }

View File

@@ -22,7 +22,6 @@
#include "ripple_peerfinder.h" #include "ripple_peerfinder.h"
#include "../../ripple/algorithm/api/CycledSet.h" #include "../../ripple/algorithm/api/CycledSet.h"
#include "../../ripple/algorithm/api/DiscreteClock.h"
#include "../../ripple/common/Resolver.h" #include "../../ripple/common/Resolver.h"
#include <deque> #include <deque>
@@ -60,29 +59,26 @@ using namespace beast;
# include "impl/Tuning.h" # include "impl/Tuning.h"
# include "impl/Checker.h" # include "impl/Checker.h"
# include "impl/Resolver.h"
#include "impl/CheckerAdapter.h" #include "impl/CheckerAdapter.h"
# include "impl/Sorts.h" # include "impl/Sorts.h"
# include "impl/Giveaways.h" # include "impl/Giveaways.h"
# include "impl/Livecache.h" # include "impl/Livecache.h"
# include "impl/Slots.h" # include "impl/SlotImp.h"
# include "impl/Counts.h"
# include "impl/Source.h" # include "impl/Source.h"
#include "impl/SourceStrings.h" #include "impl/SourceStrings.h"
# include "impl/Store.h" # include "impl/Store.h"
# include "impl/Bootcache.h" # include "impl/Bootcache.h"
# include "impl/Peer.h" //# include "impl/Peer.h"
#include "impl/StoreSqdb.h" #include "impl/StoreSqdb.h"
# include "impl/Reporting.h" # include "impl/Reporting.h"
#include "impl/FixedPeer.h" #include "impl/Logic.h"
# include "impl/Logic.h"
#include "impl/LogicType.h"
#include "impl/Checker.cpp" #include "impl/Checker.cpp"
#include "impl/Config.cpp" #include "impl/Config.cpp"
#include "impl/Endpoint.cpp" #include "impl/Endpoint.cpp"
#include "impl/Livecache.cpp" #include "impl/Livecache.cpp"
#include "impl/Manager.cpp" #include "impl/Manager.cpp"
#include "impl/Resolver.cpp"
#include "impl/SourceStrings.cpp" #include "impl/SourceStrings.cpp"
//#include "sim/sync_timer.h" //#include "sim/sync_timer.h"

View File

@@ -30,6 +30,7 @@ using namespace beast;
#include "../types/api/RipplePublicKey.h" #include "../types/api/RipplePublicKey.h"
#include "api/Slot.h"
# include "api/Endpoint.h" # include "api/Endpoint.h"
# include "api/Types.h" # include "api/Types.h"
#include "api/Callback.h" #include "api/Callback.h"

View File

@@ -56,19 +56,19 @@ is_remote_node_pred <Node> is_remote_node (Node const* node)
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/** UnaryPredicate, `true` if the remote address matches. */ /** UnaryPredicate, `true` if the remote address matches. */
class is_remote_address class is_remote_endpoint
{ {
public: public:
explicit is_remote_address (IPAddress const& address) explicit is_remote_endpoint (IPAddress const& address)
: m_address (address) : m_endpoint (address)
{ } { }
template <typename Link> template <typename Link>
bool operator() (Link const& link) const bool operator() (Link const& link) const
{ {
return link.remote_address() == m_address; return link.remote_endpoint() == m_endpoint;
} }
private: private:
IPAddress const m_address; IPAddress const m_endpoint;
}; };
} }

View File

@@ -17,6 +17,8 @@
*/ */
//============================================================================== //==============================================================================
#if 0
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
namespace Sim { namespace Sim {
@@ -37,7 +39,7 @@ public:
typedef std::list <Node> Peers; typedef std::list <Node> Peers;
typedef boost::unordered_map < typedef boost::unordered_map <
IPAddress, boost::reference_wrapper <Node>> Table; IP::Endpoint, boost::reference_wrapper <Node>> Table;
explicit Network (Params const& params, explicit Network (Params const& params,
Journal journal = Journal()); Journal journal = Journal());
@@ -48,10 +50,10 @@ public:
void prepare (); void prepare ();
Journal journal () const; Journal journal () const;
int next_node_id (); int next_node_id ();
DiscreteTime now (); clock_type::time_point now ();
Peers& nodes(); Peers& nodes();
Peers const& nodes() const; Peers const& nodes() const;
Node* find (IPAddress const& address); Node* find (IP::Endpoint const& address);
void step (); void step ();
template <typename Function> template <typename Function>
@@ -62,7 +64,7 @@ private:
Params m_params; Params m_params;
Journal m_journal; Journal m_journal;
int m_next_node_id; int m_next_node_id;
ManualClock m_clock_source; manual_clock <std::chrono::seconds> m_clock;
Peers m_nodes; Peers m_nodes;
Table m_table; Table m_table;
FunctionQueue m_queue; FunctionQueue m_queue;
@@ -82,14 +84,16 @@ public:
Link ( Link (
Node& local_node, Node& local_node,
IPAddress const& local_address, SlotImp::ptr const& slot,
IP::Endpoint const& local_endpoint,
Node& remote_node, Node& remote_node,
IPAddress const& remote_address, IP::Endpoint const& remote_endpoint,
bool inbound) bool inbound)
: m_local_node (&local_node) : m_local_node (&local_node)
, m_local_address (local_address) , m_slot (slot)
, m_local_endpoint (local_endpoint)
, m_remote_node (&remote_node) , m_remote_node (&remote_node)
, m_remote_address (remote_address) , m_remote_endpoint (remote_endpoint)
, m_inbound (inbound) , m_inbound (inbound)
, m_closed (false) , m_closed (false)
{ {
@@ -101,14 +105,15 @@ public:
bool inbound () const { return m_inbound; } bool inbound () const { return m_inbound; }
bool outbound () const { return ! m_inbound; } bool outbound () const { return ! m_inbound; }
IPAddress const& remote_address() const { return m_remote_address; } IP::Endpoint const& remote_endpoint() const { return m_remote_endpoint; }
IPAddress const& local_address() const { return m_local_address; } IP::Endpoint const& local_endpoint() const { return m_local_endpoint; }
SlotImp::ptr const& slot () const { return m_slot; }
Node& remote_node () { return *m_remote_node; } Node& remote_node () { return *m_remote_node; }
Node const& remote_node () const { return *m_remote_node; } Node const& remote_node () const { return *m_remote_node; }
Node& local_node () { return *m_local_node; } Node& local_node () { return *m_local_node; }
Node const& local_node () const { return *m_local_node; } Node const& local_node () const { return *m_local_node; }
void post (Message const& m) void post (Message const& m)
{ {
m_pending.push_back (m); m_pending.push_back (m);
@@ -133,9 +138,10 @@ public:
private: private:
Node* m_local_node; Node* m_local_node;
IPAddress m_local_address; SlotImp::ptr m_slot;
IP::Endpoint m_local_endpoint;
Node* m_remote_node; Node* m_remote_node;
IPAddress m_remote_address; IP::Endpoint m_remote_endpoint;
bool m_inbound; bool m_inbound;
bool m_closed; bool m_closed;
Messages m_current; Messages m_current;
@@ -161,8 +167,8 @@ public:
} }
bool canAccept; bool canAccept;
IPAddress listening_address; IP::Endpoint listening_endpoint;
IPAddress well_known_address; IP::Endpoint well_known_endpoint;
PeerFinder::Config config; PeerFinder::Config config;
}; };
@@ -172,17 +178,17 @@ public:
Node ( Node (
Network& network, Network& network,
Config const& config, Config const& config,
DiscreteClock <DiscreteTime> clock, clock_type& clock,
Journal journal) Journal journal)
: m_network (network) : m_network (network)
, m_id (network.next_node_id()) , m_id (network.next_node_id())
, m_config (config) , m_config (config)
, m_node_id (PeerID::createFromInteger (m_id)) , m_node_id (RipplePublicKey::createFromInteger (m_id))
, m_sink (prefix(), journal.sink()) , m_sink (prefix(), journal.sink())
, m_journal (Journal (m_sink, journal.severity()), Reporting::node) , m_journal (Journal (m_sink, journal.severity()), Reporting::node)
, m_next_port (m_config.listening_address.port() + 1) , m_next_port (m_config.listening_endpoint.port() + 1)
, m_logic (boost::in_place ( , m_logic (boost::in_place (
clock, boost::ref (*this), boost::ref (*this), boost::ref (*this), m_journal)) boost::ref (clock), boost::ref (*this), boost::ref (*this), boost::ref (*this), m_journal))
, m_whenSweep (m_network.now() + Tuning::liveCacheSecondsToLive) , m_whenSweep (m_network.now() + Tuning::liveCacheSecondsToLive)
{ {
logic().setConfig (m_config.config); logic().setConfig (m_config.config);
@@ -197,7 +203,7 @@ public:
void dump (Journal::ScopedStream& ss) const void dump (Journal::ScopedStream& ss) const
{ {
ss << listening_address(); ss << listening_endpoint();
logic().dump (ss); logic().dump (ss);
} }
@@ -216,7 +222,7 @@ public:
return m_id; return m_id;
} }
PeerID const& node_id () const RipplePublicKey const& node_id () const
{ {
return m_node_id; return m_node_id;
} }
@@ -231,9 +237,9 @@ public:
return m_logic.get(); return m_logic.get();
} }
IPAddress const& listening_address () const IP::Endpoint const& listening_endpoint () const
{ {
return m_config.listening_address; return m_config.listening_endpoint;
} }
bool canAccept () const bool canAccept () const
@@ -243,7 +249,7 @@ public:
void receive (Link const& c, Message const& m) void receive (Link const& c, Message const& m)
{ {
logic().onPeerEndpoints (c.remote_address(), m.payload()); logic().on_endpoints (c.slot (), m.payload());
} }
void pre_step () void pre_step ()
@@ -268,8 +274,8 @@ public:
if (iter->closed ()) if (iter->closed ())
{ {
// Post notification? // Post notification?
iter->local_node().logic().onPeerClosed ( iter->local_node().logic().on_closed (
iter->remote_address()); iter->remote_endpoint());
iter = links().erase (iter); iter = links().erase (iter);
} }
else else
@@ -297,11 +303,11 @@ public:
// //
//---------------------------------------------------------------------- //----------------------------------------------------------------------
void sendEndpoints (IPAddress const& remote_address, void sendEndpoints (IP::Endpoint const& remote_endpoint,
Endpoints const& endpoints) Endpoints const& endpoints)
{ {
m_network.post (std::bind (&Node::doSendEndpoints, this, m_network.post (std::bind (&Node::doSendEndpoints, this,
remote_address, endpoints)); remote_endpoint, endpoints));
} }
void connectPeers (IPAddresses const& addresses) void connectPeers (IPAddresses const& addresses)
@@ -310,23 +316,23 @@ public:
addresses)); addresses));
} }
void disconnectPeer (IPAddress const& remote_address, bool graceful) void disconnectPeer (IP::Endpoint const& remote_endpoint, bool graceful)
{ {
m_network.post (std::bind (&Node::doDisconnectPeer, this, m_network.post (std::bind (&Node::doDisconnectPeer, this,
remote_address, graceful)); remote_endpoint, graceful));
} }
void activatePeer (IPAddress const& remote_address) void activatePeer (IP::Endpoint const& remote_endpoint)
{ {
/* no underlying peer to activate */ /* no underlying peer to activate */
} }
void doSendEndpoints (IPAddress const& remote_address, void doSendEndpoints (IP::Endpoint const& remote_endpoint,
Endpoints const& endpoints) Endpoints const& endpoints)
{ {
Links::iterator const iter1 (std::find_if ( Links::iterator const iter1 (std::find_if (
links().begin (), links().end(), links().begin (), links().end(),
is_remote_address (remote_address))); is_remote_endpoint (remote_endpoint)));
if (iter1 != links().end()) if (iter1 != links().end())
{ {
// Drop the message if they closed their end // Drop the message if they closed their end
@@ -336,7 +342,7 @@ public:
// Find their link to us // Find their link to us
Links::iterator const iter2 (std::find_if ( Links::iterator const iter2 (std::find_if (
remote_node.links().begin(), remote_node.links().end(), remote_node.links().begin(), remote_node.links().end(),
is_remote_address (iter1->local_address ()))); is_remote_endpoint (iter1->local_endpoint ())));
consistency_check (iter2 != remote_node.links().end()); consistency_check (iter2 != remote_node.links().end());
// //
@@ -349,19 +355,19 @@ public:
} }
} }
void doCheckAccept (Node& remote_node, IPAddress const& remote_address) void doCheckAccept (Node& remote_node, IP::Endpoint const& remote_endpoint)
{ {
// Find our link to the remote node // Find our link to the remote node
Links::iterator iter (std::find_if (m_links.begin (), Links::iterator iter (std::find_if (m_links.begin (),
m_links.end(), is_remote_address (remote_address))); m_links.end(), is_remote_endpoint (remote_endpoint)));
// See if the logic closed the connection // See if the logic closed the connection
if (iter == m_links.end()) if (iter == m_links.end())
return; return;
// Post notifications // Post notifications
m_network.post (std::bind (&Logic::onPeerHandshake, m_network.post (std::bind (&Logic::on_handshake,
&remote_node.logic(), iter->local_address(), node_id(), false)); &remote_node.logic(), iter->local_endpoint(), node_id(), false));
m_network.post (std::bind (&Logic::onPeerHandshake, m_network.post (std::bind (&Logic::on_handshake,
&logic(), remote_address, remote_node.node_id(), false)); &logic(), remote_endpoint, remote_node.node_id(), false));
} }
void doConnectPeers (IPAddresses const& addresses) void doConnectPeers (IPAddresses const& addresses)
@@ -369,43 +375,49 @@ public:
for (IPAddresses::const_iterator iter (addresses.begin()); for (IPAddresses::const_iterator iter (addresses.begin());
iter != addresses.end(); ++iter) iter != addresses.end(); ++iter)
{ {
IPAddress const& remote_address (*iter); IP::Endpoint const& remote_endpoint (*iter);
Node* const remote_node (m_network.find (remote_address)); Node* const remote_node (m_network.find (remote_endpoint));
// Post notification // Acquire slot
m_network.post (std::bind (&Logic::onPeerConnect, Slot::ptr const local_slot (
&logic(), remote_address)); m_logic->new_outbound_slot (remote_endpoint));
if (! local_slot)
continue;
// See if the address is connectible // See if the address is connectible
if (remote_node == nullptr || ! remote_node->canAccept()) if (remote_node == nullptr || ! remote_node->canAccept())
{ {
// Firewalled or no one listening // Firewalled or no one listening
// Post notification // Post notification
m_network.post (std::bind (&Logic::onPeerClosed, m_network.post (std::bind (&Logic::on_closed,
&logic(), remote_address)); &logic(), local_slot));
continue; continue;
} }
IP::Endpoint const local_endpoint (
listening_endpoint().at_port (m_next_port++));
// Acquire slot
Slot::ptr const remote_slot (
remote_node->logic().new_inbound_slot (
remote_endpoint, local_endpoint));
if (! remote_slot)
continue;
// Connection established, create links // Connection established, create links
IPAddress const local_address ( m_links.emplace_back (*this, local_slot, local_endpoint,
listening_address().at_port (m_next_port++)); *remote_node, remote_endpoint, false);
m_links.emplace_back (*this, local_address, remote_node->m_links.emplace_back (*remote_node, remote_slot,
*remote_node, remote_address, false); remote_endpoint, *this, local_endpoint, true);
remote_node->m_links.emplace_back (*remote_node,
remote_address, *this, local_address, true);
// Post notifications // Post notifications
m_network.post (std::bind (&Logic::onPeerConnected, m_network.post (std::bind (&Logic::on_connected,
&logic(), local_address, remote_address)); &logic(), local_endpoint, remote_endpoint));
m_network.post (std::bind (&Logic::onPeerAccept,
&remote_node->logic(), remote_address, local_address));
m_network.post (std::bind (&Node::doCheckAccept, m_network.post (std::bind (&Node::doCheckAccept,
remote_node, boost::ref (*this), local_address)); remote_node, boost::ref (*this), local_endpoint));
} }
} }
void doClosed (IPAddress const& remote_address, bool graceful) void doClosed (IP::Endpoint const& remote_endpoint, bool graceful)
{ {
// Find our link to them // Find our link to them
Links::iterator const iter (std::find_if ( Links::iterator const iter (std::find_if (
m_links.begin(), m_links.end(), m_links.begin(), m_links.end(),
is_remote_address (remote_address))); is_remote_endpoint (remote_endpoint)));
// Must be connected! // Must be connected!
check_invariant (iter != m_links.end()); check_invariant (iter != m_links.end());
// Must be closed! // Must be closed!
@@ -413,46 +425,46 @@ public:
// Remove our link to them // Remove our link to them
m_links.erase (iter); m_links.erase (iter);
// Notify // Notify
m_network.post (std::bind (&Logic::onPeerClosed, m_network.post (std::bind (&Logic::on_closed,
&logic(), remote_address)); &logic(), remote_endpoint));
} }
void doDisconnectPeer (IPAddress const& remote_address, bool graceful) void doDisconnectPeer (IP::Endpoint const& remote_endpoint, bool graceful)
{ {
// Find our link to them // Find our link to them
Links::iterator const iter1 (std::find_if ( Links::iterator const iter1 (std::find_if (
m_links.begin(), m_links.end(), m_links.begin(), m_links.end(),
is_remote_address (remote_address))); is_remote_endpoint (remote_endpoint)));
if (iter1 == m_links.end()) if (iter1 == m_links.end())
return; return;
Node& remote_node (iter1->remote_node()); Node& remote_node (iter1->remote_node());
IPAddress const local_address (iter1->local_address()); IP::Endpoint const local_endpoint (iter1->local_endpoint());
// Find their link to us // Find their link to us
Links::iterator const iter2 (std::find_if ( Links::iterator const iter2 (std::find_if (
remote_node.links().begin(), remote_node.links().end(), remote_node.links().begin(), remote_node.links().end(),
is_remote_address (local_address))); is_remote_endpoint (local_endpoint)));
if (iter2 != remote_node.links().end()) if (iter2 != remote_node.links().end())
{ {
// Notify the remote that we closed // Notify the remote that we closed
check_invariant (! iter2->closed()); check_invariant (! iter2->closed());
iter2->close(); iter2->close();
m_network.post (std::bind (&Node::doClosed, m_network.post (std::bind (&Node::doClosed,
&remote_node, local_address, graceful)); &remote_node, local_endpoint, graceful));
} }
if (! iter1->closed ()) if (! iter1->closed ())
{ {
// Remove our link to them // Remove our link to them
m_links.erase (iter1); m_links.erase (iter1);
// Notify // Notify
m_network.post (std::bind (&Logic::onPeerClosed, m_network.post (std::bind (&Logic::on_closed,
&logic(), remote_address)); &logic(), remote_endpoint));
} }
/* /*
if (! graceful || ! iter2->pending ()) if (! graceful || ! iter2->pending ())
{ {
remote_node.links().erase (iter2); remote_node.links().erase (iter2);
remote_node.logic().onPeerClosed (local_address); remote_node.logic().on_closed (local_endpoint);
} }
*/ */
} }
@@ -467,8 +479,8 @@ public:
{ {
std::vector <SavedBootstrapAddress> result; std::vector <SavedBootstrapAddress> result;
SavedBootstrapAddress item; SavedBootstrapAddress item;
item.address = m_config.well_known_address; item.address = m_config.well_known_endpoint;
item.cumulativeUptimeSeconds = 0; item.cumulativeUptime = std::chrono::seconds (0);
item.connectionValence = 0; item.connectionValence = 0;
result.push_back (item); result.push_back (item);
return result; return result;
@@ -488,7 +500,7 @@ public:
{ {
} }
void async_test (IPAddress const& address, void async_test (IP::Endpoint const& address,
AbstractHandler <void (Result)> handler) AbstractHandler <void (Result)> handler)
{ {
Node* const node (m_network.find (address)); Node* const node (m_network.find (address));
@@ -516,12 +528,12 @@ private:
Network& m_network; Network& m_network;
int const m_id; int const m_id;
Config const m_config; Config const m_config;
PeerID m_node_id; RipplePublicKey m_node_id;
WrappedSink m_sink; WrappedSink m_sink;
Journal m_journal; Journal m_journal;
IP::Port m_next_port; IP::Port m_next_port;
boost::optional <Logic> m_logic; boost::optional <Logic> m_logic;
DiscreteTime m_whenSweep; clock_type::time_point m_whenSweep;
SavedBootstrapAddresses m_bootstrap_cache; SavedBootstrapAddresses m_bootstrap_cache;
}; };
@@ -537,13 +549,13 @@ void Link::step ()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
static IPAddress next_address (IPAddress address) static IP::Endpoint next_endpoint (IP::Endpoint address)
{ {
if (address.is_v4()) if (address.is_v4())
{ {
do do
{ {
address = IPAddress (IP::AddressV4 ( address = IP::Endpoint (IP::AddressV4 (
address.to_v4().value + 1)).at_port (address.port()); address.to_v4().value + 1)).at_port (address.port());
} }
while (! is_public (address)); while (! is_public (address));
@@ -554,7 +566,7 @@ static IPAddress next_address (IPAddress address)
bassert (address.is_v6()); bassert (address.is_v6());
// unimplemented // unimplemented
bassertfalse; bassertfalse;
return IPAddress(); return IP::Endpoint();
} }
Network::Network ( Network::Network (
@@ -569,9 +581,9 @@ Network::Network (
void Network::prepare () void Network::prepare ()
{ {
IPAddress const well_known_address ( IP::Endpoint const well_known_endpoint (
IPAddress::from_string ("1.0.0.1").at_port (1)); IP::Endpoint::from_string ("1.0.0.1").at_port (1));
IPAddress address (well_known_address); IP::Endpoint address (well_known_endpoint);
for (int i = 0; i < params().nodes; ++i ) for (int i = 0; i < params().nodes; ++i )
{ {
@@ -579,8 +591,8 @@ void Network::prepare ()
{ {
Node::Config config; Node::Config config;
config.canAccept = true; config.canAccept = true;
config.listening_address = address; config.listening_endpoint = address;
config.well_known_address = well_known_address; config.well_known_endpoint = well_known_endpoint;
config.config.maxPeers = params().maxPeers; config.config.maxPeers = params().maxPeers;
config.config.outPeers = params().outPeers; config.config.outPeers = params().outPeers;
config.config.wantIncoming = true; config.config.wantIncoming = true;
@@ -589,10 +601,10 @@ void Network::prepare ()
m_nodes.emplace_back ( m_nodes.emplace_back (
*this, *this,
config, config,
m_clock_source, m_clock,
m_journal); m_journal);
m_table.emplace (address, boost::ref (m_nodes.back())); m_table.emplace (address, boost::ref (m_nodes.back()));
address = next_address (address); address = next_endpoint (address);
} }
if (i != 0) if (i != 0)
@@ -600,8 +612,8 @@ void Network::prepare ()
Node::Config config; Node::Config config;
config.canAccept = Random::getSystemRandom().nextInt (100) >= config.canAccept = Random::getSystemRandom().nextInt (100) >=
(m_params.firewalled * 100); (m_params.firewalled * 100);
config.listening_address = address; config.listening_endpoint = address;
config.well_known_address = well_known_address; config.well_known_endpoint = well_known_endpoint;
config.config.maxPeers = params().maxPeers; config.config.maxPeers = params().maxPeers;
config.config.outPeers = params().outPeers; config.config.outPeers = params().outPeers;
config.config.wantIncoming = true; config.config.wantIncoming = true;
@@ -610,10 +622,10 @@ void Network::prepare ()
m_nodes.emplace_back ( m_nodes.emplace_back (
*this, *this,
config, config,
m_clock_source, m_clock,
m_journal); m_journal);
m_table.emplace (address, boost::ref (m_nodes.back())); m_table.emplace (address, boost::ref (m_nodes.back()));
address = next_address (address); address = next_endpoint (address);
} }
} }
} }
@@ -632,9 +644,9 @@ int Network::next_node_id ()
return m_next_node_id++; return m_next_node_id++;
} }
DiscreteTime Network::now () clock_type::time_point Network::now ()
{ {
return m_clock_source(); return m_clock.now();
} }
Network::Peers& Network::nodes() Network::Peers& Network::nodes()
@@ -649,7 +661,7 @@ Network::Peers const& Network::nodes() const
} }
#endif #endif
Node* Network::find (IPAddress const& address) Node* Network::find (IP::Endpoint const& address)
{ {
Table::iterator iter (m_table.find (address)); Table::iterator iter (m_table.find (address));
if (iter != m_table.end()) if (iter != m_table.end())
@@ -672,8 +684,8 @@ void Network::step ()
// Advance the manual clock so that // Advance the manual clock so that
// messages are broadcast at every step. // messages are broadcast at every step.
// //
//m_clock_source.now() += Tuning::secondsPerConnect; //m_clock += Tuning::secondsPerConnect;
m_clock_source.now() += 1; ++m_clock;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -695,7 +707,7 @@ struct PeerStats
{ {
PeerStats () PeerStats ()
: inboundActive (0) : inboundActive (0)
, outboundActive (0) , out_active (0)
, inboundSlotsFree (0) , inboundSlotsFree (0)
, outboundSlotsFree (0) , outboundSlotsFree (0)
{ {
@@ -704,26 +716,26 @@ struct PeerStats
template <typename Peer> template <typename Peer>
explicit PeerStats (Peer const& peer) explicit PeerStats (Peer const& peer)
{ {
inboundActive = peer.logic().slots().inboundActive(); inboundActive = peer.logic().counts().inboundActive();
outboundActive = peer.logic().slots().outboundActive(); out_active = peer.logic().counts().out_active();
inboundSlotsFree = peer.logic().slots().inboundSlotsFree(); inboundSlotsFree = peer.logic().counts().inboundSlotsFree();
outboundSlotsFree = peer.logic().slots().outboundSlotsFree(); outboundSlotsFree = peer.logic().counts().outboundSlotsFree();
} }
PeerStats& operator+= (PeerStats const& rhs) PeerStats& operator+= (PeerStats const& rhs)
{ {
inboundActive += rhs.inboundActive; inboundActive += rhs.inboundActive;
outboundActive += rhs.outboundActive; out_active += rhs.out_active;
inboundSlotsFree += rhs.inboundSlotsFree; inboundSlotsFree += rhs.inboundSlotsFree;
outboundSlotsFree += rhs.outboundSlotsFree; outboundSlotsFree += rhs.outboundSlotsFree;
return *this; return *this;
} }
int totalActive () const int totalActive () const
{ return inboundActive + outboundActive; } { return inboundActive + out_active; }
int inboundActive; int inboundActive;
int outboundActive; int out_active;
int inboundSlotsFree; int inboundSlotsFree;
int outboundSlotsFree; int outboundSlotsFree;
}; };
@@ -766,7 +778,7 @@ public:
double outPeers () const double outPeers () const
{ {
if (m_size > 0) if (m_size > 0)
return double (m_stats.outboundActive) / m_size; return double (m_stats.out_active) / m_size;
return 0; return 0;
} }
@@ -857,10 +869,10 @@ void report_nodes (NodeSequence const& nodes, Journal::Stream const& stream)
Logic::State const& state (logic.state()); Logic::State const& state (logic.state());
stream << stream <<
rfield (node.id ()) << rfield (node.id ()) <<
rfield (state.slots.totalActive ()) << rfield (state.counts.totalActive ()) <<
rfield (state.slots.inboundActive ()) << rfield (state.counts.inboundActive ()) <<
rfield (state.slots.outboundActive ()) << rfield (state.counts.out_active ()) <<
rfield (state.slots.connectCount ()) << rfield (state.counts.connectCount ()) <<
rfield (state.livecache.size ()) << rfield (state.livecache.size ()) <<
rfield (state.bootcache.size ()) rfield (state.bootcache.size ())
; ;
@@ -1024,7 +1036,7 @@ public:
} }
n.journal().info << n.journal().info <<
divider () << std::endl << divider () << std::endl <<
"Time " << n.now () << std::endl << "Time " << n.now ().time_since_epoch () << std::endl <<
divider () divider ()
; ;
@@ -1049,7 +1061,7 @@ public:
ss << std::endl << ss << std::endl <<
"--------------" << std::endl << "--------------" << std::endl <<
"#" << node.id() << "#" << node.id() <<
" at " << node.listening_address (); " at " << node.listening_endpoint ();
node.logic().dump (ss); node.logic().dump (ss);
} }
} }
@@ -1082,3 +1094,5 @@ static PeerFinderTests peerFinderTests;
} }
} }
} }
#endif

View File

@@ -37,10 +37,7 @@ class Peers;
/** Represents a peer connection in the overlay. /** Represents a peer connection in the overlay.
*/ */
class Peer class Peer : private LeakChecked <Peer>
: public boost::enable_shared_from_this <Peer>
, public List <Peer>::Node
, private LeakChecked <Peer>
{ {
public: public:
typedef boost::shared_ptr <Peer> Ptr; typedef boost::shared_ptr <Peer> Ptr;
@@ -74,28 +71,12 @@ public:
}; };
public: public:
static void accept (
boost::shared_ptr <NativeSocketType> const& socket,
Peers& peers,
Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder,
boost::asio::ssl::context& ctx,
bool proxyHandshake);
static void connect (
IP::Endpoint const& address,
boost::asio::io_service& io_service,
Peers& peers,
Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder,
boost::asio::ssl::context& ssl_context);
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** Called when an open slot is assigned to a handshaked peer. */ /** Called when an open slot is assigned to a handshaked peer. */
virtual void activate () = 0; virtual void activate () = 0;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
//virtual void connect (IPAddress const &address) = 0; //virtual void connect (IP::Endpoint const &address) = 0;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
virtual State state () const = 0; virtual State state () const = 0;
@@ -103,7 +84,7 @@ public:
virtual void state (State new_state) = 0; virtual void state (State new_state) = 0;
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
virtual void detach (const char*, bool onIOStrand) = 0; //virtual void detach (const char*, bool onIOStrand) = 0;
virtual void sendPacket (const PackedMessage::pointer& packet, bool onStrand) = 0; virtual void sendPacket (const PackedMessage::pointer& packet, bool onStrand) = 0;
@@ -150,7 +131,7 @@ public:
virtual bool hasRange (uint32 uMin, uint32 uMax) = 0; virtual bool hasRange (uint32 uMin, uint32 uMax) = 0;
virtual IPAddress getRemoteAddress() const = 0; virtual IP::Endpoint getRemoteAddress() const = 0;
virtual NativeSocketType& getNativeSocket () = 0; virtual NativeSocketType& getNativeSocket () = 0;
}; };

View File

@@ -73,6 +73,9 @@ public:
// //
void handleTimer (boost::system::error_code ec) void handleTimer (boost::system::error_code ec)
{ {
if (ec == boost::asio::error::operation_aborted || isStopping ())
return;
async_accept (); async_accept ();
} }
@@ -81,6 +84,9 @@ public:
void handleAccept (boost::system::error_code ec, void handleAccept (boost::system::error_code ec,
boost::shared_ptr <NativeSocketType> const& socket) boost::shared_ptr <NativeSocketType> const& socket)
{ {
if (ec == boost::asio::error::operation_aborted || isStopping ())
return;
bool delay = false; bool delay = false;
if (! ec) if (! ec)

View File

@@ -17,9 +17,10 @@
*/ */
//============================================================================== //==============================================================================
namespace ripple { #ifndef RIPPLE_OVERLAY_PEERIMP_H_INCLUDED
#define RIPPLE_OVERLAY_PEERIMP_H_INCLUDED
SETUP_LOG (Peer) namespace ripple {
class PeerImp; class PeerImp;
@@ -60,6 +61,7 @@ struct get_usable_peers
class PeerImp class PeerImp
: public Peer : public Peer
, public CountedObject <PeerImp> , public CountedObject <PeerImp>
, public boost::enable_shared_from_this <PeerImp>
{ {
private: private:
/** Time alloted for a peer to send a HELLO message (DEPRECATED) */ /** Time alloted for a peer to send a HELLO message (DEPRECATED) */
@@ -72,6 +74,8 @@ private:
static const size_t sslMinimumFinishedLength = 12; static const size_t sslMinimumFinishedLength = 12;
public: public:
typedef boost::shared_ptr <PeerImp> ptr;
boost::shared_ptr <NativeSocketType> m_shared_socket; boost::shared_ptr <NativeSocketType> m_shared_socket;
Journal m_journal; Journal m_journal;
@@ -84,7 +88,7 @@ public:
// Updated at each stage of the connection process to reflect // Updated at each stage of the connection process to reflect
// the current conditions as closely as possible. This includes // the current conditions as closely as possible. This includes
// the case where we learn the true IP via a PROXY handshake. // the case where we learn the true IP via a PROXY handshake.
IPAddress m_remoteAddress; IP::Endpoint m_remoteAddress;
// These is up here to prevent warnings about order of initializations // These is up here to prevent warnings about order of initializations
// //
@@ -119,7 +123,7 @@ public:
std::list<uint256> m_recentTxSets; std::list<uint256> m_recentTxSets;
mutable boost::mutex m_recentLock; mutable boost::mutex m_recentLock;
boost::asio::deadline_timer mActivityTimer; boost::asio::deadline_timer m_timer;
std::vector<uint8_t> m_readBuffer; std::vector<uint8_t> m_readBuffer;
std::list<PackedMessage::pointer> mSendQ; std::list<PackedMessage::pointer> mSendQ;
@@ -129,6 +133,9 @@ public:
Resource::Consumer m_usage; Resource::Consumer m_usage;
// The slot assigned to us by PeerFinder
PeerFinder::Slot::ptr m_slot;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
/** New incoming peer from the specified socket */ /** New incoming peer from the specified socket */
PeerImp ( PeerImp (
@@ -136,6 +143,7 @@ public:
Peers& peers, Peers& peers,
Resource::Manager& resourceManager, Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder, PeerFinder::Manager& peerFinder,
PeerFinder::Slot::ptr const& slot,
boost::asio::ssl::context& ssl_context, boost::asio::ssl::context& ssl_context,
MultiSocket::Flag flags) MultiSocket::Flag flags)
: m_shared_socket (socket) : m_shared_socket (socket)
@@ -153,9 +161,9 @@ public:
, m_clusterNode (false) , m_clusterNode (false)
, m_minLedger (0) , m_minLedger (0)
, m_maxLedger (0) , m_maxLedger (0)
, mActivityTimer (socket->get_io_service()) , m_timer (socket->get_io_service())
, m_slot (slot)
{ {
m_peers.peerCreated (this);
} }
/** New outgoing peer /** New outgoing peer
@@ -169,6 +177,7 @@ public:
Peers& peers, Peers& peers,
Resource::Manager& resourceManager, Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder, PeerFinder::Manager& peerFinder,
PeerFinder::Slot::ptr const& slot,
boost::asio::ssl::context& ssl_context, boost::asio::ssl::context& ssl_context,
MultiSocket::Flag flags) MultiSocket::Flag flags)
: m_journal (LogPartition::getJournal <Peer> ()) : m_journal (LogPartition::getJournal <Peer> ())
@@ -185,14 +194,14 @@ public:
, m_clusterNode (false) , m_clusterNode (false)
, m_minLedger (0) , m_minLedger (0)
, m_maxLedger (0) , m_maxLedger (0)
, mActivityTimer (io_service) , m_timer (io_service)
, m_slot (slot)
{ {
m_peers.peerCreated (this);
} }
virtual ~PeerImp () virtual ~PeerImp ()
{ {
m_peers.peerDestroyed (this); m_peers.remove (m_slot);
} }
NativeSocketType& getNativeSocket () NativeSocketType& getNativeSocket ()
@@ -234,37 +243,93 @@ public:
object and begins the process of connection establishment instead object and begins the process of connection establishment instead
of requiring the caller to construct a Peer and call connect. of requiring the caller to construct a Peer and call connect.
*/ */
void connect (IPAddress const& address) void connect (IP::Endpoint const& address)
{ {
m_remoteAddress = address; m_remoteAddress = address;
m_peers.addPeer (shared_from_this ());
m_journal.info << "Connecting to " << m_remoteAddress; m_journal.info << "Connecting to " << m_remoteAddress;
boost::system::error_code err; boost::system::error_code err;
mActivityTimer.expires_from_now (nodeVerifySeconds, err); m_timer.expires_from_now (nodeVerifySeconds, err);
mActivityTimer.async_wait (m_strand.wrap (boost::bind ( m_timer.async_wait (m_strand.wrap (boost::bind (&PeerImp::handleVerifyTimer,
&PeerImp::handleVerifyTimer, shared_from_this (), boost::asio::placeholders::error)));
boost::static_pointer_cast <PeerImp> (shared_from_this ()),
boost::asio::placeholders::error)));
if (err) if (err)
{ {
m_journal.error << "Failed to set verify timer."; m_journal.error << "Failed to set verify timer.";
detach ("c2", false); detach ("c2");
return; return;
} }
m_peerFinder.onPeerConnect (m_remoteAddress);
getNativeSocket ().async_connect ( getNativeSocket ().async_connect (
IPAddressConversion::to_asio_endpoint (address), IPAddressConversion::to_asio_endpoint (address),
m_strand.wrap (boost::bind ( m_strand.wrap (boost::bind (&PeerImp::onConnect,
&PeerImp::onConnect, shared_from_this (), boost::asio::placeholders::error)));
boost::static_pointer_cast <PeerImp> (shared_from_this ()), }
boost::asio::placeholders::error)));
/** Disconnect a peer
The peer transitions from its current state into `stateGracefulClose`
@param rsn a code indicating why the peer was disconnected
@param onIOStrand true if called on an I/O strand. It if is not, then
a callback will be queued up.
*/
void detach (const char* rsn, bool graceful = true)
{
if (! m_strand.running_in_this_thread ())
{
m_strand.post (BIND_TYPE (&PeerImp::detach,
shared_from_this (), rsn, graceful));
return;
}
if (!m_detaching)
{
// NIKB TODO No - a race is NOT ok. This needs to be fixed
// to have PeerFinder work reliably.
m_detaching = true; // Race is ok.
m_peerFinder.on_closed (m_slot);
if (m_state == stateActive)
m_peers.onPeerDisconnect (shared_from_this ());
m_state = stateGracefulClose;
if (m_clusterNode && m_journal.active(Journal::Severity::kWarning))
m_journal.warning << "Cluster peer " << m_nodeName <<
" detached: " << rsn;
mSendQ.clear ();
(void) m_timer.cancel ();
if (graceful)
{
m_socket->async_shutdown (
m_strand.wrap ( boost::bind(
&PeerImp::handleShutdown,
boost::static_pointer_cast <PeerImp> (shared_from_this ()),
boost::asio::placeholders::error)));
}
else
{
m_socket->cancel ();
}
// VFALCO TODO Stop doing this.
if (m_nodePublicKey.isValid ())
m_nodePublicKey.clear (); // Be idempotent.
}
}
/** Close the connection. */
void close (bool graceful)
{
detach ("stop", graceful);
} }
/** Outbound connection attempt has completed (not necessarily successfully) /** Outbound connection attempt has completed (not necessarily successfully)
@@ -280,24 +345,33 @@ public:
@param ec indicates success or an error code. @param ec indicates success or an error code.
*/ */
void onConnect (boost::system::error_code const& ec) void onConnect (boost::system::error_code ec)
{ {
if (m_detaching)
return;
NativeSocketType::endpoint_type local_endpoint;
if (! ec)
local_endpoint = m_socket->this_layer <
NativeSocketType> ().local_endpoint (ec);
if (ec) if (ec)
{ {
// VFALCO NOTE This log statement looks like ass // VFALCO NOTE This log statement looks like ass
m_journal.info << "Connecting to " << m_remoteAddress << m_journal.info <<
" failed " << ec.message(); "Connect to " << m_remoteAddress <<
" failed: " << ec.message();
// This should end up calling onPeerClosed() // This should end up calling onPeerClosed()
detach ("hc", true); detach ("hc");
return; return;
} }
bassert (m_state == stateConnecting); bassert (m_state == stateConnecting);
m_state = stateConnected; m_state = stateConnected;
m_peerFinder.onPeerConnected (m_socket->local_endpoint(), m_peerFinder.on_connected (m_slot,
m_remoteAddress); IPAddressConversion::from_asio (local_endpoint));
m_socket->set_verify_mode (boost::asio::ssl::verify_none); m_socket->set_verify_mode (boost::asio::ssl::verify_none);
m_socket->async_handshake ( m_socket->async_handshake (
@@ -320,10 +394,6 @@ public:
m_journal.info << "Accepted " << m_remoteAddress; m_journal.info << "Accepted " << m_remoteAddress;
m_peers.addPeer (shared_from_this ());
m_peerFinder.onPeerAccept (m_socket->local_endpoint(), m_remoteAddress);
m_socket->set_verify_mode (boost::asio::ssl::verify_none); m_socket->set_verify_mode (boost::asio::ssl::verify_none);
m_socket->async_handshake ( m_socket->async_handshake (
boost::asio::ssl::stream_base::server, boost::asio::ssl::stream_base::server,
@@ -353,56 +423,6 @@ public:
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
/** Disconnect a peer
The peer transitions from its current state into `stateGracefulClose`
@param rsn a code indicating why the peer was disconnected
@param onIOStrand true if called on an I/O strand. It if is not, then
a callback will be queued up.
*/
void detach (const char* rsn, bool onIOStrand)
{
// VFALCO NOTE So essentially, detach() is really two different functions
// depending on the value of onIOStrand.
// TODO Clean this up.
//
if (!onIOStrand)
{
m_strand.post (BIND_TYPE (&Peer::detach, shared_from_this (), rsn, true));
return;
}
if (!m_detaching)
{
// NIKB TODO No - a race is NOT ok. This needs to be fixed
// to have PeerFinder work reliably.
m_detaching = true; // Race is ok.
m_peerFinder.onPeerClosed (m_remoteAddress);
if (m_state == stateActive)
m_peers.onPeerDisconnect (shared_from_this ());
m_state = stateGracefulClose;
if (m_clusterNode && m_journal.active(Journal::Severity::kWarning))
m_journal.warning << "Cluster peer " << m_nodeName <<
" detached: " << rsn;
mSendQ.clear ();
(void) mActivityTimer.cancel ();
m_socket->async_shutdown (
m_strand.wrap ( boost::bind(
&PeerImp::handleShutdown,
boost::static_pointer_cast <PeerImp> (shared_from_this ()),
boost::asio::placeholders::error)));
if (m_nodePublicKey.isValid ())
m_nodePublicKey.clear (); // Be idempotent.
}
}
void sendPacket (const PackedMessage::pointer& packet, bool onStrand) void sendPacket (const PackedMessage::pointer& packet, bool onStrand)
{ {
@@ -442,7 +462,7 @@ public:
void charge (Resource::Charge const& fee) void charge (Resource::Charge const& fee)
{ {
if ((m_usage.charge (fee) == Resource::drop) && m_usage.disconnect ()) if ((m_usage.charge (fee) == Resource::drop) && m_usage.disconnect ())
detach ("resource", false); detach ("resource");
} }
Json::Value json () Json::Value json ()
@@ -606,7 +626,7 @@ public:
return (uMin >= m_minLedger) && (uMax <= m_maxLedger); return (uMin >= m_minLedger) && (uMax <= m_maxLedger);
} }
IPAddress getRemoteAddress() const IP::Endpoint getRemoteAddress() const
{ {
return m_remoteAddress; return m_remoteAddress;
} }
@@ -614,25 +634,25 @@ public:
private: private:
void handleShutdown (boost::system::error_code const& ec) void handleShutdown (boost::system::error_code const& ec)
{ {
if (ec == boost::asio::error::operation_aborted) if (m_detaching)
return; return;
if (m_detaching) if (ec == boost::asio::error::operation_aborted)
{
m_peers.removePeer (shared_from_this());
return; return;
}
if (ec) if (ec)
{ {
m_journal.info << "Shutdown: " << ec.message (); m_journal.info << "Shutdown: " << ec.message ();
detach ("hsd", true); detach ("hsd");
return; return;
} }
} }
void handleWrite (boost::system::error_code const& ec, size_t bytes) void handleWrite (boost::system::error_code const& ec, size_t bytes)
{ {
if (m_detaching)
return;
// Call on IO strand // Call on IO strand
mSendingPacket.reset (); mSendingPacket.reset ();
@@ -646,7 +666,7 @@ private:
if (ec) if (ec)
{ {
m_journal.info << "Write: " << ec.message (); m_journal.info << "Write: " << ec.message ();
detach ("hw", true); detach ("hw");
return; return;
} }
@@ -665,16 +685,16 @@ private:
void handleReadHeader (boost::system::error_code const& ec, void handleReadHeader (boost::system::error_code const& ec,
std::size_t bytes) std::size_t bytes)
{ {
if (ec == boost::asio::error::operation_aborted) if (m_detaching)
return; return;
if (m_detaching) if (ec == boost::asio::error::operation_aborted)
return; return;
if (ec) if (ec)
{ {
m_journal.info << "ReadHeader: " << ec.message (); m_journal.info << "ReadHeader: " << ec.message ();
detach ("hrh1", true); detach ("hrh1");
return; return;
} }
@@ -683,7 +703,7 @@ private:
// WRITEME: Compare to maximum message length, abort if too large // WRITEME: Compare to maximum message length, abort if too large
if ((msg_len > (32 * 1024 * 1024)) || (msg_len == 0)) if ((msg_len > (32 * 1024 * 1024)) || (msg_len == 0))
{ {
detach ("hrh2", true); detach ("hrh2");
return; return;
} }
@@ -693,10 +713,10 @@ private:
void handleReadBody (boost::system::error_code const& ec, void handleReadBody (boost::system::error_code const& ec,
std::size_t bytes) std::size_t bytes)
{ {
if (ec == boost::asio::error::operation_aborted) if (m_detaching)
return; return;
if (m_detaching) if (ec == boost::asio::error::operation_aborted)
return; return;
if (ec) if (ec)
@@ -705,7 +725,7 @@ private:
{ {
Application::ScopedLockType lock (getApp ().getMasterLock (), __FILE__, __LINE__); Application::ScopedLockType lock (getApp ().getMasterLock (), __FILE__, __LINE__);
detach ("hrb", true); detach ("hrb");
} }
return; return;
@@ -722,16 +742,16 @@ private:
// is in progress. // is in progress.
void handleStart (boost::system::error_code const& ec) void handleStart (boost::system::error_code const& ec)
{ {
if (ec == boost::asio::error::operation_aborted) if (m_detaching)
return; return;
if (m_detaching) if (ec == boost::asio::error::operation_aborted)
return; return;
if (ec) if (ec)
{ {
m_journal.info << "Handshake: " << ec.message (); m_journal.info << "Handshake: " << ec.message ();
detach ("hs", true); detach ("hs");
return; return;
} }
@@ -744,14 +764,14 @@ private:
if (m_usage.disconnect ()) if (m_usage.disconnect ())
{ {
detach ("resource", true); detach ("resource");
return; return;
} }
if(!sendHello ()) if(!sendHello ())
{ {
m_journal.error << "Unable to send HELLO to " << m_remoteAddress; m_journal.error << "Unable to send HELLO to " << m_remoteAddress;
detach ("hello", true); detach ("hello");
return; return;
} }
@@ -760,6 +780,9 @@ private:
void handleVerifyTimer (boost::system::error_code const& ec) void handleVerifyTimer (boost::system::error_code const& ec)
{ {
if (m_detaching)
return;
if (ec == boost::asio::error::operation_aborted) if (ec == boost::asio::error::operation_aborted)
{ {
// Timer canceled because deadline no longer needed. // Timer canceled because deadline no longer needed.
@@ -772,7 +795,7 @@ private:
{ {
// m_journal.info << "Verify: Peer failed to verify in time."; // m_journal.info << "Verify: Peer failed to verify in time.";
detach ("hvt", true); detach ("hvt");
} }
} }
@@ -795,14 +818,14 @@ private:
if ((m_state == stateHandshaked) && (type == protocol::mtHELLO)) if ((m_state == stateHandshaked) && (type == protocol::mtHELLO))
{ {
m_journal.warning << "Protocol: HELLO expected!"; m_journal.warning << "Protocol: HELLO expected!";
detach ("prb-hello-expected", true); detach ("prb-hello-expected");
return; return;
} }
if ((m_state == stateActive) && (type == protocol::mtHELLO)) if ((m_state == stateActive) && (type == protocol::mtHELLO))
{ {
m_journal.warning << "Protocol: HELLO unexpected!"; m_journal.warning << "Protocol: HELLO unexpected!";
detach ("prb-hello-unexpected", true); detach ("prb-hello-unexpected");
return; return;
} }
@@ -1307,7 +1330,7 @@ private:
{ {
bool bDetach = true; bool bDetach = true;
(void) mActivityTimer.cancel (); (void) m_timer.cancel ();
uint32 const ourTime (getApp().getOPs ().getNetworkTimeNC ()); uint32 const ourTime (getApp().getOPs ().getNetworkTimeNC ());
uint32 const minTime (ourTime - clockToleranceDeltaSeconds); uint32 const minTime (ourTime - clockToleranceDeltaSeconds);
@@ -1382,8 +1405,8 @@ private:
bassert (m_state == stateConnected); bassert (m_state == stateConnected);
m_state = stateHandshaked; m_state = stateHandshaked;
m_peerFinder.onPeerHandshake (m_remoteAddress, m_peerFinder.on_handshake (m_slot, RipplePublicKey(m_nodePublicKey),
RipplePublicKey(m_nodePublicKey), m_clusterNode); m_clusterNode);
// XXX Set timer: connection is in grace period to be useful. // XXX Set timer: connection is in grace period to be useful.
// XXX Set timer: connection idle (idle may vary depending on connection type.) // XXX Set timer: connection idle (idle may vary depending on connection type.)
@@ -1408,7 +1431,7 @@ private:
if (bDetach) if (bDetach)
{ {
m_nodePublicKey.clear (); m_nodePublicKey.clear ();
detach ("recvh", true); detach ("recvh");
} }
else else
{ {
@@ -1448,9 +1471,9 @@ private:
{ {
protocol::TMLoadSource const& node = packet.loadsources (i); protocol::TMLoadSource const& node = packet.loadsources (i);
Resource::Gossip::Item item; Resource::Gossip::Item item;
item.address = IPAddress::from_string (node.name()); item.address = IP::Endpoint::from_string (node.name());
item.balance = node.cost(); item.balance = node.cost();
if (item.address != IPAddress()) if (item.address != IP::Endpoint())
gossip.items.push_back(item); gossip.items.push_back(item);
} }
m_resourceManager.importConsumers (m_nodeName, gossip); m_resourceManager.importConsumers (m_nodeName, gossip);
@@ -1608,7 +1631,7 @@ private:
// TODO: filter out all the LAN peers // TODO: filter out all the LAN peers
void recvPeers (protocol::TMPeers& packet) void recvPeers (protocol::TMPeers& packet)
{ {
std::vector <IPAddress> list; std::vector <IP::Endpoint> list;
list.reserve (packet.nodes().size()); list.reserve (packet.nodes().size());
for (int i = 0; i < packet.nodes ().size (); ++i) for (int i = 0; i < packet.nodes ().size (); ++i)
{ {
@@ -1618,13 +1641,13 @@ private:
{ {
IP::AddressV4 v4 (ntohl (addr.s_addr)); IP::AddressV4 v4 (ntohl (addr.s_addr));
IPAddress address (v4, packet.nodes (i).ipv4port ()); IP::Endpoint address (v4, packet.nodes (i).ipv4port ());
list.push_back (address); list.push_back (address);
} }
} }
if (! list.empty()) if (! list.empty())
m_peerFinder.onLegacyEndpoints (list); m_peerFinder.on_legacy_endpoints (list);
} }
void recvEndpoints (protocol::TMEndpoints& packet) void recvEndpoints (protocol::TMEndpoints& packet)
@@ -1647,13 +1670,13 @@ private:
in_addr addr; in_addr addr;
addr.s_addr = tm.ipv4().ipv4(); addr.s_addr = tm.ipv4().ipv4();
IP::AddressV4 v4 (ntohl (addr.s_addr)); IP::AddressV4 v4 (ntohl (addr.s_addr));
endpoint.address = IPAddress (v4, tm.ipv4().ipv4port ()); endpoint.address = IP::Endpoint (v4, tm.ipv4().ipv4port ());
} }
else else
{ {
// This Endpoint describes the peer we are connected to. // This Endpoint describes the peer we are connected to.
// We will take the remote address seen on the socket and // We will take the remote address seen on the socket and
// store that in the IPAddress. If this is the first time, // store that in the IP::Endpoint. If this is the first time,
// then we'll verify that their listener can receive incoming // then we'll verify that their listener can receive incoming
// by performing a connectivity test. // by performing a connectivity test.
// //
@@ -1665,7 +1688,7 @@ private:
} }
if (! endpoints.empty()) if (! endpoints.empty())
m_peerFinder.onPeerEndpoints (m_remoteAddress, endpoints); m_peerFinder.on_endpoints (m_slot, endpoints);
} }
void recvGetObjectByHash (const boost::shared_ptr<protocol::TMGetObjectByHash>& ptr) void recvGetObjectByHash (const boost::shared_ptr<protocol::TMGetObjectByHash>& ptr)
@@ -2714,58 +2737,6 @@ void Peer::charge (boost::weak_ptr <Peer>& peer, Resource::Charge const& fee)
p->charge (fee); p->charge (fee);
} }
//------------------------------------------------------------------------------
void Peer::accept (
boost::shared_ptr <NativeSocketType> const& socket,
Peers& peers,
Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder,
boost::asio::ssl::context& ssl_context,
bool proxyHandshake)
{
MultiSocket::Flag flags (
MultiSocket::Flag::server_role | MultiSocket::Flag::ssl_required);
if (proxyHandshake)
flags = flags.with (MultiSocket::Flag::proxy);
boost::shared_ptr<PeerImp> peer (boost::make_shared <PeerImp> (
socket,
peers,
resourceManager,
peerFinder,
ssl_context,
flags));
peer->accept ();
}
//------------------------------------------------------------------------------
void Peer::connect (
IP::Endpoint const& address,
boost::asio::io_service& io_service,
Peers& peers,
Resource::Manager& resourceManager,
PeerFinder::Manager& peerFinder,
boost::asio::ssl::context& ssl_context)
{
MultiSocket::Flag flags (
MultiSocket::Flag::client_role | MultiSocket::Flag::ssl);
boost::shared_ptr<PeerImp> peer (boost::make_shared <PeerImp> (
io_service,
peers,
resourceManager,
peerFinder,
ssl_context,
flags));
peer->connect (address);
}
//------------------------------------------------------------------------------
const boost::posix_time::seconds PeerImp::nodeVerifySeconds (15); const boost::posix_time::seconds PeerImp::nodeVerifySeconds (15);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -2825,3 +2796,5 @@ std::ostream& operator<< (std::ostream& os, Peer const* peer)
} }
} }
#endif

View File

@@ -18,12 +18,16 @@
//============================================================================== //==============================================================================
#include "PeerDoor.h" #include "PeerDoor.h"
#include "PeerImp.h"
#include <boost/config.hpp> #include <boost/config.hpp>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
namespace ripple { namespace ripple {
SETUP_LOG (Peer)
class PeersLog; class PeersLog;
template <> char const* LogPartition::getPartitionName <PeersLog> () { return "Peers"; } template <> char const* LogPartition::getPartitionName <PeersLog> () { return "Peers"; }
@@ -80,7 +84,10 @@ class PeersImp
, public LeakChecked <PeersImp> , public LeakChecked <PeersImp>
{ {
public: public:
typedef boost::unordered_map <IPAddress, Peer::pointer> PeerByIP; typedef std::unordered_map <PeerFinder::Slot::ptr,
boost::weak_ptr <PeerImp>> PeersBySlot;
typedef std::unordered_map <IP::Endpoint,
boost::weak_ptr <PeerImp>> PeersByIP;
typedef boost::unordered_map < typedef boost::unordered_map <
RippleAddress, Peer::pointer> PeerByPublicKey; RippleAddress, Peer::pointer> PeerByPublicKey;
@@ -104,8 +111,8 @@ public:
boost::asio::io_service& m_io_service; boost::asio::io_service& m_io_service;
boost::asio::ssl::context& m_ssl_context; boost::asio::ssl::context& m_ssl_context;
/** Tracks peers by their IP address and port */ /** Associates slots to peers. */
PeerByIP m_ipMap; PeersBySlot m_peers;
/** Tracks peers by their public key */ /** Tracks peers by their public key */
PeerByPublicKey m_publicKeyMap; PeerByPublicKey m_publicKeyMap;
@@ -113,9 +120,6 @@ public:
/** Tracks peers by their session ID */ /** Tracks peers by their session ID */
PeerByShortId m_shortIdMap; PeerByShortId m_shortIdMap;
/** Tracks all instances of peer objects */
List <Peer> m_list;
/** The peer door for regular SSL connections */ /** The peer door for regular SSL connections */
std::unique_ptr <PeerDoor> m_doorDirect; std::unique_ptr <PeerDoor> m_doorDirect;
@@ -148,12 +152,12 @@ public:
*this, *this,
siteFiles, siteFiles,
*this, *this,
get_seconds_clock (),
LogPartition::getJournal <PeerFinderLog> ()))) LogPartition::getJournal <PeerFinderLog> ())))
, m_io_service (io_service) , m_io_service (io_service)
, m_ssl_context (ssl_context) , m_ssl_context (ssl_context)
, m_resolver (resolver) , m_resolver (resolver)
{ {
} }
~PeersImp () ~PeersImp ()
@@ -162,38 +166,86 @@ public:
// This is just to catch improper use of the Stoppable API. // This is just to catch improper use of the Stoppable API.
// //
std::unique_lock <decltype(m_mutex)> lock (m_mutex); std::unique_lock <decltype(m_mutex)> lock (m_mutex);
#ifdef BOOST_NO_CXX11_LAMBDAS
while (m_child_count != 0)
m_cond.wait (lock);
#else
m_cond.wait (lock, [this] { m_cond.wait (lock, [this] {
return this->m_child_count == 0; }); return this->m_child_count == 0; });
#endif
} }
void accept ( void accept (
bool proxyHandshake, bool proxyHandshake,
boost::shared_ptr <NativeSocketType> const& socket) boost::shared_ptr <NativeSocketType> const& socket)
{ {
Peer::accept ( // An error getting an endpoint means the connection closed.
socket, // Just do nothing and the socket will be closed by the caller.
*this, boost::system::error_code ec;
m_resourceManager, auto const local_endpoint_native (socket->local_endpoint (ec));
*m_peerFinder, if (ec)
m_ssl_context, return;
proxyHandshake); auto const remote_endpoint_native (socket->remote_endpoint (ec));
if (ec)
return;
auto const local_endpoint (
IPAddressConversion::from_asio (local_endpoint_native));
auto const remote_endpoint (
IPAddressConversion::from_asio (remote_endpoint_native));
PeerFinder::Slot::ptr const slot (m_peerFinder->new_inbound_slot (
local_endpoint, remote_endpoint));
if (slot == nullptr)
return;
MultiSocket::Flag flags (
MultiSocket::Flag::server_role | MultiSocket::Flag::ssl_required);
if (proxyHandshake)
flags = flags.with (MultiSocket::Flag::proxy);
PeerImp::ptr const peer (boost::make_shared <PeerImp> (
socket, *this, m_resourceManager, *m_peerFinder,
slot, m_ssl_context, flags));
{
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
{
std::pair <PeersBySlot::iterator, bool> const result (
m_peers.emplace (slot, peer));
assert (result.second);
}
++m_child_count;
}
// VFALCO NOTE Why not do this in the ctor?
peer->accept ();
} }
void connect (IP::Endpoint const& address) void connect (IP::Endpoint const& remote_endpoint)
{ {
Peer::connect ( PeerFinder::Slot::ptr const slot (
address, m_peerFinder->new_outbound_slot (remote_endpoint));
m_io_service,
*this, if (slot == nullptr)
m_resourceManager, return;
*m_peerFinder,
m_ssl_context); MultiSocket::Flag const flags (
MultiSocket::Flag::client_role | MultiSocket::Flag::ssl);
PeerImp::ptr const peer (boost::make_shared <PeerImp> (
m_io_service, *this, m_resourceManager, *m_peerFinder,
slot, m_ssl_context, flags));
{
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
{
std::pair <PeersBySlot::iterator, bool> const result (
m_peers.emplace (slot, peer));
assert (result.second);
}
++m_child_count;
}
// VFALCO NOTE Why not do this in the ctor?
peer->connect (remote_endpoint);
} }
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
@@ -207,17 +259,12 @@ public:
if (areChildrenStopped () && m_child_count == 0) if (areChildrenStopped () && m_child_count == 0)
{ {
m_cond.notify_all (); m_cond.notify_all ();
m_journal.info <<
"Stopped.";
stopped (); stopped ();
} }
} }
// Increment the count of dependent objects
// Caller must hold the mutex
void addref ()
{
++m_child_count;
}
// Decrement the count of dependent objects // Decrement the count of dependent objects
// Caller must hold the mutex // Caller must hold the mutex
void release () void release ()
@@ -226,17 +273,14 @@ public:
check_stopped (); check_stopped ();
} }
void peerCreated (Peer* peer) void remove (PeerFinder::Slot::ptr const& slot)
{ {
std::lock_guard <decltype(m_mutex)> lock (m_mutex); std::lock_guard <decltype(m_mutex)> lock (m_mutex);
m_list.push_back (*peer);
addref();
}
void peerDestroyed (Peer* peer) PeersBySlot::iterator const iter (m_peers.find (slot));
{ assert (iter != m_peers.end ());
std::lock_guard <decltype(m_mutex)> lock (m_mutex); m_peers.erase (iter);
m_list.erase (m_list.iterator_to (*peer));
release(); release();
} }
@@ -246,41 +290,28 @@ public:
// //
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
void connectPeers (std::vector <IPAddress> const& list) void connect (std::vector <IP::Endpoint> const& list)
{ {
for (std::vector <IPAddress>::const_iterator iter (list.begin()); for (std::vector <IP::Endpoint>::const_iterator iter (list.begin());
iter != list.end(); ++iter) iter != list.end(); ++iter)
connect (*iter); connect (*iter);
} }
void disconnectPeer (IPAddress const& address, bool graceful) void activate (PeerFinder::Slot::ptr const& slot)
{ {
m_journal.trace << m_journal.trace <<
"disconnectPeer (" << address << "Activate " << slot->remote_endpoint();
", " << graceful << ")";
std::lock_guard <decltype(m_mutex)> lock (m_mutex); std::lock_guard <decltype(m_mutex)> lock (m_mutex);
PeerByIP::iterator const it (m_ipMap.find (address)); PeersBySlot::iterator const iter (m_peers.find (slot));
assert (iter != m_peers.end ());
if (it != m_ipMap.end ()) PeerImp::ptr const peer (iter->second.lock());
it->second->detach ("disc", false); assert (peer != nullptr);
peer->activate ();
} }
void activatePeer (IPAddress const& remote_address) void send (PeerFinder::Slot::ptr const& slot,
{
m_journal.trace <<
"activatePeer (" << remote_address << ")";
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
PeerByIP::iterator const it (m_ipMap.find (remote_address));
if (it != m_ipMap.end ())
it->second->activate();
}
void sendEndpoints (IPAddress const& remote_address,
std::vector <PeerFinder::Endpoint> const& endpoints) std::vector <PeerFinder::Endpoint> const& endpoints)
{ {
bassert (! endpoints.empty()); bassert (! endpoints.empty());
@@ -309,16 +340,33 @@ public:
{ {
std::lock_guard <decltype(m_mutex)> lock (m_mutex); std::lock_guard <decltype(m_mutex)> lock (m_mutex);
PeerByIP::iterator const iter (m_ipMap.find (remote_address)); PeersBySlot::iterator const iter (m_peers.find (slot));
// Address must exist! assert (iter != m_peers.end ());
check_postcondition (iter != m_ipMap.end()); PeerImp::ptr const peer (iter->second.lock());
Peer::pointer peer (iter->second); assert (peer != nullptr);
// VFALCO TODO Why are we checking isConnected? That should not be needed // VFALCO TODO Why are we checking isConnected?
// That should not be needed
if (peer->isConnected()) if (peer->isConnected())
peer->sendPacket (msg, false); peer->sendPacket (msg, false);
} }
} }
void disconnect (PeerFinder::Slot::ptr const& slot, bool graceful)
{
if (m_journal.trace) m_journal.trace <<
"Disconnect " << slot->remote_endpoint () <<
(graceful ? "gracefully" : "");
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
PeersBySlot::iterator const iter (m_peers.find (slot));
assert (iter != m_peers.end ());
PeerImp::ptr const peer (iter->second.lock());
assert (peer != nullptr);
peer->close (graceful);
//peer->detach ("disc", false);
}
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// //
// Stoppable // Stoppable
@@ -329,7 +377,8 @@ public:
{ {
PeerFinder::Config config; PeerFinder::Config config;
config.maxPeers = getConfig ().PEERS_MAX; if (getConfig ().PEERS_MAX != 0)
config.maxPeers = getConfig ().PEERS_MAX;
config.outPeers = config.calcOutPeers(); config.outPeers = config.calcOutPeers();
@@ -367,7 +416,7 @@ public:
{ } { }
void operator()(std::string const& name, void operator()(std::string const& name,
std::vector <IPAddress> const& address) std::vector <IP::Endpoint> const& address)
{ {
if (!address.empty()) if (!address.empty())
m_peerFinder->addFixedPeer (name, address); m_peerFinder->addFixedPeer (name, address);
@@ -404,19 +453,19 @@ public:
{ {
} }
// Close all peer connections. If graceful is true then the peer objects /** Close all peer connections.
// will wait for pending i/o before closing the socket. No new data will If `graceful` is true then active
// be sent. Requirements:
// Caller must hold the mutex.
// The caller must hold the mutex */
//
// VFALCO TODO implement the graceful flag
//
void close_all (bool graceful) void close_all (bool graceful)
{ {
for (List <Peer>::iterator iter (m_list.begin ()); for (auto entry : m_peers)
iter != m_list.end(); ++iter) {
iter->detach ("stop", false); PeerImp::ptr const peer (entry.second.lock());
assert (peer != nullptr);
peer->close (graceful);
}
} }
void onStop () void onStop ()
@@ -424,8 +473,8 @@ public:
std::lock_guard <decltype(m_mutex)> lock (m_mutex); std::lock_guard <decltype(m_mutex)> lock (m_mutex);
// Take off the extra count we added in the constructor // Take off the extra count we added in the constructor
release(); release();
// Close all peers
close_all (true); close_all (false);
} }
void onChildrenStopped () void onChildrenStopped ()
@@ -535,33 +584,6 @@ public:
iter->second; iter->second;
return Peer::pointer(); return Peer::pointer();
} }
// TODO NIKB Rename these two functions. It's not immediately clear
// what they do: create a tracking entry for a peer by
// the peer's remote IP.
/** Start tracking a peer */
void addPeer (Peer::Ptr const& peer)
{
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
check_precondition (! isStopping ());
m_journal.error << "Adding peer: " << peer->getRemoteAddress();
std::pair <PeerByIP::iterator, bool> result (m_ipMap.emplace (
boost::unordered::piecewise_construct,
boost::make_tuple (peer->getRemoteAddress()),
boost::make_tuple (peer)));
check_postcondition (result.second);
}
/** Stop tracking a peer */
void removePeer (Peer::Ptr const& peer)
{
std::lock_guard <decltype(m_mutex)> lock (m_mutex);
m_ipMap.erase (peer->getRemoteAddress());
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -20,6 +20,9 @@
#ifndef RIPPLE_PEERS_H_INCLUDED #ifndef RIPPLE_PEERS_H_INCLUDED
#define RIPPLE_PEERS_H_INCLUDED #define RIPPLE_PEERS_H_INCLUDED
// VFALCO TODO Remove this include dependency it shouldn't be needed
#include "../../ripple/peerfinder/api/Slot.h"
namespace ripple { namespace ripple {
namespace PeerFinder { namespace PeerFinder {
@@ -66,14 +69,10 @@ public:
virtual ~Peers () = 0; virtual ~Peers () = 0;
// NIKB TODO This is an implementation detail - a private // VFALCO NOTE These should be a private API
// interface between Peers and Peer. It should /** @{ */
// be split out and moved elsewhere. virtual void remove (PeerFinder::Slot::ptr const& slot) = 0;
// /** @} */
// VFALCO NOTE PeerImp should have visbility to PeersImp
//
virtual void peerCreated (Peer* peer) = 0;
virtual void peerDestroyed (Peer *peer) = 0;
virtual void accept (bool proxyHandshake, virtual void accept (bool proxyHandshake,
boost::shared_ptr <NativeSocketType> const& socket) = 0; boost::shared_ptr <NativeSocketType> const& socket) = 0;
@@ -93,9 +92,6 @@ public:
// Peer 64-bit ID function // Peer 64-bit ID function
virtual Peer::pointer findPeerByShortID (Peer::ShortId const& id) = 0; virtual Peer::pointer findPeerByShortID (Peer::ShortId const& id) = 0;
virtual void addPeer (Peer::Ptr const& peer) = 0;
virtual void removePeer (Peer::Ptr const& peer) = 0;
/** Visit every active peer and return a value /** Visit every active peer and return a value
The functor must: The functor must:
- Be callable as: - Be callable as:

View File

@@ -46,6 +46,5 @@ namespace ripple {
#include "peers/PackedMessage.cpp" #include "peers/PackedMessage.cpp"
} }
#include "peers/Peer.cpp"
#include "peers/PeerDoor.cpp" #include "peers/PeerDoor.cpp"
#include "peers/Peers.cpp" #include "peers/Peers.cpp"