Exemplo n.º 1
0
TER PathCursor::advanceNode (STAmount const& amount, bool reverse) const
{
    bool multi = multiQuality_ || amount == zero;

    // If the multiQuality_ is unchanged, use the PathCursor we're using now.
    if (multi == multiQuality_)
        return advanceNode (reverse);

    // Otherwise, use a new PathCursor with the new multiQuality_.
    PathCursor withMultiQuality {rippleCalc_, pathState_, multi, nodeIndex_};
    return withMultiQuality.advanceNode (reverse);
}
Exemplo n.º 2
0
TER PathCursor::advanceNode (STAmount const& amount, bool reverse, bool callerHasLiquidity) const
{
    bool const multi = fix1141 (view ().info ().parentCloseTime)
        ? (multiQuality_ || (!callerHasLiquidity && amount == beast::zero))
        : (multiQuality_ || amount == beast::zero);

    // If the multiQuality_ is unchanged, use the PathCursor we're using now.
    if (multi == multiQuality_)
        return advanceNode (reverse);

    // Otherwise, use a new PathCursor with the new multiQuality_.
    PathCursor withMultiQuality {rippleCalc_, pathState_, multi, j_, nodeIndex_};
    return withMultiQuality.advanceNode (reverse);
}
Exemplo n.º 3
0
typename SortedList<T, Pred>::Node* SortedList<T, Pred>::merge_sort_helper(typename SortedList<T, Pred>::Node* current, unsigned chunk_size, const Sorter &sorter)
{
	if (current != tail_ && chunk_size > 1)
	{
		SortedList<T, Pred>::Node* temp = current;
		advanceNode(temp, chunk_size / 2);
		// Mergesort the left, mergesort the right, and merge the results!
		return(merge_lists(merge_sort_helper(current, chunk_size / 2, sorter),
				   chunk_size / 2,
				   merge_sort_helper(temp, chunk_size - (chunk_size / 2), sorter),
				   chunk_size - (chunk_size / 2),
				   sorter));
	}
	
	return(current);
}
Exemplo n.º 4
0
int main(int argc, char* argv[])
{
	/* Declarations */
	List list;
	List list_1;
	List list_2;
	List list_3;
	List list_4;
	List list_5;
	List list_6;
	Position pos;
	Position pos_1;
	Position pos_2;
	Position pos_3;
	Position pos_4;
	Position pos_a, pos_b;
	int len;
	int idx;
	ElementType elem;

	/* Initialize list */
	list=createList();

	/* Test functions 'insertNode' and 'appendNode' */
	printf("Test functions 'insertNode' and 'appendNode'\n\n");
	printf("Before 'insertNode':\n");
	printList(list);
	pos_1=getHeaderNode(list);
	insertNode(11, list, pos_1);
	pos_2=advanceNode(pos_1);
	insertNode(2, list, pos_2);
	pos_3=advanceNode(pos_2);
	insertNode(3, list, pos_3);
	pos_4=advanceNode(pos_3);
	insertNode(10, list, pos_4);
	insertNode(9, list, pos_2);
	printf("After 'insertNode':\n");
	printList(list);
	printf("Before 'appendNode':\n");
	printList(list);
	appendNode(7, list);
	appendNode(2, list);
	printf("After 'appendNode'\n");
	printList(list);
	printf("\n");

	/* Test functions 'cloneList', 'deleteNode' and 'deleteList' */
	printf("Test functions 'cloneList', 'deleteNode' and 'deleteList'\n\n");
	list_1=cloneList(list);
	printf("Before 'deleteNode':\n");
	printList(list_1);
	deleteNode(2, list_1);
	printf("After 'deleteNode':\n");
	printList(list_1);
	printf("Before 'deleteList':\n");
	printList(list_1);
	deleteList(list_1);
	printf("After 'deleteList':\n");
	printList(list_1);
	printf("\n");

	/* Test function 'getListLength' */
	printf("Test function 'getListLength'\n\n");
	len=getListLength(list);
	printf("Length: %d\n", len);
	printf("\n");

	/* Test functions 'findNode', 'findNodePrevious' and 'getNodeIndex' */
	printf("Test functions 'findNode', 'findNodePrevious' and 'getNodeIndex'\n\n");
	elem=2;
	pos=findNode(elem, list);
	if(pos!=NULL)
	{
		idx=getNodeIndex(pos, list);
		printList(list);
		printf("finding %d, Element at index %d found\n", elem, idx);
	}
	else
	{
		printf("finding %d, not found\n", elem);
	}
	elem=3;
	pos=findNodePrevious(elem, list);
	/* Check whether elem is found in list */
	if(pos->m_next!=NULL)
	{
		idx=getNodeIndex(pos, list);
		printf("finding previous element of %d, Element at index %d found\n", elem, idx);
	}
	else
	{
		/* elem is not in list */
		printf("finding previous element of %d, not found\n", elem);
	}
	printf("\n");

	/* Test functions 'makeListEmpty' and 'isListEmpty' */
	printf("Test functions 'makeListEmpty' and 'isListEmpty'\n\n");
	list_2=cloneList(list);
	printf("Before 'makeListEmpty':\n");
	printList(list_2);
	list_2=makeListEmpty(list_2);
	if(isListEmpty(list_2))
	{
		printf("List emptied successfully\n");
		printf("After 'makeListEmpty'\n");
		printList(list_2);
	}
	printf("\n");

	/* Test functions 'getHeaderNode', 'getFirstNode', 'getLastNode', 'advanceNode' and 'getNodeElem' */
	printf("Test functions 'getHeaderNode', 'getFirstNode', 'getLastNode', 'advanceNode' and 'getNodeElem'\n\n");
	printList(list);
	pos=getHeaderNode(list);
	printf("Header at index %d\n", getNodeIndex(pos, list));
	pos=getFirstNode(list);
	printf("First element at index %d have value %d\n", getNodeIndex(pos, list), getNodeElem(pos));
	pos=getLastNode(list);
	printf("Last element at index %d have value %d\n", getNodeIndex(pos, list), getNodeElem(pos));
	pos=getFirstNode(list);
	pos=advanceNode(pos);
	printf("Second element at index %d have value %d\n", getNodeIndex(pos, list), getNodeElem(pos));
	printf("\n");

	/* Test function 'reverseList' */
	printf("Test function 'reverseList'\n\n");
	list_3=cloneList(list);
	printf("Before 'reverseList':\n");
	printList(list_3);
	list_3=reverseList(list_3);
	printf("After 'reverseList':\n");
	printList(list_3);
	printf("\n");

	/* Test function 'swapNode' */
	printf("Test function 'swapNode'\n\n");
	list_4=cloneList(list);
	printf("Before 'swapNode':\n");
	printList(list_4);
	pos_a=getHeaderNode(list_4);
	pos_a=advanceNode(pos_a);
	pos_a=advanceNode(pos_a);
	pos_b=advanceNode(pos_a);
	swapNode(pos_a, pos_b, list_4);
	printf("After 'swapNode':\n");
	printList(list_4);
	printf("\n");

	/* Test function 'bubbleSortList' */
	printf("Test function 'bubbleSortList'\n\n");
	list_5=cloneList(list);
	printf("Before 'bubbleSortList':\n");
	printList(list_5);
	bubbleSortList(list_5);
	printf("After 'bubbleSortList':\n");
	printList(list_5);
	printf("\n");

	/* Test function 'connectList' */
	printf("Test function 'connectList'\n\n");
	printf("List 1:\n");
	printList(list);
	printf("Length: %d\n", getListLength(list));
	printf("List 2:\n");
	printList(list_5);
	printf("Length: %d\n", getListLength(list_5));
	list_6=connectList(list, list_5);
	printf("Connected list:\n");
	printList(list_6);
	printf("Length: %d\n", getListLength(list_6));
	printf("\n");

	/* Cleanup memory */
	destroyList(list);
	destroyList(list_1);
	destroyList(list_2);
	destroyList(list_3);
	destroyList(list_4);
	destroyList(list_5);
	destroyList(list_6);

	return 0;
}
Exemplo n.º 5
0
TER PathCursor::deliverNodeForward (
    AccountID const& uInAccountID,    // --> Input owner's account.
    STAmount const& saInReq,        // --> Amount to deliver.
    STAmount& saInAct,              // <-- Amount delivered, this invocation.
    STAmount& saInFees,             // <-- Fees charged, this invocation.
    bool callerHasLiquidity) const
{
    TER resultCode   = tesSUCCESS;

    // Don't deliver more than wanted.
    // Zeroed in reverse pass.
    node().directory.restart(multiQuality_);

    saInAct.clear (saInReq);
    saInFees.clear (saInReq);

    int loopCount = 0;
    auto viewJ = rippleCalc_.logs_.journal ("View");

    // XXX Perhaps make sure do not exceed node().saRevDeliver as another way to
    // stop?
    while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq)
    {
        // Did not spend all inbound deliver funds.
        if (++loopCount >
            (multiQuality_ ?
                CALC_NODE_DELIVER_MAX_LOOPS_MQ :
                CALC_NODE_DELIVER_MAX_LOOPS))
        {
            JLOG (j_.warn())
                << "deliverNodeForward: max loops cndf";
            return telFAILED_PROCESSING;
        }

        // Determine values for pass to adjust saInAct, saInFees, and
        // node().saFwdDeliver.
        advanceNode (saInAct, false, callerHasLiquidity);

        // If needed, advance to next funded offer.

        if (resultCode != tesSUCCESS)
        {
        }
        else if (!node().offerIndex_)
        {
            JLOG (j_.warn())
                << "deliverNodeForward: INTERNAL ERROR: Ran out of offers.";
            return telFAILED_PROCESSING;
        }
        else if (resultCode == tesSUCCESS)
        {
            auto const xferRate = effectiveRate (
                previousNode().issue_,
                uInAccountID,
                node().offerOwnerAccount_,
                previousNode().transferRate_);

            // First calculate assuming no output fees: saInPassAct,
            // saInPassFees, saOutPassAct.

            // Offer maximum out - limited by funds with out fees.
            auto saOutFunded = std::min (
                node().saOfferFunds, node().saTakerGets);

            // Offer maximum out - limit by most to deliver.
            auto saOutPassFunded = std::min (
                saOutFunded,
                node().saRevDeliver - node().saFwdDeliver);

            // Offer maximum in - Limited by by payout.
            auto saInFunded = mulRound (
                saOutPassFunded,
                node().saOfrRate,
                node().saTakerPays.issue (),
                true);

            // Offer maximum in with fees.
            auto saInTotal = multiplyRound (
                saInFunded, xferRate, true);
            auto saInRemaining = saInReq - saInAct - saInFees;

            if (saInRemaining < beast::zero)
                saInRemaining.clear();

            // In limited by remaining.
            auto saInSum = std::min (saInTotal, saInRemaining);

            // In without fees.
            auto saInPassAct = std::min (
                node().saTakerPays,
                divideRound (saInSum, xferRate, true));

            // Out limited by in remaining.
            auto outPass = divRound (
                saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true);
            STAmount saOutPassMax    = std::min (saOutPassFunded, outPass);

            STAmount saInPassFeesMax = saInSum - saInPassAct;

            // Will be determined by next node().
            STAmount saOutPassAct;

            // Will be determined by adjusted saInPassAct.
            STAmount saInPassFees;

            JLOG (j_.trace())
                << "deliverNodeForward:"
                << " nodeIndex_=" << nodeIndex_
                << " saOutFunded=" << saOutFunded
                << " saOutPassFunded=" << saOutPassFunded
                << " node().saOfferFunds=" << node().saOfferFunds
                << " node().saTakerGets=" << node().saTakerGets
                << " saInReq=" << saInReq
                << " saInAct=" << saInAct
                << " saInFees=" << saInFees
                << " saInFunded=" << saInFunded
                << " saInTotal=" << saInTotal
                << " saInSum=" << saInSum
                << " saInPassAct=" << saInPassAct
                << " saOutPassMax=" << saOutPassMax;

            // FIXME: We remove an offer if WE didn't want anything out of it?
            if (!node().saTakerPays || saInSum <= beast::zero)
            {
                JLOG (j_.debug())
                    << "deliverNodeForward: Microscopic offer unfunded.";

                // After math offer is effectively unfunded.
                pathState_.unfundedOffers().push_back (node().offerIndex_);
                node().bEntryAdvance = true;
                continue;
            }

            if (!saInFunded)
            {
                // Previous check should catch this.
                JLOG (j_.warn())
                    << "deliverNodeForward: UNREACHABLE REACHED";

                // After math offer is effectively unfunded.
                pathState_.unfundedOffers().push_back (node().offerIndex_);
                node().bEntryAdvance = true;
                continue;
            }

            if (!isXRP(nextNode().account_))
            {
                // ? --> OFFER --> account
                // Input fees: vary based upon the consumed offer's owner.
                // Output fees: none as XRP or the destination account is the
                // issuer.

                saOutPassAct = saOutPassMax;
                saInPassFees = saInPassFeesMax;

                JLOG (j_.trace())
                    << "deliverNodeForward: ? --> OFFER --> account:"
                    << " offerOwnerAccount_="
                    << node().offerOwnerAccount_
                    << " nextNode().account_="
                    << nextNode().account_
                    << " saOutPassAct=" << saOutPassAct
                    << " saOutFunded=" << saOutFunded;

                // Output: Debit offer owner, send XRP or non-XPR to next
                // account.
                resultCode = accountSend(view(),
                    node().offerOwnerAccount_,
                    nextNode().account_,
                    saOutPassAct, viewJ);

                if (resultCode != tesSUCCESS)
                    break;
            }
            else
            {
                // ? --> OFFER --> offer
                //
                // Offer to offer means current order book's output currency and
                // issuer match next order book's input current and issuer.
                //
                // Output fees: possible if issuer has fees and is not on either
                // side.
                STAmount saOutPassFees;

                // Output fees vary as the next nodes offer owners may vary.
                // Therefore, immediately push through output for current offer.
                resultCode = increment().deliverNodeForward (
                    node().offerOwnerAccount_,  // --> Current holder.
                    saOutPassMax,             // --> Amount available.
                    saOutPassAct,             // <-- Amount delivered.
                    saOutPassFees,            // <-- Fees charged.
                    saInAct > beast::zero);

                if (resultCode != tesSUCCESS)
                    break;

                if (saOutPassAct == saOutPassMax)
                {
                    // No fees and entire output amount.

                    saInPassFees = saInPassFeesMax;
                }
                else
                {
                    // Fraction of output amount.
                    // Output fees are paid by offer owner and not passed to
                    // previous.

                    assert (saOutPassAct < saOutPassMax);
                    auto inPassAct = mulRound (
                        saOutPassAct, node().saOfrRate, saInReq.issue (), true);
                    saInPassAct = std::min (node().saTakerPays, inPassAct);
                    auto inPassFees = multiplyRound (
                        saInPassAct, xferRate, true);
                    saInPassFees    = std::min (saInPassFeesMax, inPassFees);
                }

                // Do outbound debiting.
                // Send to issuer/limbo total amount including fees (issuer gets
                // fees).
                auto const& id = isXRP(node().issue_) ?
                        xrpAccount() : node().issue_.account;
                auto outPassTotal = saOutPassAct + saOutPassFees;
                accountSend(view(),
                    node().offerOwnerAccount_,
                    id,
                    outPassTotal,
                    viewJ);

                JLOG (j_.trace())
                    << "deliverNodeForward: ? --> OFFER --> offer:"
                    << " saOutPassAct=" << saOutPassAct
                    << " saOutPassFees=" << saOutPassFees;
            }

            JLOG (j_.trace())
                << "deliverNodeForward: "
                << " nodeIndex_=" << nodeIndex_
                << " node().saTakerGets=" << node().saTakerGets
                << " node().saTakerPays=" << node().saTakerPays
                << " saInPassAct=" << saInPassAct
                << " saInPassFees=" << saInPassFees
                << " saOutPassAct=" << saOutPassAct
                << " saOutFunded=" << saOutFunded;

            // Funds were spent.
            node().bFundsDirty = true;

            // Do inbound crediting.
            //
            // Credit offer owner from in issuer/limbo (input transfer fees left
            // with owner).  Don't attempt to have someone credit themselves, it
            // is redundant.
            if (isXRP (previousNode().issue_.currency)
                || uInAccountID != node().offerOwnerAccount_)
            {
                auto id = !isXRP(previousNode().issue_.currency) ?
                        uInAccountID : xrpAccount();
                resultCode = accountSend(view(),
                    id,
                    node().offerOwnerAccount_,
                    saInPassAct,
                    viewJ);

                if (resultCode != tesSUCCESS)
                    break;
            }

            // Adjust offer.
            //
            // Fees are considered paid from a seperate budget and are not named
            // in the offer.
            STAmount saTakerGetsNew  = node().saTakerGets - saOutPassAct;
            STAmount saTakerPaysNew  = node().saTakerPays - saInPassAct;

            if (saTakerPaysNew < beast::zero || saTakerGetsNew < beast::zero)
            {
                JLOG (j_.warn())
                    << "deliverNodeForward: NEGATIVE:"
                    << " saTakerPaysNew=" << saTakerPaysNew
                    << " saTakerGetsNew=" << saTakerGetsNew;

                resultCode = telFAILED_PROCESSING;
                break;
            }

            node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
            node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);

            view().update (node().sleOffer);

            if (saOutPassAct == saOutFunded || saTakerGetsNew == beast::zero)
            {
                // Offer became unfunded.

                JLOG (j_.debug())
                    << "deliverNodeForward: unfunded:"
                    << " saOutPassAct=" << saOutPassAct
                    << " saOutFunded=" << saOutFunded;

                pathState_.unfundedOffers().push_back (node().offerIndex_);
                node().bEntryAdvance   = true;
            }
            else
            {
                if (saOutPassAct >= saOutFunded)
                {
                    JLOG (j_.warn())
                        << "deliverNodeForward: TOO MUCH:"
                        << " saOutPassAct=" << saOutPassAct
                        << " saOutFunded=" << saOutFunded;
                }

                assert (saOutPassAct < saOutFunded);
            }

            saInAct += saInPassAct;
            saInFees += saInPassFees;

            // Adjust amount available to next node().
            node().saFwdDeliver = std::min (node().saRevDeliver,
                                        node().saFwdDeliver + saOutPassAct);
        }
    }

    JLOG (j_.trace())
        << "deliverNodeForward<"
        << " nodeIndex_=" << nodeIndex_
        << " saInAct=" << saInAct
        << " saInFees=" << saInFees;

    return resultCode;
}
Exemplo n.º 6
0
// To deliver from an order book, when computing
TER PathCursor::deliverNodeReverseImpl (
    AccountID const& uOutAccountID, // --> Output owner's account.
    STAmount const& saOutReq,       // --> Funds requested to be
                                    // delivered for an increment.
    STAmount& saOutAct,             // <-- Funds actually delivered for an
                                    // increment
    bool callerHasLiquidity
                                        ) const
{
    TER resultCode   = tesSUCCESS;

    // Accumulation of what the previous node must deliver.
    // Possible optimization: Note this gets zeroed on each increment, ideally
    // only on first increment, then it could be a limit on the forward pass.
    saOutAct.clear (saOutReq);

    JLOG (j_.trace())
        << "deliverNodeReverse>"
        << " saOutAct=" << saOutAct
        << " saOutReq=" << saOutReq
        << " saPrvDlvReq=" << previousNode().saRevDeliver;

    assert (saOutReq != zero);

    int loopCount = 0;
    auto viewJ = rippleCalc_.logs_.journal ("View");

    // While we did not deliver as much as requested:
    while (saOutAct < saOutReq)
    {
        if (++loopCount >
            (multiQuality_ ?
                CALC_NODE_DELIVER_MAX_LOOPS_MQ :
                CALC_NODE_DELIVER_MAX_LOOPS))
        {
            JLOG (j_.warn()) << "loop count exceeded";
            return telFAILED_PROCESSING;
        }

        resultCode = advanceNode (saOutAct, true, callerHasLiquidity);
        // If needed, advance to next funded offer.

        if (resultCode != tesSUCCESS || !node().offerIndex_)
            // Error or out of offers.
            break;

        auto const xferRate = effectiveRate (
            node().issue_,
            uOutAccountID,
            node().offerOwnerAccount_,
            node().transferRate_);

        JLOG (j_.trace())
            << "deliverNodeReverse:"
            << " offerOwnerAccount_=" << node().offerOwnerAccount_
            << " uOutAccountID=" << uOutAccountID
            << " node().issue_.account=" << node().issue_.account
            << " xferRate=" << xferRate;

        // Only use rate when not in multi-quality mode
        if (!multiQuality_)
        {
            if (!node().rateMax)
            {
                // Set initial rate.
                JLOG (j_.trace())
                    << "Set initial rate";

                node().rateMax = xferRate;
            }
            else if (xferRate > node().rateMax)
            {
                // Offer exceeds initial rate.
                JLOG (j_.trace())
                    << "Offer exceeds initial rate: " << *node().rateMax;

                break;  // Done. Don't bother looking for smaller transferRates.
            }
            else if (xferRate < node().rateMax)
            {
                // Reducing rate. Additional offers will only
                // be considered for this increment if they
                // are at least this good.
                //
                // At this point, the overall rate is reducing,
                // while the overall rate is not xferRate, it
                // would be wrong to add anything with a rate
                // above xferRate.
                //
                // The rate would be reduced if the current
                // offer was from the issuer and the previous
                // offer wasn't.

                JLOG (j_.trace())
                    << "Reducing rate: " << *node().rateMax;

                node().rateMax = xferRate;
            }
        }

        // Amount that goes to the taker.
        STAmount saOutPassReq = std::min (
            std::min (node().saOfferFunds, node().saTakerGets),
            saOutReq - saOutAct);

        // Maximum out - assuming no out fees.
        STAmount saOutPassAct = saOutPassReq;

        // Amount charged to the offer owner.
        //
        // The fee goes to issuer. The fee is paid by offer owner and not passed
        // as a cost to taker.
        //
        // Round down: prefer liquidity rather than microscopic fees.
        STAmount saOutPlusFees   = multiplyRound (
            saOutPassAct, xferRate, false);


        // Offer out with fees.

        JLOG (j_.trace())
            << "deliverNodeReverse:"
            << " saOutReq=" << saOutReq
            << " saOutAct=" << saOutAct
            << " node().saTakerGets=" << node().saTakerGets
            << " saOutPassAct=" << saOutPassAct
            << " saOutPlusFees=" << saOutPlusFees
            << " node().saOfferFunds=" << node().saOfferFunds;

        if (saOutPlusFees > node().saOfferFunds)
        {
            // Offer owner can not cover all fees, compute saOutPassAct based on
            // node().saOfferFunds.
            saOutPlusFees   = node().saOfferFunds;

            // Round up: prefer liquidity rather than microscopic fees. But,
            // limit by requested.
            auto fee = divideRound (saOutPlusFees, xferRate, true);
            saOutPassAct = std::min (saOutPassReq, fee);

            JLOG (j_.trace())
                << "deliverNodeReverse: Total exceeds fees:"
                << " saOutPassAct=" << saOutPassAct
                << " saOutPlusFees=" << saOutPlusFees
                << " node().saOfferFunds=" << node().saOfferFunds;
        }

        // Compute portion of input needed to cover actual output.
        auto outputFee = mulRound (
            saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true);
        if (*stAmountCalcSwitchover == false && ! outputFee)
        {
            JLOG (j_.fatal())
                << "underflow computing outputFee "
                << "saOutPassAct: " << saOutPassAct
                << " saOfrRate: " << node ().saOfrRate;
            return telFAILED_PROCESSING;
        }
        STAmount saInPassReq = std::min (node().saTakerPays, outputFee);
        STAmount saInPassAct;

        JLOG (j_.trace())
            << "deliverNodeReverse:"
            << " outputFee=" << outputFee
            << " saInPassReq=" << saInPassReq
            << " node().saOfrRate=" << node().saOfrRate
            << " saOutPassAct=" << saOutPassAct
            << " saOutPlusFees=" << saOutPlusFees;

        if (!saInPassReq) // FIXME: This is bogus
        {
            // After rounding did not want anything.
            JLOG (j_.debug())
                << "deliverNodeReverse: micro offer is unfunded.";

            node().bEntryAdvance   = true;
            continue;
        }
        // Find out input amount actually available at current rate.
        else if (!isXRP(previousNode().account_))
        {
            // account --> OFFER --> ?
            // Due to node expansion, previous is guaranteed to be the issuer.
            //
            // Previous is the issuer and receiver is an offer, so no fee or
            // quality.
            //
            // Previous is the issuer and has unlimited funds.
            //
            // Offer owner is obtaining IOUs via an offer, so credit line limits
            // are ignored.  As limits are ignored, don't need to adjust
            // previous account's balance.

            saInPassAct = saInPassReq;

            JLOG (j_.trace())
                << "deliverNodeReverse: account --> OFFER --> ? :"
                << " saInPassAct=" << saInPassAct;
        }
        else
        {
            // offer --> OFFER --> ?
            // Compute in previous offer node how much could come in.

            // TODO(tom): Fix nasty recursion here!
            resultCode = increment(-1).deliverNodeReverseImpl(
                node().offerOwnerAccount_,
                saInPassReq,
                saInPassAct,
                saOutAct > zero);

            if (amendmentRIPD1141(view().info().parentCloseTime))
            {
                // The recursive call is dry this time, but we have liquidity
                // from previous calls
                if (resultCode == tecPATH_DRY && saOutAct > zero)
                {
                    resultCode = tesSUCCESS;
                    break;
                }
            }

            JLOG (j_.trace())
                << "deliverNodeReverse: offer --> OFFER --> ? :"
                << " saInPassAct=" << saInPassAct;
        }

        if (resultCode != tesSUCCESS)
            break;

        if (saInPassAct < saInPassReq)
        {
            // Adjust output to conform to limited input.
            auto outputRequirements = divRound (saInPassAct, node ().saOfrRate,
                node ().saTakerGets.issue (), true);
            saOutPassAct = std::min (saOutPassReq, outputRequirements);
            auto outputFees = multiplyRound (saOutPassAct, xferRate, true);
            saOutPlusFees   = std::min (node().saOfferFunds, outputFees);

            JLOG (j_.trace())
                << "deliverNodeReverse: adjusted:"
                << " saOutPassAct=" << saOutPassAct
                << " saOutPlusFees=" << saOutPlusFees;
        }
        else
        {
            // TODO(tom): more logging here.
            assert (saInPassAct == saInPassReq);
        }

        // Funds were spent.
        node().bFundsDirty = true;

        // Want to deduct output to limit calculations while computing reverse.
        // Don't actually need to send.
        //
        // Sending could be complicated: could fund a previous offer not yet
        // visited.  However, these deductions and adjustments are tenative.
        //
        // Must reset balances when going forward to perform actual transfers.
        resultCode   = accountSend(view(),
            node().offerOwnerAccount_, node().issue_.account, saOutPassAct, viewJ);

        if (resultCode != tesSUCCESS)
            break;

        // Adjust offer
        STAmount saTakerGetsNew  = node().saTakerGets - saOutPassAct;
        STAmount saTakerPaysNew  = node().saTakerPays - saInPassAct;

        if (saTakerPaysNew < zero || saTakerGetsNew < zero)
        {
            JLOG (j_.warn())
                << "deliverNodeReverse: NEGATIVE:"
                << " node().saTakerPaysNew=" << saTakerPaysNew
                << " node().saTakerGetsNew=" << saTakerGetsNew;

            resultCode = telFAILED_PROCESSING;
            break;
        }

        node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
        node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);

        view().update (node().sleOffer);

        if (saOutPassAct == node().saTakerGets)
        {
            // Offer became unfunded.
            JLOG (j_.debug())
                << "deliverNodeReverse: offer became unfunded.";

            node().bEntryAdvance   = true;
            // XXX When don't we want to set advance?
        }
        else
        {
            assert (saOutPassAct < node().saTakerGets);
        }

        saOutAct += saOutPassAct;
        // Accumulate what is to be delivered from previous node.
        previousNode().saRevDeliver += saInPassAct;
    }

    if (saOutAct > saOutReq)
    {
        JLOG (j_.warn())
            << "deliverNodeReverse: TOO MUCH:"
            << " saOutAct=" << saOutAct
            << " saOutReq=" << saOutReq;
    }

    assert(saOutAct <= saOutReq);

    if (resultCode == tesSUCCESS && !saOutAct)
        resultCode = tecPATH_DRY;
    // Unable to meet request, consider path dry.
    // Design invariant: if nothing was actually delivered, return tecPATH_DRY.

    JLOG (j_.trace())
        << "deliverNodeReverse<"
        << " saOutAct=" << saOutAct
        << " saOutReq=" << saOutReq
        << " saPrvDlvReq=" << previousNode().saRevDeliver;

    return resultCode;
}
Exemplo n.º 7
0
// To deliver from an order book, when computing
TER PathCursor::deliverNodeReverse (
    Account const& uOutAccountID,  // --> Output owner's account.
    STAmount const& saOutReq,      // --> Funds requested to be
                                   // delivered for an increment.
    STAmount& saOutAct) const      // <-- Funds actually delivered for an
                                   // increment.
{
    TER resultCode   = tesSUCCESS;

    node().directory.restart(multiQuality_);

    // Accumulation of what the previous node must deliver.
    // Possible optimization: Note this gets zeroed on each increment, ideally
    // only on first increment, then it could be a limit on the forward pass.
    saOutAct.clear (saOutReq);

    WriteLog (lsTRACE, RippleCalc)
        << "deliverNodeReverse>"
        << " saOutAct=" << saOutAct
        << " saOutReq=" << saOutReq
        << " saPrvDlvReq=" << previousNode().saRevDeliver;

    assert (saOutReq != zero);

    int loopCount = 0;

    // While we did not deliver as much as requested:
    while (saOutAct < saOutReq)
    {
        if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS)
        {
            WriteLog (lsFATAL, RippleCalc) << "loop count exceeded";
            return telFAILED_PROCESSING;
        }

        resultCode = advanceNode (saOutAct, true);
        // If needed, advance to next funded offer.

        if (resultCode != tesSUCCESS || !node().offerIndex_)
            // Error or out of offers.
            break;

        auto const hasFee = node().offerOwnerAccount_ == node().issue_.account
            || uOutAccountID == node().issue_.account;
        // Issuer sending or receiving.

        const STAmount saOutFeeRate = hasFee
            ? saOne             // No fee.
            : node().transferRate_;   // Transfer rate of issuer.

        WriteLog (lsTRACE, RippleCalc)
            << "deliverNodeReverse:"
            << " offerOwnerAccount_="
            << node().offerOwnerAccount_
            << " uOutAccountID="
            << uOutAccountID
            << " node().issue_.account="
            << node().issue_.account
            << " node().transferRate_=" << node().transferRate_
            << " saOutFeeRate=" << saOutFeeRate;

        if (multiQuality_)
        {
            // In multi-quality mode, ignore rate.
        }
        else if (!node().saRateMax)
        {
            // Set initial rate.
            node().saRateMax = saOutFeeRate;

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: Set initial rate:"
                << " node().saRateMax=" << node().saRateMax
                << " saOutFeeRate=" << saOutFeeRate;
        }
        else if (saOutFeeRate > node().saRateMax)
        {
            // Offer exceeds initial rate.
            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: Offer exceeds initial rate:"
                << " node().saRateMax=" << node().saRateMax
                << " saOutFeeRate=" << saOutFeeRate;

            break;  // Done. Don't bother looking for smaller transferRates.
        }
        else if (saOutFeeRate < node().saRateMax)
        {
            // Reducing rate. Additional offers will only considered for this
            // increment if they are at least this good.
            //
            // At this point, the overall rate is reducing, while the overall
            // rate is not saOutFeeRate, it would be wrong to add anything with
            // a rate above saOutFeeRate.
            //
            // The rate would be reduced if the current offer was from the
            // issuer and the previous offer wasn't.

            node().saRateMax   = saOutFeeRate;

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: Reducing rate:"
                << " node().saRateMax=" << node().saRateMax;
        }

        // Amount that goes to the taker.
        STAmount saOutPassReq = std::min (
            std::min (node().saOfferFunds, node().saTakerGets),
            saOutReq - saOutAct);

        // Maximum out - assuming no out fees.
        STAmount saOutPassAct = saOutPassReq;

        // Amount charged to the offer owner.
        //
        // The fee goes to issuer. The fee is paid by offer owner and not passed
        // as a cost to taker.
        //
        // Round down: prefer liquidity rather than microscopic fees.
        STAmount saOutPlusFees   = STAmount::mulRound (
            saOutPassAct, saOutFeeRate, false);
        // Offer out with fees.

        WriteLog (lsTRACE, RippleCalc)
            << "deliverNodeReverse:"
            << " saOutReq=" << saOutReq
            << " saOutAct=" << saOutAct
            << " node().saTakerGets=" << node().saTakerGets
            << " saOutPassAct=" << saOutPassAct
            << " saOutPlusFees=" << saOutPlusFees
            << " node().saOfferFunds=" << node().saOfferFunds;

        if (saOutPlusFees > node().saOfferFunds)
        {
            // Offer owner can not cover all fees, compute saOutPassAct based on
            // node().saOfferFunds.
            saOutPlusFees   = node().saOfferFunds;

            // Round up: prefer liquidity rather than microscopic fees. But,
            // limit by requested.
            auto fee = STAmount::divRound (saOutPlusFees, saOutFeeRate, true);
            saOutPassAct = std::min (saOutPassReq, fee);

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: Total exceeds fees:"
                << " saOutPassAct=" << saOutPassAct
                << " saOutPlusFees=" << saOutPlusFees
                << " node().saOfferFunds=" << node().saOfferFunds;
        }

        // Compute portion of input needed to cover actual output.
        auto outputFee = STAmount::mulRound (
            saOutPassAct, node().saOfrRate, node().saTakerPays, true);
        STAmount saInPassReq = std::min (node().saTakerPays, outputFee);
        STAmount saInPassAct;

        WriteLog (lsTRACE, RippleCalc)
            << "deliverNodeReverse:"
            << " outputFee=" << outputFee
            << " saInPassReq=" << saInPassReq
            << " node().saOfrRate=" << node().saOfrRate
            << " saOutPassAct=" << saOutPassAct
            << " saOutPlusFees=" << saOutPlusFees;

        if (!saInPassReq) // FIXME: This is bogus
        {
            // After rounding did not want anything.
            WriteLog (lsDEBUG, RippleCalc)
                << "deliverNodeReverse: micro offer is unfunded.";

            node().bEntryAdvance   = true;
            continue;
        }
        // Find out input amount actually available at current rate.
        else if (!isXRP(previousNode().account_))
        {
            // account --> OFFER --> ?
            // Due to node expansion, previous is guaranteed to be the issuer.
            //
            // Previous is the issuer and receiver is an offer, so no fee or
            // quality.
            //
            // Previous is the issuer and has unlimited funds.
            //
            // Offer owner is obtaining IOUs via an offer, so credit line limits
            // are ignored.  As limits are ignored, don't need to adjust
            // previous account's balance.

            saInPassAct = saInPassReq;

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: account --> OFFER --> ? :"
                << " saInPassAct=" << saInPassAct;
        }
        else
        {
            // offer --> OFFER --> ?
            // Compute in previous offer node how much could come in.

            // TODO(tom): Fix nasty recursion here!
            resultCode = increment(-1).deliverNodeReverse(
                node().offerOwnerAccount_,
                saInPassReq,
                saInPassAct);

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: offer --> OFFER --> ? :"
                << " saInPassAct=" << saInPassAct;
        }

        if (resultCode != tesSUCCESS)
            break;

        if (saInPassAct < saInPassReq)
        {
            // Adjust output to conform to limited input.
            auto outputRequirements = STAmount::divRound (
                saInPassAct, node().saOfrRate, node().saTakerGets, true);
            saOutPassAct = std::min (saOutPassReq, outputRequirements);
            auto outputFees = STAmount::mulRound (
                saOutPassAct, saOutFeeRate, true);
            saOutPlusFees   = std::min (node().saOfferFunds, outputFees);

            WriteLog (lsTRACE, RippleCalc)
                << "deliverNodeReverse: adjusted:"
                << " saOutPassAct=" << saOutPassAct
                << " saOutPlusFees=" << saOutPlusFees;
        }
        else
        {
            // TODO(tom): more logging here.
            assert (saInPassAct == saInPassReq);
        }

        // Funds were spent.
        node().bFundsDirty = true;

        // Want to deduct output to limit calculations while computing reverse.
        // Don't actually need to send.
        //
        // Sending could be complicated: could fund a previous offer not yet
        // visited.  However, these deductions and adjustments are tenative.
        //
        // Must reset balances when going forward to perform actual transfers.
        resultCode   = ledger().accountSend (
            node().offerOwnerAccount_, node().issue_.account, saOutPassAct);

        if (resultCode != tesSUCCESS)
            break;

        // Adjust offer
        STAmount saTakerGetsNew  = node().saTakerGets - saOutPassAct;
        STAmount saTakerPaysNew  = node().saTakerPays - saInPassAct;

        if (saTakerPaysNew < zero || saTakerGetsNew < zero)
        {
            WriteLog (lsWARNING, RippleCalc)
                << "deliverNodeReverse: NEGATIVE:"
                << " node().saTakerPaysNew=" << saTakerPaysNew
                << " node().saTakerGetsNew=%s" << saTakerGetsNew;

            resultCode = telFAILED_PROCESSING;
            break;
        }

        node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
        node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);

        ledger().entryModify (node().sleOffer);

        if (saOutPassAct == node().saTakerGets)
        {
            // Offer became unfunded.
            WriteLog (lsDEBUG, RippleCalc)
                << "deliverNodeReverse: offer became unfunded.";

            node().bEntryAdvance   = true;
            // XXX When don't we want to set advance?
        }
        else
        {
            assert (saOutPassAct < node().saTakerGets);
        }

        saOutAct += saOutPassAct;
        // Accumulate what is to be delivered from previous node.
        previousNode().saRevDeliver += saInPassAct;
    }

    CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc)
        << "deliverNodeReverse: TOO MUCH:"
        << " saOutAct=" << saOutAct
        << " saOutReq=" << saOutReq;

    assert (saOutAct <= saOutReq);

    if (resultCode == tesSUCCESS && !saOutAct)
        resultCode = tecPATH_DRY;
    // Unable to meet request, consider path dry.
    // Design invariant: if nothing was actually delivered, return tecPATH_DRY.

    WriteLog (lsTRACE, RippleCalc)
        << "deliverNodeReverse<"
        << " saOutAct=" << saOutAct
        << " saOutReq=" << saOutReq
        << " saPrvDlvReq=" << previousNode().saRevDeliver;

    return resultCode;
}