mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
13
js/amount.js
13
js/amount.js
@@ -136,8 +136,8 @@ UInt160.json_rewrite = function (j) {
|
||||
// Return a new UInt160 from j.
|
||||
UInt160.from_json = function (j) {
|
||||
return 'string' === typeof j
|
||||
? (new UInt160()).parse_json(j)
|
||||
: j.clone();
|
||||
? (new UInt160()).parse_json(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
UInt160.prototype.clone = function() {
|
||||
@@ -232,8 +232,15 @@ var Currency = function () {
|
||||
this.value = NaN;
|
||||
}
|
||||
|
||||
// Given "USD" return the json.
|
||||
Currency.json_rewrite = function(j) {
|
||||
return Currency.from_json(j).to_json();
|
||||
};
|
||||
|
||||
Currency.from_json = function (j) {
|
||||
return (new Currency()).parse_json(j);
|
||||
return 'string' === typeof j
|
||||
? (new Currency()).parse_json(j)
|
||||
: j.clone();
|
||||
};
|
||||
|
||||
Currency.prototype.clone = function() {
|
||||
|
||||
59
js/remote.js
59
js/remote.js
@@ -914,6 +914,17 @@ Transaction.prototype.submit = function () {
|
||||
var self = this;
|
||||
var transaction = this.transaction;
|
||||
|
||||
if ('string' !== typeof transaction.Account)
|
||||
{
|
||||
this.emit('error', {
|
||||
'error' : 'invalidAccount',
|
||||
'error_message' : 'Bad account.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// YYY Might check paths for invalid accounts.
|
||||
|
||||
if (undefined === transaction.Fee) {
|
||||
if ('Payment' === transaction.TransactionType
|
||||
&& transaction.Flags & Remote.flags.Payment.CreateAccount) {
|
||||
@@ -980,6 +991,45 @@ Transaction.prototype.submit = function () {
|
||||
// Set options for Transactions
|
||||
//
|
||||
|
||||
Transaction._path_rewrite = function (path) {
|
||||
var path_new = [];
|
||||
|
||||
for (var index in path) {
|
||||
var node = path[index];
|
||||
var node_new = {};
|
||||
|
||||
if ('account' in node)
|
||||
node_new.account = UInt160.json_rewrite(node.account);
|
||||
|
||||
if ('issuer' in node)
|
||||
node_new.issuer = UInt160.json_rewrite(node.issuer);
|
||||
|
||||
if ('currency' in node)
|
||||
node_new.currency = Currency.json_rewrite(node.currency);
|
||||
|
||||
path_new.push(node_new);
|
||||
}
|
||||
|
||||
return path_new;
|
||||
}
|
||||
|
||||
Transaction.prototype.path_add = function (path) {
|
||||
this.transaction.Paths = this.transaction.Paths || []
|
||||
this.transaction.Paths.push(Transaction._path_rewrite(path));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// --> paths: undefined or array of path
|
||||
// A path is an array of objects containing some combination of: account, currency, issuer
|
||||
Transaction.prototype.paths = function (paths) {
|
||||
for (var index in paths) {
|
||||
this.path_add(paths[index]);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
// If the secret is in the config object, it does not need to be provided.
|
||||
Transaction.prototype.secret = function (secret) {
|
||||
this.secret = secret;
|
||||
@@ -987,7 +1037,7 @@ Transaction.prototype.secret = function (secret) {
|
||||
|
||||
Transaction.prototype.send_max = function (send_max) {
|
||||
if (send_max)
|
||||
this.transaction.SendMax = send_max.to_json();
|
||||
this.transaction.SendMax = Amount.json_rewrite(send_max);
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -1105,6 +1155,13 @@ Transaction.prototype.password_set = function (src, authorized_key, generator, p
|
||||
// --> src : UInt160 or String
|
||||
// --> dst : UInt160 or String
|
||||
// --> deliver_amount : Amount or String.
|
||||
//
|
||||
// Options:
|
||||
// .paths()
|
||||
// .path_add()
|
||||
// .secret()
|
||||
// .send_max()
|
||||
// .set_flags()
|
||||
Transaction.prototype.payment = function (src, dst, deliver_amount) {
|
||||
this.secret = this._account_secret(src);
|
||||
this.transaction.TransactionType = 'Payment';
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
<ClCompile Include="src\HTTPRequest.cpp" />
|
||||
<ClCompile Include="src\HttpsClient.cpp" />
|
||||
<ClCompile Include="src\Interpreter.cpp" />
|
||||
<ClCompile Include="src\InstanceCounter.cpp" />
|
||||
<ClCompile Include="src\Ledger.cpp" />
|
||||
<ClCompile Include="src\LedgerAcquire.cpp" />
|
||||
<ClCompile Include="src\LedgerConsensus.cpp" />
|
||||
|
||||
@@ -42,11 +42,13 @@ Application::Application() :
|
||||
mNetOps(mIOService, &mMasterLedger), mTempNodeCache(16384, 90), mHashedObjectStore(16384, 300),
|
||||
mSNTPClient(mAuxService), mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL),
|
||||
mHashNodeDB(NULL), mNetNodeDB(NULL),
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL)
|
||||
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mSweepTimer(mAuxService)
|
||||
{
|
||||
RAND_bytes(mNonce256.begin(), mNonce256.size());
|
||||
RAND_bytes(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
|
||||
mJobQueue.setThreadCount();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
extern const char *RpcDBInit[], *TxnDBInit[], *LedgerDBInit[], *WalletDBInit[], *HashNodeDBInit[], *NetNodeDBInit[];
|
||||
@@ -183,6 +185,16 @@ void Application::run()
|
||||
std::cout << "Done." << std::endl;
|
||||
}
|
||||
|
||||
void Application::sweep()
|
||||
{
|
||||
mMasterTransaction.sweep();
|
||||
mHashedObjectStore.sweep();
|
||||
mMasterLedger.sweep();
|
||||
mTempNodeCache.sweep();
|
||||
mSweepTimer.expires_from_now(boost::posix_time::seconds(60));
|
||||
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
delete mTxnDB;
|
||||
|
||||
@@ -66,6 +66,8 @@ class Application
|
||||
uint256 mNonce256;
|
||||
std::size_t mNonceST;
|
||||
|
||||
boost::asio::deadline_timer mSweepTimer;
|
||||
|
||||
std::map<std::string, Peer::pointer> mPeerMap;
|
||||
boost::recursive_mutex mPeerMapLock;
|
||||
|
||||
@@ -93,8 +95,11 @@ public:
|
||||
HashedObjectStore& getHashedObjectStore() { return mHashedObjectStore; }
|
||||
ValidationCollection& getValidations() { return mValidations; }
|
||||
JobQueue& getJobQueue() { return mJobQueue; }
|
||||
SuppressionTable& getSuppression() { return mSuppressions; }
|
||||
|
||||
bool isNew(const uint256& s) { return mSuppressions.addSuppression(s); }
|
||||
bool isNew(const uint160& s) { return mSuppressions.addSuppression(s); }
|
||||
bool isNew(const uint256& s, uint64 p) { return mSuppressions.addSuppressionPeer(s, p); }
|
||||
bool isNewFlag(const uint256& s, int f) { return mSuppressions.setFlag(s, f); }
|
||||
bool running() { return mTxnDB != NULL; }
|
||||
bool getSystemTimeOffset(int& offset) { return mSNTPClient.getOffset(offset); }
|
||||
|
||||
@@ -110,6 +115,7 @@ public:
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
void sweep();
|
||||
};
|
||||
|
||||
extern Application* theApp;
|
||||
|
||||
@@ -29,6 +29,7 @@ void splitIpPort(const std::string& strIpPort, std::string& strIp, int& iPort)
|
||||
}
|
||||
|
||||
ConnectionPool::ConnectionPool(boost::asio::io_service& io_service) :
|
||||
mLastPeer(0),
|
||||
mCtx(boost::asio::ssl::context::sslv23),
|
||||
mScanTimer(io_service),
|
||||
mPolicyTimer(io_service)
|
||||
@@ -237,7 +238,7 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
Peer::pointer peer = pair.second;
|
||||
Peer::ref peer = pair.second;
|
||||
if (!peer)
|
||||
std::cerr << "CP::RM null peer in list" << std::endl;
|
||||
else if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected())
|
||||
@@ -250,6 +251,32 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to all but the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0))
|
||||
peer->sendPacket(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
|
||||
{ // Relay message to the specified peers
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
|
||||
BOOST_FOREACH(naPeer pair, mConnectedMap)
|
||||
{
|
||||
Peer::ref peer = pair.second;
|
||||
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) > 0))
|
||||
peer->sendPacket(msg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Schedule a connection via scanning.
|
||||
//
|
||||
// Add or modify into PeerIps as a manual entry for immediate scanning.
|
||||
@@ -354,6 +381,12 @@ std::vector<Peer::pointer> ConnectionPool::getPeerVector()
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64 ConnectionPool::assignPeerId()
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mPeerLock);
|
||||
return ++mLastPeer;
|
||||
}
|
||||
|
||||
// Now know peer's node public key. Determine if we want to stay connected.
|
||||
// <-- bNew: false = redundant
|
||||
bool ConnectionPool::peerConnected(Peer::ref peer, const RippleAddress& naPeer,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef __CONNECTION_POOL__
|
||||
#define __CONNECTION_POOL__
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
@@ -14,7 +16,8 @@
|
||||
class ConnectionPool
|
||||
{
|
||||
private:
|
||||
boost::mutex mPeerLock;
|
||||
boost::mutex mPeerLock;
|
||||
uint64 mLastPeer;
|
||||
|
||||
typedef std::pair<RippleAddress, Peer::pointer> naPeer;
|
||||
typedef std::pair<ipPort, Peer::pointer> pipPeer;
|
||||
@@ -59,6 +62,8 @@ public:
|
||||
|
||||
// Send message to network.
|
||||
int relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg);
|
||||
void relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
void relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg);
|
||||
|
||||
// Manual connection request.
|
||||
// Queue for immediate scanning.
|
||||
@@ -87,6 +92,9 @@ public:
|
||||
Json::Value getPeersJson();
|
||||
std::vector<Peer::pointer> getPeerVector();
|
||||
|
||||
// Peer 64-bit ID function
|
||||
uint64 assignPeerId();
|
||||
|
||||
//
|
||||
// Scanning
|
||||
//
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(HashedObject);
|
||||
|
||||
HashedObjectStore::HashedObjectStore(int cacheSize, int cacheAge) :
|
||||
mCache(cacheSize, cacheAge), mWritePending(false)
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "uint256.h"
|
||||
#include "ScopedLock.h"
|
||||
#include "TaggedCache.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(HashedObject);
|
||||
|
||||
enum HashedObjectType
|
||||
{
|
||||
@@ -20,7 +23,7 @@ enum HashedObjectType
|
||||
hotTRANSACTION_NODE = 4
|
||||
};
|
||||
|
||||
class HashedObject
|
||||
class HashedObject : private IS_INSTANCE(HashedObject)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<HashedObject> pointer;
|
||||
@@ -61,6 +64,7 @@ public:
|
||||
|
||||
void bulkWrite();
|
||||
void waitWrite();
|
||||
void sweep() { mCache.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
15
src/InstanceCounter.cpp
Normal file
15
src/InstanceCounter.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
InstanceType* InstanceType::sHeadInstance = NULL;
|
||||
|
||||
std::vector<InstanceType::InstanceCount> InstanceType::getInstanceCounts(int min)
|
||||
{
|
||||
std::vector<InstanceCount> ret;
|
||||
for (InstanceType* i = sHeadInstance; i != NULL; i = i->mNextInstance)
|
||||
{
|
||||
int c = i->getCount();
|
||||
if (c >= min)
|
||||
ret.push_back(InstanceCount(i->getName(), c));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
80
src/InstanceCounter.h
Normal file
80
src/InstanceCounter.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef INSTANCE_COUNTER__H
|
||||
#define INSTANCE_COUNTER__H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#define DEFINE_INSTANCE(x) \
|
||||
extern InstanceType IT_##x; \
|
||||
class Instance_##x : private Instance \
|
||||
{ \
|
||||
protected: \
|
||||
Instance_##x() : Instance(IT_##x) { ; } \
|
||||
Instance_##x(const Instance_##x &) : \
|
||||
Instance(IT_##x) { ; } \
|
||||
Instance_##x& operator=(const Instance_##x&) \
|
||||
{ return *this; } \
|
||||
}
|
||||
|
||||
#define DECLARE_INSTANCE(x) \
|
||||
InstanceType IT_##x(#x);
|
||||
|
||||
#define IS_INSTANCE(x) Instance_##x
|
||||
|
||||
class InstanceType
|
||||
{
|
||||
protected:
|
||||
int mInstances;
|
||||
std::string mName;
|
||||
boost::mutex mLock;
|
||||
|
||||
InstanceType* mNextInstance;
|
||||
static InstanceType* sHeadInstance;
|
||||
|
||||
public:
|
||||
typedef std::pair<std::string, int> InstanceCount;
|
||||
|
||||
InstanceType(const char *n) : mInstances(0), mName(n)
|
||||
{
|
||||
mNextInstance = sHeadInstance;
|
||||
sHeadInstance = this;
|
||||
}
|
||||
|
||||
void addInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
++mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
void decInstance()
|
||||
{
|
||||
mLock.lock();
|
||||
--mInstances;
|
||||
mLock.unlock();
|
||||
}
|
||||
int getCount()
|
||||
{
|
||||
boost::mutex::scoped_lock(mLock);
|
||||
return mInstances;
|
||||
}
|
||||
const std::string& getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
static std::vector<InstanceCount> getInstanceCounts(int min = 1);
|
||||
};
|
||||
|
||||
class Instance
|
||||
{
|
||||
protected:
|
||||
InstanceType& mType;
|
||||
|
||||
public:
|
||||
Instance(InstanceType& t) : mType(t) { mType.addInstance(); }
|
||||
~Instance() { mType.decInstance(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(Ledger);
|
||||
|
||||
Ledger::Ledger(const RippleAddress& masterID, uint64 startAmount) : mTotCoins(startAmount), mLedgerSeq(1),
|
||||
mCloseTime(0), mParentCloseTime(0), mCloseResolution(LEDGER_TIME_ACCURACY), mCloseFlags(0),
|
||||
@@ -1072,7 +1073,7 @@ int Ledger::getPendingSaves()
|
||||
|
||||
void Ledger::pendSave(bool fromConsensus)
|
||||
{
|
||||
if (!fromConsensus && !theApp->isNew(getHash()))
|
||||
if (!fromConsensus && !theApp->isNewFlag(getHash(), SF_SAVED))
|
||||
return;
|
||||
|
||||
boost::thread thread(boost::bind(&Ledger::saveAcceptedLedger, shared_from_this(), fromConsensus));
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "types.h"
|
||||
#include "BitcoinUtil.h"
|
||||
#include "SHAMap.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
enum LedgerStateParms
|
||||
{
|
||||
@@ -38,7 +39,9 @@ enum LedgerStateParms
|
||||
#define LEDGER_JSON_DUMP_STATE 0x20000000
|
||||
#define LEDGER_JSON_FULL 0x40000000
|
||||
|
||||
class Ledger : public boost::enable_shared_from_this<Ledger>
|
||||
DEFINE_INSTANCE(Ledger);
|
||||
|
||||
class Ledger : public boost::enable_shared_from_this<Ledger>, public IS_INSTANCE(Ledger)
|
||||
{ // The basic Ledger structure, can be opened, closed, or synching
|
||||
friend class TransactionEngine;
|
||||
public:
|
||||
|
||||
@@ -845,7 +845,7 @@ void LedgerConsensus::addDisputedTransaction(const uint256& txID, const std::vec
|
||||
txn->setVote(pit.first, cit->second->hasItem(txID));
|
||||
}
|
||||
|
||||
if (!ourVote && theApp->isNew(txID))
|
||||
if (!ourVote && theApp->isNewFlag(txID, SF_RELAYED))
|
||||
{
|
||||
ripple::TMTransaction msg;
|
||||
msg.set_rawtransaction(&(tx.front()), tx.size());
|
||||
|
||||
@@ -18,6 +18,7 @@ public:
|
||||
Ledger::pointer getLedgerBySeq(uint32 index);
|
||||
Ledger::pointer getLedgerByHash(const uint256& hash);
|
||||
Ledger::pointer canonicalizeLedger(Ledger::pointer, bool cache);
|
||||
void sweep() { mLedgersByHash.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -91,6 +91,8 @@ public:
|
||||
void setLedgerRangePresent(uint32 minV, uint32 maxV) { mCompleteLedgers.setRange(minV, maxV); }
|
||||
|
||||
bool addHeldTransaction(const Transaction::pointer& trans);
|
||||
|
||||
void sweep(void) { mLedgerHistory.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -88,22 +88,31 @@ Transaction::pointer NetworkOPs::submitTransaction(const Transaction::pointer& t
|
||||
tpTrans->getSTransaction()->add(s);
|
||||
|
||||
Transaction::pointer tpTransNew = Transaction::sharedTransaction(s.getData(), true);
|
||||
assert(tpTransNew);
|
||||
|
||||
if(!tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
|
||||
if (!tpTransNew)
|
||||
{
|
||||
// Could not construct transaction.
|
||||
nothing();
|
||||
}
|
||||
else if (tpTransNew->getSTransaction()->isEquivalent(*tpTrans->getSTransaction()))
|
||||
{
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
}
|
||||
else
|
||||
{
|
||||
cLog(lsFATAL) << "Transaction reconstruction failure";
|
||||
cLog(lsFATAL) << tpTransNew->getSTransaction()->getJson(0);
|
||||
cLog(lsFATAL) << tpTrans->getSTransaction()->getJson(0);
|
||||
assert(false);
|
||||
}
|
||||
|
||||
(void) NetworkOPs::processTransaction(tpTransNew);
|
||||
assert(false);
|
||||
|
||||
tpTransNew = Transaction::pointer();
|
||||
}
|
||||
|
||||
return tpTransNew;
|
||||
}
|
||||
|
||||
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans, Peer* source)
|
||||
Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans)
|
||||
{
|
||||
Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true);
|
||||
if (dbtx) return dbtx;
|
||||
@@ -151,27 +160,28 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
|
||||
trans->setStatus(INCLUDED);
|
||||
theApp->getMasterTransaction().canonicalize(trans, true);
|
||||
|
||||
// FIXME: Need code to get all accounts affected by a transaction and re-synch
|
||||
// any of them that affect local accounts cached in memory. Or, we need to
|
||||
// no cache the account balance information and always get it from the current ledger
|
||||
// theApp->getWallet().applyTransaction(trans);
|
||||
std::set<uint64> peers;
|
||||
if (theApp->getSuppression().swapSet(trans->getID(), peers, SF_RELAYED))
|
||||
{
|
||||
ripple::TMTransaction tx;
|
||||
Serializer s;
|
||||
trans->getSTransaction()->add(s);
|
||||
tx.set_rawtransaction(&s.getData().front(), s.getLength());
|
||||
tx.set_status(ripple::tsCURRENT);
|
||||
tx.set_receivetimestamp(getNetworkTimeNC());
|
||||
|
||||
ripple::TMTransaction tx;
|
||||
Serializer s;
|
||||
trans->getSTransaction()->add(s);
|
||||
tx.set_rawtransaction(&s.getData().front(), s.getLength());
|
||||
tx.set_status(ripple::tsCURRENT);
|
||||
tx.set_receivetimestamp(getNetworkTimeNC());
|
||||
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, ripple::mtTRANSACTION);
|
||||
int sentTo = theApp->getConnectionPool().relayMessage(source, packet);
|
||||
cLog(lsINFO) << "Transaction relayed to " << sentTo << " node(s)";
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, ripple::mtTRANSACTION);
|
||||
theApp->getConnectionPool().relayMessageBut(peers, packet);
|
||||
}
|
||||
|
||||
return trans;
|
||||
}
|
||||
|
||||
cLog(lsDEBUG) << "Status other than success " << r;
|
||||
if ((mMode != omFULL) && (mMode != omTRACKING) && (theApp->isNew(trans->getID())))
|
||||
std::set<uint64> peers;
|
||||
|
||||
if ((mMode != omFULL) && (mMode != omTRACKING) &&
|
||||
theApp->getSuppression().swapSet(trans->getID(), peers, SF_RELAYED))
|
||||
{
|
||||
ripple::TMTransaction tx;
|
||||
Serializer s;
|
||||
@@ -180,7 +190,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
|
||||
tx.set_status(ripple::tsCURRENT);
|
||||
tx.set_receivetimestamp(getNetworkTimeNC());
|
||||
PackedMessage::pointer packet = boost::make_shared<PackedMessage>(tx, ripple::mtTRANSACTION);
|
||||
theApp->getConnectionPool().relayMessage(source, packet);
|
||||
theApp->getConnectionPool().relayMessageTo(peers, packet);
|
||||
}
|
||||
|
||||
trans->setStatus(INVALID);
|
||||
@@ -685,7 +695,7 @@ bool NetworkOPs::haveConsensusObject()
|
||||
}
|
||||
|
||||
// <-- bool: true to relay
|
||||
bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger,
|
||||
bool NetworkOPs::recvPropose(uint64 peerId, uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger,
|
||||
uint32 closeTime, const std::string& pubKey, const std::string& signature, const RippleAddress& nodePublic)
|
||||
{
|
||||
// JED: does mConsensus need to be locked?
|
||||
@@ -701,7 +711,7 @@ bool NetworkOPs::recvPropose(uint32 proposeSeq, const uint256& proposeHash, cons
|
||||
s.add32(closeTime);
|
||||
s.addRaw(pubKey);
|
||||
s.addRaw(signature);
|
||||
if (!theApp->isNew(s.getSHA512Half()))
|
||||
if (!theApp->isNew(s.getSHA512Half(), peerId))
|
||||
return false;
|
||||
|
||||
RippleAddress naPeerPublic = RippleAddress::createNodePublic(strCopy(pubKey));
|
||||
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
//
|
||||
Transaction::pointer submitTransaction(const Transaction::pointer& tpTrans);
|
||||
|
||||
Transaction::pointer processTransaction(Transaction::pointer transaction, Peer* source = NULL);
|
||||
Transaction::pointer processTransaction(Transaction::pointer transaction);
|
||||
Transaction::pointer findTransactionByID(const uint256& transactionID);
|
||||
int findTransactionsBySource(const uint256& uLedger, std::list<Transaction::pointer>&, const RippleAddress& sourceAccount,
|
||||
uint32 minSeq, uint32 maxSeq);
|
||||
@@ -171,8 +171,8 @@ public:
|
||||
const std::vector<unsigned char>& myNode, std::list< std::vector<unsigned char> >& newNodes);
|
||||
|
||||
// ledger proposal/close functions
|
||||
bool recvPropose(uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger, uint32 closeTime,
|
||||
const std::string& pubKey, const std::string& signature, const RippleAddress& nodePublic);
|
||||
bool recvPropose(uint64 peerId, uint32 proposeSeq, const uint256& proposeHash, const uint256& prevLedger,
|
||||
uint32 closeTime, const std::string& pubKey, const std::string& signature, const RippleAddress& nodePublic);
|
||||
bool gotTXData(const boost::shared_ptr<Peer>& peer, const uint256& hash,
|
||||
const std::list<SHAMapNode>& nodeIDs, const std::list< std::vector<unsigned char> >& nodeData);
|
||||
bool recvValidation(const SerializedValidation::pointer& val);
|
||||
|
||||
16
src/Peer.cpp
16
src/Peer.cpp
@@ -16,6 +16,7 @@
|
||||
#include "Log.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(Peer);
|
||||
|
||||
// Don't try to run past receiving nonsense from a peer
|
||||
#define TRUST_NETWORK
|
||||
@@ -29,7 +30,8 @@ Peer::Peer(boost::asio::io_service& io_service, boost::asio::ssl::context& ctx)
|
||||
mSocketSsl(io_service, ctx),
|
||||
mVerifyTimer(io_service)
|
||||
{
|
||||
// cLog(lsDEBUG) << "CREATING PEER: " << ADDRESS(this);
|
||||
cLog(lsDEBUG) << "CREATING PEER: " << ADDRESS(this);
|
||||
mPeerId = theApp->getConnectionPool().assignPeerId();
|
||||
}
|
||||
|
||||
void Peer::handle_write(const boost::system::error_code& error, size_t bytes_transferred)
|
||||
@@ -706,8 +708,12 @@ void Peer::recvTransaction(ripple::TMTransaction& packet)
|
||||
SerializerIterator sit(s);
|
||||
SerializedTransaction::pointer stx = boost::make_shared<SerializedTransaction>(boost::ref(sit));
|
||||
|
||||
if (!theApp->isNew(stx->getTransactionID(), mPeerId))
|
||||
return;
|
||||
|
||||
tx = boost::make_shared<Transaction>(stx, true);
|
||||
if (tx->getStatus() == INVALID) throw(0);
|
||||
if (tx->getStatus() == INVALID)
|
||||
throw(0);
|
||||
#ifndef TRUST_NETWORK
|
||||
}
|
||||
catch (...)
|
||||
@@ -721,7 +727,7 @@ void Peer::recvTransaction(ripple::TMTransaction& packet)
|
||||
}
|
||||
#endif
|
||||
|
||||
tx = theApp->getOPs().processTransaction(tx, this);
|
||||
tx = theApp->getOPs().processTransaction(tx);
|
||||
|
||||
if(tx->getStatus() != INCLUDED)
|
||||
{ // transaction wasn't accepted into ledger
|
||||
@@ -746,7 +752,7 @@ void Peer::recvPropose(ripple::TMProposeSet& packet)
|
||||
if ((packet.has_previousledger()) && (packet.previousledger().size() == 32))
|
||||
memcpy(prevLedger.begin(), packet.previousledger().data(), 32);
|
||||
|
||||
if(theApp->getOPs().recvPropose(packet.proposeseq(), currentTxHash, prevLedger, packet.closetime(),
|
||||
if(theApp->getOPs().recvPropose(mPeerId, packet.proposeseq(), currentTxHash, prevLedger, packet.closetime(),
|
||||
packet.nodepubkey(), packet.signature(), mNodePublic))
|
||||
{ // FIXME: Not all nodes will want proposals
|
||||
PackedMessage::pointer message = boost::make_shared<PackedMessage>(packet, ripple::mtPROPOSE_LEDGER);
|
||||
@@ -820,7 +826,7 @@ void Peer::recvValidation(const boost::shared_ptr<ripple::TMValidation>& packet)
|
||||
SerializedValidation::pointer val = boost::make_shared<SerializedValidation>(boost::ref(sit), false);
|
||||
|
||||
uint256 signingHash = val->getSigningHash();
|
||||
if (!theApp->isNew(signingHash))
|
||||
if (!theApp->isNew(signingHash, mPeerId))
|
||||
{
|
||||
cLog(lsTRACE) << "Validation is duplicate";
|
||||
return;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "PackedMessage.h"
|
||||
#include "Ledger.h"
|
||||
#include "Transaction.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
enum PeerPunish
|
||||
{
|
||||
@@ -21,7 +22,9 @@ enum PeerPunish
|
||||
|
||||
typedef std::pair<std::string,int> ipPort;
|
||||
|
||||
class Peer : public boost::enable_shared_from_this<Peer>
|
||||
DEFINE_INSTANCE(Peer);
|
||||
|
||||
class Peer : public boost::enable_shared_from_this<Peer>, public IS_INSTANCE(Peer)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<Peer> pointer;
|
||||
@@ -43,6 +46,7 @@ private:
|
||||
ipPort mIpPort;
|
||||
ipPort mIpPortConnect;
|
||||
uint256 mCookieHash;
|
||||
uint64 mPeerId;
|
||||
|
||||
uint256 mClosedLedgerHash, mPreviousLedgerHash;
|
||||
std::list<uint256> mRecentLedgers;
|
||||
@@ -159,6 +163,8 @@ public:
|
||||
uint256 getClosedLedgerHash() const { return mClosedLedgerHash; }
|
||||
bool hasLedger(const uint256& hash) const;
|
||||
bool hasTxSet(const uint256& hash) const;
|
||||
uint64 getPeerId() const { return mPeerId; }
|
||||
|
||||
RippleAddress getNodePublic() const { return mNodePublic; }
|
||||
void cycleStatus() { mPreviousLedgerHash = mClosedLedgerHash; mClosedLedgerHash.zero(); }
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "RippleAddress.h"
|
||||
#include "AccountState.h"
|
||||
#include "NicknameState.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
#include "Pathfinder.h"
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -75,7 +76,7 @@ Json::Value RPCHandler::rpcError(int iError)
|
||||
for (i=NUMBER(errorInfoA); i-- && errorInfoA[i].iError != iError;)
|
||||
;
|
||||
|
||||
Json::Value jsonResult = Json::Value(Json::objectValue);
|
||||
Json::Value jsonResult(Json::objectValue);
|
||||
|
||||
jsonResult["error"] = i >= 0 ? errorInfoA[i].pToken : lexical_cast_i(iError);
|
||||
jsonResult["error_message"] = i >= 0 ? errorInfoA[i].pMessage : lexical_cast_i(iError);
|
||||
@@ -2420,37 +2421,38 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param
|
||||
bool mAdminRequired;
|
||||
unsigned int iOptions;
|
||||
} commandsA[] = {
|
||||
{ "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true },
|
||||
{ "account_domain_set", &RPCHandler::doAccountDomainSet, 2, 3, false, optCurrent },
|
||||
{ "accept_ledger", &RPCHandler::doAcceptLedger, 0, 0, true },
|
||||
{ "account_domain_set", &RPCHandler::doAccountDomainSet, 2, 3, false, optCurrent },
|
||||
{ "account_email_set", &RPCHandler::doAccountEmailSet, 2, 3, false, optCurrent },
|
||||
{ "account_info", &RPCHandler::doAccountInfo, 1, 2, false, optCurrent },
|
||||
{ "account_message_set", &RPCHandler::doAccountMessageSet, 3, 3, false, optCurrent },
|
||||
{ "account_publish_set", &RPCHandler::doAccountPublishSet, 4, 4, false, optCurrent },
|
||||
{ "account_rate_set", &RPCHandler::doAccountRateSet, 3, 3, false, optCurrent },
|
||||
{ "account_tx", &RPCHandler::doAccountTransactions, 2, 3, false, optNetwork },
|
||||
{ "account_wallet_set", &RPCHandler::doAccountWalletSet, 2, 3, false, optCurrent },
|
||||
{ "account_wallet_set", &RPCHandler::doAccountWalletSet, 2, 3, false, optCurrent },
|
||||
{ "connect", &RPCHandler::doConnect, 1, 2, true },
|
||||
{ "data_delete", &RPCHandler::doDataDelete, 1, 1, true },
|
||||
{ "data_fetch", &RPCHandler::doDataFetch, 1, 1, true },
|
||||
{ "data_store", &RPCHandler::doDataStore, 2, 2, true },
|
||||
{ "get_counts", &RPCHandler::doGetCounts, 0, 1, true },
|
||||
{ "ledger", &RPCHandler::doLedger, 0, 2, false, optNetwork },
|
||||
{ "log_level", &RPCHandler::doLogLevel, 0, 2, true },
|
||||
{ "log_level", &RPCHandler::doLogLevel, 0, 2, true },
|
||||
{ "logrotate", &RPCHandler::doLogRotate, 0, 0, true },
|
||||
{ "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent },
|
||||
{ "nickname_info", &RPCHandler::doNicknameInfo, 1, 1, false, optCurrent },
|
||||
{ "nickname_set", &RPCHandler::doNicknameSet, 2, 3, false, optCurrent },
|
||||
{ "offer_create", &RPCHandler::doOfferCreate, 9, 10, false, optCurrent },
|
||||
{ "offer_cancel", &RPCHandler::doOfferCancel, 3, 3, false, optCurrent },
|
||||
{ "owner_info", &RPCHandler::doOwnerInfo, 1, 2, false, optCurrent },
|
||||
{ "password_fund", &RPCHandler::doPasswordFund, 2, 3, false, optCurrent },
|
||||
{ "password_fund", &RPCHandler::doPasswordFund, 2, 3, false, optCurrent },
|
||||
{ "password_set", &RPCHandler::doPasswordSet, 2, 3, false, optNetwork },
|
||||
{ "peers", &RPCHandler::doPeers, 0, 0, true },
|
||||
{ "profile", &RPCHandler::doProfile, 1, 9, false, optCurrent },
|
||||
{ "ripple", &RPCHandler::doRipple, 9, -1, false, optCurrent|optClosed },
|
||||
{ "ripple_lines_get", &RPCHandler::doRippleLinesGet, 1, 2, false, optCurrent },
|
||||
{ "ripple_line_set", &RPCHandler::doRippleLineSet, 4, 7, false, optCurrent },
|
||||
{ "send", &RPCHandler::doSend, 3, 9, false, optCurrent },
|
||||
{ "send", &RPCHandler::doSend, 3, 9, false, optCurrent },
|
||||
{ "server_info", &RPCHandler::doServerInfo, 0, 0, true },
|
||||
{ "stop", &RPCHandler::doStop, 0, 0, true },
|
||||
{ "stop", &RPCHandler::doStop, 0, 0, true },
|
||||
{ "tx", &RPCHandler::doTx, 1, 1, true },
|
||||
{ "tx_history", &RPCHandler::doTxHistory, 1, 1, false, },
|
||||
|
||||
@@ -2459,16 +2461,16 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param
|
||||
{ "unl_list", &RPCHandler::doUnlList, 0, 0, true },
|
||||
{ "unl_load", &RPCHandler::doUnlLoad, 0, 0, true },
|
||||
{ "unl_network", &RPCHandler::doUnlNetwork, 0, 0, true },
|
||||
{ "unl_reset", &RPCHandler::doUnlReset, 0, 0, true },
|
||||
{ "unl_score", &RPCHandler::doUnlScore, 0, 0, true },
|
||||
{ "unl_reset", &RPCHandler::doUnlReset, 0, 0, true },
|
||||
{ "unl_score", &RPCHandler::doUnlScore, 0, 0, true },
|
||||
|
||||
{ "validation_create", &RPCHandler::doValidationCreate, 0, 1, false },
|
||||
{ "validation_create", &RPCHandler::doValidationCreate, 0, 1, false },
|
||||
{ "validation_seed", &RPCHandler::doValidationSeed, 0, 1, false },
|
||||
|
||||
{ "wallet_accounts", &RPCHandler::doWalletAccounts, 1, 1, false, optCurrent },
|
||||
{ "wallet_add", &RPCHandler::doWalletAdd, 3, 5, false, optCurrent },
|
||||
{ "wallet_claim", &RPCHandler::doWalletClaim, 2, 4, false, optNetwork },
|
||||
{ "wallet_create", &RPCHandler::doWalletCreate, 3, 4, false, optCurrent },
|
||||
{ "wallet_create", &RPCHandler::doWalletCreate, 3, 4, false, optCurrent },
|
||||
{ "wallet_propose", &RPCHandler::doWalletPropose, 0, 1, false, },
|
||||
{ "wallet_seed", &RPCHandler::doWalletSeed, 0, 1, false, },
|
||||
|
||||
@@ -2603,6 +2605,20 @@ Json::Value RPCHandler::doLogin(const Json::Value& params)
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::doGetCounts(const Json::Value& params)
|
||||
{
|
||||
int minCount = 1;
|
||||
if (params.size() > 0)
|
||||
minCount = params[0u].asInt();
|
||||
|
||||
std::vector<InstanceType::InstanceCount> count = InstanceType::getInstanceCounts(minCount);
|
||||
|
||||
Json::Value ret(Json::objectValue);
|
||||
BOOST_FOREACH(InstanceType::InstanceCount& it, count)
|
||||
ret[it.first] = it.second;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Json::Value RPCHandler::doLogLevel(const Json::Value& params)
|
||||
{
|
||||
if (params.size() == 0)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifndef RPCHANDLER__H
|
||||
#define RPCHANDLER__H
|
||||
|
||||
// used by the RPCServer or WSDoor to carry out these RPC commands
|
||||
class NetworkOPs;
|
||||
|
||||
@@ -37,6 +40,7 @@ class RPCHandler
|
||||
Json::Value doDataDelete(const Json::Value& params);
|
||||
Json::Value doDataFetch(const Json::Value& params);
|
||||
Json::Value doDataStore(const Json::Value& params);
|
||||
Json::Value doGetCounts(const Json::Value& params);
|
||||
Json::Value doLedger(const Json::Value& params);
|
||||
Json::Value doLogRotate(const Json::Value& params);
|
||||
Json::Value doNicknameInfo(const Json::Value& params);
|
||||
@@ -156,4 +160,6 @@ public:
|
||||
Json::Value doCommand(const std::string& command, Json::Value& params,int role);
|
||||
Json::Value rpcError(int iError);
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1421,6 +1421,8 @@ bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs)
|
||||
|
||||
// Make sure the path delivers to uAccountID: uCurrencyID from uIssuerID.
|
||||
//
|
||||
// If the unadded next node as specified by arguments would not work as is, then add the necessary nodes so it would work.
|
||||
//
|
||||
// Rules:
|
||||
// - Currencies must be converted via an offer.
|
||||
// - A node names it's output.
|
||||
@@ -1449,13 +1451,14 @@ TER PathState::pushImply(
|
||||
ACCOUNT_ONE, // Placeholder for offers.
|
||||
uCurrencyID, // The offer's output is what is now wanted.
|
||||
uIssuerID);
|
||||
|
||||
}
|
||||
|
||||
const PaymentNode& pnBck = vpnNodes.back();
|
||||
|
||||
// For ripple, non-stamps, ensure the issuer is on at least one side of the transaction.
|
||||
if (tesSUCCESS == terResult
|
||||
&& !!uCurrencyID // Not stamps.
|
||||
&& (pnPrv.uAccountID != uIssuerID // Previous is not issuing own IOUs.
|
||||
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
|
||||
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
|
||||
{
|
||||
// Need to ripple through uIssuerID's account.
|
||||
@@ -1514,13 +1517,19 @@ TER PathState::pushNode(
|
||||
pnCur.saRevRedeem = STAmount(uCurrencyID, uAccountID);
|
||||
pnCur.saRevIssue = STAmount(uCurrencyID, uAccountID);
|
||||
|
||||
if (!bFirst)
|
||||
if (bFirst)
|
||||
{
|
||||
// The first node is always correct as is.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add required intermediate nodes to deliver to current account.
|
||||
terResult = pushImply(
|
||||
pnCur.uAccountID, // Current account.
|
||||
pnCur.uCurrencyID, // Wanted currency.
|
||||
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer.
|
||||
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as wanted issuer.
|
||||
|
||||
// Note: pnPrv may no longer be the immediately previous node.
|
||||
}
|
||||
@@ -1532,7 +1541,7 @@ TER PathState::pushNode(
|
||||
|
||||
if (bBckAccount)
|
||||
{
|
||||
SLE::pointer sleRippleState = mLedger->getSLE(Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
|
||||
SLE::pointer sleRippleState = lesEntries.entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
|
||||
|
||||
if (!sleRippleState)
|
||||
{
|
||||
@@ -1541,7 +1550,7 @@ TER PathState::pushNode(
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID(pnCur.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency(pnPrv.uCurrencyID)
|
||||
<< STAmount::createHumanCurrency(pnCur.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
cLog(lsINFO) << getJson();
|
||||
@@ -1555,12 +1564,12 @@ TER PathState::pushNode(
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID(pnCur.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency(pnPrv.uCurrencyID)
|
||||
<< STAmount::createHumanCurrency(pnCur.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID);
|
||||
STAmount saOwed = lesEntries.rippleOwed(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
|
||||
|
||||
if (!saOwed.isPositive() && *saOwed.negate() >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, uCurrencyID))
|
||||
if (!saOwed.isPositive() && *saOwed.negate() >= lesEntries.rippleLimit(pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID))
|
||||
{
|
||||
terResult = tepPATH_DRY;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
|
||||
SETUP_LOG();
|
||||
|
||||
DECLARE_INSTANCE(SHAMap);
|
||||
DECLARE_INSTANCE(SHAMapItem);
|
||||
DECLARE_INSTANCE(SHAMapTreeNode);
|
||||
|
||||
std::size_t hash_value(const SHAMapNode& mn)
|
||||
{
|
||||
std::size_t seed = theApp->getNonceST();
|
||||
|
||||
13
src/SHAMap.h
13
src/SHAMap.h
@@ -14,6 +14,11 @@
|
||||
#include "ScopedLock.h"
|
||||
#include "Serializer.h"
|
||||
#include "HashedObject.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(SHAMap);
|
||||
DEFINE_INSTANCE(SHAMapItem);
|
||||
DEFINE_INSTANCE(SHAMapTreeNode);
|
||||
|
||||
class SHAMap;
|
||||
|
||||
@@ -31,7 +36,7 @@ private:
|
||||
|
||||
public:
|
||||
|
||||
static const int rootDepth=0;
|
||||
static const int rootDepth = 0;
|
||||
|
||||
SHAMapNode() : mDepth(0) { ; }
|
||||
SHAMapNode(int depth, const uint256& hash);
|
||||
@@ -77,7 +82,7 @@ extern std::size_t hash_value(const SHAMapNode& mn);
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const SHAMapNode& node) { return out << node.getString(); }
|
||||
|
||||
class SHAMapItem
|
||||
class SHAMapItem : public IS_INSTANCE(SHAMapItem)
|
||||
{ // an item stored in a SHAMap
|
||||
public:
|
||||
typedef boost::shared_ptr<SHAMapItem> pointer;
|
||||
@@ -135,7 +140,7 @@ enum SHAMapType
|
||||
smtFREE =3, // A tree not part of a ledger
|
||||
};
|
||||
|
||||
class SHAMapTreeNode : public SHAMapNode
|
||||
class SHAMapTreeNode : public SHAMapNode, public IS_INSTANCE(SHAMapTreeNode)
|
||||
{
|
||||
friend class SHAMap;
|
||||
|
||||
@@ -276,7 +281,7 @@ public:
|
||||
|
||||
extern std::ostream& operator<<(std::ostream&, const SHAMapMissingNode&);
|
||||
|
||||
class SHAMap
|
||||
class SHAMap : public IS_INSTANCE(SHAMap)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<SHAMap> pointer;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "Ledger.h"
|
||||
#include "Log.h"
|
||||
|
||||
DECLARE_INSTANCE(SerializedLedgerEntry)
|
||||
|
||||
SerializedLedgerEntry::SerializedLedgerEntry(SerializerIterator& sit, const uint256& index)
|
||||
: STObject(sfLedgerEntry), mIndex(index)
|
||||
{
|
||||
|
||||
@@ -4,8 +4,11 @@
|
||||
#include "SerializedObject.h"
|
||||
#include "LedgerFormats.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
class SerializedLedgerEntry : public STObject
|
||||
DEFINE_INSTANCE(SerializedLedgerEntry);
|
||||
|
||||
class SerializedLedgerEntry : public STObject, private IS_INSTANCE(SerializedLedgerEntry)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<SerializedLedgerEntry> pointer;
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "SerializedTransaction.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(SerializedObject);
|
||||
DECLARE_INSTANCE(SerializedArray);
|
||||
|
||||
std::auto_ptr<SerializedType> STObject::makeDefaultObject(SerializedTypeID id, SField::ref name)
|
||||
{
|
||||
@@ -1110,23 +1112,24 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
|
||||
data.push_back(new STPathSet(field));
|
||||
STPathSet* tail = dynamic_cast<STPathSet*>(&data.back());
|
||||
assert(tail);
|
||||
for (Json::UInt i = 0; !object.isValidIndex(i); ++i)
|
||||
for (Json::UInt i = 0; value.isValidIndex(i); ++i)
|
||||
{
|
||||
STPath p;
|
||||
if (!object[i].isArray())
|
||||
if (!value[i].isArray())
|
||||
throw std::runtime_error("Path must be array");
|
||||
for (Json::UInt j = 0; !object[i].isValidIndex(j); ++j)
|
||||
for (Json::UInt j = 0; value[i].isValidIndex(j); ++j)
|
||||
{ // each element in this path has some combination of account, currency, or issuer
|
||||
|
||||
Json::Value pathEl = object[i][j];
|
||||
Json::Value pathEl = value[i][j];
|
||||
if (!pathEl.isObject())
|
||||
throw std::runtime_error("Path elements must be objects");
|
||||
const Json::Value& account = pathEl["account"];
|
||||
const Json::Value& currency = pathEl["currency"];
|
||||
const Json::Value& issuer = pathEl["issuer"];
|
||||
|
||||
const Json::Value& account = pathEl["account"];
|
||||
const Json::Value& currency = pathEl["currency"];
|
||||
const Json::Value& issuer = pathEl["issuer"];
|
||||
bool hasCurrency = false;
|
||||
uint160 uAccount, uCurrency, uIssuer;
|
||||
bool hasCurrency;
|
||||
|
||||
if (!account.isNull())
|
||||
{ // human account id
|
||||
if (!account.isString())
|
||||
@@ -1136,7 +1139,7 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
|
||||
uAccount.SetHex(strValue);
|
||||
{
|
||||
RippleAddress a;
|
||||
if (!a.setAccountPublic(strValue))
|
||||
if (!a.setAccountID(strValue))
|
||||
throw std::runtime_error("Account in path element invalid");
|
||||
uAccount = a.getAccountID();
|
||||
}
|
||||
@@ -1160,7 +1163,7 @@ std::auto_ptr<STObject> STObject::parseJson(const Json::Value& object, SField::r
|
||||
else
|
||||
{
|
||||
RippleAddress a;
|
||||
if (!a.setAccountPublic(issuer.asString()))
|
||||
if (!a.setAccountID(issuer.asString()))
|
||||
throw std::runtime_error("path element issuer invalid");
|
||||
uIssuer = a.getAccountID();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
#include "../json/value.h"
|
||||
|
||||
#include "SerializedTypes.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
DEFINE_INSTANCE(SerializedObject);
|
||||
DEFINE_INSTANCE(SerializedArray);
|
||||
|
||||
// Serializable object/array types
|
||||
|
||||
@@ -22,7 +26,7 @@ public:
|
||||
SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; }
|
||||
};
|
||||
|
||||
class STObject : public SerializedType
|
||||
class STObject : public SerializedType, private IS_INSTANCE(SerializedObject)
|
||||
{
|
||||
protected:
|
||||
boost::ptr_vector<SerializedType> mData;
|
||||
@@ -175,7 +179,7 @@ namespace boost
|
||||
|
||||
|
||||
|
||||
class STArray : public SerializedType
|
||||
class STArray : public SerializedType, private IS_INSTANCE(SerializedArray)
|
||||
{
|
||||
public:
|
||||
typedef std::vector<STObject> vector;
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Log.h"
|
||||
#include "HashPrefixes.h"
|
||||
|
||||
DECLARE_INSTANCE(SerializedTransaction);
|
||||
|
||||
SerializedTransaction::SerializedTransaction(TransactionType type) : STObject(sfTransaction), mType(type)
|
||||
{
|
||||
mFormat = TransactionFormat::getTxnFormat(type);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "SerializedObject.h"
|
||||
#include "TransactionFormats.h"
|
||||
#include "RippleAddress.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
#define TXN_SQL_NEW 'N'
|
||||
#define TXN_SQL_CONFLICT 'C'
|
||||
@@ -17,7 +18,9 @@
|
||||
#define TXN_SQL_INCLUDED 'I'
|
||||
#define TXN_SQL_UNKNOWN 'U'
|
||||
|
||||
class SerializedTransaction : public STObject
|
||||
DEFINE_INSTANCE(SerializedTransaction);
|
||||
|
||||
class SerializedTransaction : public STObject, private IS_INSTANCE(SerializedTransaction)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<SerializedTransaction> pointer;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "TransactionErr.h"
|
||||
|
||||
SETUP_LOG();
|
||||
DECLARE_INSTANCE(SerializedValue);
|
||||
|
||||
STAmount saZero(CURRENCY_ONE, ACCOUNT_ONE, 0);
|
||||
STAmount saOne(CURRENCY_ONE, ACCOUNT_ONE, 1);
|
||||
@@ -539,7 +540,6 @@ void STPathSet::add(Serializer& s) const
|
||||
if (!bFirst)
|
||||
{
|
||||
s.add8(STPathElement::typeBoundary);
|
||||
bFirst = false;
|
||||
}
|
||||
|
||||
BOOST_FOREACH(const STPathElement& speElement, spPath)
|
||||
@@ -557,6 +557,8 @@ void STPathSet::add(Serializer& s) const
|
||||
if (iType & STPathElement::typeIssuer)
|
||||
s.add160(speElement.getIssuerID());
|
||||
}
|
||||
|
||||
bFirst = false;
|
||||
}
|
||||
s.add8(STPathElement::typeEnd);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "uint256.h"
|
||||
#include "Serializer.h"
|
||||
#include "FieldNames.h"
|
||||
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
enum PathFlags
|
||||
{
|
||||
@@ -30,7 +30,9 @@ enum PathFlags
|
||||
#define ACCOUNT_XNS uint160(0)
|
||||
#define ACCOUNT_ONE uint160(1) // Used as a place holder
|
||||
|
||||
class SerializedType
|
||||
DEFINE_INSTANCE(SerializedValue);
|
||||
|
||||
class SerializedType : private IS_INSTANCE(SerializedValue)
|
||||
{
|
||||
protected:
|
||||
SField::ptr fName;
|
||||
|
||||
@@ -1,39 +1,100 @@
|
||||
|
||||
#include "Suppression.h"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
bool SuppressionTable::addSuppression(const uint160& suppression)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
DECLARE_INSTANCE(Suppression);
|
||||
|
||||
if (mSuppressionMap.find(suppression) != mSuppressionMap.end())
|
||||
return false;
|
||||
Suppression& SuppressionTable::findCreateEntry(const uint256& index, bool& created)
|
||||
{
|
||||
boost::unordered_map<uint256, Suppression>::iterator fit = mSuppressionMap.find(index);
|
||||
|
||||
if (fit != mSuppressionMap.end())
|
||||
{
|
||||
created = false;
|
||||
return fit->second;
|
||||
}
|
||||
created = true;
|
||||
|
||||
time_t now = time(NULL);
|
||||
time_t expireTime = now - mHoldTime;
|
||||
|
||||
boost::unordered_map< time_t, std::list<uint160> >::iterator
|
||||
it = mSuppressionTimes.begin(), end = mSuppressionTimes.end();
|
||||
while (it != end)
|
||||
// See if any supressions need to be expired
|
||||
std::map< time_t, std::list<uint256> >::iterator it = mSuppressionTimes.begin();
|
||||
if ((it != mSuppressionTimes.end()) && (it->first <= expireTime))
|
||||
{
|
||||
if (it->first <= expireTime)
|
||||
{
|
||||
BOOST_FOREACH(const uint160& lit, it->second)
|
||||
mSuppressionMap.erase(lit);
|
||||
it = mSuppressionTimes.erase(it);
|
||||
}
|
||||
else ++it;
|
||||
BOOST_FOREACH(const uint256& lit, it->second)
|
||||
mSuppressionMap.erase(lit);
|
||||
mSuppressionTimes.erase(it);
|
||||
}
|
||||
|
||||
mSuppressionMap[suppression] = now;
|
||||
mSuppressionTimes[now].push_back(suppression);
|
||||
mSuppressionTimes[now].push_back(index);
|
||||
return mSuppressionMap.insert(std::make_pair(index, Suppression())).first->second;
|
||||
}
|
||||
|
||||
bool SuppressionTable::addSuppression(const uint256& index)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry(index, created);
|
||||
return created;
|
||||
}
|
||||
|
||||
Suppression SuppressionTable::getEntry(const uint256& index)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
return findCreateEntry(index, created);
|
||||
}
|
||||
|
||||
bool SuppressionTable::addSuppressionPeer(const uint256& index, uint64 peer)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry(index, created).addPeer(peer);
|
||||
return created;
|
||||
}
|
||||
|
||||
bool SuppressionTable::addSuppressionFlags(const uint256& index, int flag)
|
||||
{
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
findCreateEntry(index, created).setFlag(flag);
|
||||
return created;
|
||||
}
|
||||
|
||||
bool SuppressionTable::setFlag(const uint256& index, int flag)
|
||||
{ // return: true = changed, false = unchanged
|
||||
assert(flag != 0);
|
||||
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
Suppression &s = findCreateEntry(index, created);
|
||||
|
||||
if ((s.getFlags() & flag) == flag)
|
||||
return false;
|
||||
|
||||
s.setFlag(flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SuppressionTable::addSuppression(const uint256& suppression)
|
||||
bool SuppressionTable::swapSet(const uint256& index, std::set<uint64>& peers, int flag)
|
||||
{
|
||||
uint160 u;
|
||||
memcpy(u.begin(), suppression.begin() + (suppression.size() - u.size()), u.size());
|
||||
return addSuppression(u);
|
||||
}
|
||||
boost::mutex::scoped_lock sl(mSuppressionMutex);
|
||||
|
||||
bool created;
|
||||
Suppression &s = findCreateEntry(index, created);
|
||||
|
||||
if ((s.getFlags() & flag) == flag)
|
||||
return false;
|
||||
|
||||
s.swapSet(peers);
|
||||
s.setFlag(flag);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,14 +1,43 @@
|
||||
#ifndef __SUPPRESSION__
|
||||
#define __SUPPRESSION__
|
||||
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include "uint256.h"
|
||||
#include "types.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
extern std::size_t hash_value(const uint160& u);
|
||||
DEFINE_INSTANCE(Suppression);
|
||||
|
||||
#define SF_RELAYED 0x01
|
||||
#define SF_SIGBAD 0x02
|
||||
#define SF_SIGGOOD 0x04
|
||||
#define SF_SAVED 0x08
|
||||
|
||||
class Suppression : private IS_INSTANCE(Suppression)
|
||||
{
|
||||
protected:
|
||||
int mFlags;
|
||||
std::set<uint64> mPeers;
|
||||
|
||||
public:
|
||||
Suppression() : mFlags(0) { ; }
|
||||
|
||||
const std::set<uint64>& peekPeers() { return mPeers; }
|
||||
void addPeer(uint64 peer) { mPeers.insert(peer); }
|
||||
bool hasPeer(uint64 peer) { return mPeers.count(peer) > 0; }
|
||||
|
||||
int getFlags(void) { return mFlags; }
|
||||
bool hasFlag(int f) { return (mFlags & f) != 0; }
|
||||
void setFlag(int f) { mFlags |= f; }
|
||||
void clearFlag(int f) { mFlags &= ~f; }
|
||||
void swapSet(std::set<uint64>& s) { mPeers.swap(s); }
|
||||
};
|
||||
|
||||
class SuppressionTable
|
||||
{
|
||||
@@ -17,18 +46,27 @@ protected:
|
||||
boost::mutex mSuppressionMutex;
|
||||
|
||||
// Stores all suppressed hashes and their expiration time
|
||||
boost::unordered_map<uint160, time_t> mSuppressionMap;
|
||||
boost::unordered_map<uint256, Suppression> mSuppressionMap;
|
||||
|
||||
// Stores all expiration times and the hashes indexed for them
|
||||
boost::unordered_map< time_t, std::list<uint160> > mSuppressionTimes;
|
||||
std::map< time_t, std::list<uint256> > mSuppressionTimes;
|
||||
|
||||
int mHoldTime;
|
||||
|
||||
Suppression& findCreateEntry(const uint256&, bool& created);
|
||||
|
||||
public:
|
||||
SuppressionTable(int holdTime = 120) : mHoldTime(holdTime) { ; }
|
||||
|
||||
bool addSuppression(const uint256& suppression);
|
||||
bool addSuppression(const uint160& suppression);
|
||||
bool addSuppression(const uint256& index);
|
||||
|
||||
bool addSuppressionPeer(const uint256& index, uint64 peer);
|
||||
bool addSuppressionFlags(const uint256& index, int flag);
|
||||
bool setFlag(const uint256& index, int flag);
|
||||
|
||||
Suppression getEntry(const uint256&);
|
||||
|
||||
bool swapSet(const uint256& index, std::set<uint64>& peers, int flag);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
int getTargetAge() const;
|
||||
|
||||
int getCacheSize();
|
||||
int getTrackSize();
|
||||
int getSweepAge();
|
||||
|
||||
void setTargetSize(int size);
|
||||
@@ -78,6 +79,12 @@ template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getCac
|
||||
return mCache.size();
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> int TaggedCache<c_Key, c_Data>::getTrackSize()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
return mMap.size();
|
||||
}
|
||||
|
||||
template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep()
|
||||
{
|
||||
boost::recursive_mutex::scoped_lock sl(mLock);
|
||||
@@ -96,7 +103,7 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep
|
||||
typename boost::unordered_map<key_type, cache_entry>::iterator cit = mCache.begin();
|
||||
while (cit != mCache.end())
|
||||
{
|
||||
if (cit->second->second.first < target)
|
||||
if (cit->second.first < target)
|
||||
mCache.erase(cit++);
|
||||
else
|
||||
++cit;
|
||||
@@ -106,7 +113,7 @@ template<typename c_Key, typename c_Data> void TaggedCache<c_Key, c_Data>::sweep
|
||||
typename boost::unordered_map<key_type, weak_data_ptr>::iterator mit = mMap.begin();
|
||||
while (mit != mMap.end())
|
||||
{
|
||||
if (mit->second->expired())
|
||||
if (mit->second.expired())
|
||||
mMap.erase(mit++);
|
||||
else
|
||||
++mit;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "SerializedTransaction.h"
|
||||
#include "Log.h"
|
||||
|
||||
DECLARE_INSTANCE(Transaction);
|
||||
|
||||
Transaction::Transaction(SerializedTransaction::ref sit, bool bValidate)
|
||||
: mInLedger(0), mStatus(INVALID), mResult(temUNCERTAIN), mTransaction(sit)
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "SHAMap.h"
|
||||
#include "SerializedTransaction.h"
|
||||
#include "TransactionErr.h"
|
||||
#include "InstanceCounter.h"
|
||||
|
||||
class Database;
|
||||
|
||||
@@ -37,8 +38,10 @@ enum TransStatus
|
||||
INCOMPLETE = 8 // needs more signatures
|
||||
};
|
||||
|
||||
DEFINE_INSTANCE(Transaction);
|
||||
|
||||
// This class is for constructing and examining transactions. Transactions are static so manipulation functions are unnecessary.
|
||||
class Transaction : public boost::enable_shared_from_this<Transaction>
|
||||
class Transaction : public boost::enable_shared_from_this<Transaction>, private IS_INSTANCE(Transaction)
|
||||
{
|
||||
public:
|
||||
typedef boost::shared_ptr<Transaction> pointer;
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
|
||||
// return value: true = we had the transaction already
|
||||
bool canonicalize(Transaction::pointer& txn, bool maybeNew);
|
||||
void sweep(void) { mCache.sweep(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1074,6 +1074,12 @@ void WSConnection::doSubmit(Json::Value& jvResult, const Json::Value& jvRequest)
|
||||
try
|
||||
{
|
||||
tpTrans = mNetwork.submitTransaction(tpTrans);
|
||||
|
||||
if (!tpTrans) {
|
||||
jvResult["error"] = "invalidTransaction";
|
||||
jvResult["error_exception"] = "Unable to sterilize transaction.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
||||
@@ -523,6 +523,65 @@ buster.testCase("Indirect ripple", {
|
||||
});
|
||||
},
|
||||
|
||||
"indirect ripple with path" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000", ["alice", "bob", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set alice's limit.";
|
||||
|
||||
testutils.credit_limit(self.remote, "alice", "600/USD/mtgox", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set bob's limit.";
|
||||
|
||||
testutils.credit_limit(self.remote, "bob", "700/USD/mtgox", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Give alice some mtgox.";
|
||||
|
||||
testutils.payment(self.remote, "mtgox", "alice", "70/USD/mtgox", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Give bob some mtgox.";
|
||||
|
||||
testutils.payment(self.remote, "mtgox", "bob", "50/USD/mtgox", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice sends via a path";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "bob", "5/USD/mtgox")
|
||||
.path_add( [ { account: "mtgox" } ])
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result != 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify alice balance with mtgox.";
|
||||
|
||||
testutils.verify_balance(self.remote, "alice", "65/USD/mtgox", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify bob balance with mtgox.";
|
||||
|
||||
testutils.verify_balance(self.remote, "bob", "55/USD/mtgox", callback);
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
// Direct ripple without no liqudity.
|
||||
// Ripple without credit path.
|
||||
// Ripple with one-way credit path.
|
||||
|
||||
Reference in New Issue
Block a user