mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-28 06:55:50 +00:00
Merge branch 'master' of github.com:jedmccaleb/NewCoin
This commit is contained in:
@@ -82,6 +82,7 @@ void sigIntHandler(int)
|
||||
|
||||
void Application::run()
|
||||
{
|
||||
#ifndef WIN32
|
||||
#ifdef SIGINT
|
||||
if (!config.RUN_STANDALONE)
|
||||
{
|
||||
@@ -90,6 +91,7 @@ void Application::run()
|
||||
sa.sa_handler = sigIntHandler;
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
assert(mTxnDB == NULL);
|
||||
|
||||
@@ -13,7 +13,7 @@ const char *TxnDBInit[] = {
|
||||
FromSeq BIGINT UNSIGNED, \
|
||||
LedgerSeq BIGINT UNSIGNED, \
|
||||
Status CHARACTER(1), \
|
||||
RawTxn BLOB \
|
||||
RawTxn BLOB, \
|
||||
TxnMeta BLOB \
|
||||
);",
|
||||
"CREATE TABLE PubKeys ( \
|
||||
|
||||
@@ -903,15 +903,15 @@ std::vector< std::pair<Transaction::pointer, TransactionMetaSet::pointer> >
|
||||
Serializer rawMeta;
|
||||
int metaSize = 2048;
|
||||
rawMeta.resize(metaSize);
|
||||
metaSize = db->getBinary("RawTxn", &*rawMeta.begin(), rawMeta.getLength());
|
||||
metaSize = db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.getLength());
|
||||
if (metaSize > rawMeta.getLength())
|
||||
{
|
||||
rawMeta.resize(metaSize);
|
||||
db->getBinary("RawTxn", &*rawMeta.begin(), rawMeta.getLength());
|
||||
db->getBinary("TxnMeta", &*rawMeta.begin(), rawMeta.getLength());
|
||||
}else rawMeta.resize(metaSize);
|
||||
|
||||
TransactionMetaSet::pointer meta= boost::make_shared<TransactionMetaSet>(txn->getID(), txn->getLedger(), rawMeta.getData());
|
||||
ret.push_back(std::make_pair<Transaction::pointer, TransactionMetaSet::pointer>(txn,meta));
|
||||
ret.push_back(std::pair<Transaction::pointer, TransactionMetaSet::pointer>(txn,meta));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ TER PaymentTransactor::doApply()
|
||||
// Ripple payment
|
||||
|
||||
STPathSet spsPaths = mTxn.getFieldPathSet(sfPaths);
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
STAmount saMaxAmountAct;
|
||||
STAmount saDstAmountAct;
|
||||
|
||||
@@ -124,6 +125,7 @@ TER PaymentTransactor::doApply()
|
||||
mEngine->getNodes(),
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
saMaxAmount,
|
||||
saDstAmount,
|
||||
uDstAccountID,
|
||||
|
||||
@@ -779,13 +779,14 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
return rpcError(rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
STPathSet spsPaths;
|
||||
STPathSet spsComputed;
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
|
||||
Pathfinder pf(raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount);
|
||||
Pathfinder pf(raSrc, raDst, uSrcCurrencyID, uSrcIssuerID, saDstAmount);
|
||||
|
||||
pf.findPaths(5, 1, spsPaths, true);
|
||||
pf.findPaths(5, 1, spsComputed, true);
|
||||
|
||||
if (spsPaths.isEmpty())
|
||||
if (spsComputed.isEmpty())
|
||||
{
|
||||
cLog(lsDEBUG) << "ripple_path_find: No paths found.";
|
||||
}
|
||||
@@ -804,9 +805,9 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
saMaxAmount.negate();
|
||||
|
||||
// Strip empty/default path.
|
||||
if (1 == spsPaths.size() && !spsPaths.begin()->size())
|
||||
if (1 == spsComputed.size() && !spsComputed.begin()->size())
|
||||
{
|
||||
spsPaths.clear();
|
||||
spsComputed.clear();
|
||||
}
|
||||
|
||||
TER terResult =
|
||||
@@ -814,11 +815,12 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
lesSnapshot,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
vpsExpanded,
|
||||
saMaxAmount, // --> Amount to send is unlimited to get an estimate.
|
||||
saDstAmount, // --> Amount to deliver.
|
||||
raDst.getAccountID(), // --> Account to deliver to.
|
||||
raSrc.getAccountID(), // --> Account sending from.
|
||||
spsPaths, // --> Path set.
|
||||
spsComputed, // --> Path set.
|
||||
false, // --> Don't allow partial payment. This is for normal fill or kill payments.
|
||||
// Must achive delivery goal.
|
||||
false, // --> Don't limit quality. Average quality is wanted for normal payments.
|
||||
@@ -836,8 +838,13 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
{
|
||||
Json::Value jvEntry(Json::objectValue);
|
||||
|
||||
STPathSet spsCanonical;
|
||||
|
||||
RippleCalc::setCanonical(spsCanonical, vpsExpanded);
|
||||
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson(0);
|
||||
jvEntry["paths_expanded"] = spsPaths.getJson(0);
|
||||
// jvEntry["paths_expanded"] = spsExpanded.getJson(0);
|
||||
jvEntry["paths_canonical"] = spsCanonical.getJson(0);
|
||||
|
||||
jvArray.append(jvEntry);
|
||||
}
|
||||
@@ -852,7 +859,7 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
<< boost::str(boost::format("ripple_path_find: %s %s %s")
|
||||
% strToken
|
||||
% strHuman
|
||||
% spsPaths.getJson(0));
|
||||
% spsComputed.getJson(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -860,6 +867,10 @@ Json::Value RPCHandler::doRipplePathFind(const Json::Value& jvRequest)
|
||||
jvResult["alternatives"] = jvArray;
|
||||
}
|
||||
|
||||
cLog(lsDEBUG)
|
||||
<< boost::str(boost::format("ripple_path_find< %s")
|
||||
% jvResult);
|
||||
|
||||
return jvResult;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,17 +32,17 @@ std::size_t hash_value(const aciSource& asValue)
|
||||
//
|
||||
|
||||
// Return true, iff lhs has less priority than rhs.
|
||||
bool PathState::lessPriority(PathState::ref lhs, PathState::ref rhs)
|
||||
bool PathState::lessPriority(PathState& lhs, PathState& rhs)
|
||||
{
|
||||
if (lhs->uQuality != rhs->uQuality)
|
||||
return lhs->uQuality > rhs->uQuality; // Bigger is worse.
|
||||
if (lhs.uQuality != rhs.uQuality)
|
||||
return lhs.uQuality > rhs.uQuality; // Bigger is worse.
|
||||
|
||||
// Best quanity is second rank.
|
||||
if (lhs->saOutPass != rhs->saOutPass)
|
||||
return lhs->saOutPass < rhs->saOutPass; // Smaller is worse.
|
||||
if (lhs.saOutPass != rhs.saOutPass)
|
||||
return lhs.saOutPass < rhs.saOutPass; // Smaller is worse.
|
||||
|
||||
// Path index is third rank.
|
||||
return lhs->mIndex > rhs->mIndex; // Bigger is worse.
|
||||
return lhs.mIndex > rhs.mIndex; // Bigger is worse.
|
||||
}
|
||||
|
||||
// Make sure last path node delivers to uAccountID: uCurrencyID from uIssuerID.
|
||||
@@ -420,7 +420,7 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s")
|
||||
cLog(lsDEBUG) << boost::str(boost::format("PathState: in=%s/%s out=%s/%s %s")
|
||||
% STAmount::createHumanCurrency(uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(uMaxIssuerID)
|
||||
% STAmount::createHumanCurrency(uOutCurrencyID)
|
||||
@@ -429,10 +429,159 @@ cLog(lsDEBUG) << boost::str(boost::format("PathState: receiver implied: account=
|
||||
}
|
||||
|
||||
void PathState::setCanonical(
|
||||
PathState::ref pspExpanded
|
||||
const PathState& psExpanded
|
||||
)
|
||||
{
|
||||
;
|
||||
saInAct = psExpanded.saInAct;
|
||||
saOutAct = psExpanded.saOutAct;
|
||||
|
||||
const uint160 uMaxCurrencyID = saInAct.getCurrency();
|
||||
const uint160 uMaxIssuerID = saInAct.getIssuer();
|
||||
|
||||
const uint160 uOutCurrencyID = saOutAct.getCurrency();
|
||||
const uint160 uOutIssuerID = saOutAct.getIssuer();
|
||||
|
||||
unsigned int uNode = 0;
|
||||
|
||||
unsigned int uEnd = psExpanded.vpnNodes.size(); // The node, indexed by 0, not to include.
|
||||
|
||||
uint160 uDstAccountID = psExpanded.vpnNodes[uEnd].uAccountID;
|
||||
|
||||
uint160 uAccountID = psExpanded.vpnNodes[0].uAccountID;
|
||||
uint160 uCurrencyID = uMaxCurrencyID;
|
||||
uint160 uIssuerID = uMaxIssuerID;
|
||||
|
||||
// Node 0 is a composit of the sending account and saInAct.
|
||||
++uNode; // skip node 0
|
||||
|
||||
// Last node is implied: Always skip last node
|
||||
--uEnd; // skip last node
|
||||
|
||||
// saInAct
|
||||
// - currency is always the same as vpnNodes[0].
|
||||
if (uMaxIssuerID != uAccountID);
|
||||
{
|
||||
// saInAct issuer is not the sender. This forces an implied node.
|
||||
|
||||
// skip node 1
|
||||
|
||||
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
|
||||
|
||||
++uNode;
|
||||
}
|
||||
|
||||
if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
|
||||
{
|
||||
// The next to last node is saOutAct if an issuer different from receiver is supplied.
|
||||
// The next to last node can be implied.
|
||||
|
||||
--uEnd;
|
||||
}
|
||||
|
||||
const PaymentNode& pnEnd = psExpanded.vpnNodes[uEnd];
|
||||
|
||||
if (uNode != uEnd
|
||||
&& !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
|
||||
{
|
||||
// The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
|
||||
--uEnd;
|
||||
}
|
||||
|
||||
// Do not include uEnd.
|
||||
for (; uNode != uEnd; ++uNode)
|
||||
{
|
||||
const PaymentNode& pnPrv = psExpanded.vpnNodes[uNode-1];
|
||||
const PaymentNode& pnCur = psExpanded.vpnNodes[uNode];
|
||||
const PaymentNode& pnNxt = psExpanded.vpnNodes[uNode+1];
|
||||
|
||||
const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
|
||||
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
|
||||
const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount);
|
||||
|
||||
bool bSkip = false;
|
||||
|
||||
if (bCurAccount)
|
||||
{
|
||||
// Currently at an account.
|
||||
|
||||
if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
|
||||
{
|
||||
// Account issues itself.
|
||||
|
||||
bSkip = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Currently at an offer.
|
||||
|
||||
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
|
||||
&& pnPrv.uCurrencyID != pnNxt.uCurrencyID)
|
||||
{
|
||||
// Offer can be implied by currenct change.
|
||||
|
||||
bSkip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bSkip)
|
||||
{
|
||||
// Copy node
|
||||
PaymentNode pnNew;
|
||||
|
||||
bool bSetAccount = bCurAccount;
|
||||
bool bSetCurrency = uCurrencyID != pnCur.uCurrencyID;
|
||||
bool bSetIssuer = !uCurrencyID && uIssuerID != pnCur.uIssuerID;
|
||||
|
||||
pnNew.uFlags = (bSetAccount ? STPathElement::typeAccount : 0)
|
||||
| (bSetCurrency ? STPathElement::typeCurrency : 0)
|
||||
| (bSetIssuer ? STPathElement::typeIssuer : 0);
|
||||
|
||||
if (bSetAccount)
|
||||
pnNew.uAccountID = pnCur.uAccountID;
|
||||
|
||||
if (bSetCurrency)
|
||||
{
|
||||
pnNew.uCurrencyID = pnCur.uCurrencyID;
|
||||
uCurrencyID = pnNew.uCurrencyID;
|
||||
}
|
||||
|
||||
if (bSetIssuer)
|
||||
pnNew.uIssuerID = pnCur.uIssuerID;
|
||||
|
||||
if (bSetCurrency && !uCurrencyID)
|
||||
uIssuerID.zero();
|
||||
|
||||
vpnNodes.push_back(pnNew);
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("setCanonical: in=%s/%s out=%s/%s %s")
|
||||
% STAmount::createHumanCurrency(uMaxCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(uMaxIssuerID)
|
||||
% STAmount::createHumanCurrency(uOutCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(uOutIssuerID)
|
||||
% getJson());
|
||||
}
|
||||
|
||||
void RippleCalc::setCanonical(STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded)
|
||||
{
|
||||
BOOST_FOREACH(PathState::ref pspExpanded, vpsExpanded)
|
||||
{
|
||||
PathState psCanonical(*pspExpanded, false);
|
||||
|
||||
psCanonical.setCanonical(*pspExpanded);
|
||||
|
||||
STPath spCanonical;
|
||||
|
||||
BOOST_FOREACH(const PaymentNode& pnElem, psCanonical.vpnNodes)
|
||||
{
|
||||
STPathElement speElem(pnElem.uFlags, pnElem.uAccountID, pnElem.uCurrencyID, pnElem.uIssuerID);
|
||||
spCanonical.addElement(speElem);
|
||||
}
|
||||
|
||||
spsDst.addPath(spCanonical);
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value PathState::getJson() const
|
||||
@@ -460,22 +609,22 @@ Json::Value PathState::getJson() const
|
||||
if (!!pnNode.uIssuerID)
|
||||
jvNode["issuer"] = RippleAddress::createHumanAccountID(pnNode.uIssuerID);
|
||||
|
||||
// if (pnNode.saRevRedeem)
|
||||
if (pnNode.saRevRedeem)
|
||||
jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText();
|
||||
|
||||
// if (pnNode.saRevIssue)
|
||||
if (pnNode.saRevIssue)
|
||||
jvNode["rev_issue"] = pnNode.saRevIssue.getFullText();
|
||||
|
||||
// if (pnNode.saRevDeliver)
|
||||
if (pnNode.saRevDeliver)
|
||||
jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText();
|
||||
|
||||
// if (pnNode.saFwdRedeem)
|
||||
if (pnNode.saFwdRedeem)
|
||||
jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText();
|
||||
|
||||
// if (pnNode.saFwdIssue)
|
||||
if (pnNode.saFwdIssue)
|
||||
jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText();
|
||||
|
||||
// if (pnNode.saFwdDeliver)
|
||||
if (pnNode.saFwdDeliver)
|
||||
jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText();
|
||||
|
||||
jvNodes.append(jvNode);
|
||||
@@ -519,12 +668,12 @@ Json::Value PathState::getJson() const
|
||||
// <-- uOfferIndex : 0=end of list.
|
||||
TER RippleCalc::calcNodeAdvance(
|
||||
const unsigned int uNode, // 0 < uNode < uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const bool bReverse)
|
||||
{
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
|
||||
const uint160& uPrvCurrencyID = pnPrv.uCurrencyID;
|
||||
const uint160& uPrvIssuerID = pnPrv.uIssuerID;
|
||||
@@ -668,8 +817,8 @@ TER RippleCalc::calcNodeAdvance(
|
||||
// XXX This can get called multiple times for same source in a row, caching result would be nice.
|
||||
// XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check
|
||||
// quality.
|
||||
curIssuerNodeConstIterator itForward = pspCur->umForward.find(asLine);
|
||||
const bool bFoundForward = itForward != pspCur->umForward.end();
|
||||
curIssuerNodeConstIterator itForward = psCur.umForward.find(asLine);
|
||||
const bool bFoundForward = itForward != psCur.umForward.end();
|
||||
|
||||
if (bFoundForward && itForward->second != uNode)
|
||||
{
|
||||
@@ -692,8 +841,8 @@ TER RippleCalc::calcNodeAdvance(
|
||||
continue;
|
||||
}
|
||||
|
||||
curIssuerNodeConstIterator itReverse = pspCur->umReverse.find(asLine);
|
||||
bool bFoundReverse = itReverse != pspCur->umReverse.end();
|
||||
curIssuerNodeConstIterator itReverse = psCur.umReverse.find(asLine);
|
||||
bool bFoundReverse = itReverse != psCur.umReverse.end();
|
||||
|
||||
if (bFoundReverse && itReverse->second != uNode)
|
||||
{
|
||||
@@ -735,7 +884,7 @@ TER RippleCalc::calcNodeAdvance(
|
||||
% STAmount::createHumanCurrency(uCurCurrencyID)
|
||||
% RippleAddress::createHumanAccountID(uCurIssuerID));
|
||||
|
||||
pspCur->umReverse.insert(std::make_pair(asLine, uNode));
|
||||
psCur.umReverse.insert(std::make_pair(asLine, uNode));
|
||||
}
|
||||
|
||||
bFundsDirty = false;
|
||||
@@ -761,7 +910,7 @@ TER RippleCalc::calcNodeAdvance(
|
||||
// till request is satisified while we the rate does not increase past the initial rate.
|
||||
TER RippleCalc::calcNodeDeliverRev(
|
||||
const unsigned int uNode, // 0 < uNode < uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID, // --> Output owner's account.
|
||||
const STAmount& saOutReq, // --> Funds wanted.
|
||||
@@ -769,8 +918,8 @@ TER RippleCalc::calcNodeDeliverRev(
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
|
||||
const uint160& uCurIssuerID = pnCur.uIssuerID;
|
||||
const uint160& uPrvAccountID = pnPrv.uAccountID;
|
||||
@@ -802,7 +951,7 @@ TER RippleCalc::calcNodeDeliverRev(
|
||||
STAmount& saTakerGets = pnCur.saTakerGets;
|
||||
STAmount& saRateMax = pnCur.saRateMax;
|
||||
|
||||
terResult = calcNodeAdvance(uNode, pspCur, bMultiQuality, true); // If needed, advance to next funded offer.
|
||||
terResult = calcNodeAdvance(uNode, psCur, bMultiQuality, true); // If needed, advance to next funded offer.
|
||||
|
||||
if (tesSUCCESS != terResult || !uOfferIndex)
|
||||
{
|
||||
@@ -908,7 +1057,7 @@ TER RippleCalc::calcNodeDeliverRev(
|
||||
|
||||
terResult = calcNodeDeliverRev(
|
||||
uNode-1,
|
||||
pspCur,
|
||||
psCur,
|
||||
bMultiQuality,
|
||||
uOfrOwnerID,
|
||||
saInPassReq,
|
||||
@@ -969,7 +1118,7 @@ TER RippleCalc::calcNodeDeliverRev(
|
||||
// <-- pnCur.saFwdDeliver: For calcNodeAccountFwd to know how much went through
|
||||
TER RippleCalc::calcNodeDeliverFwd(
|
||||
const unsigned int uNode, // 0 < uNode < uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID, // --> Input owner's account.
|
||||
const STAmount& saInReq, // --> Amount to deliver.
|
||||
@@ -978,9 +1127,9 @@ TER RippleCalc::calcNodeDeliverFwd(
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = psCur.vpnNodes[uNode+1];
|
||||
|
||||
const uint160& uNxtAccountID = pnNxt.uAccountID;
|
||||
const uint160& uCurCurrencyID = pnCur.uCurrencyID;
|
||||
@@ -1002,7 +1151,7 @@ TER RippleCalc::calcNodeDeliverFwd(
|
||||
&& saInAct + saInFees != saInReq) // Did not deliver all funds.
|
||||
{
|
||||
// Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct
|
||||
terResult = calcNodeAdvance(uNode, pspCur, bMultiQuality, false); // If needed, advance to next funded offer.
|
||||
terResult = calcNodeAdvance(uNode, psCur, bMultiQuality, false); // If needed, advance to next funded offer.
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
{
|
||||
@@ -1079,7 +1228,7 @@ TER RippleCalc::calcNodeDeliverFwd(
|
||||
// Therefore, immediately push through output for current offer.
|
||||
terResult = RippleCalc::calcNodeDeliverFwd(
|
||||
uNode+1,
|
||||
pspCur,
|
||||
psCur,
|
||||
bMultiQuality,
|
||||
uOfrOwnerID, // --> Current holder.
|
||||
saOutPassMax, // --> Amount available.
|
||||
@@ -1138,7 +1287,7 @@ TER RippleCalc::calcNodeDeliverFwd(
|
||||
if (saOutPassAct == saOutFunded)
|
||||
{
|
||||
// Offer became unfunded.
|
||||
pspCur->vUnfundedBecame.push_back(uOfferIndex);
|
||||
psCur.vUnfundedBecame.push_back(uOfferIndex);
|
||||
bEntryAdvance = true;
|
||||
}
|
||||
|
||||
@@ -1161,13 +1310,13 @@ TER RippleCalc::calcNodeDeliverFwd(
|
||||
// Called to drive from the last offer node in a chain.
|
||||
TER RippleCalc::calcNodeOfferRev(
|
||||
const unsigned int uNode, // 0 < uNode < uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
TER terResult;
|
||||
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = pspCur->vpnNodes[uNode+1];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = psCur.vpnNodes[uNode+1];
|
||||
|
||||
if (!!pnNxt.uAccountID)
|
||||
{
|
||||
@@ -1176,7 +1325,7 @@ TER RippleCalc::calcNodeOfferRev(
|
||||
|
||||
terResult = calcNodeDeliverRev(
|
||||
uNode,
|
||||
pspCur,
|
||||
psCur,
|
||||
bMultiQuality,
|
||||
|
||||
pnNxt.uAccountID,
|
||||
@@ -1201,12 +1350,12 @@ TER RippleCalc::calcNodeOfferRev(
|
||||
// - Deliver is set without transfer fees.
|
||||
TER RippleCalc::calcNodeOfferFwd(
|
||||
const unsigned int uNode, // 0 < uNode < uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality
|
||||
)
|
||||
{
|
||||
TER terResult;
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode-1];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode-1];
|
||||
|
||||
if (!!pnPrv.uAccountID)
|
||||
{
|
||||
@@ -1216,7 +1365,7 @@ TER RippleCalc::calcNodeOfferFwd(
|
||||
|
||||
terResult = calcNodeDeliverFwd(
|
||||
uNode,
|
||||
pspCur,
|
||||
psCur,
|
||||
bMultiQuality,
|
||||
pnPrv.uAccountID,
|
||||
pnPrv.saFwdDeliver, // Previous is sending this much.
|
||||
@@ -1353,16 +1502,16 @@ void RippleCalc::calcNodeRipple(
|
||||
// Issues are limited based on credit limits and amount owed.
|
||||
// No account balance adjustments as we don't know how much is going to actually be pushed through yet.
|
||||
// <-- tesSUCCESS or tepPATH_DRY
|
||||
TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality)
|
||||
TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
const unsigned int uLast = pspCur->vpnNodes.size() - 1;
|
||||
const unsigned int uLast = psCur.vpnNodes.size() - 1;
|
||||
|
||||
uint64 uRateMax = 0;
|
||||
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode ? uNode-1 : 0];
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = pspCur->vpnNodes[uNode == uLast ? uLast : uNode+1];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode ? uNode-1 : 0];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = psCur.vpnNodes[uNode == uLast ? uLast : uNode+1];
|
||||
|
||||
// Current is allowed to redeem to next.
|
||||
const bool bPrvAccount = !uNode || isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
|
||||
@@ -1433,7 +1582,7 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
||||
% saCurIssueReq.getFullText()
|
||||
% saNxtOwed.getFullText());
|
||||
|
||||
cLog(lsINFO) << pspCur->getJson();
|
||||
cLog(lsINFO) << psCur.getJson();
|
||||
|
||||
assert(!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand.
|
||||
assert(!saCurIssueReq // If not issuing, fine.
|
||||
@@ -1454,8 +1603,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
||||
// account --> ACCOUNT --> $
|
||||
// Overall deliverable.
|
||||
const STAmount& saCurWantedReq = bPrvAccount
|
||||
? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
|
||||
: pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs.
|
||||
? std::min(psCur.saOutReq-psCur.saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
|
||||
: psCur.saOutReq-psCur.saOutAct; // Previous is an offer, no limit: redeem own IOUs.
|
||||
STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer());
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s")
|
||||
@@ -1618,8 +1767,8 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
||||
{
|
||||
// offer --> ACCOUNT --> $
|
||||
const STAmount& saCurWantedReq = bPrvAccount
|
||||
? std::min(pspCur->saOutReq-pspCur->saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
|
||||
: pspCur->saOutReq-pspCur->saOutAct; // Previous is an offer, no limit: redeem own IOUs.
|
||||
? std::min(psCur.saOutReq-psCur.saOutAct, saPrvLimit+saPrvOwed) // If previous is an account, limit.
|
||||
: psCur.saOutReq-psCur.saOutAct; // Previous is an offer, no limit: redeem own IOUs.
|
||||
STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer());
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s")
|
||||
@@ -1702,17 +1851,17 @@ TER RippleCalc::calcNodeAccountRev(const unsigned int uNode, PathState::ref pspC
|
||||
// - If next node is an offer and output is XRP then we need to deliver funds to limbo.
|
||||
TER RippleCalc::calcNodeAccountFwd(
|
||||
const unsigned int uNode, // 0 <= uNode <= uLast
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
const unsigned int uLast = pspCur->vpnNodes.size() - 1;
|
||||
const unsigned int uLast = psCur.vpnNodes.size() - 1;
|
||||
|
||||
uint64 uRateMax = 0;
|
||||
|
||||
PaymentNode& pnPrv = pspCur->vpnNodes[uNode ? uNode-1 : 0];
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = pspCur->vpnNodes[uNode == uLast ? uLast : uNode+1];
|
||||
PaymentNode& pnPrv = psCur.vpnNodes[uNode ? uNode-1 : 0];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
PaymentNode& pnNxt = psCur.vpnNodes[uNode == uLast ? uLast : uNode+1];
|
||||
|
||||
const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount);
|
||||
const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount);
|
||||
@@ -1754,7 +1903,7 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
|
||||
|
||||
// For !uNode
|
||||
STAmount& saCurSendMaxPass = pspCur->saInPass; // Report how much pass sends.
|
||||
STAmount& saCurSendMaxPass = psCur.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")
|
||||
% uNode
|
||||
@@ -1780,10 +1929,10 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
|
||||
saCurRedeemAct = saCurRedeemReq;
|
||||
|
||||
if (!pspCur->saInReq.isNegative())
|
||||
if (!psCur.saInReq.isNegative())
|
||||
{
|
||||
// Limit by send max.
|
||||
saCurRedeemAct = std::min(saCurRedeemAct, pspCur->saInReq-pspCur->saInAct);
|
||||
saCurRedeemAct = std::min(saCurRedeemAct, psCur.saInReq-psCur.saInAct);
|
||||
}
|
||||
|
||||
saCurSendMaxPass = saCurRedeemAct;
|
||||
@@ -1792,17 +1941,17 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
? saCurIssueReq
|
||||
: STAmount(saCurIssueReq);
|
||||
|
||||
if (!!saCurIssueAct && !pspCur->saInReq.isNegative())
|
||||
if (!!saCurIssueAct && !psCur.saInReq.isNegative())
|
||||
{
|
||||
// Limit by send max.
|
||||
saCurIssueAct = std::min(saCurIssueAct, pspCur->saInReq-pspCur->saInAct-saCurRedeemAct);
|
||||
saCurIssueAct = std::min(saCurIssueAct, psCur.saInReq-psCur.saInAct-saCurRedeemAct);
|
||||
}
|
||||
|
||||
saCurSendMaxPass += saCurIssueAct;
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s")
|
||||
% pspCur->saInReq.getFullText()
|
||||
% pspCur->saInAct.getFullText()
|
||||
% psCur.saInReq.getFullText()
|
||||
% psCur.saInAct.getFullText()
|
||||
% saCurRedeemAct.getFullText()
|
||||
% saCurIssueReq.getFullText()
|
||||
% saCurIssueAct.getFullText()
|
||||
@@ -1819,7 +1968,7 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
|
||||
// Last node. Accept all funds. Calculate amount actually to credit.
|
||||
|
||||
STAmount& saCurReceive = pspCur->saOutPass;
|
||||
STAmount& saCurReceive = psCur.saOutPass;
|
||||
|
||||
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
|
||||
? saPrvIssueReq // No fee.
|
||||
@@ -1916,10 +2065,10 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
saCurDeliverAct = saCurDeliverReq;
|
||||
|
||||
// If limited, then limit by send max and available.
|
||||
if (!pspCur->saInReq.isNegative())
|
||||
if (!psCur.saInReq.isNegative())
|
||||
{
|
||||
// Limit by send max.
|
||||
saCurDeliverAct = std::min(saCurDeliverAct, pspCur->saInReq-pspCur->saInAct);
|
||||
saCurDeliverAct = std::min(saCurDeliverAct, psCur.saInReq-psCur.saInAct);
|
||||
|
||||
// Limit XRP by available. No limit for non-XRP as issuer.
|
||||
if (!uCurAccountID)
|
||||
@@ -1956,7 +2105,7 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
// offer --> ACCOUNT --> $
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $ : %s") % saPrvDeliverReq.getFullText());
|
||||
|
||||
STAmount& saCurReceive = pspCur->saOutPass;
|
||||
STAmount& saCurReceive = psCur.saOutPass;
|
||||
|
||||
// Amount to credit.
|
||||
saCurReceive = saPrvDeliverReq;
|
||||
@@ -2012,20 +2161,20 @@ TER RippleCalc::calcNodeAccountFwd(
|
||||
return terResult;
|
||||
}
|
||||
|
||||
TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality)
|
||||
TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
|
||||
{
|
||||
const PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
const PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeFwd> uNode=%d") % uNode);
|
||||
|
||||
TER terResult = bCurAccount
|
||||
? calcNodeAccountFwd(uNode, pspCur, bMultiQuality)
|
||||
: calcNodeOfferFwd(uNode, pspCur, bMultiQuality);
|
||||
? calcNodeAccountFwd(uNode, psCur, bMultiQuality)
|
||||
: calcNodeOfferFwd(uNode, psCur, bMultiQuality);
|
||||
|
||||
if (tesSUCCESS == terResult && uNode + 1 != pspCur->vpnNodes.size())
|
||||
if (tesSUCCESS == terResult && uNode + 1 != psCur.vpnNodes.size())
|
||||
{
|
||||
terResult = calcNodeFwd(uNode+1, pspCur, bMultiQuality);
|
||||
terResult = calcNodeFwd(uNode+1, psCur, bMultiQuality);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeFwd< uNode=%d terResult=%d") % uNode % terResult);
|
||||
@@ -2042,9 +2191,9 @@ TER RippleCalc::calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, con
|
||||
// --> [all]saWanted.mCurrency
|
||||
// --> [all]saAccount
|
||||
// <-> [0]saWanted.mAmount : --> limit, <-- actual
|
||||
TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality)
|
||||
TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
|
||||
{
|
||||
PaymentNode& pnCur = pspCur->vpnNodes[uNode];
|
||||
PaymentNode& pnCur = psCur.vpnNodes[uNode];
|
||||
const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount);
|
||||
TER terResult;
|
||||
|
||||
@@ -2060,8 +2209,8 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
|
||||
% saTransferRate.getFullText());
|
||||
|
||||
terResult = bCurAccount
|
||||
? calcNodeAccountRev(uNode, pspCur, bMultiQuality)
|
||||
: calcNodeOfferRev(uNode, pspCur, bMultiQuality);
|
||||
? calcNodeAccountRev(uNode, psCur, bMultiQuality)
|
||||
: calcNodeOfferRev(uNode, psCur, bMultiQuality);
|
||||
|
||||
// Do previous.
|
||||
if (tesSUCCESS != terResult)
|
||||
@@ -2073,7 +2222,7 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
|
||||
{
|
||||
// Continue in reverse.
|
||||
|
||||
terResult = calcNodeRev(uNode-1, pspCur, bMultiQuality);
|
||||
terResult = calcNodeRev(uNode-1, psCur, bMultiQuality);
|
||||
}
|
||||
|
||||
cLog(lsINFO) << boost::str(boost::format("calcNodeRev< uNode=%d terResult=%s/%d") % uNode % transToken(terResult) % terResult);
|
||||
@@ -2083,68 +2232,68 @@ TER RippleCalc::calcNodeRev(const unsigned int uNode, PathState::ref pspCur, con
|
||||
|
||||
// Calculate the next increment of a path.
|
||||
// The increment is what can satisfy a portion or all of the requested output at the best quality.
|
||||
// <-- pspCur->uQuality
|
||||
void RippleCalc::pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
|
||||
// <-- psCur.uQuality
|
||||
void RippleCalc::pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
|
||||
{
|
||||
// The next state is what is available in preference order.
|
||||
// This is calculated when referenced accounts changed.
|
||||
const bool bMultiQuality = iPaths == 1;
|
||||
const unsigned int uLast = pspCur->vpnNodes.size() - 1;
|
||||
const unsigned int uLast = psCur.vpnNodes.size() - 1;
|
||||
|
||||
pspCur->bConsumed = false;
|
||||
psCur.bConsumed = false;
|
||||
|
||||
// YYY This clearing should only be needed for nice logging.
|
||||
pspCur->saInPass = STAmount(pspCur->saInReq.getCurrency(), pspCur->saInReq.getIssuer());
|
||||
pspCur->saOutPass = STAmount(pspCur->saOutReq.getCurrency(), pspCur->saOutReq.getIssuer());
|
||||
psCur.saInPass = STAmount(psCur.saInReq.getCurrency(), psCur.saInReq.getIssuer());
|
||||
psCur.saOutPass = STAmount(psCur.saOutReq.getCurrency(), psCur.saOutReq.getIssuer());
|
||||
|
||||
pspCur->vUnfundedBecame.clear();
|
||||
pspCur->umReverse.clear();
|
||||
psCur.vUnfundedBecame.clear();
|
||||
psCur.umReverse.clear();
|
||||
|
||||
cLog(lsINFO) << "Path In: " << pspCur->getJson();
|
||||
cLog(lsINFO) << "Path In: " << psCur.getJson();
|
||||
|
||||
assert(pspCur->vpnNodes.size() >= 2);
|
||||
assert(psCur.vpnNodes.size() >= 2);
|
||||
|
||||
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
||||
lesCurrent.bumpSeq(); // Begin ledger varance.
|
||||
|
||||
pspCur->terStatus = calcNodeRev(uLast, pspCur, bMultiQuality);
|
||||
psCur.terStatus = calcNodeRev(uLast, psCur, bMultiQuality);
|
||||
|
||||
cLog(lsINFO) << "Path after reverse: " << pspCur->getJson();
|
||||
cLog(lsINFO) << "Path after reverse: " << psCur.getJson();
|
||||
|
||||
if (tesSUCCESS == pspCur->terStatus)
|
||||
if (tesSUCCESS == psCur.terStatus)
|
||||
{
|
||||
// Do forward.
|
||||
lesCurrent = lesCheckpoint; // Restore from checkpoint.
|
||||
lesCurrent.bumpSeq(); // Begin ledger varance.
|
||||
|
||||
pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality);
|
||||
psCur.terStatus = calcNodeFwd(0, psCur, bMultiQuality);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == pspCur->terStatus)
|
||||
if (tesSUCCESS == psCur.terStatus)
|
||||
{
|
||||
tLog(!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG)
|
||||
tLog(!psCur.saInPass || !psCur.saOutPass, lsDEBUG)
|
||||
<< boost::str(boost::format("saOutPass=%s saInPass=%s")
|
||||
% pspCur->saOutPass.getFullText()
|
||||
% pspCur->saInPass.getFullText());
|
||||
% psCur.saOutPass.getFullText()
|
||||
% psCur.saInPass.getFullText());
|
||||
|
||||
assert(!!pspCur->saOutPass && !!pspCur->saInPass);
|
||||
assert(!!psCur.saOutPass && !!psCur.saInPass);
|
||||
|
||||
pspCur->uQuality = STAmount::getRate(pspCur->saOutPass, pspCur->saInPass); // Calculate relative quality.
|
||||
psCur.uQuality = STAmount::getRate(psCur.saOutPass, psCur.saInPass); // Calculate relative quality.
|
||||
|
||||
cLog(lsINFO) << "Path after forward: " << pspCur->getJson();
|
||||
cLog(lsINFO) << "Path after forward: " << psCur.getJson();
|
||||
}
|
||||
else
|
||||
{
|
||||
pspCur->uQuality = 0;
|
||||
psCur.uQuality = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TER RippleCalc::rippleCalc(
|
||||
// Compute paths vs this ledger entry set. Up to caller to actually apply to ledger.
|
||||
LedgerEntrySet& lesActive, // <-> --> = Fee already applied to src balance.
|
||||
STAmount& saMaxAmountAct, // <-- The computed input amount.
|
||||
STAmount& saDstAmountAct, // <-- The computed output amount.
|
||||
|
||||
LedgerEntrySet& lesActive, // <-> --> = Fee already applied to src balance.
|
||||
STAmount& saMaxAmountAct, // <-- The computed input amount.
|
||||
STAmount& saDstAmountAct, // <-- The computed output amount.
|
||||
std::vector<PathState::pointer>& vpsExpanded,
|
||||
// Issuer:
|
||||
// XRP: ACCOUNT_XRP
|
||||
// non-XRP: uSrcAccountID (for any issuer) or another account with trust node.
|
||||
@@ -2178,44 +2327,38 @@ TER RippleCalc::rippleCalc(
|
||||
}
|
||||
|
||||
// Incrementally search paths.
|
||||
std::vector<PathState::pointer> vpsPaths;
|
||||
|
||||
if (!bNoRippleDirect)
|
||||
{
|
||||
// Direct path.
|
||||
// XXX Might also make a XRP bridge by default.
|
||||
|
||||
PathState::pointer pspDirect = PathState::createExpanded(
|
||||
lesActive,
|
||||
STPath(),
|
||||
uDstAccountID,
|
||||
uSrcAccountID,
|
||||
saDstAmountReq,
|
||||
saMaxAmountReq);
|
||||
PathState::pointer pspDirect = boost::make_shared<PathState>(saDstAmountReq, saMaxAmountReq, lesActive.getLedgerRef());
|
||||
|
||||
pspDirect->setIndex(vpsPaths.size());
|
||||
if (!pspDirect)
|
||||
return temUNKNOWN;
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: add: %d status: %s")
|
||||
% !!pspDirect
|
||||
% transToken(pspDirect ? pspDirect->terStatus : temUNKNOWN));
|
||||
if (pspDirect)
|
||||
{
|
||||
// Return if malformed.
|
||||
if (isTemMalformed(pspDirect->terStatus))
|
||||
return pspDirect->terStatus;
|
||||
pspDirect->setExpanded(lesActive, STPath(), uDstAccountID, uSrcAccountID);
|
||||
|
||||
if (tesSUCCESS == pspDirect->terStatus)
|
||||
{
|
||||
// Had a success.
|
||||
terResult = tesSUCCESS;
|
||||
pspDirect->setIndex(vpsExpanded.size());
|
||||
|
||||
vpsPaths.push_back(pspDirect);
|
||||
}
|
||||
else if (terNO_LINE != pspDirect->terStatus)
|
||||
{
|
||||
terResult = pspDirect->terStatus;
|
||||
}
|
||||
}
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: status: %s")
|
||||
% transToken(pspDirect->terStatus));
|
||||
// Return if malformed.
|
||||
if (isTemMalformed(pspDirect->terStatus))
|
||||
return pspDirect->terStatus;
|
||||
|
||||
if (tesSUCCESS == pspDirect->terStatus)
|
||||
{
|
||||
// Had a success.
|
||||
terResult = tesSUCCESS;
|
||||
|
||||
vpsExpanded.push_back(pspDirect);
|
||||
}
|
||||
else if (terNO_LINE != pspDirect->terStatus)
|
||||
{
|
||||
terResult = pspDirect->terStatus;
|
||||
}
|
||||
}
|
||||
|
||||
cLog(lsINFO) << "rippleCalc: Paths in set: " << spsPaths.size();
|
||||
@@ -2223,37 +2366,32 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build direct: add: %d sta
|
||||
int iIndex = 0;
|
||||
BOOST_FOREACH(const STPath& spPath, spsPaths)
|
||||
{
|
||||
PathState::pointer pspExpanded = PathState::createExpanded(
|
||||
lesActive,
|
||||
spPath,
|
||||
uDstAccountID,
|
||||
uSrcAccountID,
|
||||
saDstAmountReq,
|
||||
saMaxAmountReq);
|
||||
PathState::pointer pspExpanded = boost::make_shared<PathState>(saDstAmountReq, saMaxAmountReq, lesActive.getLedgerRef());
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d status: %s")
|
||||
if (!pspExpanded)
|
||||
return temUNKNOWN;
|
||||
|
||||
pspExpanded->setExpanded(lesActive, spPath, uDstAccountID, uSrcAccountID);
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: status: %s")
|
||||
% ++iIndex
|
||||
% !!pspExpanded
|
||||
% transToken(pspExpanded ? pspExpanded->terStatus : temUNKNOWN));
|
||||
% transToken(pspExpanded->terStatus));
|
||||
|
||||
if (pspExpanded)
|
||||
{
|
||||
// Return, if the path specification was malformed.
|
||||
if (isTemMalformed(pspExpanded->terStatus))
|
||||
return pspExpanded->terStatus;
|
||||
// Return, if the path specification was malformed.
|
||||
if (isTemMalformed(pspExpanded->terStatus))
|
||||
return pspExpanded->terStatus;
|
||||
|
||||
if (tesSUCCESS == pspExpanded->terStatus) {
|
||||
terResult = tesSUCCESS; // Had a success.
|
||||
if (tesSUCCESS == pspExpanded->terStatus) {
|
||||
terResult = tesSUCCESS; // Had a success.
|
||||
|
||||
pspExpanded->setIndex(vpsPaths.size());
|
||||
pspExpanded->setIndex(vpsExpanded.size());
|
||||
|
||||
vpsPaths.push_back(pspExpanded);
|
||||
}
|
||||
else if (terNO_LINE != pspExpanded->terStatus)
|
||||
{
|
||||
terResult = pspExpanded->terStatus;
|
||||
}
|
||||
}
|
||||
vpsExpanded.push_back(pspExpanded);
|
||||
}
|
||||
else if (terNO_LINE != pspExpanded->terStatus)
|
||||
{
|
||||
terResult = pspExpanded->terStatus;
|
||||
}
|
||||
}
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
@@ -2276,19 +2414,19 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Build path: %d: add: %d s
|
||||
int iPass = 0;
|
||||
while (temUNCERTAIN == terResult)
|
||||
{
|
||||
PathState::pointer pspBest;
|
||||
int iBest = -1;
|
||||
const LedgerEntrySet lesCheckpoint = lesActive;
|
||||
int iDry = 0;
|
||||
|
||||
// Find the best path.
|
||||
BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths)
|
||||
BOOST_FOREACH(PathState::pointer pspCur, vpsExpanded)
|
||||
{
|
||||
if (pspCur->uQuality)
|
||||
{
|
||||
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
|
||||
pspCur->saInAct = saMaxAmountAct; // Update to current amount processed.
|
||||
pspCur->saOutAct = saDstAmountAct;
|
||||
|
||||
rc.pathNext(pspCur, vpsPaths.size(), lesCheckpoint, lesActive); // Compute increment.
|
||||
rc.pathNext(*pspCur, vpsExpanded.size(), lesCheckpoint, lesActive); // Compute increment.
|
||||
|
||||
if (!pspCur->uQuality) {
|
||||
// Path was dry.
|
||||
@@ -2304,9 +2442,9 @@ int iPass = 0;
|
||||
|
||||
assert(!!pspCur->saInPass && !!pspCur->saOutPass);
|
||||
|
||||
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
|
||||
&& (!pspBest // Best is not yet set.
|
||||
|| PathState::lessPriority(pspBest, pspCur))) // Current is better than set.
|
||||
if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality.
|
||||
&& (iBest < 0 // Best is not yet set.
|
||||
|| PathState::lessPriority(*vpsExpanded[iBest], *pspCur))) // Current is better than set.
|
||||
{
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
|
||||
% STAmount::saFromRate(pspCur->uQuality)
|
||||
@@ -2314,20 +2452,21 @@ int iPass = 0;
|
||||
% pspCur->saOutPass.getFullText());
|
||||
|
||||
lesActive.swapWith(pspCur->lesEntries); // For the path, save ledger state.
|
||||
pspBest = pspCur;
|
||||
iBest = pspCur->getIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsPaths.size());
|
||||
BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths)
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size());
|
||||
BOOST_FOREACH(PathState::ref pspCur, vpsExpanded)
|
||||
{
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d best: %d consumed: %d") % pspCur->mIndex % pspCur->uQuality % (pspBest == pspCur) % pspCur->bConsumed);
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d best: %d consumed: %d") % pspCur->mIndex % pspCur->uQuality % (iBest == pspCur->getIndex()) % pspCur->bConsumed);
|
||||
}
|
||||
|
||||
if (pspBest)
|
||||
if (iBest >= 0)
|
||||
{
|
||||
// Apply best path.
|
||||
PathState::pointer pspBest = vpsExpanded[iBest];
|
||||
|
||||
cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: best: uQuality=%s saInPass=%s saOutPass=%s")
|
||||
% STAmount::saFromRate(pspBest->uQuality)
|
||||
@@ -2340,7 +2479,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
// Record best pass' LedgerEntrySet to build off of and potentially return.
|
||||
lesActive.swapWith(pspBest->lesEntries);
|
||||
|
||||
saMaxAmountAct += pspBest->saInPass;
|
||||
saMaxAmountAct += pspBest->saInPass;
|
||||
saDstAmountAct += pspBest->saOutPass;
|
||||
|
||||
if (pspBest->bConsumed)
|
||||
@@ -2355,7 +2494,7 @@ cLog(lsDEBUG) << boost::str(boost::format("rippleCalc: Summary: %d quality:%d be
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsPaths.size())
|
||||
else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsExpanded.size())
|
||||
{
|
||||
// Have not met requested amount or max send, try to do more. Prepare for next pass.
|
||||
|
||||
|
||||
@@ -107,12 +107,17 @@ public:
|
||||
return this;
|
||||
}
|
||||
|
||||
int getIndex() { return mIndex; };
|
||||
|
||||
PathState(
|
||||
const STAmount& saSend,
|
||||
const STAmount& saSendMax,
|
||||
const Ledger::ref lrLedger = Ledger::pointer()
|
||||
) : mLedger(lrLedger), saInReq(saSendMax), saOutReq(saSend) { ; }
|
||||
|
||||
PathState(const PathState& psSrc, bool bUnsed)
|
||||
: mLedger(psSrc.mLedger), saInReq(psSrc.saInReq), saOutReq(psSrc.saOutReq) { ; }
|
||||
|
||||
void setExpanded(
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
@@ -121,29 +126,14 @@ public:
|
||||
);
|
||||
|
||||
void setCanonical(
|
||||
PathState::ref pspExpanded
|
||||
const PathState& psExpanded
|
||||
);
|
||||
|
||||
Json::Value getJson() const;
|
||||
|
||||
static PathState::pointer createExpanded(
|
||||
const LedgerEntrySet& lesSource,
|
||||
const STPath& spSourcePath,
|
||||
const uint160& uReceiverID,
|
||||
const uint160& uSenderID,
|
||||
const STAmount& saSend,
|
||||
const STAmount& saSendMax
|
||||
)
|
||||
{
|
||||
PathState::pointer pspNew = boost::make_shared<PathState>(saSend, saSendMax, lesSource.getLedgerRef());
|
||||
|
||||
pspNew->setExpanded(lesSource, spSourcePath, uReceiverID, uSenderID);
|
||||
|
||||
return pspNew;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static PathState::pointer createCanonical(
|
||||
PathState::ref pspExpanded
|
||||
PathState&ref pspExpanded
|
||||
)
|
||||
{
|
||||
PathState::pointer pspNew = boost::make_shared<PathState>(pspExpanded->saOutAct, pspExpanded->saInAct);
|
||||
@@ -152,8 +142,8 @@ public:
|
||||
|
||||
return pspNew;
|
||||
}
|
||||
|
||||
static bool lessPriority(PathState::ref lhs, PathState::ref rhs);
|
||||
#endif
|
||||
static bool lessPriority(PathState& lhs, PathState& rhs);
|
||||
};
|
||||
|
||||
class RippleCalc
|
||||
@@ -168,18 +158,18 @@ public:
|
||||
// If the transaction fails to meet some constraint, still need to delete unfunded offers.
|
||||
boost::unordered_set<uint256> musUnfundedFound; // Offers that were found unfunded.
|
||||
|
||||
void pathNext(PathState::ref pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
TER calcNode(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountRev(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountFwd(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality);
|
||||
TER calcNodeAdvance(const unsigned int uNode, PathState::ref pspCur, const bool bMultiQuality, const bool bReverse);
|
||||
void pathNext(PathState& psCur, const int iPaths, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
TER calcNode(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountRev(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountFwd(const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAdvance(const unsigned int uNode, PathState& psCur, const bool bMultiQuality, const bool bReverse);
|
||||
TER calcNodeDeliverRev(
|
||||
const unsigned int uNode,
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID,
|
||||
const STAmount& saOutReq,
|
||||
@@ -187,7 +177,7 @@ public:
|
||||
|
||||
TER calcNodeDeliverFwd(
|
||||
const unsigned int uNode,
|
||||
PathState::ref pspCur,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID,
|
||||
const STAmount& saInReq,
|
||||
@@ -202,19 +192,22 @@ public:
|
||||
RippleCalc(LedgerEntrySet& lesNodes) : lesActive(lesNodes) { ; }
|
||||
|
||||
static TER rippleCalc(
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect,
|
||||
const bool bStandAlone
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
std::vector<PathState::pointer>& vpsExpanded,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect,
|
||||
const bool bStandAlone
|
||||
);
|
||||
|
||||
static void setCanonical(STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -570,10 +570,10 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
int mType;
|
||||
uint160 mAccountID;
|
||||
uint160 mCurrencyID;
|
||||
uint160 mIssuerID;
|
||||
unsigned int mType;
|
||||
uint160 mAccountID;
|
||||
uint160 mCurrencyID;
|
||||
uint160 mIssuerID;
|
||||
|
||||
public:
|
||||
STPathElement(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID,
|
||||
@@ -586,6 +586,12 @@ public:
|
||||
| (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer);
|
||||
}
|
||||
|
||||
STPathElement(unsigned int uType, const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID)
|
||||
: mType(uType), mAccountID(uAccountID), mCurrencyID(uCurrencyID), mIssuerID(uIssuerID)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int getNodeType() const { return mType; }
|
||||
bool isOffer() const { return mAccountID.isZero(); }
|
||||
bool isAccount() const { return !isOffer(); }
|
||||
@@ -634,6 +640,8 @@ public:
|
||||
std::vector<STPathElement>::const_iterator end() const { return mPath.end(); }
|
||||
|
||||
bool operator==(const STPath& t) const { return mPath == t.mPath; }
|
||||
|
||||
void setCanonical(const STPath& spExpanded);
|
||||
};
|
||||
|
||||
inline std::vector<STPathElement>::iterator range_begin(STPath & x)
|
||||
|
||||
111
src/js/remote.js
111
src/js/remote.js
@@ -194,7 +194,7 @@ var Remote = function (opts, trace) {
|
||||
this.stand_alone = undefined;
|
||||
this.online_target = false;
|
||||
this.online_state = 'closed'; // 'open', 'closed', 'connecting', 'closing'
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this.state = 'offline'; // 'online', 'offline'
|
||||
this.retry_timer = undefined;
|
||||
this.retry = undefined;
|
||||
|
||||
@@ -485,13 +485,10 @@ Remote.prototype._connect_message = function (ws, json) {
|
||||
this.emit('ledger_closed', message.ledger_hash, message.ledger_index);
|
||||
break;
|
||||
|
||||
// Account subscription event
|
||||
case 'account':
|
||||
if (this.trace) utils.logObject("remote: account: %s", message);
|
||||
break;
|
||||
|
||||
// All other messages
|
||||
default:
|
||||
unexpected = true;
|
||||
if (this.trace) utils.logObject("remote: "+message.type+": %s", message);
|
||||
this.emit('net_'+message.type, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -584,51 +581,71 @@ Remote.prototype.request_ledger_entry = function (type) {
|
||||
var self = this;
|
||||
var request = new Request(this, 'ledger_entry');
|
||||
|
||||
if (type)
|
||||
this.type = type;
|
||||
|
||||
// Transparent caching. When .request() is invoked, look in the Remote object for the result.
|
||||
// If not found, listen, cache result, and emit it.
|
||||
//
|
||||
// Transparent caching:
|
||||
this.request_default = this.request;
|
||||
this.request = function () { // Intercept default request.
|
||||
if (self._ledger_hash) {
|
||||
// XXX Add caching.
|
||||
}
|
||||
// else if (req.ledger_index)
|
||||
// else if ('ripple_state' === this.type) // YYY Could be cached per ledger.
|
||||
else if ('account_root' === this.type) {
|
||||
var cache = self.ledgers.current.account_root;
|
||||
if ('account_root' === type) {
|
||||
request.request_default = request.request;
|
||||
|
||||
if (!cache)
|
||||
{
|
||||
cache = self.ledgers.current.account_root = {};
|
||||
request.request = function () { // Intercept default request.
|
||||
var bDefault = true;
|
||||
// .self = Remote
|
||||
// this = Request
|
||||
|
||||
// console.log('request_ledger_entry: caught');
|
||||
|
||||
|
||||
if (self._ledger_hash) {
|
||||
// A specific ledger is requested.
|
||||
|
||||
// XXX Add caching.
|
||||
}
|
||||
// else if (req.ledger_index)
|
||||
// else if ('ripple_state' === request.type) // YYY Could be cached per ledger.
|
||||
else if ('account_root' === type) {
|
||||
var cache = self.ledgers.current.account_root;
|
||||
|
||||
var node = self.ledgers.current.account_root[request.message.account_root];
|
||||
|
||||
if (node) {
|
||||
// Emulate fetch of ledger entry.
|
||||
self.emit('success', {
|
||||
// YYY Missing lots of fields.
|
||||
'node' : node,
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Was not cached.
|
||||
|
||||
// XXX Only allow with trusted mode. Must sync response with advance.
|
||||
switch (response.type) {
|
||||
case 'account_root':
|
||||
request.on('success', function (message) {
|
||||
// Cache node.
|
||||
self.ledgers.current.account_root[message.node.Account] = message.node;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
// This type not cached.
|
||||
if (!cache)
|
||||
{
|
||||
cache = self.ledgers.current.account_root = {};
|
||||
}
|
||||
|
||||
this.request_default();
|
||||
var node = self.ledgers.current.account_root[request.message.account_root];
|
||||
|
||||
if (node) {
|
||||
// Emulate fetch of ledger entry.
|
||||
// console.log('request_ledger_entry: emulating');
|
||||
request.emit('success', {
|
||||
// YYY Missing lots of fields.
|
||||
'node' : node,
|
||||
});
|
||||
|
||||
bDefault = false;
|
||||
}
|
||||
else {
|
||||
// Was not cached.
|
||||
|
||||
// XXX Only allow with trusted mode. Must sync response with advance.
|
||||
switch (type) {
|
||||
case 'account_root':
|
||||
request.on('success', function (message) {
|
||||
// Cache node.
|
||||
// console.log('request_ledger_entry: caching');
|
||||
self.ledgers.current.account_root[message.node.Account] = message.node;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
// This type not cached.
|
||||
// console.log('request_ledger_entry: non-cached type');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bDefault) {
|
||||
// console.log('request_ledger_entry: invoking');
|
||||
request.request_default();
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1479,7 +1496,7 @@ Transaction.prototype.wallet_add = function (src, amount, authorized_key, public
|
||||
this.secret = this._account_secret(src);
|
||||
this.tx_json.TransactionType = 'WalletAdd';
|
||||
this.tx_json.Amount = Amount.json_rewrite(amount);
|
||||
this.tx_json.RegularKey = authorized_key;
|
||||
this.tx_json.RegularKey = authorized_key;
|
||||
this.tx_json.PublicKey = public_key;
|
||||
this.tx_json.Signature = signature;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user