mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Add ripple quality in and out.
This commit is contained in:
@@ -64,8 +64,10 @@ LedgerEntryFormat LedgerFormats[]=
|
||||
{ S_FIELD(LowLimit), STI_AMOUNT, SOE_REQUIRED, 0 },
|
||||
{ S_FIELD(HighID), STI_ACCOUNT, SOE_REQUIRED, 0 },
|
||||
{ S_FIELD(HighLimit), STI_AMOUNT, SOE_REQUIRED, 0 },
|
||||
{ S_FIELD(QualityIn), STI_UINT32, SOE_IFFLAG, 1 },
|
||||
{ S_FIELD(QualityOut), STI_UINT32, SOE_IFFLAG, 2 },
|
||||
{ S_FIELD(LowQualityIn), STI_UINT32, SOE_IFFLAG, 1 },
|
||||
{ S_FIELD(LowQualityOut), STI_UINT32, SOE_IFFLAG, 2 },
|
||||
{ S_FIELD(HighQualityIn), STI_UINT32, SOE_IFFLAG, 4 },
|
||||
{ S_FIELD(HighQualityOut), STI_UINT32, SOE_IFFLAG, 8 },
|
||||
{ S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x01000000 },
|
||||
{ sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } }
|
||||
},
|
||||
|
||||
@@ -1125,7 +1125,7 @@ Json::Value RPCServer::doPeers(const Json::Value& params)
|
||||
return obj;
|
||||
}
|
||||
|
||||
// ripple_line_set <seed> <paying_account> <destination_account> <limit_amount> [<currency>] [<accept_rate>]
|
||||
// ripple_line_set <seed> <paying_account> <destination_account> <limit_amount> [<currency>] [<quality_in>] [<quality_out>]
|
||||
Json::Value RPCServer::doRippleLineSet(const Json::Value& params)
|
||||
{
|
||||
NewcoinAddress naSeed;
|
||||
@@ -1133,7 +1133,11 @@ Json::Value RPCServer::doRippleLineSet(const Json::Value& params)
|
||||
NewcoinAddress naDstAccountID;
|
||||
STAmount saLimitAmount;
|
||||
uint256 uLedger = mNetOps->getCurrentLedger();
|
||||
uint32 uAcceptRate = params.size() >= 6 ? lexical_cast_s<uint32>(params[5u].asString()) : 0;
|
||||
bool bLimitAmount = true;
|
||||
bool bQualityIn = params.size() >= 6;
|
||||
bool bQualityOut = params.size() >= 7;
|
||||
uint32 uQualityIn = bQualityIn ? lexical_cast_s<uint32>(params[5u].asString()) : 0;
|
||||
uint32 uQualityOut = bQualityOut ? lexical_cast_s<uint32>(params[6u].asString()) : 0;
|
||||
|
||||
if (!naSeed.setSeedGeneric(params[0u].asString()))
|
||||
{
|
||||
@@ -1171,8 +1175,9 @@ Json::Value RPCServer::doRippleLineSet(const Json::Value& params)
|
||||
theConfig.FEE_DEFAULT,
|
||||
0, // YYY No source tag
|
||||
naDstAccountID,
|
||||
saLimitAmount,
|
||||
uAcceptRate);
|
||||
bLimitAmount, saLimitAmount,
|
||||
bQualityIn, uQualityIn,
|
||||
bQualityOut, uQualityOut);
|
||||
|
||||
trans = mNetOps->submitTransaction(trans);
|
||||
|
||||
@@ -1181,8 +1186,6 @@ Json::Value RPCServer::doRippleLineSet(const Json::Value& params)
|
||||
obj["seed"] = naSeed.humanSeed();
|
||||
obj["srcAccountID"] = naSrcAccountID.humanAccountID();
|
||||
obj["dstAccountID"] = naDstAccountID.humanAccountID();
|
||||
obj["limitAmount"] = saLimitAmount.getText();
|
||||
obj["acceptRate"] = uAcceptRate;
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -2133,7 +2136,7 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params
|
||||
{ "password_set", &RPCServer::doPasswordSet, 2, 3, false, optNetwork },
|
||||
{ "peers", &RPCServer::doPeers, 0, 0, true },
|
||||
{ "ripple_lines_get", &RPCServer::doRippleLinesGet, 1, 2, false, optCurrent|optClosed },
|
||||
{ "ripple_line_set", &RPCServer::doRippleLineSet, 4, 6, false, optCurrent },
|
||||
{ "ripple_line_set", &RPCServer::doRippleLineSet, 4, 7, false, optCurrent },
|
||||
{ "send", &RPCServer::doSend, 3, 7, false, optCurrent },
|
||||
{ "server_info", &RPCServer::doServerInfo, 0, 0, true },
|
||||
{ "stop", &RPCServer::doStop, 0, 0, true },
|
||||
|
||||
@@ -55,6 +55,8 @@ enum SOE_Field
|
||||
sfHash,
|
||||
sfHighID,
|
||||
sfHighLimit,
|
||||
sfHighQualityIn,
|
||||
sfHighQualityOut,
|
||||
sfIdentifier,
|
||||
sfIndexes,
|
||||
sfIndexNext,
|
||||
@@ -67,6 +69,8 @@ enum SOE_Field
|
||||
sfLimitAmount,
|
||||
sfLowID,
|
||||
sfLowLimit,
|
||||
sfLowQualityIn,
|
||||
sfLowQualityOut,
|
||||
sfMessageKey,
|
||||
sfMinimumOffer,
|
||||
sfNextAcceptExpire,
|
||||
|
||||
@@ -36,6 +36,20 @@ enum SerializedTypeID
|
||||
STI_LEDGERENTRY = 102
|
||||
};
|
||||
|
||||
enum PathFlags
|
||||
{
|
||||
PF_END = 0x00, // End of current path & path list.
|
||||
PF_BOUNDRY = 0xFF, // End of current path & new path follows.
|
||||
|
||||
PF_ACCOUNT = 0x01,
|
||||
PF_OFFER = 0x02,
|
||||
|
||||
PF_WANTED_CURRENCY = 0x10,
|
||||
PF_WANTED_ISSUER = 0x20,
|
||||
PF_REDEEM = 0x40,
|
||||
PF_ISSUE = 0x80,
|
||||
};
|
||||
|
||||
class SerializedType
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -232,13 +232,23 @@ Transaction::pointer Transaction::sharedCreate(
|
||||
Transaction::pointer Transaction::setCreditSet(
|
||||
const NewcoinAddress& naPrivateKey,
|
||||
const NewcoinAddress& naDstAccountID,
|
||||
bool bLimitAmount,
|
||||
const STAmount& saLimitAmount,
|
||||
uint32 uAcceptRate)
|
||||
bool bQualityIn,
|
||||
uint32 uQualityIn,
|
||||
bool bQualityOut,
|
||||
uint32 uQualityOut)
|
||||
{
|
||||
mTransaction->setITFieldAccount(sfDestination, naDstAccountID);
|
||||
|
||||
if (bLimitAmount)
|
||||
mTransaction->setITFieldAmount(sfLimitAmount, saLimitAmount);
|
||||
if (uAcceptRate)
|
||||
mTransaction->setITFieldU32(sfAcceptRate, uAcceptRate);
|
||||
|
||||
if (bQualityIn)
|
||||
mTransaction->setITFieldU32(sfAcceptRate, uQualityIn);
|
||||
|
||||
if (bQualityOut)
|
||||
mTransaction->setITFieldU32(sfAcceptRate, uQualityOut);
|
||||
|
||||
sign(naPrivateKey);
|
||||
|
||||
@@ -252,12 +262,19 @@ Transaction::pointer Transaction::sharedCreditSet(
|
||||
const STAmount& saFee,
|
||||
uint32 uSourceTag,
|
||||
const NewcoinAddress& naDstAccountID,
|
||||
bool bLimitAmount,
|
||||
const STAmount& saLimitAmount,
|
||||
uint32 uAcceptRate)
|
||||
bool bQualityIn,
|
||||
uint32 uQualityIn,
|
||||
bool bQualityOut,
|
||||
uint32 uQualityOut)
|
||||
{
|
||||
pointer tResult = boost::make_shared<Transaction>(ttCREDIT_SET, naPublicKey, naSourceAccount, uSeq, saFee, uSourceTag);
|
||||
|
||||
return tResult->setCreditSet(naPrivateKey, naDstAccountID, saLimitAmount, uAcceptRate);
|
||||
return tResult->setCreditSet(naPrivateKey, naDstAccountID,
|
||||
bLimitAmount, saLimitAmount,
|
||||
bQualityIn, uQualityIn,
|
||||
bQualityOut, uQualityOut);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -68,8 +68,12 @@ private:
|
||||
Transaction::pointer setCreditSet(
|
||||
const NewcoinAddress& naPrivateKey,
|
||||
const NewcoinAddress& naDstAccountID,
|
||||
bool bLimitAmount,
|
||||
const STAmount& saLimitAmount,
|
||||
uint32 uAcceptRate);
|
||||
bool bQualityIn,
|
||||
uint32 uQualityIn,
|
||||
bool bQualityOut,
|
||||
uint32 uQualityOut);
|
||||
|
||||
Transaction::pointer setNicknameSet(
|
||||
const NewcoinAddress& naPrivateKey,
|
||||
@@ -166,8 +170,12 @@ public:
|
||||
const STAmount& saFee,
|
||||
uint32 uSourceTag,
|
||||
const NewcoinAddress& naDstAccountID,
|
||||
bool bLimitAmount,
|
||||
const STAmount& saLimitAmount,
|
||||
uint32 uAcceptRate);
|
||||
bool bQualityIn,
|
||||
uint32 uQualityIn,
|
||||
bool bQualityOut,
|
||||
uint32 uQualityOut);
|
||||
|
||||
// Set Nickname
|
||||
static Transaction::pointer sharedNicknameSet(
|
||||
|
||||
@@ -111,6 +111,7 @@ STAmount TransactionEngine::rippleHolds(const uint160& uAccountID, const uint160
|
||||
return saBalance;
|
||||
}
|
||||
|
||||
// <-- saAmount: amount of uCurrency held by uAccountID. May be negative.
|
||||
STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID)
|
||||
{
|
||||
STAmount saAmount;
|
||||
@@ -138,7 +139,11 @@ STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint16
|
||||
return saAmount;
|
||||
}
|
||||
|
||||
// Returns the funds available for uAccountID for a currency/issuer.
|
||||
// Use when you need a default for rippling uAccountID's currency.
|
||||
// --> saDefault/currency/issuer
|
||||
// <-- saFunds: Funds available. May be negative.
|
||||
// If the issuer is the same as uAccountID, result is Default.
|
||||
STAmount TransactionEngine::accountFunds(const uint160& uAccountID, const STAmount& saDefault)
|
||||
{
|
||||
STAmount saFunds;
|
||||
@@ -582,30 +587,55 @@ TransactionEngineResult TransactionEngine::dirDelete(
|
||||
return terSUCCESS;
|
||||
}
|
||||
|
||||
// --> uRootIndex
|
||||
// <-- uEntryIndex
|
||||
// <-- uEntryNode
|
||||
void TransactionEngine::dirFirst(const uint256& uRootIndex, uint256& uEntryIndex, uint64& uEntryNode)
|
||||
// <-- true, if had a next entry.
|
||||
bool TransactionEngine::dirFirst(
|
||||
const uint256& uRootIndex, // --> Root of directory.
|
||||
SLE::pointer& sleNode, // <-> current node
|
||||
unsigned int& uDirEntry, // <-- next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
SLE::pointer sleRoot = entryCache(ltDIR_NODE, uRootIndex);
|
||||
sleNode = entryCache(ltDIR_NODE, uRootIndex);
|
||||
uDirEntry = 0;
|
||||
|
||||
STVector256 svIndexes = sleRoot->getIFieldV256(sfIndexes);
|
||||
assert(sleNode); // We never probe for directories.
|
||||
|
||||
return TransactionEngine::dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
|
||||
// <-- true, if had a next entry.
|
||||
bool TransactionEngine::dirNext(
|
||||
const uint256& uRootIndex, // --> Root of directory
|
||||
SLE::pointer& sleNode, // <-> current node
|
||||
unsigned int& uDirEntry, // <-> next entry
|
||||
uint256& uEntryIndex) // <-- The entry, if available. Otherwise, zero.
|
||||
{
|
||||
STVector256 svIndexes = sleNode->getIFieldV256(sfIndexes);
|
||||
std::vector<uint256>& vuiIndexes = svIndexes.peekValue();
|
||||
|
||||
if (vuiIndexes.empty())
|
||||
if (uDirEntry == vuiIndexes.size())
|
||||
{
|
||||
uEntryNode = sleRoot->getIFieldU64(sfIndexNext);
|
||||
uint64 uNodeNext = sleNode->getIFieldU64(sfIndexNext);
|
||||
|
||||
SLE::pointer sleNext = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uEntryNode));
|
||||
uEntryIndex = sleNext->getIFieldV256(sfIndexes).peekValue()[0];
|
||||
if (!uNodeNext)
|
||||
{
|
||||
uEntryIndex.zero();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
uEntryIndex = vuiIndexes[0];
|
||||
uEntryNode = 0;
|
||||
sleNode = entryCache(ltDIR_NODE, Ledger::getDirNodeIndex(uRootIndex, uNodeNext));
|
||||
uDirEntry = 0;
|
||||
|
||||
return dirNext(uRootIndex, sleNode, uDirEntry, uEntryIndex);
|
||||
}
|
||||
}
|
||||
|
||||
uEntryIndex = vuiIndexes[uDirEntry++];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set the authorized public key for an account. May also set the generator map.
|
||||
TransactionEngineResult TransactionEngine::setAuthorized(const SerializedTransaction& txn, bool bMustSetGenerator)
|
||||
{
|
||||
@@ -1329,10 +1359,15 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti
|
||||
return terNO_DST;
|
||||
}
|
||||
|
||||
STAmount saLimitAmount = txn.getITFieldAmount(sfLimitAmount);
|
||||
uint160 uCurrency = saLimitAmount.getCurrency();
|
||||
bool bFlipped = mTxnAccountID > uDstAccountID;
|
||||
uint32 uFlags = bFlipped ? lsfLowIndexed : lsfHighIndexed;
|
||||
bool bLimitAmount = txn.getITFieldPresent(sfLimitAmount);
|
||||
STAmount saLimitAmount = bLimitAmount ? txn.getITFieldAmount(sfLimitAmount) : STAmount();
|
||||
bool bQualityIn = txn.getITFieldPresent(sfQualityIn);
|
||||
uint32 uQualityIn = bQualityIn ? txn.getITFieldU32(sfQualityIn) : 0;
|
||||
bool bQualityOut = txn.getITFieldPresent(sfQualityOut);
|
||||
uint32 uQualityOut = bQualityIn ? txn.getITFieldU32(sfQualityOut) : 0;
|
||||
uint160 uCurrency = saLimitAmount.getCurrency();
|
||||
STAmount saBalance(uCurrency);
|
||||
bool bAddIndex = false;
|
||||
bool bDelIndex = false;
|
||||
@@ -1364,12 +1399,40 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!bDelIndex)
|
||||
{
|
||||
bAddIndex = !(sleRippleState->getFlags() & uFlags);
|
||||
|
||||
if (bLimitAmount)
|
||||
sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit: sfLowLimit , saLimitAmount);
|
||||
|
||||
if (!bQualityIn)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (uQualityIn)
|
||||
{
|
||||
sleRippleState->setIFieldU32(bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
|
||||
}
|
||||
else
|
||||
{
|
||||
sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityIn : sfHighQualityIn);
|
||||
}
|
||||
|
||||
if (!bQualityOut)
|
||||
{
|
||||
nothing();
|
||||
}
|
||||
else if (uQualityOut)
|
||||
{
|
||||
sleRippleState->setIFieldU32(bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
|
||||
}
|
||||
else
|
||||
{
|
||||
sleRippleState->makeIFieldAbsent(bFlipped ? sfLowQualityOut : sfHighQualityOut);
|
||||
}
|
||||
|
||||
bAddIndex = !(sleRippleState->getFlags() & uFlags);
|
||||
|
||||
if (bAddIndex)
|
||||
sleRippleState->setFlag(uFlags);
|
||||
|
||||
@@ -1401,6 +1464,10 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti
|
||||
sleRippleState->setIFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, saZero);
|
||||
sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, mTxnAccountID);
|
||||
sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uDstAccountID);
|
||||
if (uQualityIn)
|
||||
sleRippleState->setIFieldU32(bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn);
|
||||
if (uQualityOut)
|
||||
sleRippleState->setIFieldU32(bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut);
|
||||
}
|
||||
|
||||
if (bAddIndex)
|
||||
@@ -1517,19 +1584,9 @@ TransactionEngineResult TransactionEngine::doPasswordSet(const SerializedTransac
|
||||
}
|
||||
|
||||
#ifdef WORK_IN_PROGRESS
|
||||
TransactionEngineResult calcOfferFill(SAAmount& saSrc, paymentNode& pnSrc, paymentNode& pnDst)
|
||||
{
|
||||
TransactionEngineResult terResult;
|
||||
|
||||
if (!saSrc.isZero())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
// XXX Need to adjust for fees.
|
||||
// Find offers to satisfy pnDst.
|
||||
// - Does not adjust any balances as there is at least a forward pass to come.
|
||||
// --> pnDst.saWanted: currency and amount wanted
|
||||
// --> pnSrc.saIOURedeem.mCurrency: use this before saIOUIssue, limit to use.
|
||||
// --> pnSrc.saIOUIssue.mCurrency: use this after saIOURedeem, limit to use.
|
||||
@@ -1543,25 +1600,389 @@ TransactionEngineResult calcOfferFill(paymentNode& pnSrc, paymentNode& pnDst, bo
|
||||
{
|
||||
TransactionEngineResult terResult;
|
||||
|
||||
terResult = calcOfferFill(pnSrc.saIOURedeem, pnSrc, pnDst, bAllowPartial);
|
||||
if (pnDst.saWanted.isNative())
|
||||
{
|
||||
// Transfer stamps.
|
||||
|
||||
STAmount saSrcFunds = pnSrc.saAccount->accountHolds(pnSrc.saAccount, uint160(0), uint160(0));
|
||||
|
||||
if (saSrcFunds && (bAllowPartial || saSrcFunds > pnDst.saWanted))
|
||||
{
|
||||
pnSrc.saSend = min(saSrcFunds, pnDst.saWanted);
|
||||
pnDst.saReceive = pnSrc.saSend;
|
||||
}
|
||||
else
|
||||
{
|
||||
terResult = terINSUF_PATH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ripple funds.
|
||||
{
|
||||
prv->saSend = min(prv->account->saBalance(), cur->saWanted);
|
||||
// Redeem to limit.
|
||||
terResult = calcOfferFill(
|
||||
accountHolds(pnSrc.saAccount, pnDst.saWanted.getCurrency(), pnDst.saWanted.getIssuer()),
|
||||
pnSrc.saIOURedeem,
|
||||
pnDst.saIOUForgive,
|
||||
bAllowPartial);
|
||||
|
||||
if (terSUCCESS == terResult)
|
||||
{
|
||||
terResult = calcOfferFill(pnSrc.saIOUIssue, pnSrc, pnDst, bAllowPartial)
|
||||
// Issue to wanted.
|
||||
terResult = calcOfferFill(
|
||||
pnDst.saWanted, // As much as wanted is available, limited by credit limit.
|
||||
pnSrc.saIOUIssue,
|
||||
pnDst.saIOUAccept,
|
||||
bAllowPartial);
|
||||
}
|
||||
|
||||
if (terSUCCESS == terResult && !bAllowPartial)
|
||||
{
|
||||
STAmount saTotal = pnSrc.saIOURedeem;
|
||||
saTotal += pnSrc.saIOUIssue;
|
||||
STAmount saTotal = pnDst.saIOUForgive + pnSrc.saIOUAccept;
|
||||
|
||||
if (saTotal != saWanted)
|
||||
terResult = terINSUF_PATH;
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
// Get the next offer limited by funding.
|
||||
// - Stop when becomes unfunded.
|
||||
void TransactionEngine::calcOfferBridgeNext(
|
||||
const uint256& uBookRoot, // --> Which order book to look in.
|
||||
const uint256& uBookEnd, // --> Limit of how far to look.
|
||||
uint256& uBookDirIndex, // <-> Current directory. <-- 0 = no offer available.
|
||||
uint64& uBookDirNode, // <-> Which node. 0 = first.
|
||||
unsigned int& uBookDirEntry, // <-> Entry in node. 0 = first.
|
||||
STAmount& saOfferIn, // <-- How much to pay in, fee inclusive, to get saOfferOut out.
|
||||
STAmount& saOfferOut // <-- How much offer pays out.
|
||||
)
|
||||
{
|
||||
saOfferIn = 0; // XXX currency & issuer
|
||||
saOfferOut = 0; // XXX currency & issuer
|
||||
|
||||
bool bDone = false;
|
||||
|
||||
while (!bDone)
|
||||
{
|
||||
uint256 uOfferIndex;
|
||||
|
||||
// Get uOfferIndex.
|
||||
dirNext(uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex);
|
||||
|
||||
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
|
||||
|
||||
uint160 uOfferOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID();
|
||||
STAmount saOfferPays = sleOffer->getIValueFieldAmount(sfTakerGets);
|
||||
STAmount saOfferGets = sleOffer->getIValueFieldAmount(sfTakerPays);
|
||||
|
||||
if (sleOffer->getIFieldPresent(sfGetsIssuer))
|
||||
saOfferPays.setIssuer(sleOffer->getIValueFieldAccount(sfGetsIssuer).getAccountID());
|
||||
|
||||
if (sleOffer->getIFieldPresent(sfPaysIssuer))
|
||||
saOfferGets.setIssuer(sleOffer->getIValueFieldAccount(sfPaysIssuer).getAccountID());
|
||||
|
||||
if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC())
|
||||
{
|
||||
// Offer is expired.
|
||||
Log(lsINFO) << "calcOfferFirst: encountered expired offer";
|
||||
}
|
||||
else
|
||||
{
|
||||
STAmount saOfferFunds = accountFunds(uOfferOwnerID, saOfferPays);
|
||||
// Outbound fees are paid by offer owner.
|
||||
// XXX Calculate outbound fee rate.
|
||||
|
||||
if (saOfferPays.isNative())
|
||||
{
|
||||
// No additional fees for stamps.
|
||||
|
||||
nothing();
|
||||
}
|
||||
else if (saOfferPays.getIssuer() == uOfferOwnerID)
|
||||
{
|
||||
// Offerer is issue own IOUs.
|
||||
// No fees at this exact point, XXX receiving node may charge a fee.
|
||||
// XXX Make sure has a credit line with receiver, limit by credit line.
|
||||
|
||||
nothing();
|
||||
// XXX Broken - could be issuing or redeeming or both.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Offer must be redeeming IOUs.
|
||||
|
||||
// No additional
|
||||
// XXX Broken
|
||||
}
|
||||
|
||||
if (!saOfferFunds.isPositive())
|
||||
{
|
||||
// Offer is unfunded.
|
||||
Log(lsINFO) << "calcOfferFirst: offer unfunded: delete";
|
||||
}
|
||||
else if (saOfferFunds >= saOfferPays)
|
||||
{
|
||||
// Offer fully funded.
|
||||
|
||||
// Account transfering funds in to offer always pays inbound fees.
|
||||
//
|
||||
saOfferIn = saOfferGets; // XXX Add in fees?
|
||||
|
||||
saOfferOut = saOfferPays;
|
||||
|
||||
bDone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Offer partially funded.
|
||||
|
||||
// saOfferIn/saOfferFunds = saOfferGets/saOfferPays
|
||||
// XXX Round such that all saOffer funds are exhausted.
|
||||
saOfferIn = (saOfferFunds*saOfferGets)/saOfferPays; // XXX Add in fees?
|
||||
saOfferOut = saOfferFunds;
|
||||
|
||||
bDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bDone)
|
||||
{
|
||||
// mUnfunded.insert(uOfferIndex);
|
||||
}
|
||||
}
|
||||
while (bNext);
|
||||
}
|
||||
|
||||
// If either currency is not stamps, then also calculates vs stamp bridge.
|
||||
// --> saWanted: Limit of how much is wanted out.
|
||||
// <-- saPay: How much to pay into the offer.
|
||||
// <-- saGot: How much to the offer pays out. Never more than saWanted.
|
||||
void TransactionEngine::calcNodeOfferReverse(
|
||||
const uint160& uPayCurrency,
|
||||
const uint160& uPayIssuerID,
|
||||
const STAmount& saWanted, // Driver
|
||||
|
||||
STAmount& saPay,
|
||||
STAmount& saGot
|
||||
) const
|
||||
{
|
||||
TransactionEngineResult terResult = tenUNKNOWN;
|
||||
|
||||
bool bDirectNext = true; // True, if need to load.
|
||||
uint256 uDirectQuality;
|
||||
uint256 uDirectTip = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID);
|
||||
uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip);
|
||||
|
||||
bool bBridge = true; // True, if bridging active. False, missing an offer.
|
||||
uint256 uBridgeQuality;
|
||||
STAmount saBridgeIn; // Amount available.
|
||||
STAmount saBridgeOut;
|
||||
|
||||
bool bInNext = true; // True, if need to load.
|
||||
STAmount saInIn; // Amount available. Consumed in loop. Limited by offer funding.
|
||||
STAmount saInOut;
|
||||
uint256 uInTip; // Current entry.
|
||||
uint256 uInEnd;
|
||||
unsigned int uInEntry;
|
||||
|
||||
bool bOutNext = true;
|
||||
STAmount saOutIn;
|
||||
STAmount saOutOut;
|
||||
uint256 uOutTip;
|
||||
uint256 uOutEnd;
|
||||
unsigned int uOutEntry;
|
||||
|
||||
saPay.zero();
|
||||
saPay.setCurrency(uPayCurrency);
|
||||
saPay.setIssuer(uPayIssuerID);
|
||||
|
||||
saNeed = saWanted;
|
||||
|
||||
if (!saWanted.isNative() && !uTakerCurrency.isZero())
|
||||
{
|
||||
// Bridging
|
||||
uInTip = Ledger::getBookBase(uPayCurrency, uPayIssuerID, uint160(0), uint160(0));
|
||||
uInEnd = Ledger::getQualityNext(uInTip);
|
||||
uOutTip = Ledger::getBookBase(uint160(0), uint160(0), saWanted.getCurrency(), saWanted.getIssuer());
|
||||
uOutEnd = Ledger::getQualityNext(uInTip);
|
||||
}
|
||||
|
||||
while (tenUNKNOWN == terResult)
|
||||
{
|
||||
if (saNeed == saWanted)
|
||||
{
|
||||
// Got all.
|
||||
saGot = saWanted;
|
||||
terResult = terSUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculate next tips, if needed.
|
||||
|
||||
if (bDirectNext)
|
||||
{
|
||||
// Find next direct offer.
|
||||
uDirectTip = mLedger->getNextLedgerIndex(uDirectTip, uDirectEnd);
|
||||
if (!!uDirectTip)
|
||||
{
|
||||
sleDirectDir = entryCache(ltDIR_NODE, uDirectTip);
|
||||
|
||||
// XXX Need to calculate the real quality: including fees.
|
||||
uDirectQuality = STAmount::getQualityNext(uDirectTip);
|
||||
}
|
||||
|
||||
bDirectNext = false;
|
||||
}
|
||||
|
||||
if (bBridge && (bInNext || bOutNext))
|
||||
{
|
||||
// Bridging and need to calculate next bridge rate.
|
||||
// A bridge can consist of multiple offers. As offer's are consumed, the effective rate changes.
|
||||
|
||||
if (bInNext)
|
||||
{
|
||||
// sleInDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uInIndex, uInEnd));
|
||||
// Get the next funded offer.
|
||||
offerBridgeNext(uInIndex, uInEnd, uInEntry, saInIn, saInOut); // Get offer limited by funding.
|
||||
bInNext = false;
|
||||
}
|
||||
|
||||
if (bOutNext)
|
||||
{
|
||||
// sleOutDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uOutIndex, uOutEnd));
|
||||
offerNext(uOutIndex, uOutEnd, uOutEntry, saOutIn, saOutOut);
|
||||
bOutNext = false;
|
||||
}
|
||||
|
||||
if (!uInIndex || !uOutIndex)
|
||||
{
|
||||
bBridge = false; // No more offers to bridge.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Have bridge in and out entries.
|
||||
// Calculate bridge rate. Out offer pay ripple fee. In offer fee is added to in cost.
|
||||
|
||||
saBridgeOut.zero();
|
||||
|
||||
if (saInOut < saOutIn)
|
||||
{
|
||||
// Limit by in.
|
||||
|
||||
// XXX Need to include fees in saBridgeIn.
|
||||
saBridgeIn = saInIn; // All of in
|
||||
// Limit bridge out: saInOut/saBridgeOut = saOutIn/saOutOut
|
||||
// Round such that we would take all of in offer, otherwise would have leftovers.
|
||||
saBridgeOut = (saInOut * saOutOut) / saOutIn;
|
||||
}
|
||||
else if (saInOut > saOutIn)
|
||||
{
|
||||
// Limit by out, if at all.
|
||||
|
||||
// XXX Need to include fees in saBridgeIn.
|
||||
// Limit bridge in:saInIn/saInOuts = aBridgeIn/saOutIn
|
||||
// Round such that would take all of out offer.
|
||||
saBridgeIn = (saInIn * saOutIn) / saInOuts;
|
||||
saBridgeOut = saOutOut; // All of out.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Entries match,
|
||||
|
||||
// XXX Need to include fees in saBridgeIn.
|
||||
saBridgeIn = saInIn; // All of in
|
||||
saBridgeOut = saOutOut; // All of out.
|
||||
}
|
||||
|
||||
uBridgeQuality = STAmount::getRate(saBridgeIn, saBridgeOut); // Inclusive of fees.
|
||||
}
|
||||
}
|
||||
|
||||
if (bBridge)
|
||||
{
|
||||
bUseBridge = !uDirectTip || (uBridgeQuality < uDirectQuality)
|
||||
}
|
||||
else if (!!uDirectTip)
|
||||
{
|
||||
bUseBridge = false
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more offers. Declare success, even if none returned.
|
||||
saGot = saWanted-saNeed;
|
||||
terResult = terSUCCESS;
|
||||
}
|
||||
|
||||
if (terSUCCESS != terResult)
|
||||
{
|
||||
STAmount& saAvailIn = bUseBridge ? saBridgeIn : saDirectIn;
|
||||
STAmount& saAvailOut = bUseBridge ? saBridgeOut : saDirectOut;
|
||||
|
||||
if (saAvailOut > saNeed)
|
||||
{
|
||||
// Consume part of offer. Done.
|
||||
|
||||
saNeed = 0;
|
||||
saPay += (saNeed*saAvailIn)/saAvailOut; // Round up, prefer to pay more.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume entire offer.
|
||||
|
||||
saNeed -= saAvailOut;
|
||||
saPay += saAvailIn;
|
||||
|
||||
if (bUseBridge)
|
||||
{
|
||||
// Consume bridge out.
|
||||
if (saOutOut == saAvailOut)
|
||||
{
|
||||
// Consume all.
|
||||
saOutOut = 0;
|
||||
saOutIn = 0;
|
||||
bOutNext = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume portion of bridge out, must be consuming all of bridge in.
|
||||
// saOutIn/saOutOut = saSpent/saAvailOut
|
||||
// Round?
|
||||
saOutIn -= (saOutIn*saAvailOut)/saOutOut;
|
||||
saOutOut -= saAvailOut;
|
||||
}
|
||||
|
||||
// Consume bridge in.
|
||||
if (saOutIn == saAvailIn)
|
||||
{
|
||||
// Consume all.
|
||||
saInOut = 0;
|
||||
saInIn = 0;
|
||||
bInNext = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume portion of bridge in, must be consuming all of bridge out.
|
||||
// saInIn/saInOut = saAvailIn/saPay
|
||||
// Round?
|
||||
saInOut -= (saInOut*saAvailIn)/saInIn;
|
||||
saInIn -= saAvailIn;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bDirectNext = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From the destination work towards the source calculating how much must be asked for.
|
||||
// --> bAllowPartial: If false, fail if can't meet requirements.
|
||||
// <-- bSuccess: true=success, false=insufficient funds.
|
||||
@@ -1570,63 +1991,112 @@ TransactionEngineResult calcOfferFill(paymentNode& pnSrc, paymentNode& pnDst, bo
|
||||
// --> [all]saWanted.mCurrency
|
||||
// --> [all]saAccount
|
||||
// <-> [0]saWanted.mAmount : --> limit, <-- actual
|
||||
// XXX Disallow looping.
|
||||
// XXX With multiple path and due to offers, must consider consumed.
|
||||
bool calcPaymentReverse(std::vector<paymentNode>& pnNodes, bool bAllowPartial)
|
||||
{
|
||||
bool bDone = false;
|
||||
bool bSuccess = false;
|
||||
TransactionEngineResult terResult = tenUNKNOWN;
|
||||
|
||||
// path: dst .. src
|
||||
uIndex = pnNodes.size();
|
||||
|
||||
while (!bDone)
|
||||
while (tenUNKNOWN == terResult && uIndex--)
|
||||
{
|
||||
if (cur->saWanted.isZero())
|
||||
// Calculate (1) sending by fullfilling next wants and (2) setting current wants.
|
||||
|
||||
paymentNode& curPN = pnNodes[uIndex];
|
||||
paymentNode& prvPN = pnNodes[uIndex-1];
|
||||
paymentNode& nxtPN = pnNodes[uIndex+1];
|
||||
|
||||
if (!(uFlags & (PF_REDEEM|PF_ISSUE)))
|
||||
{
|
||||
// Redeem IOUs
|
||||
terResult = tenBAD_PATH;
|
||||
}
|
||||
else if (curPN.saWanted.isZero())
|
||||
{
|
||||
// Must want something.
|
||||
terResult = terINVALID;
|
||||
bDone = true;
|
||||
terResult = tenBAD_AMOUNT;
|
||||
}
|
||||
else if (cur->saWanted.isNative())
|
||||
else if (curPN->uFlags & PF_ACCOUNT)
|
||||
{
|
||||
if (prv->how == direct)
|
||||
// Account node.
|
||||
// Rippling through this accounts balances.
|
||||
// No currency change.
|
||||
// Issuer change possible.
|
||||
|
||||
SLE::pointer sleRippleCur = ;
|
||||
SLE::pointer sleRippleNxt = ;
|
||||
STAmount saBalanceCur = ;
|
||||
|
||||
if ((uFlags & PF_REDEEM) && saBalanceCur.isPositive())
|
||||
{
|
||||
// Stamp transfer desired.
|
||||
if (prv->prev())
|
||||
{
|
||||
// Stamp transfer can not have previous entries. Only stamp ripple can.
|
||||
terResult = terINVALID;
|
||||
bDone = true;
|
||||
// Redeem IOUs
|
||||
|
||||
// XXX
|
||||
curPN.saWanted += ___;
|
||||
|
||||
bSent = true;
|
||||
}
|
||||
else if (prv->account->saBalance() >= cur->saWanted)
|
||||
|
||||
if ((uFlags & PF_ISSUE) // Allowed to issue.
|
||||
&& !saWantedNxt.isZero() // Need to issue.
|
||||
&& !saBalanceCur.isPositive()) // Can issue.
|
||||
{
|
||||
// Transfer stamps.
|
||||
prv->saSend = cur->saWanted;
|
||||
bDone = true;
|
||||
bSuccess = true;
|
||||
// Issue IOUs
|
||||
|
||||
// XXX
|
||||
curPN.saWanted += ___;
|
||||
bSent = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Insufficient funds for transfer
|
||||
bDone = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Must convert to stamps via offer.
|
||||
if (calcOfferFill(prv, cur, bAllowPartial))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
else if (curPN->uFlags & PF_OFFER)
|
||||
{
|
||||
bDone = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rippling.
|
||||
// Offer node.
|
||||
// Ripple or transfering from previous node through this offer to next node.
|
||||
// Current node has a credit line with next node.
|
||||
// Next node will receive either its own IOUs or this nodes IOUs.
|
||||
// We limit what this node sends by this nodes redeem and issue max.
|
||||
// This allows path lists to be strictly redeem.
|
||||
// XXX Make sure offer book was not previously mentioned.
|
||||
|
||||
uint160 uPrvCurrency = curPN->uFlags & PF_WANTED_CURRENCY
|
||||
? curPN->saWanted.getCurrency()
|
||||
: saSendMax.getCurrency();
|
||||
|
||||
uint160 uPrvIssuer = curPN->uFlags & PF_WANTED_ISSUER
|
||||
? curPN->saWanted.getIssuer()
|
||||
: saSendMax.getIssuer();
|
||||
|
||||
calcNodeOfferReverse(
|
||||
uTakerCurrency,
|
||||
uTakerIssuer,
|
||||
nxtPN->saWanted, // Driver.
|
||||
|
||||
uTakerPaid,
|
||||
uTakerGot,
|
||||
uOwnerPaid,
|
||||
uOwnerGot,
|
||||
);
|
||||
|
||||
if (uOwnerPaid.isZero())
|
||||
{
|
||||
terResult = terZERO; // Path contributes nothing.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update wanted.
|
||||
|
||||
// Save sent amount
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
|
||||
if (tenUNKNOWN == terResult == curPN.saWanted.isZero())
|
||||
terResult = terZERO; // Path contributes nothing.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1850,6 +2320,8 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction
|
||||
return tenRIPPLE_EMPTY;
|
||||
}
|
||||
#if 0
|
||||
// 1) Calc payment in reverse: do not modify sles.
|
||||
// 2) Calc payment forward: do modify sles.
|
||||
std::vector<STPath> spPath;
|
||||
|
||||
BOOST_FOREACH(std::vector<STPath>& spPath, spsPaths)
|
||||
@@ -2023,10 +2495,11 @@ TransactionEngineResult TransactionEngine::takeOffers(
|
||||
// Have an offer to consider.
|
||||
Log(lsINFO) << "takeOffers: considering dir : " << sleOfferDir->getJson(0);
|
||||
|
||||
SLE::pointer sleBookNode;
|
||||
unsigned int uBookEntry;
|
||||
uint256 uOfferIndex;
|
||||
uint64 uOfferNode;
|
||||
|
||||
dirFirst(uTipIndex, uOfferIndex, uOfferNode);
|
||||
dirFirst(uTipIndex, sleBookNode, uBookEntry, uOfferIndex);
|
||||
|
||||
SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex);
|
||||
|
||||
|
||||
@@ -123,21 +123,28 @@ private:
|
||||
const uint256& uRootIndex,
|
||||
const uint256& uLedgerIndex); // Item being deleted
|
||||
|
||||
void dirFirst(const uint256& uRootIndex, uint256& uEntryIndex, uint64& uEntryNode);
|
||||
bool dirFirst(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
bool dirNext(const uint256& uRootIndex, SLE::pointer& sleNode, unsigned int& uDirEntry, uint256& uEntryIndex);
|
||||
|
||||
#ifdef WORK_IN_PROGRESS
|
||||
typedef struct {
|
||||
STAmount saWanted; // What this node wants from upstream.
|
||||
uint16 uFlags; // --> from path
|
||||
|
||||
STAmount saIOURedeem; // What this node will redeem downstream.
|
||||
STAmount saIOUIssue; // What this node will issue downstream.
|
||||
STAmount saSend; // Amount of stamps this node will send.
|
||||
STAccount saAccount; // --> recieving/sending account
|
||||
|
||||
STAmount saWanted; // --> What this node wants from upstream.
|
||||
|
||||
// Maybe this should just be a bool:
|
||||
STAmount saIOURedeemMax; // --> Max amount of IOUs to redeem downstream.
|
||||
// Maybe this should just be a bool:
|
||||
STAmount saIOUIssueMax; // --> Max Amount of IOUs to issue downstream.
|
||||
|
||||
STAmount saIOURedeem; // <-- What this node will redeem downstream.
|
||||
STAmount saIOUIssue; // <-- What this node will issue downstream.
|
||||
STAmount saSend; // <-- Stamps this node will send downstream.
|
||||
|
||||
STAmount saIOUForgive; // Amount of IOUs to forgive.
|
||||
STAmount saIOUAccept; // Amount of IOUs to accept.
|
||||
STAmount saRecieve; // Amount stamps to receive.
|
||||
|
||||
STAccount saAccount;
|
||||
} paymentNode;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -27,11 +27,10 @@ TransactionFormat InnerTxnFormats[]=
|
||||
{ "CreditSet", ttCREDIT_SET, {
|
||||
{ S_FIELD(Flags), STI_UINT32, SOE_FLAGS, 0 },
|
||||
{ S_FIELD(Destination), STI_ACCOUNT, SOE_REQUIRED, 0 },
|
||||
{ S_FIELD(LimitAmount), STI_AMOUNT, SOE_REQUIRED, 0 },
|
||||
{ S_FIELD(AcceptRate), STI_UINT32, SOE_IFFLAG, 1 },
|
||||
{ S_FIELD(AcceptStart), STI_UINT32, SOE_IFFLAG, 2 },
|
||||
{ S_FIELD(AcceptExpire), STI_UINT32, SOE_IFFLAG, 4 },
|
||||
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 8 },
|
||||
{ S_FIELD(SourceTag), STI_UINT32, SOE_IFFLAG, 1 },
|
||||
{ S_FIELD(LimitAmount), STI_AMOUNT, SOE_IFFLAG, 2 },
|
||||
{ S_FIELD(QualityIn), STI_UINT32, SOE_IFFLAG, 4 },
|
||||
{ S_FIELD(QualityOut), STI_UINT32, SOE_IFFLAG, 8 },
|
||||
{ S_FIELD(Extensions), STI_TL, SOE_IFFLAG, 0x02000000 },
|
||||
{ sfInvalid, NULL, STI_DONE, SOE_NEVER, -1 } }
|
||||
},
|
||||
|
||||
@@ -56,7 +56,7 @@ void printHelp(const po::options_description& desc)
|
||||
cout << " password_set <master_seed> <regular_seed> [<account>]" << endl;
|
||||
cout << " peers" << endl;
|
||||
cout << " ripple_lines_get <account>|<nickname>|<account_public_key> [<index>]" << endl;
|
||||
cout << " ripple_line_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<account_rate>]" << endl;
|
||||
cout << " ripple_line_set <seed> <paying_account> <destination_account> <limit_amount> <currency> [<quality_in>] [<quality_out>]" << endl;
|
||||
cout << " send <seed> <paying_account> <account_id> <amount> [<currency>] [<send_max>] [<send_currency>]" << endl;
|
||||
cout << " stop" << endl;
|
||||
cout << " tx <id>" << endl;
|
||||
|
||||
Reference in New Issue
Block a user