示例#1
0
static short oppointlevel (Point pt) {
	
	/*
	give me a point in the display rectangle, and I'll tell you what 
	level it refers to.
	
	to visualize the process, draw vertical lines on the screen at 
	(**outlinedata).lineindent increments.  each line corresponds to a
	level in the outline.  we match the pt up with the levels.
	*/
	
	register hdloutlinerecord ho = outlinedata;
	hdlheadrecord hsummit = (**ho).hsummit;
	Rect r = (**ho).outlinerect;
	short indent;
	
	insetrect (&r, -draggingsloppix, -draggingsloppix);
	
	if (!pointinrect (pt, r)) /*too sloppy -- no move*/
		return (-1);
	
	indent = (pt.h - (**ho).outlinerect.left) - (opnodeindent (hsummit) + dragginghotspot);
	
	return ((**hsummit).headlevel + divround (indent, (**ho).lineindent));
	} /*oppointlevel*/
ter pathcursor::delivernodeforward (
    account const& uinaccountid,    // --> input owner's account.
    stamount const& sainreq,        // --> amount to deliver.
    stamount& sainact,              // <-- amount delivered, this invocation.
    stamount& sainfees) const       // <-- fees charged, this invocation.
{
    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;

    // 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 > calc_node_deliver_max_loops)
        {
            writelog (lswarning, ripplecalc)
                << "delivernodeforward: max loops cndf";
            return telfailed_processing;
        }

        // determine values for pass to adjust sainact, sainfees, and
        // node().safwddeliver.
        advancenode (sainact, false);

        // if needed, advance to next funded offer.

        if (resultcode != tessuccess)
        {
        }
        else if (!node().offerindex_)
        {
            writelog (lswarning, ripplecalc)
                << "delivernodeforward: internal error: ran out of offers.";
            return telfailed_processing;
        }
        else if (resultcode == tessuccess)
        {
            // doesn't charge input. input funds are in limbo.
            // there's no fee if we're transferring xrp, if the sender is the
            // issuer, or if the receiver is the issuer.
            bool nofee = isnative (previousnode().issue_)
                || uinaccountid == previousnode().issue_.account
                || node().offerowneraccount_ == previousnode().issue_.account;
            const stamount sainfeerate = nofee ? saone
                : previousnode().transferrate_;  // transfer rate of issuer.

            // 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,
                true);

            // offer maximum in with fees.
            auto saintotal = mulround (sainfunded, sainfeerate, true);
            auto sainremaining = sainreq - sainact - sainfees;

            if (sainremaining < zero)
                sainremaining.clear();

            // in limited by remaining.
            auto sainsum = std::min (saintotal, sainremaining);

            // in without fees.
            auto sainpassact = std::min (
                node().satakerpays, divround (
                    sainsum, sainfeerate, true));

            // out limited by in remaining.
            auto outpass = divround (
                sainpassact, node().saofrrate, node().satakergets, 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;

            writelog (lstrace, ripplecalc)
                << "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 <= zero)
            {
                writelog (lsdebug, ripplecalc)
                    << "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.
                writelog (lswarning, ripplecalc)
                    << "delivernodeforward: unreachable reached";

                // after math offer is effectively unfunded.
                pathstate_.unfundedoffers().push_back (node().offerindex_);
                node().bentryadvance = true;
                continue;
            }

            if (!isnative(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;

                writelog (lstrace, ripplecalc)
                    << "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 = ledger().accountsend (
                    node().offerowneraccount_,
                    nextnode().account_,
                    saoutpassact);

                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.

                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, true);
                    sainpassact = std::min (node().satakerpays, inpassact);
                    auto inpassfees = mulround (
                        sainpassact, sainfeerate, 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() : (isvbc(node().issue_) ? vbcaccount() : node().issue_.account);
                auto outpasstotal = saoutpassact + saoutpassfees;
                ledger().accountsend (
                    node().offerowneraccount_,
                    id,
                    outpasstotal);

                writelog (lstrace, ripplecalc)
                    << "delivernodeforward: ? --> offer --> offer:"
                    << " saoutpassact=" << saoutpassact
                    << " saoutpassfees=" << saoutpassfees;
            }

            writelog (lstrace, ripplecalc)
                << "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 (isnative (previousnode().issue_.currency)
                || uinaccountid != node().offerowneraccount_)
            {
				auto id = !isxrp(previousnode().issue_.currency) ?
                        (isvbc(previousnode().issue_.currency) ? vbcaccount() : uinaccountid) : xrpaccount();

                resultcode = ledger().accountsend (
                    id,
                    node().offerowneraccount_,
                    sainpassact);

                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 < zero || satakergetsnew < zero)
            {
                writelog (lswarning, ripplecalc)
                    << "delivernodeforward: negative:"
                    << " satakerpaysnew=" << satakerpaysnew
                    << " satakergetsnew=" << satakergetsnew;

                resultcode = telfailed_processing;
                break;
            }

            node().sleoffer->setfieldamount (sftakergets, satakergetsnew);
            node().sleoffer->setfieldamount (sftakerpays, satakerpaysnew);

            ledger().entrymodify (node().sleoffer);

            if (saoutpassact == saoutfunded || satakergetsnew == zero)
            {
                // offer became unfunded.

                writelog (lswarning, ripplecalc)
                    << "delivernodeforward: unfunded:"
                    << " saoutpassact=" << saoutpassact
                    << " saoutfunded=" << saoutfunded;

                pathstate_.unfundedoffers().push_back (node().offerindex_);
                node().bentryadvance   = true;
            }
            else
            {
                condlog (saoutpassact >= saoutfunded, lswarning, ripplecalc)
                    << "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);
        }
    }

    writelog (lstrace, ripplecalc)
        << "delivernodeforward<"
        << " nodeindex_=" << nodeindex_
        << " sainact=" << sainact
        << " sainfees=" << sainfees;

    return resultcode;
}