Merge branch 'master' of github.com:jedmccaleb/NewCoin

This commit is contained in:
jed
2013-03-04 14:30:51 -08:00
69 changed files with 1378 additions and 369 deletions

263
bin/browser.js Executable file
View File

@@ -0,0 +1,263 @@
#!/usr/bin/node
//
// ledger_header?l=L
// transaction?h=H
// ledger_entry?l=L&h=H
// account_root?l=L&a=A
// directory?l=L&dir_root=H&i=I
// directory?l=L&o=A&i=I // owner directory
// offer?l=L&offer=H
// offer?l=L&account=A&i=I
// ripple_state=l=L&a=A&b=A&c=C
// account_lines?l=L&a=A
//
// A=address
// C=currency 3 letter code
// H=hash
// I=index
// L=current | closed | validated | index | hash
//
var extend = require("extend");
var http = require("http");
var url = require("url");
var Remote = require("../src/js/remote.js").Remote;
var program = process.argv[1];
// Build a link to a type.
var build_uri = function (params, opts) {
var c;
if (params.type === 'account_root') {
c = {
pathname: 'account_root',
query: {
l: params.ledger,
a: params.account,
},
};
} else if (params.type === 'ledger_header') {
c = {
pathname: 'ledger_header',
query: {
l: params.ledger,
},
};
} else if (params.type === 'transaction') {
c = {
pathname: 'transaction',
query: {
h: params.hash,
},
};
} else {
c = {};
}
c.protocol = "http";
c.hostname = opts.hostname;
c.port = opts.port;
return url.format(c);
};
var build_link = function (item, link) {
console.log(link);
return "<A HREF=" + link + ">" + item + "</A>";
};
var rewrite_object = function (obj, opts) {
var out = extend({}, obj);
if ('ledger_index' in obj) {
out.ledger_index =
build_link(
obj.ledger_index,
build_uri({
type: 'ledger_header',
ledger: obj.ledger_index,
}, opts)
);
}
if ('node' in obj) {
if (obj.node.LedgerEntryType === 'AccountRoot') {
out.node.PreviousTxnID =
build_link(
obj.node.PreviousTxnID,
build_uri({
type: 'transaction',
hash: obj.node.PreviousTxnID,
}, opts)
);
}
}
return out;
};
if (process.argv.length < 4 || process.argv.length > 7) {
console.log("Usage: %s ws_ip ws_port [<ip> [<port> [<start>]]]", program);
}
else {
var ws_ip = process.argv[2];
var ws_port = process.argv[3];
var ip = process.argv.length > 4 ? process.argv[4] : "127.0.0.1";
var port = process.argv.length > 5 ? process.argv[5] : "8080";
console.log("START");
var self = this;
self.base = {
hostname: ip,
port: port,
};
var remote = (new Remote({
websocket_ip: ws_ip,
websocket_port: ws_port,
trace: true
}))
.on('state', function (m) {
console.log("STATE: %s", m);
self.state = m;
})
// .once('ledger_closed', callback)
.connect()
;
console.log("SERVE");
var server = http.createServer(function (req, res) {
var input = "";
req.setEncoding();
req.on('data', function (buffer) {
// console.log("DATA: %s", buffer);
input = input + buffer;
});
req.on('end', function () {
console.log("URL: %s", req.url);
// console.log("HEADERS: %s", JSON.stringify(req.headers, undefined, 2));
var _parsed = url.parse(req.url, true);
var _url = JSON.stringify(_parsed, undefined, 2);
if (_parsed.pathname === "/account_root") {
var request = remote
.request_ledger_entry('account_root')
.ledger_index(-1)
.account_root(_parsed.query.a)
.on('success', function (m) {
console.log("account_root: %s", JSON.stringify(m, undefined, 2));
res.statusCode = 200;
res.end(
"<HTML>"
+ "<HEAD><TITLE>Title</TITLE></HEAD>"
+ "<BODY BACKGROUND=\"#FFFFFF\">"
+ "State: " + self.state
+ "<UL>"
+ "<LI><A HREF=\"/\">home</A>"
+ "<LI><A HREF=\"/account_root\">account_root</A>"
+ "</UL>"
+ "<PRE>"
+ JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+ "</PRE>"
+ "</BODY>"
+ "</HTML>"
);
})
.request();
} else if (_parsed.pathname === "/ledger_header") {
var request = remote
.request_ledger_header()
.ledger_index(-1)
.on('success', function (m) {
console.log("Ledger: %s", JSON.stringify(m, undefined, 2));
res.statusCode = 200;
res.end(
"<HTML>"
+ "<HEAD><TITLE>Title</TITLE></HEAD>"
+ "<BODY BACKGROUND=\"#FFFFFF\">"
+ "State: " + self.state
+ "<UL>"
+ "<LI><A HREF=\"/\">home</A>"
+ "<LI><A HREF=\"/ledger_header\">ledger_header</A>"
+ "</UL>"
+ "<PRE>"
+ JSON.stringify(m, undefined, 2)
+ "</PRE>"
+ "</BODY>"
+ "</HTML>"
);
})
.request();
} else if (_parsed.pathname === "/transaction") {
var request = remote
.request_transaction_entry(_parsed.query.h)
// .ledger_select(_parsed.query.l)
.on('success', function (m) {
console.log("transaction: %s", JSON.stringify(m, undefined, 2));
res.statusCode = 200;
res.end(
"<HTML>"
+ "<HEAD><TITLE>Title</TITLE></HEAD>"
+ "<BODY BACKGROUND=\"#FFFFFF\">"
+ "State: " + self.state
+ "<UL>"
+ "<LI><A HREF=\"/\">home</A>"
+ "<LI><A HREF=\"/account_root\">account_root</A>"
+ "</UL>"
+ "<PRE>"
+ JSON.stringify(rewrite_object(m, self.base), undefined, 2)
+ "</PRE>"
+ "</BODY>"
+ "</HTML>"
);
})
.request();
} else {
var test = build_uri({
type: 'account_root',
ledger: 'closed',
account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
}, self.base);
res.statusCode = req.url === "/" ? 200 : 404;
res.end(
"<HTML>"
+ "<HEAD><TITLE>Title</TITLE></HEAD>"
+ "<BODY BACKGROUND=\"#FFFFFF\">"
+ "State: " + self.state
+ "<UL>"
+ "<LI><A HREF=\"/\">home</A>"
+ "<LI><A HREF=\""+test+"\">rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh</A>"
+ "<LI><A HREF=\"/ledger_header\">ledger_header</A>"
+ "</UL>"
+ "<PRE>"+_url+"</PRE>"
+ "</BODY>"
+ "</HTML>"
);
}
});
});
server.listen(port, ip, undefined,
function () {
console.log("Listening at: http://%s:%s", ip, port);
});
}
// vim:sw=2:sts=2:ts=8:et

View File

@@ -96,6 +96,7 @@
<ClCompile Include="src\cpp\json\json_reader.cpp" />
<ClCompile Include="src\cpp\json\json_value.cpp" />
<ClCompile Include="src\cpp\json\json_writer.cpp" />
<ClCompile Include="src\cpp\ripple\AcceptedLedger.cpp" />
<ClCompile Include="src\cpp\ripple\AccountItems.cpp" />
<ClCompile Include="src\cpp\ripple\AccountSetTransactor.cpp" />
<ClCompile Include="src\cpp\ripple\AccountState.cpp" />
@@ -205,6 +206,7 @@
<ClInclude Include="database\sqlite3.h" />
<ClInclude Include="database\sqlite3ext.h" />
<ClInclude Include="database\SqliteDatabase.h" />
<ClInclude Include="src\cpp\ripple\AcceptedLedger.h" />
<ClInclude Include="src\cpp\ripple\AccountItems.h" />
<ClInclude Include="src\cpp\ripple\AccountSetTransactor.h" />
<ClInclude Include="src\cpp\ripple\AccountState.h" />

View File

@@ -345,6 +345,9 @@
<ClCompile Include="src\cpp\ripple\Offer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\AcceptedLedger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\AccountItems.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -662,6 +665,9 @@
<ClInclude Include="src\cpp\ripple\Offer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cpp\ripple\AcceptedLedger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cpp\ripple\AccountItems.h">
<Filter>Header Files</Filter>
</ClInclude>

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.7.0",
"version": "0.7.1",
"description": "Ripple JavaScript client library",
"files": [

View File

@@ -94,6 +94,7 @@
<ClCompile Include="src\cpp\json\json_reader.cpp" />
<ClCompile Include="src\cpp\json\json_value.cpp" />
<ClCompile Include="src\cpp\json\json_writer.cpp" />
<ClCompile Include="src\cpp\ripple\AcceptedLedger.cpp" />
<ClCompile Include="src\cpp\ripple\AccountItems.cpp" />
<ClCompile Include="src\cpp\ripple\AccountSetTransactor.cpp" />
<ClCompile Include="src\cpp\ripple\AccountState.cpp" />

View File

@@ -345,6 +345,9 @@
<ClCompile Include="src\cpp\ripple\LoadManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\AcceptedLedger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpp\ripple\AccountItems.cpp">
<Filter>Source Files</Filter>
</ClCompile>

View File

@@ -223,7 +223,7 @@ void SqliteDatabase::doHook(const char *db, int pages)
{
walRunning = true;
if (mWalQ)
mWalQ->addJob(jtWAL, boost::bind(&SqliteDatabase::runWal, this));
mWalQ->addJob(jtWAL, std::string("WAL:") + db, boost::bind(&SqliteDatabase::runWal, this));
else
boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach();
}

View File

@@ -0,0 +1,91 @@
#include "AcceptedLedger.h"
#include <boost/foreach.hpp>
TaggedCache<uint256, AcceptedLedger> AcceptedLedger::ALCache("AcceptedLedger", 4, 60);
ALTransaction::ALTransaction(uint32 seq, SerializerIterator& sit)
{
Serializer txnSer(sit.getVL());
SerializerIterator txnIt(txnSer);
mTxn = boost::make_shared<SerializedTransaction>(boost::ref(txnIt));
mRawMeta= sit.getVL();
mMeta = boost::make_shared<TransactionMetaSet>(mTxn->getTransactionID(), seq, mRawMeta);
mAffected = mMeta->getAffectedAccounts();
mResult = mMeta->getResultTER();
}
ALTransaction::ALTransaction(SerializedTransaction::ref txn, TransactionMetaSet::ref met) :
mTxn(txn), mMeta(met), mAffected(met->getAffectedAccounts())
{
mResult = mMeta->getResultTER();
}
ALTransaction::ALTransaction(SerializedTransaction::ref txn, TER result) :
mTxn(txn), mResult(result), mAffected(txn->getMentionedAccounts())
{ ; }
std::string ALTransaction::getEscMeta() const
{
assert(!mRawMeta.empty());
return sqlEscape(mRawMeta);
}
Json::Value ALTransaction::getJson(int j) const
{
Json::Value ret(Json::objectValue);
ret["transaction"] = mTxn->getJson(j);
if (mMeta)
{
ret["meta"] = mMeta->getJson(j);
ret["raw_meta"] = strHex(mRawMeta);
}
ret["result"] = transHuman(mResult);
if (!mAffected.empty())
{
Json::Value affected(Json::arrayValue);
BOOST_FOREACH(const RippleAddress& ra, mAffected)
{
affected.append(ra.humanAccountID());
}
ret["affected"] = affected;
}
return ret;
}
AcceptedLedger::AcceptedLedger(Ledger::ref ledger) : mLedger(ledger)
{
SHAMap& txSet = *ledger->peekTransactionMap();
for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag()))
{
SerializerIterator sit(item->peekSerializer());
insert(ALTransaction(ledger->getLedgerSeq(), sit));
}
}
AcceptedLedger::pointer AcceptedLedger::makeAcceptedLedger(Ledger::ref ledger)
{
AcceptedLedger::pointer ret = ALCache.fetch(ledger->getHash());
if (ret)
return ret;
ret = AcceptedLedger::pointer(new AcceptedLedger(ledger));
ALCache.canonicalize(ledger->getHash(), ret);
return ret;
}
void AcceptedLedger::insert(const ALTransaction& at)
{
assert(mMap.find(at.getIndex()) == mMap.end());
mMap.insert(std::make_pair(at.getIndex(), at));
}
const ALTransaction* AcceptedLedger::getTxn(int i) const
{
map_t::const_iterator it = mMap.find(i);
if (it == mMap.end())
return NULL;
return &it->second;
}

View File

@@ -0,0 +1,70 @@
#ifndef ACCEPTED_LEDGER__H
#define ACCEPTED_LEDGER__H
#include "SerializedTransaction.h"
#include "TransactionMeta.h"
#include "Ledger.h"
class ALTransaction
{
protected:
SerializedTransaction::pointer mTxn;
TransactionMetaSet::pointer mMeta;
TER mResult;
std::vector<RippleAddress> mAffected;
std::vector<unsigned char> mRawMeta;
public:
ALTransaction(uint32 ledgerSeq, SerializerIterator& sit);
ALTransaction(SerializedTransaction::ref, TransactionMetaSet::ref);
ALTransaction(SerializedTransaction::ref, TER result);
SerializedTransaction::ref getTxn() const { return mTxn; }
TransactionMetaSet::ref getMeta() const { return mMeta; }
const std::vector<RippleAddress>& getAffected() const { return mAffected; }
uint256 getTransactionID() const { return mTxn->getTransactionID(); }
TransactionType getTxnType() const { return mTxn->getTxnType(); }
TER getResult() const { return mResult; }
bool isApplied() const { return !!mMeta; }
int getIndex() const { return mMeta ? mMeta->getIndex() : 0; }
std::string getEscMeta() const;
Json::Value getJson(int) const;
};
class AcceptedLedger
{
public:
typedef boost::shared_ptr<AcceptedLedger> pointer;
typedef const pointer& ret;
typedef std::map<int, ALTransaction> map_t;
typedef map_t::value_type value_type;
typedef map_t::const_iterator const_iterator;
protected:
Ledger::pointer mLedger;
map_t mMap;
void insert(const ALTransaction&);
static TaggedCache<uint256, AcceptedLedger> ALCache;
AcceptedLedger(Ledger::ref ledger);
public:
static pointer makeAcceptedLedger(Ledger::ref ledger);
static void sweep() { ALCache.sweep(); }
Ledger::ref getLedger() const { return mLedger; }
const map_t& getMap() const { return mMap; }
int getLedgerSeq() const { return mLedger->getLedgerSeq(); }
int getTxnCount() const { return mMap.size(); }
const ALTransaction* getTxn(int) const;
};
#endif

View File

@@ -1,5 +1,6 @@
#include "Application.h"
#include "AcceptedLedger.h"
#include "Config.h"
#include "PeerDoor.h"
#include "RPCDoor.h"
@@ -45,7 +46,7 @@ Application::Application() :
mSNTPClient(mAuxService), mRPCHandler(&mNetOps), mFeeTrack(),
mRpcDB(NULL), mTxnDB(NULL), mLedgerDB(NULL), mWalletDB(NULL), mHashNodeDB(NULL), mNetNodeDB(NULL),
mConnectionPool(mIOService), mPeerDoor(NULL), mRPCDoor(NULL), mWSPublicDoor(NULL), mWSPrivateDoor(NULL),
mSweepTimer(mAuxService)
mSweepTimer(mAuxService), mShutdown(false)
{
getRand(mNonce256.begin(), mNonce256.size());
getRand(reinterpret_cast<unsigned char *>(&mNonceST), sizeof(mNonceST));
@@ -58,6 +59,7 @@ bool Instance::running = true;
void Application::stop()
{
cLog(lsINFO) << "Received shutdown request";
mShutdown = true;
mIOService.stop();
mHashedObjectStore.bulkWrite();
mValidations.flush();
@@ -302,6 +304,7 @@ void Application::sweep()
mValidations.sweep();
getMasterLedgerAcquire().sweep();
mSLECache.sweep();
AcceptedLedger::sweep();
mSweepTimer.expires_from_now(boost::posix_time::seconds(theConfig.getSize(siSweepInterval)));
mSweepTimer.async_wait(boost::bind(&Application::sweep, this));
}

View File

@@ -87,6 +87,8 @@ class Application
std::map<std::string, Peer::pointer> mPeerMap;
boost::recursive_mutex mPeerMapLock;
volatile bool mShutdown;
void startNewLedger();
void loadOldLedger(const std::string&);
@@ -140,6 +142,7 @@ public:
uint256 getNonce256() { return mNonce256; }
std::size_t getNonceST() { return mNonceST; }
bool isShutdown() { return mShutdown; }
void setup();
void run();
void stop();

View File

@@ -1,3 +1,9 @@
//
// This a trusted interface, the user is expected to provide valid input to perform valid requests.
// Error catching and reporting is not a requirement of this command line interface.
//
// Improvements to be more strict and to provide better diagnostics are welcome.
//
#include <iostream>
#include <cstdlib>
@@ -9,6 +15,7 @@
#include <boost/iostreams/stream.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>
#include <openssl/buffer.h>
#include <openssl/evp.h>
@@ -55,6 +62,63 @@ std::string EncodeBase64(const std::string& s)
return result;
}
// TODO New routine for parsing ledger parameters, other routines should standardize on this.
static bool jvParseLedger(Json::Value& jvRequest, const std::string& strLedger)
{
if (strLedger == "closed")
{
jvRequest["ledger_index"] = -1;
}
else if (strLedger == "current")
{
jvRequest["ledger_index"] = -2;
}
else if (strLedger == "validated")
{
jvRequest["ledger_index"] = -3;
}
else if (strLedger.length() > 12)
{
// YYY Could confirm this is a uint256.
jvRequest["ledger_hash"] = strLedger;
}
else
{
jvRequest["ledger_index"] = lexical_cast_s<uint32>(strLedger);
}
return true;
}
// Build a object { "currency" : "XYZ", "issuer" : "rXYX" }
static Json::Value jvParseCurrencyIssuer(const std::string& strCurrencyIssuer)
{
static boost::regex reCurIss("\\`([[:alpha:]]{3})(?:/(.+))?\\'");
boost::smatch smMatch;
if (boost::regex_match(strCurrencyIssuer, smMatch, reCurIss))
{
Json::Value jvResult(Json::objectValue);
std::string strCurrency = smMatch[1];
std::string strIssuer = smMatch[2];
jvResult["currency"] = strCurrency;
if (strIssuer.length())
{
// Could confirm issuer is a valid Ripple address.
jvResult["issuer"] = strIssuer;
}
return jvResult;
}
else
{
return rpcError(rpcINVALID_PARAMS);
}
}
Json::Value RPCParser::parseAsIs(const Json::Value& jvParams)
{
Json::Value v(Json::objectValue);
@@ -145,6 +209,63 @@ Json::Value RPCParser::parseAccountTransactions(const Json::Value& jvParams)
return jvRequest;
}
// book_offers <taker_pays> <taker_gets> [<taker> [<ledger> [<limit> [<proof> [<marker>]]]]]
// limit: 0 = no limit
// proof: 0 or 1
//
// Mnemonic: taker pays --> offer --> taker gets
Json::Value RPCParser::parseBookOffers(const Json::Value& jvParams)
{
Json::Value jvRequest(Json::objectValue);
Json::Value jvTakerPays = jvParseCurrencyIssuer(jvParams[0u].asString());
Json::Value jvTakerGets = jvParseCurrencyIssuer(jvParams[1u].asString());
if (isRpcError(jvTakerPays))
{
return jvTakerPays;
}
else
{
jvRequest["taker_pays"] = jvTakerPays;
}
if (isRpcError(jvTakerGets))
{
return jvTakerGets;
}
else
{
jvRequest["taker_gets"] = jvTakerGets;
}
if (jvParams.size() >= 3)
{
jvRequest["issuer"] = jvParams[2u].asString();
}
if (jvParams.size() >= 4 && !jvParseLedger(jvRequest, jvParams[3u].asString()))
return jvRequest;
if (jvParams.size() >= 5)
{
int iLimit = jvParams[5u].asInt();
if (iLimit > 0)
jvRequest["limit"] = iLimit;
}
if (jvParams.size() >= 6 && jvParams[5u].asInt())
{
jvRequest["proof"] = true;
}
if (jvParams.size() == 7)
jvRequest["marker"] = jvParams[6u];
return jvRequest;
}
// connect <ip> [port]
Json::Value RPCParser::parseConnect(const Json::Value& jvParams)
{
@@ -509,11 +630,11 @@ Json::Value RPCParser::parseCommand(std::string strMethod, Json::Value jvParams)
// Request-response methods
// - Returns an error, or the request.
// - To modify the method, provide a new method in the request.
{ "accept_ledger", &RPCParser::parseAsIs, 0, 0 },
{ "account_info", &RPCParser::parseAccountInfo, 1, 2 },
{ "account_lines", &RPCParser::parseAccountItems, 1, 2 },
{ "account_offers", &RPCParser::parseAccountItems, 1, 2 },
{ "account_tx", &RPCParser::parseAccountTransactions, 2, 4 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 },
{ "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 },
{ "get_counts", &RPCParser::parseGetCounts, 0, 1 },
@@ -622,7 +743,7 @@ int commandLineRPC(const std::vector<std::string>& vCmd)
jvRequest = rpParser.parseCommand(vCmd[0], jvRpcParams);
// std::cerr << "Request: " << jvRequest << std::endl;
cLog(lsTRACE) << "RPC Request: " << jvRequest << std::endl;
if (jvRequest.isMember("error"))
{
@@ -756,6 +877,8 @@ bool responseRPC(
// Build the request.
void requestRPC(const std::string& strMethod, const Json::Value& jvParams, const std::map<std::string, std::string>& mHeaders, const std::string& strPath, boost::asio::streambuf& sb, const std::string& strHost)
{
cLog(lsDEBUG) << "requestRPC: strPath='" << strPath << "'";
std::ostream osRequest(&sb);
osRequest <<
@@ -804,7 +927,7 @@ void callRPC(
strMethod,
jvParams,
mapRequestHeaders,
"/", _1, _2),
strPath, _1, _2),
RPC_REPLY_MAX_BYTES,
boost::posix_time::seconds(RPC_NOTIFY_SECONDS),
boost::bind(&responseRPC, callbackFuncP, _1, _2, _3));

View File

@@ -14,6 +14,7 @@ protected:
Json::Value parseAccountItems(const Json::Value& jvParams);
Json::Value parseAccountTransactions(const Json::Value& jvParams);
Json::Value parseAsIs(const Json::Value& jvParams);
Json::Value parseBookOffers(const Json::Value& jvParams);
Json::Value parseConnect(const Json::Value& jvParams);
#if ENABLE_INSECURE
Json::Value parseDataDelete(const Json::Value& jvParams);

View File

@@ -235,14 +235,10 @@ void ConnectionPool::policyHandler(const boost::system::error_code& ecResult)
int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& msg)
{
int sentTo = 0;
boost::recursive_mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
std::vector<Peer::pointer> peerVector = getPeerVector();
BOOST_FOREACH(Peer::ref peer, peerVector)
{
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())
if ((!fromPeer || !(peer.get() == fromPeer)) && peer->isConnected())
{
++sentTo;
peer->sendPacket(msg);
@@ -254,11 +250,9 @@ int ConnectionPool::relayMessage(Peer* fromPeer, const PackedMessage::pointer& m
void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
{ // Relay message to all but the specified peers
boost::recursive_mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(const vtConMap& pair, mConnectedMap)
std::vector<Peer::pointer> peerVector = getPeerVector();
BOOST_FOREACH(Peer::ref peer, peerVector)
{
Peer::ref peer = pair.second;
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) == 0))
peer->sendPacket(msg);
}
@@ -267,13 +261,11 @@ void ConnectionPool::relayMessageBut(const std::set<uint64>& fromPeers, const Pa
void ConnectionPool::relayMessageTo(const std::set<uint64>& fromPeers, const PackedMessage::pointer& msg)
{ // Relay message to the specified peers
boost::recursive_mutex::scoped_lock sl(mPeerLock);
BOOST_FOREACH(const uint64& peerID, fromPeers)
std::vector<Peer::pointer> peerVector = getPeerVector();
BOOST_FOREACH(Peer::ref peer, peerVector)
{
const boost::unordered_map<uint64, Peer::pointer>::iterator& it = mPeerIdMap.find(peerID);
if ((it != mPeerIdMap.end()) && it->second->isConnected())
it->second->sendPacket(msg);
if (peer->isConnected() && (fromPeers.count(peer->getPeerId()) != 0))
peer->sendPacket(msg);
}
}

View File

@@ -20,7 +20,7 @@ const char *TxnDBInit[] = {
TxnMeta BLOB \
);",
"CREATE TABLE AccountTransactions ( \
TransID CHARACTER(64), \
TransID CHARACTER(64) PRIMARY KEY, \
Account CHARACTER(64), \
LedgerSeq BIGINT UNSIGNED \
);",

View File

@@ -12,6 +12,7 @@
// These must stay at the top of this file
std::map<int, SField::ptr> SField::codeToField;
boost::mutex SField::mapMutex;
int SField::num = 0;
SField sfInvalid(-1), sfGeneric(0);
SField sfLedgerEntry(STI_LEDGERENTRY, 1, "LedgerEntry");

View File

@@ -51,6 +51,7 @@ public:
protected:
static std::map<int, ptr> codeToField;
static boost::mutex mapMutex;
static int num;
SField(SerializedTypeID id, int val);
@@ -61,6 +62,7 @@ public:
const int fieldValue; // Code number for protocol
std::string fieldName;
int fieldMeta;
int fieldNum;
bool signingField;
SField(int fc, SerializedTypeID tid, int fv, const char* fn) :
@@ -68,6 +70,7 @@ public:
{
boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this;
fieldNum = ++num;
}
SField(SerializedTypeID tid, int fv, const char *fn) :
@@ -76,9 +79,14 @@ public:
{
boost::mutex::scoped_lock sl(mapMutex);
codeToField[fieldCode] = this;
fieldNum = ++num;
}
SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never) { ; }
SField(int fc) : fieldCode(fc), fieldType(STI_UNKNOWN), fieldValue(0), fieldMeta(sMD_Never)
{
boost::mutex::scoped_lock sl(mapMutex);
fieldNum = ++num;
}
~SField();
@@ -96,6 +104,9 @@ public:
bool isKnown() const { return fieldType != STI_UNKNOWN; }
bool isBinary() const { return fieldValue < 256; }
bool isDiscardable() const { return fieldValue > 256; }
int getCode() const { return fieldCode; }
int getNum() const { return fieldNum; }
static int getNumFields() { return num; }
bool isSigningField() const { return signingField; }
void notSigningField() { signingField = false; }

View File

@@ -51,7 +51,8 @@ bool HashedObjectStore::store(HashedObjectType type, uint32 index,
if (!mWritePending)
{
mWritePending = true;
theApp->getJobQueue().addJob(jtWRITE, boost::bind(&HashedObjectStore::bulkWrite, this));
theApp->getJobQueue().addJob(jtWRITE, "HashedObject::store",
boost::bind(&HashedObjectStore::bulkWrite, this));
}
}
// else
@@ -113,7 +114,6 @@ void HashedObjectStore::bulkWrite()
default: type = "U";
}
pSt.reset();
pSt.bind(1, it->getHash().GetHex());
pSt.bind(2, type);
pSt.bind(3, it->getIndex());
@@ -124,6 +124,7 @@ void HashedObjectStore::bulkWrite()
cLog(lsFATAL) << "Error saving hashed object " << ret;
assert(false);
}
pSt.reset();
}
db->executeSQL("END TRANSACTION;");
@@ -186,12 +187,12 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
static SqliteStatement pSt(theApp->getHashNodeDB()->getDB()->getSqliteDB(),
"SELECT ObjType,LedgerIndex,Object FROM CommittedObjects WHERE Hash = ?;");
pSt.reset();
pSt.bind(1, hash.GetHex());
int ret = pSt.step();
if (pSt.isDone(ret))
{
pSt.reset();
mNegativeCache.add(hash);
cLog(lsTRACE) << "HOS: " << hash <<" fetch: not in db";
return obj;
@@ -200,6 +201,7 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
type = pSt.peekString(0);
index = pSt.getUInt32(1);
pSt.getBlob(2).swap(data);
pSt.reset();
}
#else

View File

@@ -370,10 +370,14 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, int
if (ecCancel)
{
cLog(lsTRACE) << "Deadline cancel error: " << ecCancel.message();
cLog(lsTRACE) << "HttpsClient::invokeComplete: Deadline cancel error: " << ecCancel.message();
}
mDeqSites.pop_front();
cLog(lsDEBUG) << "HttpsClient::invokeComplete: Deadline popping: " << mDeqSites.size();
if (!mDeqSites.empty())
{
mDeqSites.pop_front();
}
bool bAgain = true;

View File

@@ -50,6 +50,11 @@ public:
sMultiThreaded = true;
}
static bool isMultiThread()
{
return sMultiThreaded;
}
void addInstance()
{
if (sMultiThreaded)

View File

@@ -98,7 +98,7 @@ bool Job::operator<=(const Job& j) const
return mJobIndex <= j.mJobIndex;
}
void JobQueue::addJob(JobType type, const boost::function<void(Job&)>& jobFunc)
void JobQueue::addJob(JobType type, const std::string& name, const boost::function<void(Job&)>& jobFunc)
{
assert(type != jtINVALID);
@@ -107,7 +107,7 @@ void JobQueue::addJob(JobType type, const boost::function<void(Job&)>& jobFunc)
if (type != jtCLIENT) // FIXME: Workaround incorrect client shutdown ordering
assert(mThreadCount != 0); // do not add jobs to a queue with no threads
mJobSet.insert(Job(type, ++mLastJob, mJobLoads[type], jobFunc));
mJobSet.insert(Job(type, name, ++mLastJob, mJobLoads[type], jobFunc));
++mJobCounts[type];
mJobCond.notify_one();
}
@@ -176,9 +176,9 @@ Json::Value JobQueue::getJson(int)
if (count != 0)
pri["per_second"] = static_cast<int>(count);
if (latencyPeak != 0)
pri["peak_latency"] = static_cast<int>(latencyPeak);
pri["peak_time"] = static_cast<int>(latencyPeak);
if (latencyAvg != 0)
pri["avg_latency"] = static_cast<int>(latencyAvg);
pri["avg_time"] = static_cast<int>(latencyAvg);
priorities.append(pri);
}
}
@@ -256,16 +256,18 @@ void JobQueue::threadEntry()
break;
std::set<Job>::iterator it = mJobSet.begin();
Job job(*it);
mJobSet.erase(it);
--mJobCounts[job.getType()];
{
Job job(*it);
mJobSet.erase(it);
--mJobCounts[job.getType()];
if (job.getType() == jtDEATH)
break;
if (job.getType() == jtDEATH)
break;
sl.unlock();
cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job";
job.doJob();
sl.unlock();
cLog(lsTRACE) << "Doing " << Job::toString(job.getType()) << " job";
job.doJob();
} // must destroy job without holding lock
sl.lock();
}
--mThreadCount;

View File

@@ -54,6 +54,7 @@ protected:
uint64 mJobIndex;
boost::function<void(Job&)> mJob;
LoadEvent::pointer mLoadMonitor;
std::string mName;
public:
@@ -62,12 +63,15 @@ public:
Job(JobType type, uint64 index) : mType(type), mJobIndex(index)
{ ; }
Job(JobType type, uint64 index, LoadMonitor& lm, const boost::function<void(Job&)>& job)
: mType(type), mJobIndex(index), mJob(job)
{ mLoadMonitor = boost::make_shared<LoadEvent>(boost::ref(lm), true, 1); }
Job(JobType type, const std::string& name, uint64 index, LoadMonitor& lm, const boost::function<void(Job&)>& job)
: mType(type), mJobIndex(index), mJob(job), mName(name)
{
mLoadMonitor = boost::make_shared<LoadEvent>(boost::ref(lm), false, 1);
}
JobType getType() const { return mType; }
void doJob(void) { mJob(*this); }
void doJob(void) { mLoadMonitor->start(); mJob(*this); mLoadMonitor->setName(mName); }
void rename(const std::string& n) { mName = n; }
bool operator<(const Job& j) const;
bool operator>(const Job& j) const;
@@ -97,7 +101,7 @@ public:
JobQueue();
void addJob(JobType type, const boost::function<void(Job&)>& job);
void addJob(JobType type, const std::string& name, const boost::function<void(Job&)>& job);
int getJobCount(JobType t); // Jobs at this priority
int getJobCountGE(JobType t); // All jobs at or greater than this priority

View File

@@ -403,9 +403,9 @@ uint256 Ledger::getHash()
void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
{ // can be called in a different thread
cLog(lsTRACE) << "saveAcceptedLedger " << (fromConsensus ? "fromConsensus " : "fromAcquire ") << getLedgerSeq();
static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers where LedgerSeq = %d;");
static boost::format ledgerExists("SELECT LedgerSeq FROM Ledgers INDEXED BY SeqLedger where LedgerSeq = %d;");
static boost::format deleteLedger("DELETE FROM Ledgers WHERE LedgerSeq = %d;");
static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransId = '%s';");
static boost::format AcctTransExists("SELECT LedgerSeq FROM AccountTransactions WHERE TransID = '%s';");
static boost::format transExists("SELECT Status FROM Transactions WHERE TransID = '%s';");
static boost::format
updateTx("UPDATE Transactions SET LedgerSeq = %d, Status = '%c', TxnMeta = %s WHERE TransID = '%s';");
@@ -428,6 +428,8 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
addRaw(s);
theApp->getHashedObjectStore().store(hotLEDGER, mLedgerSeq, s.peekData(), mHash);
AcceptedLedger::pointer aLedger = AcceptedLedger::makeAcceptedLedger(shared_from_this());
{
{
ScopedLock sl(theApp->getLedgerDB()->getDBLock());
@@ -435,37 +437,26 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
theApp->getLedgerDB()->getDB()->executeSQL(boost::str(deleteLedger % mLedgerSeq));
}
SHAMap& txSet = *peekTransactionMap();
Database *db = theApp->getTxnDB()->getDB();
{
ScopedLock dbLock(theApp->getTxnDB()->getDBLock());
db->executeSQL("BEGIN TRANSACTION;");
SHAMapTreeNode::TNType type;
for (SHAMapItem::pointer item = txSet.peekFirstItem(type); !!item;
item = txSet.peekNextItem(item->getTag(), type))
BOOST_FOREACH(const AcceptedLedger::value_type& vt, aLedger->getMap())
{
assert(type == SHAMapTreeNode::tnTRANSACTION_MD);
SerializerIterator sit(item->peekSerializer());
Serializer rawTxn(sit.getVL());
Serializer rawMeta(sit.getVL());
std::string escMeta(sqlEscape(rawMeta.peekData()));
SerializerIterator txnIt(rawTxn);
SerializedTransaction txn(txnIt);
assert(txn.getTransactionID() == item->getTag());
TransactionMetaSet meta(item->getTag(), mLedgerSeq, rawMeta.peekData());
theApp->getMasterTransaction().inLedger(item->getTag(), mLedgerSeq);
cLog(lsTRACE) << "Saving: " << vt.second.getJson(0);
uint256 txID = vt.second.getTransactionID();
theApp->getMasterTransaction().inLedger(txID, mLedgerSeq);
// Make sure transaction is in AccountTransactions.
if (!SQL_EXISTS(db, boost::str(AcctTransExists % item->getTag().GetHex())))
if (!SQL_EXISTS(db, boost::str(AcctTransExists % txID.GetHex())))
{
// Transaction not in AccountTransactions
const std::vector<RippleAddress> accts = meta.getAffectedAccounts();
const std::vector<RippleAddress>& accts = vt.second.getAffected();
if (!accts.empty())
{
std::string sql = "INSERT INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES ";
std::string sql = "INSERT OR REPLACE INTO AccountTransactions (TransID, Account, LedgerSeq) VALUES ";
bool first = true;
for (std::vector<RippleAddress>::const_iterator it = accts.begin(), end = accts.end(); it != end; ++it)
{
@@ -476,7 +467,7 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
sql += "('";
first = false;
}
sql += txn.getTransactionID().GetHex();
sql += txID.GetHex();
sql += "','";
sql += it->humanAccountID();
sql += "',";
@@ -491,20 +482,21 @@ void Ledger::saveAcceptedLedger(bool fromConsensus, LoadEvent::pointer event)
cLog(lsWARNING) << "Transaction in ledger " << mLedgerSeq << " affects no accounts";
}
if (SQL_EXISTS(db, boost::str(transExists % txn.getTransactionID().GetHex())))
if (SQL_EXISTS(db, boost::str(transExists % txID.GetHex())))
{
// In Transactions, update LedgerSeq, metadata and Status.
db->executeSQL(boost::str(updateTx
% getLedgerSeq()
% TXN_SQL_VALIDATED
% escMeta
% txn.getTransactionID().GetHex()));
% vt.second.getEscMeta()
% txID.GetHex()));
}
else
{
// Not in Transactions, insert the whole thing..
db->executeSQL(
txn.getMetaSQLInsertHeader() + txn.getMetaSQL(getLedgerSeq(), escMeta) + ";");
SerializedTransaction::getMetaSQLInsertHeader() +
vt.second.getTxn()->getMetaSQL(getLedgerSeq(), vt.second.getEscMeta()) + ";");
}
}
db->executeSQL("COMMIT TRANSACTION;");
@@ -548,9 +540,9 @@ Ledger::pointer Ledger::loadByIndex(uint32 ledgerIndex)
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
" from Ledgers WHERE LedgerSeq = ?;");
pSt.reset();
pSt.bind(1, ledgerIndex);
ledger = getSQL1(&pSt);
pSt.reset();
}
if (ledger)
Ledger::getSQL2(ledger);
@@ -569,9 +561,9 @@ Ledger::pointer Ledger::loadByHash(const uint256& ledgerHash)
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
" from Ledgers WHERE LedgerHash = ?;");
pSt.reset();
pSt.bind(1, ledgerHash.GetHex());
ledger = getSQL1(&pSt);
pSt.reset();
}
if (ledger)
{
@@ -729,19 +721,20 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
ScopedLock sl(con->getDBLock());
static SqliteStatement pSt(con->getDB()->getSqliteDB(),
"SELECT LedgerHash,PrevHash FROM Ledgers Where LedgerSeq = ?;");
"SELECT LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger Where LedgerSeq = ?;");
pSt.reset();
pSt.bind(1, ledgerIndex);
int ret = pSt.step();
if (pSt.isDone(ret))
{
pSt.reset();
cLog(lsTRACE) << "Don't have ledger " << ledgerIndex;
return false;
}
if (!pSt.isRow(ret))
{
pSt.reset();
assert(false);
cLog(lsFATAL) << "Unexpected statement result " << ret;
return false;
@@ -749,6 +742,7 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
ledgerHash.SetHex(pSt.peekString(0), true);
parentHash.SetHex(pSt.peekString(1), true);
pSt.reset();
return true;
@@ -779,6 +773,45 @@ bool Ledger::getHashesByIndex(uint32 ledgerIndex, uint256& ledgerHash, uint256&
#endif
}
std::map< uint32, std::pair<uint256, uint256> > Ledger::getHashesByIndex(uint32 minSeq, uint32 maxSeq)
{
#ifndef NO_SQLITE_PREPARE
std::map< uint32, std::pair<uint256, uint256> > ret;
DatabaseCon *con = theApp->getLedgerDB();
ScopedLock sl(con->getDBLock());
static SqliteStatement pSt(con->getDB()->getSqliteDB(),
"SELECT LedgerSeq,LedgerHash,PrevHash FROM Ledgers INDEXED BY SeqLedger "
"WHERE LedgerSeq >= ? AND LedgerSeq <= ?;");
std::pair<uint256, uint256> hashes;
pSt.bind(1, minSeq);
pSt.bind(2, maxSeq);
do
{
int r = pSt.step();
if (pSt.isDone(r))
{
pSt.reset();
return ret;
}
if (!pSt.isRow(r))
{
pSt.reset();
return ret;
}
hashes.first.SetHex(pSt.peekString(1), true);
hashes.second.SetHex(pSt.peekString(2), true);
ret[pSt.getUInt32(0)] = hashes;
} while(1);
#else
#error SQLite prepare is required
#endif
}
Ledger::pointer Ledger::getLastFullLedger()
{
try

View File

@@ -202,6 +202,7 @@ public:
static Ledger::pointer loadByHash(const uint256& ledgerHash);
static uint256 getHashByIndex(uint32 index);
static bool getHashesByIndex(uint32 index, uint256& ledgerHash, uint256& parentHash);
static std::map< uint32, std::pair<uint256, uint256> > getHashesByIndex(uint32 minSeq, uint32 maxSeq);
void pendSave(bool fromConsensus);
// next/prev function

View File

@@ -392,7 +392,7 @@ void LedgerConsensus::checkLCL()
{
case lcsPRE_CLOSE: status = "PreClose"; break;
case lcsESTABLISH: status = "Establish"; break;
case lcsFINISHED: status = "Finised"; break;
case lcsFINISHED: status = "Finished"; break;
case lcsACCEPTED: status = "Accepted"; break;
default: status = "unknown";
}
@@ -400,6 +400,7 @@ void LedgerConsensus::checkLCL()
cLog(lsWARNING) << "View of consensus changed during " << status << " (" << netLgrCount << ") status="
<< status << ", " << (mHaveCorrectLCL ? "CorrectLCL" : "IncorrectLCL");
cLog(lsWARNING) << mPrevLedgerHash << " to " << netLgr;
cLog(lsWARNING) << mPreviousLedger->getJson(0);
if (sLog(lsDEBUG))
{
@@ -762,6 +763,7 @@ void LedgerConsensus::updateOurPositions()
int neededWeight;
if (mClosePercent < AV_MID_CONSENSUS_TIME)
neededWeight = AV_INIT_CONSENSUS_PCT;
else if (mClosePercent < AV_LATE_CONSENSUS_TIME)
@@ -769,41 +771,50 @@ void LedgerConsensus::updateOurPositions()
else if (mClosePercent < AV_STUCK_CONSENSUS_TIME)
neededWeight = AV_LATE_CONSENSUS_PCT;
else
neededWeight = AV_STUCK_CONSENSUS_TIME;
neededWeight = AV_STUCK_CONSENSUS_PCT;
uint32 closeTime = 0;
mHaveCloseTimeConsensus = false;
int thresh = mPeerPositions.size();
if (thresh == 0)
if (mPeerPositions.empty())
{ // no other times
mHaveCloseTimeConsensus = true;
closeTime = roundCloseTime(mOurPosition->getCloseTime());
}
else
{
int threshVote = mPeerPositions.size(); // Threshold for non-zero vote
int threshConsensus = mPeerPositions.size(); // Threshold to declare consensus
if (mProposing)
{
++closeTimes[roundCloseTime(mOurPosition->getCloseTime())];
++thresh;
++threshVote;
++threshConsensus;
}
thresh = ((thresh * neededWeight) + (neededWeight / 2)) / 100;
if (thresh == 0)
thresh = 1;
threshVote = ((threshVote * neededWeight) + (neededWeight / 2)) / 100;
threshConsensus = ((threshConsensus * AV_CT_CONSENSUS_PCT) + (AV_CT_CONSENSUS_PCT / 2)) / 100;
if (threshVote == 0)
threshVote = 1;
if (threshConsensus == 0)
threshConsensus = 1;
cLog(lsINFO) << "Proposers:" << mPeerPositions.size() << " nw:" << neededWeight
<< " thrV:" << threshVote << " thrC:" << threshConsensus;
for (std::map<uint32, int>::iterator it = closeTimes.begin(), end = closeTimes.end(); it != end; ++it)
{
cLog(lsTRACE) << "CCTime: " << it->first << " has " << it->second << ", " << thresh << " required";
if (it->second >= thresh)
cLog(lsDEBUG) << "CCTime: " << it->first << " has " << it->second << ", " << threshVote << " required";
if (it->second >= threshVote)
{
cLog(lsDEBUG) << "Close time consensus reached: " << it->first;
mHaveCloseTimeConsensus = true;
closeTime = it->first;
thresh = it->second;
threshVote = it->second;
if (threshVote >= threshConsensus)
mHaveCloseTimeConsensus = true;
}
}
tLog(!mHaveCloseTimeConsensus, lsDEBUG) << "No CT consensus: Proposers:" << mPeerPositions.size()
<< " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << thresh << " Pos:" << closeTime;
<< " Proposing:" << (mProposing ? "yes" : "no") << " Thresh:" << threshConsensus << " Pos:" << closeTime;
}
if (!changes &&
@@ -1044,7 +1055,10 @@ bool LedgerConsensus::peerHasSet(Peer::ref peer, const uint256& hashSet, ripple:
set.push_back(peer);
boost::unordered_map<uint256, TransactionAcquire::pointer>::iterator acq = mAcquiring.find(hashSet);
if (acq != mAcquiring.end())
acq->second->peerHas(peer);
{
TransactionAcquire::pointer ta = acq->second; // make sure it doesn't go away
ta->peerHas(peer);
}
return true;
}

View File

@@ -1066,6 +1066,8 @@ STAmount LedgerEntrySet::rippleHolds(const uint160& uAccountID, const uint160& u
return saBalance;
}
// Returns the amount an account can spend without going into debt.
//
// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative.
STAmount LedgerEntrySet::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
{

View File

@@ -11,7 +11,7 @@ std::map<std::string, LedgerEntryFormat*> LedgerEntryFormat::byName;
#define DECLARE_LEF(name, type) lef = new LedgerEntryFormat(#name, type); (*lef) LEF_BASE
static bool LEFInit()
void LEFInit()
{
LedgerEntryFormat* lef;
@@ -111,12 +111,8 @@ static bool LEFInit()
<< SOElement(sfReserveBase, SOE_REQUIRED)
<< SOElement(sfReserveIncrement, SOE_REQUIRED)
;
return true;
}
bool LEFInitComplete = LEFInit();
LedgerEntryFormat* LedgerEntryFormat::getLgrFormat(LedgerEntryType t)
{
std::map<int, LedgerEntryFormat*>::iterator it = byType.find(static_cast<int>(t));

View File

@@ -58,7 +58,7 @@ class LedgerEntryFormat
public:
std::string t_name;
LedgerEntryType t_type;
std::vector<SOElement::ref> elements;
SOTemplate elements;
static std::map<int, LedgerEntryFormat*> byType;
static std::map<std::string, LedgerEntryFormat*> byName;
@@ -70,7 +70,7 @@ public:
}
LedgerEntryFormat& operator<<(const SOElement& el)
{
elements.push_back(new SOElement(el));
elements.push_back(el);
return *this;
}

View File

@@ -119,11 +119,11 @@ Ledger::pointer LedgerMaster::closeLedger(bool recover)
return closingLedger;
}
TER LedgerMaster::doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply)
TER LedgerMaster::doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply)
{
TER result = mEngine.applyTransaction(txn, params, didApply);
// CHECKME: Should we call this even on gross failures?
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
TER result = mEngine.applyTransaction(*txn, params, didApply);
// if (didApply)
theApp->getOPs().pubProposedTransaction(mEngine.getLedger(), txn, result);
return result;
}
@@ -145,6 +145,8 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger)
uint32 seq = ledger->getLedgerSeq();
uint256 prevHash = ledger->getParentHash();
std::map< uint32, std::pair<uint256, uint256> > ledgerHashes;
while (seq > 0)
{
{
@@ -155,10 +157,20 @@ void LedgerMaster::asyncAccept(Ledger::pointer ledger)
break;
}
uint256 tHash, pHash;
if (!Ledger::getHashesByIndex(seq, tHash, pHash) || (tHash != prevHash))
std::map< uint32, std::pair<uint256, uint256> >::iterator it = ledgerHashes.find(seq);
if (it == ledgerHashes.end())
{
if (theApp->isShutdown())
return;
ledgerHashes = Ledger::getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq);
it = ledgerHashes.find(seq);
if (it == ledgerHashes.end())
break;
}
if (it->second.first != prevHash)
break;
prevHash = pHash;
prevHash = it->second.second;
}
resumeAcquiring();
@@ -174,7 +186,8 @@ bool LedgerMaster::acquireMissingLedger(Ledger::ref origLedger, const uint256& l
{
cLog(lsTRACE) << "Ledger hash found in database";
mTooFast = true;
theApp->getJobQueue().addJob(jtPUBOLDLEDGER, boost::bind(&LedgerMaster::asyncAccept, this, ledger));
theApp->getJobQueue().addJob(jtPUBOLDLEDGER, "LedgerMaster::asyncAccept",
boost::bind(&LedgerMaster::asyncAccept, this, ledger));
return true;
}
@@ -528,7 +541,8 @@ void LedgerMaster::tryPublish()
{
theApp->getOPs().clearNeedNetworkLedger();
mPubThread = true;
theApp->getJobQueue().addJob(jtPUBLEDGER, boost::bind(&LedgerMaster::pubThread, this));
theApp->getJobQueue().addJob(jtPUBLEDGER, "Ledger::pubThread",
boost::bind(&LedgerMaster::pubThread, this));
}
}

View File

@@ -75,7 +75,7 @@ public:
// The published ledger is the last fully validated ledger
Ledger::ref getValidatedLedger() { return mPubLedger; }
TER doTransaction(const SerializedTransaction& txn, TransactionEngineParams params, bool& didApply);
TER doTransaction(SerializedTransaction::ref txn, TransactionEngineParams params, bool& didApply);
void pushLedger(Ledger::ref newLedger);
void pushLedger(Ledger::ref newLCL, Ledger::ref newOL, bool fromConsensus);

View File

@@ -39,17 +39,18 @@
# define PROPOSE_INTERVAL 12
// Avalanche tuning
#define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes
# define AV_INIT_CONSENSUS_PCT 50 // percentage of nodes on our UNL that must vote yes
#define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance
#define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing
# define AV_MID_CONSENSUS_TIME 50 // percentage of previous close time before we advance
# define AV_MID_CONSENSUS_PCT 65 // percentage of nodes that most vote yes after advancing
#define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance
#define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing
# define AV_LATE_CONSENSUS_TIME 85 // percentage of previous close time before we advance
# define AV_LATE_CONSENSUS_PCT 70 // percentage of nodes that most vote yes after advancing
#define AV_STUCK_CONSENSUS_TIME 200
#define AV_STUCK_CONSENSUS_PCT 95
# define AV_STUCK_CONSENSUS_TIME 200
# define AV_STUCK_CONSENSUS_PCT 95
# define AV_CT_CONSENSUS_PCT 75
class ContinuousLedgerTiming
{

View File

@@ -318,7 +318,10 @@ void LoadManager::threadEntry()
bool change;
if (theApp->getJobQueue().isOverloaded())
{
cLog(lsINFO) << theApp->getJobQueue().getJson(0);
change = theApp->getFeeTrack().raiseLocalFee();
}
else
change = theApp->getFeeTrack().lowerLocalFee();
if (change)

View File

@@ -1,4 +1,7 @@
#include "LoadMonitor.h"
#include "Log.h"
SETUP_LOG();
void LoadMonitor::update()
{ // call with the mutex
@@ -52,8 +55,12 @@ void LoadMonitor::addLatency(int latency)
mLatencyMSPeak = lp;
}
void LoadMonitor::addCountAndLatency(int counts, int latency)
void LoadMonitor::addCountAndLatency(const std::string& name, int counts, int latency)
{
if (latency > 1000)
{
cLog(lsWARNING) << "Job: " << name << " ExecutionTime: " << latency;
}
if (latency == 1)
latency = 0;
boost::mutex::scoped_lock sl(mLock);

View File

@@ -32,7 +32,7 @@ public:
void addCount(int counts);
void addLatency(int latency);
void addCountAndLatency(int counts, int latency);
void addCountAndLatency(const std::string& name, int counts, int latency);
void setTargetLatency(uint64 avg, uint64 pk)
{
@@ -60,6 +60,7 @@ protected:
LoadMonitor& mMonitor;
bool mRunning;
int mCount;
std::string mName;
boost::posix_time::ptime mStartTime;
public:
@@ -76,6 +77,11 @@ public:
stop();
}
void setName(const std::string& name)
{
mName = name;
}
void start()
{ // okay to call if already started
mRunning = true;
@@ -86,7 +92,7 @@ public:
{
assert(mRunning);
mRunning = false;
mMonitor.addCountAndLatency(mCount,
mMonitor.addCountAndLatency(mName, mCount,
static_cast<int>((boost::posix_time::microsec_clock::universal_time() - mStartTime).total_milliseconds()));
}
};

View File

@@ -267,7 +267,7 @@ void NetworkOPs::runTransactionQueue()
assert(dbtx);
bool didApply;
TER r = mLedgerMaster->doTransaction(*dbtx->getSTransaction(),
TER r = mLedgerMaster->doTransaction(dbtx->getSTransaction(),
tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply);
dbtx->setResult(r);
@@ -352,7 +352,7 @@ Transaction::pointer NetworkOPs::processTransaction(Transaction::pointer trans,
boost::recursive_mutex::scoped_lock sl(theApp->getMasterLock());
Transaction::pointer dbtx = theApp->getMasterTransaction().fetch(trans->getID(), true);
bool didApply;
TER r = mLedgerMaster->doTransaction(*trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply);
TER r = mLedgerMaster->doTransaction(trans->getSTransaction(), tapOPEN_LEDGER | tapNO_CHECK_SIGN, didApply);
trans->setResult(r);
if (isTemMalformed(r)) // malformed, cache bad
@@ -426,30 +426,6 @@ Transaction::pointer NetworkOPs::findTransactionByID(const uint256& transactionI
return Transaction::load(transactionID);
}
#if 0
int NetworkOPs::findTransactionsBySource(const uint256& uLedger, std::list<Transaction::pointer>& txns,
const RippleAddress& sourceAccount, uint32 minSeq, uint32 maxSeq)
{
AccountState::pointer state = getAccountState(uLedger, sourceAccount);
if (!state) return 0;
if (minSeq > state->getSeq()) return 0;
if (maxSeq > state->getSeq()) maxSeq = state->getSeq();
if (maxSeq > minSeq) return 0;
int count = 0;
for(unsigned int i = minSeq; i <= maxSeq; ++i)
{
Transaction::pointer txn = Transaction::findFrom(sourceAccount, i);
if(txn)
{
txns.push_back(txn);
++count;
}
}
return count;
}
#endif
int NetworkOPs::findTransactionsByDestination(std::list<Transaction::pointer>& txns,
const RippleAddress& destinationAccount, uint32 startLedgerSeq, uint32 endLedgerSeq, int maxTransactions)
{
@@ -1313,9 +1289,9 @@ Json::Value NetworkOPs::pubBootstrapAccountInfo(Ledger::ref lpAccepted, const Ri
return jvObj;
}
void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult)
void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult)
{
Json::Value jvObj = transJson(stTxn, terResult, false, lpCurrent, "transaction");
Json::Value jvObj = transJson(*stTxn, terResult, false, lpCurrent, "transaction");
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
@@ -1332,15 +1308,19 @@ void NetworkOPs::pubProposedTransaction(Ledger::ref lpCurrent, const SerializedT
it = mSubRTTransactions.erase(it);
}
}
TransactionMetaSet::pointer ret;
pubAccountTransaction(lpCurrent,stTxn,terResult,false,ret);
ALTransaction alt(stTxn, terResult);
cLog(lsTRACE) << "pubProposed: " << alt.getJson(0);
pubAccountTransaction(lpCurrent, ALTransaction(stTxn, terResult));
}
void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
void NetworkOPs::pubLedger(Ledger::ref accepted)
{
// Ledgers are published only when they acquire sufficient validations
// Holes are filled across connection loss or other catastrophe
AcceptedLedger::pointer alpAccepted = AcceptedLedger::makeAcceptedLedger(accepted);
Ledger::ref lpAccepted = alpAccepted->getLedger();
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
@@ -1358,6 +1338,8 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
jvObj["reserve_base"] = Json::UInt(lpAccepted->getReserve(0));
jvObj["reserve_inc"] = Json::UInt(lpAccepted->getReserveInc());
jvObj["txn_count"] = Json::UInt(alpAccepted->getTxnCount());
NetworkOPs::subMapType::const_iterator it = mSubLedger.begin();
while (it != mSubLedger.end())
{
@@ -1376,21 +1358,10 @@ void NetworkOPs::pubLedger(Ledger::ref lpAccepted)
// Don't lock since pubAcceptedTransaction is locking.
if (!mSubTransactions.empty() || !mSubRTTransactions.empty() || !mSubAccount.empty() || !mSubRTAccount.empty())
{
SHAMap& txSet = *lpAccepted->peekTransactionMap();
for (SHAMapItem::pointer item = txSet.peekFirstItem(); !!item; item = txSet.peekNextItem(item->getTag()))
BOOST_FOREACH(const AcceptedLedger::value_type& vt, alpAccepted->getMap())
{
SerializerIterator it(item->peekSerializer());
// OPTIMIZEME: Could get transaction from txn master, but still must call getVL
Serializer txnSer(it.getVL());
SerializerIterator txnIt(txnSer);
SerializedTransaction stTxn(txnIt);
TransactionMetaSet::pointer meta = boost::make_shared<TransactionMetaSet>(
stTxn.getTransactionID(), lpAccepted->getLedgerSeq(), it.getVL());
pubAcceptedTransaction(lpAccepted, stTxn, meta->getResultTER(), meta);
cLog(lsTRACE) << "pubAccepted: " << vt.second.getJson(0);
pubAcceptedTransaction(lpAccepted, vt.second);
}
}
}
@@ -1401,7 +1372,7 @@ void NetworkOPs::reportFeeChange()
(theApp->getFeeTrack().getLoadFactor() == mLastLoadFactor))
return;
theApp->getJobQueue().addJob(jtCLIENT, boost::bind(&NetworkOPs::pubServer, this));
theApp->getJobQueue().addJob(jtCLIENT, "reportFeeChange->pubServer", boost::bind(&NetworkOPs::pubServer, this));
}
Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terResult, bool bAccepted, Ledger::ref lpCurrent, const std::string& strType)
@@ -1431,11 +1402,10 @@ Json::Value NetworkOPs::transJson(const SerializedTransaction& stTxn, TER terRes
return jvObj;
}
void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta)
void NetworkOPs::pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTx)
{
Json::Value jvObj = transJson(stTxn, terResult, true, lpCurrent, "transaction");
if (meta) jvObj["meta"] = meta->getJson(0);
Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), true, alAccepted, "transaction");
jvObj["meta"] = alTx.getMeta()->getJson(0);
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
@@ -1466,14 +1436,14 @@ void NetworkOPs::pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedT
it = mSubRTTransactions.erase(it);
}
}
theApp->getOrderBookDB().processTxn(stTxn, terResult, meta, jvObj);
pubAccountTransaction(lpCurrent, stTxn, terResult, true, meta);
theApp->getOrderBookDB().processTxn(alAccepted, alTx, jvObj);
pubAccountTransaction(alAccepted, alTx);
}
void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult, bool bAccepted, TransactionMetaSet::pointer& meta)
void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTx)
{
boost::unordered_set<InfoSub::pointer> notify;
bool bAccepted = alTx.isApplied();
int iProposed = 0;
int iAccepted = 0;
@@ -1484,8 +1454,7 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr
if (!mSubAccount.empty() || (!mSubRTAccount.empty()) )
{
std::vector<RippleAddress> accounts = meta ? meta->getAffectedAccounts() : stTxn.getMentionedAccounts();
BOOST_FOREACH(const RippleAddress& affectedAccount, accounts)
BOOST_FOREACH(const RippleAddress& affectedAccount, alTx.getAffected())
{
subInfoMapIterator simiIt = mSubRTAccount.find(affectedAccount.getAccountID());
@@ -1534,9 +1503,10 @@ void NetworkOPs::pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTr
if (!notify.empty())
{
Json::Value jvObj = transJson(stTxn, terResult, bAccepted, lpCurrent, "account");
Json::Value jvObj = transJson(*alTx.getTxn(), alTx.getResult(), bAccepted, lpCurrent, "account");
if (meta) jvObj["meta"] = meta->getJson(0);
if (alTx.isApplied())
jvObj["meta"] = alTx.getMeta()->getJson(0);
BOOST_FOREACH(InfoSub::ref isrListener, notify)
{
@@ -1696,12 +1666,14 @@ bool NetworkOPs::subLedger(InfoSub::ref isrListener, Json::Value& jvResult)
jvResult["reserve_base"] = Json::UInt(lpClosed->getReserve(0));
jvResult["reserve_inc"] = Json::UInt(lpClosed->getReserveInc());
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return mSubLedger.insert(std::make_pair(isrListener->getSeq(), isrListener)).second;
}
// <-- bool: true=erased, false=was not there
bool NetworkOPs::unsubLedger(uint64 uSeq)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return !!mSubLedger.erase(uSeq);
}
@@ -1722,36 +1694,42 @@ bool NetworkOPs::subServer(InfoSub::ref isrListener, Json::Value& jvResult)
jvResult["load_base"] = theApp->getFeeTrack().getLoadBase();
jvResult["load_factor"] = theApp->getFeeTrack().getLoadFactor();
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return mSubServer.insert(std::make_pair(isrListener->getSeq(), isrListener)).second;
}
// <-- bool: true=erased, false=was not there
bool NetworkOPs::unsubServer(uint64 uSeq)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return !!mSubServer.erase(uSeq);
}
// <-- bool: true=added, false=already there
bool NetworkOPs::subTransactions(InfoSub::ref isrListener)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second;
}
// <-- bool: true=erased, false=was not there
bool NetworkOPs::unsubTransactions(uint64 uSeq)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return !!mSubTransactions.erase(uSeq);
}
// <-- bool: true=added, false=already there
bool NetworkOPs::subRTTransactions(InfoSub::ref isrListener)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return mSubTransactions.insert(std::make_pair(isrListener->getSeq(), isrListener)).second;
}
// <-- bool: true=erased, false=was not there
bool NetworkOPs::unsubRTTransactions(uint64 uSeq)
{
boost::recursive_mutex::scoped_lock sl(mMonitorLock);
return !!mSubTransactions.erase(uSeq);
}
@@ -1774,5 +1752,170 @@ InfoSub::pointer NetworkOPs::addRpcSub(const std::string& strUrl, InfoSub::ref r
return rspEntry;
}
// FIXME : support iLimit.
void NetworkOPs::getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult)
{
boost::unordered_map<uint160, STAmount> umBalance;
Json::Value jvOffers = Json::Value(Json::arrayValue);
const uint256 uBookBase = Ledger::getBookBase(uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID);
const uint256 uBookEnd = Ledger::getQualityNext(uBookBase);
uint256 uTipIndex = uBookBase;
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerPaysCurrencyID=%s uTakerPaysIssuerID=%s") % STAmount::createHumanCurrency(uTakerPaysCurrencyID) % RippleAddress::createHumanAccountID(uTakerPaysIssuerID));
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTakerGetsCurrencyID=%s uTakerGetsIssuerID=%s") % STAmount::createHumanCurrency(uTakerGetsCurrencyID) % RippleAddress::createHumanAccountID(uTakerGetsIssuerID));
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookBase=%s") % uBookBase);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uBookEnd=%s") % uBookEnd);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex);
LedgerEntrySet lesActive(lpLedger);
bool bDone = false;
bool bDirectAdvance = true;
SLE::pointer sleOfferDir;
uint256 uOfferIndex;
unsigned int uBookEntry;
STAmount saDirRate;
// unsigned int iLeft = iLimit;
uint32 uTransferRate = lesActive.rippleTransferRate(uTakerGetsIssuerID);
while (!bDone) {
if (bDirectAdvance) {
bDirectAdvance = false;
cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDirectAdvance"));
sleOfferDir = lesActive.entryCache(ltDIR_NODE, lpLedger->getNextLedgerIndex(uTipIndex, uBookEnd));
if (!sleOfferDir)
{
cLog(lsTRACE) << boost::str(boost::format("getBookPage: bDone"));
bDone = true;
}
else
{
uTipIndex = sleOfferDir->getIndex();
saDirRate = STAmount::setRate(Ledger::getQuality(uTipIndex));
SLE::pointer sleBookNode;
lesActive.dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uTipIndex=%s") % uTipIndex);
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex);
}
}
if (!bDone)
{
SLE::pointer sleOffer = lesActive.entryCache(ltOFFER, uOfferIndex);
const uint160 uOfferOwnerID = sleOffer->getFieldAccount(sfAccount).getAccountID();
STAmount saTakerGets = sleOffer->getFieldAmount(sfTakerGets);
STAmount saTakerPays = sleOffer->getFieldAmount(sfTakerPays);
STAmount saOwnerFunds;
if (uTakerGetsIssuerID == uOfferOwnerID)
{
// If offer is selling issuer's own IOUs, it is fully funded.
saOwnerFunds = saTakerGets;
}
else
{
boost::unordered_map<uint160, STAmount>::const_iterator umBalanceEntry = umBalance.find(uOfferOwnerID);
if (umBalanceEntry != umBalance.end())
{
// Found in running balance table.
saOwnerFunds = umBalanceEntry->second;
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (cached)") % saOwnerFunds.getFullText());
}
else
{
// Did not find balance in table.
saOwnerFunds = lesActive.accountHolds(uOfferOwnerID, uTakerGetsCurrencyID, uTakerGetsIssuerID);
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s (new)") % saOwnerFunds.getFullText());
if (saOwnerFunds.isNegative())
{
// Treat negative funds as zero.
saOwnerFunds.zero();
}
}
}
Json::Value jvOffer = sleOffer->getJson(0);
STAmount saTakerGetsFunded;
STAmount saOwnerFundsLimit;
uint32 uOfferRate;
if (uTransferRate != QUALITY_ONE // Have a tranfer fee.
&& uTakerID != uTakerGetsIssuerID // Not taking offers of own IOUs.
&& uTakerGetsIssuerID != uOfferOwnerID) { // Offer owner not issuing ownfunds
// Need to charge a transfer fee to offer owner.
uOfferRate = uTransferRate;
saOwnerFundsLimit = STAmount::divide(saOwnerFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9));
}
else
{
uOfferRate = QUALITY_ONE;
saOwnerFundsLimit = saOwnerFunds;
}
if (saOwnerFundsLimit >= saTakerGets)
{
// Sufficient funds no shenanigans.
saTakerGetsFunded = saTakerGets;
}
else
{
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerGets=%s") % saTakerGets.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saTakerPays=%s") % saTakerPays.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saOwnerFunds=%s") % saOwnerFunds.getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: saDirRate=%s") % saDirRate.getText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate).getFullText());
// cLog(lsINFO) << boost::str(boost::format("getBookPage: multiply=%s") % STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays).getFullText());
STAmount saTakerPaysFunded;
saTakerGetsFunded = saOwnerFundsLimit;
saTakerPaysFunded = std::min(saTakerPays, STAmount::multiply(saTakerGetsFunded, saDirRate, saTakerPays));
// Only provide, if not fully funded.
jvOffer["taker_gets_funded"] = saTakerGetsFunded.getJson(0);
jvOffer["taker_pays_funded"] = saTakerPaysFunded.getJson(0);
}
STAmount saOwnerPays = QUALITY_ONE == uOfferRate
? saTakerGetsFunded
: std::min(saOwnerFunds, STAmount::multiply(saTakerGetsFunded, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferRate, -9)));
STAmount saOwnerBalance = saOwnerFunds-saOwnerPays;
umBalance[uOfferOwnerID] = saOwnerBalance;
if (!saOwnerFunds.isZero() || uOfferOwnerID == uTakerID)
{
// Only provide funded offers and offers of the taker.
jvOffers.append(jvOffer);
}
if (!lesActive.dirNext(uTipIndex, sleOfferDir, uBookEntry, uOfferIndex))
{
bDirectAdvance = true;
}
else
{
cLog(lsTRACE) << boost::str(boost::format("getBookPage: uOfferIndex=%s") % uOfferIndex);
}
}
}
jvResult["offers"] = jvOffers;
// jvResult["marker"] = Json::Value(Json::arrayValue);
// jvResult["nodes"] = Json::Value(Json::arrayValue);
}
// vim:ts=4

View File

@@ -14,6 +14,7 @@
#include "LedgerAcquire.h"
#include "LedgerProposal.h"
#include "JobQueue.h"
#include "AcceptedLedger.h"
// Operations that clients may wish to perform against the network
// Master operational handler, server sequencer, network tracker
@@ -119,7 +120,6 @@ protected:
boost::recursive_mutex mMonitorLock;
subInfoMapType mSubAccount;
subInfoMapType mSubRTAccount;
subRpcMapType mRpcSubMap;
@@ -141,8 +141,8 @@ protected:
Json::Value pubBootstrapAccountInfo(Ledger::ref lpAccepted, const RippleAddress& naAccountID);
void pubAcceptedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta);
void pubAccountTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult,bool accepted,TransactionMetaSet::pointer& meta);
void pubAcceptedTransaction(Ledger::ref alAccepted, const ALTransaction& alTransaction);
void pubAccountTransaction(Ledger::ref lpCurrent, const ALTransaction& alTransaction);
void pubServer();
@@ -230,6 +230,12 @@ public:
Json::Value getOwnerInfo(Ledger::pointer lpLedger, const RippleAddress& naAccount);
//
// Book functions
//
void getBookPage(Ledger::pointer lpLedger, const uint160& uTakerPaysCurrencyID, const uint160& uTakerPaysIssuerID, const uint160& uTakerGetsCurrencyID, const uint160& uTakerGetsIssuerID, const uint160& uTakerID, const bool bProof, const unsigned int iLimit, const Json::Value& jvMarker, Json::Value& jvResult);
// raw object operations
bool findRawLedger(const uint256& ledgerHash, std::vector<unsigned char>& rawLedger);
bool findRawTransaction(const uint256& transactionHash, std::vector<unsigned char>& rawTransaction);
@@ -301,7 +307,7 @@ public:
// Monitoring: publisher side
//
void pubLedger(Ledger::ref lpAccepted);
void pubProposedTransaction(Ledger::ref lpCurrent, const SerializedTransaction& stTxn, TER terResult);
void pubProposedTransaction(Ledger::ref lpCurrent, SerializedTransaction::ref stTxn, TER terResult);
//

View File

@@ -321,6 +321,7 @@ TER OfferCreateTransactor::doApply()
cLog(lsWARNING) << "OfferCreate> " << mTxn.getJson(0);
const uint32 uTxFlags = mTxn.getFlags();
const bool bPassive = isSetBit(uTxFlags, tfPassive);
const bool bMarket = isSetBit(uTxFlags, tfMarket);
STAmount saTakerPays = mTxn.getFieldAmount(sfTakerPays);
STAmount saTakerGets = mTxn.getFieldAmount(sfTakerGets);
@@ -457,9 +458,21 @@ TER OfferCreateTransactor::doApply()
// cLog(lsWARNING) << "OfferCreate: takeOffers: uPaysIssuerID=" << RippleAddress::createHumanAccountID(uPaysIssuerID);
// cLog(lsWARNING) << "OfferCreate: takeOffers: uGetsIssuerID=" << RippleAddress::createHumanAccountID(uGetsIssuerID);
if (tesSUCCESS != terResult
|| !saTakerPays // Wants nothing more.
if (tesSUCCESS != terResult)
{
// Fail as is.
nothing();
}
else if (saTakerPays.isNegative() || saTakerGets.isNegative())
{
terResult = isSetBit(mParams, tapOPEN_LEDGER)
? telFAILED_PROCESSING // Ledger is not final, can vote no.
: tecFAILED_PROCESSING;
}
else if (
!saTakerPays // Wants nothing more.
|| !saTakerGets // Offering nothing more.
|| bMarket // Do not persist.
|| !mEngine->getNodes().accountFunds(mTxnAccountID, saTakerGets).isPositive() // Not funded.
|| bUnfunded) // Consider unfunded.
{

View File

@@ -187,15 +187,14 @@ BookListeners::pointer OrderBookDB::getBookListeners(const uint160& currencyIn,
*/
// Based on the meta, send the meta to the streams that are listening
// We need to determine which streams a given meta effects
void OrderBookDB::processTxn(const SerializedTransaction& stTxn, TER terResult,
TransactionMetaSet::pointer& meta, Json::Value& jvObj)
void OrderBookDB::processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj)
{
boost::recursive_mutex::scoped_lock sl(mLock);
if (terResult == tesSUCCESS)
if (alTx.getResult() == tesSUCCESS)
{
// check if this is an offer or an offer cancel or a payment that consumes an offer
//check to see what the meta looks like
BOOST_FOREACH(STObject& node, meta->getNodes())
BOOST_FOREACH(STObject& node, alTx.getMeta()->getNodes())
{
try
{

View File

@@ -6,6 +6,7 @@
#include <boost/unordered_map.hpp>
#include "Ledger.h"
#include "AcceptedLedger.h"
#include "OrderBook.h"
@@ -66,7 +67,7 @@ public:
const uint160& issuerIn, const uint160& issuerOut);
// see if this txn effects any orderbook
void processTxn(const SerializedTransaction& stTxn, TER terResult,TransactionMetaSet::pointer& meta,Json::Value& jvObj);
void processTxn(Ledger::ref ledger, const ALTransaction& alTx, Json::Value& jvObj);
};

View File

@@ -857,7 +857,7 @@ void Peer::recvTransaction(ripple::TMTransaction& packet)
return;
}
theApp->getJobQueue().addJob(jtTRANSACTION,
theApp->getJobQueue().addJob(jtTRANSACTION, "recvTransction->checkTransaction",
boost::bind(&checkTransaction, _1, flags, stx, boost::weak_ptr<Peer>(shared_from_this())));
#ifndef TRUST_NETWORK
@@ -986,7 +986,7 @@ void Peer::recvPropose(const boost::shared_ptr<ripple::TMProposeSet>& packet)
prevLedger.isNonZero() ? prevLedger : consensusLCL,
set.proposeseq(), proposeHash, set.closetime(), signerPublic, suppression);
theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut,
theApp->getJobQueue().addJob(isTrusted ? jtPROPOSAL_t : jtPROPOSAL_ut, "recvPropose->checkPropose",
boost::bind(&checkPropose, _1, packet, proposal, consensusLCL,
mNodePublic, boost::weak_ptr<Peer>(shared_from_this())));
}
@@ -1062,7 +1062,7 @@ void Peer::recvValidation(const boost::shared_ptr<ripple::TMValidation>& packet)
}
bool isTrusted = theApp->getUNL().nodeInUNL(val->getSignerPublic());
theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut,
theApp->getJobQueue().addJob(isTrusted ? jtVALIDATION_t : jtVALIDATION_ut, "recvValidation->checkValidation",
boost::bind(&checkValidation, _1, val, signingHash, isTrusted, packet,
boost::weak_ptr<Peer>(shared_from_this())));
}
@@ -1299,7 +1299,7 @@ void Peer::recvProofWork(ripple::TMProofWork& packet)
return;
}
theApp->getJobQueue().addJob(jtPROOFWORK,
theApp->getJobQueue().addJob(jtPROOFWORK, "recvProof->doProof",
boost::bind(&Peer::doProofOfWork, _1, boost::weak_ptr<Peer>(shared_from_this()), pow));
return;

View File

@@ -19,12 +19,16 @@ Json::Value rpcError(int iError, Json::Value jvResult)
{ rpcACT_MALFORMED, "actMalformed", "Account malformed." },
{ rpcACT_NOT_FOUND, "actNotFound", "Account not found." },
{ rpcBAD_BLOB, "badBlob", "Blob must be a non-empty hex string." },
{ rpcBAD_ISSUER, "badIssuer", "Issuer account malformed." },
{ rpcBAD_MARKET, "badMarket", "No such market." },
{ rpcBAD_SECRET, "badSecret", "Secret does not match account." },
{ rpcBAD_SEED, "badSeed", "Disallowed seed." },
{ rpcBAD_SYNTAX, "badSyntax", "Syntax error." },
{ rpcCOMMAND_MISSING, "commandMissing", "Missing command entry." },
{ rpcDST_ACT_MALFORMED, "dstActMalformed", "Destination account is malformed." },
{ rpcDST_ACT_MISSING, "dstActMissing", "Destination account does not exist." },
{ rpcDST_AMT_MALFORMED, "dstAmtMalformed", "Destination amount/currency/issuer is malformed." },
{ rpcDST_ISR_MALFORMED, "dstIsrMalformed", "Destination issuer is malformed." },
{ rpcFORBIDDEN, "forbidden", "Bad credentials." },
{ rpcFAIL_GEN_DECRPYT, "failGenDecrypt", "Failed to decrypt generator." },
{ rpcGETS_ACT_MALFORMED, "getsActMalformed", "Gets account malformed." },
@@ -60,7 +64,6 @@ Json::Value rpcError(int iError, Json::Value jvResult)
{ rpcSRC_ACT_MALFORMED, "srcActMalformed", "Source account is malformed." },
{ rpcSRC_ACT_MISSING, "srcActMissing", "Source account not provided." },
{ rpcSRC_ACT_NOT_FOUND, "srcActNotFound", "Source account not found." },
{ rpcBAD_SECRET, "badSecret", "Secret does not match account." },
{ rpcSRC_AMT_MALFORMED, "srcAmtMalformed", "Source amount/currency/issuer is malformed." },
{ rpcSRC_CUR_MALFORMED, "srcCurMalformed", "Source currency is malformed." },
{ rpcSRC_ISR_MALFORMED, "srcIsrMalformed", "Source issuer is malformed." },
@@ -88,4 +91,9 @@ Json::Value rpcError(int iError, Json::Value jvResult)
return jvResult;
}
bool isRpcError(Json::Value jvResult)
{
return jvResult.isObject() && jvResult.isMember("error");
}
// vim:ts=4

View File

@@ -45,11 +45,15 @@ enum {
rpcACT_MALFORMED,
rpcQUALITY_MALFORMED,
rpcBAD_BLOB,
rpcBAD_ISSUER,
rpcBAD_MARKET,
rpcBAD_SECRET,
rpcBAD_SEED,
rpcCOMMAND_MISSING,
rpcDST_ACT_MALFORMED,
rpcDST_ACT_MISSING,
rpcDST_AMT_MALFORMED,
rpcDST_ISR_MALFORMED,
rpcGETS_ACT_MALFORMED,
rpcGETS_AMT_MALFORMED,
rpcHOST_IP_MALFORMED,
@@ -67,7 +71,6 @@ enum {
rpcSRC_AMT_MALFORMED,
rpcSRC_CUR_MALFORMED,
rpcSRC_ISR_MALFORMED,
rpcBAD_SECRET,
// Internal error (should never happen)
rpcINTERNAL, // Generic internal error.
@@ -77,6 +80,8 @@ enum {
rpcNO_GEN_DECRPYT,
};
bool isRpcError(Json::Value jvResult);
Json::Value rpcError(int iError, Json::Value jvResult=Json::Value(Json::objectValue));
#endif
// vim:ts=4

View File

@@ -176,7 +176,7 @@ Json::Value RPCHandler::transactionSign(Json::Value jvRequest, bool bSubmit)
}
Ledger::pointer lSnapshot = boost::make_shared<Ledger>(
boost::ref(*theApp->getOPs().getCurrentLedger()), false);
boost::ref(*mNetOps->getCurrentLedger()), false);
{
ScopedUnlock su(theApp->getMasterLock());
Pathfinder pf(lSnapshot, raSrcAddressID, dstAccountID,
@@ -558,18 +558,6 @@ Json::Value RPCHandler::accountFromString(Ledger::ref lrLedger, RippleAddress& n
return Json::Value(Json::objectValue);
}
Json::Value RPCHandler::doAcceptLedger(Json::Value jvRequest)
{
if (!theConfig.RUN_STANDALONE)
return rpcError(rpcNOT_STANDALONE);
Json::Value jvResult(Json::objectValue);
jvResult["newLedger"] = theApp->getOPs().acceptLedger();
return jvResult;
}
// {
// ident : <indent>,
// account_index : <index> // optional
@@ -1033,6 +1021,106 @@ Json::Value RPCHandler::doAccountOffers(Json::Value jvRequest)
return jvResult;
}
// {
// "ledger_hash" : ledger, // Optional.
// "ledger_index" : ledger_index, // Optional.
// "taker_gets" : { "currency": currency, "issuer" : address },
// "taker_pays" : { "currency": currency, "issuer" : address },
// "taker" : address, // Optional.
// "marker" : element, // Optional.
// "limit" : integer, // Optional.
// "proof" : boolean // Defaults to false.
// }
Json::Value RPCHandler::doBookOffers(Json::Value jvRequest)
{
Ledger::pointer lpLedger;
Json::Value jvResult = lookupLedger(jvRequest, lpLedger);
if (!lpLedger)
return jvResult;
ScopedUnlock su(theApp->getMasterLock());
if (!jvRequest.isMember("taker_pays") || !jvRequest.isMember("taker_gets"))
return rpcError(rpcINVALID_PARAMS);
uint160 uTakerPaysCurrencyID;
uint160 uTakerPaysIssuerID;
Json::Value jvTakerPays = jvRequest["taker_pays"];
// Parse mandatory currency.
if (!jvTakerPays.isMember("currency")
|| !STAmount::currencyFromString(uTakerPaysCurrencyID, jvTakerPays["currency"].asString()))
{
cLog(lsINFO) << "Bad taker_pays currency.";
return rpcError(rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerPays.isMember("issuer"))
&& (!jvTakerPays["issuer"].isString()
|| !STAmount::issuerFromString(uTakerPaysIssuerID, jvTakerPays["issuer"].asString())))
// Don't allow illegal issuers.
|| (!uTakerPaysCurrencyID != !uTakerPaysIssuerID)
|| ACCOUNT_ONE == uTakerPaysIssuerID)
{
cLog(lsINFO) << "Bad taker_pays issuer.";
return rpcError(rpcSRC_ISR_MALFORMED);
}
uint160 uTakerGetsCurrencyID;
uint160 uTakerGetsIssuerID;
Json::Value jvTakerGets = jvRequest["taker_gets"];
// Parse mandatory currency.
if (!jvTakerGets.isMember("currency")
|| !STAmount::currencyFromString(uTakerGetsCurrencyID, jvTakerGets["currency"].asString()))
{
cLog(lsINFO) << "Bad taker_pays currency.";
return rpcError(rpcSRC_CUR_MALFORMED);
}
// Parse optional issuer.
else if (((jvTakerGets.isMember("issuer"))
&& (!jvTakerGets["issuer"].isString()
|| !STAmount::issuerFromString(uTakerGetsIssuerID, jvTakerGets["issuer"].asString())))
// Don't allow illegal issuers.
|| (!uTakerGetsCurrencyID != !uTakerGetsIssuerID)
|| ACCOUNT_ONE == uTakerGetsIssuerID)
{
cLog(lsINFO) << "Bad taker_gets issuer.";
return rpcError(rpcDST_ISR_MALFORMED);
}
if (uTakerPaysCurrencyID == uTakerGetsCurrencyID
&& uTakerPaysIssuerID == uTakerGetsIssuerID) {
cLog(lsINFO) << "taker_gets same as taker_pays.";
return rpcError(rpcBAD_MARKET);
}
RippleAddress raTakerID;
if (!jvRequest.isMember("taker"))
{
raTakerID.setAccountID(ACCOUNT_ONE);
}
else if (!raTakerID.setAccountID(jvRequest["taker"].asString()))
{
return rpcError(rpcBAD_ISSUER);
}
const bool bProof = jvRequest.isMember("proof");
const unsigned int iLimit = jvRequest.isMember("limit") ? jvRequest["limit"].asUInt() : 0;
const Json::Value jvMarker = jvRequest.isMember("marker") ? jvRequest["marker"] : Json::Value(Json::nullValue);
mNetOps->getBookPage(lpLedger, uTakerPaysCurrencyID, uTakerPaysIssuerID, uTakerGetsCurrencyID, uTakerGetsIssuerID, raTakerID.getAccountID(), bProof, iLimit, jvMarker, jvResult);
return jvResult;
}
// Result:
// {
// random: <uint256>
@@ -1366,7 +1454,7 @@ Json::Value RPCHandler::doConsensusInfo(Json::Value)
{
Json::Value ret(Json::objectValue);
ret["info"] = theApp->getOPs().getConsensusInfo();
ret["info"] = mNetOps->getConsensusInfo();
return ret;
}
@@ -1375,7 +1463,7 @@ Json::Value RPCHandler::doServerInfo(Json::Value)
{
Json::Value ret(Json::objectValue);
ret["info"] = theApp->getOPs().getServerInfo(true, mRole == ADMIN);
ret["info"] = mNetOps->getServerInfo(true, mRole == ADMIN);
return ret;
}
@@ -1384,7 +1472,7 @@ Json::Value RPCHandler::doServerState(Json::Value)
{
Json::Value ret(Json::objectValue);
ret["state"] = theApp->getOPs().getServerInfo(false, mRole == ADMIN);
ret["state"] = mNetOps->getServerInfo(false, mRole == ADMIN);
return ret;
}
@@ -1453,7 +1541,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest)
if (txn->getLedger() != 0)
{
Ledger::pointer lgr = theApp->getOPs().getLedgerBySeq(txn->getLedger());
Ledger::pointer lgr = mNetOps->getLedgerBySeq(txn->getLedger());
if (lgr)
{
bool okay = false;
@@ -1476,7 +1564,7 @@ Json::Value RPCHandler::doTx(Json::Value jvRequest)
}
}
if (okay)
ret["validated"] = theApp->getOPs().isValidated(lgr);
ret["validated"] = mNetOps->isValidated(lgr);
}
}
@@ -1594,7 +1682,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest)
try
{
#endif
int vl = theApp->getOPs().getValidatedSeq();
int vl = mNetOps->getValidatedSeq();
ScopedUnlock su(theApp->getMasterLock());
Json::Value ret(Json::objectValue);
@@ -1613,7 +1701,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest)
obj["inLedger"] = it->get<2>();
if (it->get<2>() > vl)
obj["validated"] = false;
else if (theApp->getOPs().haveLedger(it->get<2>()))
else if (mNetOps->haveLedger(it->get<2>()))
obj["validated"] = true;
ret["transactions"].append(obj);
}
@@ -1634,7 +1722,7 @@ Json::Value RPCHandler::doAccountTransactions(Json::Value jvRequest)
uint32 s = it->second->getLgrSeq();
if (s > vl)
obj["validated"] = false;
else if (theApp->getOPs().haveLedger(s))
else if (mNetOps->haveLedger(s))
obj["validated"] = true;
}
@@ -2807,11 +2895,11 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole)
unsigned int iOptions;
} commandsA[] = {
// Request-response methods
{ "accept_ledger", &RPCHandler::doAcceptLedger, true, optCurrent },
{ "account_info", &RPCHandler::doAccountInfo, false, optCurrent },
{ "account_lines", &RPCHandler::doAccountLines, false, optCurrent },
{ "account_offers", &RPCHandler::doAccountOffers, false, optCurrent },
{ "account_tx", &RPCHandler::doAccountTransactions, false, optNetwork },
{ "book_offers", &RPCHandler::doBookOffers, false, optCurrent },
{ "connect", &RPCHandler::doConnect, true, optNone },
{ "consensus_info", &RPCHandler::doConsensusInfo, true, optNone },
{ "get_counts", &RPCHandler::doGetCounts, true, optNone },
@@ -2888,6 +2976,8 @@ Json::Value RPCHandler::doCommand(const Json::Value& jvRequest, int iRole)
&& mNetOps->getOperatingMode() != NetworkOPs::omTRACKING
&& mNetOps->getOperatingMode() != NetworkOPs::omFULL)
{
cLog(lsINFO) << "Insufficient network mode for RPC: " << mNetOps->strOperatingMode();
return rpcError(rpcNO_NETWORK);
}
// XXX Should verify we have a current ledger.

View File

@@ -43,12 +43,11 @@ class RPCHandler
Json::Value accountFromString(Ledger::ref lrLedger, RippleAddress& naAccount, bool& bIndex, const std::string& strIdent, const int iIndex, const bool bStrict);
Json::Value doAcceptLedger(Json::Value jvRequest);
Json::Value doAccountInfo(Json::Value params);
Json::Value doAccountLines(Json::Value params);
Json::Value doAccountOffers(Json::Value params);
Json::Value doAccountTransactions(Json::Value params);
Json::Value doBookOffers(Json::Value params);
Json::Value doConnect(Json::Value params);
Json::Value doConsensusInfo(Json::Value params);
#if ENABLE_INSECURE

View File

@@ -29,6 +29,12 @@ RPCSub::RPCSub(const std::string& strUrl, const std::string& strUsername, const
if (mPort < 0)
mPort = mSSL ? 443 : 80;
cLog(lsINFO) << boost::str(boost::format("callRPC sub: ip='%s' port=%d ssl=%d path='%s'")
% mIp
% mPort
% mSSL
% mPath);
}
// XXX Could probably create a bunch of send jobs in a single get of the lock.

View File

@@ -325,10 +325,10 @@ void PathState::setExpanded(
uMaxCurrencyID, // Max specifes the currency.
uSenderIssuerID);
cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uSenderID)
% STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uSenderIssuerID));
cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uSenderID)
% STAmount::createHumanCurrency(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uSenderIssuerID));
if (tesSUCCESS == terStatus
&& uMaxIssuerID != uSenderIssuerID) { // Issuer was not same as sender.
@@ -347,19 +347,19 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: pushed: account=%s currenc
: uOutIssuerID // Use implied node.
: ACCOUNT_XRP;
cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s")
% RippleAddress::createHumanAccountID(uNxtCurrencyID)
% RippleAddress::createHumanAccountID(uNxtAccountID));
cLog(lsDEBUG) << boost::str(boost::format("PathState: implied check: uNxtCurrencyID=%s uNxtAccountID=%s")
% RippleAddress::createHumanAccountID(uNxtCurrencyID)
% RippleAddress::createHumanAccountID(uNxtAccountID));
// Can't just use push implied, because it can't compensate for next account.
if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer.
|| uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next...
|| uMaxIssuerID != uNxtAccountID) // Next is not implied issuer
{
cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uMaxIssuerID)
% RippleAddress::createHumanAccountID(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID));
cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uMaxIssuerID)
% RippleAddress::createHumanAccountID(uMaxCurrencyID)
% RippleAddress::createHumanAccountID(uMaxIssuerID));
// Add account implied by SendMax.
terStatus = pushNode(
!!uMaxCurrencyID
@@ -375,7 +375,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: sender implied: account=%s
{
if (tesSUCCESS == terStatus)
{
cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:"));
cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:"));
terStatus = pushNode(speElement.getNodeType(), speElement.getAccountID(), speElement.getCurrency(), speElement.getIssuerID());
}
}
@@ -389,10 +389,10 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: element in path:"));
|| pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer.
{
// Add implied account.
cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uOutIssuerID)
% RippleAddress::createHumanAccountID(uOutCurrencyID)
% RippleAddress::createHumanAccountID(uOutIssuerID));
cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=%s currency=%s issuer=%s")
% RippleAddress::createHumanAccountID(uOutIssuerID)
% RippleAddress::createHumanAccountID(uOutCurrencyID)
% RippleAddress::createHumanAccountID(uOutIssuerID));
terStatus = pushNode(
!!uOutCurrencyID
? STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer
@@ -2494,9 +2494,9 @@ int iIndex = 0;
pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID);
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s")
% ++iIndex
% transToken(pspExpanded->terStatus));
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s")
% ++iIndex
% transToken(pspExpanded->terStatus));
// Return, if the path specification was malformed.
if (isTemMalformed(pspExpanded->terStatus))
@@ -2550,17 +2550,18 @@ int iPass = 0;
pspCur->saOutAct = saDstAmountAct;
rc.pathNext(pspCur, bMultiQuality, lesCheckpoint, lesActive); // Compute increment.
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s")
% pspCur->mIndex
% pspCur->uQuality
% STAmount::saFromRate(pspCur->uQuality));
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s")
% pspCur->mIndex
% pspCur->uQuality
% STAmount::saFromRate(pspCur->uQuality));
if (!pspCur->uQuality) {
// Path was dry.
++iDry;
}
else {
else
{
tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG)
<< boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
% STAmount::saFromRate(pspCur->uQuality)
@@ -2586,15 +2587,18 @@ int iPass = 0;
}
}
}
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
if (sLog(lsDEBUG))
{
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d")
% pspCur->mIndex
% STAmount::saFromRate(pspCur->uQuality)
% pspCur->uQuality
% (iBest == pspCur->getIndex())
% pspCur->bConsumed);
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
{
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d")
% pspCur->mIndex
% STAmount::saFromRate(pspCur->uQuality)
% pspCur->uQuality
% (iBest == pspCur->getIndex())
% pspCur->bConsumed);
}
}
if (iBest >= 0)

View File

@@ -23,8 +23,14 @@ DECLARE_INSTANCE(SHAMapTreeNode);
void SHAMapNode::setHash() const
{
std::size_t h = theApp->getNonceST() + mDepth;
std::size_t h = theApp->getNonceST() + (mDepth * 0x9e3779b9);
mHash = mNodeID.hash_combine(h);
#if 0
const unsigned int *ptr = reinterpret_cast<const unsigned int *>(mNodeID.begin());
for (int i = (mDepth + 3) / 4; i != 0; --i)
boost::hash_combine(h, *ptr++);
mHash = h;
#endif
}
std::size_t hash_value(const SHAMapNode& mn)
@@ -64,6 +70,7 @@ SHAMap::SHAMap(SHAMapType t, const uint256& hash) : mSeq(1), mState(smsSynching)
SHAMap::pointer SHAMap::snapShot(bool isMutable)
{ // Return a new SHAMap that is an immutable snapshot of this one
// Initially nodes are shared, but CoW is forced on both ledgers
boost::recursive_mutex::scoped_lock sl(mLock);
SHAMap::pointer ret = boost::make_shared<SHAMap>(mType);
SHAMap& newMap = *ret;
newMap.mSeq = ++mSeq;
@@ -158,10 +165,11 @@ SHAMapTreeNode::pointer SHAMap::walkTo(const uint256& id, bool modify)
while (!inNode->isLeaf())
{
int branch = inNode->selectBranch(id);
if (inNode->isEmptyBranch(branch))
return inNode;
uint256 childHash = inNode->getChildHash(branch);
if (childHash.isZero())
return inNode;
try
{
inNode = getNode(inNode->getChildNodeID(branch), childHash, false);
@@ -205,7 +213,6 @@ SHAMapTreeNode::pointer SHAMap::getNode(const SHAMapNode& id, const uint256& has
std::cerr << "ID: " << id << std::endl;
std::cerr << "TgtHash " << hash << std::endl;
std::cerr << "NodHash " << node->getNodeHash() << std::endl;
dump();
throw std::runtime_error("invalid node");
}
#endif

View File

@@ -43,20 +43,21 @@ public:
SHAMapNode() : mDepth(0), mHash(0) { ; }
SHAMapNode(int depth, const uint256& hash);
virtual ~SHAMapNode() { ; }
int getDepth() const { return mDepth; }
const uint256& getNodeID() const { return mNodeID; }
bool isValid() const { return (mDepth >= 0) && (mDepth < 64); }
bool isValid() const { return (mDepth >= 0) && (mDepth < 64); }
bool isRoot() const { return mDepth == 0; }
size_t getHash() const { if (mHash == 0) setHash(); return mHash; }
virtual bool isPopulated() const { return false; }
virtual bool isPopulated() const { return false; }
SHAMapNode getParentNodeID() const
{
assert(mDepth);
return SHAMapNode(mDepth - 1, mNodeID);
}
SHAMapNode getChildNodeID(int m) const;
int selectBranch(const uint256& hash) const;
@@ -68,7 +69,6 @@ public:
bool operator!=(const uint256&) const;
bool operator<=(const SHAMapNode&) const;
bool operator>=(const SHAMapNode&) const;
bool isRoot() const { return mDepth == 0; }
virtual std::string getString() const;
void dump() const;

View File

@@ -51,7 +51,7 @@ SerializedLedgerEntry::SerializedLedgerEntry(LedgerEntryType type, const uint256
SerializedLedgerEntry::pointer SerializedLedgerEntry::getMutable() const
{
SerializedLedgerEntry::pointer ret = boost::make_shared<SerializedLedgerEntry>(boost::ref(*this));
SerializedLedgerEntry::pointer ret = boost::make_shared<SerializedLedgerEntry>(boost::cref(*this));
ret->mMutable = true;
return ret;
}

View File

@@ -130,14 +130,29 @@ std::auto_ptr<SerializedType> STObject::makeDeserializedObject(SerializedTypeID
}
}
void STObject::set(const std::vector<SOElement::ref>& type)
void SOTemplate::push_back(const SOElement &r)
{
if (mIndex.empty())
mIndex.resize(SField::getNumFields() + 1, -1);
assert(r.e_field.getNum() < mIndex.size());
assert(getIndex(r.e_field) == -1);
mIndex[r.e_field.getNum()] = mTypes.size();
mTypes.push_back(new SOElement(r));
}
int SOTemplate::getIndex(SField::ref f) const
{
assert(f.getNum() < mIndex.size());
return mIndex[f.getNum()];
}
void STObject::set(const SOTemplate& type)
{
mData.clear();
mType.clear();
mType = &type;
BOOST_FOREACH(SOElement::ref elem, type)
BOOST_FOREACH(const SOElement* elem, type.peek())
{
mType.push_back(elem);
if (elem->flags != SOE_REQUIRED)
giveObject(makeNonPresentObject(elem->e_field));
else
@@ -145,15 +160,14 @@ void STObject::set(const std::vector<SOElement::ref>& type)
}
}
bool STObject::setType(const std::vector<SOElement::ref> &type)
bool STObject::setType(const SOTemplate &type)
{
boost::ptr_vector<SerializedType> newData(type.size());
boost::ptr_vector<SerializedType> newData(type.peek().size());
bool valid = true;
mType.clear();
mType.reserve(type.size());
mType = &type;
BOOST_FOREACH(SOElement::ref elem, type)
BOOST_FOREACH(const SOElement* elem, type.peek())
{
bool match = false;
for (boost::ptr_vector<SerializedType>::iterator it = mData.begin(); it != mData.end(); ++it)
@@ -180,8 +194,6 @@ bool STObject::setType(const std::vector<SOElement::ref> &type)
}
newData.push_back(makeNonPresentObject(elem->e_field));
}
mType.push_back(elem);
}
BOOST_FOREACH(const SerializedType& t, mData)
@@ -201,7 +213,7 @@ bool STObject::setType(const std::vector<SOElement::ref> &type)
bool STObject::isValidForType()
{
boost::ptr_vector<SerializedType>::iterator it = mData.begin();
BOOST_FOREACH(SOElement::ref elem, mType)
BOOST_FOREACH(const SOElement* elem, mType->peek())
{
if (it == mData.end())
return false;
@@ -215,14 +227,9 @@ bool STObject::isValidForType()
bool STObject::isFieldAllowed(SField::ref field)
{
if (isFree())
if (mType == NULL)
return true;
BOOST_FOREACH(SOElement::ref elem, mType)
{ // are any required elemnents missing
if (elem->e_field == field)
return true;
}
return false;
return mType->getIndex(field) != -1;
}
bool STObject::set(SerializerIterator& sit, int depth)
@@ -361,6 +368,9 @@ uint256 STObject::getSigningHash(uint32 prefix) const
int STObject::getFieldIndex(SField::ref field) const
{
if (mType != NULL)
return mType->getIndex(field);
int i = 0;
BOOST_FOREACH(const SerializedType& elem, mData)
{
@@ -1243,11 +1253,11 @@ BOOST_AUTO_TEST_CASE( FieldManipulation_test )
SField sfTestU32(STI_UINT32, 255, "TestU32");
SField sfTestObject(STI_OBJECT, 255, "TestObject");
std::vector<SOElement::ref> elements;
elements.push_back(new SOElement(sfFlags, SOE_REQUIRED));
elements.push_back(new SOElement(sfTestVL, SOE_REQUIRED));
elements.push_back(new SOElement(sfTestH256, SOE_OPTIONAL));
elements.push_back(new SOElement(sfTestU32, SOE_REQUIRED));
SOTemplate elements;
elements.push_back(SOElement(sfFlags, SOE_REQUIRED));
elements.push_back(SOElement(sfTestVL, SOE_REQUIRED));
elements.push_back(SOElement(sfTestH256, SOE_OPTIONAL));
elements.push_back(SOElement(sfTestU32, SOE_REQUIRED));
STObject object1(elements, sfTestObject);
STObject object2(object1);

View File

@@ -18,32 +18,44 @@ DEFINE_INSTANCE(SerializedArray);
class SOElement
{ // An element in the description of a serialized object
public:
typedef SOElement const * ref; // used to point to one element
SField::ref e_field;
const SOE_Flags flags;
SField::ref e_field;
const SOE_Flags flags;
SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; }
};
SOElement(SField::ref fi, SOE_Flags fl) : e_field(fi), flags(fl) { ; }
class SOTemplate
{
protected:
std::vector<const SOElement*> mTypes;
std::vector<int> mIndex; // field num -> index
public:
SOTemplate() { ; }
const std::vector<const SOElement*>& peek() const { return mTypes; }
void push_back(const SOElement& r);
int getIndex(SField::ref) const;
};
class STObject : public SerializedType, private IS_INSTANCE(SerializedObject)
{
protected:
boost::ptr_vector<SerializedType> mData;
std::vector<SOElement::ref> mType;
const SOTemplate* mType;
STObject* duplicate() const { return new STObject(*this); }
STObject(SField::ref name, boost::ptr_vector<SerializedType>& data) : SerializedType(name) { mData.swap(data); }
STObject(SField::ref name, boost::ptr_vector<SerializedType>& data) : SerializedType(name), mType(NULL)
{ mData.swap(data); }
public:
STObject() { ; }
STObject() : mType(NULL) { ; }
STObject(SField::ref name) : SerializedType(name) { ; }
STObject(SField::ref name) : SerializedType(name), mType(NULL) { ; }
STObject(const std::vector<SOElement::ref>& type, SField::ref name) : SerializedType(name)
STObject(const SOTemplate& type, SField::ref name) : SerializedType(name)
{ set(type); }
STObject(const std::vector<SOElement::ref>& type, SerializerIterator& sit, SField::ref name) : SerializedType(name)
STObject(const SOTemplate& type, SerializerIterator& sit, SField::ref name) : SerializedType(name)
{ set(sit); setType(type); }
std::auto_ptr<STObject> oClone() const { return std::auto_ptr<STObject>(new STObject(*this)); }
@@ -54,12 +66,12 @@ public:
static std::auto_ptr<SerializedType> deserialize(SerializerIterator& sit, SField::ref name);
bool setType(const std::vector<SOElement::ref>& type);
bool setType(const SOTemplate& type);
bool isValidForType();
bool isFieldAllowed(SField::ref);
bool isFree() const { return mType.empty(); }
bool isFree() const { return mType == NULL; }
void set(const std::vector<SOElement::ref>&);
void set(const SOTemplate&);
bool set(SerializerIterator& u, int depth = 0);
virtual SerializedTypeID getSType() const { return STI_OBJECT; }

View File

@@ -233,7 +233,7 @@ std::string SerializedTransaction::getSQLInsertReplaceHeader()
std::string SerializedTransaction::getMetaSQLInsertHeader()
{
return "INSERT INTO Transactions " + getMetaSQLValueHeader() + " VALUES ";
return "INSERT OR REPLACE INTO Transactions " + getMetaSQLValueHeader() + " VALUES ";
}
std::string SerializedTransaction::getSQL(uint32 inLedger, char status) const

View File

@@ -6,25 +6,22 @@
DECLARE_INSTANCE(SerializedValidation);
std::vector<SOElement::ref> sValidationFormat;
SOTemplate sValidationFormat;
static bool SVFInit()
void SVFInit()
{
sValidationFormat.push_back(new SOElement(sfFlags, SOE_REQUIRED));
sValidationFormat.push_back(new SOElement(sfLedgerHash, SOE_REQUIRED));
sValidationFormat.push_back(new SOElement(sfLedgerSequence, SOE_OPTIONAL));
sValidationFormat.push_back(new SOElement(sfCloseTime, SOE_OPTIONAL));
sValidationFormat.push_back(new SOElement(sfLoadFee, SOE_OPTIONAL));
sValidationFormat.push_back(new SOElement(sfBaseFee, SOE_OPTIONAL));
sValidationFormat.push_back(new SOElement(sfFeatures, SOE_OPTIONAL));
sValidationFormat.push_back(new SOElement(sfSigningTime, SOE_REQUIRED));
sValidationFormat.push_back(new SOElement(sfSigningPubKey, SOE_REQUIRED));
sValidationFormat.push_back(new SOElement(sfSignature, SOE_OPTIONAL));
return true;
sValidationFormat.push_back(SOElement(sfFlags, SOE_REQUIRED));
sValidationFormat.push_back(SOElement(sfLedgerHash, SOE_REQUIRED));
sValidationFormat.push_back(SOElement(sfLedgerSequence, SOE_OPTIONAL));
sValidationFormat.push_back(SOElement(sfCloseTime, SOE_OPTIONAL));
sValidationFormat.push_back(SOElement(sfLoadFee, SOE_OPTIONAL));
sValidationFormat.push_back(SOElement(sfBaseFee, SOE_OPTIONAL));
sValidationFormat.push_back(SOElement(sfFeatures, SOE_OPTIONAL));
sValidationFormat.push_back(SOElement(sfSigningTime, SOE_REQUIRED));
sValidationFormat.push_back(SOElement(sfSigningPubKey, SOE_REQUIRED));
sValidationFormat.push_back(SOElement(sfSignature, SOE_OPTIONAL));
};
bool SVFinitComplete = SVFInit();
const uint32 SerializedValidation::sFullFlag = 0x1;
SerializedValidation::SerializedValidation(SerializerIterator& sit, bool checkSignature)

View File

@@ -476,8 +476,8 @@ int Serializer::encodeLengthLength(int length)
if (length < 0) throw std::overflow_error("len<0");
if (length <= 192) return 1;
if (length <= 12480) return 2;
if (length >= 918744) return 3;
throw std::overflow_error("len>918644");
if (length <= 918744) return 3;
throw std::overflow_error("len>918744");
}
int Serializer::decodeLengthLength(int b1)

View File

@@ -116,7 +116,11 @@ bool Transaction::sign(const RippleAddress& naAccountPrivate)
bool Transaction::checkSign() const
{
assert(mFromPubKey.isValid());
if (!mFromPubKey.isValid())
{
Log(lsWARNING) << "Transaction has bad source public key";
return false;
}
return mTransaction->checkSign(mFromPubKey);
}
@@ -245,16 +249,6 @@ Transaction::pointer Transaction::load(const uint256& id)
return transactionFromSQL(sql);
}
Transaction::pointer Transaction::findFrom(const RippleAddress& fromID, uint32 seq)
{
std::string sql = "SELECT LedgerSeq,Status,RawTxn FROM Transactions WHERE FromID='";
sql.append(fromID.humanAccountID());
sql.append("' AND FromSeq='");
sql.append(boost::lexical_cast<std::string>(seq));
sql.append("';");
return transactionFromSQL(sql);
}
bool Transaction::convertToTransactions(uint32 firstLedgerSeq, uint32 secondLedgerSeq,
bool checkFirstTransactions, bool checkSecondTransactions, const SHAMap::SHAMapDiff& inMap,
std::map<uint256, std::pair<Transaction::pointer, Transaction::pointer> >& outMap)

View File

@@ -99,7 +99,6 @@ public:
// database functions
void save();
static Transaction::pointer load(const uint256& id);
static Transaction::pointer findFrom(const RippleAddress& fromID, uint32 seq);
// conversion function
static bool convertToTransactions(uint32 ourLedgerSeq, uint32 otherLedgerSeq,

View File

@@ -10,6 +10,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
} transResultInfoA[] = {
{ tecCLAIM, "tecCLAIM", "Fee claimed. Sequence used. No action." },
{ tecDIR_FULL, "tecDIR_FULL", "Can not add entry to full directory." },
{ tecFAILED_PROCESSING, "tecFAILED_PROCESSING", "Failed to correctly process transaction." },
{ tecINSUF_RESERVE_LINE, "tecINSUF_RESERVE_LINE", "Insufficent reserve to add trust line." },
{ tecINSUF_RESERVE_OFFER, "tecINSUF_RESERVE_OFFER", "Insufficent reserve to create offer." },
{ tecNO_DST, "tecNO_DST", "Destination does not exist. Send XRP to create it." },
@@ -42,6 +43,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman)
{ telBAD_DOMAIN, "telBAD_DOMAIN", "Domain too long." },
{ telBAD_PATH_COUNT, "telBAD_PATH_COUNT", "Malformed: Too many paths." },
{ telBAD_PUBLIC_KEY, "telBAD_PUBLIC_KEY", "Public key too long." },
{ telFAILED_PROCESSING, "telFAILED_PROCESSING", "Failed to correctly process transaction." },
{ telINSUF_FEE_P, "telINSUF_FEE_P", "Fee insufficient." },
{ telNO_DST_PARTIAL, "telNO_DST_PARTIAL", "Partial payment to create account not allowed." },

View File

@@ -16,6 +16,7 @@ enum TER // aka TransactionEngineResult
telBAD_DOMAIN,
telBAD_PATH_COUNT,
telBAD_PUBLIC_KEY,
telFAILED_PROCESSING,
telINSUF_FEE_P,
telNO_DST_PARTIAL,
@@ -121,6 +122,7 @@ enum TER // aka TransactionEngineResult
tecUNFUNDED_ADD = 102,
tecUNFUNDED_OFFER = 103,
tecUNFUNDED_PAYMENT = 104,
tecFAILED_PROCESSING = 105,
tecDIR_FULL = 121,
tecINSUF_RESERVE_LINE = 122,
tecINSUF_RESERVE_OFFER = 123,

View File

@@ -16,7 +16,7 @@ std::map<std::string, TransactionFormat*> TransactionFormat::byName;
#define DECLARE_TF(name, type) tf = new TransactionFormat(#name, type); (*tf) TF_BASE
static bool TFInit()
void TFInit()
{
TransactionFormat* tf;
@@ -84,12 +84,8 @@ static bool TFInit()
<< SOElement(sfReserveBase, SOE_REQUIRED)
<< SOElement(sfReserveIncrement, SOE_REQUIRED)
;
return true;
}
bool TFInitComplete = TFInit();
TransactionFormat* TransactionFormat::getTxnFormat(TransactionType t)
{
std::map<int, TransactionFormat*>::iterator it = byType.find(static_cast<int>(t));

View File

@@ -31,7 +31,7 @@ class TransactionFormat
public:
std::string t_name;
TransactionType t_type;
std::vector<SOElement::ref> elements;
SOTemplate elements;
static std::map<int, TransactionFormat*> byType;
static std::map<std::string, TransactionFormat*> byName;
@@ -43,7 +43,7 @@ public:
}
TransactionFormat& operator<<(const SOElement& el)
{
elements.push_back(new SOElement(el));
elements.push_back(el);
return *this;
}
@@ -68,7 +68,8 @@ const uint32 tfAccountSetMask = ~(tfRequireDestTag|tfOptionalDestTag|tfRequireA
// OfferCreate flags:
const uint32 tfPassive = 0x00010000;
const uint32 tfOfferCreateMask = ~(tfPassive);
const uint32 tfMarket = 0x00020000;
const uint32 tfOfferCreateMask = ~(tfPassive|tfMarket);
// Payment flags:
const uint32 tfNoRippleDirect = 0x00010000;

View File

@@ -293,7 +293,8 @@ void ValidationCollection::condWrite()
if (mWriting)
return;
mWriting = true;
theApp->getJobQueue().addJob(jtWRITE, boost::bind(&ValidationCollection::doWrite, this, _1));
theApp->getJobQueue().addJob(jtWRITE, "ValidationCollection::doWrite",
boost::bind(&ValidationCollection::doWrite, this, _1));
}
void ValidationCollection::doWrite(Job&)

View File

@@ -152,17 +152,17 @@ public:
ptr->preDestroy(); // Must be done before we return
// Must be done without holding the websocket send lock
theApp->getJobQueue().addJob(jtCLIENT,
theApp->getJobQueue().addJob(jtCLIENT, "WSClient::destroy",
boost::bind(&WSConnection<endpoint_type>::destroy, ptr));
}
void on_message(connection_ptr cpClient, message_ptr mpMessage)
{
theApp->getJobQueue().addJob(jtCLIENT,
theApp->getJobQueue().addJob(jtCLIENT, "WSClient::command",
boost::bind(&WSServerHandler<endpoint_type>::do_message, this, _1, cpClient, mpMessage));
}
void do_message(Job&, connection_ptr cpClient, message_ptr mpMessage)
void do_message(Job& job, connection_ptr cpClient, message_ptr mpMessage)
{
Json::Value jvRequest;
Json::Reader jrReader;
@@ -190,6 +190,8 @@ public:
}
else
{
if (jvRequest.isMember("command"))
job.rename(std::string("WSClient::") + jvRequest["command"].asString());
boost::shared_ptr< WSConnection<endpoint_type> > conn;
{
boost::mutex::scoped_lock sl(mMapLock);

View File

@@ -16,6 +16,10 @@
namespace po = boost::program_options;
extern bool AddSystemEntropy();
extern void TFInit();
extern void LEFInit();
extern void SVFInit();
using namespace std;
using namespace boost::unit_test;
@@ -51,7 +55,6 @@ void startServer()
theApp->run(); // Blocks till we get a stop RPC.
}
bool init_unit_test()
{
theApp = new Application();
@@ -66,34 +69,30 @@ void printHelp(const po::options_description& desc)
cerr << desc << endl;
cerr << "Commands: " << endl;
cerr << " account_domain_set <seed> <paying_account> [<domain>]" << endl;
cerr << " account_email_set <seed> <paying_account> [<email_address>]" << endl;
cerr << " account_tx <account>|<nickname>|<account_public_key> <ledger>|(<minledger> <maxledger>)" << endl;
cerr << " account_lines <account>|<nickname>|<account_public_key> [<index>]" << endl;
cerr << " account_offers <account>|<nickname>|<account_public_key> [<index>]" << endl;
cerr << " account_info <account>|<nickname>" << endl;
cerr << " account_info <seed>|<pass_phrase>|<key> [<index>]" << endl;
cerr << " account_message_set <seed> <paying_account> <pub_key>" << endl;
cerr << " account_publish_set <seed> <paying_account> <hash> <size>" << endl;
cerr << " account_rate_set <seed> <paying_account> <rate>" << endl;
cerr << " account_wallet_set <seed> <paying_account> [<wallet_hash>]" << endl;
cerr << " account_lines <account>|<nickname>|<account_public_key> [<index>]" << endl;
cerr << " account_offers <account>|<nickname>|<account_public_key> [<index>]" << endl;
cerr << " account_tx <account>|<nickname>|<account_public_key> <ledger>|(<minledger> <maxledger>)" << endl;
cerr << " book_offers <taker_pays> <taker_gets> [<taker [<ledger> [<limit> [<proof> [<marker>]]]]]" << endl;
cerr << " connect <ip> [<port>]" << endl;
cerr << " consensus_info" << endl;
#if ENABLE_INSECURE
cerr << " data_delete <key>" << endl;
cerr << " data_fetch <key>" << endl;
cerr << " data_store <key> <value>" << endl;
#endif
cerr << " get_counts" << endl;
cerr << " ledger [<id>|current|lastclosed] [full]" << endl;
cerr << " ledger_accept" << endl;
cerr << " ledger_closed" << endl;
cerr << " ledger_current" << endl;
cerr << " ledger_header <ledger>" << endl;
cerr << " logrotate " << endl;
cerr << " nickname_info <nickname>" << endl;
cerr << " nickname_set <seed> <paying_account> <nickname> [<offer_minimum>] [<authorization>]" << endl;
cerr << " offer_create <seed> <paying_account> <taker_pays_amount> <taker_pays_currency> <taker_pays_issuer> <takers_gets_amount> <takers_gets_currency> <takers_gets_issuer> <expires> [passive]" << endl;
cerr << " offer_cancel <seed> <paying_account> <sequence>" << endl;
cerr << " password_fund <seed> <paying_account> [<account>]" << endl;
cerr << " password_set <master_seed> <regular_seed> [<account>]" << endl;
cerr << " peers" << endl;
cerr << " random" << endl;
cerr << " ripple ..." << endl;
// cerr << " ripple_line_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<quality_in>] [<quality_out>]" << endl;
cerr << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl;
// cerr << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl;
cerr << " stop" << endl;
cerr << " tx <id>" << endl;
cerr << " unl_add <domain>|<public> [<comment>]" << endl;
@@ -109,6 +108,19 @@ void printHelp(const po::options_description& desc)
cerr << " wallet_claim <master_seed> <regular_seed> [<source_tag>] [<account_annotation>]" << endl;
cerr << " wallet_seed [<seed>|<passphrase>|<passkey>]" << endl;
cerr << " wallet_propose [<passphrase>]" << endl;
// Transaction helpers (that were removed):
// cerr << " account_domain_set <seed> <paying_account> [<domain>]" << endl;
// cerr << " account_email_set <seed> <paying_account> [<email_address>]" << endl;
// cerr << " account_rate_set <seed> <paying_account> <rate>" << endl;
// cerr << " account_wallet_set <seed> <paying_account> [<wallet_hash>]" << endl;
// cerr << " nickname_info <nickname>" << endl;
// cerr << " nickname_set <seed> <paying_account> <nickname> [<offer_minimum>] [<authorization>]" << endl;
// cerr << " offer_create <seed> <paying_account> <taker_pays_amount> <taker_pays_currency> <taker_pays_issuer> <takers_gets_amount> <takers_gets_currency> <takers_gets_issuer> <expires> [passive]" << endl;
// cerr << " offer_cancel <seed> <paying_account> <sequence>" << endl;
// cerr << " password_fund <seed> <paying_account> [<account>]" << endl;
// cerr << " password_set <master_seed> <regular_seed> [<account>]" << endl;
// cerr << " trust_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<quality_in>] [<quality_out>]" << endl;
}
int main(int argc, char* argv[])
@@ -180,6 +192,10 @@ int main(int argc, char* argv[])
InstanceType::multiThread();
TFInit();
LEFInit();
SVFInit();
if (vm.count("unittest"))
{
unit_test_main(init_unit_test, argc, argv);

View File

@@ -613,11 +613,16 @@ Amount.prototype.parse_issuer = function (issuer) {
Amount.prototype.parse_json = function (j) {
if ('string' === typeof j) {
// .../.../... notation is not a wire format. But allowed for easier testing.
// var m = j.match(/^(.+)\/(...)(?:\/(.+))?$/);
var m = j.match(/^(.+)\/(...)\/(.+)$/);
if (m) {
this._currency = Currency.from_json(m[2]);
this._issuer = UInt160.from_json(m[3]);
if (m[3]) {
this._issuer = UInt160.from_json(m[3]);
} else {
this._issuer = UInt160.from_json('1');
}
this.parse_value(m[1]);
}
else {

View File

@@ -91,6 +91,26 @@ Request.prototype.ledger_index = function (ledger_index) {
return this;
};
Request.prototype.ledger_select = function (ledger_spec) {
if (ledger_spec === 'closed') {
this.message.ledger_index = -1;
} else if (ledger_spec === 'current') {
this.message.ledger_index = -2;
} else if (ledger_spec === 'verified') {
this.message.ledger_index = -3;
} else if (String(ledger_spec).length > 12) { // XXX Better test needed
this.message.ledger_hash = ledger_spec;
} else {
this.message.ledger_index = ledger_spec;
}
return this;
};
Request.prototype.account_root = function (account) {
this.message.account_root = UInt160.json_rewrite(account);
@@ -846,15 +866,25 @@ Remote.prototype.request_unsubscribe = function (streams) {
return request;
};
// --> current: true, for the current ledger.
Remote.prototype.request_transaction_entry = function (hash, current) {
// .ledger_choose()
// .ledger_hash()
// .ledger_index()
Remote.prototype.request_transaction_entry = function (hash) {
//utils.assert(this.trusted); // If not trusted, need to check proof, maybe talk packet protocol.
return (new Request(this, 'transaction_entry'))
.ledger_choose(current)
.tx_hash(hash);
};
// DEPRECATED: use request_transaction_entry
Remote.prototype.request_tx = function (hash) {
var request = new Request(this, 'tx');
request.message.transaction = hash;
return request;
};
Remote.prototype.request_account_info = function (accountID) {
var request = new Request(this, 'account_info');

View File

@@ -106,14 +106,14 @@ UInt.prototype.parse_generic = function (j) {
case undefined:
case "0":
case this.constructor.STR_ZERO:
case this.constructor.ADDRESS_ZERO:
case this.constructor.ACCOUNT_ZERO:
case this.constructor.HEX_ZERO:
this._value = nbi();
break;
case "1":
case this.constructor.STR_ONE:
case this.constructor.ADDRESS_ONE:
case this.constructor.ACCOUNT_ONE:
case this.constructor.HEX_ONE:
this._value = new BigInteger([1]);

View File

@@ -24,8 +24,8 @@ UInt160.width = 20;
UInt160.prototype = extend({}, UInt.prototype);
UInt160.prototype.constructor = UInt160;
var ADDRESS_ZERO = UInt160.ADDRESS_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
var ADDRESS_ONE = UInt160.ADDRESS_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji";
var ACCOUNT_ZERO = UInt160.ACCOUNT_ZERO = "rrrrrrrrrrrrrrrrrrrrrhoLvTp";
var ACCOUNT_ONE = UInt160.ACCOUNT_ONE = "rrrrrrrrrrrrrrrrrrrrBZbvji";
var HEX_ZERO = UInt160.HEX_ZERO = "0000000000000000000000000000000000000000";
var HEX_ONE = UInt160.HEX_ONE = "0000000000000000000000000000000000000001";
var STR_ZERO = UInt160.STR_ZERO = utils.hexToString(HEX_ZERO);

View File

@@ -24,9 +24,6 @@ UInt256.width = 32;
UInt256.prototype = extend({}, UInt.prototype);
UInt256.prototype.constructor = UInt256;
// XXX Generate these constants (or remove them)
var ADDRESS_ZERO = UInt256.ADDRESS_ZERO = "XXX";
var ADDRESS_ONE = UInt256.ADDRESS_ONE = "XXX";
var HEX_ZERO = UInt256.HEX_ZERO = "00000000000000000000000000000000" +
"00000000000000000000000000000000";
var HEX_ONE = UInt256.HEX_ONE = "00000000000000000000000000000000" +

View File

@@ -17,16 +17,16 @@ buster.testCase("Amount", {
buster.assert.equals(nbi(), UInt160.from_generic("0")._value);
},
"Parse 0 export" : function () {
buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_generic("0").to_json());
buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_generic("0").to_json());
},
"Parse 1" : function () {
buster.assert.equals(new BigInteger([1]), UInt160.from_generic("1")._value);
},
"Parse rrrrrrrrrrrrrrrrrrrrrhoLvTp export" : function () {
buster.assert.equals(UInt160.ADDRESS_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json());
buster.assert.equals(UInt160.ACCOUNT_ZERO, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrrhoLvTp").to_json());
},
"Parse rrrrrrrrrrrrrrrrrrrrBZbvji export" : function () {
buster.assert.equals(UInt160.ADDRESS_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json());
buster.assert.equals(UInt160.ACCOUNT_ONE, UInt160.from_json("rrrrrrrrrrrrrrrrrrrrBZbvji").to_json());
},
"Parse mtgox export" : function () {
buster.assert.equals(config.accounts["mtgox"].account, UInt160.from_json("mtgox").to_json());