mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-03 21:45:32 +00:00
Compare commits
4 Commits
ripple/was
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eefb26e9a9 | ||
|
|
710f319cbe | ||
|
|
3a696131ef | ||
|
|
d1fb93e0cf |
@@ -272,7 +272,6 @@ words:
|
||||
- venv
|
||||
- vfalco
|
||||
- vinnie
|
||||
- wasmi
|
||||
- wextra
|
||||
- wptr
|
||||
- writeme
|
||||
|
||||
@@ -153,7 +153,6 @@ tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.net
|
||||
xrpl.core > xrpl.basics
|
||||
xrpl.core > xrpl.json
|
||||
xrpl.core > xrpl.ledger
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
xrpl.ledger > xrpl.protocol
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -64,9 +64,6 @@ DerivedData
|
||||
/.vs/
|
||||
/.vscode/
|
||||
|
||||
# zed IDE.
|
||||
/.zed/
|
||||
|
||||
# AI tools.
|
||||
/.augment
|
||||
/.claude
|
||||
|
||||
@@ -75,6 +75,10 @@ This release contains bug fixes only and no API changes.
|
||||
|
||||
[Version 2.5.0](https://github.com/XRPLF/rippled/releases/tag/2.5.0) was released on Jun 24, 2025.
|
||||
|
||||
### Breaking changes in 2.5.0
|
||||
|
||||
- `feature`: The `vetoed` field is now always a boolean. Obsolete amendments now have `"vetoed": true` and a new `"obsolete": true` field, instead of the previous `"vetoed": "Obsolete"` string value. This change improves type safety for API clients.
|
||||
|
||||
### Additions and bugfixes in 2.5.0
|
||||
|
||||
- `tx`: Added `ctid` field to the response and improved error handling. ([#4738](https://github.com/XRPLF/rippled/pull/4738))
|
||||
|
||||
@@ -103,7 +103,6 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(secp256k1 REQUIRED)
|
||||
find_package(SOCI REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(wasmi REQUIRED)
|
||||
find_package(xxHash REQUIRED)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -45,7 +45,6 @@ target_link_libraries(
|
||||
Xrpl::opts
|
||||
Xrpl::syslibs
|
||||
secp256k1::secp256k1
|
||||
wasmi::wasmi
|
||||
xrpl.libpb
|
||||
xxHash::xxhash
|
||||
$<$<BOOL:${voidstar}>:antithesis-sdk-cpp>)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"requires": [
|
||||
"zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1765850150.075",
|
||||
"xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1765850149.987",
|
||||
"wasmi/1.0.6#407c9db14601a8af1c7dd3b388f3e4cd%1768164779.349",
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
|
||||
@@ -35,7 +35,6 @@ class Xrpl(ConanFile):
|
||||
"openssl/3.5.4",
|
||||
"secp256k1/0.7.0",
|
||||
"soci/4.0.3",
|
||||
"wasmi/1.0.6",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
|
||||
@@ -216,7 +215,6 @@ class Xrpl(ConanFile):
|
||||
"soci::soci",
|
||||
"secp256k1::secp256k1",
|
||||
"sqlite3::sqlite",
|
||||
"wasmi::wasmi",
|
||||
"xxhash::xxhash",
|
||||
"zlib::zlib",
|
||||
]
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
#ifndef XRPL_CORE_SERVICEREGISTRY_H_INCLUDED
|
||||
#define XRPL_CORE_SERVICEREGISTRY_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/SHAMapHash.h>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/ledger/CachedSLEs.h>
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// Forward declarations
|
||||
namespace NodeStore {
|
||||
class Database;
|
||||
}
|
||||
namespace Resource {
|
||||
class Manager;
|
||||
}
|
||||
namespace perf {
|
||||
class PerfLog;
|
||||
}
|
||||
|
||||
class AcceptedLedger;
|
||||
class AmendmentTable;
|
||||
class Cluster;
|
||||
class CollectorManager;
|
||||
class DatabaseCon;
|
||||
class Family;
|
||||
class HashRouter;
|
||||
class InboundLedgers;
|
||||
class InboundTransactions;
|
||||
class JobQueue;
|
||||
class LedgerCleaner;
|
||||
class LedgerMaster;
|
||||
class LedgerReplayer;
|
||||
class LoadFeeTrack;
|
||||
class LoadManager;
|
||||
class ManifestCache;
|
||||
class NetworkOPs;
|
||||
class OpenLedger;
|
||||
class OrderBookDB;
|
||||
class Overlay;
|
||||
class PathRequests;
|
||||
class PeerReservationTable;
|
||||
class PendingSaves;
|
||||
class RelationalDatabase;
|
||||
class ServerHandler;
|
||||
class SHAMapStore;
|
||||
class TimeKeeper;
|
||||
class TransactionMaster;
|
||||
class TxQ;
|
||||
class ValidatorList;
|
||||
class ValidatorSite;
|
||||
|
||||
template <class Adaptor>
|
||||
class Validations;
|
||||
class RCLValidationsAdaptor;
|
||||
using RCLValidations = Validations<RCLValidationsAdaptor>;
|
||||
|
||||
using NodeCache = TaggedCache<SHAMapHash, Blob>;
|
||||
|
||||
/** Service registry for dependency injection.
|
||||
|
||||
This abstract interface provides access to various services and components
|
||||
used throughout the application. It separates the service locator pattern
|
||||
from the Application lifecycle management.
|
||||
|
||||
Components that need access to services can hold a reference to
|
||||
ServiceRegistry rather than Application when they only need service
|
||||
access and not lifecycle management.
|
||||
|
||||
*/
|
||||
class ServiceRegistry
|
||||
{
|
||||
public:
|
||||
ServiceRegistry() = default;
|
||||
virtual ~ServiceRegistry() = default;
|
||||
|
||||
// Core infrastructure services
|
||||
virtual CollectorManager&
|
||||
getCollectorManager() = 0;
|
||||
|
||||
virtual Family&
|
||||
getNodeFamily() = 0;
|
||||
|
||||
virtual TimeKeeper&
|
||||
timeKeeper() = 0;
|
||||
|
||||
virtual JobQueue&
|
||||
getJobQueue() = 0;
|
||||
|
||||
virtual NodeCache&
|
||||
getTempNodeCache() = 0;
|
||||
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
|
||||
// Protocol and validation services
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
|
||||
virtual HashRouter&
|
||||
getHashRouter() = 0;
|
||||
|
||||
virtual LoadFeeTrack&
|
||||
getFeeTrack() = 0;
|
||||
|
||||
virtual LoadManager&
|
||||
getLoadManager() = 0;
|
||||
|
||||
virtual RCLValidations&
|
||||
getValidations() = 0;
|
||||
|
||||
virtual ValidatorList&
|
||||
validators() = 0;
|
||||
|
||||
virtual ValidatorSite&
|
||||
validatorSites() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
validatorManifests() = 0;
|
||||
|
||||
virtual ManifestCache&
|
||||
publisherManifests() = 0;
|
||||
|
||||
// Network services
|
||||
virtual Overlay&
|
||||
overlay() = 0;
|
||||
|
||||
virtual Cluster&
|
||||
cluster() = 0;
|
||||
|
||||
virtual PeerReservationTable&
|
||||
peerReservations() = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
|
||||
// Storage services
|
||||
virtual NodeStore::Database&
|
||||
getNodeStore() = 0;
|
||||
|
||||
virtual SHAMapStore&
|
||||
getSHAMapStore() = 0;
|
||||
|
||||
virtual RelationalDatabase&
|
||||
getRelationalDatabase() = 0;
|
||||
|
||||
// Ledger services
|
||||
virtual InboundLedgers&
|
||||
getInboundLedgers() = 0;
|
||||
|
||||
virtual InboundTransactions&
|
||||
getInboundTransactions() = 0;
|
||||
|
||||
virtual TaggedCache<uint256, AcceptedLedger>&
|
||||
getAcceptedLedgerCache() = 0;
|
||||
|
||||
virtual LedgerMaster&
|
||||
getLedgerMaster() = 0;
|
||||
|
||||
virtual LedgerCleaner&
|
||||
getLedgerCleaner() = 0;
|
||||
|
||||
virtual LedgerReplayer&
|
||||
getLedgerReplayer() = 0;
|
||||
|
||||
virtual PendingSaves&
|
||||
pendingSaves() = 0;
|
||||
|
||||
virtual OpenLedger&
|
||||
openLedger() = 0;
|
||||
|
||||
virtual OpenLedger const&
|
||||
openLedger() const = 0;
|
||||
|
||||
// Transaction and operation services
|
||||
virtual NetworkOPs&
|
||||
getOPs() = 0;
|
||||
|
||||
virtual OrderBookDB&
|
||||
getOrderBookDB() = 0;
|
||||
|
||||
virtual TransactionMaster&
|
||||
getMasterTransaction() = 0;
|
||||
|
||||
virtual TxQ&
|
||||
getTxQ() = 0;
|
||||
|
||||
virtual PathRequests&
|
||||
getPathRequests() = 0;
|
||||
|
||||
// Server services
|
||||
virtual ServerHandler&
|
||||
getServerHandler() = 0;
|
||||
|
||||
virtual perf::PerfLog&
|
||||
getPerfLog() = 0;
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
@@ -16,7 +16,6 @@
|
||||
// Add new amendments to the top of this list.
|
||||
// Keep it sorted in reverse chronological order.
|
||||
|
||||
XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo)
|
||||
XRPL_FEATURE(PermissionDelegationV1_1, Supported::no, VoteBehavior::DefaultNo)
|
||||
|
||||
@@ -450,6 +450,7 @@ JSS(node_write_retries); // out: GetCounts
|
||||
JSS(node_writes_delayed); // out::GetCounts
|
||||
JSS(nth); // out: RPC server_definitions
|
||||
JSS(obligations); // out: GatewayBalances
|
||||
JSS(obsolete); // out: AmendmentTableImpl
|
||||
JSS(offers); // out: NetworkOPs, AccountOffers, Subscribe
|
||||
JSS(offer_id); // out: insertNFTokenOfferID
|
||||
JSS(offline); // in: TransactionSign
|
||||
|
||||
@@ -876,48 +876,42 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.fund(XRP(1000), alice, buyer, gw);
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
uint256 const nftAlice0ID = token::getNextID(env, alice, 0, tfTransferable);
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
env.close();
|
||||
uint8_t aliceCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
uint256 const nftXrpOnlyID = token::getNextID(env, alice, 0, tfOnlyXRP | tfTransferable);
|
||||
env(token::mint(alice, 0), txflags(tfOnlyXRP | tfTransferable));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
uint256 nftNoXferID = token::getNextID(env, alice, 0);
|
||||
env(token::mint(alice, 0));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 1);
|
||||
|
||||
// alice creates sell offers for her nfts.
|
||||
uint256 const plainOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftAlice0ID, XRP(10)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 2);
|
||||
|
||||
uint256 const audOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftAlice0ID, gwAUD(30)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 3);
|
||||
|
||||
uint256 const xrpOnlyOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftXrpOnlyID, XRP(20)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 4);
|
||||
|
||||
uint256 const noXferOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftNoXferID, XRP(30)), txflags(tfSellNFToken));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 5);
|
||||
|
||||
// alice creates a sell offer that will expire soon.
|
||||
uint256 const aliceExpOfferIndex = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
@@ -925,17 +919,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
txflags(tfSellNFToken),
|
||||
token::expiration(lastClose(env) + 5));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
|
||||
// buyer creates a Buy offer that will expire soon.
|
||||
uint256 const buyerExpOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, XRP(40)),
|
||||
token::owner(alice),
|
||||
token::expiration(lastClose(env) + 5));
|
||||
env.close();
|
||||
uint8_t buyerCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 6);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preflight
|
||||
@@ -943,12 +927,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// Set a negative fee.
|
||||
env(token::acceptSellOffer(buyer, noXferOfferIndex), fee(STAmount(10ull, true)), ter(temBAD_FEE));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// Set an invalid flag.
|
||||
env(token::acceptSellOffer(buyer, noXferOfferIndex), txflags(0x00008000), ter(temINVALID_FLAG));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// Supply nether an sfNFTokenBuyOffer nor an sfNFTokenSellOffer field.
|
||||
{
|
||||
@@ -956,7 +940,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv.removeMember(sfNFTokenSellOffer.jsonName);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
|
||||
// A buy offer may not contain a sfNFTokenBrokerFee field.
|
||||
@@ -965,7 +949,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv[sfNFTokenBrokerFee.jsonName] = STAmount(500000).getJson(JsonOptions::none);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
|
||||
// A sell offer may not contain a sfNFTokenBrokerFee field.
|
||||
@@ -974,7 +958,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
jv[sfNFTokenBrokerFee.jsonName] = STAmount(500000).getJson(JsonOptions::none);
|
||||
env(jv, ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
|
||||
// A brokered offer may not contain a negative or zero brokerFee.
|
||||
@@ -982,7 +966,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(gwAUD(0)),
|
||||
ter(temMALFORMED));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim
|
||||
@@ -990,48 +974,33 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// The buy offer must be non-zero.
|
||||
env(token::acceptBuyOffer(buyer, beast::zero), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// The buy offer must be present in the ledger.
|
||||
uint256 const missingOfferIndex = keylet::nftoffer(alice, 1).key;
|
||||
env(token::acceptBuyOffer(buyer, missingOfferIndex), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// The buy offer must not have expired.
|
||||
// NOTE: this is only a preclaim check with the
|
||||
// fixExpiredNFTokenOfferRemoval amendment disabled.
|
||||
env(token::acceptBuyOffer(alice, buyerExpOfferIndex), ter(tecEXPIRED));
|
||||
env(token::acceptBuyOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
|
||||
env.close();
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
buyerCount--;
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// The sell offer must be non-zero.
|
||||
env(token::acceptSellOffer(buyer, beast::zero), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// The sell offer must be present in the ledger.
|
||||
env(token::acceptSellOffer(buyer, missingOfferIndex), ter(tecOBJECT_NOT_FOUND));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// The sell offer must not have expired.
|
||||
// NOTE: this is only a preclaim check with the
|
||||
// fixExpiredNFTokenOfferRemoval amendment disabled.
|
||||
env(token::acceptSellOffer(buyer, aliceExpOfferIndex), ter(tecEXPIRED));
|
||||
env.close();
|
||||
// Alice's count is decremented by one when the expired offer is
|
||||
// removed.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
aliceCount--;
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim brokered
|
||||
@@ -1043,13 +1012,8 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(pay(gw, buyer, gwAUD(30)));
|
||||
env.close();
|
||||
aliceCount++;
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// We're about to exercise offer brokering, so we need
|
||||
// corresponding buy and sell offers.
|
||||
@@ -1058,38 +1022,35 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(29)), token::owner(alice));
|
||||
env.close();
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// gw attempts to broker offers that are not for the same token.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, xrpOnlyOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// gw attempts to broker offers that are not for the same currency.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, plainOfferIndex), ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// In a brokered offer, the buyer must offer greater than or
|
||||
// equal to the selling price.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex), ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
}
|
||||
{
|
||||
// buyer creates a buy offer for one of alice's nfts.
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(31)), token::owner(alice));
|
||||
env.close();
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Broker sets their fee in a denomination other than the one
|
||||
// used by the offers
|
||||
@@ -1097,14 +1058,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(XRP(40)),
|
||||
ter(tecNFTOKEN_BUY_SELL_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Broker fee way too big.
|
||||
env(token::brokerOffers(gw, buyerOfferIndex, audOfferIndex),
|
||||
token::brokerFee(gwAUD(31)),
|
||||
ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Broker fee is smaller, but still too big once the offer
|
||||
// seller's minimum is taken into account.
|
||||
@@ -1112,13 +1073,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
token::brokerFee(gwAUD(1.5)),
|
||||
ter(tecINSUFFICIENT_PAYMENT));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim buy
|
||||
@@ -1127,18 +1087,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftAlice0ID, gwAUD(30)), token::owner(alice));
|
||||
env.close();
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Don't accept a buy offer if the sell flag is set.
|
||||
env(token::acceptBuyOffer(buyer, plainOfferIndex), ter(tecNFTOKEN_OFFER_TYPE_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
|
||||
// An account can't accept its own offer.
|
||||
env(token::acceptBuyOffer(buyer, buyerOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// An offer acceptor must have enough funds to pay for the offer.
|
||||
env(pay(buyer, gw, gwAUD(30)));
|
||||
@@ -1146,7 +1105,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
|
||||
env(token::acceptBuyOffer(alice, buyerOfferIndex), ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// alice gives her NFT to gw, so alice no longer owns nftAlice0.
|
||||
{
|
||||
@@ -1155,7 +1114,7 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(gw, offerIndex));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
}
|
||||
env(pay(gw, buyer, gwAUD(30)));
|
||||
env.close();
|
||||
@@ -1163,13 +1122,12 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
// alice can't accept a buy offer for an NFT she no longer owns.
|
||||
env(token::acceptBuyOffer(alice, buyerOfferIndex), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Remove buyer's offer.
|
||||
env(token::cancelOffer(buyer, {buyerOfferIndex}));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
}
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim sell
|
||||
@@ -1178,24 +1136,23 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const buyerOfferIndex = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftXrpOnlyID, XRP(30)), token::owner(alice));
|
||||
env.close();
|
||||
buyerCount++;
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Don't accept a sell offer without the sell flag set.
|
||||
env(token::acceptSellOffer(alice, buyerOfferIndex), ter(tecNFTOKEN_OFFER_TYPE_MISMATCH));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
|
||||
// An account can't accept its own offer.
|
||||
env(token::acceptSellOffer(alice, plainOfferIndex), ter(tecCANT_ACCEPT_OWN_NFTOKEN_OFFER));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// The seller must currently be in possession of the token they
|
||||
// are selling. alice gave nftAlice0ID to gw.
|
||||
env(token::acceptSellOffer(buyer, plainOfferIndex), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// gw gives nftAlice0ID back to alice. That allows us to check
|
||||
// buyer attempting to accept one of alice's offers with
|
||||
@@ -1206,14 +1163,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(alice, offerIndex));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, alice) == aliceCount);
|
||||
BEAST_EXPECT(ownerCount(env, alice) == 7);
|
||||
}
|
||||
env(pay(buyer, gw, gwAUD(30)));
|
||||
env.close();
|
||||
BEAST_EXPECT(env.balance(buyer, gwAUD) == gwAUD(0));
|
||||
env(token::acceptSellOffer(buyer, audOfferIndex), ter(tecINSUFFICIENT_FUNDS));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -2812,7 +2769,6 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const nftokenID1 = token::getNextID(env, issuer, 0, tfTransferable);
|
||||
env(token::mint(minter, 0), token::issuer(issuer), txflags(tfTransferable));
|
||||
env.close();
|
||||
uint8_t issuerCount, minterCount, buyerCount;
|
||||
|
||||
// Test how adding an Expiration field to an offer affects permissions
|
||||
// for cancelling offers.
|
||||
@@ -2836,12 +2792,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
uint256 const offerBuyerToMinter = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
env.close();
|
||||
issuerCount = 1;
|
||||
minterCount = 3;
|
||||
buyerCount = 1;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// Test who gets to cancel the offers. Anyone outside of the
|
||||
// offer-owner/destination pair should not be able to cancel
|
||||
@@ -2853,36 +2806,32 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::cancelOffer(buyer, {offerIssuerToMinter}), ter(tecNO_PERMISSION));
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// The offer creator can cancel their own unexpired offer.
|
||||
env(token::cancelOffer(minter, {offerMinterToAnyone}));
|
||||
minterCount--;
|
||||
|
||||
// The destination of a sell offer can cancel the NFT owner's
|
||||
// unexpired offer.
|
||||
env(token::cancelOffer(issuer, {offerMinterToIssuer}));
|
||||
minterCount--;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// Anyone can cancel expired offers.
|
||||
env(token::cancelOffer(issuer, {offerBuyerToMinter}));
|
||||
buyerCount--;
|
||||
env(token::cancelOffer(buyer, {offerIssuerToMinter}));
|
||||
issuerCount--;
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
// Show that:
|
||||
// 1. An unexpired sell offer with an expiration can be accepted.
|
||||
@@ -2895,70 +2844,44 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(minter, nftokenID0, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const offer1 = keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
env(token::createOffer(minter, nftokenID1, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
|
||||
// Anyone can accept an unexpired sell offer.
|
||||
env(token::acceptSellOffer(buyer, offer0));
|
||||
minterCount--;
|
||||
buyerCount++;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// No one can accept an expired sell offer.
|
||||
env(token::acceptSellOffer(buyer, offer1), ter(tecEXPIRED));
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, the first accept
|
||||
// attempt deletes the expired offer. Without the amendment,
|
||||
// the offer remains and we can try to accept it again.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: offer was deleted by first accept attempt
|
||||
minterCount--;
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: offer still exists, second accept also
|
||||
// fails
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
}
|
||||
env(token::acceptSellOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// Check if the expired sell offer behavior matches amendment status
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
// The expired sell offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offer still exists and needs to be
|
||||
// cancelled
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
minterCount--;
|
||||
}
|
||||
// Ensure that owner counts are correct with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
// Anyone can cancel the expired sell offer.
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -2966,11 +2889,10 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(buyer, nftokenID0, XRP(0)), txflags(tfSellNFToken), token::destination(minter));
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
buyerCount--;
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
// Show that:
|
||||
// 1. An unexpired buy offer with an expiration can be accepted.
|
||||
@@ -2981,16 +2903,14 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
|
||||
uint256 const offer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
buyerCount++;
|
||||
|
||||
uint256 const offer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID1, drops(1)), token::owner(minter), token::expiration(expiration));
|
||||
buyerCount++;
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// An unexpired buy offer can be accepted.
|
||||
env(token::acceptBuyOffer(minter, offer0));
|
||||
@@ -2999,48 +2919,26 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// An expired buy offer cannot be accepted.
|
||||
env(token::acceptBuyOffer(minter, offer1), ter(tecEXPIRED));
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, the first accept
|
||||
// attempt deletes the expired offer. Without the amendment,
|
||||
// the offer remains and we can try to accept it again.
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: offer was deleted by first accept attempt
|
||||
buyerCount--;
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecOBJECT_NOT_FOUND));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: offer still exists, second accept also
|
||||
// fails
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
}
|
||||
env(token::acceptBuyOffer(issuer, offer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// Check if the expired buy offer behavior matches amendment status
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
// The expired buy offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offer still exists and can be
|
||||
// cancelled
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
}
|
||||
// Ensure that owner counts are the same with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
// Anyone can cancel the expired buy offer.
|
||||
env(token::cancelOffer(issuer, {offer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -3049,10 +2947,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
// Show that in brokered mode:
|
||||
// 1. An unexpired sell offer with an expiration can be accepted.
|
||||
@@ -3065,74 +2962,50 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env(token::createOffer(minter, nftokenID0, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const sellOffer1 = keylet::nftoffer(minter, env.seq(minter)).key;
|
||||
env(token::createOffer(minter, nftokenID1, drops(1)),
|
||||
token::expiration(expiration),
|
||||
txflags(tfSellNFToken));
|
||||
minterCount++;
|
||||
|
||||
uint256 const buyOffer0 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID0, drops(1)), token::owner(minter));
|
||||
buyerCount++;
|
||||
|
||||
uint256 const buyOffer1 = keylet::nftoffer(buyer, env.seq(buyer)).key;
|
||||
env(token::createOffer(buyer, nftokenID1, drops(1)), token::owner(minter));
|
||||
buyerCount++;
|
||||
|
||||
env.close();
|
||||
BEAST_EXPECT(lastClose(env) < expiration);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 3);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// An unexpired offer can be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer0, sellOffer0));
|
||||
minterCount--;
|
||||
|
||||
// Close enough ledgers to get past the expiration.
|
||||
while (lastClose(env) < expiration)
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// If the sell offer is expired it cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// With amendment: expired offers are deleted
|
||||
minterCount--;
|
||||
}
|
||||
// The expired sell offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// The buy offer was deleted, so no need to cancel it
|
||||
// The sell offer still exists, so we can cancel it
|
||||
env(token::cancelOffer(buyer, {buyOffer1}));
|
||||
buyerCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
|
||||
minterCount--;
|
||||
buyerCount--;
|
||||
}
|
||||
// Anyone can cancel the expired sell offer.
|
||||
env(token::cancelOffer(buyer, {buyOffer1, sellOffer1}));
|
||||
env.close();
|
||||
// Ensure that owner counts are the same with and without the
|
||||
// amendment
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0 && issuerCount == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1 && minterCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1 && buyerCount == 1);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
|
||||
// Transfer nftokenID0 back to minter so we start the next test in
|
||||
// a simple place.
|
||||
@@ -3141,10 +3014,9 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
env.close();
|
||||
env(token::acceptSellOffer(minter, offerSellBack));
|
||||
env.close();
|
||||
buyerCount--;
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == issuerCount);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == minterCount);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == buyerCount);
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 0);
|
||||
}
|
||||
// Show that in brokered mode:
|
||||
// 1. An unexpired buy offer with an expiration can be accepted.
|
||||
@@ -3182,28 +3054,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// If the buy offer is expired it cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// The expired buy offer is still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
if (features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// After amendment: expired offers were deleted during broker
|
||||
// attempt
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 1);
|
||||
// The buy offer was deleted, so no need to cancel it
|
||||
// The sell offer still exists, so we can cancel it
|
||||
env(token::cancelOffer(minter, {sellOffer1}));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Before amendment: expired offers still exist in ledger
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Anyone can cancel the expired buy offer.
|
||||
env(token::cancelOffer(minter, {buyOffer1, sellOffer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
@@ -3261,19 +3122,17 @@ class NFTokenBaseUtil_test : public beast::unit_test::suite
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// If the offers are expired they cannot be brokered.
|
||||
env(token::brokerOffers(issuer, buyOffer1, sellOffer1), ter(tecEXPIRED));
|
||||
env.close();
|
||||
|
||||
// The expired offers are still in the ledger.
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
if (!features[fixExpiredNFTokenOfferRemoval])
|
||||
{
|
||||
// Before amendment: expired offers still exist in ledger
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
// Anyone can cancel the expired offers
|
||||
env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
|
||||
}
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 2);
|
||||
BEAST_EXPECT(ownerCount(env, buyer) == 2);
|
||||
|
||||
// Anyone can cancel the expired offers.
|
||||
env(token::cancelOffer(issuer, {buyOffer1, sellOffer1}));
|
||||
env.close();
|
||||
BEAST_EXPECT(ownerCount(env, issuer) == 0);
|
||||
BEAST_EXPECT(ownerCount(env, minter) == 1);
|
||||
@@ -6877,9 +6736,7 @@ public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testWithFeats(
|
||||
allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT -
|
||||
fixExpiredNFTokenOfferRemoval);
|
||||
testWithFeats(allFeatures - fixNFTokenReserve - featureNFTokenMintOffer - featureDynamicNFT);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6910,15 +6767,6 @@ class NFTokenWOModify_test : public NFTokenBaseUtil_test
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenWOExpiredOfferRemoval_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testWithFeats(allFeatures - fixExpiredNFTokenOfferRemoval);
|
||||
}
|
||||
};
|
||||
|
||||
class NFTokenAllFeatures_test : public NFTokenBaseUtil_test
|
||||
{
|
||||
void
|
||||
|
||||
@@ -140,10 +140,17 @@ class Feature_test : public beast::unit_test::suite
|
||||
feature.isMember(jss::enabled) && !feature[jss::enabled].asBool(),
|
||||
feature[jss::name].asString() + " enabled");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) &&
|
||||
(feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"),
|
||||
feature[jss::name].asString() + " vetoed");
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
|
||||
feature[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
feature[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::obsolete) && feature[jss::obsolete].asBool() == true,
|
||||
feature[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!feature.isMember(jss::obsolete), feature[jss::name].asString() + " no obsolete");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::supported) && feature[jss::supported].asBool(),
|
||||
feature[jss::name].asString() + " supported");
|
||||
@@ -242,6 +249,7 @@ class Feature_test : public beast::unit_test::suite
|
||||
(*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
|
||||
(*it)[jss::name].asString() + " supported");
|
||||
BEAST_EXPECT(!(*it).isMember(jss::vetoed));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::obsolete));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::majority));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::count));
|
||||
BEAST_EXPECT(!(*it).isMember(jss::validations));
|
||||
@@ -302,11 +310,20 @@ class Feature_test : public beast::unit_test::suite
|
||||
if (expectEnabled)
|
||||
BEAST_EXPECTS(!(*it).isMember(jss::vetoed), (*it)[jss::name].asString() + " vetoed");
|
||||
else
|
||||
{
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!(*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asBool() == expectVeto) &&
|
||||
((*it)[jss::vetoed].isBool() || (*it)[jss::vetoed].asString() == "Obsolete"),
|
||||
(*it)[jss::name].asString() + " vetoed");
|
||||
(*it).isMember(jss::vetoed) && (*it)[jss::vetoed].isBool(),
|
||||
(*it)[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
(*it)[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
(*it)[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::obsolete) && (*it)[jss::obsolete].asBool() == true,
|
||||
(*it)[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!(*it).isMember(jss::obsolete), (*it)[jss::name].asString() + " no obsolete");
|
||||
}
|
||||
BEAST_EXPECTS(
|
||||
(*it).isMember(jss::supported) && (*it)[jss::supported].asBool() == expectSupported,
|
||||
(*it)[jss::name].asString() + " supported");
|
||||
@@ -369,10 +386,17 @@ class Feature_test : public beast::unit_test::suite
|
||||
(expectVeto || expectObsolete) ^ feature.isMember(jss::majority),
|
||||
feature[jss::name].asString() + " majority");
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool() == !expectObsolete &&
|
||||
(!feature[jss::vetoed].isBool() || feature[jss::vetoed].asBool() == expectVeto) &&
|
||||
(feature[jss::vetoed].isBool() || feature[jss::vetoed].asString() == "Obsolete"),
|
||||
feature[jss::name].asString() + " vetoed");
|
||||
feature.isMember(jss::vetoed) && feature[jss::vetoed].isBool(),
|
||||
feature[jss::name].asString() + " vetoed is bool");
|
||||
BEAST_EXPECTS(
|
||||
feature[jss::vetoed].asBool() == (expectVeto || expectObsolete),
|
||||
feature[jss::name].asString() + " vetoed value");
|
||||
if (expectObsolete)
|
||||
BEAST_EXPECTS(
|
||||
feature.isMember(jss::obsolete) && feature[jss::obsolete].asBool() == true,
|
||||
feature[jss::name].asString() + " obsolete");
|
||||
else
|
||||
BEAST_EXPECTS(!feature.isMember(jss::obsolete), feature[jss::name].asString() + " no obsolete");
|
||||
BEAST_EXPECTS(feature.isMember(jss::count), feature[jss::name].asString() + " count");
|
||||
BEAST_EXPECTS(feature.isMember(jss::threshold), feature[jss::name].asString() + " threshold");
|
||||
BEAST_EXPECTS(feature.isMember(jss::validations), feature[jss::name].asString() + " validations");
|
||||
@@ -460,7 +484,8 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
auto feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
|
||||
jrr = env.rpc("feature", featureName, "reject")[jss::result];
|
||||
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||
@@ -470,7 +495,8 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
|
||||
jrr = env.rpc("feature", featureName, "accept")[jss::result];
|
||||
if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
|
||||
@@ -480,7 +506,8 @@ class Feature_test : public beast::unit_test::suite
|
||||
return;
|
||||
feature = *(jrr.begin());
|
||||
BEAST_EXPECTS(feature[jss::name] == featureName, "name");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isString() && feature[jss::vetoed].asString() == "Obsolete", "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && feature[jss::vetoed].asBool() == true, "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::obsolete].isBool() && feature[jss::obsolete].asBool() == true, "obsolete");
|
||||
|
||||
// anything other than accept or reject is an error
|
||||
jrr = env.rpc("feature", featureName, "maybe");
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/beast/utility/PropertyStream.h>
|
||||
#include <xrpl/core/ServiceRegistry.h>
|
||||
#include <xrpl/protocol/Protocol.h>
|
||||
#include <xrpl/shamap/TreeNodeCache.h>
|
||||
|
||||
@@ -92,7 +91,7 @@ class Validations;
|
||||
class RCLValidationsAdaptor;
|
||||
using RCLValidations = Validations<RCLValidationsAdaptor>;
|
||||
|
||||
class Application : public ServiceRegistry, public beast::PropertyStream::Source
|
||||
class Application : public beast::PropertyStream::Source
|
||||
{
|
||||
public:
|
||||
/* VFALCO NOTE
|
||||
@@ -147,12 +146,92 @@ public:
|
||||
virtual boost::asio::io_context&
|
||||
getIOContext() = 0;
|
||||
|
||||
virtual CollectorManager&
|
||||
getCollectorManager() = 0;
|
||||
virtual Family&
|
||||
getNodeFamily() = 0;
|
||||
virtual TimeKeeper&
|
||||
timeKeeper() = 0;
|
||||
virtual JobQueue&
|
||||
getJobQueue() = 0;
|
||||
virtual NodeCache&
|
||||
getTempNodeCache() = 0;
|
||||
virtual CachedSLEs&
|
||||
cachedSLEs() = 0;
|
||||
virtual AmendmentTable&
|
||||
getAmendmentTable() = 0;
|
||||
virtual HashRouter&
|
||||
getHashRouter() = 0;
|
||||
virtual LoadFeeTrack&
|
||||
getFeeTrack() = 0;
|
||||
virtual LoadManager&
|
||||
getLoadManager() = 0;
|
||||
virtual Overlay&
|
||||
overlay() = 0;
|
||||
virtual TxQ&
|
||||
getTxQ() = 0;
|
||||
virtual ValidatorList&
|
||||
validators() = 0;
|
||||
virtual ValidatorSite&
|
||||
validatorSites() = 0;
|
||||
virtual ManifestCache&
|
||||
validatorManifests() = 0;
|
||||
virtual ManifestCache&
|
||||
publisherManifests() = 0;
|
||||
virtual Cluster&
|
||||
cluster() = 0;
|
||||
virtual PeerReservationTable&
|
||||
peerReservations() = 0;
|
||||
virtual RCLValidations&
|
||||
getValidations() = 0;
|
||||
virtual NodeStore::Database&
|
||||
getNodeStore() = 0;
|
||||
virtual InboundLedgers&
|
||||
getInboundLedgers() = 0;
|
||||
virtual InboundTransactions&
|
||||
getInboundTransactions() = 0;
|
||||
|
||||
virtual TaggedCache<uint256, AcceptedLedger>&
|
||||
getAcceptedLedgerCache() = 0;
|
||||
|
||||
virtual LedgerMaster&
|
||||
getLedgerMaster() = 0;
|
||||
virtual LedgerCleaner&
|
||||
getLedgerCleaner() = 0;
|
||||
virtual LedgerReplayer&
|
||||
getLedgerReplayer() = 0;
|
||||
virtual NetworkOPs&
|
||||
getOPs() = 0;
|
||||
virtual OrderBookDB&
|
||||
getOrderBookDB() = 0;
|
||||
virtual ServerHandler&
|
||||
getServerHandler() = 0;
|
||||
virtual TransactionMaster&
|
||||
getMasterTransaction() = 0;
|
||||
virtual perf::PerfLog&
|
||||
getPerfLog() = 0;
|
||||
|
||||
virtual std::pair<PublicKey, SecretKey> const&
|
||||
nodeIdentity() = 0;
|
||||
|
||||
virtual std::optional<PublicKey const>
|
||||
getValidationPublicKey() const = 0;
|
||||
|
||||
virtual Resource::Manager&
|
||||
getResourceManager() = 0;
|
||||
virtual PathRequests&
|
||||
getPathRequests() = 0;
|
||||
virtual SHAMapStore&
|
||||
getSHAMapStore() = 0;
|
||||
virtual PendingSaves&
|
||||
pendingSaves() = 0;
|
||||
virtual OpenLedger&
|
||||
openLedger() = 0;
|
||||
virtual OpenLedger const&
|
||||
openLedger() const = 0;
|
||||
virtual RelationalDatabase&
|
||||
getRelationalDatabase() = 0;
|
||||
|
||||
virtual std::chrono::milliseconds
|
||||
getIOLatency() = 0;
|
||||
|
||||
|
||||
@@ -903,7 +903,10 @@ AmendmentTableImpl::injectJson(
|
||||
if (!fs.enabled && isAdmin)
|
||||
{
|
||||
if (fs.vote == AmendmentVote::obsolete)
|
||||
v[jss::vetoed] = "Obsolete";
|
||||
{
|
||||
v[jss::vetoed] = true;
|
||||
v[jss::obsolete] = true;
|
||||
}
|
||||
else
|
||||
v[jss::vetoed] = fs.vote == AmendmentVote::down;
|
||||
}
|
||||
|
||||
@@ -53,17 +53,7 @@ NFTokenAcceptOffer::preclaim(PreclaimContext const& ctx)
|
||||
return {nullptr, tecOBJECT_NOT_FOUND};
|
||||
|
||||
if (hasExpired(ctx.view, (*offerSLE)[~sfExpiration]))
|
||||
{
|
||||
// Before fixExpiredNFTokenOfferRemoval amendment, expired
|
||||
// offers caused tecEXPIRED in preclaim, leaving them on ledger
|
||||
// forever. After the amendment, we allow expired offers to
|
||||
// reach doApply() where they get deleted and tecEXPIRED is
|
||||
// returned.
|
||||
if (!ctx.view.rules().enabled(fixExpiredNFTokenOfferRemoval))
|
||||
return {nullptr, tecEXPIRED};
|
||||
// Amendment enabled: return the expired offer to be handled in
|
||||
// doApply
|
||||
}
|
||||
return {nullptr, tecEXPIRED};
|
||||
|
||||
if ((*offerSLE)[sfAmount].negative())
|
||||
return {nullptr, temBAD_OFFER};
|
||||
@@ -309,7 +299,7 @@ NFTokenAcceptOffer::pay(AccountID const& from, AccountID const& to, STAmount con
|
||||
{
|
||||
// This should never happen, but it's easy and quick to check.
|
||||
if (amount < beast::zero)
|
||||
return tecINTERNAL; // LCOV_EXCL_LINE
|
||||
return tecINTERNAL;
|
||||
|
||||
auto const result = accountSend(view(), from, to, amount, j_);
|
||||
|
||||
@@ -420,39 +410,6 @@ NFTokenAcceptOffer::doApply()
|
||||
auto bo = loadToken(ctx_.tx[~sfNFTokenBuyOffer]);
|
||||
auto so = loadToken(ctx_.tx[~sfNFTokenSellOffer]);
|
||||
|
||||
// With fixExpiredNFTokenOfferRemoval amendment, check for expired offers
|
||||
// and delete them, returning tecEXPIRED. This ensures expired offers
|
||||
// are properly cleaned up from the ledger.
|
||||
if (view().rules().enabled(fixExpiredNFTokenOfferRemoval))
|
||||
{
|
||||
bool foundExpired = false;
|
||||
|
||||
auto const deleteOfferIfExpired = [this, &foundExpired](std::shared_ptr<SLE> const& offer) -> TER {
|
||||
if (offer && hasExpired(view(), (*offer)[~sfExpiration]))
|
||||
{
|
||||
JLOG(j_.trace()) << "Offer is expired, deleting: " << offer->key();
|
||||
if (!nft::deleteTokenOffer(view(), offer))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
JLOG(j_.fatal()) << "Unable to delete expired offer '" << offer->key() << "': ignoring";
|
||||
return tecINTERNAL;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
JLOG(j_.trace()) << "Deleted offer " << offer->key();
|
||||
foundExpired = true;
|
||||
}
|
||||
return tesSUCCESS;
|
||||
};
|
||||
|
||||
if (auto const r = deleteOfferIfExpired(bo); !isTesSuccess(r))
|
||||
return r;
|
||||
if (auto const r = deleteOfferIfExpired(so); !isTesSuccess(r))
|
||||
return r;
|
||||
|
||||
if (foundExpired)
|
||||
return tecEXPIRED;
|
||||
}
|
||||
|
||||
if (bo && !nft::deleteTokenOffer(view(), bo))
|
||||
{
|
||||
// LCOV_EXCL_START
|
||||
|
||||
Reference in New Issue
Block a user