{"action":"created","comment":{"url":"https://api.github.com/repos/ripple/rippled/pulls/comments/85991597","pull_request_review_id":6673275,"id":85991597,"diff_hunk":"@@ -629,6 +626,418 @@ CreateOffer::cross (\n     }\n }\n \n+std::pair<TER, Amounts>\n+CreateOffer::crossV2 (\n+    PaymentSandbox& psb,\n+    PaymentSandbox& psbCancel,\n+    Amounts const& takerAmount)\n+{\n+    try\n+    {\n+        // If the taker is unfunded before we begin crossing there's nothing\n+        // to do - just return an error.\n+        //\n+        // We check this in preclaim, but when selling XRP charged fees can\n+        // cause a user's available balance to go to 0 (by causing it to dip\n+        // below the reserve) so we check this case again.\n+        STAmount const inStartBalance = accountFunds (\n+            psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);\n+        if (inStartBalance.signum() <= 0)\n+        {\n+            // The account balance can't cover even part of the offer.\n+            JLOG (j_.debug()) <<\n+                \"Not crossing: taker is unfunded.\";\n+            return { tecUNFUNDED_OFFER, takerAmount };\n+        }\n+\n+        // If the gateway has a transfer rate, accommodate that.  The\n+        // gateway takes its cut without any special consent from the\n+        // offer taker.  Set sendMax to allow for the gateway's cut.\n+        Rate gatewayXferRate {QUALITY_ONE};\n+        STAmount sendMax = takerAmount.in;\n+        if (! sendMax.native() && (account_ != sendMax.getIssuer()))\n+        {\n+            gatewayXferRate = transferRate (psb, sendMax.getIssuer());\n+            if (gatewayXferRate.value != QUALITY_ONE)\n+            {\n+                sendMax = multiplyRound (takerAmount.in,\n+                    gatewayXferRate, takerAmount.in.issue(), true);\n+            }\n+        }\n+\n+        // Payment flow code compares quality after the transfer rate is\n+        // included.  Since transfer rate is incorporated compute threshold.\n+        Quality threshold { takerAmount.out, sendMax };\n+\n+        // If we're creating a passive offer adjust the threshold so we only\n+        // cross offers that have a better quality than this one.\n+        std::uint32_t const txFlags = ctx_.tx.getFlags();\n+        if (txFlags & tfPassive)\n+            ++threshold;\n+\n+        // Don't send more than our balance.\n+        if (sendMax > inStartBalance)\n+            sendMax = inStartBalance;\n+\n+        // Always invoke flow() with the default path.  However if neither\n+        // of the takerAmount currencies are XRP then we cross through an\n+        // additional path with XRP as the intermediate between two books.\n+        // This second path we have to build ourselves.\n+        STPathSet paths;\n+        if (!takerAmount.in.native() & !takerAmount.out.native())\n+        {\n+            STPath path;\n+            path.emplace_back (boost::none, xrpCurrency(), boost::none);\n+            paths.emplace_back (std::move(path));\n+        }\n+        // Special handling for the tfSell flag.\n+        STAmount deliver = takerAmount.out;\n+        if (txFlags & tfSell)\n+        {\n+            // We are selling, so we will accept *more* than the offer\n+            // specified.  Since we don't know how much they might offer,\n+            // we allow delivery of the largest possible amount.\n+            if (deliver.native())\n+                deliver = STAmount { STAmount::cMaxNative };\n+            else\n+                // We can't use the maximum possible currency here because\n+                // there might be a gateway transfer rate to account for.\n+                // Since the transfer rate cannot exceed 200%, we use 1/2\n+                // maxValue for our limit.\n+                deliver = STAmount { takerAmount.out.issue(),\n+                    STAmount::cMaxValue / 2, STAmount::cMaxOffset };\n+        }\n+\n+        // Call the payment engine's flow() to do the actual work.\n+        auto const result = flow (psb, deliver, account_, account_,\n+            paths,\n+            true,                       // default path\n+            ! (txFlags & tfFillOrKill), // partial payment\n+            true,                       // owner pays transfer fee\n+            true,                       // offer crossing\n+            threshold,\n+            sendMax, j_);\n+\n+        // If stale offers were found remove them.\n+        for (auto const& toRemove : result.removableOffers)\n+        {\n+            if (auto otr = psb.peek (keylet::offer (toRemove)))\n+                offerDelete (psb, otr, j_);\n+            if (auto otr = psbCancel.peek (keylet::offer (toRemove)))\n+                offerDelete (psbCancel, otr, j_);\n+        }\n+\n+        // Determine the size of the final offer after crossing.\n+        auto afterCross = takerAmount; // If !tesSUCCESS offer unchanged\n+        if (isTesSuccess (result.result()))\n+        {\n+            STAmount const takerInBalance = accountFunds (\n+                psb, account_, takerAmount.in, fhZERO_IF_FROZEN, j_);\n+\n+            if (takerInBalance.signum() <= 0)\n+            {\n+                // If offer crossing exhausted the account's funds don't\n+                // create the offer.\n+                afterCross.in.clear();\n+                afterCross.out.clear();\n+            }\n+            else\n+            {\n+                STAmount const rate {\n+                    Quality{takerAmount.out, takerAmount.in}.rate() };\n+\n+                if (txFlags & tfSell)\n+                {\n+                    // If selling then scale the new out amount based on how\n+                    // much we sold during crossing.  This preserves the offer\n+                    // Quality,\n+\n+                    // Reduce the offer that is placed by the crossed amount.\n+                    // Note that we must ignore the portion of the\n+                    // actualAmountIn that may have been consumed by a\n+                    // gateway's transfer rate.\n+                    STAmount nonGatewayAmountIn = result.actualAmountIn;\n+                    if (gatewayXferRate.value != QUALITY_ONE)\n+                        nonGatewayAmountIn = divideRound (result.actualAmountIn,\n+                            gatewayXferRate, takerAmount.in.issue(), true);\n+\n+                    afterCross.in -= nonGatewayAmountIn;\n+\n+                    // It's possible that the divRound will cause our subtract\n+                    // to go slightly negative.  So limit afterCross.in to zero.\n+                    if (afterCross.in.signum() < 0)\n+                        // We should verify that the difference *is* small, but\n+                        // what is a good threshold to check?\n+                        afterCross.in.clear();\n+\n+                    afterCross.out = divRound (afterCross.in,\n+                        rate, takerAmount.out.issue(), true);\n+                }\n+                else\n+                {\n+                    // If not selling, we scale the input based on the\n+                    // remaining output.  This too preserves the offer\n+                    // Quality.\n+                    afterCross.out -= result.actualAmountOut;\n+                    assert (afterCross.out >= zero);\n+                    if (afterCross.out.signum() < 0)\n+                        afterCross.out.clear();\n+                    afterCross.in = mulRound (afterCross.out,\n+                        rate, takerAmount.in.issue(), true);\n+                }\n+            }\n+        }\n+\n+        // Return how much of the offer is left.\n+        return { tesSUCCESS, afterCross };\n+    }\n+    catch (std::exception const& e)\n+    {\n+        JLOG (j_.error()) <<\n+            \"Exception during offer crossing: \" << e.what ();\n+    }\n+    return { tecINTERNAL, takerAmount };\n+}\n+\n+enum class SBoxCmp\n+{\n+    same,\n+    dustDiff,\n+    offerDelDiff,\n+    diff\n+};\n+\n+static std::string to_string (SBoxCmp c)\n+{\n+    switch (c)\n+    {\n+    case SBoxCmp::same:\n+        return \"same\";\n+    case SBoxCmp::dustDiff:\n+        return \"dust diffs\";\n+    case SBoxCmp::offerDelDiff:\n+        return \"offer del diffs\";\n+    case SBoxCmp::diff:\n+        return \"different\";\n+    }\n+    return {};\n+}\n+\n+static SBoxCmp compareSandboxes (char const* name, ApplyContext const& ctx,\n+    PaymentSandbox const& psbV1, PaymentSandbox const& psbV2, beast::Journal j)\n+{\n+    SBoxCmp c = SBoxCmp::same;\n+    CashDiff diff = cashFlowDiff (\n+        CashFilter::treatZeroOfferAsDeletion, psbV1,\n+        CashFilter::none, psbV2);\n+\n+    if (diff.hasDiff())\n+    {\n+        using namespace beast::severities;\n+        c = SBoxCmp::dustDiff;\n+        Severity s = kInfo;\n+        std::string diffDesc = \", but only dust.\";\n+        diff.rmDust();\n+        if (diff.hasDiff())\n+        {\n+            // From here on we want to note the transaction ID of differences.\n+            std::stringstream txIdSs;\n+            txIdSs << \".  tx: \" << ctx.tx.getTransactionID();\n+            auto txID = txIdSs.str();\n+\n+            // Sometimes one version deletes offers that the other doesn't\n+            // delete.  That's okay, but keep track of it.\n+            c = SBoxCmp::offerDelDiff;\n+            s = kWarning;\n+            int sides = diff.rmLhsDeletedOffers() ? 1 : 0;\n+            sides |= diff.rmRhsDeletedOffers() ? 2 : 0;\n+            if (!diff.hasDiff())\n+            {\n+                char const* type = \"\";\n+                switch (sides)\n+                {\n+                case 1: type = \"; V1 deleted more offers\"; break;\n+                case 2: type = \"; V2 deleted more offers\"; break;\n+                case 3: type = \"; V1 and V2 deleted different offers\"; break;\n+                default: break;\n+                }\n+                diffDesc = std::string(type) + txID;\n+            }\n+            else\n+            {\n+                // A difference without a broad classification...\n+                c = SBoxCmp::diff;\n+                std::stringstream ss;\n+                ss << \"; common entries: \" << diff.commonCount()\n+                    << \"; V1 unique: \" << diff.lhsOnlyCount()\n+                    << \"; V2 unique: \" << diff.rhsOnlyCount() << txID;\n+                diffDesc = ss.str();\n+            }\n+        }\n+        j.stream (s) << \"OfferCrossV2: \" << name << \" different\" << diffDesc;\n+    }\n+    return c;\n+}\n+\n+std::pair<TER, Amounts>\n+CreateOffer::cross (\n+    PaymentSandbox& psb,\n+    PaymentSandbox& psbCancel,\n+    Amounts const& takerAmount)\n+{\n+    // There are amendments for V2 offer crossing and for comparing results\n+    // between V1 and V2 offer crossing.  Turn those into bools.\n+    bool const useV2 {\n+        psb.rules().enabled (\n+            featureOfferCrossV2, ctx_.app.config().features) };\n+\n+    bool const doCompare {\n+        psb.rules().enabled (\n+            featureCompareOfferCrossV1V2, ctx_.app.config().features) };\n+\n+    if (doCompare)\n+    {\n+        // If we're comparing then there's a good chance we're replaying\n+        // a transaction.  Make it easy to see the transaction ID in the\n+        // debugger.\n+        std::stringstream ss;\n+        ss << ctx_.tx.getTransactionID();\n+        auto const txID = ss.str();\n+        char const* txIDptr = txID.c_str();\n+        (void) txIDptr;\n+    }\n+\n+    // Decide which version of offer crossing to run.  Run both if comparing.\n+    using CallCrossPtr = std::pair<TER, Amounts>\n+        (CreateOffer::*) (PaymentSandbox&, PaymentSandbox&, Amounts const&);\n+\n+    auto callCross =\n+        [this] (bool use, CallCrossPtr callPtr, PaymentSandbox& psb,\n+            PaymentSandbox& psbCancel, Amounts const& takerAmount)\n+        {\n+            if (use)\n+                return ((this)->*callPtr)(psb, psbCancel, takerAmount);\n+            return std::make_pair (tecINTERNAL, takerAmount);\n+        };","path":"src/ripple/app/tx/impl/CreateOffer.cpp","position":null,"original_position":382,"commit_id":"50a36fcb5cd3b904cf01bef9e09d93f71f73b8f1","original_commit_id":"f357f77a097c47411fa508e8b1adaf86239d9591","user":{"login":"seelabs","id":5853458,"avatar_url":"https://avatars.githubusercontent.com/u/5853458?v=3","gravatar_id":"","url":"https://api.github.com/users/seelabs","html_url":"https://github.com/seelabs","followers_url":"https://api.github.com/users/seelabs/followers","following_url":"https://api.github.com/users/seelabs/following{/other_user}","gists_url":"https://api.github.com/users/seelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/seelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seelabs/subscriptions","organizations_url":"https://api.github.com/users/seelabs/orgs","repos_url":"https://api.github.com/users/seelabs/repos","events_url":"https://api.github.com/users/seelabs/events{/privacy}","received_events_url":"https://api.github.com/users/seelabs/received_events","type":"User","site_admin":false},"body":"I was thinking something like this:\r\n```\r\n    auto const v1r = (useV2 && !doCompare)\r\n        ? std::make_pair(tecINTERNAL, takerAmount)\r\n        : crossV1(psb, psbCancel, takerAmount);\r\n```\r\nIf we did that, it'd be shorter (6 loc vs. 14 loc), and easier to understand too (no need to refer to the lambda, no pointers to members, no typedef for the pointers to members).\r\n\r\nIt's a nit for sure, you're call.","created_at":"2016-11-01T18:21:12Z","updated_at":"2016-11-01T18:21:12Z","html_url":"https://github.com/ripple/rippled/pull/1884#discussion_r85991597","pull_request_url":"https://api.github.com/repos/ripple/rippled/pulls/1884","_links":{"self":{"href":"https://api.github.com/repos/ripple/rippled/pulls/comments/85991597"},"html":{"href":"https://github.com/ripple/rippled/pull/1884#discussion_r85991597"},"pull_request":{"href":"https://api.github.com/repos/ripple/rippled/pulls/1884"}}},"pull_request":{"url":"https://api.github.com/repos/ripple/rippled/pulls/1884","id":90471816,"html_url":"https://github.com/ripple/rippled/pull/1884","diff_url":"https://github.com/ripple/rippled/pull/1884.diff","patch_url":"https://github.com/ripple/rippled/pull/1884.patch","issue_url":"https://api.github.com/repos/ripple/rippled/issues/1884","number":1884,"state":"open","locked":false,"title":"Use payment flow code for offer crossing (RIPD-1094):","user":{"login":"scottschurr","id":7840379,"avatar_url":"https://avatars.githubusercontent.com/u/7840379?v=3","gravatar_id":"","url":"https://api.github.com/users/scottschurr","html_url":"https://github.com/scottschurr","followers_url":"https://api.github.com/users/scottschurr/followers","following_url":"https://api.github.com/users/scottschurr/following{/other_user}","gists_url":"https://api.github.com/users/scottschurr/gists{/gist_id}","starred_url":"https://api.github.com/users/scottschurr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scottschurr/subscriptions","organizations_url":"https://api.github.com/users/scottschurr/orgs","repos_url":"https://api.github.com/users/scottschurr/repos","events_url":"https://api.github.com/users/scottschurr/events{/privacy}","received_events_url":"https://api.github.com/users/scottschurr/received_events","type":"User","site_admin":false},"body":"Replace Taker.cpp with calls to the new payment flow code.\n\nThis change required a number of tweaks in the payment flow code.  These tweaks are conditionalized on whether or not offer crossing is taking place.  The flag is explicitly passed as a parameter to the flow code.\n\nFor testing, a class was added that identifies differences in the contents of two PaymentSandboxes.  That code may be reusable in the future.\n\nNone of the V1 offer crossing code is removed.  Both versions of the code are co-resident to support an amendment cut-over.\n\nThe code that identifies differences between V1 and V2 offer crossing is also on an amendment.  That makes it easy to enable or disable difference logging by changing the config file.  This approach models what was done with the payment Flow code.  The differencing code should never be enabled on the network.\n\nExtensive offer crossing unit tests are added to examine and verify the behavior of corner cases.  The tests are currently configured to run against both V1 and V2 offer crossing.  This gives us confidence that most cases run identically and some of the (few) differences in behavior are documented.\n","created_at":"2016-10-22T06:11:21Z","updated_at":"2016-11-01T18:21:12Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b7c87c768b957205f5486a9d0e23e5b6c47b7e80","assignee":{"login":"nbougalis","id":5085415,"avatar_url":"https://avatars.githubusercontent.com/u/5085415?v=3","gravatar_id":"","url":"https://api.github.com/users/nbougalis","html_url":"https://github.com/nbougalis","followers_url":"https://api.github.com/users/nbougalis/followers","following_url":"https://api.github.com/users/nbougalis/following{/other_user}","gists_url":"https://api.github.com/users/nbougalis/gists{/gist_id}","starred_url":"https://api.github.com/users/nbougalis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nbougalis/subscriptions","organizations_url":"https://api.github.com/users/nbougalis/orgs","repos_url":"https://api.github.com/users/nbougalis/repos","events_url":"https://api.github.com/users/nbougalis/events{/privacy}","received_events_url":"https://api.github.com/users/nbougalis/received_events","type":"User","site_admin":false},"assignees":[{"login":"nbougalis","id":5085415,"avatar_url":"https://avatars.githubusercontent.com/u/5085415?v=3","gravatar_id":"","url":"https://api.github.com/users/nbougalis","html_url":"https://github.com/nbougalis","followers_url":"https://api.github.com/users/nbougalis/followers","following_url":"https://api.github.com/users/nbougalis/following{/other_user}","gists_url":"https://api.github.com/users/nbougalis/gists{/gist_id}","starred_url":"https://api.github.com/users/nbougalis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nbougalis/subscriptions","organizations_url":"https://api.github.com/users/nbougalis/orgs","repos_url":"https://api.github.com/users/nbougalis/repos","events_url":"https://api.github.com/users/nbougalis/events{/privacy}","received_events_url":"https://api.github.com/users/nbougalis/received_events","type":"User","site_admin":false},{"login":"seelabs","id":5853458,"avatar_url":"https://avatars.githubusercontent.com/u/5853458?v=3","gravatar_id":"","url":"https://api.github.com/users/seelabs","html_url":"https://github.com/seelabs","followers_url":"https://api.github.com/users/seelabs/followers","following_url":"https://api.github.com/users/seelabs/following{/other_user}","gists_url":"https://api.github.com/users/seelabs/gists{/gist_id}","starred_url":"https://api.github.com/users/seelabs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seelabs/subscriptions","organizations_url":"https://api.github.com/users/seelabs/orgs","repos_url":"https://api.github.com/users/seelabs/repos","events_url":"https://api.github.com/users/seelabs/events{/privacy}","received_events_url":"https://api.github.com/users/seelabs/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/ripple/rippled/pulls/1884/commits","review_comments_url":"https://api.github.com/repos/ripple/rippled/pulls/1884/comments","review_comment_url":"https://api.github.com/repos/ripple/rippled/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ripple/rippled/issues/1884/comments","statuses_url":"https://api.github.com/repos/ripple/rippled/statuses/50a36fcb5cd3b904cf01bef9e09d93f71f73b8f1","head":{"label":"scottschurr:flow-offer-cross-8","ref":"flow-offer-cross-8","sha":"50a36fcb5cd3b904cf01bef9e09d93f71f73b8f1","user":{"login":"scottschurr","id":7840379,"avatar_url":"https://avatars.githubusercontent.com/u/7840379?v=3","gravatar_id":"","url":"https://api.github.com/users/scottschurr","html_url":"https://github.com/scottschurr","followers_url":"https://api.github.com/users/scottschurr/followers","following_url":"https://api.github.com/users/scottschurr/following{/other_user}","gists_url":"https://api.github.com/users/scottschurr/gists{/gist_id}","starred_url":"https://api.github.com/users/scottschurr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scottschurr/subscriptions","organizations_url":"https://api.github.com/users/scottschurr/orgs","repos_url":"https://api.github.com/users/scottschurr/repos","events_url":"https://api.github.com/users/scottschurr/events{/privacy}","received_events_url":"https://api.github.com/users/scottschurr/received_events","type":"User","site_admin":false},"repo":{"id":20703176,"name":"rippled","full_name":"scottschurr/rippled","owner":{"login":"scottschurr","id":7840379,"avatar_url":"https://avatars.githubusercontent.com/u/7840379?v=3","gravatar_id":"","url":"https://api.github.com/users/scottschurr","html_url":"https://github.com/scottschurr","followers_url":"https://api.github.com/users/scottschurr/followers","following_url":"https://api.github.com/users/scottschurr/following{/other_user}","gists_url":"https://api.github.com/users/scottschurr/gists{/gist_id}","starred_url":"https://api.github.com/users/scottschurr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scottschurr/subscriptions","organizations_url":"https://api.github.com/users/scottschurr/orgs","repos_url":"https://api.github.com/users/scottschurr/repos","events_url":"https://api.github.com/users/scottschurr/events{/privacy}","received_events_url":"https://api.github.com/users/scottschurr/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/scottschurr/rippled","description":"Ripple peer-to-peer network daemon","fork":true,"url":"https://api.github.com/repos/scottschurr/rippled","forks_url":"https://api.github.com/repos/scottschurr/rippled/forks","keys_url":"https://api.github.com/repos/scottschurr/rippled/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scottschurr/rippled/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scottschurr/rippled/teams","hooks_url":"https://api.github.com/repos/scottschurr/rippled/hooks","issue_events_url":"https://api.github.com/repos/scottschurr/rippled/issues/events{/number}","events_url":"https://api.github.com/repos/scottschurr/rippled/events","assignees_url":"https://api.github.com/repos/scottschurr/rippled/assignees{/user}","branches_url":"https://api.github.com/repos/scottschurr/rippled/branches{/branch}","tags_url":"https://api.github.com/repos/scottschurr/rippled/tags","blobs_url":"https://api.github.com/repos/scottschurr/rippled/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scottschurr/rippled/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scottschurr/rippled/git/refs{/sha}","trees_url":"https://api.github.com/repos/scottschurr/rippled/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scottschurr/rippled/statuses/{sha}","languages_url":"https://api.github.com/repos/scottschurr/rippled/languages","stargazers_url":"https://api.github.com/repos/scottschurr/rippled/stargazers","contributors_url":"https://api.github.com/repos/scottschurr/rippled/contributors","subscribers_url":"https://api.github.com/repos/scottschurr/rippled/subscribers","subscription_url":"https://api.github.com/repos/scottschurr/rippled/subscription","commits_url":"https://api.github.com/repos/scottschurr/rippled/commits{/sha}","git_commits_url":"https://api.github.com/repos/scottschurr/rippled/git/commits{/sha}","comments_url":"https://api.github.com/repos/scottschurr/rippled/comments{/number}","issue_comment_url":"https://api.github.com/repos/scottschurr/rippled/issues/comments{/number}","contents_url":"https://api.github.com/repos/scottschurr/rippled/contents/{+path}","compare_url":"https://api.github.com/repos/scottschurr/rippled/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scottschurr/rippled/merges","archive_url":"https://api.github.com/repos/scottschurr/rippled/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scottschurr/rippled/downloads","issues_url":"https://api.github.com/repos/scottschurr/rippled/issues{/number}","pulls_url":"https://api.github.com/repos/scottschurr/rippled/pulls{/number}","milestones_url":"https://api.github.com/repos/scottschurr/rippled/milestones{/number}","notifications_url":"https://api.github.com/repos/scottschurr/rippled/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scottschurr/rippled/labels{/name}","releases_url":"https://api.github.com/repos/scottschurr/rippled/releases{/id}","deployments_url":"https://api.github.com/repos/scottschurr/rippled/deployments","created_at":"2014-06-10T21:58:27Z","updated_at":"2016-01-15T01:44:41Z","pushed_at":"2016-11-01T16:15:00Z","git_url":"git://github.com/scottschurr/rippled.git","ssh_url":"git@github.com:scottschurr/rippled.git","clone_url":"https://github.com/scottschurr/rippled.git","svn_url":"https://github.com/scottschurr/rippled","homepage":"https://ripple.com","size":78957,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"ripple:develop","ref":"develop","sha":"7688a97d9571ad76c1d4a2e074fb60fd7f9cca97","user":{"login":"ripple","id":3165523,"avatar_url":"https://avatars.githubusercontent.com/u/3165523?v=3","gravatar_id":"","url":"https://api.github.com/users/ripple","html_url":"https://github.com/ripple","followers_url":"https://api.github.com/users/ripple/followers","following_url":"https://api.github.com/users/ripple/following{/other_user}","gists_url":"https://api.github.com/users/ripple/gists{/gist_id}","starred_url":"https://api.github.com/users/ripple/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ripple/subscriptions","organizations_url":"https://api.github.com/users/ripple/orgs","repos_url":"https://api.github.com/users/ripple/repos","events_url":"https://api.github.com/users/ripple/events{/privacy}","received_events_url":"https://api.github.com/users/ripple/received_events","type":"Organization","site_admin":false},"repo":{"id":2724167,"name":"rippled","full_name":"ripple/rippled","owner":{"login":"ripple","id":3165523,"avatar_url":"https://avatars.githubusercontent.com/u/3165523?v=3","gravatar_id":"","url":"https://api.github.com/users/ripple","html_url":"https://github.com/ripple","followers_url":"https://api.github.com/users/ripple/followers","following_url":"https://api.github.com/users/ripple/following{/other_user}","gists_url":"https://api.github.com/users/ripple/gists{/gist_id}","starred_url":"https://api.github.com/users/ripple/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ripple/subscriptions","organizations_url":"https://api.github.com/users/ripple/orgs","repos_url":"https://api.github.com/users/ripple/repos","events_url":"https://api.github.com/users/ripple/events{/privacy}","received_events_url":"https://api.github.com/users/ripple/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ripple/rippled","description":"Decentralized cryptocurrency blockchain daemon implementing the Ripple Consensus Ledger in C++","fork":false,"url":"https://api.github.com/repos/ripple/rippled","forks_url":"https://api.github.com/repos/ripple/rippled/forks","keys_url":"https://api.github.com/repos/ripple/rippled/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ripple/rippled/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ripple/rippled/teams","hooks_url":"https://api.github.com/repos/ripple/rippled/hooks","issue_events_url":"https://api.github.com/repos/ripple/rippled/issues/events{/number}","events_url":"https://api.github.com/repos/ripple/rippled/events","assignees_url":"https://api.github.com/repos/ripple/rippled/assignees{/user}","branches_url":"https://api.github.com/repos/ripple/rippled/branches{/branch}","tags_url":"https://api.github.com/repos/ripple/rippled/tags","blobs_url":"https://api.github.com/repos/ripple/rippled/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ripple/rippled/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ripple/rippled/git/refs{/sha}","trees_url":"https://api.github.com/repos/ripple/rippled/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ripple/rippled/statuses/{sha}","languages_url":"https://api.github.com/repos/ripple/rippled/languages","stargazers_url":"https://api.github.com/repos/ripple/rippled/stargazers","contributors_url":"https://api.github.com/repos/ripple/rippled/contributors","subscribers_url":"https://api.github.com/repos/ripple/rippled/subscribers","subscription_url":"https://api.github.com/repos/ripple/rippled/subscription","commits_url":"https://api.github.com/repos/ripple/rippled/commits{/sha}","git_commits_url":"https://api.github.com/repos/ripple/rippled/git/commits{/sha}","comments_url":"https://api.github.com/repos/ripple/rippled/comments{/number}","issue_comment_url":"https://api.github.com/repos/ripple/rippled/issues/comments{/number}","contents_url":"https://api.github.com/repos/ripple/rippled/contents/{+path}","compare_url":"https://api.github.com/repos/ripple/rippled/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ripple/rippled/merges","archive_url":"https://api.github.com/repos/ripple/rippled/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ripple/rippled/downloads","issues_url":"https://api.github.com/repos/ripple/rippled/issues{/number}","pulls_url":"https://api.github.com/repos/ripple/rippled/pulls{/number}","milestones_url":"https://api.github.com/repos/ripple/rippled/milestones{/number}","notifications_url":"https://api.github.com/repos/ripple/rippled/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ripple/rippled/labels{/name}","releases_url":"https://api.github.com/repos/ripple/rippled/releases{/id}","deployments_url":"https://api.github.com/repos/ripple/rippled/deployments","created_at":"2011-11-07T04:40:15Z","updated_at":"2016-10-31T19:58:12Z","pushed_at":"2016-11-01T06:26:58Z","git_url":"git://github.com/ripple/rippled.git","ssh_url":"git@github.com:ripple/rippled.git","clone_url":"https://github.com/ripple/rippled.git","svn_url":"https://github.com/ripple/rippled","homepage":"https://ripple.com","size":132394,"stargazers_count":1166,"watchers_count":1166,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":361,"mirror_url":null,"open_issues_count":118,"forks":361,"open_issues":118,"watchers":1166,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/ripple/rippled/pulls/1884"},"html":{"href":"https://github.com/ripple/rippled/pull/1884"},"issue":{"href":"https://api.github.com/repos/ripple/rippled/issues/1884"},"comments":{"href":"https://api.github.com/repos/ripple/rippled/issues/1884/comments"},"review_comments":{"href":"https://api.github.com/repos/ripple/rippled/pulls/1884/comments"},"review_comment":{"href":"https://api.github.com/repos/ripple/rippled/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ripple/rippled/pulls/1884/commits"},"statuses":{"href":"https://api.github.com/repos/ripple/rippled/statuses/50a36fcb5cd3b904cf01bef9e09d93f71f73b8f1"}}}}
{"actor":{"display_login":"seelabs"}}