Example #1
0
/** Reset the context, discarding any changes made and adjust the fee */
XRPAmount
Transactor::reset(XRPAmount fee)
{
    ctx_.discard();

    auto const txnAcct = view().peek(
        keylet::account(ctx_.tx.getAccountID(sfAccount)));

    auto const balance = txnAcct->getFieldAmount (sfBalance).xrp ();

    // balance should have already been checked in checkFee / preFlight.
    assert(balance != beast::zero && (!view().open() || balance >= fee));

    // We retry/reject the transaction if the account balance is zero or we're
    // applying against an open ledger and the balance is less than the fee
    if (fee > balance)
        fee = balance;

    // Since we reset the context, we need to charge the fee and update
    // the account's sequence number again.
    txnAcct->setFieldAmount (sfBalance, balance - fee);
    txnAcct->setFieldU32 (sfSequence, ctx_.tx.getSequence() + 1);

    view().update (txnAcct);

    return fee;
}
Example #2
0
// Based on the meta, send the meta to the streams that are listening.
// We need to determine which streams a given meta effects.
void OrderBookDB::processTxn (
    Ledger::ref ledger, const AcceptedLedgerTx& alTx, Json::Value const& jvObj)
{
    ScopedLockType sl (mLock);

    if (alTx.getResult () == tesSUCCESS)
    {
        // Check if this is an offer or an offer cancel or a payment that
        // consumes an offer.
        // Check to see what the meta looks like.
        for (auto& node : alTx.getMeta ()->getNodes ())
        {
            try
            {
                if (node.getFieldU16 (sfLedgerEntryType) == ltOFFER)
                {
                    SField const* field = nullptr;

                    // We need a field that contains the TakerGets and TakerPays
                    // parameters.
                    if (node.getFName () == sfModifiedNode)
                        field = &sfPreviousFields;
                    else if (node.getFName () == sfCreatedNode)
                        field = &sfNewFields;
                    else if (node.getFName () == sfDeletedNode)
                        field = &sfFinalFields;

                    if (field)
                    {
                        auto data = dynamic_cast<const STObject*> (
                            node.peekAtPField (*field));

                        if (data)
                        {
                            // determine the OrderBook
                            auto listeners = getBookListeners (
                                {data->getFieldAmount (sfTakerGets).issue(),
                                 data->getFieldAmount (sfTakerPays).issue()});

                            if (listeners)
                                listeners->publish (jvObj);
                        }
                    }
                }
            }
            catch (...)
            {
                WriteLog (lsINFO, OrderBookDB)
                    << "Fields not found in OrderBookDB::processTxn";
            }
        }
    }
}
Example #3
0
void
balance::operator()(Env const& env) const
{
    if (isXRP(value_.issue()))
    {
        auto const sle = env.le(account_);
        if (none_)
        {
            env.test.expect(! sle);
        }
        else if (env.test.expect(sle))
        {
            env.test.expect(sle->getFieldAmount(
                sfBalance) == value_);
        }
    }
    else
    {
        auto const sle = env.le(
            getRippleStateIndex(account_.id(),
                value_.issue()));
        if (none_)
        {
            env.test.expect(! sle);
        }
        else if (env.test.expect(sle))
        {
            auto amount =
                sle->getFieldAmount(sfBalance);
            amount.setIssuer(
                value_.issue().account);
            if (account_.id() >
                    value_.issue().account)
                amount.negate();
            env.test.expect(amount == value_);
        }
    }
}
Example #4
0
STAmount creditLimit (
    LedgerEntrySet& ledger,
    AccountID const& account,
    AccountID const& issuer,
    Currency const& currency)
{
    STAmount result ({currency, account});

    auto sleDivvyState = ledger.entryCache (ltRIPPLE_STATE,
        getDivvyStateIndex (account, issuer, currency));

    if (sleDivvyState)
    {
        result = sleDivvyState->getFieldAmount (
            account < issuer ? sfLowLimit : sfHighLimit);
        result.setIssuer (account);
    }

    assert (result.getIssuer () == account);
    assert (result.getCurrency () == currency);
    return result;
}
Example #5
0
// {
//   account: <account>|<account_public_key>
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
Json::Value doAccountChannels (RPC::Context& context)
{
    auto const& params (context.params);
    if (! params.isMember (jss::account))
        return RPC::missing_field_error (jss::account);

    std::shared_ptr<ReadView const> ledger;
    auto result = RPC::lookupLedger (ledger, context);
    if (! ledger)
        return result;

    std::string strIdent (params[jss::account].asString ());
    AccountID accountID;

    result = RPC::accountFromString (accountID, strIdent);
    if (result)
        return result;

    if (! ledger->exists(keylet::account (accountID)))
        return rpcError (rpcACT_NOT_FOUND);

    std::string strDst;
    if (params.isMember (jss::destination_account))
        strDst = params[jss::destination_account].asString ();
    auto hasDst = ! strDst.empty ();

    AccountID raDstAccount;
    if (hasDst)
    {
        result = RPC::accountFromString (raDstAccount, strDst);
        if (result)
            return result;
    }

    unsigned int limit;
    if (auto err = readLimitField(limit, RPC::Tuning::accountChannels, context))
        return *err;

    Json::Value jsonChannels{Json::arrayValue};
    struct VisitData
    {
        std::vector <std::shared_ptr<SLE const>> items;
        AccountID const& accountID;
        bool hasDst;
        AccountID const& raDstAccount;
    };
    VisitData visitData = {{}, accountID, hasDst, raDstAccount};
    unsigned int reserve (limit);
    uint256 startAfter;
    std::uint64_t startHint;

    if (params.isMember (jss::marker))
    {
        // We have a start point. Use limit - 1 from the result and use the
        // very last one for the resume.
        Json::Value const& marker (params[jss::marker]);

        if (! marker.isString ())
            return RPC::expected_field_error (jss::marker, "string");

        startAfter.SetHex (marker.asString ());
        auto const sleChannel = ledger->read({ltPAYCHAN, startAfter});

        if (! sleChannel)
            return rpcError (rpcINVALID_PARAMS);

        if (sleChannel->getFieldAmount (sfLowLimit).getIssuer () == accountID)
            startHint = sleChannel->getFieldU64 (sfLowNode);
        else if (sleChannel->getFieldAmount (sfHighLimit).getIssuer () == accountID)
            startHint = sleChannel->getFieldU64 (sfHighNode);
        else
            return rpcError (rpcINVALID_PARAMS);

        addChannel (jsonChannels, *sleChannel);
        visitData.items.reserve (reserve);
    }
    else
    {
        startHint = 0;
        // We have no start point, limit should be one higher than requested.
        visitData.items.reserve (++reserve);
    }

    if (! forEachItemAfter(*ledger, accountID,
            startAfter, startHint, reserve,
        [&visitData](std::shared_ptr<SLE const> const& sleCur)
        {

            if (sleCur && sleCur->getType () == ltPAYCHAN &&
                (! visitData.hasDst ||
                 visitData.raDstAccount == (*sleCur)[sfDestination]))
            {
                visitData.items.emplace_back (sleCur);
                return true;
            }

            return false;
        }))
    {
        return rpcError (rpcINVALID_PARAMS);
    }

    if (visitData.items.size () == reserve)
    {
        result[jss::limit] = limit;

        result[jss::marker] = to_string (visitData.items.back()->key());
        visitData.items.pop_back ();
    }

    result[jss::account] = context.app.accountIDCache().toBase58 (accountID);

    for (auto const& item : visitData.items)
        addChannel (jsonChannels, *item);

    context.loadType = Resource::feeMediumBurdenRPC;
    result[jss::channels] = std::move(jsonChannels);
    return result;
}
Example #6
0
// {
//   account: <account>|<account_public_key>
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
Json::Value doAccountLines (RPC::Context& context)
{
    auto const& params (context.params);
    if (! params.isMember (jss::account))
        return RPC::missing_field_error (jss::account);

    std::shared_ptr<ReadView const> ledger;
    auto result = RPC::lookupLedger (ledger, context);
    if (! ledger)
        return result;

    std::string strIdent (params[jss::account].asString ());
    AccountID accountID;

    // Intentional assignment in if.  Extra parentheses silence warning.
    if ((result = RPC::accountFromString (accountID, strIdent)))
        return result;

    if (! ledger->exists(keylet::account (accountID)))
        return rpcError (rpcACT_NOT_FOUND);

    std::string strPeer;
    if (params.isMember (jss::peer))
        strPeer = params[jss::peer].asString ();
    auto hasPeer = ! strPeer.empty ();

    AccountID raPeerAccount;
    if (hasPeer)
    {
        // Intentional assignment in if.  Extra parentheses silence warning.
        if ((result = RPC::accountFromString (raPeerAccount, strPeer)))
            return result;
    }

    unsigned int limit;
    if (auto err = readLimitField(limit, RPC::Tuning::accountLines, context))
        return *err;

    Json::Value& jsonLines (result[jss::lines] = Json::arrayValue);
    VisitData visitData = {{}, accountID, hasPeer, raPeerAccount};
    unsigned int reserve (limit);
    uint256 startAfter;
    std::uint64_t startHint;

    if (params.isMember (jss::marker))
    {
        // We have a start point. Use limit - 1 from the result and use the
        // very last one for the resume.
        Json::Value const& marker (params[jss::marker]);

        if (! marker.isString ())
            return RPC::expected_field_error (jss::marker, "string");

        startAfter.SetHex (marker.asString ());
        auto const sleLine = ledger->read({ltRIPPLE_STATE, startAfter});

        if (! sleLine)
            return rpcError (rpcINVALID_PARAMS);

        if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == accountID)
            startHint = sleLine->getFieldU64 (sfLowNode);
        else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == accountID)
            startHint = sleLine->getFieldU64 (sfHighNode);
        else
            return rpcError (rpcINVALID_PARAMS);

        // Caller provided the first line (startAfter), add it as first result
        auto const line = RippleState::makeItem (accountID, sleLine);
        if (line == nullptr)
            return rpcError (rpcINVALID_PARAMS);

        addLine (jsonLines, *line);
        visitData.items.reserve (reserve);
    }
    else
    {
        startHint = 0;
        // We have no start point, limit should be one higher than requested.
        visitData.items.reserve (++reserve);
    }

    {
        if (! forEachItemAfter(*ledger, accountID,
                startAfter, startHint, reserve,
            [&visitData](std::shared_ptr<SLE const> const& sleCur)
            {
                auto const line =
                    RippleState::makeItem (visitData.accountID, sleCur);
                if (line != nullptr &&
                    (! visitData.hasPeer ||
                     visitData.raPeerAccount == line->getAccountIDPeer ()))
                {
                    visitData.items.emplace_back (line);
                    return true;
                }

                return false;
            }))
        {
            return rpcError (rpcINVALID_PARAMS);
        }
    }

    if (visitData.items.size () == reserve)
    {
        result[jss::limit] = limit;

        RippleState::pointer line (visitData.items.back ());
        result[jss::marker] = to_string (line->key());
        visitData.items.pop_back ();
    }

    result[jss::account] = context.app.accountIDCache().toBase58 (accountID);

    for (auto const& item : visitData.items)
        addLine (jsonLines, *item.get ());

    context.loadType = Resource::feeMediumBurdenRPC;
    return result;
}
Example #7
0
RippleAddress SerializedLedgerEntry::getSecondOwner ()
{
    return RippleAddress::createAccountID (getFieldAmount (sfHighLimit).getIssuer ());
}
Example #8
0
// Append a node, then create and insert before it any implied nodes.  Order
// book nodes may go back to back.
//
// For each non-matching pair of IssuedCurrency, there's an order book.
//
// <-- resultCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH,
//                 terNO_LINE, tecPATH_DRY
TER PathState::pushNode (
    const int iType,
    AccountID const& account,    // If not specified, means an order book.
    Currency const& currency,  // If not specified, default to previous.
    AccountID const& issuer)     // If not specified, default to previous.
{
    path::Node node;
    const bool pathIsEmpty = nodes_.empty ();

    // TODO(tom): if pathIsEmpty, we probably don't need to do ANYTHING below.
    // Indeed, we might just not even call pushNode in the first place!

    auto const& backNode = pathIsEmpty ? path::Node () : nodes_.back ();

    // true, iff node is a ripple account. false, iff node is an offer node.
    const bool hasAccount = (iType & STPathElement::typeAccount);

    // Is currency specified for the output of the current node?
    const bool hasCurrency = (iType & STPathElement::typeCurrency);

    // Issuer is specified for the output of the current node.
    const bool hasIssuer = (iType & STPathElement::typeIssuer);

    TER resultCode = tesSUCCESS;

    JLOG (j_.trace)
         << "pushNode> " << iType << ": "
         << (hasAccount ? to_string(account) : std::string("-")) << " "
         << (hasCurrency ? to_string(currency) : std::string("-")) << "/"
         << (hasIssuer ? to_string(issuer) : std::string("-")) << "/";

    node.uFlags = iType;
    node.issue_.currency = hasCurrency ?
            currency : backNode.issue_.currency;

    // TODO(tom): we can probably just return immediately whenever we hit an
    // error in these next pages.

    if (iType & ~STPathElement::typeAll)
    {
        // Of course, this could never happen.
        JLOG (j_.debug) << "pushNode: bad bits.";
        resultCode = temBAD_PATH;
    }
    else if (hasIssuer && isXRP (node.issue_))
    {
        JLOG (j_.debug) << "pushNode: issuer specified for XRP.";

        resultCode = temBAD_PATH;
    }
    else if (hasIssuer && !issuer)
    {
        JLOG (j_.debug) << "pushNode: specified bad issuer.";

        resultCode = temBAD_PATH;
    }
    else if (!hasAccount && !hasCurrency && !hasIssuer)
    {
        // You can't default everything to the previous node as you would make
        // no progress.
        JLOG (j_.debug)
            << "pushNode: offer must specify at least currency or issuer.";
        resultCode = temBAD_PATH;
    }
    else if (hasAccount)
    {
        // Account link
        node.account_ = account;
        node.issue_.account = hasIssuer ? issuer :
                (isXRP (node.issue_) ? xrpAccount() : account);
        // Zero value - for accounts.
        node.saRevRedeem = STAmount ({node.issue_.currency, account});
        node.saRevIssue = node.saRevRedeem;

        // For order books only - zero currency with the issuer ID.
        node.saRevDeliver = STAmount (node.issue_);
        node.saFwdDeliver = node.saRevDeliver;

        if (pathIsEmpty)
        {
            // The first node is always correct as is.
        }
        else if (!account)
        {
            JLOG (j_.debug)
                << "pushNode: specified bad account.";
            resultCode = temBAD_PATH;
        }
        else
        {
            // Add required intermediate nodes to deliver to current account.
            JLOG (j_.trace)
                << "pushNode: imply for account.";

            resultCode = pushImpliedNodes (
                node.account_,
                node.issue_.currency,
                isXRP(node.issue_.currency) ? xrpAccount() : account);

            // Note: backNode may no longer be the immediately previous node.
        }

        if (resultCode == tesSUCCESS && !nodes_.empty ())
        {
            auto const& backNode = nodes_.back ();
            if (backNode.isAccount())
            {
                auto sleRippleState = view().peek(
                    keylet::line(backNode.account_, node.account_, backNode.issue_.currency));

                // A "RippleState" means a balance betweeen two accounts for a
                // specific currency.
                if (!sleRippleState)
                {
                    JLOG (j_.trace)
                            << "pushNode: No credit line between "
                            << backNode.account_ << " and " << node.account_
                            << " for " << node.issue_.currency << "." ;

                    JLOG (j_.trace) << getJson ();

                    resultCode   = terNO_LINE;
                }
                else
                {
                    JLOG (j_.trace)
                            << "pushNode: Credit line found between "
                            << backNode.account_ << " and " << node.account_
                            << " for " << node.issue_.currency << "." ;

                    auto sleBck  = view().peek (
                        keylet::account(backNode.account_));
                    // Is the source account the highest numbered account ID?
                    bool bHigh = backNode.account_ > node.account_;

                    if (!sleBck)
                    {
                        JLOG (j_.warning)
                            << "pushNode: delay: can't receive IOUs from "
                            << "non-existent issuer: " << backNode.account_;

                        resultCode   = terNO_ACCOUNT;
                    }
                    else if ((sleBck->getFieldU32 (sfFlags) & lsfRequireAuth) &&
                             !(sleRippleState->getFieldU32 (sfFlags) &
                                  (bHigh ? lsfHighAuth : lsfLowAuth)) &&
                             sleRippleState->getFieldAmount(sfBalance) == zero)
                    {
                        JLOG (j_.warning)
                                << "pushNode: delay: can't receive IOUs from "
                                << "issuer without auth.";

                        resultCode   = terNO_AUTH;
                    }

                    if (resultCode == tesSUCCESS)
                    {
                        STAmount saOwed = creditBalance (view(),
                            node.account_, backNode.account_,
                            node.issue_.currency);
                        STAmount saLimit;

                        if (saOwed <= zero)
                        {
                            saLimit = creditLimit (view(),
                                node.account_,
                                backNode.account_,
                                node.issue_.currency);
                            if (-saOwed >= saLimit)
                            {
                                JLOG (j_.debug) <<
                                    "pushNode: dry:" <<
                                    " saOwed=" << saOwed <<
                                    " saLimit=" << saLimit;

                                resultCode   = tecPATH_DRY;
                            }
                        }
                    }
                }
            }
        }

        if (resultCode == tesSUCCESS)
            nodes_.push_back (node);
    }
    else
    {
        // Offer link.
        //
        // Offers bridge a change in currency and issuer, or just a change in
        // issuer.
        if (hasIssuer)
            node.issue_.account = issuer;
        else if (isXRP (node.issue_.currency))
            node.issue_.account = xrpAccount();
        else if (isXRP (backNode.issue_.account))
            node.issue_.account = backNode.account_;
        else
            node.issue_.account = backNode.issue_.account;

        node.saRateMax = STAmount::saZero;
        node.saRevDeliver = STAmount (node.issue_);
        node.saFwdDeliver = node.saRevDeliver;

        if (!isConsistent (node.issue_))
        {
            JLOG (j_.debug)
                << "pushNode: currency is inconsistent with issuer.";

            resultCode = temBAD_PATH;
        }
        else if (backNode.issue_ == node.issue_)
        {
            JLOG (j_.debug) <<
                "pushNode: bad path: offer to same currency and issuer";
            resultCode = temBAD_PATH;
        }
        else {
            JLOG (j_.trace) << "pushNode: imply for offer.";

            // Insert intermediary issuer account if needed.
            resultCode   = pushImpliedNodes (
                xrpAccount(), // Rippling, but offers don't have an account.
                backNode.issue_.currency,
                backNode.issue_.account);
        }

        if (resultCode == tesSUCCESS)
            nodes_.push_back (node);
    }

    JLOG (j_.trace) << "pushNode< : " << transToken (resultCode);
    return resultCode;
}
Example #9
0
// {
//   account: <account>|<account_public_key>
//   account_index: <number>        // optional, defaults to 0.
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
Json::Value doAccountLines (RPC::Context& context)
{
    auto const& params (context.params);
    if (! params.isMember (jss::account))
        return RPC::missing_field_error (jss::account);

    Ledger::pointer ledger;
    Json::Value result (RPC::lookupLedger (params, ledger, context.netOps));
    if (! ledger)
        return result;

    std::string strIdent (params[jss::account].asString ());
    bool bIndex (params.isMember (jss::account_index));
    int iIndex (bIndex ? params[jss::account_index].asUInt () : 0);
    DivvyAddress divvyAddress;

    auto jv = RPC::accountFromString (
        divvyAddress, bIndex, strIdent, iIndex, false);
    if (! jv.empty ())
    {
        for (auto it = jv.begin (); it != jv.end (); ++it)
            result[it.memberName ()] = it.key ();

        return result;
    }

    if (! ledger->exists(getAccountRootIndex(
            divvyAddress.getAccountID())))
        return rpcError (rpcACT_NOT_FOUND);

    std::string strPeer (params.isMember (jss::peer)
        ? params[jss::peer].asString () : "");
    bool bPeerIndex (params.isMember (jss::peer_index));
    int iPeerIndex (bIndex ? params[jss::peer_index].asUInt () : 0);

    DivvyAddress divvyAddressPeer;

    if (! strPeer.empty ())
    {
        result[jss::peer] = divvyAddress.humanAccountID ();

        if (bPeerIndex)
            result[jss::peer_index] = iPeerIndex;

        result = RPC::accountFromString (
            divvyAddressPeer, bPeerIndex, strPeer, iPeerIndex, false);

        if (! result.empty ())
            return result;
    }

    AccountID raPeerAccount;
    if (divvyAddressPeer.isValid ())
        raPeerAccount = divvyAddressPeer.getAccountID ();

    unsigned int limit;
    if (params.isMember (jss::limit))
    {
        auto const& jvLimit (params[jss::limit]);
        if (! jvLimit.isIntegral ())
            return RPC::expected_field_error (jss::limit, "unsigned integer");

        limit = jvLimit.isUInt () ? jvLimit.asUInt () :
            std::max (0, jvLimit.asInt ());

        if (context.role != Role::ADMIN)
        {
            limit = std::max (RPC::Tuning::minLinesPerRequest,
                std::min (limit, RPC::Tuning::maxLinesPerRequest));
        }
    }
    else
    {
        limit = RPC::Tuning::defaultLinesPerRequest;
    }

    Json::Value& jsonLines (result[jss::lines] = Json::arrayValue);
    AccountID const& raAccount(divvyAddress.getAccountID ());
    VisitData visitData = { {}, raAccount, divvyAddressPeer, raPeerAccount };
    unsigned int reserve (limit);
    uint256 startAfter;
    std::uint64_t startHint;

    if (params.isMember (jss::marker))
    {
        // We have a start point. Use limit - 1 from the result and use the
        // very last one for the resume.
        Json::Value const& marker (params[jss::marker]);

        if (! marker.isString ())
            return RPC::expected_field_error (jss::marker, "string");

        startAfter.SetHex (marker.asString ());
        auto const sleLine = fetch(*ledger, startAfter,
            getApp().getSLECache());

        if (sleLine == nullptr || sleLine->getType () != ltRIPPLE_STATE)
            return rpcError (rpcINVALID_PARAMS);

        if (sleLine->getFieldAmount (sfLowLimit).getIssuer () == raAccount)
            startHint = sleLine->getFieldU64 (sfLowNode);
        else if (sleLine->getFieldAmount (sfHighLimit).getIssuer () == raAccount)
            startHint = sleLine->getFieldU64 (sfHighNode);
        else
            return rpcError (rpcINVALID_PARAMS);

        // Caller provided the first line (startAfter), add it as first result
        auto const line = DivvyState::makeItem (raAccount, sleLine);
        if (line == nullptr)
            return rpcError (rpcINVALID_PARAMS);

        addLine (jsonLines, *line);
        visitData.items.reserve (reserve);
    }
    else
    {
        startHint = 0;
        // We have no start point, limit should be one higher than requested.
        visitData.items.reserve (++reserve);
    }

    if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(),
            startAfter, startHint, reserve,
        [&visitData](std::shared_ptr<SLE const> const& sleCur)
        {
            auto const line =
                DivvyState::makeItem (visitData.accountID, sleCur);
            if (line != nullptr &&
                (! visitData.divvyAddressPeer.isValid () ||
                visitData.raPeerAccount == line->getAccountIDPeer ()))
            {
                visitData.items.emplace_back (line);
                return true;
            }

            return false;
        }))
    {
        return rpcError (rpcINVALID_PARAMS);
    }

    if (visitData.items.size () == reserve)
    {
        result[jss::limit] = limit;

        DivvyState::pointer line (visitData.items.back ());
        result[jss::marker] = to_string (line->key());
        visitData.items.pop_back ();
    }

    result[jss::account] = divvyAddress.humanAccountID ();

    for (auto const& item : visitData.items)
        addLine (jsonLines, *item.get ());

    context.loadType = Resource::feeMediumBurdenRPC;
    return result;
}
Example #10
0
DivvyAddress STLedgerEntry::getSecondOwner () const
{
    return DivvyAddress::createAccountID (getFieldAmount (sfHighLimit).getIssuer ());
}
Example #11
0
// Based on the meta, send the meta to the streams that are listening.
// We need to determine which streams a given meta effects.
void OrderBookDB::processTxn (
    std::shared_ptr<ReadView const> const& ledger,
        const AcceptedLedgerTx& alTx)
{
    std::lock_guard <std::recursive_mutex> sl (mLock);
    
    Json::Value jvObj;
    bool bJvObjInitialized = false;

    if (alTx.getResult () == tesSUCCESS)
    {
        std::set<BookListeners::pointer> listenersSet;
        // Check if this is an offer or an offer cancel or a payment that
        // consumes an offer.
        // Check to see what the meta looks like.
        for (auto& node : alTx.getMeta ()->getNodes ())
        {
            try
            {
                if (node.getFieldU16 (sfLedgerEntryType) == ltOFFER)
                {
                    SField const* field = nullptr;

                    // We need a field that contains the TakerGets and TakerPays
                    // parameters.
                    if (node.getFName () == sfModifiedNode)
                        field = &sfPreviousFields;
                    else if (node.getFName () == sfCreatedNode)
                        field = &sfNewFields;
                    else if (node.getFName () == sfDeletedNode)
                        field = &sfFinalFields;

                    if (field)
                    {
                        auto data = dynamic_cast<const STObject*> (
                            node.peekAtPField (*field));

                        if (data &&
                            data->isFieldPresent (sfTakerPays) &&
                            data->isFieldPresent (sfTakerGets))
                        {
                            // determine the OrderBook
                            auto listeners = getBookListeners (
                                {data->getFieldAmount (sfTakerGets).issue(),
                                 data->getFieldAmount (sfTakerPays).issue()});

                            if (listeners)
                            {
                                listenersSet.insert(listeners);
                            }
                        }
                    }
                }
            }
            catch (std::exception const&)
            {
                JLOG (j_.info)
                    << "Fields not found in OrderBookDB::processTxn";
            }
        }
        
        for (auto& listeners: listenersSet)
        {
            if (!bJvObjInitialized)
            {
                jvObj = NetworkOPs_transJson (*alTx.getTxn (), alTx.getResult (), true, ledger, app_);
                jvObj[jss::meta] = alTx.getMeta ()->getJson (0);
                bJvObjInitialized = true;
            }
            listeners->publish (jvObj);
        }
    }
}
Example #12
0
// {
//   account: <account>|<account_public_key>
//   account_index: <number>        // optional, defaults to 0.
//   ledger_hash : <ledger>
//   ledger_index : <ledger_index>
//   limit: integer                 // optional
//   marker: opaque                 // optional, resume previous query
// }
Json::Value doAccountOffers (RPC::Context& context)
{
    auto const& params (context.params);
    if (! params.isMember (jss::account))
        return RPC::missing_field_error (jss::account);

    Ledger::pointer ledger;
    Json::Value result (RPC::lookupLedger (params, ledger, context.netOps));
    if (! ledger)
        return result;

    std::string strIdent (params[jss::account].asString ());
    bool bIndex (params.isMember (jss::account_index));
    int const iIndex (bIndex ? params[jss::account_index].asUInt () : 0);
    DivvyAddress divvyAddress;

    Json::Value const jv = RPC::accountFromString (
        divvyAddress, bIndex, strIdent, iIndex, false);
    if (! jv.empty ())
    {
        for (Json::Value::const_iterator it (jv.begin ()); it != jv.end (); ++it)
            result[it.memberName ()] = it.key ();

        return result;
    }

    // Get info on account.
    result[jss::account] = divvyAddress.humanAccountID ();

    if (bIndex)
        result[jss::account_index] = iIndex;

    if (! ledger->exists(getAccountRootIndex(
            divvyAddress.getAccountID())))
        return rpcError (rpcACT_NOT_FOUND);

    unsigned int limit;
    if (params.isMember (jss::limit))
    {
        auto const& jvLimit (params[jss::limit]);
        if (! jvLimit.isIntegral ())
            return RPC::expected_field_error (jss::limit, "unsigned integer");

        limit = jvLimit.isUInt () ? jvLimit.asUInt () :
            std::max (0, jvLimit.asInt ());

        if (context.role != Role::ADMIN)
        {
            limit = std::max (RPC::Tuning::minOffersPerRequest,
                std::min (limit, RPC::Tuning::maxOffersPerRequest));
        }
    }
    else
    {
        limit = RPC::Tuning::defaultOffersPerRequest;
    }

    AccountID const& raAccount (divvyAddress.getAccountID ());
    Json::Value& jsonOffers (result[jss::offers] = Json::arrayValue);
    std::vector <std::shared_ptr<SLE const>> offers;
    unsigned int reserve (limit);
    uint256 startAfter;
    std::uint64_t startHint;

    if (params.isMember(jss::marker))
    {
        // We have a start point. Use limit - 1 from the result and use the
        // very last one for the resume.
        Json::Value const& marker (params[jss::marker]);

        if (! marker.isString ())
            return RPC::expected_field_error (jss::marker, "string");

        startAfter.SetHex (marker.asString ());
        auto const sleOffer = fetch (*ledger, startAfter,
            getApp().getSLECache());

        if (sleOffer == nullptr ||
            sleOffer->getType () != ltOFFER ||
            raAccount != sleOffer->getFieldAccount160 (sfAccount))
        {
            return rpcError (rpcINVALID_PARAMS);
        }

        startHint = sleOffer->getFieldU64(sfOwnerNode);

        // Caller provided the first offer (startAfter), add it as first result
        Json::Value& obj (jsonOffers.append (Json::objectValue));
        sleOffer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]);
        sleOffer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]);
        obj[jss::seq] = sleOffer->getFieldU32 (sfSequence);
        obj[jss::flags] = sleOffer->getFieldU32 (sfFlags);

        offers.reserve (reserve);
    }
    else
    {
        startHint = 0;
        // We have no start point, limit should be one higher than requested.
        offers.reserve (++reserve);
    }

    if (! forEachItemAfter(*ledger, raAccount, getApp().getSLECache(),
            startAfter, startHint, reserve,
        [&offers](std::shared_ptr<SLE const> const& offer)
        {
            if (offer->getType () == ltOFFER)
            {
                offers.emplace_back (offer);
                return true;
            }

            return false;
        }))
    {
        return rpcError (rpcINVALID_PARAMS);
    }

    if (offers.size () == reserve)
    {
        result[jss::limit] = limit;

        result[jss::marker] = to_string (offers.back ()->getIndex ());
        offers.pop_back ();
    }

    for (auto const& offer : offers)
    {
        Json::Value& obj (jsonOffers.append (Json::objectValue));
        offer->getFieldAmount (sfTakerPays).setJson (obj[jss::taker_pays]);
        offer->getFieldAmount (sfTakerGets).setJson (obj[jss::taker_gets]);
        obj[jss::seq] = offer->getFieldU32 (sfSequence);
        obj[jss::flags] = offer->getFieldU32 (sfFlags);
    }

    context.loadType = Resource::feeMediumBurdenRPC;
    return result;
}