From 625ca21d2f97fa658bfb9e1b7f19a1234df3cba7 Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Fri, 16 Feb 2024 22:35:32 +0100 Subject: [PATCH] options build --- .vscode/settings.json | 2 +- Builds/CMake/RippledCore.cmake | 6 + src/ripple/app/hook/impl/applyHook.cpp | 5 + src/ripple/app/ledger/OrderBookDB.cpp | 10 + src/ripple/app/ledger/OrderBookDB.h | 8 + src/ripple/app/tx/impl/InvariantCheck.cpp | 10 + src/ripple/app/tx/impl/OptionCreate.cpp | 422 ++++++++++++++++ src/ripple/app/tx/impl/OptionCreate.h | 54 +++ src/ripple/app/tx/impl/OptionExecute.cpp | 203 ++++++++ src/ripple/app/tx/impl/OptionExecute.h | 54 +++ src/ripple/app/tx/impl/OptionList.cpp | 88 ++++ src/ripple/app/tx/impl/OptionList.h | 54 +++ src/ripple/app/tx/impl/applySteps.cpp | 35 ++ src/ripple/basics/impl/mulDiv.cpp | 2 +- .../container/detail/aged_ordered_container.h | 55 +-- .../detail/aged_unordered_container.h | 55 +-- src/ripple/ledger/ApplyView.h | 2 +- src/ripple/ledger/impl/ApplyViewBase.cpp | 8 + src/ripple/peerfinder/impl/Bootcache.h | 7 - src/ripple/peerfinder/impl/Livecache.h | 11 - src/ripple/protocol/Feature.h | 3 +- src/ripple/protocol/Indexes.h | 33 ++ src/ripple/protocol/LedgerFormats.h | 18 + src/ripple/protocol/Option.h | 103 ++++ src/ripple/protocol/STValidation.h | 3 +- src/ripple/protocol/TxFlags.h | 15 + src/ripple/protocol/TxFormats.h | 3 + src/ripple/protocol/impl/Feature.cpp | 1 + src/ripple/protocol/impl/Indexes.cpp | 66 +++ src/ripple/protocol/impl/LedgerFormats.cpp | 31 ++ src/ripple/protocol/impl/Option.cpp | 37 ++ src/ripple/protocol/impl/TxFormats.cpp | 29 ++ src/ripple/protocol/jss.h | 7 +- src/ripple/rpc/handlers/ServerInfo.cpp | 1 + src/ripple/shamap/impl/SHAMapInnerNode.cpp | 2 +- src/test/app/ClaimReward_test.cpp | 2 + src/test/app/Invoke_test.cpp | 1 + src/test/app/Option_test.cpp | 459 ++++++++++++++++++ src/test/jtx/impl/escrow.cpp | 1 + src/test/jtx/impl/paychan.cpp | 1 + src/test/rpc/ServerDefinitions_test.cpp | 1 + 41 files changed, 1795 insertions(+), 113 deletions(-) create mode 100644 src/ripple/app/tx/impl/OptionCreate.cpp create mode 100644 src/ripple/app/tx/impl/OptionCreate.h create mode 100644 src/ripple/app/tx/impl/OptionExecute.cpp create mode 100644 src/ripple/app/tx/impl/OptionExecute.h create mode 100644 src/ripple/app/tx/impl/OptionList.cpp create mode 100644 src/ripple/app/tx/impl/OptionList.h create mode 100644 src/ripple/protocol/Option.h create mode 100644 src/ripple/protocol/impl/Option.cpp create mode 100644 src/test/app/Option_test.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 9e4544373..1642d6324 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,7 @@ "C_Cpp.clang_format_path": ".clang-format", "C_Cpp.clang_format_fallbackStyle": "{ ColumnLimit: 0 }", "[cpp]":{ - "editor.wordBasedSuggestions": false, + "editor.wordBasedSuggestions": "off", "editor.suggest.insertMode": "replace", "editor.semanticHighlighting.enabled": true, "editor.tabSize": 4, diff --git a/Builds/CMake/RippledCore.cmake b/Builds/CMake/RippledCore.cmake index cc3441af5..40f88cd6d 100644 --- a/Builds/CMake/RippledCore.cmake +++ b/Builds/CMake/RippledCore.cmake @@ -88,6 +88,7 @@ target_sources (xrpl_core PRIVATE src/ripple/protocol/impl/Issue.cpp src/ripple/protocol/impl/Keylet.cpp src/ripple/protocol/impl/LedgerFormats.cpp + src/ripple/protocol/impl/Option.cpp src/ripple/protocol/impl/PublicKey.cpp src/ripple/protocol/impl/Quality.cpp src/ripple/protocol/impl/Rate2.cpp @@ -219,6 +220,7 @@ install ( src/ripple/protocol/Keylet.h src/ripple/protocol/KnownFormats.h src/ripple/protocol/LedgerFormats.h + src/ripple/protocol/Option.h src/ripple/protocol/Protocol.h src/ripple/protocol/PublicKey.h src/ripple/protocol/Quality.h @@ -446,6 +448,9 @@ target_sources (rippled PRIVATE src/ripple/app/tx/impl/NFTokenCreateOffer.cpp src/ripple/app/tx/impl/NFTokenMint.cpp src/ripple/app/tx/impl/OfferStream.cpp + src/ripple/app/tx/impl/OptionCreate.cpp + src/ripple/app/tx/impl/OptionExecute.cpp + src/ripple/app/tx/impl/OptionList.cpp src/ripple/app/tx/impl/PayChan.cpp src/ripple/app/tx/impl/Payment.cpp src/ripple/app/tx/impl/SetAccount.cpp @@ -732,6 +737,7 @@ if (tests) src/test/app/NFTokenBurn_test.cpp src/test/app/NFTokenDir_test.cpp src/test/app/OfferStream_test.cpp + src/test/app/Option_test.cpp src/test/app/Offer_test.cpp src/test/app/OversizeMeta_test.cpp src/test/app/Path_test.cpp diff --git a/src/ripple/app/hook/impl/applyHook.cpp b/src/ripple/app/hook/impl/applyHook.cpp index 0b18e6a8d..bf5c9f160 100644 --- a/src/ripple/app/hook/impl/applyHook.cpp +++ b/src/ripple/app/hook/impl/applyHook.cpp @@ -1,12 +1,17 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include +#include +#include #include #include #include diff --git a/src/ripple/app/ledger/OrderBookDB.cpp b/src/ripple/app/ledger/OrderBookDB.cpp index 343e7f626..887b63c8b 100644 --- a/src/ripple/app/ledger/OrderBookDB.cpp +++ b/src/ripple/app/ledger/OrderBookDB.cpp @@ -85,9 +85,11 @@ OrderBookDB::update(std::shared_ptr const& ledger) return; } + decltype(optionBooks_) optionBooks; decltype(allBooks_) allBooks; decltype(xrpBooks_) xrpBooks; + optionBooks.reserve(optionBooks_.size()); allBooks.reserve(allBooks_.size()); xrpBooks.reserve(xrpBooks_.size()); @@ -161,6 +163,14 @@ OrderBookDB::addOrderBook(Book const& book) xrpBooks_.insert(book.in); } +void +OrderBookDB::addOptionOrderBook(Option const& option) +{ + std::lock_guard sl(mLock); + + optionBooks_.insert(option.issue); +} + // return list of all orderbooks that want this issuerID and currencyID std::vector OrderBookDB::getBooksByTakerPays(Issue const& issue) diff --git a/src/ripple/app/ledger/OrderBookDB.h b/src/ripple/app/ledger/OrderBookDB.h index ea7c60c5f..047b840c5 100644 --- a/src/ripple/app/ledger/OrderBookDB.h +++ b/src/ripple/app/ledger/OrderBookDB.h @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace ripple { @@ -40,6 +41,9 @@ public: void addOrderBook(Book const&); + void + addOptionOrderBook(Option const&); + /** @return a list of all orderbooks that want this issuerID and currencyID. */ std::vector @@ -55,6 +59,7 @@ public: BookListeners::pointer getBookListeners(Book const&); + BookListeners::pointer makeBookListeners(Book const&); @@ -71,6 +76,9 @@ private: // Maps order books by "issue in" to "issue out": hardened_hash_map> allBooks_; + // does an order book to XRP exist + hash_set optionBooks_; + // does an order book to XRP exist hash_set xrpBooks_; diff --git a/src/ripple/app/tx/impl/InvariantCheck.cpp b/src/ripple/app/tx/impl/InvariantCheck.cpp index e0843eb42..b05f7a1d4 100644 --- a/src/ripple/app/tx/impl/InvariantCheck.cpp +++ b/src/ripple/app/tx/impl/InvariantCheck.cpp @@ -109,6 +109,10 @@ XRPNotCreated::visitEntry( if (isXRP((*before)[sfAmount])) drops_ -= (*before)[sfAmount].xrp().drops(); break; + case ltOPTION_OFFER: + if (isXRP((*before)[sfAmount])) + drops_ -= (*before)[sfAmount].xrp().drops(); + break; default: break; } @@ -131,6 +135,10 @@ XRPNotCreated::visitEntry( if (!isDelete && isXRP((*after)[sfAmount])) drops_ += (*after)[sfAmount].xrp().drops(); break; + case ltOPTION_OFFER: + if (!isDelete && isXRP((*after)[sfAmount])) + drops_ += (*after)[sfAmount].xrp().drops(); + break; default: break; } @@ -492,6 +500,8 @@ LedgerEntryTypesMatch::visitEntry( case ltURI_TOKEN: case ltIMPORT_VLSEQ: case ltUNL_REPORT: + case ltOPTION: + case ltOPTION_OFFER: break; default: invalidTypeAdded_ = true; diff --git a/src/ripple/app/tx/impl/OptionCreate.cpp b/src/ripple/app/tx/impl/OptionCreate.cpp new file mode 100644 index 000000000..ba8ec7059 --- /dev/null +++ b/src/ripple/app/tx/impl/OptionCreate.cpp @@ -0,0 +1,422 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +TxConsequences +OptionCreate::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, TxConsequences::normal}; +} + +NotTEC +OptionCreate::preflight(PreflightContext const& ctx) +{ + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + if (ctx.tx.getFlags() & tfOptionCreateMask) + { + JLOG(ctx.j.warn()) << "OptionCreate: Invalid flags set."; + return temINVALID_FLAG; + } + + auto const flags = ctx.tx.getFlags(); + std::optional const swapID = ctx.tx[~sfInvoiceID]; + std::uint32_t const quantity = ctx.tx.getFieldU32(sfQualityIn); + + bool const isClose = (flags & tfPosition) != 0; + + if (isClose && !swapID) + { + JLOG(ctx.j.warn()) + << "OptionCreate: Cannot close option without swap ID."; + return temMALFORMED; + } + + if (quantity % 100) + { + JLOG(ctx.j.warn()) + << "OptionCreate: Quantity must be a multple of 100."; + return temMALFORMED; + } +} + +std::optional +sealOption( + Sandbox& sb, + STAmount strikePrice, + std::uint32_t expiration, + bool isPut, + bool isSell) +{ + const uint256 uBookBaseOpp = getOptionBookBase( + strikePrice.getIssuer(), + strikePrice.getCurrency(), + strikePrice.mantissa(), + expiration); + // std::cout << "BOOK BASE: " << uBookBaseOpp << "\n"; + const uint256 uBookEndOpp = getOptionQualityNext(uBookBaseOpp); + // std::cout << "BOOK BASE END: " << uBookEndOpp << "\n"; + auto key = sb.succ(uBookBaseOpp, uBookEndOpp); + if (!key) + { + return std::nullopt; + } + + auto sleOfferDir = sb.read(keylet::page(key.value())); + uint256 offerIndex; + unsigned int bookEntry; + + if (!cdirFirst(sb, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex)) + { + return std::nullopt; + } + + // auto sleOffer = sb.read(keylet::unchecked(offerIndex)); + // STAmount premium = sleOffer->getFieldAmount(sfAmount); + // auto const dir = to_string(sleOffer->getFieldH256(sfBookDirectory)); + // std::cout << "dir: " << dir << "\n"; + // auto const uTipIndex = sleOfferDir->key(); + // auto const optionQuality = getOptionQuality(uTipIndex); + // std::cout << "optionQuality: " << optionQuality << "\n"; + // STAmount dirRate = STAmount(premium.issue(), optionQuality); + // std::cout << "dirRate: " << dirRate << "\n"; + // BEAST_EXPECT(100 / rate == 110); + + do + { + auto sleItem = sb.read(keylet::child(offerIndex)); + if (!sleItem) + { + continue; + } + + auto const flags = sleItem->getFlags(); + bool const _isPut = (flags & tfType) != 0; + bool const _isSell = (flags & tfAction) != 0; + + // Skip Sealed Options + if (sleItem->getFieldU32(sfQualityOut) == 0) + { + continue; + } + + // looking for oposite option position. + if (_isPut == isPut && _isSell != isSell) + { + uint256 const sealID = sleItem->getFieldH256(sfInvoiceID); + if (sealID.isNonZero()) + { + continue; + } + else + { + return offerIndex; + } + } + } while ( + cdirNext(sb, sleOfferDir->key(), sleOfferDir, bookEntry, offerIndex)); + + return std::nullopt; +} + +TER +OptionCreate::doApply() +{ + if (!view().rules().enabled(featureOptions)) + return temDISABLED; + + Sandbox sb(&ctx_.view()); + + beast::Journal const& j = ctx_.journal; + + AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); + uint256 const optionID = ctx_.tx.getFieldH256(sfOfferID); + auto const flags = ctx_.tx.getFlags(); + STAmount const premium = ctx_.tx.getFieldAmount(sfAmount); + std::uint32_t const quantity = ctx_.tx.getFieldU32(sfQualityIn); + std::optional const swapID = ctx_.tx[~sfInvoiceID]; + + STAmount const totalPremium = + STAmount(premium.issue(), (premium.mantissa() * quantity)); + + auto sleSrcAcc = sb.peek(keylet::account(srcAccID)); + if (!sleSrcAcc) + return terNO_ACCOUNT; + + auto sleOptionAcc = sb.peek(keylet::unchecked(optionID)); + if (!sleOptionAcc) + return tecINTERNAL; + + STAmount const strikePrice = sleOptionAcc->getFieldAmount(sfAmount); + std::uint32_t const expiration = sleOptionAcc->getFieldU32(sfExpiration); + STAmount const quantityShares = STAmount(strikePrice.issue(), isXRP(premium) ? quantity * 1000000 : quantity); + + bool const isPut = (flags & tfType) != 0; + bool const isSell = (flags & tfAction) != 0; + bool const isClose = (flags & tfPosition) != 0; + + auto optionBookDirKeylet = keylet::optionBook( + strikePrice.getIssuer(), + strikePrice.getCurrency(), + strikePrice.mantissa(), + expiration); + auto optionOfferKeylet = + ripple::keylet::optionOffer(srcAccID, ctx_.tx.getSeqProxy().value()); + + std::optional const sealID = + sealOption(sb, strikePrice, expiration, isPut, isSell); + + // Update Sealed Option + AccountID oppAccID{AccountID{}}; + if (sealID) + { + JLOG(j.warn()) << "Updating Sealed Offer: sealID"; + auto sealedKeylet = ripple::keylet::unchecked(*sealID); + auto sealedOption = sb.peek(sealedKeylet); + sealedOption->setFieldH256(sfInvoiceID, optionOfferKeylet.key); + uint32_t currSealed = sealedOption->getFieldU32(sfQualityOut); + sealedOption->setFieldU32(sfQualityOut, currSealed - quantity); + oppAccID = sealedOption->getAccountID(sfOwner); + sb.update(sealedOption); + } + + // Block No Market Buys + if (!isSell && !sealID) + { + JLOG(j.warn()) << "OptionCreate: No open sell offers exist."; + return tecNO_TARGET; + } + + // Insert New Option (3 Times [issuer, party, counter-party]) + if (!isClose) + { + JLOG(j.warn()) << "Creating Option Offer: !isClose"; + // Add Option to Issuer + if (!isXRP(premium)) + { + // unimplemented + return tecINTERNAL; + } + + // Add Option to Self + std::uint32_t ownerCount{(*sleSrcAcc)[sfOwnerCount]}; + ++ownerCount; + XRPAmount const newReserve{sb.fees().accountReserve(ownerCount)}; + adjustOwnerCount(sb, sleSrcAcc, 1, j); + + auto optionOffer = std::make_shared(optionOfferKeylet); + auto const page = sb.dirInsert( + keylet::ownerDir(account_), + optionOfferKeylet, + describeOwnerDir(account_)); + if (!page) + { + std::cout << "NO PAGE" + << "\n"; + return tecDIR_FULL; + } + + optionOffer->setFlag(flags); + optionOffer->setAccountID(sfOwner, srcAccID); + optionOffer->setFieldU64(sfOwnerNode, *page); + optionOffer->setFieldH256(sfOfferID, optionID); + optionOffer->setFieldU32(sfQualityIn, quantity); + optionOffer->setFieldU32(sfQualityOut, quantity); + optionOffer->setFieldAmount(sfTakerPays, STAmount(0)); // Premium + optionOffer->setFieldAmount(sfAmount, STAmount(0)); // Locked + if (sealID) + { + JLOG(j.warn()) << "Updating Option Offer: sealID"; + optionOffer->setFieldH256(sfInvoiceID, *sealID); + optionOffer->setFieldU32(sfQualityOut, 0); + } + if (isSell) + { + JLOG(j.warn()) << "Updating Option Offer: isSell"; + // Update the locked balance and premium + optionOffer->setFieldAmount(sfTakerPays, premium); // Premium + optionOffer->setFieldAmount(sfAmount, quantityShares); // Locked + } + + Option const option{ + strikePrice.issue(), strikePrice.mantissa(), expiration}; + + // std::cout << "BOOK BASE: " << strikePrice.mantissa() << "\n"; + // std::cout << "PREMIUM: " << premium.mantissa() << "\n"; + + auto dir = + keylet::optionQuality(optionBookDirKeylet, premium.mantissa()); + bool const bookExisted = static_cast(sb.peek(dir)); + auto const bookNode = + sb.dirAppend(dir, optionOfferKeylet, [&](SLE::ref sle) { + sle->setFieldH160( + sfTakerPaysIssuer, strikePrice.issue().account); + sle->setFieldH160( + sfTakerPaysCurrency, strikePrice.issue().currency); + sle->setFieldU64(sfBaseFee, strikePrice.mantissa()); + sle->setFieldU32(sfExpiration, expiration); + sle->setFieldU64(sfExchangeRate, premium.mantissa()); + }); + + if (!bookNode) + { + JLOG(j_.debug()) << "final result: failed to add offer to book"; + return tecDIR_FULL; + } + + // if (!bookExisted) + // ctx_.app.getOrderBookDB().addOptionOrderBook(option); + + optionOffer->setFieldH256(sfBookDirectory, dir.key); + optionOffer->setFieldU64(sfBookNode, *bookNode); + sb.insert(optionOffer); + } + + if (isClose) + { + JLOG(j.warn()) << "Updating Option Offer: isClose"; + auto optionOffer = sb.peek(optionOfferKeylet); + uint256 const sealID = optionOffer->getFieldH256(sfInvoiceID); + if (!sealID) + { + JLOG(j.warn()) + << "OptionCreate: Cannot close option that has not sealed."; + return tecINTERNAL; + } + + // Update Swap Option + auto swapKeylet = ripple::keylet::unchecked(*swapID); + auto swapOption = sb.peek(swapKeylet); + if (!swapOption) + { + JLOG(j.warn()) << "OptionCreate: Swap Option does not exist."; + return tecINTERNAL; + } + swapOption->setFieldH256(sfInvoiceID, optionOfferKeylet.key); + sb.update(swapOption); + + // Update New Option + optionOffer->setFieldH256(sfInvoiceID, *swapID); + sb.update(optionOffer); + + // Erase Swap Sealed Option + uint256 const swapSealedID = swapOption->getFieldH256(sfInvoiceID); + if (!swapSealedID) + { + JLOG(j.warn()) << "OptionCreate: Swap Option is not sealed."; + return tecINTERNAL; + } + auto swapSealedKeylet = ripple::keylet::unchecked(swapSealedID); + auto swapSealedOption = sb.peek(swapSealedKeylet); + if (!swapSealedOption) + { + JLOG(j.warn()) + << "OptionCreate: Swap Sealed Option does not exist."; + return tecINTERNAL; + } + sb.erase(swapSealedOption); + + // Erase New Sealed Option + auto sealedKeylet = ripple::keylet::unchecked(sealID); + auto sealedOption = sb.peek(sealedKeylet); + sb.erase(sealedOption); + } + + // xfer the premium from the buyer to the seller + if (!isSell && sealID) + { + JLOG(j.warn()) << "Updating Option Balances: !isSell && sealID"; + auto sleOppAcc = sb.peek(keylet::account(oppAccID)); + if (!sleOppAcc) + return terNO_ACCOUNT; + + // Native + if (isXRP(premium)) + { + // ensure the account can cover the native + if (mSourceBalance < totalPremium.xrp()) + return tecUNFUNDED_PAYMENT; + + // subtract the balance from the buyer + { + STAmount bal = mSourceBalance; + bal -= totalPremium.xrp(); + if (bal < beast::zero || bal > mSourceBalance) + return tecINTERNAL; + sleSrcAcc->setFieldAmount(sfBalance, bal); + } + + // add the balance to the writer + { + STAmount bal = sleOppAcc->getFieldAmount(sfBalance); + STAmount prior = bal; + bal += totalPremium.xrp(); + if (bal < beast::zero || bal < prior) + return tecINTERNAL; + sleOppAcc->setFieldAmount(sfBalance, bal); + } + } + + sb.update(sleOppAcc); + } + else + { + JLOG(j.warn()) << "Updating Option Balances: isSell"; + if (isXRP(quantityShares)) + { + // subtract the balance from the writer + if (mSourceBalance < quantityShares.xrp()) + return tecUNFUNDED_PAYMENT; + { + STAmount bal = mSourceBalance; + bal -= quantityShares.xrp(); + if (bal < beast::zero || bal > mSourceBalance) + return tecINTERNAL; + + sleSrcAcc->setFieldAmount(sfBalance, bal); + } + } + } + + // apply + sb.update(sleSrcAcc); + sb.apply(ctx_.rawView()); + return tesSUCCESS; +} + +XRPAmount +OptionCreate::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + return Transactor::calculateBaseFee(view, tx); +} + +} // namespace ripple \ No newline at end of file diff --git a/src/ripple/app/tx/impl/OptionCreate.h b/src/ripple/app/tx/impl/OptionCreate.h new file mode 100644 index 000000000..45ef89de1 --- /dev/null +++ b/src/ripple/app/tx/impl/OptionCreate.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_OPTIONCREATE_H_INCLUDED +#define RIPPLE_TX_OPTIONCREATE_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class OptionCreate : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit OptionCreate(ApplyContext& ctx) : Transactor(ctx) + { + } + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/app/tx/impl/OptionExecute.cpp b/src/ripple/app/tx/impl/OptionExecute.cpp new file mode 100644 index 000000000..53b1c6a99 --- /dev/null +++ b/src/ripple/app/tx/impl/OptionExecute.cpp @@ -0,0 +1,203 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +TxConsequences +OptionExecute::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, TxConsequences::normal}; +} + +NotTEC +OptionExecute::preflight(PreflightContext const& ctx) +{ + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + if (ctx.tx.getFlags() & tfOptionExpireMask) + { + // There are no flags (other than universal). + JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set."; + return temINVALID_FLAG; + } +} + + +TER +OptionExecute::doApply() +{ + if (!view().rules().enabled(featureOptions)) + return temDISABLED; + + Sandbox sb(&ctx_.view()); + + beast::Journal const& j = ctx_.journal; + + AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); + uint256 const optionID = ctx_.tx.getFieldH256(sfOfferID); + uint256 const offerID = ctx_.tx.getFieldH256(sfInvoiceID); + auto const flags = ctx_.tx.getFlags(); + + auto sleSrcAcc = sb.peek(keylet::account(srcAccID)); + if (!sleSrcAcc) + return terNO_ACCOUNT; + + auto optionOfferKeylet = ripple::keylet::unchecked(offerID); + auto sleOptionOffer = sb.peek(optionOfferKeylet); + if (!sleOptionOffer) + return tecNO_TARGET; + + AccountID const ownrAccID = sleOptionOffer->getAccountID(sfOwner); + auto const sealedID = sleOptionOffer->getFieldH256(sfInvoiceID); + auto oppOfferKeylet = ripple::keylet::unchecked(sealedID); + auto sleSealedOffer = sb.peek(oppOfferKeylet); + if (!sleSealedOffer) + return tecNO_TARGET; + + AccountID const oppAccID = sleSealedOffer->getAccountID(sfOwner); + auto sleOppAcc = sb.peek(keylet::account(oppAccID)); + if (!sleOppAcc) + return terNO_ACCOUNT; + + auto const optionFlags = sleOptionOffer->getFlags(); + bool const isPut = (optionFlags & tfType) != 0; + bool const isSell = (optionFlags & tfAction) != 0; + + auto sleOption = sb.peek(keylet::unchecked(optionID)); + if (!sleOption) + return tecINTERNAL; + + STAmount const strikePrice = sleOption->getFieldAmount(sfAmount); + STAmount const quantityShares = sleSealedOffer->getFieldAmount(sfAmount); + std::uint32_t const quantity = sleOptionOffer->getFieldU32(sfQualityIn); + STAmount const totalValue = STAmount(strikePrice.issue(), (strikePrice.mantissa() * quantity)); + + if (flags & tfOptionExpire) + { + JLOG(j.warn()) << "OptionExecute: EXPIRE OPTION"; + sb.erase(sleSealedOffer); + sb.erase(sleOptionOffer); + sb.apply(ctx_.rawView()); + return tesSUCCESS; + } + + switch (isPut) + { + case 0: { + JLOG(j.warn()) << "OptionExecute: EXERCISE CALL"; + if (isXRP(quantityShares)) + { + if (mSourceBalance < totalValue.xrp()) + return tecUNFUNDED_PAYMENT; + + STAmount hBalance = mSourceBalance; + + // subtract the total value from the buyer + { + hBalance -= totalValue.xrp(); + if (hBalance < beast::zero || hBalance > mSourceBalance) + return tecINTERNAL; + } + + // add the total value to the writer + { + STAmount wBalance = sleOppAcc->getFieldAmount(sfBalance); + STAmount prior = wBalance; + wBalance += totalValue.xrp(); + if (wBalance < beast::zero || wBalance < prior) + return tecINTERNAL; + sleOppAcc->setFieldAmount(sfBalance, wBalance); + } + + // add the shares to the buyer + { + STAmount prior = hBalance; + hBalance += quantityShares.xrp(); + if (hBalance < beast::zero || hBalance < prior) + return tecINTERNAL; + } + sleSrcAcc->setFieldAmount(sfBalance, hBalance); + } + break; + } + case 1: { + JLOG(j.warn()) << "OptionExecute: EXERCISE PUT"; + if (isXRP(quantityShares)) + { + // add the total value to the holder + { + STAmount hBalance = mSourceBalance; + hBalance += totalValue.xrp(); + if (hBalance < beast::zero || hBalance < mSourceBalance) + return tecINTERNAL; + sleSrcAcc->setFieldAmount(sfBalance, hBalance); + } + + STAmount wBalance = sleOppAcc->getFieldAmount(sfBalance); + if (wBalance < totalValue.xrp()) + return tecUNFUNDED_PAYMENT; + // subtract the total value from the writer + { + STAmount prior = wBalance; + wBalance -= totalValue.xrp(); + if (wBalance < beast::zero || wBalance > prior) + return tecINTERNAL; + } + + // add the shares to the writer + { + STAmount prior = wBalance; + wBalance += quantityShares.xrp(); + if (wBalance < beast::zero || wBalance < prior) + return tecINTERNAL; + } + sleOppAcc->setFieldAmount(sfBalance, wBalance); + } + break; + } + default: + return tecINTERNAL; + } + + // apply + sb.update(sleOppAcc); + sb.update(sleSrcAcc); + sb.erase(sleSealedOffer); + sb.erase(sleOptionOffer); + sb.apply(ctx_.rawView()); + return tesSUCCESS; +} + +XRPAmount +OptionExecute::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + return Transactor::calculateBaseFee(view, tx); +} + +} // namespace ripple diff --git a/src/ripple/app/tx/impl/OptionExecute.h b/src/ripple/app/tx/impl/OptionExecute.h new file mode 100644 index 000000000..c6fa1288a --- /dev/null +++ b/src/ripple/app/tx/impl/OptionExecute.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_OPTIONEXECUTE_H_INCLUDED +#define RIPPLE_TX_OPTIONEXECUTE_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class OptionExecute : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit OptionExecute(ApplyContext& ctx) : Transactor(ctx) + { + } + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/app/tx/impl/OptionList.cpp b/src/ripple/app/tx/impl/OptionList.cpp new file mode 100644 index 000000000..013fa9943 --- /dev/null +++ b/src/ripple/app/tx/impl/OptionList.cpp @@ -0,0 +1,88 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +TxConsequences +OptionList::makeTxConsequences(PreflightContext const& ctx) +{ + return TxConsequences{ctx.tx, TxConsequences::normal}; +} + +NotTEC +OptionList::preflight(PreflightContext const& ctx) +{ + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) + return ret; + + JLOG(ctx.j.warn()) << (ctx.tx.getFlags()) << "\n"; + JLOG(ctx.j.warn()) << (ctx.tx.getFlags() & tfUniversalMask) << "\n"; + if (ctx.tx.getFlags() & tfUniversalMask) + { + // There are no flags (other than universal). + JLOG(ctx.j.warn()) << "Malformed transaction: Invalid flags set."; + return temINVALID_FLAG; + } +} + +TER +OptionList::doApply() +{ + if (!view().rules().enabled(featureOptions)) + return temDISABLED; + + Sandbox sb(&ctx_.view()); + + beast::Journal const& j = ctx_.journal; + + AccountID const srcAccID = ctx_.tx.getAccountID(sfAccount); + std::uint32_t const expiration = ctx_.tx.getFieldU32(sfExpiration); + STAmount const amount = ctx_.tx.getFieldAmount(sfAmount); + + auto sleSrcAcc = sb.peek(keylet::account(srcAccID)); + if (!sleSrcAcc) + return terNO_ACCOUNT; + + auto const optionKeylet = keylet::option(amount.getIssuer(), expiration); + auto const sleOption = std::make_shared(optionKeylet); + (*sleOption)[sfAmount] = amount; + (*sleOption)[sfExpiration] = expiration; + + // apply + sb.insert(sleOption); + sb.apply(ctx_.rawView()); + return tesSUCCESS; +} + +XRPAmount +OptionList::calculateBaseFee(ReadView const& view, STTx const& tx) +{ + return Transactor::calculateBaseFee(view, tx); +} + +} // namespace ripple diff --git a/src/ripple/app/tx/impl/OptionList.h b/src/ripple/app/tx/impl/OptionList.h new file mode 100644 index 000000000..1d73b6636 --- /dev/null +++ b/src/ripple/app/tx/impl/OptionList.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2024 XRPL-Labs + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_OPTIONLIST_H_INCLUDED +#define RIPPLE_TX_OPTIONLIST_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class OptionList : public Transactor +{ +public: + static constexpr ConsequencesFactoryType ConsequencesFactory{Custom}; + + explicit OptionList(ApplyContext& ctx) : Transactor(ctx) + { + } + + static XRPAmount + calculateBaseFee(ReadView const& view, STTx const& tx); + + static TxConsequences + makeTxConsequences(PreflightContext const& ctx); + + static NotTEC + preflight(PreflightContext const& ctx); + + TER + doApply() override; +}; + +} // namespace ripple + +#endif diff --git a/src/ripple/app/tx/impl/applySteps.cpp b/src/ripple/app/tx/impl/applySteps.cpp index b1708e0b1..fee667983 100644 --- a/src/ripple/app/tx/impl/applySteps.cpp +++ b/src/ripple/app/tx/impl/applySteps.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -37,8 +38,12 @@ #include #include #include +#include +#include +#include #include #include +#include #include #include #include @@ -122,6 +127,12 @@ invoke_preflight(PreflightContext const& ctx) return invoke_preflight_helper(ctx); case ttESCROW_CANCEL: return invoke_preflight_helper(ctx); + case ttOPTION_CREATE: + return invoke_preflight_helper(ctx); + case ttOPTION_EXECUTE: + return invoke_preflight_helper(ctx); + case ttOPTION_LIST: + return invoke_preflight_helper(ctx); case ttPAYCHAN_CLAIM: return invoke_preflight_helper(ctx); case ttPAYCHAN_CREATE: @@ -243,6 +254,12 @@ invoke_preclaim(PreclaimContext const& ctx) return invoke_preclaim(ctx); case ttESCROW_CANCEL: return invoke_preclaim(ctx); + case ttOPTION_CREATE: + return invoke_preclaim(ctx); + case ttOPTION_EXECUTE: + return invoke_preclaim(ctx); + case ttOPTION_LIST: + return invoke_preclaim(ctx); case ttPAYCHAN_CLAIM: return invoke_preclaim(ctx); case ttPAYCHAN_CREATE: @@ -326,6 +343,12 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx) return EscrowFinish::calculateBaseFee(view, tx); case ttESCROW_CANCEL: return EscrowCancel::calculateBaseFee(view, tx); + case ttOPTION_CREATE: + return OptionCreate::calculateBaseFee(view, tx); + case ttOPTION_EXECUTE: + return OptionExecute::calculateBaseFee(view, tx); + case ttOPTION_LIST: + return OptionList::calculateBaseFee(view, tx); case ttPAYCHAN_CLAIM: return PayChanClaim::calculateBaseFee(view, tx); case ttPAYCHAN_CREATE: @@ -469,6 +492,18 @@ invoke_apply(ApplyContext& ctx) EscrowCancel p(ctx); return p(); } + case ttOPTION_CREATE: { + OptionCreate p(ctx); + return p(); + } + case ttOPTION_EXECUTE: { + OptionExecute p(ctx); + return p(); + } + case ttOPTION_LIST: { + OptionList p(ctx); + return p(); + } case ttPAYCHAN_CLAIM: { PayChanClaim p(ctx); return p(); diff --git a/src/ripple/basics/impl/mulDiv.cpp b/src/ripple/basics/impl/mulDiv.cpp index 20e72e047..433c7dddd 100644 --- a/src/ripple/basics/impl/mulDiv.cpp +++ b/src/ripple/basics/impl/mulDiv.cpp @@ -30,7 +30,7 @@ mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div) { using namespace boost::multiprecision; - uint128_t result; + boost::multiprecision::uint128_t result; result = multiply(result, value, mul); result /= div; diff --git a/src/ripple/beast/container/detail/aged_ordered_container.h b/src/ripple/beast/container/detail/aged_ordered_container.h index 23534a26b..10dca962b 100644 --- a/src/ripple/beast/container/detail/aged_ordered_container.h +++ b/src/ripple/beast/container/detail/aged_ordered_container.h @@ -145,111 +145,78 @@ private: }; // VFALCO TODO This should only be enabled for maps. - class pair_value_compare - : public beast::detail::empty_base_optimization -#ifdef _LIBCPP_VERSION - , - public std::binary_function -#endif + class pair_value_compare : public Compare { public: -#ifndef _LIBCPP_VERSION using first_argument = value_type; using second_argument = value_type; using result_type = bool; -#endif bool operator()(value_type const& lhs, value_type const& rhs) const { - return this->member()(lhs.first, rhs.first); + return Compare::operator()(lhs.first, rhs.first); } pair_value_compare() { } - pair_value_compare(pair_value_compare const& other) - : beast::detail::empty_base_optimization(other) + pair_value_compare(pair_value_compare const& other) : Compare(other) { } private: friend aged_ordered_container; - pair_value_compare(Compare const& compare) - : beast::detail::empty_base_optimization(compare) + pair_value_compare(Compare const& compare) : Compare(compare) { } }; // Compares value_type against element, used in insert_check // VFALCO TODO hoist to remove template argument dependencies - class KeyValueCompare - : public beast::detail::empty_base_optimization -#ifdef _LIBCPP_VERSION - , - public std::binary_function -#endif + class KeyValueCompare : public Compare { public: -#ifndef _LIBCPP_VERSION using first_argument = Key; using second_argument = element; using result_type = bool; -#endif KeyValueCompare() = default; - KeyValueCompare(Compare const& compare) - : beast::detail::empty_base_optimization(compare) + KeyValueCompare(Compare const& compare) : Compare(compare) { } - // VFALCO NOTE WE might want only to enable these overloads - // if Compare has is_transparent -#if 0 - template - bool operator() (K const& k, element const& e) const - { - return this->member() (k, extract (e.value)); - } - - template - bool operator() (element const& e, K const& k) const - { - return this->member() (extract (e.value), k); - } -#endif - bool operator()(Key const& k, element const& e) const { - return this->member()(k, extract(e.value)); + return Compare::operator()(k, extract(e.value)); } bool operator()(element const& e, Key const& k) const { - return this->member()(extract(e.value), k); + return Compare::operator()(extract(e.value), k); } bool operator()(element const& x, element const& y) const { - return this->member()(extract(x.value), extract(y.value)); + return Compare::operator()(extract(x.value), extract(y.value)); } Compare& compare() { - return beast::detail::empty_base_optimization::member(); + return *this; } Compare const& compare() const { - return beast::detail::empty_base_optimization::member(); + return *this; } }; diff --git a/src/ripple/beast/container/detail/aged_unordered_container.h b/src/ripple/beast/container/detail/aged_unordered_container.h index 920e6196b..fcdccd2a6 100644 --- a/src/ripple/beast/container/detail/aged_unordered_container.h +++ b/src/ripple/beast/container/detail/aged_unordered_container.h @@ -148,115 +148,84 @@ private: }; // VFALCO TODO hoist to remove template argument dependencies - class ValueHash : private beast::detail::empty_base_optimization -#ifdef _LIBCPP_VERSION - , - public std::unary_function -#endif + class ValueHash : public Hash { public: -#ifndef _LIBCPP_VERSION using argument_type = element; using result_type = size_t; -#endif ValueHash() { } - ValueHash(Hash const& h) - : beast::detail::empty_base_optimization(h) + ValueHash(Hash const& h) : Hash(h) { } std::size_t operator()(element const& e) const { - return this->member()(extract(e.value)); + return Hash::operator()(extract(e.value)); } Hash& hash_function() { - return this->member(); + return *this; } Hash const& hash_function() const { - return this->member(); + return *this; } }; // Compares value_type against element, used in find/insert_check // VFALCO TODO hoist to remove template argument dependencies - class KeyValueEqual - : private beast::detail::empty_base_optimization -#ifdef _LIBCPP_VERSION - , - public std::binary_function -#endif + class KeyValueEqual : public KeyEqual { public: -#ifndef _LIBCPP_VERSION using first_argument_type = Key; using second_argument_type = element; using result_type = bool; -#endif KeyValueEqual() { } - KeyValueEqual(KeyEqual const& keyEqual) - : beast::detail::empty_base_optimization(keyEqual) + KeyValueEqual(KeyEqual const& keyEqual) : KeyEqual(keyEqual) { } - // VFALCO NOTE WE might want only to enable these overloads - // if KeyEqual has is_transparent -#if 0 - template - bool operator() (K const& k, element const& e) const - { - return this->member() (k, extract (e.value)); - } - - template - bool operator() (element const& e, K const& k) const - { - return this->member() (extract (e.value), k); - } -#endif - bool operator()(Key const& k, element const& e) const { - return this->member()(k, extract(e.value)); + return KeyEqual::operator()(k, extract(e.value)); } bool operator()(element const& e, Key const& k) const { - return this->member()(extract(e.value), k); + return KeyEqual::operator()(extract(e.value), k); } bool operator()(element const& lhs, element const& rhs) const { - return this->member()(extract(lhs.value), extract(rhs.value)); + return KeyEqual::operator()(extract(lhs.value), extract(rhs.value)); } KeyEqual& key_eq() { - return this->member(); + return *this; } KeyEqual const& key_eq() const { - return this->member(); + return *this; } }; diff --git a/src/ripple/ledger/ApplyView.h b/src/ripple/ledger/ApplyView.h index c68a29e7a..9a4b9fbc3 100644 --- a/src/ripple/ledger/ApplyView.h +++ b/src/ripple/ledger/ApplyView.h @@ -277,7 +277,7 @@ public: Keylet const& key, std::function const&)> const& describe) { - if (key.type != ltOFFER) + if (key.type != ltOFFER && key.type != ltOPTION_OFFER) { assert(!"Only Offers are appended to book directories. " "Call dirInsert() instead."); diff --git a/src/ripple/ledger/impl/ApplyViewBase.cpp b/src/ripple/ledger/impl/ApplyViewBase.cpp index dc98a35f7..9191634b4 100644 --- a/src/ripple/ledger/impl/ApplyViewBase.cpp +++ b/src/ripple/ledger/impl/ApplyViewBase.cpp @@ -19,6 +19,14 @@ #include #include +#include + +// #include +// #include +// #include +// #include +// #include +// #include namespace ripple { namespace detail { diff --git a/src/ripple/peerfinder/impl/Bootcache.h b/src/ripple/peerfinder/impl/Bootcache.h index eb6455879..b48f248ae 100644 --- a/src/ripple/peerfinder/impl/Bootcache.h +++ b/src/ripple/peerfinder/impl/Bootcache.h @@ -91,17 +91,10 @@ private: using value_type = map_type::value_type; struct Transform -#ifdef _LIBCPP_VERSION - : std::unary_function< - map_type::right_map::const_iterator::value_type const&, - beast::IP::Endpoint const&> -#endif { -#ifndef _LIBCPP_VERSION using first_argument_type = map_type::right_map::const_iterator::value_type const&; using result_type = beast::IP::Endpoint const&; -#endif explicit Transform() = default; diff --git a/src/ripple/peerfinder/impl/Livecache.h b/src/ripple/peerfinder/impl/Livecache.h index 12e2373fa..8ecd68e84 100644 --- a/src/ripple/peerfinder/impl/Livecache.h +++ b/src/ripple/peerfinder/impl/Livecache.h @@ -69,14 +69,9 @@ public: public: // Iterator transformation to extract the endpoint from Element struct Transform -#ifdef _LIBCPP_VERSION - : public std::unary_function -#endif { -#ifndef _LIBCPP_VERSION using first_argument = Element; using result_type = Endpoint; -#endif explicit Transform() = default; @@ -239,15 +234,9 @@ public: template struct Transform -#ifdef _LIBCPP_VERSION - : public std:: - unary_function> -#endif { -#ifndef _LIBCPP_VERSION using first_argument = typename lists_type::value_type; using result_type = Hop; -#endif explicit Transform() = default; diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 2d46df876..a5a20f62b 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -74,7 +74,7 @@ namespace detail { // Feature.cpp. Because it's only used to reserve storage, and determine how // large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than // the actual number of amendments. A LogicError on startup will verify this. -static constexpr std::size_t numFeatures = 70; +static constexpr std::size_t numFeatures = 71; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -358,6 +358,7 @@ extern uint256 const fixXahauV2; extern uint256 const featureRemit; extern uint256 const featureZeroB2M; extern uint256 const fixNSDelete; +extern uint256 const featureOptions; } // namespace ripple diff --git a/src/ripple/protocol/Indexes.h b/src/ripple/protocol/Indexes.h index 2d8c32140..40d1b5282 100644 --- a/src/ripple/protocol/Indexes.h +++ b/src/ripple/protocol/Indexes.h @@ -297,6 +297,27 @@ import_vlseq(PublicKey const& key) noexcept; Keylet uritoken(AccountID const& issuer, Blob const& uri); +Keylet +broker() noexcept;; + +Keylet +brokerStateDir(AccountID const& id, AccountID const& account) noexcept; + +Keylet +brokerState(AccountID const& id, AccountID const& issuer, uint256 const& ns) noexcept; + +Keylet +option(AccountID const& issuer, std::uint32_t expiration) noexcept; + +Keylet +optionBook(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration) noexcept; + +Keylet +optionOffer(AccountID const& id, std::uint32_t seq) noexcept; + +Keylet +optionQuality(Keylet const& k, std::uint64_t q) noexcept; + } // namespace keylet // Everything below is deprecated and should be removed in favor of keylets: @@ -311,6 +332,18 @@ getQualityNext(uint256 const& uBase); std::uint64_t getQuality(uint256 const& uBase); +uint256 +getOptionBookBase(AccountID const& issuer, Currency const& currency, std::uint64_t strike, std::uint32_t expiration); + +uint256 +getOptionQualityNext(uint256 const& uBase); + +// VFALCO This name could be better +std::uint64_t +getOptionQuality(uint256 const& uBase); + +/** The initial directory page for a specific quality */ + uint256 getTicketIndex(AccountID const& account, std::uint32_t uSequence); diff --git a/src/ripple/protocol/LedgerFormats.h b/src/ripple/protocol/LedgerFormats.h index 6134a8f33..b72dce974 100644 --- a/src/ripple/protocol/LedgerFormats.h +++ b/src/ripple/protocol/LedgerFormats.h @@ -254,6 +254,18 @@ enum LedgerEntryType : std::uint16_t \sa keylet::emitted */ ltEMITTED_TXN = 'E', + + /** A ledger object + + \sa keylet::broker_state + */ + ltOPTION = 'X', + + /** A ledger object + + \sa keylet::broker_state + */ + ltOPTION_OFFER = 'y', }; // clang-format off @@ -315,6 +327,12 @@ enum LedgerSpecificFlags { // ltURI_TOKEN lsfBurnable = 0x00000001, // True, issuer can burn the token + + // ltOPTION_OFFER + lsfType = 0x00010000, + lsfAction = 0x00020000, + lsfPosition = 0x00040000, + lsfCovered = 0x00080000, }; //------------------------------------------------------------------------------ diff --git a/src/ripple/protocol/Option.h b/src/ripple/protocol/Option.h new file mode 100644 index 000000000..098e5c091 --- /dev/null +++ b/src/ripple/protocol/Option.h @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_PROTOCOL_OPTION_H_INCLUDED +#define RIPPLE_PROTOCOL_OPTION_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +class Option final : public CountedObject