mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
Fixes for rippling XRP via offers.
This commit is contained in:
@@ -471,7 +471,7 @@ TER RippleCalc::calcNodeDeliverRev(
|
|||||||
return terResult;
|
return terResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For current offer, get input from deliver/limbo and put output to deliver/limbo using offers.
|
// For current offer, get input from deliver/limbo and output to next account or deliver for next offers.
|
||||||
TER RippleCalc::calcNodeDeliverFwd(
|
TER RippleCalc::calcNodeDeliverFwd(
|
||||||
const unsigned int uNode, // 0 < uNode < uLast
|
const unsigned int uNode, // 0 < uNode < uLast
|
||||||
PathState::ref pspCur,
|
PathState::ref pspCur,
|
||||||
@@ -488,8 +488,8 @@ TER RippleCalc::calcNodeDeliverFwd(
|
|||||||
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1];
|
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1];
|
||||||
|
|
||||||
const uint160& uNxtAccountID = pnNxt.uAccountID;
|
const uint160& uNxtAccountID = pnNxt.uAccountID;
|
||||||
const uint160& uCurIssuerID = pnCur.uIssuerID;
|
|
||||||
const uint160& uCurCurrencyID = pnCur.uCurrencyID;
|
const uint160& uCurCurrencyID = pnCur.uCurrencyID;
|
||||||
|
const uint160& uCurIssuerID = pnCur.uIssuerID;
|
||||||
const uint160& uPrvCurrencyID = pnPrv.uCurrencyID;
|
const uint160& uPrvCurrencyID = pnPrv.uCurrencyID;
|
||||||
const uint160& uPrvIssuerID = pnPrv.uIssuerID;
|
const uint160& uPrvIssuerID = pnPrv.uIssuerID;
|
||||||
const STAmount& saTransferRate = pnPrv.saTransferRate;
|
const STAmount& saTransferRate = pnPrv.saTransferRate;
|
||||||
@@ -502,6 +502,7 @@ TER RippleCalc::calcNodeDeliverFwd(
|
|||||||
|
|
||||||
saInAct.zero(saInReq);
|
saInAct.zero(saInReq);
|
||||||
saInFees.zero(saInReq);
|
saInFees.zero(saInReq);
|
||||||
|
saCurDeliverAct.zero(uCurCurrencyID, uCurIssuerID);
|
||||||
|
|
||||||
while (tesSUCCESS == terResult
|
while (tesSUCCESS == terResult
|
||||||
&& saInAct + saInFees != saInReq) // Did not deliver all funds.
|
&& saInAct + saInFees != saInReq) // Did not deliver all funds.
|
||||||
@@ -553,13 +554,13 @@ TER RippleCalc::calcNodeDeliverFwd(
|
|||||||
{
|
{
|
||||||
// ? --> OFFER --> account
|
// ? --> OFFER --> account
|
||||||
// Input fees: vary based upon the consumed offer's owner.
|
// Input fees: vary based upon the consumed offer's owner.
|
||||||
// Output fees: none as the destination account is the issuer.
|
// Output fees: none as XRP or the destination account is the issuer.
|
||||||
|
|
||||||
// Debit offer owner, send to issuer which must be next account.
|
|
||||||
lesActive.accountSend(uOfrOwnerID, uCurIssuerID, saOutPassMax);
|
|
||||||
|
|
||||||
saOutPassAct = saOutPassMax;
|
saOutPassAct = saOutPassMax;
|
||||||
|
|
||||||
|
// Debit offer owner, send XRP or non-XPR to next account.
|
||||||
|
lesActive.accountSend(uOfrOwnerID, uNxtAccountID, saOutPassAct);
|
||||||
|
|
||||||
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> account: saOutPassAct=%s")
|
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> account: saOutPassAct=%s")
|
||||||
% saOutPassAct);
|
% saOutPassAct);
|
||||||
}
|
}
|
||||||
@@ -585,6 +586,13 @@ TER RippleCalc::calcNodeDeliverFwd(
|
|||||||
// Offer maximum in split into fees by next payout.
|
// Offer maximum in split into fees by next payout.
|
||||||
saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate);
|
saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate);
|
||||||
saInPassFees = STAmount::multiply(saInFunded, saInFeeRate)-saInPassAct;
|
saInPassFees = STAmount::multiply(saInFunded, saInFeeRate)-saInPassAct;
|
||||||
|
|
||||||
|
// Do outbound debiting.
|
||||||
|
// Send to issuer/limbo total amount (no fees to issuer).
|
||||||
|
lesActive.accountSend(uOfrOwnerID, !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP, saOutPassAct);
|
||||||
|
|
||||||
|
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: ? --> OFFER --> offer: saOutPassAct=%s")
|
||||||
|
% saOutPassAct);
|
||||||
}
|
}
|
||||||
|
|
||||||
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saTakerGets=%s saTakerPays=%s saInPassAct=%s saOutPassAct=%s")
|
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saTakerGets=%s saTakerPays=%s saInPassAct=%s saOutPassAct=%s")
|
||||||
@@ -600,10 +608,6 @@ TER RippleCalc::calcNodeDeliverFwd(
|
|||||||
// Credit offer owner from in issuer/limbo (don't take transfer fees).
|
// Credit offer owner from in issuer/limbo (don't take transfer fees).
|
||||||
lesActive.accountSend(!!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP, uOfrOwnerID, saInPassAct);
|
lesActive.accountSend(!!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP, uOfrOwnerID, saInPassAct);
|
||||||
|
|
||||||
// Do outbound debiting.
|
|
||||||
// Send to issuer/limbo total amount (no fees to issuer).
|
|
||||||
lesActive.accountSend(uOfrOwnerID, !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP, saOutPassAct);
|
|
||||||
|
|
||||||
// Adjust offer
|
// Adjust offer
|
||||||
// Fees are considered paid from a seperate budget and are not named in the offer.
|
// Fees are considered paid from a seperate budget and are not named in the offer.
|
||||||
sleOffer->setFieldAmount(sfTakerGets, saTakerGets - saOutPassAct);
|
sleOffer->setFieldAmount(sfTakerGets, saTakerGets - saOutPassAct);
|
||||||
@@ -845,6 +849,7 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
|||||||
|
|
||||||
const uint160& uCurrencyID = pnCur.uCurrencyID;
|
const uint160& uCurrencyID = pnCur.uCurrencyID;
|
||||||
|
|
||||||
|
// XXX Don't look up quality for XRP
|
||||||
const uint32 uQualityIn = uNode ? lesActive.rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
|
const uint32 uQualityIn = uNode ? lesActive.rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
|
||||||
const uint32 uQualityOut = uNode != uLast ? lesActive.rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE;
|
const uint32 uQualityOut = uNode != uLast ? lesActive.rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE;
|
||||||
|
|
||||||
@@ -910,15 +915,16 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
|||||||
|| !saNxtOwed.isNegative() // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: Sender holding next IOUs.
|
|| !saNxtOwed.isNegative() // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: Sender holding next IOUs.
|
||||||
|| -saNxtOwed == saCurRedeemReq); // If issue req, then redeem req must consume all owed.
|
|| -saNxtOwed == saCurRedeemReq); // If issue req, then redeem req must consume all owed.
|
||||||
|
|
||||||
if (bPrvAccount && bNxtAccount)
|
if (!uNode)
|
||||||
{
|
{
|
||||||
if (!uNode)
|
// ^ --> ACCOUNT --> account|offer
|
||||||
{
|
// Nothing to do, there is no previous to adjust.
|
||||||
// ^ --> ACCOUNT --> account|offer
|
|
||||||
// Nothing to do, there is no previous to adjust.
|
nothing();
|
||||||
nothing();
|
}
|
||||||
}
|
else if (bPrvAccount && bNxtAccount)
|
||||||
else if (uNode == uLast)
|
{
|
||||||
|
if (uNode == uLast)
|
||||||
{
|
{
|
||||||
// account --> ACCOUNT --> $
|
// account --> ACCOUNT --> $
|
||||||
// Overall deliverable.
|
// Overall deliverable.
|
||||||
@@ -1190,6 +1196,8 @@ TER RippleCalc::calcNodeAccountFwd(
|
|||||||
const uint160& uPrvAccountID = bPrvAccount ? pnPrv.uAccountID : uCurAccountID;
|
const uint160& uPrvAccountID = bPrvAccount ? pnPrv.uAccountID : uCurAccountID;
|
||||||
const uint160& uNxtAccountID = bNxtAccount ? pnNxt.uAccountID : uCurAccountID; // Offers are always issue.
|
const uint160& uNxtAccountID = bNxtAccount ? pnNxt.uAccountID : uCurAccountID; // Offers are always issue.
|
||||||
|
|
||||||
|
const uint160& uCurIssuerID = pnCur.uIssuerID;
|
||||||
|
|
||||||
const uint160& uCurrencyID = pnCur.uCurrencyID;
|
const uint160& uCurrencyID = pnCur.uCurrencyID;
|
||||||
|
|
||||||
uint32 uQualityIn = uNode ? lesActive.rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
|
uint32 uQualityIn = uNode ? lesActive.rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
|
||||||
@@ -1220,6 +1228,9 @@ TER RippleCalc::calcNodeAccountFwd(
|
|||||||
const STAmount& saCurDeliverReq = pnCur.saRevDeliver;
|
const STAmount& saCurDeliverReq = pnCur.saRevDeliver;
|
||||||
STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
|
STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
|
||||||
|
|
||||||
|
// For !uNode
|
||||||
|
STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends.
|
||||||
|
|
||||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uNode=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s")
|
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uNode=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s")
|
||||||
% uNode
|
% uNode
|
||||||
% uLast
|
% uLast
|
||||||
@@ -1246,7 +1257,6 @@ TER RippleCalc::calcNodeAccountFwd(
|
|||||||
const STAmount saCurSendMaxReq = pspCur->saInReq.isNegative()
|
const STAmount saCurSendMaxReq = pspCur->saInReq.isNegative()
|
||||||
? pspCur->saInReq // Negative for no limit, doing a calculation.
|
? pspCur->saInReq // Negative for no limit, doing a calculation.
|
||||||
: pspCur->saInReq-pspCur->saInAct; // request - done.
|
: pspCur->saInReq-pspCur->saInAct; // request - done.
|
||||||
STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends.
|
|
||||||
|
|
||||||
saCurRedeemAct = saCurRedeemReq
|
saCurRedeemAct = saCurRedeemReq
|
||||||
// Redeem requested.
|
// Redeem requested.
|
||||||
@@ -1347,31 +1357,71 @@ TER RippleCalc::calcNodeAccountFwd(
|
|||||||
}
|
}
|
||||||
else if (bPrvAccount && !bNxtAccount)
|
else if (bPrvAccount && !bNxtAccount)
|
||||||
{
|
{
|
||||||
// account --> ACCOUNT --> offer
|
if (uNode)
|
||||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> offer"));
|
|
||||||
|
|
||||||
saCurDeliverAct.zero(saCurDeliverReq);
|
|
||||||
|
|
||||||
// redeem -> issue.
|
|
||||||
// wants to redeem and current would and can issue.
|
|
||||||
// If redeeming cur to next is done, this implies can issue.
|
|
||||||
if (saPrvRedeemReq) // Previous wants to redeem.
|
|
||||||
{
|
{
|
||||||
// Rate : 1.0 : transfer_rate
|
// Non-XRP, current node is the issuer.
|
||||||
calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax);
|
cLog(lsDEBUG) << boost::str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> offer"));
|
||||||
}
|
|
||||||
|
|
||||||
// issue -> issue
|
saCurDeliverAct.zero(saCurDeliverReq);
|
||||||
if (saPrvRedeemReq == saPrvRedeemAct // Previous done redeeming: Previous has no IOUs.
|
|
||||||
&& saPrvIssueReq) // Previous wants to issue. To next must be ok.
|
// redeem -> issue/deliver.
|
||||||
|
// Previous wants to redeem.
|
||||||
|
// Current is issuing to an offer so leave funds in account as "limbo".
|
||||||
|
if (saPrvRedeemReq) // Previous wants to redeem.
|
||||||
|
{
|
||||||
|
// Rate : 1.0 : transfer_rate
|
||||||
|
// XXX Is having the transfer rate here correct?
|
||||||
|
calcNodeRipple(QUALITY_ONE, lesActive.rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// issue -> issue/deliver
|
||||||
|
if (saPrvRedeemReq == saPrvRedeemAct // Previous done redeeming: Previous has no IOUs.
|
||||||
|
&& saPrvIssueReq) // Previous wants to issue. To next must be ok.
|
||||||
|
{
|
||||||
|
// Rate: quality in : 1.0
|
||||||
|
calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust prv --> cur balance : take all inbound
|
||||||
|
lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Rate: quality in : 1.0
|
// Delivering amount requested from downstream.
|
||||||
calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax);
|
saCurDeliverAct = saCurDeliverReq;
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust prv --> cur balance : take all inbound
|
// If limited, then limit by send max and available.
|
||||||
// XXX Currency must be in amount.
|
if (!pspCur->saInReq.isNegative())
|
||||||
lesActive.rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false);
|
{
|
||||||
|
saCurDeliverAct = pspCur->saInReq-pspCur->saInAct;
|
||||||
|
|
||||||
|
// Limit XRP by available. No limit for non-XRP as issuer.
|
||||||
|
if (!uCurAccountID)
|
||||||
|
saCurDeliverAct = std::min(saCurDeliverAct, lesActive.accountHolds(uCurAccountID, CURRENCY_XRP, ACCOUNT_XRP));
|
||||||
|
|
||||||
|
}
|
||||||
|
saCurSendMaxPass = saCurDeliverAct; // Record amount sent for pass.
|
||||||
|
|
||||||
|
if (!!uCurrencyID)
|
||||||
|
{
|
||||||
|
// Non-XRP, current node is the issuer.
|
||||||
|
// We could be delivering to multiple accounts, so we don't know which ripple balance will be adjusted. Assume
|
||||||
|
// just issuing.
|
||||||
|
|
||||||
|
cLog(lsDEBUG) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer"));
|
||||||
|
|
||||||
|
// As the issuer, would only issue.
|
||||||
|
// Don't need to actually deliver. As from delivering leave in the issuer as limbo.
|
||||||
|
nothing();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cLog(lsDEBUG) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer"));
|
||||||
|
|
||||||
|
// Deliver XRP to limbo.
|
||||||
|
lesActive.accountSend(uCurAccountID, ACCOUNT_XRP, saCurDeliverAct);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!bPrvAccount && bNxtAccount)
|
else if (!bPrvAccount && bNxtAccount)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -534,5 +534,86 @@ buster.testCase("Offer tests", {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"//ripple cross currency payment" :
|
||||||
|
// 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", ["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" : [ "0/USD/mtgox", "500" ],
|
||||||
|
"bob" : "100/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();
|
||||||
|
});
|
||||||
|
},
|
||||||
});
|
});
|
||||||
// vim:sw=2:sts=2:ts=8
|
// vim:sw=2:sts=2:ts=8
|
||||||
|
|||||||
Reference in New Issue
Block a user