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

This commit is contained in:
jed
2012-11-20 13:57:56 -08:00
11 changed files with 155 additions and 97 deletions

View File

@@ -84,30 +84,32 @@ void HashedObjectStore::bulkWrite()
fAdd("INSERT INTO CommittedObjects (Hash,ObjType,LedgerIndex,Object) VALUES ('%s','%c','%u',%s);");
Database* db = theApp->getHashNodeDB()->getDB();
ScopedLock sl = theApp->getHashNodeDB()->getDBLock();
db->executeSQL("BEGIN TRANSACTION;");
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
{
if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex())))
{
char type;
switch(it->getType())
{
case hotLEDGER: type = 'L'; break;
case hotTRANSACTION: type = 'T'; break;
case hotACCOUNT_NODE: type = 'A'; break;
case hotTRANSACTION_NODE: type = 'N'; break;
default: type = 'U';
}
std::string rawData;
db->escape(&(it->getData().front()), it->getData().size(), rawData);
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData ));
}
}
ScopedLock sl = theApp->getHashNodeDB()->getDBLock();
db->executeSQL("END TRANSACTION;");
db->executeSQL("BEGIN TRANSACTION;");
BOOST_FOREACH(const boost::shared_ptr<HashedObject>& it, set)
{
if (!SQL_EXISTS(db, boost::str(fExists % it->getHash().GetHex())))
{
char type;
switch(it->getType())
{
case hotLEDGER: type = 'L'; break;
case hotTRANSACTION: type = 'T'; break;
case hotACCOUNT_NODE: type = 'A'; break;
case hotTRANSACTION_NODE: type = 'N'; break;
default: type = 'U';
}
std::string rawData;
db->escape(&(it->getData().front()), it->getData().size(), rawData);
db->executeSQL(boost::str(fAdd % it->getHash().GetHex() % type % it->getIndex() % rawData ));
}
}
db->executeSQL("END TRANSACTION;");
}
}
}
@@ -115,7 +117,6 @@ HashedObject::pointer HashedObjectStore::retrieve(const uint256& hash)
{
HashedObject::pointer obj;
{
ScopedLock sl(theApp->getHashNodeDB()->getDBLock());
obj = mCache.fetch(hash);
if (obj)
{

View File

@@ -23,6 +23,8 @@ const char* Job::toString(JobType t)
case jtCLIENT: return "clientCommand";
case jtPEER: return "peerCommand";
case jtDISK: return "diskAccess";
case jtLEDGER: return "acceptLedger";
case jtRPC: return "rpc";
default: assert(false); return "unknown";
}
}

View File

@@ -36,6 +36,8 @@ enum JobType
jtCLIENT = 10,
jtPEER = 11,
jtDISK = 12,
jtLEDGER = 13,
jtRPC = 14,
};
#define NUM_JOB_TYPES 16

View File

@@ -1021,9 +1021,12 @@ void LedgerConsensus::beginAccept(bool synchronous)
theApp->getOPs().newLCL(mPeerPositions.size(), mCurrentMSeconds, mNewLedgerHash);
if (synchronous)
accept(consensusSet);
accept(consensusSet, LoadEvent::pointer());
else
theApp->getIOService().post(boost::bind(&LedgerConsensus::accept, shared_from_this(), consensusSet));
{
theApp->getIOService().post(boost::bind(&LedgerConsensus::accept, shared_from_this(), consensusSet,
theApp->getJobQueue().getLoadEvent(jtLEDGER)));
}
}
void LedgerConsensus::playbackProposals()
@@ -1170,7 +1173,7 @@ uint32 LedgerConsensus::roundCloseTime(uint32 closeTime)
return closeTime - (closeTime % mCloseResolution);
}
void LedgerConsensus::accept(SHAMap::ref set)
void LedgerConsensus::accept(SHAMap::ref set, LoadEvent::pointer)
{
boost::recursive_mutex::scoped_lock masterLock(theApp->getMasterLock());
assert(set->getHash() == mOurPosition->getCurrentHash());

View File

@@ -18,6 +18,7 @@
#include "CanonicalTXSet.h"
#include "TransactionEngine.h"
#include "InstanceCounter.h"
#include "LoadMonitor.h"
DEFINE_INSTANCE(LedgerConsensus);
@@ -120,7 +121,7 @@ protected:
boost::unordered_set<uint160> mDeadNodes;
// final accept logic
void accept(SHAMap::ref txSet);
void accept(SHAMap::ref txSet, LoadEvent::pointer);
void weHave(const uint256& id, Peer::ref avoidPeer);
void startAcquiring(const TransactionAcquire::pointer&);

View File

@@ -1318,6 +1318,8 @@ Json::Value RPCHandler::doCommand(const std::string& command, Json::Value& param
cLog(lsTRACE) << "RPC:" << command;
cLog(lsTRACE) << "RPC params:" << params;
LoadEvent::pointer le = theApp->getJobQueue().getLoadEvent(jtRPC);
mRole = role;
static struct {

View File

@@ -287,11 +287,15 @@ TER RippleCalc::calcNodeDeliverRev(
const STAmount& saTransferRate = pnCur.saTransferRate;
STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be set.
STAmount& saCurDlvFwd = pnCur.saFwdDeliver;
uint256& uDirectTip = pnCur.uDirectTip;
uDirectTip = 0; // Restart book searching.
saCurDlvFwd.zero(saOutReq); // For forward pass zero deliver.
saPrvDlvReq.zero(pnPrv.uCurrencyID, pnPrv.uIssuerID);
saOutAct.zero(saOutReq);
@@ -472,14 +476,15 @@ TER RippleCalc::calcNodeDeliverRev(
}
// For current offer, get input from deliver/limbo and output to next account or deliver for next offers.
// <-- pnCur.saFwdDeliver: For calcNodeAccountFwd to know how much went through
TER RippleCalc::calcNodeDeliverFwd(
const unsigned int uNode, // 0 < uNode < uLast
PathState::ref pspCur,
const bool bMultiQuality,
const uint160& uInAccountID, // --> Input owner's account.
const STAmount& saInReq, // --> Amount to deliver.
STAmount& saInAct, // <-- Amount delivered.
STAmount& saInFees) // <-- Fees charged.
STAmount& saInAct, // <-- Amount delivered, this invokation.
STAmount& saInFees) // <-- Fees charged, this invokation.
{
TER terResult = tesSUCCESS;
@@ -492,9 +497,9 @@ TER RippleCalc::calcNodeDeliverFwd(
const uint160& uCurIssuerID = pnCur.uIssuerID;
const uint160& uPrvCurrencyID = pnPrv.uCurrencyID;
const uint160& uPrvIssuerID = pnPrv.uIssuerID;
const STAmount& saTransferRate = pnPrv.saTransferRate;
const STAmount& saInTransRate = pnPrv.saTransferRate;
STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
STAmount& saCurDeliverAct = pnCur.saFwdDeliver; // Zeroed in reverse pass.
uint256& uDirectTip = pnCur.uDirectTip;
@@ -502,11 +507,11 @@ TER RippleCalc::calcNodeDeliverFwd(
saInAct.zero(saInReq);
saInFees.zero(saInReq);
saCurDeliverAct.zero(uCurCurrencyID, uCurIssuerID);
while (tesSUCCESS == terResult
&& saInAct + saInFees != saInReq) // Did not deliver all funds.
{
// Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct
terResult = calcNodeAdvance(uNode, pspCur, bMultiQuality, false); // If needed, advance to next funded offer.
if (tesSUCCESS == terResult)
@@ -522,28 +527,33 @@ TER RippleCalc::calcNodeDeliverFwd(
STAmount& saTakerPays = pnCur.saTakerPays;
STAmount& saTakerGets = pnCur.saTakerGets;
const STAmount saInFeeRate = !!uPrvCurrencyID
? uInAccountID == uPrvIssuerID || uOfrOwnerID == uPrvIssuerID // Issuer receiving or sending.
? saOne // No fee.
: saTransferRate // Transfer rate of issuer.
: saOne;
const STAmount saInFeeRate = !uPrvCurrencyID // XRP.
|| uInAccountID == uPrvIssuerID // Sender is issuer.
|| uOfrOwnerID == uPrvIssuerID // Reciever is issuer.
? saOne // No fee.
: saInTransRate; // Transfer rate of issuer.
//
// First calculate assuming no output fees.
// XXX Make sure derived in does not exceed actual saTakerPays due to rounding.
// First calculate assuming no output fees: saInPassAct, saInPassFees, saOutPassAct
STAmount saOutFunded = std::max(saOfferFunds, saTakerGets); // Offer maximum out - There are no out fees.
STAmount saInFunded = STAmount::multiply(saOutFunded, saOfrRate, saInReq); // Offer maximum in - Limited by by payout.
STAmount saInTotal = STAmount::multiply(saInFunded, saTransferRate); // Offer maximum in with fees.
STAmount saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by saInReq.
STAmount saOutFunded = std::min(saOfferFunds, saTakerGets); // Offer maximum out - If there are no out fees.
STAmount saInFunded = STAmount::multiply(saOutFunded, saOfrRate, saTakerPays); // Offer maximum in - Limited by by payout.
STAmount saInTotal = STAmount::multiply(saInFunded, saInTransRate); // Offer maximum in with fees.
STAmount saInSum = std::min(saInTotal, saInReq-saInAct-saInFees); // In limited by remaining.
STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees.
STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saOutFunded); // Out.
STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saTakerGets); // Out limited by in remaining.
STAmount saInPassFees(saInReq.getCurrency(), saInReq.getIssuer());
STAmount saOutPassAct(saOfferFunds.getCurrency(), saOfferFunds.getIssuer());
STAmount saInPassFeesMax = saInSum-saInPassAct;
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saOutFunded=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s")
STAmount saOutPassAct; // Will be determined by next node.
STAmount saInPassFees; // Will be determined by adjusted saInPassAct.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: uNode=%d saOutFunded=%s saInReq=%s saInAct=%s saInFees=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s")
% uNode
% saOutFunded
% saInReq
% saInAct
% saInFees
% saInFunded
% saInTotal
% saInSum
@@ -557,13 +567,14 @@ TER RippleCalc::calcNodeDeliverFwd(
// Output fees: none as XRP or the destination account is the issuer.
saOutPassAct = saOutPassMax;
saInPassFees = saInPassFeesMax;
cLog(lsDEBUG) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> account: uOfrOwnerID=%s uNxtAccountID=%s saOutPassAct=%s")
% RippleAddress::createHumanAccountID(uOfrOwnerID)
% RippleAddress::createHumanAccountID(uNxtAccountID)
% saOutPassAct.getFullText());
// Debit offer owner, send XRP or non-XPR to next account.
// Output: Debit offer owner, send XRP or non-XPR to next account.
lesActive.accountSend(uOfrOwnerID, uNxtAccountID, saOutPassAct);
}
else
@@ -571,43 +582,60 @@ TER RippleCalc::calcNodeDeliverFwd(
// ? --> OFFER --> offer
// Offer to offer means current order book's output currency and issuer match next order book's input current and
// issuer.
// Output fees: possible if issuer has fees and is not on either side.
STAmount saOutPassFees;
// Output fees vary as the next nodes offer owners may vary.
// Therefore, immediately push through output for current offer.
terResult = RippleCalc::calcNodeDeliverFwd(
uNode+1,
pspCur,
bMultiQuality,
uOfrOwnerID,
saOutPassMax,
uOfrOwnerID, // --> Current holder.
saOutPassMax, // --> Amount available.
saOutPassAct, // <-- Amount delivered.
saOutPassFees); // <-- Fees charged.
if (tesSUCCESS != terResult)
break;
// Offer maximum in split into fees by next payout.
saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate);
saInPassFees = STAmount::multiply(saInFunded, saInFeeRate)-saInPassAct;
if (saOutPassAct == saOutPassMax)
{
// No fees and entire output amount.
saInPassFees = saInPassFeesMax;
}
else
{
// Fraction of output amount.
// Output fees are paid by offer owner and not passed to previous.
saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate, saInReq);
saInPassFees = std::min(saInPassFeesMax, STAmount::multiply(saInPassAct, saInFeeRate));
}
// Do outbound debiting.
// Send to issuer/limbo total amount (no fees to issuer).
lesActive.accountSend(uOfrOwnerID, !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP, saOutPassAct);
// Send to issuer/limbo total amount including fees (issuer gets fees).
lesActive.accountSend(uOfrOwnerID, !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP, saOutPassAct+saOutPassFees);
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> offer: saOutPassAct=%s")
% saOutPassAct);
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> offer: saOutPassAct=%s saOutPassFees=%s")
% saOutPassAct
% saOutPassFees);
}
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saTakerGets=%s saTakerPays=%s saInPassAct=%s saOutPassAct=%s")
% saTakerGets.getFullText()
% saTakerPays.getFullText()
% saInPassAct.getFullText()
% saOutPassAct.getFullText());
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: uNode=%d saTakerGets=%s saTakerPays=%s saInPassAct=%s saInPassFees=%s saOutPassAct=%s saOutFunded=%s")
% uNode
% saTakerGets
% saTakerPays
% saInPassAct
% saInPassFees
% saOutPassAct
% saOutFunded);
// Funds were spent.
bFundsDirty = true;
// Do inbound crediting.
// Credit offer owner from in issuer/limbo (don't take transfer fees).
// Credit offer owner from in issuer/limbo (input transfer fees left with owner).
lesActive.accountSend(!!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP, uOfrOwnerID, saInPassAct);
// Adjust offer
@@ -617,7 +645,7 @@ TER RippleCalc::calcNodeDeliverFwd(
lesActive.entryModify(sleOffer);
if (saOutPassAct == saTakerGets)
if (saOutPassAct == saOutFunded)
{
// Offer became unfunded.
pspCur->vUnfundedBecame.push_back(uOfferIndex);
@@ -632,6 +660,11 @@ TER RippleCalc::calcNodeDeliverFwd(
}
}
cLog(lsDEBUG) << boost::str(boost::format("calcNodeDeliverFwd< uNode=%d saInAct=%s saInFees=%s")
% uNode
% saInAct
% saInFees);
return terResult;
}
@@ -696,7 +729,7 @@ TER RippleCalc::calcNodeOfferFwd(
pspCur,
bMultiQuality,
pnPrv.uAccountID,
pnPrv.saFwdDeliver,
pnPrv.saFwdDeliver, // Previous is sending this much.
saInAct,
saInFees);
@@ -1354,6 +1387,11 @@ TER RippleCalc::calcNodeAccountFwd(
}
else if (bPrvAccount && !bNxtAccount)
{
// Current account is issuer to next offer.
// Determine deliver to offer amount.
// Don't adjust outbound balances- keep funds with issuer as limbo.
// If issuer hold's an offer owners inbound IOUs, there is no fee and redeem/issue will transparently happen.
if (uNode)
{
// Non-XRP, current node is the issuer.
@@ -1444,7 +1482,7 @@ TER RippleCalc::calcNodeAccountFwd(
saCurIssueAct.zero(saCurIssueReq);
// deliver -> redeem
if (saPrvDeliverReq) // Previous wants to deliver.
if (saPrvDeliverReq && saCurRedeemReq) // Previous wants to deliver and can current redeem.
{
// Rate : 1.0 : quality out
calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax);

View File

@@ -53,13 +53,19 @@ SerializedTransaction::pointer TransactionMaster::fetch(SHAMapItem::ref item, bo
return txn;
}
static void saveTransactionHelper(Transaction::pointer txn, LoadEvent::pointer)
{
Transaction::saveTransaction(txn);
}
bool TransactionMaster::canonicalize(Transaction::pointer& txn, bool may_be_new)
{
uint256 tid = txn->getID();
if (!tid) return false;
if (mCache.canonicalize(tid, txn)) return true;
if (may_be_new)
theApp->getAuxService().post(boost::bind(&Transaction::saveTransaction, txn));
theApp->getAuxService().post(boost::bind(&saveTransactionHelper, txn,
theApp->getJobQueue().getLoadEvent(jtDISK)));
return false;
}
// vim:ts=4

View File

@@ -19,6 +19,7 @@ var WebSocket = require('ws');
var EventEmitter = require('events').EventEmitter;
var Amount = require('./amount.js').Amount;
var Currency = require('./amount.js').Currency;
var UInt160 = require('./amount.js').UInt160;
// Request events emitted:

View File

@@ -335,6 +335,7 @@ buster.testCase("Offer tests", {
},
"ripple currency conversion : entire offer" :
// mtgox in, XRP out
function (done) {
var self = this;
var seq;
@@ -380,7 +381,7 @@ buster.testCase("Offer tests", {
function (callback) {
self.what = "Verify offer balance.";
testutils.verify_offer(self.remote, "bob", seq, "500", "100/USD/mtgox", callback);
testutils.verify_offer(self.remote, "bob", seq, "100/USD/mtgox", "500", callback);
},
function (callback) {
self.what = "Alice converts USD to XRP.";
@@ -475,7 +476,7 @@ buster.testCase("Offer tests", {
function (callback) {
self.what = "Verify offer balance.";
testutils.verify_offer(self.remote, "bob", seq, "300", "60/USD/mtgox", callback);
testutils.verify_offer(self.remote, "bob", seq, "60/USD/mtgox", "300", callback);
},
function (callback) {
self.what = "Verify balances.";
@@ -696,7 +697,7 @@ buster.testCase("Offer cross currency", {
function (callback) {
self.what = "Verify offer partially consumed.";
testutils.verify_offer(self.remote, "carol", seq, "250", "25/USD/mtgox", callback);
testutils.verify_offer(self.remote, "carol", seq, "25/USD/mtgox", "250", callback);
},
], function (error) {
buster.refute(error, self.what);
@@ -704,7 +705,7 @@ buster.testCase("Offer cross currency", {
});
},
"// ripple cross currency bridged payment" :
"ripple cross currency bridged payment" :
// alice --> [USD/mtgox --> carol --> XRP] --> [XRP --> dan --> EUR/bitstamp] --> bob
function (done) {
@@ -774,6 +775,7 @@ buster.testCase("Offer cross currency", {
self.remote.transaction()
.payment("alice", "bob", "30/EUR/bitstamp")
.send_max("333/USD/mtgox")
.path_add( [ { currency: "XRP" } ])
.on('proposed', function (m) {
// console.log("proposed: %s", JSON.stringify(m));
@@ -781,28 +783,28 @@ buster.testCase("Offer cross currency", {
})
.submit();
},
// function (callback) {
// self.what = "Verify balances.";
//
// testutils.verify_balances(self.remote,
// {
// "alice" : "470/USD/mtgox",
// "bob" : "30/EUR/bitstamp",
// "carol" : "30/USD/mtgox",
// "dan" : "370/EUR/bitstamp",
// },
// callback);
// },
// function (callback) {
// self.what = "Verify carol offer partially consumed.";
//
// testutils.verify_offer(self.remote, "carol", seq_carol, "250", "25/USD/mtgox", callback);
// },
// function (callback) {
// self.what = "Verify dan offer partially consumed.";
//
// testutils.verify_offer(self.remote, "dan", seq_dan, "250", "25/USD/mtgox", callback);
// },
function (callback) {
self.what = "Verify balances.";
testutils.verify_balances(self.remote,
{
"alice" : "470/USD/mtgox",
"bob" : "30/EUR/bitstamp",
"carol" : "30/USD/mtgox",
"dan" : "370/EUR/bitstamp",
},
callback);
},
function (callback) {
self.what = "Verify carol offer partially consumed.";
testutils.verify_offer(self.remote, "carol", seq_carol, "20/USD/mtgox", "200", callback);
},
function (callback) {
self.what = "Verify dan offer partially consumed.";
testutils.verify_offer(self.remote, "dan", seq_dan, "200", "20/EUR/mtgox", callback);
},
], function (error) {
buster.refute(error, self.what);
done();

View File

@@ -326,7 +326,7 @@ var verify_balances = function (remote, balances, callback) {
// --> seq: sequence number of creating transaction.
// --> taker_gets: json amount
// --> taker_pays: json amount
var verify_offer = function (remote, owner, seq, taker_gets, taker_pays, callback) {
var verify_offer = function (remote, owner, seq, taker_pays, taker_gets, callback) {
assert(6 === arguments.length);
remote.request_ledger_entry('offer')
@@ -349,12 +349,12 @@ var verify_offer_not_found = function (remote, owner, seq, callback) {
remote.request_ledger_entry('offer')
.offer_id(owner, seq)
.on('success', function (m) {
console.log("verify_no_offer: found offer: %s", JSON.stringify(m));
console.log("verify_offer_not_found: found offer: %s", JSON.stringify(m));
callback('entryFound');
})
.on('error', function (m) {
// console.log("verify_no_offer: success: %s", JSON.stringify(m));
// console.log("verify_offer_not_found: success: %s", JSON.stringify(m));
callback('remoteError' !== m.error
|| 'entryNotFound' !== m.remote.error);