Rearrange sources (#4997)

This commit is contained in:
Pretty Printer
2024-06-20 09:22:15 -05:00
committed by John Freeman
parent 2e902dee53
commit e416ee72ca
994 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_CHARGE_H_INCLUDED
#define RIPPLE_RESOURCE_CHARGE_H_INCLUDED
#include <ios>
#include <string>
namespace ripple {
namespace Resource {
/** A consumption charge. */
class Charge
{
public:
/** The type used to hold a consumption charge. */
using value_type = int;
// A default constructed Charge has no way to get a label. Delete
Charge() = delete;
/** Create a charge with the specified cost and name. */
Charge(value_type cost, std::string const& label = std::string());
/** Return the human readable label associated with the charge. */
std::string const&
label() const;
/** Return the cost of the charge in Resource::Manager units. */
value_type
cost() const;
/** Converts this charge into a human readable string. */
std::string
to_string() const;
bool
operator==(Charge const&) const;
bool
operator!=(Charge const&) const;
private:
value_type m_cost;
std::string m_label;
};
std::ostream&
operator<<(std::ostream& os, Charge const& v);
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,101 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_CONSUMER_H_INCLUDED
#define RIPPLE_RESOURCE_CONSUMER_H_INCLUDED
#include <ripple/basics/Log.h>
#include <ripple/resource/Charge.h>
#include <ripple/resource/Disposition.h>
namespace ripple {
namespace Resource {
struct Entry;
class Logic;
/** An endpoint that consumes resources. */
class Consumer
{
private:
friend class Logic;
Consumer(Logic& logic, Entry& entry);
public:
Consumer();
~Consumer();
Consumer(Consumer const& other);
Consumer&
operator=(Consumer const& other);
/** Return a human readable string uniquely identifying this consumer. */
std::string
to_string() const;
/** Returns `true` if this is a privileged endpoint. */
bool
isUnlimited() const;
/** Raise the Consumer's privilege level to a Named endpoint.
The reference to the original endpoint descriptor is released.
*/
void
elevate(std::string const& name);
/** Returns the current disposition of this consumer.
This should be checked upon creation to determine if the consumer
should be disconnected immediately.
*/
Disposition
disposition() const;
/** Apply a load charge to the consumer. */
Disposition
charge(Charge const& fee);
/** Returns `true` if the consumer should be warned.
This consumes the warning.
*/
bool
warn();
/** Returns `true` if the consumer should be disconnected. */
bool
disconnect(beast::Journal const& j);
/** Returns the credit balance representing consumption. */
int
balance();
// Private: Retrieve the entry associated with the consumer
Entry&
entry();
private:
Logic* m_logic;
Entry* m_entry;
};
std::ostream&
operator<<(std::ostream& os, Consumer const& v);
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_DISPOSITION_H_INCLUDED
#define RIPPLE_RESOURCE_DISPOSITION_H_INCLUDED
namespace ripple {
namespace Resource {
/** The disposition of a consumer after applying a load charge. */
enum Disposition {
/** No action required. */
ok
/** Consumer should be warned that consumption is high. */
,
warn
/** Consumer should be disconnected for excess consumption. */
,
drop
};
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,69 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_FEES_H_INCLUDED
#define RIPPLE_RESOURCE_FEES_H_INCLUDED
#include <ripple/resource/Charge.h>
namespace ripple {
namespace Resource {
/** Schedule of fees charged for imposing load on the server. */
/** @{ */
extern Charge const
feeInvalidRequest; // A request that we can immediately tell is invalid
extern Charge const feeRequestNoReply; // A request that we cannot satisfy
extern Charge const feeInvalidSignature; // An object whose signature we had to
// check and it failed
extern Charge const feeUnwantedData; // Data we have no use for
extern Charge const feeBadData; // Data we have to verify before rejecting
// RPC loads
extern Charge const
feeInvalidRPC; // An RPC request that we can immediately tell is invalid.
extern Charge const feeReferenceRPC; // A default "reference" unspecified load
extern Charge const feeExceptionRPC; // An RPC load that causes an exception
extern Charge const feeLightRPC; // A normal RPC command
extern Charge const feeLowBurdenRPC; // A slightly burdensome RPC load
extern Charge const feeMediumBurdenRPC; // A somewhat burdensome RPC load
extern Charge const feeHighBurdenRPC; // A very burdensome RPC load
extern Charge const feePathFindUpdate; // An update to an existing PF request
// Peer loads
extern Charge const feeLightPeer; // Requires no reply
extern Charge const feeLowBurdenPeer; // Quick/cheap, slight reply
extern Charge const feeMediumBurdenPeer; // Requires some work
extern Charge const feeHighBurdenPeer; // Extensive work
// Good things
extern Charge const
feeNewTrustedNote; // A new transaction/validation/proposal we trust
extern Charge const feeNewValidTx; // A new, valid transaction
extern Charge const feeSatisfiedRequest; // Data we requested
// Administrative
extern Charge const feeWarning; // The cost of receiving a warning
extern Charge const feeDrop; // The cost of being dropped for excess load
/** @} */
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,48 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_GOSSIP_H_INCLUDED
#define RIPPLE_RESOURCE_GOSSIP_H_INCLUDED
#include <ripple/beast/net/IPEndpoint.h>
namespace ripple {
namespace Resource {
/** Data format for exchanging consumption information across peers. */
struct Gossip
{
explicit Gossip() = default;
/** Describes a single consumer. */
struct Item
{
explicit Item() = default;
int balance;
beast::IP::Endpoint address;
};
std::vector<Item> items;
};
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,77 @@
# Resource::Manager #
The ResourceManager module has these responsibilities:
- Uniquely identify endpoints which impose load.
- Track the load used across endpoints.
- Provide an interface to share load information in a cluster.
- Warn and/or disconnect endpoints for imposing load.
## Description ##
To prevent monopolization of server resources or attacks on servers,
resource consumption is monitored at each endpoint. When consumption
exceeds certain thresholds, costs are imposed. Costs could include charging
additional XRP for transactions, requiring a proof of work to be
performed, or simply disconnecting the endpoint.
Currently, consumption endpoints include websocket connections used to
service clients, and peer connections used to create the peer to peer
overlay network implementing the Ripple protocol.
The current "balance" of a Consumer represents resource consumption
debt or credit. Debt is accrued when bad loads are imposed. Credit is
granted when good loads are imposed. When the balance crosses heuristic
thresholds, costs are increased on the endpoint. The balance is
represented as a unitless relative quantity. This balance is currently
held by the Entry struct in the impl/Entry.h file.
Costs associated with specific transactions are defined in the
impl/Fees files.
Although RPC connections consume resources, they are transient and
cannot be rate limited. It is advised not to expose RPC interfaces
to the general public.
## Consumer Types ##
Consumers are placed into three classifications (as identified by the
Resource::Kind enumeration):
- InBound,
- OutBound, and
- Admin
Each caller determines for itself the classification of the Consumer it is
creating.
## Resource Loading ##
It is expected that a client will impose a higher load on the server
when it first connects: the client may need to catch up on transactions
it has missed, or get trust lines, or transfer fees. The Manager must
expect this initial peak load, but not allow that high load to continue
because over the long term that would unduly stress the server.
If a client places a sustained high load on the server, that client
is initially given a warning message. If that high load continues
the Manager may tell the heavily loaded server to drop the connection
entirely and not allow re-connection for some amount of time.
Each load is monitored by capturing peaks and then decaying those peak
values over time: this is implemented by the DecayingSample class.
## Gossip ##
Each server in a cluster creates a list of IP addresses of end points
that are imposing a significant load. This list is called Gossip, which
is passed to other nodes in that cluster. Gossip helps individual
servers in the cluster identify IP addreses that might be unduly loading
the entire cluster. Again the recourse of the individual servers is to
drop connections to those IP addresses that occur commonly in the gossip.
## Access ##
In rippled, the Application holds a unique instance of Resource::Manager,
which may be retrieved by calling the method
`Application::getResourceManager()`.

View File

@@ -0,0 +1,89 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_MANAGER_H_INCLUDED
#define RIPPLE_RESOURCE_MANAGER_H_INCLUDED
#include <ripple/beast/insight/Collector.h>
#include <ripple/beast/net/IPEndpoint.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/beast/utility/PropertyStream.h>
#include <ripple/json/json_value.h>
#include <ripple/resource/Consumer.h>
#include <ripple/resource/Gossip.h>
#include <boost/utility/string_view.hpp>
namespace ripple {
namespace Resource {
/** Tracks load and resource consumption. */
class Manager : public beast::PropertyStream::Source
{
protected:
Manager();
public:
virtual ~Manager() = 0;
/** Create a new endpoint keyed by inbound IP address or the forwarded
* IP if proxied. */
virtual Consumer
newInboundEndpoint(beast::IP::Endpoint const& address) = 0;
virtual Consumer
newInboundEndpoint(
beast::IP::Endpoint const& address,
bool const proxy,
std::string_view forwardedFor) = 0;
/** Create a new endpoint keyed by outbound IP address and port. */
virtual Consumer
newOutboundEndpoint(beast::IP::Endpoint const& address) = 0;
/** Create a new unlimited endpoint keyed by forwarded IP. */
virtual Consumer
newUnlimitedEndpoint(beast::IP::Endpoint const& address) = 0;
/** Extract packaged consumer information for export. */
virtual Gossip
exportConsumers() = 0;
/** Extract consumer information for reporting. */
virtual Json::Value
getJson() = 0;
virtual Json::Value
getJson(int threshold) = 0;
/** Import packaged consumer information.
@param origin An identifier that unique labels the origin.
*/
virtual void
importConsumers(std::string const& origin, Gossip const& gossip) = 0;
};
//------------------------------------------------------------------------------
std::unique_ptr<Manager>
make_Manager(
beast::insight::Collector::ptr const& collector,
beast::Journal journal);
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_TYPES_H_INCLUDED
#define RIPPLE_RESOURCE_TYPES_H_INCLUDED
namespace ripple {
namespace Resource {
struct Key;
struct Entry;
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,114 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_ENTRY_H_INCLUDED
#define RIPPLE_RESOURCE_ENTRY_H_INCLUDED
#include <ripple/basics/DecayingSample.h>
#include <ripple/beast/clock/abstract_clock.h>
#include <ripple/beast/core/List.h>
#include <ripple/resource/impl/Key.h>
#include <ripple/resource/impl/Tuning.h>
#include <cassert>
namespace ripple {
namespace Resource {
using clock_type = beast::abstract_clock<std::chrono::steady_clock>;
// An entry in the table
// VFALCO DEPRECATED using boost::intrusive list
struct Entry : public beast::List<Entry>::Node
{
Entry() = delete;
/**
@param now Construction time of Entry.
*/
explicit Entry(clock_type::time_point const now)
: refcount(0)
, local_balance(now)
, remote_balance(0)
, lastWarningTime()
, whenExpires()
{
}
std::string
to_string() const
{
return key->address.to_string();
}
/**
* Returns `true` if this connection should have no
* resource limits applied--it is still possible for certain RPC commands
* to be forbidden, but that depends on Role.
*/
bool
isUnlimited() const
{
return key->kind == kindUnlimited;
}
// Balance including remote contributions
int
balance(clock_type::time_point const now)
{
return local_balance.value(now) + remote_balance;
}
// Add a charge and return normalized balance
// including contributions from imports.
int
add(int charge, clock_type::time_point const now)
{
return local_balance.add(charge, now) + remote_balance;
}
// Back pointer to the map key (bit of a hack here)
Key const* key;
// Number of Consumer references
int refcount;
// Exponentially decaying balance of resource consumption
DecayingSample<decayWindowSeconds, clock_type> local_balance;
// Normalized balance contribution from imports
int remote_balance;
// Time of the last warning
clock_type::time_point lastWarningTime;
// For inactive entries, time after which this entry will be erased
clock_type::time_point whenExpires;
};
inline std::ostream&
operator<<(std::ostream& os, Entry const& v)
{
os << v.to_string();
return os;
}
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,55 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_IMPORT_H_INCLUDED
#define RIPPLE_RESOURCE_IMPORT_H_INCLUDED
#include <ripple/resource/Consumer.h>
#include <ripple/resource/impl/Entry.h>
namespace ripple {
namespace Resource {
/** A set of imported consumer data from a gossip origin. */
struct Import
{
struct Item
{
explicit Item() = default;
int balance;
Consumer consumer;
};
// Dummy argument required for zero-copy construction
Import(int = 0) : whenExpires()
{
}
// When the imported data expires
clock_type::time_point whenExpires;
// List of remote entries
std::vector<Item> items;
};
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_KEY_H_INCLUDED
#define RIPPLE_RESOURCE_KEY_H_INCLUDED
#include <ripple/beast/net/IPEndpoint.h>
#include <ripple/resource/impl/Kind.h>
#include <cassert>
namespace ripple {
namespace Resource {
// The consumer key
struct Key
{
Kind kind;
beast::IP::Endpoint address;
Key() = delete;
Key(Kind k, beast::IP::Endpoint const& addr) : kind(k), address(addr)
{
}
struct hasher
{
std::size_t
operator()(Key const& v) const
{
return m_addr_hash(v.address);
}
private:
beast::uhash<> m_addr_hash;
};
struct key_equal
{
explicit key_equal() = default;
bool
operator()(Key const& lhs, Key const& rhs) const
{
return lhs.kind == rhs.kind && lhs.address == rhs.address;
}
private:
};
};
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,39 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_KIND_H_INCLUDED
#define RIPPLE_RESOURCE_KIND_H_INCLUDED
namespace ripple {
namespace Resource {
/**
* Kind of consumer.
* kindInbound: Inbound connection.
* kindOutbound: Outbound connection.
* kindUnlimited: Inbound connection with no resource limits, but could be
* subjected to administrative restrictions, such as
* use of some RPC commands like "stop".
*/
enum Kind { kindInbound, kindOutbound, kindUnlimited };
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,563 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_LOGIC_H_INCLUDED
#define RIPPLE_RESOURCE_LOGIC_H_INCLUDED
#include <ripple/basics/Log.h>
#include <ripple/basics/UnorderedContainers.h>
#include <ripple/basics/chrono.h>
#include <ripple/beast/clock/abstract_clock.h>
#include <ripple/beast/insight/Insight.h>
#include <ripple/beast/utility/PropertyStream.h>
#include <ripple/json/json_value.h>
#include <ripple/protocol/jss.h>
#include <ripple/resource/Fees.h>
#include <ripple/resource/Gossip.h>
#include <ripple/resource/impl/Import.h>
#include <cassert>
#include <mutex>
namespace ripple {
namespace Resource {
class Logic
{
private:
using clock_type = Stopwatch;
using Imports = hash_map<std::string, Import>;
using Table = hash_map<Key, Entry, Key::hasher, Key::key_equal>;
using EntryIntrusiveList = beast::List<Entry>;
struct Stats
{
Stats(beast::insight::Collector::ptr const& collector)
{
warn = collector->make_meter("warn");
drop = collector->make_meter("drop");
}
beast::insight::Meter warn;
beast::insight::Meter drop;
};
Stats m_stats;
Stopwatch& m_clock;
beast::Journal m_journal;
std::recursive_mutex lock_;
// Table of all entries
Table table_;
// Because the following are intrusive lists, a given Entry may be in
// at most list at a given instant. The Entry must be removed from
// one list before placing it in another.
// List of all active inbound entries
EntryIntrusiveList inbound_;
// List of all active outbound entries
EntryIntrusiveList outbound_;
// List of all active admin entries
EntryIntrusiveList admin_;
// List of all inactve entries
EntryIntrusiveList inactive_;
// All imported gossip data
Imports importTable_;
//--------------------------------------------------------------------------
public:
Logic(
beast::insight::Collector::ptr const& collector,
clock_type& clock,
beast::Journal journal)
: m_stats(collector), m_clock(clock), m_journal(journal)
{
}
~Logic()
{
// These have to be cleared before the Logic is destroyed
// since their destructors call back into the class.
// Order matters here as well, the import table has to be
// destroyed before the consumer table.
//
importTable_.clear();
table_.clear();
}
Consumer
newInboundEndpoint(beast::IP::Endpoint const& address)
{
Entry* entry(nullptr);
{
std::lock_guard _(lock_);
auto [resultIt, resultInserted] = table_.emplace(
std::piecewise_construct,
std::make_tuple(kindInbound, address.at_port(0)), // Key
std::make_tuple(m_clock.now())); // Entry
entry = &resultIt->second;
entry->key = &resultIt->first;
++entry->refcount;
if (entry->refcount == 1)
{
if (!resultInserted)
{
inactive_.erase(inactive_.iterator_to(*entry));
}
inbound_.push_back(*entry);
}
}
JLOG(m_journal.debug()) << "New inbound endpoint " << *entry;
return Consumer(*this, *entry);
}
Consumer
newOutboundEndpoint(beast::IP::Endpoint const& address)
{
Entry* entry(nullptr);
{
std::lock_guard _(lock_);
auto [resultIt, resultInserted] = table_.emplace(
std::piecewise_construct,
std::make_tuple(kindOutbound, address), // Key
std::make_tuple(m_clock.now())); // Entry
entry = &resultIt->second;
entry->key = &resultIt->first;
++entry->refcount;
if (entry->refcount == 1)
{
if (!resultInserted)
inactive_.erase(inactive_.iterator_to(*entry));
outbound_.push_back(*entry);
}
}
JLOG(m_journal.debug()) << "New outbound endpoint " << *entry;
return Consumer(*this, *entry);
}
/**
* Create endpoint that should not have resource limits applied. Other
* restrictions, such as permission to perform certain RPC calls, may be
* enabled.
*/
Consumer
newUnlimitedEndpoint(beast::IP::Endpoint const& address)
{
Entry* entry(nullptr);
{
std::lock_guard _(lock_);
auto [resultIt, resultInserted] = table_.emplace(
std::piecewise_construct,
std::make_tuple(kindUnlimited, address.at_port(1)), // Key
std::make_tuple(m_clock.now())); // Entry
entry = &resultIt->second;
entry->key = &resultIt->first;
++entry->refcount;
if (entry->refcount == 1)
{
if (!resultInserted)
inactive_.erase(inactive_.iterator_to(*entry));
admin_.push_back(*entry);
}
}
JLOG(m_journal.debug()) << "New unlimited endpoint " << *entry;
return Consumer(*this, *entry);
}
Json::Value
getJson()
{
return getJson(warningThreshold);
}
/** Returns a Json::objectValue. */
Json::Value
getJson(int threshold)
{
clock_type::time_point const now(m_clock.now());
Json::Value ret(Json::objectValue);
std::lock_guard _(lock_);
for (auto& inboundEntry : inbound_)
{
int localBalance = inboundEntry.local_balance.value(now);
if ((localBalance + inboundEntry.remote_balance) >= threshold)
{
Json::Value& entry =
(ret[inboundEntry.to_string()] = Json::objectValue);
entry[jss::local] = localBalance;
entry[jss::remote] = inboundEntry.remote_balance;
entry[jss::type] = "inbound";
}
}
for (auto& outboundEntry : outbound_)
{
int localBalance = outboundEntry.local_balance.value(now);
if ((localBalance + outboundEntry.remote_balance) >= threshold)
{
Json::Value& entry =
(ret[outboundEntry.to_string()] = Json::objectValue);
entry[jss::local] = localBalance;
entry[jss::remote] = outboundEntry.remote_balance;
entry[jss::type] = "outbound";
}
}
for (auto& adminEntry : admin_)
{
int localBalance = adminEntry.local_balance.value(now);
if ((localBalance + adminEntry.remote_balance) >= threshold)
{
Json::Value& entry =
(ret[adminEntry.to_string()] = Json::objectValue);
entry[jss::local] = localBalance;
entry[jss::remote] = adminEntry.remote_balance;
entry[jss::type] = "admin";
}
}
return ret;
}
Gossip
exportConsumers()
{
clock_type::time_point const now(m_clock.now());
Gossip gossip;
std::lock_guard _(lock_);
gossip.items.reserve(inbound_.size());
for (auto& inboundEntry : inbound_)
{
Gossip::Item item;
item.balance = inboundEntry.local_balance.value(now);
if (item.balance >= minimumGossipBalance)
{
item.address = inboundEntry.key->address;
gossip.items.push_back(item);
}
}
return gossip;
}
//--------------------------------------------------------------------------
void
importConsumers(std::string const& origin, Gossip const& gossip)
{
auto const elapsed = m_clock.now();
{
std::lock_guard _(lock_);
auto [resultIt, resultInserted] = importTable_.emplace(
std::piecewise_construct,
std::make_tuple(origin), // Key
std::make_tuple(
m_clock.now().time_since_epoch().count())); // Import
if (resultInserted)
{
// This is a new import
Import& next(resultIt->second);
next.whenExpires = elapsed + gossipExpirationSeconds;
next.items.reserve(gossip.items.size());
for (auto const& gossipItem : gossip.items)
{
Import::Item item;
item.balance = gossipItem.balance;
item.consumer = newInboundEndpoint(gossipItem.address);
item.consumer.entry().remote_balance += item.balance;
next.items.push_back(item);
}
}
else
{
// Previous import exists so add the new remote
// balances and then deduct the old remote balances.
Import next;
next.whenExpires = elapsed + gossipExpirationSeconds;
next.items.reserve(gossip.items.size());
for (auto const& gossipItem : gossip.items)
{
Import::Item item;
item.balance = gossipItem.balance;
item.consumer = newInboundEndpoint(gossipItem.address);
item.consumer.entry().remote_balance += item.balance;
next.items.push_back(item);
}
Import& prev(resultIt->second);
for (auto& item : prev.items)
{
item.consumer.entry().remote_balance -= item.balance;
}
std::swap(next, prev);
}
}
}
//--------------------------------------------------------------------------
// Called periodically to expire entries and groom the table.
//
void
periodicActivity()
{
std::lock_guard _(lock_);
auto const elapsed = m_clock.now();
for (auto iter(inactive_.begin()); iter != inactive_.end();)
{
if (iter->whenExpires <= elapsed)
{
JLOG(m_journal.debug()) << "Expired " << *iter;
auto table_iter = table_.find(*iter->key);
++iter;
erase(table_iter);
}
else
{
break;
}
}
auto iter = importTable_.begin();
while (iter != importTable_.end())
{
Import& import(iter->second);
if (iter->second.whenExpires <= elapsed)
{
for (auto item_iter(import.items.begin());
item_iter != import.items.end();
++item_iter)
{
item_iter->consumer.entry().remote_balance -=
item_iter->balance;
}
iter = importTable_.erase(iter);
}
else
++iter;
}
}
//--------------------------------------------------------------------------
// Returns the disposition based on the balance and thresholds
static Disposition
disposition(int balance)
{
if (balance >= dropThreshold)
return Disposition::drop;
if (balance >= warningThreshold)
return Disposition::warn;
return Disposition::ok;
}
void
erase(Table::iterator iter)
{
std::lock_guard _(lock_);
Entry& entry(iter->second);
assert(entry.refcount == 0);
inactive_.erase(inactive_.iterator_to(entry));
table_.erase(iter);
}
void
acquire(Entry& entry)
{
std::lock_guard _(lock_);
++entry.refcount;
}
void
release(Entry& entry)
{
std::lock_guard _(lock_);
if (--entry.refcount == 0)
{
JLOG(m_journal.debug()) << "Inactive " << entry;
switch (entry.key->kind)
{
case kindInbound:
inbound_.erase(inbound_.iterator_to(entry));
break;
case kindOutbound:
outbound_.erase(outbound_.iterator_to(entry));
break;
case kindUnlimited:
admin_.erase(admin_.iterator_to(entry));
break;
default:
assert(false);
break;
}
inactive_.push_back(entry);
entry.whenExpires = m_clock.now() + secondsUntilExpiration;
}
}
Disposition
charge(Entry& entry, Charge const& fee)
{
std::lock_guard _(lock_);
clock_type::time_point const now(m_clock.now());
int const balance(entry.add(fee.cost(), now));
JLOG(m_journal.trace()) << "Charging " << entry << " for " << fee;
return disposition(balance);
}
bool
warn(Entry& entry)
{
if (entry.isUnlimited())
return false;
std::lock_guard _(lock_);
bool notify(false);
auto const elapsed = m_clock.now();
if (entry.balance(m_clock.now()) >= warningThreshold &&
elapsed != entry.lastWarningTime)
{
charge(entry, feeWarning);
notify = true;
entry.lastWarningTime = elapsed;
}
if (notify)
{
JLOG(m_journal.info()) << "Load warning: " << entry;
++m_stats.warn;
}
return notify;
}
bool
disconnect(Entry& entry)
{
if (entry.isUnlimited())
return false;
std::lock_guard _(lock_);
bool drop(false);
clock_type::time_point const now(m_clock.now());
int const balance(entry.balance(now));
if (balance >= dropThreshold)
{
JLOG(m_journal.warn())
<< "Consumer entry " << entry << " dropped with balance "
<< balance << " at or above drop threshold " << dropThreshold;
// Adding feeDrop at this point keeps the dropped connection
// from re-connecting for at least a little while after it is
// dropped.
charge(entry, feeDrop);
++m_stats.drop;
drop = true;
}
return drop;
}
int
balance(Entry& entry)
{
std::lock_guard _(lock_);
return entry.balance(m_clock.now());
}
//--------------------------------------------------------------------------
void
writeList(
clock_type::time_point const now,
beast::PropertyStream::Set& items,
EntryIntrusiveList& list)
{
for (auto& entry : list)
{
beast::PropertyStream::Map item(items);
if (entry.refcount != 0)
item["count"] = entry.refcount;
item["name"] = entry.to_string();
item["balance"] = entry.balance(now);
if (entry.remote_balance != 0)
item["remote_balance"] = entry.remote_balance;
}
}
void
onWrite(beast::PropertyStream::Map& map)
{
clock_type::time_point const now(m_clock.now());
std::lock_guard _(lock_);
{
beast::PropertyStream::Set s("inbound", map);
writeList(now, s, inbound_);
}
{
beast::PropertyStream::Set s("outbound", map);
writeList(now, s, outbound_);
}
{
beast::PropertyStream::Set s("admin", map);
writeList(now, s, admin_);
}
{
beast::PropertyStream::Set s("inactive", map);
writeList(now, s, inactive_);
}
}
};
} // namespace Resource
} // namespace ripple
#endif

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
/*
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_RESOURCE_TUNING_H_INCLUDED
#define RIPPLE_RESOURCE_TUNING_H_INCLUDED
#include <chrono>
namespace ripple {
namespace Resource {
/** Tunable constants. */
enum {
// Balance at which a warning is issued
warningThreshold = 5000
// Balance at which the consumer is disconnected
,
dropThreshold = 15000
// The number of seconds in the exponential decay window
// (This should be a power of two)
,
decayWindowSeconds = 32
// The minimum balance required in order to include a load source in gossip
,
minimumGossipBalance = 1000
};
// The number of seconds until an inactive table item is removed
std::chrono::seconds constexpr secondsUntilExpiration{300};
// Number of seconds until imported gossip expires
std::chrono::seconds constexpr gossipExpirationSeconds{30};
} // namespace Resource
} // namespace ripple
#endif