mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -9,9 +9,14 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "../ripple/JobQueue.h"
|
||||
#include "../ripple/Log.h"
|
||||
|
||||
SETUP_NLOG("DataBase");
|
||||
|
||||
using namespace std;
|
||||
|
||||
SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"",""), walRunning(false)
|
||||
SqliteDatabase::SqliteDatabase(const char* host) : Database(host,"",""), mWalQ(NULL), walRunning(false)
|
||||
{
|
||||
mConnection = NULL;
|
||||
mCurrentStmt = NULL;
|
||||
@@ -22,7 +27,7 @@ void SqliteDatabase::connect()
|
||||
int rc = sqlite3_open(mHost.c_str(), &mConnection);
|
||||
if (rc)
|
||||
{
|
||||
cout << "Can't open database: " << mHost << " " << rc << endl;
|
||||
cLog(lsFATAL) << "Can't open database: " << mHost << " " << rc;
|
||||
sqlite3_close(mConnection);
|
||||
assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
|
||||
}
|
||||
@@ -46,9 +51,9 @@ bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok)
|
||||
if (!fail_ok)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "SQL Perror:" << rc << endl;
|
||||
cout << "Statement: " << sql << endl;
|
||||
cout << "Error: " << sqlite3_errmsg(mConnection) << endl;
|
||||
cLog(lsWARNING) << "SQL Perror:" << rc;
|
||||
cLog(lsWARNING) << "Statement: " << sql;
|
||||
cLog(lsWARNING) << "Error: " << sqlite3_errmsg(mConnection);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
@@ -70,9 +75,9 @@ bool SqliteDatabase::executeSQL(const char* sql, bool fail_ok)
|
||||
if (!fail_ok)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "SQL Serror:" << rc << endl;
|
||||
cout << "Statement: " << sql << endl;
|
||||
cout << "Error: " << sqlite3_errmsg(mConnection) << endl;
|
||||
cLog(lsWARNING) << "SQL Serror:" << rc;
|
||||
cLog(lsWARNING) << "Statement: " << sql;
|
||||
cLog(lsWARNING) << "Error: " << sqlite3_errmsg(mConnection);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
@@ -130,7 +135,7 @@ bool SqliteDatabase::getNextRow()
|
||||
else
|
||||
{
|
||||
assert((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
|
||||
cout << "SQL Rerror:" << rc << endl;
|
||||
cLog(lsWARNING) << "SQL Rerror:" << rc;
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
@@ -194,27 +199,32 @@ static int SqliteWALHook(void *s, sqlite3* dbCon, const char *dbName, int walSiz
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
bool SqliteDatabase::setupCheckpointing()
|
||||
bool SqliteDatabase::setupCheckpointing(JobQueue *q)
|
||||
{
|
||||
mWalQ = q;
|
||||
sqlite3_wal_hook(mConnection, SqliteWALHook, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SqliteDatabase::doHook(const char *db, int pages)
|
||||
{
|
||||
if (pages < 256)
|
||||
if (pages < 512)
|
||||
return;
|
||||
boost::mutex::scoped_lock sl(walMutex);
|
||||
if (walDBs.insert(db).second && !walRunning)
|
||||
{
|
||||
walRunning = true;
|
||||
boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach();
|
||||
if (mWalQ)
|
||||
mWalQ->addJob(jtWAL, boost::bind(&SqliteDatabase::runWal, this));
|
||||
else
|
||||
boost::thread(boost::bind(&SqliteDatabase::runWal, this)).detach();
|
||||
}
|
||||
}
|
||||
|
||||
void SqliteDatabase::runWal()
|
||||
{
|
||||
std::set<std::string> walSet;
|
||||
std::string name = sqlite3_db_filename(mConnection, "main");
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -231,10 +241,14 @@ void SqliteDatabase::runWal()
|
||||
BOOST_FOREACH(const std::string& db, walSet)
|
||||
{
|
||||
int log, ckpt;
|
||||
sqlite3_wal_checkpoint_v2(mConnection, db.c_str(), SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
|
||||
int ret = sqlite3_wal_checkpoint_v2(mConnection, db.c_str(), SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
|
||||
if (ret != SQLITE_OK)
|
||||
{
|
||||
cLog((ret == SQLITE_LOCKED) ? lsDEBUG : lsWARNING) << "WAL "
|
||||
<< sqlite3_db_filename(mConnection, "main") << " / " << db << " errror " << ret;
|
||||
}
|
||||
}
|
||||
walSet.clear();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ class SqliteDatabase : public Database
|
||||
bool mMoreRows;
|
||||
|
||||
boost::mutex walMutex;
|
||||
JobQueue* mWalQ;
|
||||
std::set<std::string> walDBs;
|
||||
bool walRunning;
|
||||
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
uint64 getBigInt(int colIndex);
|
||||
|
||||
sqlite3* peekConnection() { return mConnection; }
|
||||
virtual bool setupCheckpointing();
|
||||
virtual bool setupCheckpointing(JobQueue*);
|
||||
virtual SqliteDatabase* getSqliteDB() { return this; }
|
||||
|
||||
void runWal();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
class SqliteDatabase;
|
||||
class JobQueue;
|
||||
|
||||
class Database
|
||||
{
|
||||
@@ -86,8 +87,8 @@ public:
|
||||
// float getSingleDBValueFloat(const char* sql);
|
||||
// char* getSingleDBValueStr(const char* sql, std::string& retStr);
|
||||
|
||||
virtual bool setupCheckpointing() { return false; }
|
||||
virtual SqliteDatabase* getSqliteDB() { return NULL; }
|
||||
virtual bool setupCheckpointing(JobQueue*) { return false; }
|
||||
virtual SqliteDatabase* getSqliteDB() { return NULL; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1023,7 +1023,7 @@ STAmount STAmount::setRate(uint64 rate)
|
||||
// --> uOfferPaysRate: >= QUALITY_ONE | TransferRate for third party IOUs paid by offer owner.
|
||||
// --> saOfferRate: Original saOfferGets/saOfferPays, when offer was made.
|
||||
// --> saOfferFunds: Limit for saOfferPays : How much can pay including fees.
|
||||
// --> saTakerFunds: Limit for saOfferGets : How much taker really wants. : Driver
|
||||
// --> saTakerFunds: Limit for saOfferGets : How much can pay including fees.
|
||||
// --> saOfferPays: Request : this should be reduced as the offer is fullfilled.
|
||||
// --> saOfferGets: Request : this should be reduced as the offer is fullfilled.
|
||||
// --> saTakerPays: Limit for taker to Pays.
|
||||
@@ -1050,7 +1050,7 @@ bool STAmount::applyOffer(
|
||||
// Limit offerer funds available, by transfer fees.
|
||||
STAmount saOfferFundsAvailable = QUALITY_ONE == uOfferPaysRate
|
||||
? saOfferFunds
|
||||
: STAmount::divide(saOfferFunds, STAmount(CURRENCY_ONE, uOfferPaysRate, -9));
|
||||
: STAmount::divide(saOfferFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uOfferPaysRate, -9));
|
||||
|
||||
cLog(lsINFO) << "applyOffer: uOfferPaysRate=" << uOfferPaysRate;
|
||||
cLog(lsINFO) << "applyOffer: saOfferFundsAvailable=" << saOfferFundsAvailable.getFullText();
|
||||
@@ -1058,8 +1058,9 @@ bool STAmount::applyOffer(
|
||||
// Limit taker funds available, by transfer fees.
|
||||
STAmount saTakerFundsAvailable = QUALITY_ONE == uTakerPaysRate
|
||||
? saTakerFunds
|
||||
: STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, uTakerPaysRate, -9));
|
||||
: STAmount::divide(saTakerFunds, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9));
|
||||
|
||||
cLog(lsINFO) << "applyOffer: TAKER_FEES=" << STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9).getFullText();
|
||||
cLog(lsINFO) << "applyOffer: uTakerPaysRate=" << uTakerPaysRate;
|
||||
cLog(lsINFO) << "applyOffer: saTakerFundsAvailable=" << saTakerFundsAvailable.getFullText();
|
||||
|
||||
@@ -1085,10 +1086,14 @@ bool STAmount::applyOffer(
|
||||
cLog(lsINFO) << "applyOffer: saOfferGetsAvailable=" << saOfferGetsAvailable.getFullText();
|
||||
|
||||
STAmount saTakerPaysAvailable = std::min(saTakerPays, saTakerFundsAvailable);
|
||||
cLog(lsINFO) << "applyOffer: saTakerPaysAvailable=" << saTakerPaysAvailable.getFullText();
|
||||
STAmount saTakerPaysMax = std::min(saTakerPaysAvailable, saOfferGetsAvailable);
|
||||
cLog(lsINFO) << "applyOffer: saTakerPaysMax=" << saTakerPaysMax.getFullText();
|
||||
STAmount saTakerGetsMax = saTakerPaysMax >= saOfferGetsAvailable
|
||||
? saOfferPaysAvailable // Potentially take entire offer. Avoid math shenanigans.
|
||||
: std::min(saOfferPaysAvailable, multiply(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer.
|
||||
: std::min(saOfferPaysAvailable, divide(saTakerPaysMax, saOfferRate, saTakerGets)); // Taker a portion of offer.
|
||||
cLog(lsINFO) << "applyOffer: saOfferRate=" << saOfferRate.getFullText();
|
||||
cLog(lsINFO) << "applyOffer: saTakerGetsMax=" << saTakerGetsMax.getFullText();
|
||||
|
||||
saTakerGot = std::min(saTakerGets, saTakerGetsMax); // Limit by wanted.
|
||||
saTakerPaid = saTakerGot == saOfferPaysAvailable
|
||||
@@ -1106,11 +1111,12 @@ bool STAmount::applyOffer(
|
||||
{
|
||||
// Compute fees in a rounding safe way.
|
||||
|
||||
// TakerCost includes transfer fees.
|
||||
STAmount saTakerCost = STAmount::multiply(saTakerPaid, STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9));
|
||||
|
||||
STAmount saTransferRate = STAmount(CURRENCY_ONE, ACCOUNT_ONE, uTakerPaysRate, -9);
|
||||
cLog(lsINFO) << "applyOffer: saTransferRate=" << saTransferRate.getFullText();
|
||||
|
||||
// TakerCost includes transfer fees.
|
||||
STAmount saTakerCost = STAmount::multiply(saTakerPaid, saTransferRate);
|
||||
|
||||
cLog(lsINFO) << "applyOffer: saTakerCost=" << saTakerCost.getFullText();
|
||||
cLog(lsINFO) << "applyOffer: saTakerFunds=" << saTakerFunds.getFullText();
|
||||
saTakerIssuerFee = saTakerCost > saTakerFunds
|
||||
|
||||
@@ -62,10 +62,10 @@ void Application::stop()
|
||||
{
|
||||
cLog(lsINFO) << "Received shutdown request";
|
||||
mIOService.stop();
|
||||
mJobQueue.shutdown();
|
||||
mHashedObjectStore.bulkWrite();
|
||||
mValidations.flush();
|
||||
mAuxService.stop();
|
||||
mJobQueue.shutdown();
|
||||
|
||||
cLog(lsINFO) << "Stopped: " << mIOService.stopped();
|
||||
Instance::shutdown();
|
||||
@@ -122,9 +122,9 @@ void Application::setup()
|
||||
boost::thread t5(boost::bind(&InitDB, &mHashNodeDB, "hashnode.db", HashNodeDBInit, HashNodeDBCount));
|
||||
boost::thread t6(boost::bind(&InitDB, &mNetNodeDB, "netnode.db", NetNodeDBInit, NetNodeDBCount));
|
||||
t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); t6.join();
|
||||
mTxnDB->getDB()->setupCheckpointing();
|
||||
mLedgerDB->getDB()->setupCheckpointing();
|
||||
mHashNodeDB->getDB()->setupCheckpointing();
|
||||
mTxnDB->getDB()->setupCheckpointing(&mJobQueue);
|
||||
mLedgerDB->getDB()->setupCheckpointing(&mJobQueue);
|
||||
mHashNodeDB->getDB()->setupCheckpointing(&mJobQueue);
|
||||
|
||||
if (theConfig.START_UP == Config::FRESH)
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@ JobQueue::JobQueue() : mLastJob(0), mThreadCount(0), mShuttingDown(false)
|
||||
mJobLoads[jtTRANSACTION].setTargetLatency(250, 1000);
|
||||
mJobLoads[jtPROPOSAL_ut].setTargetLatency(500, 1250);
|
||||
mJobLoads[jtPUBLEDGER].setTargetLatency(1000, 2500);
|
||||
mJobLoads[jtWAL].setTargetLatency(1000, 2500);
|
||||
mJobLoads[jtVALIDATION_t].setTargetLatency(500, 1500);
|
||||
mJobLoads[jtWRITE].setTargetLatency(750, 1500);
|
||||
mJobLoads[jtTRANSACTION_l].setTargetLatency(100, 500);
|
||||
@@ -41,6 +42,7 @@ const char* Job::toString(JobType t)
|
||||
case jtTRANSACTION: return "transaction";
|
||||
case jtPUBLEDGER: return "publishLedger";
|
||||
case jtVALIDATION_t: return "trustedValidation";
|
||||
case jtWAL: return "writeAhead";
|
||||
case jtWRITE: return "writeObjects";
|
||||
case jtTRANSACTION_l: return "localTransaction";
|
||||
case jtPROPOSAL_t: return "trustedProposal";
|
||||
|
||||
@@ -27,12 +27,13 @@ enum JobType
|
||||
jtCLIENT = 4, // A websocket command from the client
|
||||
jtTRANSACTION = 5, // A transaction received from the network
|
||||
jtPUBLEDGER = 6, // Publish a fully-accepted ledger
|
||||
jtVALIDATION_t = 7, // A validation from a trusted source
|
||||
jtWRITE = 8, // Write out hashed objects
|
||||
jtTRANSACTION_l = 9, // A local transaction
|
||||
jtPROPOSAL_t = 10, // A proposal from a trusted source
|
||||
jtADMIN = 11, // An administrative operation
|
||||
jtDEATH = 12, // job of death, used internally
|
||||
jtWAL = 7, // Write-ahead logging
|
||||
jtVALIDATION_t = 8, // A validation from a trusted source
|
||||
jtWRITE = 9, // Write out hashed objects
|
||||
jtTRANSACTION_l = 10, // A local transaction
|
||||
jtPROPOSAL_t = 11, // A proposal from a trusted source
|
||||
jtADMIN = 12, // An administrative operation
|
||||
jtDEATH = 13, // job of death, used internally
|
||||
|
||||
// special types not dispatched by the job pool
|
||||
jtPEER = 17,
|
||||
|
||||
@@ -1129,21 +1129,25 @@ STAmount LedgerEntrySet::accountFunds(const uint160& uAccountID, const STAmount&
|
||||
// Calculate transit fee.
|
||||
STAmount LedgerEntrySet::rippleTransferFee(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount)
|
||||
{
|
||||
STAmount saTransitFee;
|
||||
|
||||
if (uSenderID != uIssuerID && uReceiverID != uIssuerID)
|
||||
{
|
||||
uint32 uTransitRate = rippleTransferRate(uIssuerID);
|
||||
|
||||
if (QUALITY_ONE != uTransitRate)
|
||||
{
|
||||
STAmount saTransitRate(CURRENCY_ONE, uTransitRate, -9);
|
||||
STAmount saTransitRate(CURRENCY_ONE, ACCOUNT_ONE, uTransitRate, -9);
|
||||
|
||||
saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer());
|
||||
STAmount saTransferTotal = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer());
|
||||
STAmount saTransferFee = saTransferTotal-saAmount;
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("rippleTransferFee: saTransferFee=%s")
|
||||
% saTransferFee.getFullText());
|
||||
|
||||
return saTransferFee;
|
||||
}
|
||||
}
|
||||
|
||||
return saTransitFee;
|
||||
return STAmount(saAmount.getCurrency(), saAmount.getIssuer());
|
||||
}
|
||||
|
||||
TER LedgerEntrySet::trustCreate(
|
||||
@@ -1239,7 +1243,7 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
|
||||
|
||||
saBalance.setIssuer(ACCOUNT_ONE);
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s (0) -> %s : %s")
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: create line: %s --> %s : %s")
|
||||
% RippleAddress::createHumanAccountID(uSenderID)
|
||||
% RippleAddress::createHumanAccountID(uReceiverID)
|
||||
% saAmount.getFullText());
|
||||
@@ -1258,21 +1262,21 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
|
||||
{
|
||||
STAmount saBalance = sleRippleState->getFieldAmount(sfBalance);
|
||||
|
||||
if (!bSenderHigh)
|
||||
saBalance.negate(); // Put balance in low terms.
|
||||
if (bSenderHigh)
|
||||
saBalance.negate(); // Put balance in sender terms.
|
||||
|
||||
STAmount saBefore = saBalance;
|
||||
|
||||
saBalance += saAmount;
|
||||
saBalance -= saAmount;
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: %s -- (%s > %s) -> %s : %s")
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCredit: %s --> %s : before=%s amount=%s after=%s")
|
||||
% RippleAddress::createHumanAccountID(uSenderID)
|
||||
% saBefore.getFullText()
|
||||
% saBalance.getFullText()
|
||||
% RippleAddress::createHumanAccountID(uReceiverID)
|
||||
% saAmount.getFullText());
|
||||
% saBefore.getFullText()
|
||||
% saAmount.getFullText()
|
||||
% saBalance.getFullText());
|
||||
|
||||
if (!bSenderHigh)
|
||||
if (bSenderHigh)
|
||||
saBalance.negate();
|
||||
|
||||
sleRippleState->setFieldAmount(sfBalance, saBalance);
|
||||
@@ -1286,8 +1290,8 @@ TER LedgerEntrySet::rippleCredit(const uint160& uSenderID, const uint160& uRecei
|
||||
}
|
||||
|
||||
// Send regardless of limits.
|
||||
// --> saAmount: Amount/currency/issuer for receiver to get.
|
||||
// <-- saActual: Amount actually sent. Sender pay's fees.
|
||||
// --> saAmount: Amount/currency/issuer to deliver to reciever.
|
||||
// <-- saActual: Amount actually cost. Sender pay's fees.
|
||||
TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, STAmount& saActual)
|
||||
{
|
||||
const uint160 uIssuerID = saAmount.getIssuer();
|
||||
@@ -1314,12 +1318,12 @@ TER LedgerEntrySet::rippleSend(const uint160& uSenderID, const uint160& uReceive
|
||||
|
||||
saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above.
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("rippleSend> %s -- %s--> %s (%s) : %s")
|
||||
cLog(lsINFO) << boost::str(boost::format("rippleSend> %s -- > %s : deliver=%s fee=%s cost=%s")
|
||||
% RippleAddress::createHumanAccountID(uSenderID)
|
||||
% saTransitFee.getFullText()
|
||||
% RippleAddress::createHumanAccountID(uReceiverID)
|
||||
% saActual.getFullText()
|
||||
% saAmount.getFullText());
|
||||
% saAmount.getFullText()
|
||||
% saTransitFee.getFullText()
|
||||
% saActual.getFullText());
|
||||
|
||||
terResult = rippleCredit(uIssuerID, uReceiverID, saAmount);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
// Put at the beginning of a C++ file that needs its own log partition
|
||||
#define SETUP_LOG() static LogPartition logPartition(__FILE__)
|
||||
#define SETUP_NLOG(x) static LogPartition logPartition(x)
|
||||
|
||||
// Standard conditional log
|
||||
#define cLog(x) if (!logPartition.doLog(x)) do {} while (0); else Log(x, logPartition)
|
||||
|
||||
@@ -26,6 +26,13 @@ TER OfferCreateTransactor::takeOffers(
|
||||
STAmount& saTakerGot,
|
||||
bool& bUnfunded)
|
||||
{
|
||||
// The book has the most elements. Take the perspective of the book.
|
||||
// Book is ordered for taker: taker pays / taker gets, < is better
|
||||
|
||||
// The order is for the other books currencys for get and pays are opposites.
|
||||
// We want the same ratio for the respective currencies.
|
||||
// So we swap paid and gets for determing take quality.
|
||||
|
||||
assert(saTakerPays && saTakerGets);
|
||||
|
||||
cLog(lsINFO) << "takeOffers: against book: " << uBookBase.ToString();
|
||||
@@ -58,10 +65,13 @@ TER OfferCreateTransactor::takeOffers(
|
||||
sleOfferDir = mEngine->entryCache(ltDIR_NODE, mEngine->getLedger()->getNextLedgerIndex(uTipIndex, uBookEnd));
|
||||
if (sleOfferDir)
|
||||
{
|
||||
cLog(lsINFO) << "takeOffers: possible counter offer found";
|
||||
|
||||
uTipIndex = sleOfferDir->getIndex();
|
||||
uTipQuality = Ledger::getQuality(uTipIndex);
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("takeOffers: possible counter offer found: uTipQuality=%d uTipQuality=%s")
|
||||
% uTipQuality
|
||||
% uTipIndex.ToString());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,7 +87,25 @@ TER OfferCreateTransactor::takeOffers(
|
||||
|| (bPassive && uTakeQuality == uTipQuality))
|
||||
{
|
||||
// Done.
|
||||
cLog(lsINFO) << "takeOffers: done";
|
||||
STAmount saTipRate = sleOfferDir ? STAmount::setRate(uTipQuality) : saTakerRate;
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("takeOffers: done: dir=%d uTakeQuality=%d %c uTipQuality=%d saTakerRate=%s %c saTipRate=%s bPassive=%d")
|
||||
% !!sleOfferDir
|
||||
% uTakeQuality
|
||||
% (uTakeQuality == uTipQuality
|
||||
? '='
|
||||
: uTakeQuality < uTipQuality
|
||||
? '<'
|
||||
: '>')
|
||||
% uTipQuality
|
||||
% saTakerRate
|
||||
% (saTakerRate == saTipRate
|
||||
? '='
|
||||
: saTakerRate < saTipRate
|
||||
? '<'
|
||||
: '>')
|
||||
% saTipRate
|
||||
% bPassive);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
@@ -143,9 +171,6 @@ TER OfferCreateTransactor::takeOffers(
|
||||
}
|
||||
else
|
||||
{
|
||||
STAmount saPay = saTakerPays - saTakerPaid;
|
||||
if (saTakerFunds < saPay)
|
||||
saPay = saTakerFunds;
|
||||
STAmount saSubTakerPaid;
|
||||
STAmount saSubTakerGot;
|
||||
STAmount saTakerIssuerFee;
|
||||
@@ -156,7 +181,6 @@ TER OfferCreateTransactor::takeOffers(
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerPaid: " << saTakerPaid.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerFunds: " << saTakerFunds.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferFunds: " << saOfferFunds.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferPays: " << saOfferPays.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferGets: " << saOfferGets.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saOfferRate: " << saOfferRate.getFullText();
|
||||
@@ -168,7 +192,7 @@ TER OfferCreateTransactor::takeOffers(
|
||||
mEngine->getNodes().rippleTransferRate(uOfferOwnerID, uTakerAccountID, uTakerGetsAccountID),
|
||||
saOfferRate,
|
||||
saOfferFunds,
|
||||
saPay,
|
||||
saTakerFunds,
|
||||
saOfferPays,
|
||||
saOfferGets,
|
||||
saTakerPays,
|
||||
@@ -221,17 +245,28 @@ TER OfferCreateTransactor::takeOffers(
|
||||
{
|
||||
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerAccountID, saSubTakerGot); // Offer owner pays taker.
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee); // Offer owner pays issuer transfer fee.
|
||||
// if (tesSUCCESS == terResult)
|
||||
// terResult = mEngine->getNodes().accountSend(uOfferOwnerID, uTakerGetsAccountID, saOfferIssuerFee); // Offer owner pays issuer transfer fee.
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uOfferOwnerID, saSubTakerPaid); // Taker pays offer owner.
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee); // Taker pays issuer transfer fee.
|
||||
// if (tesSUCCESS == terResult)
|
||||
// terResult = mEngine->getNodes().accountSend(uTakerAccountID, uTakerPaysAccountID, saTakerIssuerFee); // Taker pays issuer transfer fee.
|
||||
|
||||
// Reduce amount considered paid by taker's rate (not actual cost).
|
||||
saTakerPaid += std::min(saPay, STAmount::multiply(saSubTakerGot, saTakerRate, saPay));
|
||||
STAmount saPay = saTakerPays - saTakerPaid;
|
||||
if (saTakerFunds < saPay)
|
||||
saPay = saTakerFunds;
|
||||
|
||||
STAmount saTakerUsed = STAmount::multiply(saSubTakerGot, saTakerRate, saTakerPays);
|
||||
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saPay: " << saPay.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saSubTakerGot: " << saSubTakerGot.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerRate: " << saTakerRate.getFullText();
|
||||
cLog(lsINFO) << "takeOffers: applyOffer: saTakerUsed: " << saTakerUsed.getFullText();
|
||||
|
||||
saTakerPaid += std::min(saPay, saTakerUsed);
|
||||
saTakerGot += saSubTakerGot;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
@@ -368,8 +403,8 @@ TER OfferCreateTransactor::doApply()
|
||||
}
|
||||
}
|
||||
|
||||
STAmount saOfferPaid;
|
||||
STAmount saOfferGot;
|
||||
STAmount saPaid;
|
||||
STAmount saGot;
|
||||
bool bUnfunded = false;
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
@@ -383,27 +418,29 @@ TER OfferCreateTransactor::doApply()
|
||||
|
||||
// Take using the parameters of the offer.
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: BEFORE saTakerGets=" << saTakerGets.getFullText();
|
||||
|
||||
terResult = takeOffers(
|
||||
bPassive,
|
||||
uTakeBookBase,
|
||||
mTxnAccountID,
|
||||
mTxnAccount,
|
||||
saTakerGets,
|
||||
saTakerGets, // Reverse as we are the taker for taking.
|
||||
saTakerPays,
|
||||
saOfferPaid, // How much would have spent at full price.
|
||||
saOfferGot, // How much was got.
|
||||
saPaid, // How much would have spent at full price.
|
||||
saGot, // How much was got.
|
||||
bUnfunded);
|
||||
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers=" << terResult;
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferPaid=" << saOfferPaid.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saOfferGot=" << saOfferGot.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saTakerPays=" << saTakerPays.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saPaid=" << saPaid.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: saGot=" << saGot.getFullText();
|
||||
|
||||
if (tesSUCCESS == terResult && !bUnfunded)
|
||||
{
|
||||
saTakerPays -= saOfferGot; // Reduce payin from takers by what offer just got.
|
||||
saTakerGets -= saOfferPaid; // Reduce payout to takers by what srcAccount just paid.
|
||||
saTakerPays -= saGot; // Reduce pay in from takers by what offer just got.
|
||||
saTakerGets -= saPaid; // Reduce pay out to takers by what srcAccount just paid.
|
||||
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerPays=" << saTakerPays.getFullText();
|
||||
cLog(lsWARNING) << "OfferCreate: takeOffers: AFTER saTakerGets=" << saTakerGets.getFullText();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +468,7 @@ TER OfferCreateTransactor::doApply()
|
||||
// Hope for more reserve to come in or more offers to consume.
|
||||
terResult = tecINSUF_RESERVE_OFFER;
|
||||
}
|
||||
else if (!saOfferPaid && !saOfferGot)
|
||||
else if (!saPaid && !saGot)
|
||||
{
|
||||
// Ledger is final, insufficent reserve to create offer, processed nothing.
|
||||
|
||||
|
||||
@@ -1009,7 +1009,7 @@ Amount.prototype.parse_native = function (j) {
|
||||
var m;
|
||||
|
||||
if ('string' === typeof j)
|
||||
m = j.match(/^(-?)(\d+)(\.\d{0,6})?$/);
|
||||
m = j.match(/^(-?)(\d*)(\.\d{0,6})?$/);
|
||||
|
||||
if (m) {
|
||||
if (undefined === m[3]) {
|
||||
@@ -1056,8 +1056,8 @@ Amount.prototype.parse_value = function (j) {
|
||||
}
|
||||
else if ('string' === typeof j) {
|
||||
var i = j.match(/^(-?)(\d+)$/);
|
||||
var d = !i && j.match(/^(-?)(\d+)\.(\d*)$/);
|
||||
var e = !e && j.match(/^(-?)(\d+)e(-?\d+)$/);
|
||||
var d = !i && j.match(/^(-?)(\d*)\.(\d*)$/);
|
||||
var e = !e && j.match(/^(-?)(\d*)e(-?\d+)$/);
|
||||
|
||||
if (e) {
|
||||
// e notation
|
||||
|
||||
@@ -1349,4 +1349,118 @@ buster.testCase("Offer cross currency", {
|
||||
},
|
||||
});
|
||||
|
||||
buster.testCase("Offer tests 3", {
|
||||
'setUp' : testutils.build_setup(),
|
||||
// 'setUp' : testutils.build_setup({ verbose: true }),
|
||||
// 'setUp' : testutils.build_setup({ verbose: true, standalone: true }),
|
||||
'tearDown' : testutils.build_teardown(),
|
||||
|
||||
"offer create then cross offer" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set transfer rate.";
|
||||
|
||||
self.remote.transaction()
|
||||
.account_set("mtgox")
|
||||
.transfer_rate(1005000000)
|
||||
.once('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "1000/USD/mtgox",
|
||||
"bob" : "1000/USD/mtgox",
|
||||
"mtgox" : "50/USD/alice",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"mtgox" : [ "1/USD/bob" ],
|
||||
"alice" : [ "50/USD/mtgox" ]
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits 2.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"mtgox" : "0/USD/alice",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer alice.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("alice", "50/USD/mtgox", "150000.0")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: offer_create: %s", json.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq_carol = m.tx_json.sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer bob.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("bob", "100.0", ".1/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: offer_create: %s", json.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq_carol = m.tx_json.sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// function (callback) {
|
||||
// self.what = "Display ledger";
|
||||
//
|
||||
// self.remote.request_ledger('current', true)
|
||||
// .on('success', function (m) {
|
||||
// console.log("Ledger: %s", JSON.stringify(m, undefined, 2));
|
||||
//
|
||||
// callback();
|
||||
// })
|
||||
// .request();
|
||||
// },
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
"alice" : "-49.96666666666667/USD/mtgox",
|
||||
"bob" : "0.9665/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
], function (error) {
|
||||
// console.log("result: error=%s", error);
|
||||
buster.refute(error);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
});
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -1,921 +0,0 @@
|
||||
|
||||
var async = require("async");
|
||||
var buster = require("buster");
|
||||
|
||||
var Amount = require("../src/js/amount").Amount;
|
||||
var Remote = require("../src/js/remote").Remote;
|
||||
var Transaction = require("../src/js/transaction").Transaction;
|
||||
var Server = require("./server").Server;
|
||||
|
||||
var testutils = require("./testutils");
|
||||
|
||||
require("../src/js/amount").config = require("./config");
|
||||
require("../src/js/remote").config = require("./config");
|
||||
|
||||
buster.testRunner.timeout = 5000;
|
||||
|
||||
buster.testCase("Reserve", {
|
||||
'setUp' : testutils.build_setup(),
|
||||
// 'setUp' : testutils.build_setup({ verbose: true, standalone: false }),
|
||||
'tearDown' : testutils.build_teardown(),
|
||||
|
||||
"offer create then cancel in one ledger" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.remote.transaction()
|
||||
.offer_create("root", "500", "100/USD/root")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS', m);
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
|
||||
buster.assert(final_create);
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (m, callback) {
|
||||
self.remote.transaction()
|
||||
.offer_cancel("root", m.tx_json.Sequence)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS', m);
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_cancel: %s", JSON.stringify(m, undefined, 2));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
buster.assert(final_create);
|
||||
done();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (m, callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (message) {
|
||||
// console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash);
|
||||
final_create = message;
|
||||
})
|
||||
.ledger_accept();
|
||||
}
|
||||
], function (error) {
|
||||
// console.log("result: error=%s", error);
|
||||
buster.refute(error);
|
||||
|
||||
if (error) done();
|
||||
});
|
||||
},
|
||||
|
||||
"offer_create then ledger_accept then offer_cancel then ledger_accept." :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
var offer_seq;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.remote.transaction()
|
||||
.offer_create("root", "500", "100/USD/root")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
|
||||
offer_seq = m.tx_json.Sequence;
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
|
||||
final_create = m;
|
||||
|
||||
callback();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
if (!final_create) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash);
|
||||
|
||||
})
|
||||
.ledger_accept();
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
function (callback) {
|
||||
// console.log("CANCEL: offer_cancel: %d", offer_seq);
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_cancel("root", offer_seq)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_cancel: %s", JSON.stringify(m));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
buster.assert(final_create);
|
||||
|
||||
done();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// See if ledger_accept will crash.
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
], function (error) {
|
||||
// console.log("result: error=%s", error);
|
||||
buster.refute(error);
|
||||
|
||||
if (error) done();
|
||||
});
|
||||
},
|
||||
|
||||
"//new user offer_create then ledger_accept then offer_cancel then ledger_accept." :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
var offer_seq;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.remote.transaction()
|
||||
.payment('root', 'alice', "1000")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
buster.assert.equals(m.result, 'tesSUCCESS');
|
||||
callback();
|
||||
})
|
||||
.submit()
|
||||
},
|
||||
function (callback) {
|
||||
self.remote.transaction()
|
||||
.offer_create("alice", "500", "100/USD/alice")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
|
||||
offer_seq = m.tx_json.Sequence;
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_create: %s", JSON.stringify(m));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
|
||||
final_create = m;
|
||||
|
||||
callback();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
if (!final_create) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: %d: %s", ledger_index, ledger_hash);
|
||||
|
||||
})
|
||||
.ledger_accept();
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
function (callback) {
|
||||
// console.log("CANCEL: offer_cancel: %d", offer_seq);
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_cancel("alice", offer_seq)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_cancel: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.on('final', function (m) {
|
||||
// console.log("FINAL: offer_cancel: %s", JSON.stringify(m));
|
||||
|
||||
buster.assert.equals('tesSUCCESS', m.metadata.TransactionResult);
|
||||
buster.assert(final_create);
|
||||
|
||||
done();
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// See if ledger_accept will crash.
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
], function (error) {
|
||||
// console.log("result: error=%s", error);
|
||||
buster.refute(error);
|
||||
if (error) done();
|
||||
});
|
||||
},
|
||||
|
||||
"offer cancel past and future sequence" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var final_create;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.remote.transaction()
|
||||
.payment('root', 'alice', Amount.from_json("10000.0"))
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: CreateAccount: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS', m);
|
||||
})
|
||||
.on('error', function(m) {
|
||||
// console.log("error: %s", m);
|
||||
|
||||
buster.assert(false);
|
||||
callback(m);
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// Past sequence but wrong
|
||||
function (m, callback) {
|
||||
self.remote.transaction()
|
||||
.offer_cancel("root", m.tx_json.Sequence)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_cancel past: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS', m);
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// Same sequence
|
||||
function (m, callback) {
|
||||
self.remote.transaction()
|
||||
.offer_cancel("root", m.tx_json.Sequence+1)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_cancel same: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'temBAD_SEQUENCE', m);
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// Future sequence
|
||||
function (m, callback) {
|
||||
// After a malformed transaction, need to recover correct sequence.
|
||||
self.remote.set_account_seq("root", self.remote.account_seq("root")-1);
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_cancel("root", m.tx_json.Sequence+2)
|
||||
.on('proposed', function (m) {
|
||||
// console.log("ERROR: offer_cancel future: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'temBAD_SEQUENCE');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// See if ledger_accept will crash.
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: A: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
function (callback) {
|
||||
self.remote
|
||||
.once('ledger_closed', function (mesage) {
|
||||
// console.log("LEDGER_CLOSED: B: %d: %s", ledger_index, ledger_hash);
|
||||
callback();
|
||||
})
|
||||
.ledger_accept();
|
||||
},
|
||||
function (callback) {
|
||||
callback();
|
||||
}
|
||||
], function (error) {
|
||||
// console.log("result: error=%s", error);
|
||||
buster.refute(error);
|
||||
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"ripple currency conversion : entire offer" :
|
||||
// mtgox in, XRP out
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Owner count 0.";
|
||||
|
||||
testutils.verify_owner_count(self.remote, "bob", 0, callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "100/USD/mtgox",
|
||||
"bob" : "1000/USD/mtgox"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Owner counts after trust.";
|
||||
|
||||
testutils.verify_owner_counts(self.remote,
|
||||
{
|
||||
"alice" : 1,
|
||||
"bob" : 1,
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"mtgox" : "100/USD/alice"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("bob", "100/USD/mtgox", "500")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Owner counts after offer create.";
|
||||
|
||||
testutils.verify_owner_counts(self.remote,
|
||||
{
|
||||
"alice" : 1,
|
||||
"bob" : 2,
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer balance.";
|
||||
|
||||
testutils.verify_offer(self.remote, "bob", seq, "100/USD/mtgox", "500", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice converts USD to XRP.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "alice", "500")
|
||||
.send_max("100/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
"alice" : [ "0/USD/mtgox", String(10000000000+500-2*(Transaction.fees['default'].to_number())) ],
|
||||
"bob" : "100/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer consumed.";
|
||||
|
||||
testutils.verify_offer_not_found(self.remote, "bob", seq, callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Owner counts after consumed.";
|
||||
|
||||
testutils.verify_owner_counts(self.remote,
|
||||
{
|
||||
"alice" : 1,
|
||||
"bob" : 1,
|
||||
},
|
||||
callback);
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"ripple currency conversion : offerer into debt" :
|
||||
// alice in, carol out
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "2000/EUR/carol",
|
||||
"bob" : "100/USD/alice",
|
||||
"carol" : "1000/EUR/bob"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer to exchange.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("bob", "50/USD/alice", "200/EUR/carol")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tecUNFUNDED_OFFER');
|
||||
|
||||
seq = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
// function (callback) {
|
||||
// self.what = "Alice converts USD to EUR via offer.";
|
||||
//
|
||||
// self.remote.transaction()
|
||||
// .offer_create("alice", "200/EUR/carol", "50/USD/alice")
|
||||
// .on('proposed', function (m) {
|
||||
// // console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
// callback(m.result !== 'tesSUCCESS');
|
||||
//
|
||||
// seq = m.tx_json.Sequence;
|
||||
// })
|
||||
// .submit();
|
||||
// },
|
||||
// function (callback) {
|
||||
// self.what = "Verify balances.";
|
||||
//
|
||||
// testutils.verify_balances(self.remote,
|
||||
// {
|
||||
// "alice" : [ "-50/USD/bob", "200/EUR/carol" ],
|
||||
// "bob" : [ "50/USD/alice", "-200/EUR/carol" ],
|
||||
// "carol" : [ "-200/EUR/alice", "200/EUR/bob" ],
|
||||
// },
|
||||
// callback);
|
||||
// },
|
||||
// function (callback) {
|
||||
// self.what = "Verify offer consumed.";
|
||||
//
|
||||
// testutils.verify_offer_not_found(self.remote, "bob", seq, callback);
|
||||
// },
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"ripple currency conversion : in parts" :
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq;
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "200/USD/mtgox",
|
||||
"bob" : "1000/USD/mtgox"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"mtgox" : "200/USD/alice"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("bob", "100/USD/mtgox", "500")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice converts USD to XRP.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "alice", "200")
|
||||
.send_max("100/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer balance.";
|
||||
|
||||
testutils.verify_offer(self.remote, "bob", seq, "60/USD/mtgox", "300", callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
"alice" : [ "160/USD/mtgox", String(10000000000+200-2*(Transaction.fees['default'].to_number())) ],
|
||||
"bob" : "40/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice converts USD to XRP should fail due to PartialPayment.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "alice", "600")
|
||||
.send_max("100/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tecPATH_PARTIAL');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice converts USD to XRP.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "alice", "600")
|
||||
.send_max("100/USD/mtgox")
|
||||
.set_flags('PartialPayment')
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer consumed.";
|
||||
|
||||
testutils.verify_offer_not_found(self.remote, "bob", seq, callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
"alice" : [ "100/USD/mtgox", String(10000000000+200+300-4*(Transaction.fees['default'].to_number())) ],
|
||||
"bob" : "100/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
buster.testCase("Offer cross currency", {
|
||||
'setUp' : testutils.build_setup(),
|
||||
'tearDown' : testutils.build_teardown(),
|
||||
|
||||
"ripple cross currency payment - start with XRP" :
|
||||
// alice --> [XRP --> carol --> USD/mtgox] --> bob
|
||||
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq;
|
||||
|
||||
// self.remote.set_trace();
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"carol" : "1000/USD/mtgox",
|
||||
"bob" : "2000/USD/mtgox"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"mtgox" : "500/USD/carol"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("carol", "500", "50/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice send USD/mtgox converting from XRP.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "bob", "25/USD/mtgox")
|
||||
.send_max("333")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
// "alice" : [ "500" ],
|
||||
"bob" : "25/USD/mtgox",
|
||||
"carol" : "475/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer consumed.";
|
||||
|
||||
testutils.verify_offer_not_found(self.remote, "bob", seq, callback);
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"ripple cross currency payment - end with XRP" :
|
||||
// alice --> [USD/mtgox --> carol --> XRP] --> bob
|
||||
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq;
|
||||
|
||||
// self.remote.set_trace();
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "1000/USD/mtgox",
|
||||
"carol" : "2000/USD/mtgox"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"mtgox" : "500/USD/alice"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("carol", "50/USD/mtgox", "500")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice send XRP to bob converting from USD/mtgox.";
|
||||
|
||||
self.remote.transaction()
|
||||
.payment("alice", "bob", "250")
|
||||
.send_max("333/USD/mtgox")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("proposed: %s", JSON.stringify(m));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify balances.";
|
||||
|
||||
testutils.verify_balances(self.remote,
|
||||
{
|
||||
"alice" : "475/USD/mtgox",
|
||||
"bob" : "10000000250",
|
||||
"carol" : "25/USD/mtgox",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Verify offer partially consumed.";
|
||||
|
||||
testutils.verify_offer(self.remote, "carol", seq, "25/USD/mtgox", "250", callback);
|
||||
},
|
||||
], function (error) {
|
||||
buster.refute(error, self.what);
|
||||
done();
|
||||
});
|
||||
},
|
||||
|
||||
"ripple cross currency bridged payment" :
|
||||
// alice --> [USD/mtgox --> carol --> XRP] --> [XRP --> dan --> EUR/bitstamp] --> bob
|
||||
|
||||
function (done) {
|
||||
var self = this;
|
||||
var seq_carol;
|
||||
var seq_dan;
|
||||
|
||||
// self.remote.set_trace();
|
||||
|
||||
async.waterfall([
|
||||
function (callback) {
|
||||
self.what = "Create accounts.";
|
||||
|
||||
testutils.create_accounts(self.remote, "root", "10000.0", ["alice", "bob", "carol", "dan", "bitstamp", "mtgox"], callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Set limits.";
|
||||
|
||||
testutils.credit_limits(self.remote,
|
||||
{
|
||||
"alice" : "1000/USD/mtgox",
|
||||
"bob" : "1000/EUR/bitstamp",
|
||||
"carol" : "1000/USD/mtgox",
|
||||
"dan" : "1000/EUR/bitstamp"
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Distribute funds.";
|
||||
|
||||
testutils.payments(self.remote,
|
||||
{
|
||||
"bitstamp" : "400/EUR/dan",
|
||||
"mtgox" : "500/USD/alice",
|
||||
},
|
||||
callback);
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer carol.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("carol", "50/USD/mtgox", "500")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq_carol = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Create offer dan.";
|
||||
|
||||
self.remote.transaction()
|
||||
.offer_create("dan", "500", "50/EUR/bitstamp")
|
||||
.on('proposed', function (m) {
|
||||
// console.log("PROPOSED: offer_create: %s", JSON.stringify(m));
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
|
||||
seq_dan = m.tx_json.Sequence;
|
||||
})
|
||||
.submit();
|
||||
},
|
||||
function (callback) {
|
||||
self.what = "Alice send EUR/bitstamp to bob converting from USD/mtgox.";
|
||||
|
||||
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));
|
||||
|
||||
callback(m.result !== 'tesSUCCESS');
|
||||
})
|
||||
.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, "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();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
Reference in New Issue
Block a user